typescript-sdk/.changeset/scope-challenge-server.md
Sam Morrow 6032170087
Some checks failed
Conformance Tests / client-conformance (push) Has been cancelled
Conformance Tests / server-conformance (push) Has been cancelled
Deploy Docs / deploy-docs (push) Has been cancelled
Examples / examples (build + e2e) (push) Has been cancelled
/ build (push) Has been cancelled
/ test (20) (push) Has been cancelled
/ test (22) (push) Has been cancelled
/ test (24) (push) Has been cancelled
/ test-e2e (20) (push) Has been cancelled
/ test-e2e (22) (push) Has been cancelled
/ test-e2e (24) (push) Has been cancelled
/ test-runtimes (bun, 1.x) (push) Has been cancelled
/ test-runtimes (deno, v2.x) (push) Has been cancelled
Publish Any Commit / pkg-publish (push) Has been cancelled
Release / Version (push) Has been cancelled
Release / Publish (push) Has been cancelled
feat(server): add request-time OAuth scope challenges (#1624)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-09-16 16:51:56 +00:00

1.2 KiB

@modelcontextprotocol/server @modelcontextprotocol/node
minor minor

Add request-time OAuth scope challenges for tools, resources, resource templates, and prompts. Each primitive's scopeChallenge callback receives the parsed request and verified authentication info, then either continues or returns the exact scope set for an insufficient_scope response. requireScopes provides a small helper for static all-of checks.

createMcpHandler and Streamable HTTP transports return HTTP 403 with an insufficient_scope challenge before handler execution or SSE setup. The preflight is active whenever a registered primitive carries a scopeChallenge callback — there is no handler- or transport-level configuration. The challenge's WWW-Authenticate header is built by the same formatter as the bearer-auth 401/403 answers, and its resource_metadata parameter is derived from the verified AuthInfo: requireBearerAuth / verifyBearerToken now stamp their configured resourceMetadataUrl onto the AuthInfo they return (new optional AuthInfo.resourceMetadataUrl field), with a fallback to the well-known location for an HTTP(S) RFC 8707 resource identifier; the parameter is omitted when neither is available.