MCP OAuth tokens are cached per server registration name, not per PRM `resource`

Status Closed — not planned
Reported on v2.1.220
Maintainer reply None cached
Activity 1 comment · opened Jul 28, 2026 · closed Oct 8, 2026

Summary

Claude Code stores MCP OAuth credentials keyed by the server registration name. When several registrations sit behind one authorization server and advertise an identical resource in their Protected Resource Metadata (RFC 9728), each registration still requires its own full authorization. Sharing the resource value has no effect.

Reproduce

  1. Put two MCP servers behind one gateway that advertises the same PRM resource for both (e.g. bare origin https://gw.example.internal), on paths /a/mcp and /b/mcp.
  2. claude mcp add --transport http srv-a https://gw.example.internal/a/mcp -s user --client-id <id> and likewise for srv-b.
  3. Authenticate srv-a only.
  4. claude mcp list

Observed

srv-a is ✔ Connected; srv-b stays ! Needs authentication. Authenticating srv-b requires a full browser round trip including credential entry.

~/.claude.json shows why: OAuth state is stored under mcpServers.<registration-name>.oauth.*, split per registration. In our case all registrations share the same static clientId and callback port, so the split is purely in the credential store, not in client identity.

Expected

Tokens obtained for a given PRM resource are reusable across registrations that advertise that same resource, since that value is precisely what the token is audienced for.

Why it matters

With an N-service gateway this turns "one authorization per day" into N, each on its own expiry clock, so the re-auth prompts scatter through the working day. We reached this after closing three separate gateway-side workarounds; the remaining constraint is entirely on the client side.

Environment

  • Claude Code 2.1.220, Linux
  • Authorization server: Keycloak, static public client + PKCE S256, offline_access

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗