MCP OAuth credentials repeatedly lost: discoveryState bloats keychain entry causing JSON truncation and parse failure on restart

Resolved 💬 3 comments Opened Feb 25, 2026 by jordfan Closed Mar 1, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

When an OAuth MCP server (e.g. Supabase) is connected, Claude Code stores the OAuth discoveryState (authorization server metadata) inside the same Claude Code-credentials keychain entry as the auth tokens. The Supabase discovery metadata is ~1.5 KB on its own, which pushes the total credentials JSON to ~2010 bytes where it gets truncated mid-string when written to the keychain.

On the next session start, JSON.parse() throws a SyntaxError on the malformed JSON. Claude Code resets the credential store to defaults, wiping the MCP access token. The user is prompted to re-authenticate, and the cycle repeats on every restart.

What Should Happen?

MCP OAuth access tokens should survive session restarts. The discoveryState (static, re-fetchable OAuth server metadata) should not be co-located with auth tokens in a size-limited credential store, or the store should handle larger payloads without truncation.

Error Messages/Logs

Inspect the stored credentials to see the truncation:

$ security find-generic-password -s "Claude Code-credentials" -w | xxd | tail -3
000007c0: 6573 5f73 7570 706f 7274 6564 223a 5b22  es_supported":["
000007d0: 6175 7468 6f72 697a 6174 0a              authorizat.

Total size: 2010 bytes. JSON ends mid-string in the Supabase discoveryState.
`jq .` fails: "parse error: Unfinished string at EOF at line 2, column 0"

Byte layout of the truncated blob:
  bytes 0–451:   claudeAiOauth section (Claude Max token)
  bytes 451–734: mcpOAuth header (serverName, clientId, "accessToken":"", "expiresAt":0)
  bytes 734–2010: discoveryState — 1,276 bytes of OAuth server metadata, TRUNCATED

Steps to Reproduce

  1. Add an OAuth MCP server with a large discovery document (e.g. plugin:supabase:supabase at https://mcp.supabase.com/mcp) to Claude Code
  2. Complete the OAuth flow — MCP connects successfully
  3. Restart Claude Code
  4. Observe: MCP prompts for re-authentication immediately
  5. Repeat indefinitely — auth is lost on every restart

To confirm truncation is occurring:

security find-generic-password -s "Claude Code-credentials" -w | wc -c
# ~2011 bytes

security find-generic-password -s "Claude Code-credentials" -w | jq .
# parse error: Unfinished string at EOF at line 2, column 0

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

2.1.56 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Auth method: Claude Max subscription (OAuth via Claude Code CLI — no API key)

Suggested fixes (any of these would resolve it):

  1. Do not persist discoveryState in the credentials blob — re-fetch it on OAuth flow init (it's a single unauthenticated GET to a well-known endpoint and never changes)
  2. If it must be cached, store it separately from auth tokens (e.g. a non-size-constrained file in ~/.claude/)
  3. Handle JSON.parse failure on the credential blob gracefully — do not wipe existing tokens on parse error; instead attempt partial recovery or re-fetch only the missing fields

Workaround I applied to fix a secondary symptom (statusline not showing usage): added a regex fallback in ~/.claude/statusline-command.sh to extract the access token from the truncated JSON without relying on jq, since accessToken appears in the first ~200 bytes before the truncation point.

View original on GitHub ↗

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