One MCP server, three connectors: Claude, ChatGPT, Grok
The same OAuth 2.1 server can pass Claude, ChatGPT, and Grok connector validation, provided your discovery, registration, and redirect handling account for how each client differs.
Updated
On this page
Claude, ChatGPT, and Grok all speak MCP and all use OAuth 2.1 with the same discovery machinery: protected-resource metadata (RFC 9728), authorization server metadata (RFC 8414), PKCE, and Streamable HTTP. Build the flow once (the connector checklist and the Auth0 proxy pattern both apply to all three), then handle the per-client differences below.
The differences that actually matter
| Claude (claude.ai) | ChatGPT | Grok (grok.com) | |
|---|---|---|---|
| Where users add it | Customize → Connectors | Settings → Connectors (developer mode) | grok.com/connectors → New Connector → Custom |
| Client identification | Dynamic client registration | CIMD, DCR, or a predefined client | OAuth flow in-app; register via DCR |
| Redirect URI | https://claude.ai/api/mcp/auth_callback and https://claude.com/api/mcp/auth_callback |
https://chatgpt.com/connector_platform_oauth_redirect |
Capture from the DCR request (see below) |
| DCR frequency | Per connector setup | Once per MCP server connection, then the registered client is reused | Per connector setup |
Redirect URIs: allow what registers, don’t hardcode
The single most common cross-client failure is a redirect-URI allowlist
written for one client. Claude’s callback is not ChatGPT’s
(connector_platform_oauth_redirect), and clients occasionally append
callback IDs or paths; the full derived redirect_uri must be honored, not
just the base host.
The pattern for a DCR endpoint that serves all three:
- Accept the registration and store the exact
redirect_uristhe client sends. - Validate the host against a short allowlist of client origins:
claude.ai,claude.com,chatgpt.com,openai.com,grok.com,x.ai. - At
/authorizetime, verify the requestedredirect_uriexactly matches one stored for that client, then let it through.
This also future-proofs you: when a client changes its callback path (it has happened), your server keeps working without a redeploy.
ChatGPT: CIMD is preferred, DCR is the fallback
ChatGPT identifies itself three ways, in order of preference:
- Client ID Metadata Documents (CIMD): the client presents a URL as
its
client_id; your authorization server fetches that URL for the client’s metadata. Support it if your stack can. - Dynamic client registration: used when you advertise a
registration_endpointand CIMD isn’t available. ChatGPT registers once per connection and reuses the client. - A predefined OAuth client: you paste a client ID/secret into the connector configuration. This is the escape hatch when your identity provider can’t do either of the above.
If you built the thin DCR proxy, option 2 works as-is; just add ChatGPT’s redirect to the allowed origins.
Grok: same flow, younger surface
Grok’s “Bring Your Own MCP” (grok.com/connectors → New Connector → Custom) takes your MCP server URL and runs the OAuth flow inside Grok. The discovery sequence is the standard one; the practical differences:
- Don’t hardcode a Grok callback URL from a blog post (including this one).
Log the
redirect_urisin your DCR endpoint’s requests the first time you connect, and let the allow-what-registers pattern above handle it. - The DNS and TLS pre-flight rules from the failure map apply unchanged: a public hostname and a publicly-trusted certificate, no tunnels with non-public DNS.
One test matrix
For each client, run the same four checks from the checklist: 401 probe, both metadata documents, DCR, then connect for real. Log every request your server sees during the first connection of each client; the logs are the authoritative documentation of what that client actually does, and they’re what you’ll want when one of the three changes behavior.
Next steps
To finish the rollout:
- Run the connector checklist once per client: the same four curls, three times.
- Connect each client for real and keep the first-connection logs.
- If one client fails where the others succeed, debugging connector failures isolates the step.
For the authorization server side, each provider has its own walkthrough: Auth0, Stytch, Supabase, and Hydra. The prompt library has a per-client prompt to paste.
Put it into practice
Add OAuth to a FastMCP server
Point an agent at your FastMCP codebase and have it wire bearer-token auth plus the discovery endpoints MCP clients require.
Preview prompt
Add OAuth 2.1 protection to my FastMCP server so it passes claude.ai's custom connector validation.
Requirements:
1. Every /mcp request without a valid bearer token must return 401 with a WWW-Authenticate header of the form: Bearer resource_metadata="https://MY_DOMAIN/.well-known/oauth-protected-resource"
2. Serve GET /.well-known/oauth-protected-resource returning {"resource": "https://MY_DOMAIN/mcp", "authorization_servers": ["https://MY_DOMAIN"]}
3. Serve GET /.well-known/oauth-authorization-server (RFC 8414) advertising authorization_endpoint, token_endpoint, and registration_endpoint on MY_DOMAIN, with code_challenge_methods_supported including "S256" and grant_types_supported including "authorization_code" and "refresh_token".
4. Validate incoming JWTs: signature against my provider's JWKS, plus iss, exp, and aud claims. Reject failures with 401, never 403.
5. Use FastMCP's auth hooks if the installed version has them; otherwise wrap the ASGI app with middleware. Do not fork or vendor the library.
My identity provider is: [Auth0 / other — fill in]. My domain is: [fill in].
Before writing code, read the current FastMCP version in my lockfile and check what auth support it ships. Show me the endpoint map you intend to implement before touching files, then implement it and give me curl commands to verify all four discovery steps.Use this site from your agent
Add this URL as a connector in Claude, ChatGPT, or Grok. No sign-in required.
https://quickoauth.com/mcp