Repository navigation
Conversation
When the plugin was registered under a prefix such as /api with authorization enabled, the protected resource metadata routes answered 401, because the bearer-token check exempted them by URL (/.well-known/ at the origin root only), and the resource_metadata URL advertised in WWW-Authenticate pointed at the origin root, where nothing is served. - Exempt the plugin's well-known and OAuth flow routes by route config instead of URL, so they are public whatever the prefix. The root /.well-known/ URL exemption stays for application routes. - Advertise the URL the metadata is actually served at, including the prefix. Without a prefix it is unchanged. - Add authorization.resourceMetadataUrl to override the advertised URL, and export wellKnownRoutes and getResourceMetadataUrl so applications can also serve the metadata at the RFC 9728 origin-root location. Fixes #340 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LmhsFjk8W96X2PvmShmDm9
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
With the plugin registered under a prefix (e.g.
/api) and authorization enabled, OAuth discovery failed:/api/.well-known/oauth-protected-resourceand/api/.well-known/oauth-protected-resource/api/mcpanswered401 authorization_required, because the bearer-token check exempted only URLs starting with/.well-known/or/mcp/.well-known;resource_metadataURL inWWW-Authenticate(https://host/.well-known/oauth-protected-resource/api/mcp) was 404, because the routes are registered under the prefix.The same URL-based exemption also made
/api/oauth/authorize,/api/oauth/callbackand/api/oauth/registerrequire a token.Fix
Symbol, so it cannot clash with application config), and the preHandler skips routes that have it. They are now public whatever the prefix.resource_metadataURL is now{origin of resourceUri}{prefix}/.well-known/oauth-protected-resource{path of resourceUri}, i.e. the URL the plugin actually serves. Without a prefix this is byte-for-byte the RFC 9728 §3.1 URL advertised today.authorization.resourceMetadataUrloverrides the advertised URL, andwellKnownRoutesplusgetResourceMetadataUrlare exported, so an application can serve the metadata at the RFC 9728 location as well:Design decisions
onReadyon the root instance from inside the plugin, which breaks encapsulation and could collide with the application's own/.well-known/*routes or with a second MCP instance under another prefix. So by default the plugin advertises a URL it really serves, and the application, which owns the root, can mount the standard location explicitly.resource_metadatafrom theWWW-Authenticateheader when present, and RFC 9728 §5.1 lets that parameter be any URL. So a prefixed server is discoverable by any compliant client without extra setup. Clients that probe without a challenge need the root mount above, which is documented in the README ("Under a Route Prefix")./apiin the URL (/api/.well-known/oauth-protected-resource/api/mcp)? The plugin builds the same route set as without a prefix (root route,{path of resourceUri}route, legacy/mcproute) and the prefix applies to all of them. Keeping one formula means the advertised URL and the served route cannot drift apart. The shorter/api/.well-known/oauth-protected-resourceis also served and returns the same document./.well-known/. The README promised that every path under/.well-known/skips the token check, and applications may serve their own documents there (e.g.apple-app-site-association) withprotectedRoutes: 'all'. Removing it would be a breaking change in a fix, so it stays; the plugin's own routes no longer depend on it. Dropping it (and the odd/mcp/.well-knownexemption) could be done in the next major. The/oauth/*URL exemptions are kept for the same reason.Interaction with other work
/mcpmetadata route advertisesresourceUri) touchessrc/routes/well-known.tsand the README in different places; the two PRs are independent and can merge in either order. I kept them separate because they fix different things (routing and challenge URL vs. theresourcevalue) and fix(auth): advertise resourceUri from the legacy /mcp metadata route #348 is a one-line behaviour change that is easier to review alone.Tests
New
test/well-known-prefix.test.ts(failing before the fix, except the no-prefix regression guard and the "other routes still need a token" guard):/apireturn 200 withprotectedRoutes: 'all'and'mcp';resource_metadataURL resolves to 200 withresource === resourceUri, for/apiand a nested/v1/tenantprefix;/api/oauth/authorizeis reachable without a token;resourceMetadataUrl+ root-mountedwellKnownRoutesadvertises and serves the RFC 9728 origin-root URL;npm run lint,npm run typecheck, and the fullnpm run test(775 tests) pass.Fixes #340
🤖 Generated with Claude Code
https://claude.ai/code/session_01LmhsFjk8W96X2PvmShmDm9