OAuth

How web clients connect to Patchcord via OAuth 2.1 with automatic identity detection.

How it works

  1. Client connects to https://your-domain/mcp
  2. Server returns 401 with OAuth metadata pointer
  3. Client discovers endpoints via /.well-known/oauth-authorization-server
  4. Client dynamically registers via /register
  5. Server resolves an identity for that client
  6. Client completes OAuth flow and receives an access token
  7. Client uses MCP tools as the assigned agent

No manual bearer-token generation is needed for web clients.

Identity resolution

Patchcord resolves OAuth identities in this order:

  1. Explicit PATCHCORD_OAUTH_CLIENTS mapping for the client_id
  2. Known-client detection from redirect_uris, client_name, and client_uri
  3. Derived fallback from client_name
  4. Reject registration if no usable identity can be derived

Known-client detection can be extended with PATCHCORD_KNOWN_OAUTH_CLIENTS.

Redirect validation

Patchcord validates redirects differently depending on the client:

  • Known clients must use redirect URIs on allowed domains for that client
  • Unknown clients must keep redirect_uri domains aligned with client_uri

This blocks the obvious "claim to be ChatGPT, redirect to evil.com" class of spoofing.

Auto-detected clients

ClientDetected fromAgent ID
Claude.aiclaude.ai, anthropic.comclaudeai
ChatGPTchatgpt.com, openai.comchatgpt
Geminigemini.google.comgemini
GitHub Copilotgithub.com/copilot, copilot.microsoft.comcopilot
Cursorcursor.com, cursor.shcursor
Windsurfwindsurf, codeiumwindsurf
Perplexityperplexity.aiperplexity
Poepoe.compoe
Mistralmistral.aimistral
DeepSeekchat.deepseek.comdeepseek
Groqgroq.comgroq

If a client does not match a known pattern but does provide client_name, Patchcord derives an agent ID from that name. If neither a known match nor a usable client_name exists, registration is rejected.

Note: Some clients listed above (Cursor, Windsurf) are detected server-side but their OAuth flows don't complete in practice. Those clients should use the bearer-only endpoint /mcp/bearer instead. See Client Setup for details.

Setup per client

Claude.ai

  1. Settings > Connectors
  2. Click Add custom connector
  3. Fill in:
    • Name: Patchcord
    • Remote MCP server URL: https://your-domain/mcp
    • Leave OAuth Client ID and Secret empty
  4. Click Add, then Connect

ChatGPT

Requires Pro/Team/Enterprise/Edu with Developer Mode.

  1. Enable Developer Mode
  2. In a chat, open the tools/apps panel
  3. Add MCP server with URL: https://your-domain/mcp
  4. Complete the OAuth flow when prompted

Other MCP clients

Any client that supports remote MCP servers with OAuth 2.1 can connect using the same URL. The server advertises its capabilities at:

GET /.well-known/oauth-authorization-server

Response includes registration_endpoint, authorization_endpoint, and token_endpoint.

Explicit mappings

Map specific OAuth client IDs to project identities:

PATCHCORD_OAUTH_CLIENTS=e67c18c6-af0a-406d-b03e-2da5dec3e1c6=myproject:chatgpt

Mappings use client_id=namespace:agent.

Adding custom known clients

Extend the known-client list with:

PATCHCORD_KNOWN_OAUTH_CLIENTS=myapp:myapp.com,myapp.io;internal:internal.example.com

Each entry is agent_id:domain1,domain2,....

Default namespace fallback

Change the default namespace for unrecognized OAuth clients:

PATCHCORD_OAUTH_DEFAULT_NAMESPACE=myproject

Default is default.

Coexistence with bearer tokens

OAuth and static bearer tokens work on the same server:

Auth methodUsed byIdentity source
Static bearer tokenClaude Code, Codex CLIDatabase (bearer_tokens table)
OAuth 2.1Web MCP clientsExplicit mapping, known-client detection, or derived client_name fallback

Both share the same message bus.

Token lifecycle

  • Access tokens default to 24 hours
  • Refresh tokens default to 365 days
  • OAuth state is stored in Supabase, so it survives server restarts

Debugging

docker logs patchcord-server 2>&1 | grep "OAuth client registered"

Example output:

OAuth client registered: client_id=abc-123 client_name='ChatGPT' -> identity=myproject:chatgpt

Troubleshooting

ProblemFix
Client detected as wrong agentCheck PATCHCORD_OAUTH_CLIENTS override
OAuth flow failsVerify PATCHCORD_PUBLIC_URL uses HTTPS
ChatGPT doesn't see new toolsStart a new chat — tool lists are cached per chat
Token refresh failsRun python3 -m patchcord.cli.migrate to ensure tables exist