Skip to content

Coding agents · MCP

API keys in MCP config files: the risk and the fix

Short answer

An API key typed into an MCP config file, such as Claude Code’s .mcp.json or Cursor’s mcp.json, sits in plain text on disk, often in a project folder that is committed to git, synced or read by the agent itself.

Claude Code and Cursor can both read keys from environment variables instead, so the config file holds only a variable name. Where an MCP server supports OAuth sign-in, you may not need a key at all.

Tools connected to a server

The risk

Why API keys in MCP config files leak

MCP config files are built to be shared and read, which makes them a poor place for a live API key.
Source code on a monitor

MCP (Model Context Protocol) servers connect a coding agent such as Claude Code or Cursor to outside tools. When a server needs an API key, its config file is the tempting place to put it, and a risky one:

  • Project-level config is meant to be shared. Cursor’s documentation suggests committing a project’s .cursor/mcp.json so teammates get the same tools, and Claude Code’s .mcp.json is designed for teams too.
  • The agent works inside the same folder, so it can read the config file unless you block it.
  • Config files end up in backups, synced folders, screenshots and screen shares.
  • The MCP server receives the key whatever you do, so a key with broad permissions gives the server broad power.

Compared

Where to keep an MCP server’s API key, compared

Four ways to hand an MCP server its credential, compared on git safety, agent access and our overall verdict.
Options for giving an MCP server its credential
OptionCommitted to git?Readable by the agent?Verdict
Key typed into project config Often Yes Avoid
Key typed into your global user config No Possibly Better, but still plain text on disk
Environment variable reference No Only through commands that read the environment Recommended
OAuth sign-in, where the server supports it No There is no long-lived key to read Best when available

Claude Code

The fix in Claude Code: ${VAR} in .mcp.json

Claude Code can fill in environment variables when it reads .mcp.json, so the file never needs the key itself.

Claude Code expands ${VAR} and ${VAR:-default} in a project’s .mcp.json, in the command, args, env, url and headers fields. If a variable is not set and has no default, Claude Code warns you and leaves the text unexpanded.

.mcp.json (Claude Code): safe to commit, because it holds no key
{
  "mcpServers": {
    "payments": {
      "type": "http",
      "url": "https://mcp.example.com/mcp",
      "headers": { "Authorization": "Bearer ${PAYMENTS_MCP_TOKEN}" }
    }
  }
}

Cursor

The fix in Cursor: ${env:NAME} in mcp.json

Cursor resolves environment references in mcp.json too, with one catch about where it reads the variables from.

Cursor resolves ${env:NAME} in the command, args, env, url and headers fields of mcp.json. Local (stdio) servers can also load an envFile; remote servers cannot.

Cursor reads environment variables from its own process. If you start it from a desktop icon, variables set only in your shell profile may be missing; restart Cursor after changing them.

.cursor/mcp.json (Cursor): the key comes from the environment
{
  "mcpServers": {
    "payments": {
      "command": "node",
      "args": ["path/to/payments-mcp-server.js"],
      "env": { "PAYMENTS_API_KEY": "${env:PAYMENTS_API_KEY}" }
    }
  }
}

Clean-up

What to do if a key is already in a config file

If a key has already sat in a config file, treat it as exposed and work through these four steps.
A person beside a locked safe
  1. Revoke it

    Assume the old key is exposed. Create a new key at the provider and delete the old one.

  2. Narrow the new key

    Give the new key only the scopes the MCP server needs.

  3. Swap in a variable

    Replace the value in the config with ${VAR} (Claude Code) or ${env:NAME} (Cursor).

  4. Look for copies

    Check git history, other branches, backups, shared snippets and screenshots for the old value.

Argoned

Where Argoned fits

Argoned helps one step before the config file: getting the key from its owner to you, sealed all the way.
A phone sharing a link

Argoned does not inject keys into MCP servers today. It helps one step earlier: when a client or colleague creates the key, they send it to you as a sealed link, an encrypted link that opens once, instead of pasting it into a chat or an email. You open it, set the environment variable, and the key never lands in a file you share.

FAQ

Questions

Short answers about committing MCP config files, empty variables in Cursor, what the server can see and Argoned’s role.
Two people beside a large question mark
Is it safe to commit .mcp.json or .cursor/mcp.json?

Yes, if it contains only variable references and no keys or tokens.

Why does my ${env:NAME} variable come through empty in Cursor?

Cursor only sees variables that exist in its own process. If you launched it from a desktop icon, start it from a terminal or set the variable system-wide, then restart Cursor.

Does an environment variable hide the key from the MCP server?

No. The server needs the key to work. Environment variables keep the key out of files and chats; a narrowly scoped key limits what the server can do with it.

Can Argoned put keys into my MCP config?

Not today. A sealed link is opened in a web browser by a person. Open it, then set the environment variable yourself.

Next

More guides on handing logins and API keys to AI agents and people without pasting them in plain text.
A person beside a locked laptop

Your agent needs a login. Send it sealed.

Seal a login or API key in your browser and send a link instead of the password.

£0 · Free plan, no card needed · Encrypted in your browser