-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Description
discoverAuthorizationServerMetadata() in packages/client/src/client/auth.ts tries multiple well-known URLs in sequence (RFC 8414 path-insert, OIDC path-insert, OIDC path-appended), but throws immediately on 5xx responses instead of continuing to the next URL. Only 4xx responses trigger the fallback.
This breaks MCP servers deployed at a subpath behind a reverse proxy or API gateway (e.g., Azure Application Gateway, nginx, Traefik) when the proxy returns 502 for paths it does not route.
Reproduction
- Deploy an MCP server at a subpath, e.g.
https://example.com/my-app/mcp - The server's Protected Resource Metadata correctly advertises the authorization server as
https://example.com/my-app - The server correctly serves metadata at
https://example.com/my-app/.well-known/oauth-authorization-server(200 ✅) - The reverse proxy has no rule for the root-level
/.well-known/path, sohttps://example.com/.well-known/oauth-authorization-server/my-appreturns 502 (no backend)
The SDK tries URLs in this order:
https://example.com/.well-known/oauth-authorization-server/my-app→ 502 → ❌ throws immediatelyhttps://example.com/.well-known/openid-configuration/my-app→ never reachedhttps://example.com/my-app/.well-known/openid-configuration→ never reached (would have worked)
Expected Behavior
A 5xx from a reverse proxy for a non-existent well-known path is semantically equivalent to a 404 — the endpoint does not exist. The discovery loop should continue to the next fallback URL, not throw.
Root Cause
Two locations in auth.ts:
1. discoverAuthorizationServerMetadata() (~line 1017):
if (response.status >= 400 && response.status < 500) {
continue; // Try next URL — but ONLY for 4xx
}
throw new Error(\`HTTP \${response.status}...\`); // 5xx throws immediately2. shouldAttemptFallback() (~line 820):
function shouldAttemptFallback(response: Response | undefined, pathname: string): boolean {
}Both functions only treat 4xx as "try next". A 502/503/504 from a reverse proxy kills the entire discovery.
Suggested Fix
Change the condition to continue on any non-OK response:
// In discoverAuthorizationServerMetadata:
await response.text?.().catch(() => {});
continue; // Try next URL for any non-OK status
}// In shouldAttemptFallback:
function shouldAttemptFallback(response: Response | undefined, pathname: string): boolean {
}Environment
@modelcontextprotocol/sdkversion: 1.25.3 (bundled inmcp-remote0.1.38)- MCP server: ASP.NET Core with
ModelContextProtocol.AspNetCorev1.0.0, deployed on AKS behind Azure Application Gateway Ingress Controller (AGIC) - The server correctly implements RFC 9728 Protected Resource Metadata and serves OAuth metadata at the subpath, but cannot serve at the RFC 8414 path-insert URL because the reverse proxy does not route
/.well-known/*to the app
Related
geelen/mcp-remoteDoes not support web native/WinterCG Request/Response model #207 — same underlying issue, path-insert metadata URL is non-compliantgeelen/mcp-remoteAdd ways for server processes to self-terminate when host is gone #208 — open PR fixinggetMetadataUrlfor path componentsgeelen/mcp-remotestderr is not available until after start is called #174 — reports missing{baseUrl}/.well-known/oauth-authorization-serverfallback- MCP spec Authorization Server Metadata Discovery documents the 3-URL fallback order