The symptom that brings most people here
The classic report sounds like this: connecting to the server works, but creating the identity fails — often with a message rendered by your own server that reads something like:- It’s the identity step that fails, not the connection. A plain reachability test passes; the OAuth authorize hop is what breaks.
- It often looks intermittent. A fresh setup works for a little while, then the same error returns — and everyone on the team hits it.
What MCP Manager actually does during the identity step
Knowing the exact sequence tells you where to look on your server. When you add a server by URL and create an identity, MCP Manager runs the standard OAuth 2.1 flow the MCP authorization spec defines:- Discovery. MCP Manager fetches your server’s
/.well-known/oauth-authorization-server(RFC 8414) and, where present, the protected-resource metadata (RFC 9728) to learn yourregistration_endpoint,authorization_endpoint, andtoken_endpoint. - Dynamic client registration (DCR). If a
registration_endpointis advertised, MCP Manager POSTs to it (RFC 7591) and your server returns a freshclient_id. This is a server-to-server call from MCP Manager’s backend. - Authorization. MCP Manager redirects your browser to the
authorization_endpointwith thatclient_idand a PKCE challenge. You approve, and your server redirects back to MCP Manager’s fixed callback URL. - Token exchange + refresh. MCP Manager exchanges the code at the
token_endpoint, stores the tokens encrypted, and refreshes them automatically from then on.
client_id — and renders the error you’re seeing.
Cause #1 — ephemeral client storage on a multi-instance host
This is the overwhelmingly common case, and it matches the intermittent symptom exactly. Many MCP server frameworks default to an in-memory store for dynamically registered clients. On a single long-lived process that’s fine. But on an autoscaling or serverless host — Google Cloud Run, AWS Lambda/Fargate, Kubernetes with more than one replica, anything behind a load balancer — that store is per-instance and ephemeral: it isn’t shared between instances and doesn’t survive a scale event or restart. The DCR call writes the client into one instance’s memory; the browser authorize request is load-balanced to another instance that never saw it.The fix: shared storage + a stable key
The fix is on the server, and it has two parts — getting only the first is a common near-miss:- Give the OAuth layer a network-accessible, shared client store so a registration written by one instance is visible to every instance. Redis (e.g. Cloud Memorystore) is the usual choice; a database, Firestore, or object storage also work.
- Use a stable encryption key shared across instances. If the client store is encrypted with a per-instance ephemeral key, a second instance still can’t decrypt what the first wrote — so you get the same failure even with shared storage. Derive the key from a secret manager so every instance loads the same one.
FastMCP’s OIDCProxy, specifically
FastMCP’s built-inOIDCProxy is a frequent source of this exact failure on Cloud Run, because its client store defaults to in-memory and its default encryption key is ephemeral. The fix maps directly onto the two parts above:
- Set the proxy’s
client_storageto a shared backend so registrations are durable and visible to every instance — see FastMCP’sclient_storageparameter reference. - Wrap that store so it’s encrypted with a stable key (for example, a Fernet key derived from a Secret Manager secret), rather than the default ephemeral key.
The other usual suspects
If the failure is consistent rather than intermittent, or the storage fix doesn’t resolve it, work down this list. Each is a distinct server-side cause.Missing or incomplete well-known metadata
Missing or incomplete well-known metadata
/.well-known/oauth-authorization-server (RFC 8414), or it omits registration_endpoint, MCP Manager can’t discover where to register and can’t run automatic DCR — so it falls back to asking you for a Client ID and Secret. Verify what your server actually advertises by fetching that well-known path and reading the JSON. Confirm registration_endpoint, authorization_endpoint, and token_endpoint are present and point at reachable URLs. MCP Manager handles a server that legitimately has no metadata by guiding you to pre-registration — but if you intended DCR to work, missing metadata is why it didn’t.Redirect URI not allowed
Redirect URI not allowed
redirect_uris supplied during DCR and enforce their own allowlist at the authorize step. If MCP Manager’s callback isn’t on it, the authorize hop is rejected. Add the exact callback URL to your server’s allowed redirect URIs:An allowlist of well-known clients
An allowlist of well-known clients
registration_endpoint but in
practice only accept a fixed allowlist
of well-known clients (Claude, Cursor,
ChatGPT, and the like), rejecting any
dynamically registered client. That’s
a limitation in the server’s DCR
implementation — open DCR means
accepting clients you didn’t
pre-approve. Either widen the server
to accept dynamically registered
clients, or expose a pre-registration
path so MCP Manager can connect with a
Client ID and Secret you issue it.Token endpoint auth method or scope mismatch
Token endpoint auth method or scope mismatch
token_endpoint_auth_method (client_secret_post, client_secret_basic, or none) and requests the scopes your metadata advertises. If your server requires a method it doesn’t advertise, or a scope it doesn’t list in scopes_supported, registration can succeed while the token exchange fails. Make sure the methods and scopes your server enforces match the ones it advertises in its metadata.A quick diagnostic path
What MCP Manager does on its side
When the OAuth callback fails, MCP Manager doesn’t fail silently. It records an alert (error.inbound_server.oauth_callback_failed) capturing the provider’s error code and description and the redirect URI involved, and deep-links you to it so you can read exactly what the upstream returned. The server’s identity then shows a Not connected state, and you can retry through the reconnect flow once the server side is fixed — which also lets you switch the server to pre-registration or token auth if you decide not to rely on DCR. See Alerts and Authentication & Identity.
.png?fit=max&auto=format&n=gKqTvJPtsRi2bLNx&q=85&s=8abbce3efb590630de2102c43d32aadf)
.png?fit=max&auto=format&n=Dy9YsIECUbR9JZiT&q=85&s=a1f404cd7f7aeb1727c89d81137ae1ac)