Where do MCP server credentials leak? Mostly from where they are kept. A typical MCP setup stores each server's API key in a plain-text JSON file in the user's home directory or project, or passes it as an environment variable to a local server process that runs with the user's full privileges. Remote servers may use OAuth instead, with tokens held by the client. Credentials leak when those files are committed or copied, when a malicious or compromised server package reads them, and when a server forwards a token it should never have accepted. The fixes are narrow tokens (or OAuth), secrets referenced rather than written, pinned server packages, and one gateway in front that holds the credentials so agents never do.
What is MCP explains the protocol; MCP gateways covers governing tool access in general. This guide is about the credentials.
Where MCP tokens live
| Setup | Where the credential sits | Who can read it |
|---|---|---|
| Local server (stdio), personal | ~/.claude.json for Claude Code1, ~/.cursor/mcp.json for Cursor2, as a literal or an environment variable | You, any process running as you, and the server itself |
| Local server, team-shared | .mcp.json (Claude Code) or .cursor/mcp.json (Cursor) in the repository12 | Everyone with the repository, if a literal key is committed |
| Remote server, static token | An Authorization or API-key header in the same config files | Same as above, plus the server |
| Remote server, OAuth | Tokens held by the client; Claude Code says they are "stored securely and refreshed automatically", with a pre-configured client secret in the macOS keychain or a credentials file1 | The client; the token is issued to that client |
Two details raise the stakes. First, these files are plaintext: Claude Code's ~/.claude.json holds app state, OAuth sign-in and personal MCP servers3, and old copies of it are kept in ~/.claude/backups/3. Second, local servers are programs, not configuration: the specification warns that MCP servers run with the same privileges as the client4. A local server you give a GitHub token to can also read your SSH keys.
GitGuardian's 2026 report counts 24,008 unique secrets exposed in MCP configuration files in public GitHub commits5. Those are the files above, committed by mistake.
Reference secrets, do not write them
Both major clients support references instead of literals. Claude Code expands ${VAR} and ${VAR:-default} in .mcp.json so teams can share configuration "while maintaining flexibility for … sensitive values like API keys", and in a remote server's URL and headers it reads some credential variables as empty so a project's file cannot send your credentials to a server it names1. Cursor supports ${env:NAME} interpolation and an envFile for stdio servers, and recommends "restricted API keys with minimal required permissions" and environment variables for secrets2.
{
"mcpServers": {
"issues": {
"type": "http",
"url": "https://mcp.example.com/mcp",
"headers": { "Authorization": "Bearer ${ISSUES_MCP_TOKEN}" }
}
}
}A reference keeps the key out of the repository; it does not keep it away from the server process, the agent's shell or anything else running as you. That is what the next sections address.
What the specification says
The MCP specification's Security Best Practices page names several credential attacks. Three matter most for teams running servers.
Token passthrough. An MCP server that accepts a token not issued to it, and forwards it to a downstream API, is an anti-pattern the authorization specification explicitly forbids4. The risks it lists: security controls that depend on the token's audience are bypassed, the server cannot tell clients apart, downstream logs show the wrong identity, and a token accepted by several services lets an attacker who compromises one reach the others4. The rule is short: "MCP servers MUST NOT accept any tokens that were not explicitly issued for the MCP server"4. If you build an MCP server in front of an internal API, give it its own credential for that API, and validate the audience of tokens clients present.
Local server compromise. The specification describes three attacks on local servers: a malicious startup command in a client configuration, a malicious payload inside the server, and an insecure server left listening on localhost reached through DNS rebinding4. Its example of a malicious startup command installs a package and posts ~/.ssh/id_rsa to a remote host4. Clients that offer one-click local server setup must show the exact command and require explicit approval, and should run servers sandboxed with minimal default privileges4. Claude Code, for example, asks for approval before using project-scoped servers from .mcp.json in interactive sessions1.
Scope minimization. An access token carrying broad scopes such as files:* or admin:*, stolen from a log or memory, "enables lateral data access, privilege chaining, and difficult revocation"4. The specification recommends a minimal initial scope set with incremental elevation when a privileged operation is first attempted, and lists wildcard scopes and bundled privileges as common mistakes4.
What malicious MCP packages have done
Two reported cases show both ways a server package can turn on its user.
postmark-mcp (2025): a server that abused the access it was given. Koi Security found that the npm package postmark-mcp, impersonating Postmark's MCP server, which Postmark publishes on GitHub rather than npm, added a backdoor in version 1.0.16 after 15 clean versions; it silently copied outgoing emails to an attacker-controlled address, as reported by The Register6. The package had about 1,500 downloads in a week before removal; Postmark said it "had absolutely nothing to do with this package", and the advice was to remove it, review email logs and rotate credentials sent by email6.
Mini Shai-Hulud (May 2026): a server package that stole the environment. The OSV advisory MAL-2026-4068 records malicious versions 0.2.8 and 0.3.8 of @antv/mcp-server-antv as part of a campaign in which a compromised npm maintainer account published 631 malicious versions across 314 packages in a 22-minute burst7. Each added a preinstall hook running an obfuscated script that, per the advisory, took AWS keys, GCP service accounts, Azure credentials, GitHub and npm tokens, Kubernetes tokens, SSH keys and database connection strings, and persisted through CI workflow injection and AI agent session hooks7. Nobody had to call a tool: installing the package was enough.
The first case is a reason to scope the token you give a server. The second is a reason to keep every other credential away from the machine that installs it. AI supply chain attacks in 2026 puts both in context.
Reducing the blast radius
- Prefer OAuth where the server offers it. A token issued to one client, with narrow scopes and an expiry, revocable per client, beats a long-lived personal access token pasted into JSON. It does not make a stolen token harmless; it makes it smaller and shorter-lived.
- Use the narrowest token the server accepts. Read-only tokens or read-only server modes when the agent does not need to write; one token per server; never an organization-admin token.
- One token per person and per server. Shared tokens make revocation break everyone and make audit trails useless.
- Pin and verify server packages. Install servers from the vendor's own publication, pin exact versions, read changelogs before upgrading, and treat a server like any other dependency in your supply chain review.
- Review startup commands.
npx some-package@latestin a config file runs whatever was published last. - Run local servers with less. Containers or the client's sandbox, without access to
~/.ssh, cloud credential files or unrelated environment variables. Coding agents and secrets has the Claude Code sandbox settings. - Keep secrets out of config files with references, and scan for them before push.
- Rehearse revocation. For each server, know where its token is issued and how fast you can revoke it.
Put one gateway in front
The structural fix is to stop distributing server credentials to every laptop and agent. A gateway registers each MCP server once, with its credential, and agents reach the server through the gateway with their own identity. The credential lives in one sealed place; revoking an agent's access does not require rotating the server's token; and one log records which agent called which tool.
A gateway does not remove the risks inside the tool call. A server you trust can return content that steers the model, and a granted tool can still be misused by an agent following injected instructions. Least-privilege grants, read-only credentials and human review of writes remain necessary.
How INFRO's MCP gateway handles server credentials
INFRO's MCP gateway, on the Business plan, puts INFRO between your agents and the MCP servers they use. As documented in its security model:
- Server credentials are sealed. An owner or admin registers a server with up to eight header values, such as a bearer token; they are encrypted at rest with AES-256-GCM, bound to the organization and the server, and never returned by any API, console screen, log, error or trace. Only header names are shown.
- The agent never holds the server credential, and the server never sees the INFRO key. Agents call with their INFRO gateway key; INFRO adds the server's headers to each call, and strips the agent's
AuthorizationandX-INFRO-*headers — no passthrough in either direction. - Access is denied by default. A gateway key with no grant sees no tools; a grant gives one key one server in a chosen mode, and only tools an admin approved are served. A tool whose definition changed on the server since approval is refused.
- Guardrails apply to tool calls. A
secretrule withblockon the input stage refuses tool calls whose arguments contain a credential, a common sign of an agent being steered into sending one out. - Every call is recorded with metadata (server, tool, outcome, sizes, key, project, environment) on the same trace as the agent's model calls; contents only if you opt in.
Two limits, stated plainly. Servers that only accept OAuth sign-in, such as Notion, Vercel and Asana, cannot be registered yet; INFRO's gateway takes servers with no authentication or a static header credential. And INFRO governs calls that go through it: a server token that also sits on a laptop, or is stolen from anywhere, can be used directly against the server. Register with a dedicated, narrowly scoped token, keep it nowhere else, and keep revocation at the issuing service as the backstop.
Summary
MCP credentials leak from where they are kept: plaintext JSON, inherited environments and local server processes with full user privileges. Follow the specification — no token passthrough, consent before running local servers, minimal scopes — and learn from the real cases: a server package can abuse the access you give it, or steal everything else on the machine. Use OAuth or the narrowest token, reference secrets instead of writing them, pin server packages, and centralize credentials in one gateway so agents never hold them.
