2026-06-21 Remote Obsidian MCP Connector
What I set out to do
Turn the local Obsidian MCP (the @bitbonsai/mcpvault stdio server that only works on this Mac) into a remote MCP I can add as a custom connector from the Claude iOS/Desktop app. Started as an open exploration of “what’s possible and recommended,” not a fixed plan.
What I actually did
Worked it end to end with Claude over a long session.
Design. Brainstormed and wrote a spec + plan. Key constraint: a custom connector means Anthropic’s cloud connects to the server, not the phone, so it must be publicly reachable, speak Streamable HTTP (not stdio), and do OAuth. Chose Option A: keep the server on the Mac (reads the live iCloud vault), expose it via a Cloudflare Tunnel, and gate it with Cloudflare Access + OAuth. Rejected the cloud-hosted option (would need a vault sync pipeline). Everything managed declaratively in nix home-manager, matching how programs.mcp already works.
Build (mostly via Playwright driving the Cloudflare dashboard).
- New nix module
obsidian-remote-mcp.nix: a stdio→Streamable-HTTP bridge +cloudflaredtunnel, both as launchd agents. - Created the
obsidian-mcptunnel, stored its token in~/.config/secrets/.secrets, routedobsidian-mcp.achhina.com → localhost:8788. - Auth: a self-hosted Access application on that hostname with Managed OAuth and a single-email policy. Corrected course mid-flight — Cloudflare’s docs say a self-hosted app (not an MCP portal) is the right pattern for one server; the portal would have left the raw tunnel hostname open as a back door.
- Identity provider: set up Google — created a GCP OAuth client + consent screen and published the app to production.
- Debugged the connector’s first failure: Claude’s Dynamic Client Registration was rejected because the Managed-OAuth
allowed_urislist was empty. Added Claude’sclaude.ai/claude.comcallbacks → registration returned 201 → connector worked. - Added an Obsidian favicon via a Cloudflare Single Redirect (which, usefully, runs before Access).
Security review (asked Claude to harden everything):
- Caught a real hole: supergateway binds all interfaces, so the unauthenticated bridge was reachable on the LAN (
http://<mac-ip>:8788), bypassing Access entirely. Swapped the bridge tomcp-proxy, which binds127.0.0.1only. - Redacted the tunnel token that had leaked into the session transcript (
jqin place). - Turned off DCR localhost/loopback (only Claude’s explicit callbacks remain).
- Deleted the leftover MCP-portal objects from the abandoned approach.
- Enabled Google login on the existing
achhina.comAccess apps too.
Made it permanent. Once the pre-existing hm/Zotero copyApps blocker was fixed, deployed so the bridge + tunnel run as managed launchd agents (survive reboot, no more manual nohup). Finally removed the now-redundant local stdio Obsidian server from Claude Desktop (kept it for the CLI/Codex).
What was striking
- supergateway has no host-binding flag — silent all-interfaces exposure. The kind of thing that looks fine (“serves on localhost”) until you check the listening socket.
mcp-proxydefaults to loopback. - Self-hosted Access app + Managed OAuth is the clean single-server pattern; the MCP portal is for aggregating many and would’ve left a back door.
- Cloudflare dash API mutations are CSRF-protected (GETs work, PUT/DELETE 403), so most changes went through the UI via Playwright. Authenticated
fetchreads were the workhorse for verification. - Cloudflare Single Redirects execute before Access — the favicon redirect worked without any auth bypass.
- RFC 7396 merge needs an explicit
nullto delete a key — omittingobsidianfrom the desktop patch wasn’t enough; had to setobsidian = null. .secretshas zsh-only vars, so wrapper scripts must useset -e, notset -eu.
Top 3 tomorrow
- Reconnect the obsidian MCP in this Claude Code session (
/mcp) or restart — to restore the in-session vault tools. - Restart Claude Desktop so it drops the local stdio obsidian server.
- Phone read + write round-trip sanity check (already effectively confirmed by
Inbox/Test Note from Phone.md). - Optionally rotate the Google client secret (briefly visible in a setup screenshot).
Update — later same session
Kept going after the first write:
- Acted on the security review: the LAN-binding fix (mcp-proxy on loopback), transcript token redaction, DCR tightening, and deletion of the abandoned MCP-portal objects.
- Made it permanent: once the pre-existing
hm/ZoterocopyAppsblocker was fixed, the bridge +cloudflarednow run as declarative launchd agents (RunAtLoad+KeepAlive, survive reboot) — no more manualnohup. - keepAwake = true: the caffeinate agent holds a
PreventSystemSleepassertion, so the Mac won’t sleep on AC power and the connector stays reachable. (caffeinate -sis AC-only; on battery it still sleeps —-iif I ever want battery too.) - Permissions: added
Bash(cloudflared *)to the nix-managed Claude allowlist for tunnel management. - Removed the redundant local stdio obsidian server from Claude Desktop (kept for the CLI/Codex).
- Commits on
main:74e3ce7,2e98e49,bfad4db,8cd6ede.
Debugging note — the obsidian MCP “failure”. While writing this journal, every mcp__obsidian__* call failed. Chased it down: mcpvault and the vault are completely healthy — a fresh stdio launch listed all 15 tools and returned vault stats (version 0.11.4). The fault was this Claude Code session’s connection to the server: it disconnected and reconnected under a new id mid-session — most likely because repeated hm runs regenerated ~/.config/mcp/mcp.json, prompting Claude Code to tear down and reconnect the server — and the new handle was stale. Fix is a session-level /mcp reconnect or restart. I’d first (wrongly) blamed a mcpvault version regression; that was a misread of its serverInfo string. Lesson: a dead MCP connection looks identical to a broken server — launch the server standalone before blaming it. (This journal was written via direct file write as the fallback.)
Follow-ups
- 2026-06-22: extended both the global and app Access session durations to 1 month (730h) to stop the ~daily re-auth — see 2026-06-22 Remote MCP One-Month Access Sessions.
Related
- Config:
obsidian-remote-mcp.nix,claude-desktop.nix(commits onmain) - Endpoint:
obsidian-mcp.achhina.com - Concepts: Model Context Protocol, Cloudflare Access, Cloudflare Tunnel