You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(server): runtime-neutral OAuth discovery serving for web-standard hosts
The RFC 9728 Protected Resource Metadata and RFC 8414 Authorization Server
metadata documents could only be served through the Express metadata router,
so fetch-handler hosts (Cloudflare Workers, Deno, Bun, Hono) hand-rolled the
well-known routes, CORS, and method handling. The core moves to
@modelcontextprotocol/server as oauthMetadataResponse (a Response-or-undefined
matcher in the style of hostHeaderValidationResponse), built on the exported
buildOAuthProtectedResourceMetadata, with getOAuthProtectedResourceMetadataUrl
now defined here. The Express router adapts the same core with unchanged
behavior, proven by its untouched test suite.
The neutral core improves on the Express quirks where green-field allows:
matching happens before validation so unmatched traffic always falls through
(a misconfigured issuer surfaces on the discovery routes, or at startup via
an eager buildOAuthProtectedResourceMetadata call, never as a whole-server
outage), a single trailing slash is tolerated, HEAD is served per RFC 9110,
reflected CORS preflights carry Vary, and the insecure-issuer escape hatch is
an explicit dangerouslyAllowInsecureIssuerUrl option instead of a
module-scope environment read.
The router mounts two well-known routes: `/.well-known/oauth-protected-resource/mcp` — the path-aware RFC 9728 location, the same string `getOAuthProtectedResourceMetadataUrl(mcpServerUrl)` put into the challenge — and `/.well-known/oauth-authorization-server`, a mirror of `oauthMetadata` for clients that probe your origin directly. An unauthenticated client follows `401` → `resource_metadata` → `authorization_servers` to find your AS, obtains a token, and retries.
91
91
92
+
On a web-standard host, `oauthMetadataResponse` from `@modelcontextprotocol/server` serves the same two documents from a `fetch(request)` handler — it returns the matched document `Response` (with permissive CORS and `405` handling) or `undefined` to fall through to your own routing:
`requireBearerAuth` attaches the verified `AuthInfo` to `req.auth`, `toNodeHandler` forwards it, and tool handlers inside `buildServer` read it as `ctx.http.authInfo` — the exact object your verifier returned.
0 commit comments