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 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", which are different fixes in 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 spec 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 is not 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 the issuer 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 spec requires an authorization server to serve either RFC 8414 or OpenID Connect Discovery, and requires clients to support both. In the MCP TypeScript SDK that string is oauthMetadata.issuer, which buildOAuthProtectedResourceMetadata copies into authorization_servers verbatim; in the Python SDK it is issuer_url on AuthSettings; in FastMCP it is authorization_servers on RemoteAuthProvider. Where the provider serves the document at a URL no client will construct, serve or 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
Copies a prompt that sends your coding agent to this page, then has it find and apply the right fix for your project.
The cause depends on your platform. Pick yours.
Why it happens
Nothing here is guesswork on the client's part: 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 spec 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. For an issuer with no path there are two. The spec says nothing about what a client does when all of them fail, so each one improvises, and that is why the same 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 localhost left from development, or a trailing slash that changes every URL built from it. The issuer is right but the provider serves its document at a form outside the 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 at all, in which case the client falls back to the MCP server's own 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 in the same message, because it requires the body to parse as authorization server metadata before it counts the URL as answered. So does a provider that rejects the request rather than the path — 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
Each step in the exchange, in order.
- 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 spec 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 no form left to tryFlow stops here
What lands on you next
Two documents and five well-known URLs stood between a client and a login screen. Past them the questions are no longer about reaching anything.
-
Discovery works. How does a client we have never seen get a client_id?
- Client ID Metadata Documents
The client presents an HTTPS URL as its
client_idand the authorization server fetches the metadata from it, so no client has to be pre-registered by hand before it can connect. -
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 spec 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_validatedEach validation of a client-presented token emits its own event, so a 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.
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.