- It reads them. It never writes, refreshes, rotates or deletes them.
- It never transmits a token anywhere except to the MCP server that issued it.
- It never caches a token, on disk or in memory across calls. Every call re-reads the credential store, because Claude Code refreshes on its own schedule and a cached token goes stale silently.
- It never prints one.
agent-codemode serversshows a name, a transport and an expiry — never secret material. - An expired token raises a loud error. It never degrades into an empty result.
This is the part to read before you install anything, including this.
On macOS, Claude Code stores every MCP OAuth token in a single Keychain item —
service Claude Code-credentials. That item is readable, without a prompt, by
any process running as you that Claude Code itself could have spawned. That
includes this package. It also includes every hook you have configured, every
MCP server that runs as a local subprocess, every npx package one of those
pulls in, and every shell command an agent decides to run.
So: every MCP token you hold — production included — is readable by any process started from a Claude Code session. That is a property of how the credential store works, not something this package introduces. Removing this package does not change it. This package is simply an honest, readable demonstration of it.
Two practical consequences:
- Treat your agent's MCP server list as a blast radius. A server you
authenticate for convenience is a server any code your agent runs can reach
with your identity. If
hyre-prodis onenpxaway from an untrusted postinstall script, that is worth knowing deliberately rather than discovering later. - Prefer read-scoped tokens where the server offers them. Most MCP servers issue one token for everything they can do. Where a server distinguishes scopes, take the narrow one.
This package adds no network listener, no daemon, no background process, and no persistent state. It runs, reads a credential, makes one JSON-RPC call to the server that credential belongs to, and exits.
The one capability it adds that you did not already have is convenience —
calling those servers without a model deciding to. That is the point, and it is
also the thing to be deliberate about: a script with your Linear token can close
tickets at 3am with no one reading the diff. Write your gates so that the
destructive path is explicit, the way
examples/standup.ts keeps sending behind --post.
Open a GitHub issue for anything non-sensitive. For something you would rather not post publicly, email jan@wilmake.com.