Open the config file your AI client uses to connect to MCP servers and you’ll probably find at least one secret sitting in it, unencrypted, readable by anything with file access on your machine. That’s not a bug in any one client — it’s how the ecosystem’s most common setup works by default, and it’s exactly the risk the OWASP MCP Top 10 lists first, as MCP01, Token Mismanagement & Secret Exposure. Here’s what’s actually sitting in those files, who can read it, and what changes if something other than a text file holds the secret instead.
What’s actually in the file
A local MCP config — claude_desktop_config.json, .cursor/mcp.json, or whatever your client calls it — lists each server under an mcpServers key. For a server that authenticates with an API key instead of OAuth, the key itself usually sits right there as a header value:
{
"mcpServers": {
"some-server": {
"url": "https://example.com/mcp",
"headers": { "Authorization": "Bearer sk_live_a1b2c3..." }
}
}
}No encryption, no OS keychain, just a string in a JSON file next to your other settings. For a local, stdio-run server, the same file often carries the equivalent in an env block — a database URL, a cloud provider key, whatever environment variable that server’s process needs on startup. It’s the simplest thing that could work, and for a single person running a handful of servers on a laptop they control, it’s arguably fine. The problem is that this exact pattern is also what ships when a server moves from one laptop to a team.
OAuth doesn’t fully get you out of this
If a server uses OAuth instead of a static API key, the config file itself usually holds only a URL, which looks safer. But the access and refresh tokens that OAuth hands back still have to live somewhere on disk between sessions, and what that grant actually covers is often broader than the one task you connected it for. Exactly where a given client caches those tokens, and how well-protected that location is, varies by client and isn’t something most people check. The result is the same underlying question either way: if someone got read access to your filesystem, how many connected accounts could they act as, and for how long before a token expires or gets revoked?
Why this is worse than “don’t commit secrets to git”
The standard advice for API keys — keep them out of version control, rotate them, use a secrets manager — was built around keys that live in one application’s deployment environment. MCP config files break that model in a few specific ways:
- One file, many secrets. A single config can hold the credentials for every server you’ve connected — your issue tracker, your CRM, your cloud provider — so one exposed file is a multi-account incident, not a single-key one.
- It travels with dotfiles. People back up, sync, or version-control their dotfiles without thinking of an MCP config as one of the sensitive ones, because it doesn’t look like a
.envfile. A dotfiles repo pushed to a personal GitHub account, even a private one, is a secret dump waiting to be noticed. - It’s per-client, per-machine. The same server connected in Claude, Cursor, and a second laptop means the same credential copied into three separate plaintext files, each one a separate place it can leak from.
- Revoking it is manual. If you suspect a key leaked, you’re rotating it at the source and then hunting down every config file that referenced the old one — there’s no single place that lists every server a given key is wired into.
What actually reduces the exposure
A few things help regardless of what tooling you use. Treat your MCP config file like the .env file it functionally is: never commit it, exclude it explicitly in your dotfiles repo, and check whether your OS full-disk or file-level encryption is actually on, since that’s the main thing standing between a stolen laptop and a readable config. Prefer OAuth over a static API key when a server offers both — a real OAuth flow at least gives you expiry and a revocation path that a bare key doesn’t. And keep an actual list, somewhere, of which key lives in which config file, because the moment you need to rotate one under pressure is the worst time to be reconstructing that list from memory.
None of that changes the underlying architecture, though: the secret still has to exist in plaintext on disk at the moment your MCP client reads it, in as many copies as you have clients and machines.
The bottom line
MCP’s simplest connection path — a key or token pasted into a local config file — is also its most exposed one, and it’s exposed in ways that standard secrets-management advice doesn’t fully cover, because the file holds several accounts’ worth of access and travels with your dotfiles rather than a deployment pipeline. Know what’s in your config before you back it up or sync it anywhere, and treat every entry in it as a live credential, because that’s exactly what it is. See gate’s MCP security hub for the rest of this series.