On this page
Authorization server metadata won't load
Failed to fetch authorization server metadata from all attempted URLs The client knows which authorization server to use, but none of the well-known URLs it probes returned a metadata document, so it never learns where /authorize and /token are and the flow stops short of a login.
- MCP specification
- 2026-07-28
Is this you?
Take the issuer out of your own metadata document rather than from your configuration, then probe the URLs a client builds from it. That separates "the issuer string is wrong" from "the issuer is right and the document is somewhere no client looks" — two different fixes in two different places.
- This error
- Every line in step 2 shows a non-
200status. Also unhealthy, and easier to miss: a200whose content type istext/html, which is a login page or a WAF challenge rather than a document — clients count that as a failed attempt, and VS Code reports it with200in the error message because it requires the body to parse as authorization server metadata first. A415or403where a browser gets200means the provider is rejecting the request rather than the path, so compare the headers the client sends. And if step 3 prints a string that differs from step 1 by so much as a trailing slash, the document is reachable but unusable: RFC 8414 §3.3 requires theissuerto be identical to the identifier used to build the URL, and the specification says a client that finds otherwise MUST NOT use the metadata. - Then the fix is the next section.
- Working
- At least one of the three answers
200withapplication/json, and step 3 prints a string identical to step 1 — same scheme, same host, same path, same trailing slash or absence of one. The other two returning404is normal and not a problem: a provider is required to serve one of the two discovery mechanisms, not both at every URL shape. Which one answers does not matter, only that a conformant client would have asked for it. - Then this isn't your problem — try the error reference.
5 other strings clients print for this
- Error populating auth server metadata for <issuer>: AggregateError: Failed to fetch authorization server metadata from all attempted URLs
- Failed to fetch authorization server metadata from <url>: <status> <body>
- Failed to authenticate with MCP server '<name>': Failed to fetch authorization server metadata for client registration (attempted issuers: <urls>)
- HTTP <status> trying to load OpenID provider metadata from <url>
- failed to fetch authorization server metadata: failed to fetch metadata from any authorization server
Dotted spans such as <url> are placeholders — your client prints your own value there.
Fix it
Read the issuer your MCP server publishes in authorization_servers, then check that it answers one of the three well-known URLs a client probes: /.well-known/oauth-authorization-server with the issuer's path inserted, /.well-known/openid-configuration with the path inserted, and the path-appended <issuer>/.well-known/openid-configuration. One is enough — the 2026-07-28 specification requires an authorization server to serve either RFC 8414 or OpenID Connect Discovery, and requires clients to support both. That issuer string is oauthMetadata.issuer in the MCP TypeScript SDK, issuer_url on AuthSettings in the Python SDK, and authorization_servers on FastMCP's RemoteAuthProvider. If your provider serves the document at a URL no client constructs, forward it from your own origin instead: both reference SDKs already mount /.well-known/oauth-authorization-server, and FastMCP documents the forwarding route.
Have your agent fix this
Copy a prompt that sends your coding agent to this page to find and apply the right fix for your project.
The cause depends on your platform. Pick yours.
Why it happens
The client is not guessing. It takes the first entry of authorization_servers out of your protected resource metadata document and builds well-known URLs from it.
For an issuer whose URL carries a path, the 2026-07-28 specification requires three, in this order — RFC 8414 with the path inserted between origin and well-known suffix, OpenID Connect Discovery with the path inserted, then OpenID Connect Discovery with the suffix appended after the path. An issuer with no path has two. The specification says nothing about what a client does when all of them fail, so each improvises, which is why one cause surfaces as five different strings.
Three things put a client in that position. The issuer string is wrong — a placeholder, a container-internal hostname, a development localhost, or a trailing slash that changes every URL built from it.
Or the issuer is right and the document sits outside that ladder: Keycloak documents only the path-appended /realms/{realm-name}/.well-known/openid-configuration, and an Amazon Cognito user pool serves OpenID Connect discovery on cognito-idp.<region>.amazonaws.com, a different host from the rest of the pool's endpoints.
Or there was no protected resource document to read, so the client falls back to your MCP server's origin as the issuer and probes your server instead of your provider — the MCP Inspector collapses that fallback to the bare origin and drops the mount path with it.
A fourth case looks like a network problem and is not. A 200 that is not a metadata document counts as a failed attempt: an SSO interstitial, a WAF challenge page, or JSON with no issuer field. VS Code reports those with status 200, because it requires the body to parse as authorization server metadata before the URL counts as answered.
A provider that rejects the request rather than the path counts as a failed attempt too — one MCP Inspector report has an authorization server returning 415 Unsupported Media Type because the client sent Content-Type: application/json on the discovery GET.
The exchange
- MCP client
- MCP server resource server
- Authorization server issuer has a path
-
Everything ahead of this error worked. The 401 carried a resource_metadata pointer, the document was served, and it named an authorization server — so the client has an issuer and is not guessing.
MCP client to MCP server
GET …/oauth-protected-resource/mcp the URL the 401 challenge named -
MCP server to MCP client
200 OK authorization_servers: ["https://auth.example.com/tenant1"] -
Three well-known forms, in the order the 2026-07-28 specification requires for an issuer whose URL carries a path. Any one of them answering with a metadata document ends the ladder; a provider only has to serve one.
MCP client to Authorization server
GET /.well-known/oauth-authorization-server/tenant1 RFC 8414, path inserted -
Authorization server to MCP client
404 Not Found -
MCP client to Authorization server
GET /.well-known/openid-configuration/tenant1 OpenID Connect Discovery, path inserted -
Authorization server to MCP client
404 Not Found -
MCP client to Authorization server
GET /tenant1/.well-known/openid-configuration OpenID Connect Discovery, path appended -
Authorization server to MCP client
404 Not Found (this step fails) no form left to tryFlow stops here
Two documents and five well-known URLs stood between a client and a login screen; past them, the questions stop being about reaching anything.
-
Discovery works. How does a client we've never seen get a client_id?
- Client ID Metadata Documents
It brings its own: the client presents an HTTPS URL as its
client_idand the authorization server fetches the metadata from it — nothing to pre-register by hand before a first connection. -
Two routes need two different identity providers. Can one gateway do that?
-
mcp-oauth-inboundNot in one project — the runtime rejects a second MCP OAuth policy. Routes share the gateway's authorization server, and the provider behind it is a single configuration.
-
The user consented. What does the API behind the server receive?
-
mcp-token-exchange-inboundNot the client's token: the specification forbids passthrough with a MUST NOT. The inbound credential is dropped and an independent upstream one is minted per user or per gateway.
-
A token arrived and we rejected it. Where is that written down?
-
mcp_auth_downstream_token_validatedIn its own event: each validation of a client-presented token emits one, so the rejection resolves to a named outcome rather than to a
401in an access log.
All of these attach to one MCP route's policies.inbound — the same policy engine on the way in and on the way out. Your MCP server keeps
the code and the authentication it has today.
Stop debugging auth on every MCP server
A gateway in front of your MCP servers handles discovery, audience binding, and token validation once — for every server and every client.