Repository navigation
Add server conformance for SEP-2350 scope challenges #480
Description
Goal
Add one language-neutral server conformance scenario for request-time OAuth insufficient_scope behavior from SEP-2350. The scenario should exercise every invocable MCP server primitive: tools/call, resources/read for both a static URI and a template-expanded URI, and prompts/get.
Portable fixture contract
Reuse the existing cross-SDK fixtures so implementations fail on observable wire behavior rather than on a missing test-only primitive:
| Operation | Fixture | Required scopes in one challenge |
|---|---|---|
| Tool | tools/call / test_simple_text |
mcp:conformance:tools:call, mcp:conformance:tools:test_simple_text |
| Static resource | resources/read / test://static-text |
mcp:conformance:resources:read, mcp:conformance:resources:static |
| Template resource | resources/read / test://template/123/data |
mcp:conformance:resources:read, mcp:conformance:resources:template:123 |
| Prompt | prompts/get / test_simple_prompt |
mcp:conformance:prompts:get, mcp:conformance:prompts:test_simple_prompt |
Servers should treat mcp-conformance-scope-low as a valid opaque token with only mcp:conformance:baseline, and mcp-conformance-scope-full as a valid opaque token with all required scopes. Unauthenticated behavior remains unchanged for existing scenarios.
For each operation, the low token should produce HTTP 403 and a Bearer WWW-Authenticate challenge containing error="insufficient_scope", the fixture protected-resource metadata URL, and both required scopes in one challenge. Retrying the same operation with the full token should produce the normal successful MCP result.
Rationale
src/seps/sep-2350.yaml currently excludes the server single-challenge requirement because the challenge was the only source of truth for required scopes. The fixed fixture contract supplies independent ground truth, so sep-2350-server-single-challenge can become an observable check.
This deliberately does not test general token validation or require a mock authorization server; it isolates the SDK/server request-time challenge seam. It also avoids the TypeScript-specific client/auth imports and guessed admin-action fixture in the older, unmerged #106 approach.
Proof
Run the official scenario through the existing SDK runner against:
- Unmodified
modelcontextprotocol/typescript-sdk@main: build succeeds, but low-token operations return HTTP 200 and the scenario exits non-zero. SamMorrowDrums/typescript-sdk@scope-challenge-server-sdk(feat(server): add request-time OAuth scope challenges typescript-sdk#1624): all challenge and upgraded-retry checks pass.
Related
- SEP-2350: SEP-2350: Clarify client-side scope accumulation in step-up authorization modelcontextprotocol#2350
- Existing client coverage: Conformance coverage for SEP-2350 (client-side scope accumulation in step-up auth) #281 / SEP-2350: scope-accumulation check in auth/scope-step-up #282
- Existing server-row exclusion: chore(sep-2350): exclude the server single-challenge requirement from traceability #302
- Older overlapping proposal: feat: add step-up auth scenario for server auth testing #106
- TypeScript SDK implementation: feat(server): add request-time OAuth scope challenges typescript-sdk#1624
Activity