The core problem
MCP servers need API keys and credentials to function. Storing them in plaintext in settings.json or .mcp.json creates a real risk: accidental commit to Git, exposure in logs, or lateral movement after a compromise. The absolute rule is that secrets must never appear in versioned files.
Three approaches depending on context
| Approach | Security | Complexity | Ideal for |
|---|---|---|---|
| OS Keychain | High | Medium | Solo devs, macOS/Linux |
.env + .gitignore | Medium | Low | Small teams, prototyping |
| Secret Vaults | Very high | High | Enterprise, SOC 2, HIPAA |
Approach 1: OS Keychain (recommended)
The secret is encrypted at rest by the OS and accessible only to authorized processes. The MCP configuration exposes only a retrieval command, never the value.
# macOS: store a GitHub tokensecurity add-generic-password \ -a "claude-mcp" -s "github-token" \ -w "ghp_your_token_here"{ "mcpServers": { "github": { "command": "bash", "args": ["-c", "GITHUB_TOKEN=$(security find-generic-password -s 'github-token' -w) npx @github/mcp-server"] } }}Linux: use secret-tool (libsecret, GNOME Keyring or KWallet) with the same wrapper script logic.
Approach 2: .env + .gitignore
Simple and sufficient for solo use or a small team with good discipline.
# Create the secrets filecat > ~/.claude/.env << EOFGITHUB_TOKEN=ghp_your_tokenOPENAI_KEY=sk-your_keyEOFchmod 600 ~/.claude/.env # Restrictive permissionsecho ".env" >> ~/.claude/.gitignoreIn the MCP config, reference with "${GITHUB_TOKEN}" in the env field. Claude Code resolves environment variables at server startup.
Template for teams: commit mcp-config.template.json with placeholders, generate the real file via envsubst. Never commit the generated file.
Approach 3: Secret Vaults (enterprise)
HashiCorp Vault, AWS Secrets Manager, or 1Password CLI enable automated rotation, centralized auditing, and granular access control. The principle remains identical: a wrapper script retrieves the secret at runtime and exports it as an environment variable before launching the MCP server.
# HashiCorp Vaultexport GITHUB_TOKEN=$(vault kv get -field=token secret/claude/github)npx @github/mcp-serverWhat to never do
Hardcode a token directly in the command or args value of a versioned MCP server. Even in a private repo, secrets in plaintext in Git remain in the history even after deletion and can be exposed by a git log or unauthorized access.
A pre-commit hook that scans for token patterns (ghp_, sk-, Bearer ) before each commit is a simple additional protection to put in place.
Enter your email to read the full card and get the complete PDF bundle.
All content is free and open-source. We just ask for your email.