MCP HTTP OAuth: Client doesn't read resource_metadata from WWW-Authenticate header (RFC 9728)
Summary
When connecting to an HTTP MCP server that requires OAuth and is mounted at a sub-path (e.g., /mcp), Claude Code fails to trigger the OAuth browser flow. The server correctly returns a 401 with:
WWW-Authenticate: Bearer error="invalid_token", resource_metadata="http://localhost:8888/.well-known/oauth-protected-resource/mcp"
Per RFC 9728 §5.1, the resource_metadata parameter in the WWW-Authenticate header tells the client exactly where to find the protected resource metadata. This metadata in turn contains the authorization_servers list needed to start the OAuth flow.
Claude Code appears to ignore the resource_metadata parameter and instead checks /.well-known/oauth-protected-resource (without the path suffix), which returns 404. It then gives up with "Needs authentication" without opening a browser.
Expected behavior
Claude Code should parse the resource_metadata URL from the WWW-Authenticate header and fetch that URL directly, as specified in RFC 9728 §5.1. This is the primary discovery mechanism defined by the spec for finding the protected resource metadata when the resource is at a sub-path.
Steps to reproduce
- Run an MCP server with OAuth on a sub-path:
``python``
mcp.run(transport="streamable-http", path="/mcp")
- Add the server:
``bash``
claude mcp add --transport http my-server http://localhost:8888/mcp
- Restart Claude Code
- Observe: Shows "Needs authentication", browser never opens
Evidence
# Server returns 401 with resource_metadata in WWW-Authenticate header
curl -sv http://localhost:8888/mcp 2>&1 | grep www-authenticate
# www-authenticate: Bearer ... resource_metadata="http://localhost:8888/.well-known/oauth-protected-resource/mcp"
# The path-suffixed URL (from the header) works correctly:
curl -s http://localhost:8888/.well-known/oauth-protected-resource/mcp
# {"resource":"http://localhost:8888/mcp","authorization_servers":["https://...keycard.cloud/"],...}
# But the base path (which Claude Code appears to check) returns 404:
curl -s http://localhost:8888/.well-known/oauth-protected-resource
# Not Found
Environment
- Claude Code version: 2.1.62
- MCP Python SDK: 1.27.1
- FastMCP: 3.2.4
- OS: macOS
- Server: Uses Keycard (
keycardai-fastmcp) for OAuth with Okta as upstream IdP
Workaround
MCP SDK and FastMCP PRs are being submitted to also serve the protected resource metadata at the base well-known path (/.well-known/oauth-protected-resource) as a compatibility fallback, so that clients that don't parse the resource_metadata header parameter can still discover the metadata.
Related issues
- #44830 — RFC 9728 discovery poisoning / cached needs-auth state (different bug, same area)
- #46539 — Relative
resource_metadataURL in WWW-Authenticate header (MS365, different root cause) - #45288 — Feature request to trigger OAuth immediately after plugin install
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗