Why does MCP TypeScript SDK drop the base path when resolving OAuth metadata?
In the modelcontextprotocol/typescript-sdk OAuth client, when protected resource metadata lists an authorization server with a base path (for example https://api.asgardeo.io/t/pavinduorg (opens external site) or a Keycloak realm URL), the SDK resolves the OAuth metadata URL to the origin root and loses that path. Why does this happen and is there a workaround until it is fixed?
Originally asked on GitHub: https://github.com/modelcontextprotocol/typescript-sdk/issues/545
4 answers
Status: the GitHub issue is closed as COMPLETED, although this thread does not state which SDK version contains the fix, so check the SDK releases after upgrading.
The cause is in src/client/auth.ts around line 272: the discovery URL is built with a relative path that starts with a leading slash (/.well-known/oauth-authorization-server). When resolved against the authorization server URL, that leading slash resets to the origin, so https://api.asgardeo.io/t/pavinduorg (opens external site) becomes https://api.asgardeo.io/.well-known/oauth-authorization-server (opens external site). A contributor said this matches the 2025-03-26 MCP spec, which requires .well-known at the domain root, but other commenters agreed it is still a real bug for the newer protected-resource-metadata flow because issuer path components are ignored.
The thread also corrected the expected URL format: per RFC 8414, the path component belongs after the well-known suffix, so the metadata URL should be https://api.asgardeo.io/.well-known/oauth-authorization-server/t/pavinduorg (opens external site), not https://api.asgardeo.io/t/pavinduorg/.well-known/oauth-authorization-server (opens external site).
Workarounds discussed in the thread:
- Host the authorization server metadata at a root-path-based issuer and point that endpoint at the content of the real authorization server metadata.
- A maintainer pointed out that OIDC-style metadata is spec compliant to host on a sub-path and is supported by the SDK, while OAuth metadata discovery is not. If your provider offers OIDC metadata, that may be worth trying.
No way to override buildDiscoveryUrls was found in the SDK code, so callers could not supply a custom discovery URL. Commenters reported hitting this with Keycloak, Okta custom authorization servers, and Azure Entra.
Did this work for you?What a GitHub user tried on GitHub (Didn't work)
Configured an authorization server URL with a base path (https://api.asgardeo.io/t/pavinduorg (opens external site)) in the MCP server's protected resource metadata. The SDK requested https://api.asgardeo.io/.well-known/oauth-authorization-server (opens external site) instead of a URL containing the tenant path.
Source: https://github.com/modelcontextprotocol/typescript-sdk/issues/545 (opens external site)
Did this work for you?What @allenzhou101 tried on GitHub (Outcome unclear)
As a workaround, hosted the Authorization Server Metadata at a root-path-based issuer and pointed that endpoint at the content of the real ASM.
Did this work for you?What @voidwisp tried on GitHub (Didn't work)
Looked for a way to override buildDiscoveryUrls in the SDK so a custom discovery URL could be supplied for providers like Okta, but found no such override in the code.
Did this work for you?
Related questions
- MCP TypeScript SDK StreamableHttp without sticky sessions in multi-replica deployments?
- MCP stdio server: SyntaxError Unexpected token in deserializeMessage when using console.log
- MCP typescript-sdk: tool inputSchema/outputSchema reject JSON Schema type 'array'?
- Use Zod union/intersection as MCP tool paramsSchema in typescript-sdk?
- MCP Inspector: "Invalid transport type: undefined" when connecting to an SSE server