Keychain: Claude Code-credentials-* items created daily and never cleaned up (75 items, 1156 duplicated OAuth tokens)
Keychain: a new Claude Code-credentials-<hash> item is created ~daily and never cleaned up (75 items, 1,156 duplicated OAuth tokens)
Environment
- Claude Code 2.1.222, Node v26.6.0, macOS 26.6 (arm64)
CLAUDE_CONFIG_DIR=~/.local/claude(launchd-injected)
Summary
Claude Code creates a new macOS Keychain generic-password item named Claude Code-credentials-<8 hex> on a roughly daily cadence, and never removes the previous ones. Each item holds a complete copy of the mcpOAuth blob — every MCP server's OAuth access token. Old copies are retained indefinitely.
Observed
total "Claude Code" keychain items : 75
distinct creation days : 70
date range : 2026-04-16 → 2026-08-05 (111 days)
creation time : ~11:07 UTC, one per day
size per item : ~6.3 KB, 15–17 mcpOAuth entries each
total duplicated token copies : 1,156
The live store (Claude Code-credentials-8dfec7ce, created 27 May, rewritten on each auth) held 20 entries; the daily items held 15–17 — i.e. they are stale divergent snapshots, not current copies. The token counts drift across snapshots rather than growing monotonically, which argues against this being an intentional backup or history feature.
Steps to reproduce
Run Claude Code daily for a period, then:
security dump-keychain | grep -c 'Claude Code-credentials'
Why this matters
- Revocation doesn't revoke. MCP OAuth tokens are long-lived (
expiresAt: 0, no refresh for most). Every historical token ever issued remains recoverable from the Keychain, so re-authenticating or rotating a token does not retire the old one. - Blast radius. These are production SaaS credentials — a mix of first-party and third-party integrations. A single Keychain compromise exposes ~4 months of credential history rather than the current set.
- Unbounded growth. ~1 item/day with no cleanup path, and no UI that surfaces it.
Mitigating context: the login Keychain is encrypted at rest and unlocked by the user's login, so this is a blast-radius and hygiene problem rather than an open door.
Likely cause (inferred, not confirmed)
The service-name hash appears to vary with time rather than being derived from a stable input such as the config-dir path. A stable key would produce one item that gets overwritten; a time-varying key produces exactly this pattern — a new row per day and no overwrite.
Suggested fixes
- Derive the Keychain service name from stable input (config dir / install identity) so the item is updated in place.
- On write, delete superseded
Claude Code-credentials-*items. - Ship a cleanup path for users who have already accumulated them.
Workaround
Delete all but the live item (identify it as the one whose modification time updates when you authenticate an MCP server):
security delete-generic-password -s "Claude Code-credentials-<hash>"
Back the secrets up first. I pruned 73 items this way with no ill effects; MCP servers continued working.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗