MCP OAuth storage wiped mid-session (no update involved); CLI silently registers a new dynamic client, breaking auth for all remote MCP servers

Status Open
Reported on v2.1.217
Maintainer reply None cached
Activity 2 comments · opened Jul 23, 2026

Environment

  • Claude Code 2.1.217 (macOS, darwin; embedded Node v26.3.0)
  • Two remote HTTP MCP servers with OAuth: https://mcp.hospitable.com/mcp (dynamic client registration) and https://mcp.frontapp.com/mcp (static client_id + client_secret configured via claude mcp add --client-id ... --client-secret)
  • Multiple concurrent interactive sessions (~6 different projects active in the window)

Summary

All stored MCP OAuth credentials (access tokens, refresh tokens, dynamic client registrations, AND a statically-configured client_secret) vanished from storage mid-session, with no CLI update involved — the same 2.1.217 binary was running before, during, and after. When the next session found storage empty, it silently registered a brand-new dynamic client and cached needs-auth, so the user just sees "Needs authentication" again with no indication credentials were lost. Over 8 days this produced 4 distinct dynamic client_ids registered against the same server.

We had previously attributed recurring auth loss to update-time OAuth storage wipes (observed 2026-07-01 on 2.1.197→2.1.198, and again ~2026-07-06). This incident shows storage can also be lost with no update, pointing at a concurrent read-modify-write race on the shared credential store.

Timeline (2026-07-22, UTC; all logs from ~/Library/Caches/claude-cli-nodejs/<proj>/mcp-logs-hospitable/)

  • 04:08 — fresh interactive OAuth for hospitable completes: client a251208c-adcc-4089-9e9c-0df9ff5c62ce, Token expires in: 43200, Has refresh token: true, Saving tokens.
  • 04:24–05:07 — sessions in 6 different projects connect successfully with that token ("Successfully connected" each time; token read fine at 05:07:13).
  • 05:11:27 — a session in another project logs:

``
No token data found
No client info found
No access token in storage
Authorization URL: ...client_id=a2513799-a9b4-4a12-930a-32dda1e8a151...
`
i.e. storage is completely empty **4 minutes after another session read it successfully**, with ~11 hours left on the access token — and the CLI silently performs a fresh dynamic client registration (
a2513799...`), persisting it as the stored client info.

  • 05:12:39 — the second OAuth server (Front, static client credentials) starts logging Skipping connection (cached needs-auth) in a long-running session whose token refresh had succeeded at 00:21:26 ("Token refresh successful"). Its stored static client_secret is now missing (claude mcp get front no longer shows "client_secret configured", while client_id survives in ~/.claude.json).
  • 20:31 — still on 2.1.217 (User-Agent claude-code/2.1.217), a new session finds "No access token in storage", "Found client info" = the self-registered a2513799, and reports needs-auth.
  • The 2.1.218 auto-update landed hours after the wipe — versions in User-Agent strings confirm 2.1.217 throughout the incident window.

Distinct defects

  1. Mid-session credential-storage loss. The entire MCP OAuth store (per-server tokens + client registrations + static client_secret) can be lost while sessions are running. The correlation with ~6 concurrent sessions suggests a last-writer-wins race: a process serializing the credentials blob from a stale in-memory copy (or treating a transient keychain read failure as "empty") clobbers everything other sessions saved.
  2. Silent re-registration masks the loss. When storage reads empty, the CLI immediately registers a new dynamic client and persists it. There is no warning that previously-stored credentials disappeared; the user only discovers auth is gone at next tool use. This also churns client registrations server-side — MCP providers see a stream of orphaned clients (6 from us in 8 days: a242f8ce, a251208c, a2513799, a252fa71, a2530341, a253076f), and any refresh-token chain tied to the old client is dead.
  3. claude mcp login client reuse is inconsistent. With a valid registered client in storage (a2513799, self-registered at the 05:11 wipe), four consecutive claude mcp login hospitable attempts behaved differently: attempt 1 registered a new client (a252fa71), attempt 2 after "Authentication timeout" registered another (a2530341), attempt 3 reused a2530341, attempt 4 registered yet another (a253076f). Whatever invalidates the stored registration compounds the orphan-client churn on the provider side.

Expected

  • Concurrent sessions must not clobber each other's saved MCP OAuth credentials (per-server keys or file locking rather than whole-blob last-writer-wins).
  • A keychain/storage read failure should be treated as an error (retry/surface), not as "no credentials exist".
  • If stored credentials for a server disappear while a static client_id remains configured, surface a loud warning instead of silently re-registering / silently caching needs-auth.
  • claude mcp login should reuse a stored, still-valid client registration.

Impact

Every remote OAuth MCP server needs full interactive re-auth every few days; for servers with static client secrets the stored secret must be re-added from a password manager each time. We now run a SessionStart hook solely to detect these wipes.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗