Skip to content

fix(auth): preserve resource URI without trailing slash (#1968)#1972

Open
MukundaKatta wants to merge 3 commits intomodelcontextprotocol:v1.xfrom
MukundaKatta:fix/oauth-resource-trailing-slash-v1x
Open

fix(auth): preserve resource URI without trailing slash (#1968)#1972
MukundaKatta wants to merge 3 commits intomodelcontextprotocol:v1.xfrom
MukundaKatta:fix/oauth-resource-trailing-slash-v1x

Conversation

@MukundaKatta
Copy link
Copy Markdown

What

Fixes #1968.

When parsing OAuth protected resource metadata, selectResourceURL returned new URL(resourceMetadata.resource) and the auth + token request paths serialized that value via URL.href. For bare-origin URIs that round trip appends a trailing slash:

new URL("https://example.com").href // "https://example.com/"

So a PRM document that publishes "resource": "https://example.com" ends up sending resource=https%3A%2F%2Fexample.com%2F on the wire. That changes the resource indicator from the value the server told us to use, and it breaks any AS that compares it against the configured audience exactly.

In particular Microsoft Entra ID rejects the call with:

AADSTS9010010: The resource parameter provided in the request doesn't match with the requested scopes.

(see also: modelcontextprotocol/inspector#927 for the symptom downstream.)

How

  • selectResourceURL now returns URL | string | undefined and returns resourceMetadata.resource verbatim from the PRM path. The custom-validation path still returns a URL because validateResourceURL is typed that way.
  • startAuthorization, executeTokenRequest, exchangeAuthorization, refreshAuthorization, and fetchToken accept resource?: URL | string and serialize it via String(resource) instead of resource.href.
  • The validateResourceURL provider hook signature is unchanged. checkResourceAllowed still validates the metadata value as a URL.

Diff is contained to src/client/auth.ts. Existing PRM-with-path coverage still passes (paths like https://api.example.com/mcp-server are unaffected — the URL.href round-trip only added a slash for bare origins).

Tests

  • Added a regression test that publishes resource: "https://example.com" in PRM and asserts the auth URL receives resource=https://example.com (no trailing slash).
  • Updated the cached-discovery-state test, which previously asserted the buggy normalized form, to expect the un-normalized resource string.
  • npm test passes locally (1580/1580).

…protocol#1968)

When handling RFC 9728 protected resource metadata, `selectResourceURL`
routed the metadata's `resource` value through `new URL(...).href`. For
bare-origin URIs that round trip appends a trailing slash:

    new URL("https://example.com").href === "https://example.com/"

The resulting `resource` parameter no longer matches what the server
published in PRM, which breaks providers that require an exact match.
Microsoft Entra ID rejects the request with AADSTS9010010 when the
`resource` parameter does not match the audience of the requested scope.

Return the original metadata string verbatim from `selectResourceURL`
and serialize it with `String(resource)` instead of `URL.href` in the
authorization and token request paths. The validation step still parses
the value as a URL via `checkResourceAllowed`. Also adjusted the cached
discovery-state test to expect the un-normalized resource value, and
added a regression test for the bare-domain case.

Fixes modelcontextprotocol#1968
@MukundaKatta MukundaKatta requested review from a team as code owners April 28, 2026 03:49
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 28, 2026

🦋 Changeset detected

Latest commit: d61bfde

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

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
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 28, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@modelcontextprotocol/sdk@1972

commit: d61bfde

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant