Skip to content

fix: include transport path in protected resource metadata URL (RFC 9728 §3)#2670

Open
afischh wants to merge 1 commit into
modelcontextprotocol:mainfrom
afischh:fix/1264-clean
Open

fix: include transport path in protected resource metadata URL (RFC 9728 §3)#2670
afischh wants to merge 1 commit into
modelcontextprotocol:mainfrom
afischh:fix/1264-clean

Conversation

@afischh
Copy link
Copy Markdown

@afischh afischh commented May 23, 2026

Problem

Fixes #1264.

The resource field in /.well-known/oauth-protected-resource is supposed to identify the protected resource server as defined in RFC 9728 §2:

The resource parameter [...] MUST be a URI that identifies the protected resource.

When a server mounts its MCP transport at a path (e.g. /mcp or /sse), the resource field must include that path — https://example.com/mcp, not just https://example.com.

Before this fix, both StreamableHTTPServerTransport and the SSE server always used the bare resource_server_url (without the transport path), so the metadata URL was derived incorrectly.

Root cause

build_protected_resource_metadata_url strips the path from the resource URL and reconstructs /.well-known/oauth-protected-resource/<path>. When the resource URL lacks the transport path, the well-known URL is wrong and RFC 9728 §3 lookup fails for path-mounted servers.

Fix

In both lowlevel/server.py (StreamableHTTP) and mcpserver/server.py (SSE), compute actual_resource_url by appending the transport path to resource_server_url before passing it to build_protected_resource_metadata_url and embedding it in the metadata:

actual_resource_url = AnyHttpUrl(
    str(auth.resource_server_url).rstrip("/") + streamable_http_path
)

Tests

  • test_resource_url_includes_transport_path — parametrized unit test: verifies correct resource value and correct metadata URL for /mcp, trailing-slash base URL + /mcp, and /sse transport paths
  • test_protected_resource_metadata_contains_transport_path — async integration test using a real TestClient

…728)

Per RFC 9728 §3, the `resource` field in `/.well-known/oauth-protected-resource`
must identify the actual protected endpoint URL — e.g. `http://localhost:8000/mcp`,
not the bare server base `http://localhost:8000/`. Without the path, VS Code Copilot
and other spec-compliant clients reject the server with:

  Protected Resource Metadata resource "http://localhost:8000/" does not match
  MCP server resolved resource "http://localhost:8000/mcp"

Fix: append `streamable_http_path` / `sse_path` to `resource_server_url` before
passing it to `create_protected_resource_routes` and `build_resource_metadata_url`
in both the lowlevel and mcpserver transports.

Fixes modelcontextprotocol#1264

Signed-off-by: Alex Fisch <afischh@gmail.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Protected Resource Metadata resource erroneous when setting up authentication on server

1 participant