anthropic
MCP OAuth tokens sit in plaintext on Linux
Promtime
anthropicClaude Code 2.1.257 stores MCP OAuth access tokens on Linux in plaintext JSON, in ~/.claude/.credentials.json protected by mode 0600 and nothing else, according to SecretSpec. The check followed authentication to several remote MCP servers, while Anthropic's MCP documentation says authentication tokens are "stored securely".
At a glance
- The file carries a top-level mcpOAuth object in which each entry holds accessToken, clientId, discoveryState, redirectUri, serverName and serverUrl; one redacted example covers a cloudflare-observability server.
- Anthropic's credential-management documentation describes an encrypted Keychain on macOS with a file fallback, the same plaintext file on Linux, and on Windows a profile-directory file inheriting that directory's access controls.
- Codex documents auto, file and keyring backends for the same job, so a plaintext credential file is not the only option available to Linux users of that CLI.
The gap here reads as a wording problem as much as an engineering one: "stored securely" in the MCP documentation covers a Keychain on one platform and file permissions on another, and the difference only surfaces when someone opens the file. A bearer token that survives a restart has to live somewhere, and on a shared or backed-up Linux workstation the blast radius of a readable home directory likely extends to every remote MCP server the user has approved.
Claude Code 2.1.257 keeps MCP access tokens in a 0600 JSON file
SecretSpec checked Claude Code 2.1.257 after authenticating to several remote MCP servers. The credentials file had mode 0600, as it should, and alongside the rest of its contents it carried a top-level mcpOAuth object holding the access tokens for those servers.
One published entry, with every credential value redacted, keys a cloudflare-observability server and stores accessToken, clientId, discoveryState, redirectUri, serverName and serverUrl in the same object. That layout matches Anthropic's credential-management documentation, which describes the macOS Keychain, a fallback to the same file, and a Windows path under the user profile directory.
The browser flow makes the secret easy to miss: a user runs claude mcp login, approves access, and returns to a connected server without ever copying a token from a dashboard. Claude Code still receives a credential and has to persist it for the connection to survive a restart.
OAuth standardizes delegation and renewal, while Codex makes the storage backend configurable
OAuth lets Claude Code discover the authorization server, request specific scopes, complete the authorization-code exchange, refresh an access token and revoke the grant. Anthropic's MCP documentation also lets users pin the scopes Claude Code requests. What the protocol does not specify is a secure local vault.
SecretSpec's comparison puts OAuth credentials and scoped API tokens in the same column on the properties that matter for storage: both must be persisted by the client, both can be scoped and expire, both can usually be revoked independently, and both can be replayed if stolen. OAuth adds standard interactive delegation and, often, standard automatic renewal.
Codex documents auto, file and keyring backends for MCP OAuth storage, where mcp_oauth_credentials_store = "keyring" selects the system keyring. SecretSpec describes that as narrower than a general secret-provider interface, but enough to keep a plaintext credential file from being the only option on Linux.
SecretSpec 0.20 ships 33 provider integrations behind one interface
SecretSpec proposes that Claude Code's persistence layer become replaceable, with the OAuth client calling a credential-store interface backed by the SecretSpec TypeScript SDK and whichever provider the user or organization selected. The SDK embeds the Rust resolver, so the TypeScript side needs no bespoke code for each backend.
Version 0.20 carries 33 provider integrations covering local keyrings, password managers, encrypted files, cloud secret managers and deployment targets, with providers declaring their capabilities so a credential store can require readable and writable storage. A user can set the system keyring as the default with secretspec config global init --provider keyring --profile default.
SecretSpec has applied the same idea to other tools, shipping Git and Docker credential helpers and proposing a generic, operation-scoped secret resolver interface for Nix. Those contributions are possible, it says, because the projects are open source and their credential boundaries can be inspected and changed from outside.
Only Anthropic can change this path
Claude Code's public repository does not include the core CLI implementation and its license is all rights reserved, so an upstream patch to the MCP OAuth storage path is not available to outside contributors. SecretSpec says its 0.21 release is being built around versioned resolver and provider IPC, letting applications use its providers over a local protocol without embedding an SDK; no date has been given for that release.
Comments
No comments yet. Be the first.
Join the conversation
Sign in with Google to leave a comment. Your name and avatar come from your Google profile, and the comment appears after moderation.
We only use your name and avatar from Google. We never store your email address.
