使用说明
Obsidian Cloud MCP
Access your Obsidian vault from Claude.ai on any device, including mobile, via an OAuth-protected MCP server running natively on a home Mac Studio.
What is this?
This repository provides the Cloudflare Worker OAuth layer and documentation for an Obsidian MCP setup. The MCP server, Cloudflare Tunnel ingress, and always-on configuration all run natively on a Mac Studio M1 (home, always-on), with no cloud server or Docker required.
The Problem: Obsidian runs locally, so AI assistants can't access your vault when you're on mobile or using cloud-based AI interfaces like Claude.ai.
The Solution: Run the MCP server natively on an always-on home Mac, expose it via a Cloudflare Tunnel, and protect it with an OAuth 2.1 provider on Cloudflare Workers.
Architecture
graph LR
claude["claude.ai"] -->|"HTTPS"| cfedge["Cloudflare edge<br/>(TLS termination)"]
cfedge -->|"tunnel"| cfd["cloudflared<br/>(LaunchDaemon, system)"]
cfd -->|"http 127.0.0.1:8081"| prm["PRM responder<br/>(LaunchAgent, bp)"]
cfd -->|"http 127.0.0.1:3000"| mcp["MCP server<br/>(LaunchAgent, bp)"]
mcp -->|"http 127.0.0.1:27123"| obs["Obsidian.app (native)<br/>Local REST API plugin"]
obs --> vault["/Users/bp/vault"]
obs -->|"Obsidian Sync"| sync["other devices"]
worker["Cloudflare Worker<br/>OAuth 2.1 + /introspect<br/>(UNCHANGED)"] -. "token introspection" .-> mcp
cfedge -. "/.well-known/oauth-protected-resource" .-> worker
Detailed Component View
graph TB
subgraph "User Devices"
claude["Claude.ai<br/>(any device)"]
browser["Browser<br/>(initial setup)"]
end
subgraph "Cloudflare Edge"
worker["Cloudflare Worker<br/><b>OAuth Provider</b>"]
kv["KV Store<br/>(tokens, clients)"]
do["Durable Object<br/>(session state)"]
worker --- kv
worker --- do
end
subgraph github["GitHub"]
gh_oauth["GitHub OAuth"]
end
subgraph "Mac Studio M1 (native, always-on)"
cfd["cloudflared<br/>(LaunchDaemon, system)"]
prm["PRM responder<br/>127.0.0.1:8081"]
mcp["MCP server<br/>(benpeter/obsidian-mcp-server@92ebb79)<br/>127.0.0.1:3000"]
obs["Obsidian.app<br/>+ Sync + Local REST API<br/>127.0.0.1:27123"]
vault["/Users/bp/vault"]
cfd -->|"/.well-known/..."| prm
cfd -->|"/mcp"| mcp
mcp -->|"REST API"| obs
obs --- vault
end
claude -->|"1. MCP Request"| cfd
cfd -->|"2. 401 + Resource Metadata"| claude
claude -->|"3. OAuth Flow"| worker
worker -->|"4. GitHub Login"| gh_oauth
gh_oauth -->|"5. Token"| worker
worker -->|"6. Access Token"| claude
claude -->|"7. MCP + Bearer Token"| cfd
mcp -->|"8. Validate Token"| worker
style worker fill:#f59e0b,color:#000
style mcp fill:#7c3aed,color:#fff
style obs fill:#7c3aed,color:#fff
style cfd fill:#22c55e,color:#000
Authentication Flow (RFC 7662 Token Introspection)
The MCP server and OAuth server are on separate hosts. Claude.ai discovers the OAuth server via Protected Resource Metadata (RFC 9728), then authenticates via GitHub OAuth on Cloudflare. The MCP server validates tokens by calling Cloudflare's introspection endpoint.
sequenceDiagram
participant C as Claude.ai
participant M as Mac Studio<br/>(MCP Server)
participant CF as Cloudflare<br/>(OAuth Provider)
participant GH as GitHub<br/>(Identity)
Note over C: User adds MCP connector
C->>M: POST /mcp (no token)
M-->>C: 401 Unauthorized
C->>M: GET /.well-known/oauth-protected-resource
M-->>C: {"authorization_servers": ["cloudflare-worker-url"]}
C->>CF: GET /.well-known/oauth-authorization-server
CF-->>C: {endpoints...}
C->>CF: POST /register (Dynamic Client Registration)
CF-->>C: {client_id, client_secret}
C->>CF: GET /authorize (+ PKCE)
CF->>GH: GitHub OAuth redirect
GH-->>CF: Authorization code
Note over CF: Verify email in whitelist
CF-->>C: Authorization code
C->>CF: POST /token (code + code_verifier)
CF-->>C: Access token (opaque)
Note over C: MCP connection established
C->>M: POST /mcp + Bearer token
M->>CF: POST /introspect (token)
CF-->>M: {"active": true, "sub": "..."}
M-->>C: MCP response (tools, resources)
Components
Mac Studio (native)
| Component | Purpose | Bind |
|---|---|---|
| cloudflared | Cloudflare Tunnel ingress, TLS at edge | system LaunchDaemon |
| MCP server | Obsidian MCP with Token Introspection | 127.0.0.1:3000 (LaunchAgent, bp) |
| PRM responder | Serves /.well-known/oauth-protected-resource |
127.0.0.1:8081 (LaunchAgent, bp) |
| Obsidian.app | Vault + Sync + Local REST API plugin | 127.0.0.1:27123 (login item, bp) |
The MCP server is benpeter/obsidian-mcp-server at commit 92ebb79 (a fork of cyanheads/obsidian-mcp-server carrying RFC 7662 Token Introspection support). The source is vendored at studio/mcp-server-src/.
Obsidian runs as a native app (not headless/Docker), against the real vault at /Users/bp/vault. Auto-login (bp) and launchd ensure the endpoint recovers unattended after reboot or power loss.
Cloudflare Worker
| Endpoint | Purpose |
|---|---|
/.well-known/oauth-authorization-server |
OAuth discovery (RFC 8414) |
/.well-known/oauth-protected-resource |
Resource metadata (RFC 9728) |
/authorize |
OAuth authorization |
/callback |
GitHub OAuth callback |
/token |
Token exchange |
/register |
Dynamic client registration (RFC 7591) |
/introspect |
Token introspection (RFC 7662) |
MCP Server Environment
| Variable | Description |
|---|---|
MCP_AUTH_MODE |
introspection — validates tokens via remote endpoint |
TOKEN_INTROSPECTION_URL |
URL of the Cloudflare /introspect endpoint |
OBSIDIAN_API_KEY |
Local REST API plugin key |
OBSIDIAN_BASE_URL |
http://127.0.0.1:27123 |
MCP_TRANSPORT_TYPE |
http — Streamable HTTP transport |
MCP_HTTP_HOST |
127.0.0.1 (tunnel is local; not exposed to LAN) |
MCP_HTTP_PORT |
3000 |
Quick Start
Step 1: Cloudflare Worker Setup
The Cloudflare Worker acts as OAuth provider for Claude.ai.
-
Create a GitHub OAuth App:
- Go to GitHub → Settings → Developer settings → OAuth Apps
- Authorization callback URL:
https://<your-worker>.workers.dev/callback - Note the Client ID and Client Secret
-
Deploy the Worker:
cd cloudflare-mcp-server npm install npx wrangler login # Create KV namespace npx wrangler kv namespace create OAUTH_KV # Update wrangler.jsonc with the KV namespace ID # Set secrets npx wrangler secret put GITHUB_CLIENT_ID npx wrangler secret put GITHUB_CLIENT_SECRET npx wrangler secret put COOKIE_ENCRYPTION_KEY # openssl rand -base64 32 # Deploy npx wrangler deploy -
Update GitHub OAuth App callback URL with the deployed worker URL.
Step 2: DNS
Create a CNAME pointing your subdomain at the Cloudflare Tunnel:
cloudflared tunnel create obsidian-mcp-studio
cloudflared tunnel route dns obsidian-mcp-studio mcp.yourdomain.com
(Requires the domain to be hosted on Cloudflare DNS.)
Step 3: Mac Studio Setup
See studio/README.md for the full native setup runbook:
- Install and configure
cloudflaredas a system LaunchDaemon - Build and deploy the MCP server as a LaunchAgent
- Configure the PRM responder LaunchAgent
- Set up Obsidian auto-launch and Local REST API plugin
- Enable auto-login for always-on recovery
Step 4: Configure Claude.ai
In Claude.ai settings, add a custom MCP connector:
| Field | Value |
|---|---|
| Server URL | https://obsidian-mcp.mostly-hallucinations.com/mcp |
Claude.ai will automatically:
- Discover the OAuth server via Protected Resource Metadata
- Redirect you to GitHub for login
- Establish the MCP connection after successful auth
Email Whitelist
Access is restricted by GitHub email. The allowed email is configured in cloudflare-mcp-server/src/github-handler.ts:
const ALLOWED_EMAILS = new Set<string>(["your-email@example.com"]);
Security
Authentication
| Layer | Method | Details |
|---|---|---|
| MCP Server | OAuth 2.1 | Via Cloudflare Worker, tokens validated via RFC 7662 Introspection |
| OAuth Identity | GitHub OAuth | Email whitelist restricts access |
Network
- MCP only via HTTPS — Cloudflare Tunnel with TLS at the edge
- REST API loopback-only — Port 27123 bound to 127.0.0.1, not exposed to LAN
- MCP server loopback-only — Port 3000 bound to 127.0.0.1
- No inbound ports — Cloudflare Tunnel is outbound-only; no port-forwarding required
HTTPS
- Automatic certificates managed by Cloudflare (CF-managed TLS on the edge)
- No Let's Encrypt, no port 80/443 inbound on the Mac
Troubleshooting
MCP server returning 401:
- Token introspection might be failing
- Check MCP logs:
/Users/bp/obsidian-mcp/logs/mcp.err.log - Verify Cloudflare Worker is deployed and
/introspectendpoint works
Tunnel not connecting:
- Check cloudflared logs:
/Library/Logs/com.benpeter.cloudflared.err.log - Restart:
sudo launchctl kickstart -k system/com.benpeter.cloudflared
OAuth redirect errors:
- Verify GitHub OAuth App callback URL matches worker URL
- Check email whitelist in
github-handler.ts
Obsidian REST API not responding:
- Confirm Obsidian.app is running:
curl -s http://127.0.0.1:27123/ - Check that the Local REST API plugin is enabled and bound to
127.0.0.1
See studio/README.md for full restart and health-check commands.
Deployment
There is no IaC or GitHub Actions deployment for the Mac Studio setup — it's a hand-managed home machine. The setup runbook and ops reference are in studio/README.md.
The Cloudflare Worker is deployed manually via wrangler deploy from cloudflare-mcp-server/ (see Quick Start above).
Costs
| Component | Monthly Cost |
|---|---|
| Cloudflare Worker | Free tier |
Domain (mostly-hallucinations.com) |
~$1 |
| Obsidian Sync | $4-8 |
| Mac Studio (owned hardware) | $0 incremental |
| Total | ~$5-9/month |
Repository Structure
obsidian-cloud-mcp/
+-- cloudflare-mcp-server/ # Cloudflare Worker (OAuth + Introspection)
| +-- src/
| | +-- index.ts # MCP proxy + OAuthProvider
| | +-- github-handler.ts # GitHub OAuth + email whitelist + /introspect
| | +-- utils.ts # OAuth helpers
| | +-- workers-oauth-utils.ts # CSRF, state management
| +-- wrangler.jsonc # Cloudflare config
+-- studio/ # Native Mac Studio deployment
| +-- README.md # Setup runbook and ops reference
| +-- mcp-server-src/ # Vendored MCP server source (benpeter/obsidian-mcp-server@92ebb79)
+-- docs/ # Design documents
| +-- migration/
| +-- 2026-05-29-mac-studio-migration-design.md
+-- mcp-server/ # Smart-server WIP (not deployed)
+-- LICENSE
License
Apache 2.0 — see LICENSE
