Repository navigation
[v2] RFC 9728 PRM URLs and resource matching drop query components #3065
Description
Activity
I can reproduce this. All three spots build from
urlparse(...).pathand never touch.query:build_resource_metadata_urlin server/auth/routes.py, the path-based branch ofbuild_protected_resource_metadata_discovery_urlsin client/auth/utils.py, andcheck_resource_allowedin shared/auth_utils.py, which only compares scheme, netloc and path before the hierarchical prefix check that_validate_resource_matchrelies on.Worth noting
resource_url_from_server_urlalready keeps the query (it strips only the fragment), so by the time the mismatched PRM reachescheck_resource_allowedthe query is present on both sides and simply ignored. That makes the comparison the localized gap.One design question for whoever picks this up: the path match is intentionally hierarchical (a parent-path token works for child paths), but a query component isn't hierarchical, so I don't think you can fold it into the same
startswithcompare. Do we want exact query equality as an added condition, matching the simple-string comparison used elsewhere in this module for issuer checks?- addedbugSomething isn't workingSomething isn't workingP2Moderate issues affecting some users, edge cases, potentially valuable featureModerate issues affecting some users, edge cases, potentially valuable featureneeds confirmationNeeds confirmation that the PR is actually required or needed.Needs confirmation that the PR is actually required or needed.authIssues and PRs related to Authentication / OAuthIssues and PRs related to Authentication / OAuthv1Affects the v1.x maintenance lineAffects the v1.x maintenance line
on Aug 14, 2026 Related seam we keep hitting: clients that canonicalize/compare PRM
resourcewithout preserving query components fail against servers that put tenant/session state in the query, even when Inspector’s happy path looked fine. Same family as trailing-slash / path mismatch — Inspector-green, shipping-client-red.We’re collecting those client/server PRM identity mismatches in a compatibility scorecard if useful: https://compatlab-waitlist.vercel.app (permission-only).
What happened?
While testing the v2 auth/protected-resource-metadata path, I noticed query-bearing resource identifiers are treated as if the query is not part of the resource.
For a resource server URL like:
three SDK paths currently drop or ignore
?tenant=a:mcp.server.auth.routes.build_resource_metadata_url()returns:mcp.client.auth.utils.build_protected_resource_metadata_discovery_urls(None, resource)also tries:mcp.shared.auth_utils.check_resource_allowed()treats different query components as matching, so the client accepts protected resource metadata for?tenant=bwhen the server URL was?tenant=a.This is latent for path-only deployments, but it matters for query-routed or multi-tenant resource identifiers.
What did you expect?
RFC 9728 derives the protected-resource metadata URL by inserting
/.well-known/oauth-protected-resourcebefore the protected resource path and/or query. If the resource identifier includes a query component, the derived metadata URL and resource validation should not silently collapse it with a different query.For
https://api.example.com/mcp?tenant=a, I expected the path-specific metadata URL to be:And a PRM document whose
resourceishttps://api.example.com/mcp?tenant=bshould not validate for a client configured withhttps://api.example.com/mcp?tenant=a.Code to reproduce
Current output:
SDK version
Current
mainbranch, v2 development line.Area
Auth
AI-assisted (Claude/Codex) for navigation and review; change authored and understood by me.