MCP Forge

How to handle secrets in MCP servers safely

A practical guide. Updated 2026.

Most MCP servers need a secret of some kind: an API key for the service they wrap, a database password, a signing key. The fastest way to get one working is to paste the key straight into a config file or hard-code it next to the fetch call. That is also the fastest way to leak it. Config files get committed, copied into chat, and synced to backups. This guide covers where secrets should actually live, how to keep them out of URLs and logs, and how to lock down the files that hold them.

Stop pasting keys into client config

When you register a stdio server with Claude Desktop or Cursor, the client config lets you set environment variables for the process. That is the right place to inject a secret, because the value is passed to your server at launch and never has to live in your source tree. Reference an environment variable on the host, do not inline the literal key.

{
  "mcpServers": {
    "billing": {
      "command": "node",
      "args": ["/opt/billing-mcp/server.js"],
      "env": { "STRIPE_API_KEY": "${STRIPE_API_KEY}" }
    }
  }
}

The catch: this client config file itself often sits in a synced or backed-up location (~/Library/Application Support/Claude/ on macOS, for example). If you inline the real key there, it rides along to every backup. Keep the literal value in your shell or a secrets manager and let the config reference it.

Read secrets from the environment, never from code

Inside the server, pull each secret from the environment and fail loudly if it is missing. A server that silently starts with no key will hit a confusing 401 later; one that exits at boot tells you exactly what is wrong.

const apiKey = process.env.STRIPE_API_KEY;
if (!apiKey) {
  console.error("STRIPE_API_KEY is not set");
  process.exit(1);
}

For local development, a .env file loaded by something like dotenv is fine, but it must be git-ignored. Add it before you write a single line that reads it:

echo ".env" >> .gitignore

Keep secrets out of URLs and query strings

A surprising number of leaks come from putting a token in a URL instead of a header. URLs are not private. They land in server access logs, proxy logs, browser history, and the Referer header sent to the next site. If your MCP tool calls an upstream API, send the secret in an Authorization header, not as ?api_key=....

// avoid
await fetch(`https://api.example.com/v1/data?api_key=${apiKey}`);

// prefer
await fetch("https://api.example.com/v1/data", {
  headers: { Authorization: `Bearer ${apiKey}` }
});

Lock down file permissions

Any file that holds a secret (a .env, a service-account JSON, a private key) should be readable only by the user that runs the server. On a shared box, world-readable secrets are a one-line exfiltration. Set the mode explicitly and check it.

chmod 600 .env
ls -l .env
# -rw------- 1 you you 142 .env

The same applies to the directory: chmod 700 the folder so another user cannot list or traverse into it. If you write secrets at runtime, set the mode at creation time (fs.writeFile(path, data, { mode: 0o600 })) rather than relying on a later chmod that might never run.

Never log a secret

Logging is where secrets quietly escape. A debug line that dumps the request config, an error handler that prints the full upstream response, or a crash that serializes the whole environment will all spill keys into log files and observability tools that a dozen people can read. Be deliberate about what you print.

const mask = (s) => s ? `***${s.slice(-4)}` : "(unset)";
console.log("auth ok for key", mask(apiKey));

Catch the mistakes before you ship

These rules are easy to state and easy to forget under deadline. A plaintext key slips into a committed config, a .env goes out world-readable, a token sneaks into a sample URL. The free mcp-audit tool scans an MCP server project for exactly these patterns, including plaintext secrets in config and code, so a leak shows up in review instead of in someone else's logs. Run it in CI and on every config you hand to a client.

Ship secrets the safe way by default

MCP Forge Kit comes wired for secrets done right: environment-based config, git-ignored env files, header-based upstream auth, redacted logging, and a CI step that runs mcp-audit so a leaked key never makes it past review.

Get MCP Forge Kit, €39

Related: Authentication · Deploy securely · Security checklist