Secrets Management for MCP Configs
Patterns for keeping API keys and tokens out of plain-text JSON files: env-from-shell, 1Password CLI, macOS Keychain, and per-machine wrappers.
Detailed Explanation
The Plain-Text JSON Problem
By default, every MCP config holds secrets in cleartext: GitHub PATs, database passwords, OpenAI API keys, BRAVE_API_KEYs. The file lives in your home directory, syncs through cloud backups (iCloud, Dropbox), and might end up in time-machine snapshots. There are better options.
Pattern 1: Reference shell env vars (Cursor only)
Cursor expands ${VAR_NAME} syntax in env values:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_PAT}" }
}
}
}
Set GITHUB_PAT in your shell profile or via direnv. Claude Desktop does not expand these — it treats them as literals.
Pattern 2: 1Password CLI wrapper
Wrap the server command in op run:
{
"mcpServers": {
"github": {
"command": "op",
"args": [
"run",
"--env-file=/Users/me/.config/mcp/github.env",
"--",
"npx",
"-y",
"@modelcontextprotocol/server-github"
]
}
}
}
Where github.env contains GITHUB_PERSONAL_ACCESS_TOKEN=op://Personal/GitHub-MCP/credential. The 1Password CLI fetches the secret at launch time and never writes it to disk.
Pattern 3: macOS Keychain wrapper
Write a tiny shell script that pulls the secret from security find-generic-password and execs the real command:
#!/usr/bin/env bash
export GITHUB_PERSONAL_ACCESS_TOKEN="$(security find-generic-password -s github-mcp -w)"
exec npx -y @modelcontextprotocol/server-github
Point the MCP config at the script:
"command": "/Users/me/.local/bin/launch-github-mcp.sh",
"args": []
Pattern 4: gitignore + restricted file perms
The pragmatic minimum:
chmod 600 ~/Library/Application\ Support/Claude/claude_desktop_config.json
echo ".cursor/mcp.json" >> .gitignore
Use the secret redactor before sharing snippets in chat or bug reports.
Auditing what's in your config
Run grep -E '(token|key|password|secret)' ~/.cursor/mcp.json periodically to catch accidentally-committed plaintext.
Use Case
Anyone using MCP with non-trivial credentials (write-scoped GitHub tokens, production database read replicas, paid API keys with billing implications) should adopt at least pattern 4, and preferably 2 or 3.