This SDK preserves the resource identifier's query component in the derived Protected Resource Metadata URL, per RFC 9728 §3 — the well-known string is inserted "between the host component and the path and/or query components, if any". The shared conformance catalog pins that behaviour with explicit expected result shapes, at priority: high.
Not every implementation in the family has adopted it yet. At the time of writing, at least one still derives from origin + path and drops the query.
Why the divergence is not cosmetic
A client that derives the metadata URL for https://api.example.com/mcp?tenant=a the query-less way fetches …/.well-known/oauth-protected-resource/mcp, reaches the route (which is keyed on path), and receives a document whose resource member is https://api.example.com/mcp?tenant=a. RFC 9728 §3.3 requires a conformant client to discard a document whose resource does not match what it asked for. So discovery fails, silently, and only for query-bearing identifiers.
That is the multi-tenant case: two identifiers differing only by query must not collapse onto one document, which is the whole point of the catalog case.
Two details worth carrying into any port
- An empty query — a bare trailing
? — derives the query-less URL. RFC 3986 would permit reading it as present-but-empty; the family agrees on treating it as absent, and this is the one sub-case where that agreement already existed.
- Read the raw query, not the decoded one. The percent-encoding has to survive verbatim, for the same reason the raw authority does: the derived URL is spliced into
resource_metadata on a live 401, and re-encoding it changes the identifier the document is meant to name.
This issue exists so the deferral in the changelog points somewhere. The work is in the other implementations, not here.
This SDK preserves the resource identifier's query component in the derived Protected Resource Metadata URL, per RFC 9728 §3 — the well-known string is inserted "between the host component and the path and/or query components, if any". The shared conformance catalog pins that behaviour with explicit expected result shapes, at
priority: high.Not every implementation in the family has adopted it yet. At the time of writing, at least one still derives from origin + path and drops the query.
Why the divergence is not cosmetic
A client that derives the metadata URL for
https://api.example.com/mcp?tenant=athe query-less way fetches…/.well-known/oauth-protected-resource/mcp, reaches the route (which is keyed on path), and receives a document whoseresourcemember ishttps://api.example.com/mcp?tenant=a. RFC 9728 §3.3 requires a conformant client to discard a document whoseresourcedoes not match what it asked for. So discovery fails, silently, and only for query-bearing identifiers.That is the multi-tenant case: two identifiers differing only by query must not collapse onto one document, which is the whole point of the catalog case.
Two details worth carrying into any port
?— derives the query-less URL. RFC 3986 would permit reading it as present-but-empty; the family agrees on treating it as absent, and this is the one sub-case where that agreement already existed.resource_metadataon a live 401, and re-encoding it changes the identifier the document is meant to name.This issue exists so the deferral in the changelog points somewhere. The work is in the other implementations, not here.