MCP HTTP OAuth: Client doesn't read resource_metadata from WWW-Authenticate header (RFC 9728)

Resolved 💬 3 comments Opened May 13, 2026 by Isak-keycard Closed May 17, 2026

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

  1. Run an MCP server with OAuth on a sub-path:

``python
mcp.run(transport="streamable-http", path="/mcp")
``

  1. Add the server:

``bash
claude mcp add --transport http my-server http://localhost:8888/mcp
``

  1. Restart Claude Code
  2. 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_metadata URL in WWW-Authenticate header (MS365, different root cause)
  • #45288 — Feature request to trigger OAuth immediately after plugin install

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗