Repository navigation
feat(server): add protected resource metadata discovery scenario - #572
JosephDoUrden wants to merge 1 commit into
Conversation
Add auth/protected-resource-metadata, a server scenario that probes an MCP endpoint without a token and grades the Protected Resource Metadata it publishes (RFC 9728): the 401 challenge, discovery via resource_metadata or the well-known URIs, the 200 application/json response, the resource field and a non-empty authorization_servers list, plus a SHOULD-level scope check that is SKIPPED at 2025-06-18, where the spec says nothing about scope in the challenge. A 2xx to the unauthenticated probe reports every check untestable. The fetch and parse logic lives in a standalone helper under src/scenarios/server/auth/prm.ts so other server auth scenarios can reuse it. Every request is bounded to 10 s, headers and body, so a server that holds a stream open fails with a reason naming the timeout instead of hanging the suite. Discovery candidates are fetched once per URL, and the challenge parser survives a token68 challenge and an unquoted resource_metadata value. The scenario starts in the pending suite because the everything-server does not run with authorization enabled; an auth-enabled fixture with one break mode per check backs the vitest cases. Closes modelcontextprotocol#565
commit: |
|
One RFC 9728 requirement the scenario doesn't cover yet: §3.2 says "Parameters with zero values MUST be omitted from the response." The checks already enforce other parts of §3.2 (200, This came up in the C# SDK: modelcontextprotocol/csharp-sdk#1999 fixes the SDK's protected resource metadata emitting One exception to keep in mind: A few questions, since I'm not sure where this fits best:
If it does go in, an 🤖 Generated with Claude Code |
closes #565
adds
auth/protected-resource-metadata, a server scenario. the harness plays a client with no token. one unauthenticated post, then metadata discovery in client order:resource_metadatafrom the challenge, the path well-known url, the root one.six checks come out of that.
server-auth-401: 401 plus aWWW-Authenticateheader.server-prm-discovery: the metadata can be found; at 2025-06-18 the challenge url is required, from 2025-11-25 a well-known document on its own is fine.server-prm-response: 200,application/json, json object body (rfc 9728 §3.2).server-prm-resource:resourceidentifies the server (§3.3); at the root well-known url the origin is accepted too, same as the ts sdk.server-prm-authorization-servers: non-empty array of urls.server-www-authenticate-scope:scopein the challenge, warning only; skipped at 2025-06-18, the should first appears in 2025-11-25.a 2xx to the probe means the server has no auth. every check then reports "not testable" per #248, never skipped.
every request is bounded to 10 s, headers and body. a server that holds the sse stream open, or never ends the json body, fails the checks it blocks with a reason naming the timeout. the suite does not hang. two tests cover it with a mock server.
the fetch and parse logic sits in
src/scenarios/server/auth/prm.ts. the challenge parser handles multiple challenges, quoted pairs, a token68 challenge before the bearer one, and an unquotedresource_metadatavalue, which shows up in the wild. a url named by two sources is fetched once.fixture:
examples/servers/typescript/auth-prm-server.ts, port 3013.PRM_BREAKpicks one defect per check. unknown values exit with the allowed list. readme has the table. 25 vitest cases.against the real ts sdk (
typescript-sdk/examples/oauth/server.ts): 5/5 plus the scope warning at 2025-11-25 and 2026-07-28, scope skipped at 2025-06-18. note--urlmust match the server's own resource exactly.localhostagainst a server that names127.0.0.1failsserver-prm-resource, which is what rfc 9728 §3.3 asks for.starts in the pending suite. the everything-servers do not run with auth on. targeted run:
npm start -- server --scenario auth/protected-resource-metadata --url http://localhost:3013/mcpon the dpop stack (#395, #527, #528, #529): those branches also add
src/scenarios/server/auth/spec-references.ts, so there is an add/add conflict on that one file. the three rfc 9728 entries are identical. happy to rebase whichever lands second. the helper here was written in parallel, not extracted from #529.spec refs point at the dated 2025-11-25 pages, each anchor checked.