Why Every MCP Setup Guide Is Teaching You to Store API Keys Wrong
Source: Dev.to
The Problem with Storing MCP Credentials in Plain‑Text Files
If you have set up Claude Desktop, Cursor, or any MCP server in the last six months, you probably followed a guide that told you to add something like this to a JSON config file on your filesystem:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxxxxxxxxxx"
}
}
}
}
That file (e.g., claude_desktop_config.json or ~/.cursor/mcp.json) now contains your actual token in plain text.
Why this is a serious security mistake
- Plain‑text credential – any process on your machine can read the file.
- AI assistant exposure – the assistant reads the config to start the MCP server, so the token ends up in the assistant’s context for every session.
- Prompt injection risk – a malicious prompt could ask the assistant to “show me my MCP config file,” and the assistant will happily reveal the token.
- Filesystem access – any tool with file‑system permissions can read the file.
- Version‑control leakage – the file can be accidentally committed to a repository.
This isn’t hypothetical. Check Point Research documented API‑key exfiltration through AI coding tools in CVE‑2026‑21852. The attack surface is exactly this: credentials stored in files that AI agents can read.
The guides are correct about how MCP works (environment variables in the config file are how MCP servers receive credentials).
What they don’t address is how to keep those credentials secure.
A Better Approach: Reference Environment Variables
Instead of embedding the token directly, reference a variable that lives in your shell profile:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
}
}
}
}
Add the variable to ~/.zshrc or ~/.bashrc:
export GITHUB_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxx"
- Pros: The token is no longer in the MCP config file.
- Cons: It’s still stored in a shell‑profile file that AI agents can read, and it remains in the process environment where any child process can access it.
The Real Solution: AgentSecrets
AgentSecrets runs as an MCP server that gives your AI assistant access to credentials without ever writing the values to a file.
Installation
brew install The-17/tap/agentsecrets
Store credentials securely in the OS keychain
agentsecrets secrets set GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
Connect to your AI tools
agentsecrets mcp install
The last command auto‑configures both Claude Desktop and Cursor. Your config file now looks like this:
{
"mcpServers": {
"agentsecrets": {
"command": "agentsecrets",
"args": ["mcp", "serve"]
}
}
}
- No token values appear in the config.
- No credentials are stored in any file or environment variable that the AI can read.
How it works at runtime
- Agent: “I need to call the GitHub API to check this repo.”
- AgentSecrets MCP: routes the request through a proxy.
- Proxy: retrieves
GITHUB_TOKENfrom the OS keychain, injects it into the request. - Agent: receives the API response.
The token never enters the agent’s context. It lives only in the OS keychain (macOS Keychain, Windows Credential Manager, or Linux Secret Service), which requires system‑level authentication to access.
Why the OS keychain is safe
| Property | Explanation |
|---|---|
| Access requires authentication | Reading may need biometric (Touch ID) or system password; not just an app password. |
| Isolated per‑application | Other processes cannot read entries without explicit user consent. |
| Not a file | No path to accidentally commit or expose; the data is stored in a protected system store. |
| AI agents cannot read it | The assistant has no direct access to the keychain, guaranteeing confidentiality. |
Resulting configuration (safe to share)
{
"mcpServers": {
"agentsecrets": {
"command": "agentsecrets",
"args": ["mcp", "serve"]
}
}
}
- You can commit this file to version control, share it with teammates, or even publish it publicly—no credentials are exposed.
- Your real credentials remain in the OS keychain, synced (zero‑knowledge encrypted) to AgentSecrets’ cloud, and are only ever used by the proxy at transport time.
TL;DR
- Storing API keys directly in MCP JSON configs is a critical security flaw.
- Referencing shell environment variables is a step forward, but still leaves the secret exposed.
- AgentSecrets eliminates the problem by keeping credentials in the OS keychain and providing them to AI tools only when needed, never storing them in files or environment variables that the AI can read.
Adopt AgentSecrets today to protect your tokens while still enjoying seamless AI‑assisted development.
Fig File Problem Gets Worse on Teams
Sharing MCP configurations across a development team means sharing the methods by which credentials are stored — and too often that means sharing the credentials themselves.
With AgentSecrets Workspaces
-
Team lead sets up workspace
agentsecrets workspace create "Acme Engineering" agentsecrets secrets set GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx -
New developer joins
agentsecrets login agentsecrets workspace switch "Acme Engineering" agentsecrets secrets pull agentsecrets mcp install
The new developer’s MCP setup is complete.
- No credentials were shared in Slack.
- No
.envfile was sent via email. - No one typed a token into a chat window.
The credentials moved from the workspace to the local OS keychain encrypted end‑to‑end.
When You Set Up Any MCP Server That Needs Credentials
- Do not put token values in the config file.
- Do not put token values in environment variables in the config.
- Store the credential with
agentsecrets secrets set.
agentsecrets mcp install
- Reference key names only — never values.
- The config file is for configuration.
- The OS keychain is for credentials.
These are different things and they should live in different places.
Every MCP setup guide that shows you a JSON file with an actual token value in it is showing you how to make it work, not how to make it secure. Now you know the difference.
References
- GitHub:
- Documentation: