obsidian-bridge
README.md
# GrokBot ↔ Obsidian Bridge
A small, fail-closed OAuth reverse proxy that lets Grok Bot and other
compatible MCP clients reach a self-hosted Obsidian vault. The public
process terminates owner approval. Authenticated Streamable HTTP is
forwarded only to a loopback Optimike Obsidian MCP backend.

This repository defaults to the `standard` tool profile and
`headless-readonly` runtime. It contains no real vault, hostname,
credential, private path, or note.
## How it works
1. Clone this repository and run the guided installer.
2. The installer pins and builds public Optimike Obsidian MCP `v3.0.0`,
installs the bridge in a local venv, and writes a mode-600 environment
file. It does not start the proxy, start Optimike, or touch a vault.
3. You start Optimike yourself on loopback, then start this proxy on
loopback, then put HTTPS in front of the proxy.
4. Grok Bot discovers OAuth, registers a client, and waits for owner
approval. After PKCE exchange it calls `/mcp`. The proxy strips
client secrets and forwards the session to Optimike.
Optimike source is not vendored here. See `docs/UPSTREAM.md`.
## Requirements
- Python 3.11+
- Node.js 22.7.5+ and npm
- git
- A public HTTPS hostname that forwards to `127.0.0.1:8099`
- An Optimike checkout pinned by the installer to tag `v3.0.0`
(`0a40387206a19deb6430f9b61b81fc7a07704717`)
- A vault copy you already manage. This installer never creates, opens,
or mutates one.
## One command after clone
```bash
./scripts/install.sh --non-interactive
```
The script refuses root and sudo, never pipes a download into a shell,
and never prints the generated owner code. Then inspect health:
```bash
python3 scripts/doctor.py
```
Fill the remaining local vault placeholder in `.env.local`. Start Optimike
yourself with `headless-readonly`, `standard`, `readonly`, production JWT
authentication, and the generated internal secret:
```bash
set -a
. ./.env.local
set +a
cd .runtime/upstream
NODE_ENV=production MCP_TRANSPORT_TYPE=http MCP_HTTP_HOST=127.0.0.1 \
MCP_HTTP_PORT=3010 MCP_AUTH_MODE=jwt \
MCP_AUTH_SECRET_KEY="$OBSIDIAN_BRIDGE_UPSTREAM_JWT_SECRET" \
node dist/index.js
```
In a second terminal, start the public OAuth proxy:
```bash
.venv/bin/python -m obsidian_bridge serve
```
`GET /health` should return `{"status":"ok"}`. Do not expose the Python
port directly.
## Configure the plugin
```bash
python3 scripts/configure_plugin.py https://mcp.example.com/mcp
```
The generated `mcp.json` and `.mcp.json` contain only the public URL.
Do not add an `Authorization` header. Grok Bot, Codex, and Cursor use
OAuth discovery and PKCE.
## Add Composio beside this bridge
[Corey Ganim's Grok Bot tip](https://x.com/coreyganim/status/2092559429275447742)
uses [Composio Connect](https://docs.composio.dev/docs/composio-connect) to go
beyond a client's native connector catalog. Composio describes Connect as one
hosted MCP endpoint for 1000+ apps through 7 meta-tools. It is not literally
every MCP server, and it is not bundled with this repository.
Keep the two connections separate:
| Client | This repository | Optional Composio connection |
| --- | --- | --- |
| Grok Bot | Install the Grok plugin and connect `obsidian-bridge` | Add Composio Connect as a second MCP connector |
| Cursor | Install the Cursor plugin or add this MCP URL | Add Composio Connect as a second MCP server |
| Codex | Install the Codex plugin or add this MCP URL | Prefer Composio's native Codex plugin, or choose Connect MCP explicitly |
The `.grok-plugin`, `.cursor-plugin`, and `.codex-plugin` manifests here install
only the read-only Obsidian bridge. Composio traffic does not pass through the
vault and Composio does not replace this bridge. See the
[tutorial](docs/TUTORIAL.md#optional-add-composio-as-a-second-connector) for
the complete setup model and safety boundaries.
## Security model
- single-owner OAuth 2.1 with DCR, S256 PKCE, and rotating refresh tokens
- owner code is required on the approval page and is never a bearer token
- loopback bind; TLS stays outside this process
- the client bearer token is stripped and replaced with a short-lived,
separately signed internal JWT before Optimike sees the request
- `/mcp/full` and non-readonly modes fail closed
- secrets stay in a mode-600 local file and out of git, argv, and stdout
Read `docs/SECURITY.md` and `docs/ARCHITECTURE.md` before exposing the
endpoint.
## Test and audit
```bash
python3 -m unittest discover -s tests -v
python3 src/privacy_scan.py --root .
python3 scripts/audit_git_history.py
```
## License
This bridge is MIT. Optimike Obsidian MCP remains a separate Apache-2.0
project and is not copied into this tree. See `docs/UPSTREAM.md`.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues