MCP OAuth client does not send RFC 8707 `resource` (2.1.251) — strict MCP servers reject the token (401)
Summary
On Claude Code 2.1.251, the MCP OAuth client does not send the RFC 8707resource parameter in either the /authorize or /token request, even though
the MCP 2025-06-18 Authorization spec requires it (MUST) in both. The issued
access token therefore carries aud=<client_id> (the client's default audience)
instead of the MCP server's canonical resource URI, and spec-compliant (strict)
MCP servers reject it with 401.
This is distinct from #52871 (which reports resource being sent but corrupted
with a trailing slash on host-only URLs). Here resource is not sent at all.
Reproduction
- MCP server (HTTP transport) whose RFC 9728 Protected Resource Metadata
declares resource: http://localhost:8130/mcp, backed by Keycloak 26.7.2 with
the experimental resource-indicators feature enabled and the OAuth client's
resource_url set to http://localhost:8130/mcp.
claude mcp add --transport http mcp-a http://localhost:8130/mcp/mcp→ complete the browser OAuth flow (login + consent).- Inspect the issued access token.
Observed
- Token:
aud=mcp-client(client_id default), noresourceclaim. - The MCP server (validates
aud == http://localhost:8130/mcpper spec) rejects
it: 401 claim check failed: aud actual="mcp-client".
- The full flow otherwise succeeds (discovery → browser login → consent → token
issuance). The only blocker is the missing resource.
Evidence (AS-side control tests)
Control tests against the same AS isolate the cause:
| Case | resource in /authorize | resource in /token | Result |
|------|--------------------------|----------------------|--------|
| A | — | — | aud=mcp-client (default) ← matches the token Claude Code obtained |
| B | — | exact URI | 400 invalid_target |
| C | exact URI | — | aud=http://localhost:8130/mcp (correct) |
| D | trailing-slash URI | — | 400 invalid_target |
| E | — | trailing-slash URI | 400 invalid_target |
The token Claude Code obtained was aud=mcp-client and the exchange
succeeded. Any presence of resource (exact or trailing-slash) in either request
would have either set aud (C) or failed the exchange (B/D/E). Thereforeresource was absent from both requests — ruling out "sent but ignored by
the AS".
Version note
Observed on 2.1.251. Related: #52871 reports 2.1.119 does sendresource but appends a trailing slash to host-only URLs (corrupting it). The two
are distinct (not-sent vs. sent-but-corrupted) but both concern the resource
parameter; together they suggest the behavior is version-dependent.
Expected
Per the MCP 2025-06-18 Authorization spec, the client MUST include resource
(the MCP server's canonical URI from the RFC 9728 metadata) in both the
authorization request and the token request. The issued token should then carryaud=<resource URI>, which strict MCP servers accept.
Impact
Any strict MCP server validating aud will reject tokens issued by Claude Code
2.1.251, breaking interoperability. The only workaround is for the server to relaxaud validation (accept the client_id as an extra audience), which defeats the
purpose of audience binding.