Skip to content

Commit 6c0ebb5

Browse files
docs(auth): clarify that resource_server_url must include the transport path
The `resource_server_url` setting must equal the full public URL of the MCP endpoint, including the transport path, so that the value advertised in RFC 9728 Protected Resource Metadata matches the URL the client used to reach the server. RFC 9728 §3.3 requires strict equality. Update the `AuthSettings.resource_server_url` docstring to spell this out, fix the `examples/snippets/servers/oauth_server.py` snippet so it ends in `/mcp`, and select `/mcp` or `/sse` in the `simple-auth` example based on the chosen transport instead of hardcoding `/mcp` regardless of the `--transport=sse` flag. Refs #1264
1 parent e8e6484 commit 6c0ebb5

4 files changed

Lines changed: 26 additions & 8 deletions

File tree

README.v2.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,10 +1021,14 @@ mcp = MCPServer(
10211021
"Weather Service",
10221022
# Token verifier for authentication
10231023
token_verifier=SimpleTokenVerifier(),
1024-
# Auth settings for RFC 9728 Protected Resource Metadata
1024+
# Auth settings for RFC 9728 Protected Resource Metadata.
1025+
# `resource_server_url` MUST be the full public URL of the MCP endpoint, including
1026+
# the transport path (e.g. `/mcp` for streamable-http, `/sse` for sse). RFC 9728
1027+
# §3.3 requires strict equality between the client's resource identifier and the
1028+
# `resource` value advertised in the protected resource metadata.
10251029
auth=AuthSettings(
10261030
issuer_url=AnyHttpUrl("https://auth.example.com"), # Authorization Server URL
1027-
resource_server_url=AnyHttpUrl("http://localhost:3001"), # This server's URL
1031+
resource_server_url=AnyHttpUrl("http://localhost:3001/mcp"), # Public MCP endpoint URL
10281032
required_scopes=["user"],
10291033
),
10301034
)

examples/servers/simple-auth/mcp_simple_auth/server.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,12 @@ def main(port: int, auth_server: str, transport: Literal["sse", "streamable-http
126126
# Parse auth server URL
127127
auth_server_url = AnyHttpUrl(auth_server)
128128

129-
# Create settings
129+
# Create settings. server_url is the public URL of the MCP endpoint and must
130+
# include the transport path so it matches the URL the client used to reach
131+
# the server (RFC 9728 §3.3 strict equality).
130132
host = "localhost"
131-
server_url = f"http://{host}:{port}/mcp"
133+
transport_path = "/sse" if transport == "sse" else "/mcp"
134+
server_url = f"http://{host}:{port}{transport_path}"
132135
settings = ResourceServerSettings(
133136
host=host,
134137
port=port,

examples/snippets/servers/oauth_server.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@ async def verify_token(self, token: str) -> AccessToken | None:
2121
"Weather Service",
2222
# Token verifier for authentication
2323
token_verifier=SimpleTokenVerifier(),
24-
# Auth settings for RFC 9728 Protected Resource Metadata
24+
# Auth settings for RFC 9728 Protected Resource Metadata.
25+
# `resource_server_url` MUST be the full public URL of the MCP endpoint, including
26+
# the transport path (e.g. `/mcp` for streamable-http, `/sse` for sse). RFC 9728
27+
# §3.3 requires strict equality between the client's resource identifier and the
28+
# `resource` value advertised in the protected resource metadata.
2529
auth=AuthSettings(
2630
issuer_url=AnyHttpUrl("https://auth.example.com"), # Authorization Server URL
27-
resource_server_url=AnyHttpUrl("http://localhost:3001"), # This server's URL
31+
resource_server_url=AnyHttpUrl("http://localhost:3001/mcp"), # Public MCP endpoint URL
2832
required_scopes=["user"],
2933
),
3034
)

src/mcp/server/auth/settings.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ class AuthSettings(BaseModel):
2525
# Resource Server settings (when operating as RS only)
2626
resource_server_url: AnyHttpUrl | None = Field(
2727
...,
28-
description="The URL of the MCP server to be used as the resource identifier "
29-
"and base route to look up OAuth Protected Resource Metadata.",
28+
description=(
29+
"The full public URL of this MCP server, used as the resource identifier "
30+
"and base route to look up OAuth Protected Resource Metadata (RFC 9728). "
31+
"Must include the transport path (e.g. https://example.com/mcp for "
32+
"streamable-http, https://example.com/sse for sse) so that the value "
33+
"advertised in protected resource metadata exactly matches the URL the "
34+
"client used to reach the server. RFC 9728 §3.3 requires strict equality "
35+
"between the client's resource identifier and this value."
36+
),
3037
)

0 commit comments

Comments
 (0)