-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Description
Initial Checks
- I confirm that I'm using the latest version of MCP Python SDK
- I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Description
Environment
- Client: VS Code MCP (http)
- Server: examples/servers/simple-auth
- Config:
{ "servers": { "Get-Time": { "url": "http://localhost:8001/mcp", "type": "http" } } }
Problem 1: Protected Resource Metadata “resource” mismatch
On startup, VS Code shows:
Connection state: Error Error sending message to http://localhost:8001/mcp: Error: Protected Resource Metadata resource "http://localhost:8001/" does not match MCP server resolved resource "http://localhost:8001/mcp". The MCP server must follow OAuth spec https://datatracker.ietf.org/doc/html/rfc9728#PRConfigurationValidation
Per RFC 9728 validation, the example should set the resource identifier to include the path component:
- Expected in example:
resource_server_url=AnyHttpUrl(f"{settings.server_url}mcp"), # <--- add mcp
- File:
python-sdk/examples/servers/simple-auth/mcp_simple_auth/server.py
Lines 71 to 84 in 71889d7
app = FastMCP( name="MCP Resource Server", instructions="Resource Server that validates tokens via Authorization Server introspection", host=settings.host, port=settings.port, debug=True, # Auth configuration for RS mode token_verifier=token_verifier, auth=AuthSettings( issuer_url=settings.auth_server_url, required_scopes=[settings.mcp_scope], resource_server_url=settings.server_url, ), )
When I include /mcp
in resource_server_url
, this error goes away.
Problem 2: WWW-Authenticate resource_metadata vs actual route
The server builds resource_metadata_url
for the WWW-Authenticate
header and also registers the well-known route. These two do not match when the resource identifier has a path.
-
Header construction (resource_metadata_url):
- Code:
python-sdk/src/mcp/server/fastmcp/server.py
Lines 942 to 951 in 71889d7
resource_metadata_url = AnyHttpUrl( str(self.settings.auth.resource_server_url).rstrip("/") + "/.well-known/oauth-protected-resource" ) routes.append( Route( self.settings.streamable_http_path, endpoint=RequireAuthMiddleware(streamable_http_app, required_scopes, resource_metadata_url), ) ) - Behavior:
- resource_server_url = http://localhost:8001 → resource_metadata_url = http://localhost:8001/.well-known/oauth-protected-resource
- resource_server_url = http://localhost:8001/mcp → resource_metadata_url = http://localhost:8001/mcp/.well-known/oauth-protected-resource
- Code:
-
Actual route registration (well-known endpoint):
- Code:
python-sdk/src/mcp/server/fastmcp/server.py
Lines 961 to 981 in 71889d7
# Add protected resource metadata endpoint if configured as RS if self.settings.auth and self.settings.auth.resource_server_url: from mcp.server.auth.handlers.metadata import ProtectedResourceMetadataHandler from mcp.server.auth.routes import cors_middleware from mcp.shared.auth import ProtectedResourceMetadata protected_resource_metadata = ProtectedResourceMetadata( resource=self.settings.auth.resource_server_url, authorization_servers=[self.settings.auth.issuer_url], scopes_supported=self.settings.auth.required_scopes, ) routes.append( Route( "/.well-known/oauth-protected-resource", endpoint=cors_middleware( ProtectedResourceMetadataHandler(protected_resource_metadata).handle, ["GET", "OPTIONS"], ), methods=["GET", "OPTIONS"], ) ) - Behavior:
- resource_server_url = http://localhost:8001 → endpoint = http://localhost:8001/.well-known/oauth-protected-resource (matches)
- resource_server_url = http://localhost:8001/mcp → endpoint = http://localhost:8001/.well-known/oauth-protected-resource (mismatch)
- Code:
Result: the client follows the WWW-Authenticate
header to http://localhost:8001/mcp/.well-known/oauth-protected-resource
, but the server only serves the route at the root.
Problem 3: Well-known URL construction (RFC 9728 §3.1)
RFC 9728 §3.1 states that when the resource identifier contains a path, the well-known suffix must be inserted between the host and the path:
If the resource identifier value contains a path or query component, any terminating slash (/) following the host component MUST be removed before inserting /.well-known/ and the well-known URI path suffix between the host component and the path and/or query components.
- If resource = http://localhost:8001 → well-known = http://localhost:8001/.well-known/oauth-protected-resource
- If resource = http://localhost:8001/mcp → well-known = http://localhost:8001/.well-known/oauth-protected-resource/mcp
Current implementation:
- Header builds: http://localhost:8001/mcp/.well-known/oauth-protected-resource (appends after the path)
- Route registers only: http://localhost:8001/.well-known/oauth-protected-resource (at the root)
- Neither matches the RFC-required path-aware form.
Expected behavior (RFC 9728)
RFC 9728 §3.1: insert /.well-known/oauth-protected-resource
between the host and the resource path.
-
If
resource_server_url = http://localhost:8001
(no path):- Header should point to:
http://localhost:8001/.well-known/oauth-protected-resource
- Route should be served at:
http://localhost:8001/.well-known/oauth-protected-resource
- Header should point to:
-
If
resource_server_url = http://localhost:8001/mcp
(has path/mcp
):- Header should point to:
http://localhost:8001/.well-known/oauth-protected-resource/mcp
- Route should be served at:
http://localhost:8001/.well-known/oauth-protected-resource/mcp
- Header should point to:
Example Code
Python & MCP Python SDK
Python 3.11
MCP Python SDK v1.15.0