-
Notifications
You must be signed in to change notification settings - Fork 2.3k
fix: Always put the well-known endpoints at the server root #1288
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
seanhoughton
wants to merge
1
commit into
modelcontextprotocol:main
Choose a base branch
from
seanhoughton:fix/protected-resource-url
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
fix: Always put the well-known endpoints at the server root #1288
seanhoughton
wants to merge
1
commit into
modelcontextprotocol:main
from
seanhoughton:fix/protected-resource-url
+7
−3
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Bug Report: Incorrect `.well-known/oauth-protected-resource` endpoint path when `resource_server_url` ends with `/sse` ## Summary When configuring FastMCP with OAuth2 authentication and setting the `resource_server_url` to end with `/sse` (as required by VSCode MCP clients), FastMCP incorrectly serves the `.well-known/oauth-protected-resource` endpoint at `/sse/.well-known/oauth-protected-resource` instead of the expected root path `/.well-known/oauth-protected-resource`. ## Environment - **FastMCP version**: Part of `mcp` Python library (check with `pip show mcp`) - **Python version**: 3.13 - **Operating System**: macOS - **MCP Client**: VSCode with MCP extension - **Affected Files**: - `mcp/server/fastmcp/server.py` (lines ~790-797) - `mcp/server/auth/routes.py` (lines ~215-224) ## Expected Behavior 1. The `.well-known/oauth-protected-resource` endpoint should always be served at the root path (`/.well-known/oauth-protected-resource`) regardless of the `resource_server_url` configuration 2. The `resource` field in the `.well-known` response should point to the actual protected resource (e.g., `/sse`) 3. OAuth2 discovery should work correctly with MCP clients like VSCode ## Actual Behavior When `resource_server_url` is set to `http://localhost:8099/sse`, FastMCP: 1. Serves `.well-known/oauth-protected-resource` at `/sse/.well-known/oauth-protected-resource` 2. The SSE endpoint `/sse` returns a `www-authenticate` header with `resource_metadata="http://localhost:8099/sse/.well-known/oauth-protected-resource"` 3. OAuth2 discovery fails because clients expect the `.well-known` endpoint at the root ## Steps to Reproduce 1. Create a FastMCP server with OAuth2 configuration: ```python from mcp.server.fastmcp import FastMCP from mcp.server.auth.settings import AuthSettings from pydantic import AnyHttpUrl # Configure auth settings with /sse endpoint auth_settings = AuthSettings( issuer_url=AnyHttpUrl("https://login.microsoftonline.com/tenant-id/v2.0"), resource_server_url=AnyHttpUrl("http://localhost:8099/sse"), # Note: ends with /sse required_scopes=["https://example.com/scope"] ) mcp = FastMCP( "Test Server", token_verifier=your_token_verifier, auth=auth_settings, ) app = mcp.sse_app() ``` 2. Start the server: `uvicorn server:app --port 8099` 3. Test the endpoints: ```bash # This should work but returns 404 curl http://localhost:8099/.well-known/oauth-protected-resource # This works but shouldn't be the location curl http://localhost:8099/sse/.well-known/oauth-protected-resource # SSE endpoint references wrong .well-known location curl -I http://localhost:8099/sse # Returns: resource_metadata="http://localhost:8099/sse/.well-known/oauth-protected-resource" ``` ## Root Cause Analysis **Exact Location of Bug**: - **File**: `mcp/server/fastmcp/server.py` - **Lines**: ~790-797 in the `sse_app()` method - **Function**: `FastMCP.sse_app()` **The Issue**: When setting up OAuth2 authentication, FastMCP constructs the `resource_metadata_url` incorrectly: ```python # BUGGY CODE - Line ~790-797 in sse_app() method resource_metadata_url = AnyHttpUrl( str(self.settings.auth.resource_server_url).rstrip("/") + "/.well-known/oauth-protected-resource" ) ``` When `resource_server_url` is `http://localhost:8099/sse`, this creates `http://localhost:8099/sse/.well-known/oauth-protected-resource`. However, the actual `.well-known` endpoint is created by `create_protected_resource_routes()` (in `mcp/server/auth/routes.py` lines ~215-224), which always creates it at the root path: ```python # CORRECT CODE - This always creates /.well-known/oauth-protected-resource at root return [ Route( "/.well-known/oauth-protected-resource", endpoint=cors_middleware(handler.handle, ["GET", "OPTIONS"]), methods=["GET", "OPTIONS"], ) ] ``` **The Fix**: The `resource_metadata_url` should be constructed from the base URL, not the `resource_server_url`: ```python # PROPOSED FIX if self.settings.auth and self.settings.auth.resource_server_url: from pydantic import AnyHttpUrl from urllib.parse import urlparse # Extract base URL from resource_server_url parsed = urlparse(str(self.settings.auth.resource_server_url)) base_url = f"{parsed.scheme}://{parsed.netloc}" resource_metadata_url = AnyHttpUrl( base_url + "/.well-known/oauth-protected-resource" ) ``` ## Impact - **High**: Breaks OAuth2 discovery for MCP clients like VSCode - MCP servers cannot be properly authenticated when using the recommended `/sse` resource URL pattern - Workarounds require custom endpoint overrides, defeating the purpose of built-in auth support ## Proposed Solution The `.well-known/oauth-protected-resource` endpoint should always be served at the root path (`/.well-known/oauth-protected-resource`), regardless of the `resource_server_url` configuration. The `resource_server_url` should only affect: 1. The `resource` field value in the `.well-known` response 2. The `resource_metadata` reference in `www-authenticate` headers ## Current Workaround Override the built-in `.well-known` endpoint with a custom implementation: ```python async def custom_well_known_endpoint(request): return JSONResponse({ "resource": f"{config.EXTERNAL_ADDRESS}/sse", "authorization_servers": ["https://login.microsoftonline.com/tenant/v2.0"], "scopes_supported": ["https://example.com/scope"], "bearer_methods_supported": ["header"] }) # Override the built-in endpoint app.router.routes.insert(0, Route("/.well-known/oauth-protected-resource", custom_well_known_endpoint, methods=["GET"])) ``` ## Additional Context - This issue specifically affects integration with VSCode MCP clients, which require the resource URL to end with `/sse` - The OAuth2 specification (RFC 8414) defines `.well-known` endpoints should be at predictable root paths - Other OAuth2 implementations (e.g., Auth0, Okta) serve `.well-known` endpoints at root regardless of resource configuration ## Related Documentation - [RFC 8414 - OAuth 2.0 Authorization Server Metadata](https://tools.ietf.org/html/rfc8414) - [MCP OAuth2 Authentication Documentation](https://modelcontextprotocol.io/docs/concepts/authentication)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Bug Report: Incorrect
.well-known/oauth-protected-resource
endpoint path whenresource_server_url
ends with/sse
This is reported in 1264
Summary
When configuring FastMCP with OAuth2 authentication and setting the
resource_server_url
to end with/sse
(as required by VSCode MCP clients), FastMCP incorrectly serves the.well-known/oauth-protected-resource
endpoint at/sse/.well-known/oauth-protected-resource
instead of the expected root path/.well-known/oauth-protected-resource
.Environment
mcp
Python library (check withpip show mcp
)mcp/server/fastmcp/server.py
(lines ~790-797)mcp/server/auth/routes.py
(lines ~215-224)Expected Behavior
.well-known/oauth-protected-resource
endpoint should always be served at the root path (/.well-known/oauth-protected-resource
) regardless of theresource_server_url
configurationresource
field in the.well-known
response should point to the actual protected resource (e.g.,/sse
)Actual Behavior
When
resource_server_url
is set tohttp://localhost:8099/sse
, FastMCP:.well-known/oauth-protected-resource
at/sse/.well-known/oauth-protected-resource
/sse
returns awww-authenticate
header withresource_metadata="http://localhost:8099/sse/.well-known/oauth-protected-resource"
.well-known
endpoint at the rootSteps to Reproduce
Start the server:
uvicorn server:app --port 8099
Test the endpoints:
Root Cause Analysis
Exact Location of Bug:
mcp/server/fastmcp/server.py
sse_app()
methodFastMCP.sse_app()
The Issue: When setting up OAuth2 authentication, FastMCP constructs the
resource_metadata_url
incorrectly:When
resource_server_url
ishttp://localhost:8099/sse
, this createshttp://localhost:8099/sse/.well-known/oauth-protected-resource
.However, the actual
.well-known
endpoint is created bycreate_protected_resource_routes()
(inmcp/server/auth/routes.py
lines ~215-224), which always creates it at the root path:The Fix: The
resource_metadata_url
should be constructed from the base URL, not theresource_server_url
:Impact
/sse
resource URL patternProposed Solution
The
.well-known/oauth-protected-resource
endpoint should always be served at the root path (/.well-known/oauth-protected-resource
), regardless of theresource_server_url
configuration. Theresource_server_url
should only affect:resource
field value in the.well-known
responseresource_metadata
reference inwww-authenticate
headersCurrent Workaround
Override the built-in
.well-known
endpoint with a custom implementation:Additional Context
/sse
.well-known
endpoints should be at predictable root paths.well-known
endpoints at root regardless of resource configurationRelated Documentation