MCP OAuth writes corrupt shared credential store, causing Team plan logout across all sessions

Resolved 💬 4 comments Opened Apr 9, 2026 by prodan-s Closed May 9, 2026

Summary

Authenticating an HTTP MCP server (e.g. Linear at https://mcp.linear.app/mcp) causes Claude Code to lose its Team plan authentication across all active sessions. All sessions display "you've been logged out" and CC falls back to API billing.

Root Cause

Claude Code stores both its own subscription OAuth (claudeAiOauth) and all MCP server OAuth tokens (mcpOAuth) in a single macOS Keychain entry (Claude Code-credentials, one JSON blob). When an MCP OAuth write occurs with a stale cache, null Keychain read, or concurrent read-modify-write race, the resulting JSON is written back without valid claudeAiOauth — logging out all sessions.

Environment

  • CC version: 2.1.96 (macOS ARM64)
  • Plan: Team (Stripe subscription)
  • Concurrent sessions: 5 interactive + 1 headless + MCP companions = 36-47 processes sharing one Keychain entry
  • MCP servers: Linear (HTTP/OAuth), Vercel (HTTP/OAuth, disabled), 7 stdio servers

Reproduction

  1. Have 2+ CC sessions running with Team plan auth
  2. Run /mcp and authenticate Linear MCP (https://mcp.linear.app/mcp)
  3. After OAuth callback completes, all sessions show "logged out"
  4. claude auth status reports loggedIn: false
  5. Keychain entry either missing entirely, or contains only mcpOAuth (no claudeAiOauth)

Repro protocol (before/after measurement)

# Before:
security find-generic-password -a "$USER" -s "Claude Code-credentials" -w | python3 -c "import json,sys; d=json.load(sys.stdin); print(list(d.keys()), len(json.dumps(d)), 'bytes')"
# Output: ['claudeAiOauth', 'mcpOAuth'] 1261 bytes

# Trigger Linear MCP auth via /mcp...

# After:
# Expected smoking gun: mcpOAuth present, claudeAiOauth missing

Contributing Bugs

This is not a single bug but at least 5 interacting issues:

| # | Issue | Mechanism |
|---|-------|-----------|
| 1 | #24317 | Concurrent session race condition. proper-lockfile with 7.5s max lock budget is insufficient for 10+ processes. Single-use refresh token: one session refreshing invalidates all others. |
| 2 | #33995 | Token refresh never persists to disk. refreshOAuthToken() succeeds in memory but update() is never called to write back. |
| 3 | #36779 | MCP OAuth write drops claudeAiOauth. Only mcpOAuth persisted, auth token missing. Closest exact-match to this symptom. |
| 4 | #28542 | JSON truncation. Discovery metadata bloats shared Keychain JSON past ~2010 bytes → truncation → JSON.parse() fails → full credential reset. |
| 5 | #37512 | CLAUDE_CODE_OAUTH_TOKEN silently deletes Keychain. Write to plaintext backend triggers delete from Keychain backend. |

Additional Related Issues

  • #5706 — MCP refresh tokens stored but never used (OPEN since 2025, 29 comments)
  • #43000 — Multi-terminal MCP OAuth: storage key includes callback port, causing duplicate entries
  • #44830 — RFC 9728 discovery poisoning: one transient failure permanently blocks OAuth
  • #40834 — Switching auth modes invalidates ALL HTTP MCP OAuth tokens
  • #38074 — Background ccr_inference sessions disrupt CLI sessions every ~10 min
  • #42603 — Plugin hooks can read/refresh CC credentials with zero permission checks
  • #1757 — Users constantly required to re-login (130+ comments, oldest auth issue)

Proposed Fix

The fundamental issue is the shared credential store. claudeAiOauth and mcpOAuth should not be in the same Keychain entry. Either:

  1. Separate Keychain entries for CC auth vs MCP auth (eliminates the clobber entirely)
  2. Atomic read-modify-write with proper locking (the current proper-lockfile budget is too low for typical multi-session environments)
  3. Per-session MCP OAuth state that doesn't touch the shared credential store

Local Workaround

I built a credential backup/restore system (cc-auth-guard.sh) that:

  • Snapshots the Keychain JSON at each SessionStart
  • Detects when claudeAiOauth disappears after MCP auth
  • Auto-restores from the most recent good backup (merging current mcpOAuth with backed-up claudeAiOauth)

This mitigates the symptom but doesn't fix the race condition.

View original on GitHub ↗

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