Cloudflare Worker — OAuth 2.1 + DCR + PKCE edge proxy for any PAT-authenticated MCP server.
Connects claude.ai (and any OAuth MCP client) to MCP servers that only support PAT/token auth — like the official GitHub MCP server.
This tool is free, open source, and took real time to build.
If it saves you hours of debugging OAuth flows, Dockerfiles, and Cloudflare configs — please consider sponsoring. Every contribution directly funds continued open source tooling for the regenerative AI ecosystem.
"Life before Profits."
— Dave Ladouceur / LIFEAI / Regen Dev Corp
claude.ai speaks OAuth 2.1. GitHub's MCP server speaks PAT. There is no official bridge.
api.githubcopilot.com/mcp requires a registered GitHub OAuth App per host — something only GitHub controls. Third-party clients including claude.ai are not registered, and the workaround in their own docs is "use Docker locally" — which defeats the purpose of a cloud AI assistant.
A single Cloudflare Worker (~200 lines) that:
- Speaks OAuth 2.1 to claude.ai — full DCR, PKCE, auth codes, access tokens
- Speaks PAT to the upstream MCP server
- Stores nothing sensitive in code — all secrets are CF Worker bindings
- Gates authorization with a PIN — only you can approve new connections
- Runs at the CF edge — zero infrastructure, zero maintenance, ~$0/month
claude.ai ──OAuth 2.1──▶ [this Worker] ──PAT──▶ api.githubcopilot.com/mcp
(Cloudflare edge)
1. claude.ai → GET /.well-known/oauth-authorization-server → discovers OAuth metadata
2. claude.ai → POST /register → gets client_id (DCR, RFC 7591)
3. claude.ai → GET /authorize?code_challenge=... → Worker serves PIN consent page
4. You → enter PIN in browser → Worker issues auth code
5. claude.ai → POST /token {code, code_verifier} → Worker issues access token (PKCE)
6. claude.ai → POST /mcp Authorization: Bearer <token> → Worker validates, injects PAT, proxies
Any Claude instance with the Cloudflare Developer Platform MCP connected can deploy this Worker entirely from chat — no Wrangler, no terminal, no local machine.
See deploy-with-claude.md for the complete step-by-step CF API runbook.
- Cloudflare account (free tier works)
- Wrangler CLI
- GitHub PAT with
reposcope (or any upstream Bearer token) - A domain on Cloudflare DNS — or use the free
*.workers.devsubdomain
git clone https://github.com/LIFEAI/cf-oauth-mcp-proxy.git
cd cf-oauth-mcp-proxywrangler kv namespace create github-mcp-oauth
# Copy the id from output[vars]
BASE_URL = "https://YOUR_WORKER_DOMAIN" # your Worker's public URL
UPSTREAM_MCP = "https://api.githubcopilot.com/mcp" # target MCP endpoint
[[kv_namespaces]]
binding = "OAUTH_KV"
id = "PASTE_YOUR_KV_ID_HERE"wrangler secret put UPSTREAM_TOKEN # your GitHub PAT (ghp_...)
wrangler secret put AUTH_PIN # PIN you'll type in browser to authorizeAUTH_PIN — alphanumeric, 6–12 chars, store in your password manager. Anyone with this PIN can authorize a new client connection.
Your AUTH_PIN is a short alphanumeric string you type in the browser consent page to authorize a new client connection. Generate one before running wrangler secret put AUTH_PIN.
Linux / macOS:
openssl rand -base64 12 | tr -dc 'A-Z0-9' | head -c 8Windows PowerShell:
-join ((65..90 + 48..57) | Get-Random -Count 8 | % {[char]$_})Windows cmd.exe (no PowerShell):
powershell -command "-join ((65..90 + 48..57) | Get-Random -Count 8 | % {[char]$_})"Rules: 6–12 characters, alphanumeric only (A-Z, 0-9). Store it in your password manager — you'll need it every time a new client connects. Anyone with this PIN can authorize a connection to your proxy.
Once you have it:
wrangler secret put AUTH_PIN
# paste your generated PIN when promptedTo rotate: run wrangler secret put AUTH_PIN again with a new value. All existing tokens remain valid — only new OAuth flows are affected.
wrangler deployIn Cloudflare dashboard: DNS → add proxied A record → Workers → Routes → add YOUR_WORKER_DOMAIN/* → cf-oauth-mcp-proxy
Important: Each MCP client (claude.ai, Claude Code, etc.) runs its own independent OAuth flow and gets its own KV access token. They never interfere with each other. You will enter your PIN once per client. Connect claude.ai first — it is the fastest sanity check that your Worker, KV, PIN, and redirect are all working correctly.
- Settings → Connectors → Add custom connector
- URL:
https://YOUR_WORKER_DOMAIN/mcp - Click Connect — browser opens the PIN consent page
- Enter your AUTH_PIN → click Authorize Access
- Redirected back to claude.ai ✅
Verify it works by asking Claude to list your private repos before moving on.
# Add the connector (Claude Code handles OAuth automatically)
claude mcp add-json github '{"type":"http","url":"https://YOUR_WORKER_DOMAIN/mcp"}'Restart Claude Code. On first use run /mcp — browser opens for PIN auth.
Claude Code stores its own token in your system keychain independently of claude.ai.
# Verify
claude mcp list
claude mcp get githubWindows note: If
add-jsonreturnsInvalid input, use:claude mcp add --transport http github https://YOUR_WORKER_DOMAIN/mcp
Each client that completes the OAuth flow gets its own entry in KV:
KV store:
token:<uuid-A> → { client_id: "claude-ai-dcr-id", created_at: ... } ← claude.ai token
token:<uuid-B> → { client_id: "claude-code-dcr-id", created_at: ... } ← Claude Code token
Both tokens are valid simultaneously. Each expires after 30 days independently.
To revoke a specific client: disconnect it in claude.ai settings or run claude mcp remove github.
Claude Code users: You don't need this proxy. Claude Code uses the
ghCLI which is already pre-authenticated with full private repo access. This proxy is built for claude.ai web and other OAuth MCP clients that can't inject a PAT directly.
- Go to Settings → Connectors → Add custom connector
- Enter your Worker URL:
https://YOUR_WORKER_DOMAIN/mcp - Click Connect — your browser opens the PIN consent page
- Enter your AUTH_PIN and click Authorize Access
- Redirected back to claude.ai ✅ Full private repo access.
To enable per-conversation: click + → Connectors → toggle on.
To revoke: Settings → Connectors → remove the connector. Token expires automatically after 30 days.
For any other MCP client that supports OAuth 2.1 (not Claude Code), use the included install-mcp.cmd:
install-mcp.cmd https://YOUR_WORKER_DOMAINOr run without arguments to be prompted:
install-mcp.cmdThe script will:
- Check
claudeCLI is installed - Remove any existing
githubMCP entry - Try
claude mcp add-json(Claude Code 2.1.1+) - Fall back to
claude mcp add --transport httpif needed - Verify with
claude mcp list - Print next steps including the OAuth PIN URL
If your team uses an OAuth-capable MCP host other than Claude Code:
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://YOUR_WORKER_DOMAIN/mcp"
}
}
}| Variable | Required | Description |
|---|---|---|
BASE_URL |
✅ | Public URL of this Worker (used in OAuth metadata) |
UPSTREAM_MCP |
✅ | URL of the upstream MCP server |
| Secret | Description |
|---|---|
UPSTREAM_TOKEN |
PAT or Bearer token for the upstream MCP server |
AUTH_PIN |
PIN to gate the consent page |
| Binding | Purpose |
|---|---|
OAUTH_KV |
Clients, auth codes (5 min TTL), access tokens (30 day TTL) |
| Method | Path | Description |
|---|---|---|
GET |
/.well-known/oauth-authorization-server |
RFC 8414 discovery |
POST |
/register |
RFC 7591 Dynamic Client Registration |
GET |
/authorize |
PIN consent page |
POST |
/authorize |
PIN submit → issues auth code |
POST |
/token |
Auth code + PKCE verifier → access token |
POST |
/revoke |
Token revocation |
* |
/* |
Authenticated MCP proxy to upstream |
| Layer | Mechanism |
|---|---|
| Upstream token | CF Worker secret binding — encrypted at rest, never in source |
| Client authorization | PIN-gated consent page — only PIN holder can authorize |
| Code interception | PKCE S256 — stolen auth codes are useless without code_verifier |
| Token storage | KV with 30-day TTL — revocable at any time |
| Unauthenticated requests | 401 with WWW-Authenticate header |
Optional hardening: Add a Cloudflare WAF rule to restrict /mcp to Anthropic's IP ranges only.
Change UPSTREAM_MCP in wrangler.toml. The Worker is fully generic.
# Any PAT-authenticated MCP server
UPSTREAM_MCP = "https://mcp.linear.app/sse"
UPSTREAM_MCP = "https://my-internal-mcp.company.com/mcp"We tried. The official ghcr.io/github/github-mcp-server image requires the right CMD args. The npm package is deprecated. Supergateway crashes on reconnect. Cloudflare Worker is 200 lines with zero runtime dependencies, deploys in 3 seconds, and costs nothing.
Stay on CF Workers for this pattern.
This pattern emerged from hours of trial and error building the LIFEAI regenerative AI ecosystem. If it saved you time — please sponsor:
Even $5/month helps sustain open tooling for the people building at the intersection of AI, regenerative systems, and financial engineering. Thank you.
Built by Dave Ladouceur / LIFEAI / Regen Dev Corp
Life before Profits.
MIT — use freely, fork liberally, adapt for any MCP server.