Skip to content

fix(client): OAuth well-known fallback on 4xx and authorization-server origin - #2785

Open
tiagovilasboas wants to merge 5 commits into
modelcontextprotocol:mainfrom
tiagovilasboas:cursor/oauth-well-known-fallback-6ad7
Open

tiagovilasboas wants to merge 5 commits into
modelcontextprotocol:mainfrom
tiagovilasboas:cursor/oauth-well-known-fallback-6ad7

Conversation

@tiagovilasboas

@tiagovilasboas tiagovilasboas commented Sep 10, 2026

Copy link
Copy Markdown

Summary

Fixes OAuth well-known metadata discovery for remote MCP servers behind CDNs / split resource vs authorization-server hosts.

#2783 — fallback on 4xx, not only 404

Path-inserted well-known URLs (/.well-known/oauth-protected-resource/<path>, /.well-known/oauth-authorization-server/<path>) are often answered with 401 or 403 by CDNs / bot protection instead of 404. Discovery already treated any 4xx (plus 502) as a miss and retried the root well-known document; this change keeps that predicate and adds coverage so 401/403/404 fall back, 200 short-circuits, and non-502 5xx does not fall back.

Repro: Retool https://hashnote.retool.com/mcp — path-aware PRM returns 401, root PRM returns 200.

#2784 — fallback URL must use the authorization-server host

When the resource URL and the authorization server are on different hosts, the root fallback was built from the resource URL (new URL('/.well-known/…', issuer)). That fetched an unrelated metadata document.

The fallback is now built from metadataServerUrl (the authorization server / issuer host). discoverOAuthMetadata(resourceUrl, { authorizationServerUrl }) again uses the resource path only as the well-known suffix and keeps the retry on the AS origin.

Repro: Superhuman — path-aware AS https://id.superhuman.com/.well-known/oauth-authorization-server/apis/mcp → 403; root https://id.superhuman.com/.well-known/oauth-authorization-server → 200. The client must not fall back to https://docs.superhuman.com/.well-known/oauth-authorization-server.

Test plan

  • Unit tests with mocked fetch in packages/client/test/client/auth.test.ts
    • 401 / 403 / 404 on path-aware discovery fall back to root
    • 200 on path-aware discovery does not fall back
    • 500 does not fall back (existing)
    • AS fallback URLs stay on the authorization-server origin
  • pnpm --filter @modelcontextprotocol/client test — 884 passed (full client suite)
  • vitest run test/client/auth.test.ts — 268 passed

Fixes #2783
Fixes #2784

@tiagovilasboas
tiagovilasboas requested a review from a team as a code owner September 10, 2026 23:15
@changeset-bot

changeset-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6cee975

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@modelcontextprotocol/client Patch
@modelcontextprotocol/codemod Patch
@modelcontextprotocol/core Patch
@modelcontextprotocol/server-legacy Patch
@modelcontextprotocol/server Patch
@modelcontextprotocol/core-internal Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Sep 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@2785

@modelcontextprotocol/codemod

npm i https://pkg.pr.new/@modelcontextprotocol/codemod@2785

@modelcontextprotocol/core

npm i https://pkg.pr.new/@modelcontextprotocol/core@2785

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@2785

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/@modelcontextprotocol/server-legacy@2785

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@2785

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@2785

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@2785

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@2785

commit: 6cee975

Path-inserted well-known discovery already retries on 4xx, but the root
fallback URL was built from the resource/issuer URL. When the
authorization server lives on a different host, that fetched the wrong
metadata document.

Build the fallback from metadataServerUrl (the AS) and pass the resource
URL only as the path prefix in discoverOAuthMetadata.

Fixes modelcontextprotocol#2783
Fixes modelcontextprotocol#2784

Co-authored-by: Tiago Vilas Boas <tcarvalhovb@gmail.com>
Add mocked-fetch cases for 401/403/404 path-aware fallback, 200
short-circuit when resource and authorization-server hosts differ, and
continuation on 4xx during authorization-server metadata discovery.

Co-authored-by: Tiago Vilas Boas <tcarvalhovb@gmail.com>
Co-authored-by: Tiago Vilas Boas <tcarvalhovb@gmail.com>
Co-authored-by: Tiago Vilas Boas <tcarvalhovb@gmail.com>
@cursor
cursor Bot force-pushed the cursor/oauth-well-known-fallback-6ad7 branch from 72454b8 to 2392b11 Compare September 15, 2026 19:30

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant