MCP OAuth: no per-server "clear authentication", and no re-registration when a cached DCR client is rejected
Environment
- Claude Code 2.1.215 (native install), macOS
- Remote HTTP MCP server using OAuth 2.0 Dynamic Client Registration (RFC 7591). Concretely hit with a self-hosted Metabase MCP (
/api/metabase-mcp), but the issue is general to any DCR-based OAuth MCP server.
Problem
Claude Code caches the dynamically-registered OAuth client_id per server (on macOS in the keychain item Claude Code-credentials, under an mcpOAuth object). When the server drops or rotates its registered clients — e.g. a self-hosted instance redeploys/restarts and doesn't persist DCR client registrations — the cached client_id becomes invalid. From that point:
/mcp→ _server_ → Authenticate reuses the staleclient_idrather than performing a fresh registration, so the browser flow fails identically every time (the auth server rejects the unknown client; Metabase surfaces a generic{"error":"invalid_request","error_description":"The authorization request is invalid."}).- There is no per-server "clear authentication" action in the
/mcpUI — only "Authenticate", which reuses the dead client. The only recovery is either manual keychain surgery to delete just that server'smcpOAuthentry, or deleting the entireClaude Code-credentialsitem (which also logs you out of Claude and drops every server's registration, forcing a full re-auth).
Net effect: a self-hosted OAuth MCP server that recycles DCR clients on deploy permanently wedges every developer who authenticated before the deploy, with no in-product recovery.
Expected behavior
Either (ideally both):
- (a) Auto re-register. When the authorization or token endpoint rejects a cached client (
invalid_client, or a genericinvalid_requestthat resolves to an unknown/again-invalid client), transparently perform a fresh DCR (registration_endpoint) and retry, instead of reusing the cachedclient_id. - (b) Per-server "clear authentication" / "re-register" action in the
/mcpUI, so a single server can be reset without nuking the whole credential store.
Reproduction
- Configure a remote HTTP MCP server that advertises OAuth with a
registration_endpoint(DCR) and authenticate it via/mcp(Claude caches aclient_id). - Invalidate that client server-side — e.g. restart/redeploy an instance that stores DCR clients ephemerally (or otherwise rotate/expire registered clients).
/mcp→ _server_ → Authenticate → fails, and keeps failing on every retry. Theclient_idin the/oauth/authorize?...URL is unchanged across attempts (onlycode_challenge/staterotate), confirming the stale cached client is being reused.
Diagnostic tell (for others hitting this)
In the browser's /oauth/authorize?... URL: a stable client_id across two Authenticate attempts ⇒ stale cached client (this issue). A different client_id each attempt ⇒ Claude is re-registering and something else is wrong.
Related nicety
A machine-local event (a Claude Code native auto-update, or a Claude account switch/re-login) can reset the local MCP OAuth cache, forcing re-auth of all servers at once. A short note in the MCP docs about which events invalidate cached MCP OAuth would cut down on "why did every server suddenly need re-auth?" confusion.
Related issues
- #33704 (docs: re-authentication behavior after refresh-token expiry) — adjacent, but about refresh expiry, not stale DCR clients / missing clear-auth.
- #53803 (closed) — refresh-token reuse; different failure mode.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗