Skip to content

fix(auth): strip trailing slashes from OAuth metadata URLs#3013

Open
piyushbag wants to merge 4 commits into
modelcontextprotocol:mainfrom
piyushbag:fix-1919-oauth-metadata-trailing-slash
Open

fix(auth): strip trailing slashes from OAuth metadata URLs#3013
piyushbag wants to merge 4 commits into
modelcontextprotocol:mainfrom
piyushbag:fix-1919-oauth-metadata-trailing-slash

Conversation

@piyushbag

Copy link
Copy Markdown

Summary

  • strip trailing slashes from issuer in build_metadata() before constructing OAuthMetadata
  • strip trailing slashes from resource and each authorization_servers entry in create_protected_resource_routes()
  • pass canonical URL strings so metadata models with url_preserve_empty_path serialize without a synthetic root slash
  • add regression tests for AnyHttpUrl issuer inputs and root-level protected-resource metadata responses

Fixes #1919. Also fixes #1265.

Test plan

  • uv run pytest tests/server/auth/test_routes.py -q
  • uv run pytest tests/server/auth/test_protected_resource.py -q
  • uv run pytest tests/server/auth/ -q
  • uv run ruff check src/mcp/server/auth/routes.py tests/server/auth/test_routes.py tests/server/auth/test_protected_resource.py
  • uv run pyright src/mcp/server/auth/routes.py

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/mcp/server/auth/routes.py">

<violation number="1" location="src/mcp/server/auth/routes.py:172">
P2: Issuer canonicalization is over-broad: `rstrip('/')` also rewrites non-root path issuers (e.g. `/tenant/` -> `/tenant`), which can break exact issuer identifier matching.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread src/mcp/server/auth/routes.py
# Create metadata
metadata = OAuthMetadata(
issuer=issuer_url,
issuer=cast(AnyHttpUrl, str(issuer_url).rstrip("/")),

@cubic-dev-ai cubic-dev-ai Bot Jun 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Issuer canonicalization is over-broad: rstrip('/') also rewrites non-root path issuers (e.g. /tenant/ -> /tenant), which can break exact issuer identifier matching.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/mcp/server/auth/routes.py, line 172:

<comment>Issuer canonicalization is over-broad: `rstrip('/')` also rewrites non-root path issuers (e.g. `/tenant/` -> `/tenant`), which can break exact issuer identifier matching.</comment>

<file context>
@@ -169,7 +169,7 @@ def build_metadata(
     # Create metadata
     metadata = OAuthMetadata(
-        issuer=issuer_url,
+        issuer=cast(AnyHttpUrl, str(issuer_url).rstrip("/")),
         authorization_endpoint=authorization_url,
         token_endpoint=token_url,
</file context>
Fix with cubic

Pydantic AnyHttpUrl adds a trailing slash to bare hostnames, which breaks
RFC 8414/9728 exact issuer and resource comparison during OAuth discovery.
Pass canonical URL strings into metadata models so served wire JSON omits
the synthetic root slash. Fixes modelcontextprotocol#1919 and modelcontextprotocol#1265.
Update docs, stories, and tests that asserted trailing slashes on root
issuer and authorization_server metadata fields after the routes.py fix.
Remove the Pydantic AnyUrl xfail now that routes.strip trailing slashes,
and assert issuer against the canonical form.
@piyushbag piyushbag force-pushed the fix-1919-oauth-metadata-trailing-slash branch from a591757 to c65b029 Compare July 11, 2026 11:48
@piyushbag

Copy link
Copy Markdown
Author

Rebased onto current main. Updated client test_build_metadata expectations now that issuers are served without a synthetic trailing slash, and removed the obsolete Pydantic AnyUrl xfail.

Local: targeted auth route and metadata tests pass.

build_metadata now serves path-less issuers without a trailing slash.
Update authorize iss defaults, PRM/ASM overrides, and assertions so RFC
9207 string compares match the canonical metadata form.
@piyushbag

Copy link
Copy Markdown
Author

CI failures were issuer string mismatches after the metadata slash strip (iss / PRM authorization_servers still carried a trailing slash).

Aligned the interaction auth fixtures and assertions with the canonical slash-free issuer. Local: full tests/interaction/auth/ plus route/metadata unit tests pass (87).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant