In early 2026, security researchers scanning public repositories found 24,008 live API keys and credentials sitting inside Claude Desktop MCP configuration files. Stripe keys. OpenAI keys. Database passwords. All committed to version control, indexable by anyone with a search engine.
This wasn’t a supply chain attack. Nobody got hacked. Engineers just did what felt natural: they opened claude_desktop_config.json, pasted in their API key, and moved on. The file ended up in a dotfiles repo. The repo went public. The key never expired.
Multiply that by the MCP ecosystem and you get 24,008 known exposures — with an unknown number more in private repositories or on developer machines that haven’t been audited yet.
Why MCP configs are a secret leak waiting to happen
MCP servers are configured in JSON. To connect a server to an external API, you typically pass credentials directly:
{ "mcpServers": { "stripe": { "command": "npx", "args": ["-y", "@stripe/mcp"], "env": { "STRIPE_SECRET_KEY": "sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" } } }}This pattern is in every official MCP quickstart guide. It’s how the ecosystem bootstrapped itself. And it’s a fundamental security failure.
The problems compound:
-
Claude reads the config file directly. Claude Code is a filesystem agent running with your full user permissions. When debugging a broken MCP connection, Claude’s first instinct is often to read
claude_desktop_config.jsonto check the configuration. The credentials are right there, and there is no redaction layer — the raw value goes from file to context window to Anthropic’s API in a normal working session. -
The Bash tool runs as you — and reads everything. Claude Code can and does run shell commands like
env | grep STRIPE,cat .env, orecho $OPENAI_API_KEYwhile helping with debugging, deployment, or diagnosis. Every one of those commands prints credentials to stdout that Claude reads verbatim. If you’ve watched Claude read a session and seen keys appear, this is why. There is no secret detection, no warning. -
Credentials in args, not env. Many MCP configurations embed credentials in command arguments rather than the env block:
{"command": "npx","args": ["-y", "@modelcontextprotocol/server-postgres",}That connection string is a command-line argument. It shows up in
ps aux. Claude sees it when it reads the config file. -
Developers version-control their dotfiles.
.config/Claude/ends up in dotfiles repos that often go public at some point. -
No rotation pressure. A key sitting in a config file never throws an auth error unless you rotate it, so nobody notices until an incident.
-
Poorly written MCP servers echo credentials in responses. A server that returns authentication errors with the attempted key value, or logs debug output to stdout, hands Claude the credential in its tool response.
The 81% surge in AI credential leaks
The 24,008 MCP config exposures are part of a broader pattern. In 2025, AI-related credential leaks surged 81% year-over-year. The cause is structural: AI coding tools dramatically accelerate the pace at which credentials are created, configured, and — accidentally — shared.
When you’re moving fast with an AI assistant, the workflow becomes:
- Ask the agent to set up an integration
- The agent generates the config
- You paste in your key
- You ship
Step 4 is where things go wrong. The key ends up in your working tree, in your history, in your dotfiles. The AI helped you ship faster — and helped you leak faster too.
What zero-knowledge architecture changes
A zero-knowledge secrets vault inverts the model. Instead of storing credentials in config files, you store them in an encrypted vault. Instead of passing raw keys to the AI, you give the AI a tool that can use credentials without ever seeing them.
With OpaqueVault, the MCP configuration looks like this:
{ "mcpServers": { "opaquevault": { "command": "ov", "args": ["mcp", "serve"] } }}No credentials in the config. The ov mcp serve process holds a Key Encryption Key in memory, decrypts secrets on demand, and injects them as environment variables into subprocesses — never returning them to Claude’s context.
When Claude needs to run a Stripe API call, it calls vault_run with the command. The vault spawns the process with STRIPE_SECRET_KEY populated in its environment. Claude gets the command’s output and exit code. It never sees the key.
The no get_secret design principle
One detail worth understanding: OpaqueVault intentionally has no get_secret tool. Claude cannot ask the vault “give me the value of STRIPE_SECRET_KEY.” There is no such endpoint, server-side or client-side.
This is not an oversight. It’s the most important security invariant in the system. An MCP tool that returns plaintext secret values would mean every session of Claude — including future AI models with different threat models — could exfiltrate your credentials by calling that tool.
Instead, the MCP API surface is:
vault_run— run a command with secrets injectedvault_inject_env— spawn a shell with secrets in environmentvault_list_secrets— list secret names (not values)vault_create_secret,vault_update_secret,vault_delete_secret— lifecycle managementvault_status— health check
Metadata but no values. The AI can manage your secrets without ever reading them.
Fixing the 24,008 exposures
If you’re currently storing credentials in MCP config files, the migration path is:
- Audit your config file today. Open
claude_desktop_config.json(or your IDE’s equivalent). Every key in anenvblock is a credential that needs to move. - Rotate everything you find. Assume anything in that file has been read. Revoke and reissue.
- Move credentials to a vault. OpaqueVault is designed for this exact workflow.
ov secret create my-app/production/STRIPE_SECRET_KEYgets the credential out of your config and into encrypted storage. - Replace env blocks with vault_run calls. Tell Claude to use
vault_runto execute API-dependent commands rather than having raw keys available. - Check your dotfiles repo. If you version-control your config directory, audit the git history for any credentials that were committed, then purge them with
git filter-repo.
The 24,008 number is a snapshot of discovered, indexed exposures in public repos. The more pervasive risk is the one that never shows up in a scan: credentials that flow from config files and shell environments into Claude’s context window during ordinary working sessions — debugging, deployment, diagnosis — and from there to Anthropic’s API infrastructure. No commit, no leak detector, no SIEM alert. Just a normal day of development.
Claude Code is a capable filesystem agent running with your full user permissions. It reads files, runs shell commands, and inspects process environments. Credentials in claude_desktop_config.json or .env are not just a disk-persistence problem — they’re in active play every session. The ecosystem needs a better default. That default is zero-knowledge.
OpaqueVault is an MCP-native, zero-knowledge secret vault built for AI coding agents. Secrets are encrypted client-side with ML-KEM-768 + X25519 hybrid PQC. The server stores only ciphertext. Claude never sees plaintext values.