Summary
When PRM parsing fails against OAuthProtectedResourceMetadataSchema (Zod validation error, e.g. missing the RFC 9728-required resource field, or any other schema violation), the SDK silently swallows the exception with no log, warning, or diagnostic. resourceMetadata stays undefined and the flow proceeds as if PRM discovery legitimately returned nothing, leaving downstream failures as the only user-visible signal.
Network failures were fixed to propagate; validation failures are still silent.
Location
packages/client/src/client/auth.ts (current main, ~L1163-1176), in the main auth() flow:
try {
resourceMetadata = await discoverOAuthProtectedResourceMetadata(
serverUrl,
{ resourceMetadataUrl: effectiveResourceMetadataUrl },
fetchFn
);
} catch (error) {
// Network failures (DNS, connection refused) surface as TypeError — propagate
// those rather than masking a transient reachability problem.
if (error instanceof TypeError) {
throw error;
}
// RFC 9728 not available — selectResourceURL will handle undefined
}
The TypeError propagation was added since earlier versions of this code (which had a bare catch {}), which is good; network problems now surface. But the "silently continue" branch is taken for all non-network errors, including Zod validation failures on a PRM that did fetch successfully.
The distinction matters:
- PRM 404 / 500 / non-200: correctly treated as "RFC 9728 not available" → soft fallback is reasonable
- PRM present, Zod-invalid: this is a spec/config mismatch on the server side. Silent fallback masks it.
Currently both are handled identically.
Reproduction
-
Point a client using this SDK at an MCP server whose PRM document returns HTTP 200 with a body that is invalid, for example, that omits the RFC 9728-required resource field (only authorization_servers, scopes_supported, etc.).
Example PRM body:
{
"authorization_servers": ["https://login.microsoftonline.com/{tenant}/v2.0"],
"scopes_supported": ["https://graph.microsoft.com/Mail.Read"],
"bearer_methods_supported": ["header"]
}
- Call
auth() with no authServerMetadataUrl override.
- Expected: an actionable error or log naming the Zod validation failure as the root cause.
- Actual: no log, no warning;
resourceMetadata becomes undefined and the downstream flow surfaces whatever unrelated error the fallback path produces.
Why this matters
The failure mode is genuinely hard to diagnose. Nothing in the observable output points at PRM validation. I spent a significant amount of time attributing this to authorization-server-derivation logic (thinking the SDK was ignoring authorization_servers from PRM), when in fact the SDK reads that field correctly - a completely different code path (schema validation) was throwing and being swallowed.
Adjacent acknowledgment in-repo:
PR #2092 ("fix(client,core): tighten OAuth PRM resource validation per RFC 8707 §2") explicitly notes in its body:
"since auth() swallows PRM errors and would mask the assertion"
The PR author works around this in their tests but doesn't scope the PR to fixing it. This issue is that scope.
Suggested fixes
- Minimum: log the swallowed exception.
} catch (error) {
if (error instanceof TypeError) throw error;
// RFC 9728 not available — selectResourceURL will handle undefined
console.warn(
'MCP OAuth: protected resource metadata discovery failed. ' +
'Downstream errors may reference the fallback path rather than PRM. Reason:',
error
);
}
- Better: differentiate schema-validation errors (Zod exception on a 200 response) from HTTP/network errors. Zod errors represent server-side spec violations and deserve a loud signal; HTTP failures are the "server doesn't implement RFC 9728" case where soft fallback is appropriate.
Related
Environment
- @modelcontextprotocol/sdk - reproduced against current main (2536-line auth.ts)
- Reproduced with clients: Cline (VS Code) and Claude Code (with
authServerMetadataUrl unset)
Summary
When PRM parsing fails against
OAuthProtectedResourceMetadataSchema(Zod validation error, e.g. missing the RFC 9728-requiredresourcefield, or any other schema violation), the SDK silently swallows the exception with no log, warning, or diagnostic.resourceMetadatastays undefined and the flow proceeds as if PRM discovery legitimately returned nothing, leaving downstream failures as the only user-visible signal.Network failures were fixed to propagate; validation failures are still silent.
Location
packages/client/src/client/auth.ts(currentmain, ~L1163-1176), in the mainauth()flow:The
TypeErrorpropagation was added since earlier versions of this code (which had a barecatch {}), which is good; network problems now surface. But the "silently continue" branch is taken for all non-network errors, including Zod validation failures on a PRM that did fetch successfully.The distinction matters:
Currently both are handled identically.
Reproduction
Point a client using this SDK at an MCP server whose PRM document returns HTTP 200 with a body that is invalid, for example, that omits the RFC 9728-required
resourcefield (onlyauthorization_servers,scopes_supported,etc.).Example PRM body:
{ "authorization_servers": ["https://login.microsoftonline.com/{tenant}/v2.0"], "scopes_supported": ["https://graph.microsoft.com/Mail.Read"], "bearer_methods_supported": ["header"] }auth()with noauthServerMetadataUrloverride.resourceMetadatabecomes undefined and the downstream flow surfaces whatever unrelated error the fallback path produces.Why this matters
The failure mode is genuinely hard to diagnose. Nothing in the observable output points at PRM validation. I spent a significant amount of time attributing this to authorization-server-derivation logic (thinking the SDK was ignoring
authorization_serversfrom PRM), when in fact the SDK reads that field correctly - a completely different code path (schema validation) was throwing and being swallowed.Adjacent acknowledgment in-repo:
PR #2092 ("fix(client,core): tighten OAuth PRM resource validation per RFC 8707 §2") explicitly notes in its body:
The PR author works around this in their tests but doesn't scope the PR to fixing it. This issue is that scope.
Suggested fixes
Related
Environment
authServerMetadataUrlunset)