MCP Authentication
Every remote MCP server has to decide how callers prove who they are, and the specification leaves most of that decision open.
Where to start
The spec makes authorization OPTIONAL, and says HTTP transports SHOULD rather than MUST use its OAuth flow. So the choice is what your callers can present, not what the spec requires. A static key works with every agent SDK. Platform identity avoids a stored secret between workloads you own. The spec's OAuth flow is what the ChatGPT and Claude connector directories require.
Authentication methods
Learn how to implement each authentication method, with working code for your agent.
How the methods differ
Compare capabilities of different authentication options for your MCP servers.
- MCP spec
- Mixed — claude.ai and Claude Code run the spec's OAuth flow; a vault token or a static header sits outside it, not against it
- Works with
- Four surfaces, four credential fields; only Claude Code, which runs locally, sends a header name of your choosing
- Effort
- An afternoon per surface
- MCP spec
- Outside the spec's OAuth flow, which the spec makes optional
- Works with
- Supported by every agent SDK; hosted connectors are more limited
- Effort
- An afternoon
- MCP spec
- Platform request signing rather than MCP authorization; the spec does not cover it
- Works with
- Your own agents. No MCP client signs SigV4; AWS ships a local proxy that does
- Effort
- An afternoon with AWS's transport, a day with the IAM policies
- MCP spec
- An official MCP extension, still Draft — shipped in the TypeScript and Python SDKs
- Works with
- Your own agents — the extension support matrix lists no client that implements it
- Effort
- An afternoon, if your IdP already issues client-credentials tokens
- MCP spec
- Platform identity rather than MCP authorization; the spec does not cover it
- Works with
- Your own workloads only; no MCP client can mint these tokens
- Effort
- A day, mostly IAM bindings
- MCP spec
- Out of scope of the spec's OAuth flow, which the spec makes optional
- Works with
- Your own workloads, plus Microsoft Foundry Agent Service; no chat or desktop client can mint one
- Effort
- A day, mostly the app registration and role assignments
- MCP spec
- Transport-layer authentication, outside the spec's OAuth profile — which the spec makes OPTIONAL
- Works with
- Your own agents only; no chat or desktop client config exposes a certificate field
- Effort
- An afternoon in code, longer if the PKI does not exist yet
- MCP spec
- The spec's own flow — though the spec still makes authorization OPTIONAL
- Works with
- Claude, ChatGPT, Cursor and VS Code run it themselves; among agent SDKs, only those shipping an OAuth provider
- Effort
- Days — most of it is the authorization server, not MCP
| Method | MCP spec | Works with | Effort |
|---|---|---|---|
| Anthropic-hosted agents Claude Managed Agents | Mixed — claude.ai and Claude Code run the spec's OAuth flow; a vault token or a static header sits outside it, not against it | Four surfaces, four credential fields; only Claude Code, which runs locally, sends a header name of your choosing | An afternoon per surface |
| API key Any platform | Outside the spec's OAuth flow, which the spec makes optional | Supported by every agent SDK; hosted connectors are more limited | An afternoon |
| AWS IAM (SigV4) AWS | Platform request signing rather than MCP authorization; the spec does not cover it | Your own agents. No MCP client signs SigV4; AWS ships a local proxy that does | An afternoon with AWS's transport, a day with the IAM policies |
| Client credentials (M2M) Any platform | An official MCP extension, still Draft — shipped in the TypeScript and Python SDKs | Your own agents — the extension support matrix lists no client that implements it | An afternoon, if your IdP already issues client-credentials tokens |
| Google Cloud IAM Google Cloud | Platform identity rather than MCP authorization; the spec does not cover it | Your own workloads only; no MCP client can mint these tokens | A day, mostly IAM bindings |
| Microsoft Entra ID Azure | Out of scope of the spec's OAuth flow, which the spec makes optional | Your own workloads, plus Microsoft Foundry Agent Service; no chat or desktop client can mint one | A day, mostly the app registration and role assignments |
| mTLS Any platform | Transport-layer authentication, outside the spec's OAuth profile — which the spec makes OPTIONAL | Your own agents only; no chat or desktop client config exposes a certificate field | An afternoon in code, longer if the PKI does not exist yet |
| OAuth 2.1 Any platform | The spec's own flow — though the spec still makes authorization OPTIONAL | Claude, ChatGPT, Cursor and VS Code run it themselves; among agent SDKs, only those shipping an OAuth provider | Days — most of it is the authorization server, not MCP |
What your identity provider supports
The spec's OAuth flow needs dynamic client registration (RFC 7591) and resource indicators (RFC 8707). Support is patchy, so check yours before you commit to it.
Microsoft Entra ID
- Registration
- No
- Resource indicators
- No
No dynamic client registration, and no RFC 8414 metadata endpoint. Ask for an audience with scope={resource}/.default rather than the resource parameter.
Amazon Cognito
- Registration
- No
- Resource indicators
- No
Cognito's own docs state it does not support dynamic client registration. Pre-register the client.
SourceKeycloak
- Registration
- Yes
- Resource indicators
- No
Keycloak documents that it "cannot recognize the resource parameter". Use the scope parameter plus an audience mapper on a client scope.
Auth0
- Registration
- Yes
- Resource indicators
- Partial
Dynamic registration must be enabled per tenant. Audience is set with the audience parameter rather than resource.
Ory Hydra
- Registration
- Yes
- Resource indicators
- No
Dynamic registration is off by default. Its registration response includes empty client_uri and logo_uri fields, which some clients reject during validation.
Zitadel
- Registration
- No
- Resource indicators
- No
Dynamic client registration is not implemented; the tracking issue has been open since April 2025.
Sourceauthentik
- Registration
- No
- Resource indicators
- No
Not shipped. An implementation for the enterprise OAuth2 provider is in review.
Source| Provider | Registration | Resource indicators | Notes |
|---|---|---|---|
| Microsoft Entra ID Source | No | No | No dynamic client registration, and no RFC 8414 metadata endpoint. Ask for an audience with scope={resource}/.default rather than the resource parameter. |
| Amazon Cognito Source | No | No | Cognito's own docs state it does not support dynamic client registration. Pre-register the client. |
| Keycloak Source | Yes | No | Keycloak documents that it "cannot recognize the resource parameter". Use the scope parameter plus an audience mapper on a client scope. |
| Auth0 Source | Yes | Partial | Dynamic registration must be enabled per tenant. Audience is set with the audience parameter rather than resource. |
| Ory Hydra Source | Yes | No | Dynamic registration is off by default. Its registration response includes empty client_uri and logo_uri fields, which some clients reject during validation. |
| Zitadel Source | No | No | Dynamic client registration is not implemented; the tracking issue has been open since April 2025. |
| authentik Source | No | No | Not shipped. An implementation for the enterprise OAuth2 provider is in review. |
Provider support last verified July 24, 2026
When a connection fails
Find and fix the errors MCP clients report when authentication fails.
- Authorization with the MCP server failed A token was issued and the client says it is connected, but the credential never reaches your server intact: either the client does not attach it, or it carries an audience your server is right to reject.
- Couldn't reach the MCP server One toast covers four different failures. Claude Code and `curl` reach your server from your machine; a claude.ai connector reaches it from Anthropic's infrastructure over the public internet, so the request dies at whichever hop only exists on that path — DNS, your edge, a redirect, or OAuth discovery.
- Dynamic Client Registration not supported The MCP client reached your authorization server's metadata and found no way to obtain a client ID — no registration endpoint, no Client ID Metadata Document support, and none configured in the client — so it stopped before the OAuth flow began.
- Failed to fetch authorization server metadata from all attempted URLs The client knows which authorization server it is supposed to use, but none of the well-known URLs it is required to try returned a metadata document — so it never learns where `/authorize` and `/token` are, and the flow stops one step short of a login.
- MCP headers config ignored when server has OAuth discovery The credential is sitting in your client config and never reaches the wire: the client found OAuth discovery metadata on your server first, and switched to the OAuth flow instead of sending the header you set.
- mcp_authentication_failed_error A managed agent's connection to your MCP server failed on authentication: the server rejected the credential from the attached vault, required authentication when no matching credential was configured, or an `mcp_oauth` token refresh failed.
- resource indicator is missing, or unknown The client reached your authorization server and asked for a token bound to your MCP server, and the authorization server refused the binding: the `resource` value it was handed names something it does not recognise, or the client sent no `resource` at all and this server requires one. No token is minted, so nothing ever arrives at your MCP server to be accepted or rejected.
- Resource server does not implement OAuth 2.0 Protected Resource Metadata. Your server answers 401 correctly, but the response carries no pointer to protected resource metadata — so the client has nothing to discover the authorization server with, and the OAuth flow never starts.
- The request signature we calculated does not match the signature you provided AWS recomputed the signature over the request it actually received, got a different value from the one you sent, and answered `403` before your MCP server ran — so nothing about your tools, your handler or your IAM policy is implicated yet.
- Token audience validation failed The token is real — right issuer, good signature, not expired — but the `aud` claim names something other than this MCP server, so the server is required to reject it, and does.
- Your MCP server returns a login page or 403 instead of 401 Something in front of your MCP server is enforcing authentication built for humans in browsers. Instead of the 401 an MCP client needs, it returns a login page, a redirect to one, or a bare 403 — so the client never learns how to authenticate.
One policy engine for APIs, AI, and MCP
Put your MCP servers behind a gateway that handles the OAuth details providers leave out, filters tools per role, and logs every call.