Repository navigation
OAuth resource indicator from protected resource metadata is normalized with a trailing slash #1968
Description
Description
When protected resource metadata contains a pathless resource URI, the TypeScript SDK normalizes it through new URL(...).href, which changes the resource indicator value by adding a trailing slash.
For example, protected resource metadata may contain:
{
"resource": "https://example.com",
"authorization_servers": ["https://login.microsoftonline.com/{tenant}/oauth2/v2.0"],
"scopes_supported": ["https://example.com/mcp:tools"]
}The SDK then constructs OAuth requests with:
resource=https%3A%2F%2Fexample.com%2F
instead of:
resource=https%3A%2F%2Fexample.com
This changes the resource identifier from the value explicitly published in protected resource metadata.
Impact
This breaks OAuth with providers that require the resource parameter to exactly match the resource implied by the requested scopes. Microsoft Entra ID returns:
AADSTS9010010: The resource parameter provided in the request doesn't match with the requested scopes.
Removing the trailing slash makes the request work.
Expected behavior
If protected resource metadata explicitly provides "resource": "https://example.com", then the SDK should preserve that exact value when adding the OAuth resource parameter to both the authorization request and the token request.
Validation can still parse the value as a URL, but serialization should use the original metadata string rather than URL.href.
Actual behavior
The SDK serializes the resource via URL.href, which changes a pathless URI:
new URL("https://example.com").href
// "https://example.com/"Likely cause
In the current SDK code, selectResourceURL() returns a URL object:
return new URL(resourceMetadata.resource);Then the authorization and token request code serializes with .href:
authorizationUrl.searchParams.set("resource", resource.href);
tokenRequestParams.set("resource", resource.href);This turns "https://example.com" into "https://example.com/".
Suggested fix
Preserve the resource metadata value as a string after validation. For example, return resourceMetadata.resource from selectResourceURL() and serialize with String(resource) rather than resource.href.
This may require changing the internal resource type from URL | undefined to string | URL | undefined, or introducing a small alias for OAuth resource indicators.
Related issue
This appears to be the SDK-level root cause of this Inspector issue:
Activity
fix(auth): preserve resource URI without trailing slash (modelcontext…
chore: add changeset for modelcontextprotocol#1968 fix
hugosmoreira commentedon Jul 30, 2026
I'm taking this issue. I confirmed the selectResourceURL/raw metadata path still normalizes the resource on current main, and found no open PR. I'll preserve the exact metadata string, add focused authorization and token-request regression coverage, and run the client package checks.
matthewcarlisle commentedon Sep 1, 2026
Bueller?
test(client): pin pathless PRM resource end to end, document selectRe…
fix(client): keep selectResourceURL returning a URL and send the PRM …
fix(auth): preserve resource URI without trailing slash (#1968) (#1972)