Quick answer: API keys stored directly in .cursor/mcp.json, .claude/mcp.json, or any MCP config file are readable by every process with filesystem access, frequently end up in git history, and are shared across every agent using that config — with no audit trail. The fix is to replace raw keys in the config with a single scoped bearer token pointing at a credential broker. The config stays in the file; your real service credentials don't.
Why MCP config files are a different risk than .env
The .env file is a well-understood risk — developers know it shouldn't be committed. MCP config files are newer, less scrutinised, and carry habits that don't translate.
A few things that make them worse in practice:
They're designed to be shared. Teams share Cursor settings via dotfiles repos, Slack snippets, and onboarding docs. Those sharing habits were formed when config files held editor preferences, not Stripe keys.
They're not in .gitignore by default. A .env in a new project is gitignored by most scaffolding tools. A .cursor/mcp.json is not — it requires a deliberate choice, and that choice isn't always made.
They cover every agent using that machine. One .cursor/mcp.json applies to every Cursor agent, every project, every task. A key in that file gives every agent running on your machine access to whatever that key permits.
They're outside your application's security boundary. Your app's .env is at least scoped to one project directory. MCP config files often live in your home directory, readable by every process running under your user.
The four specific risks
1. Git commits. The most common path. A developer adds their MCP config to a dotfiles repo — reasonable for editor settings, catastrophic for API keys. GitHub will flag it, but by then the key has been in the remote history and is no longer safe.
2. Conversation context leakage. Some agents include their configuration in the context they send to the model provider. A raw API key in your MCP config can end up in a prompt that gets logged, cached, or used for training depending on your provider's settings. For more on this risk pattern, see what happens when you paste an API key into an agent's prompt.
3. Shared blast radius. One key in the config grants the same access to every agent using it — a Cursor agent refactoring your codebase has the same Stripe permissions as one explicitly building a payment flow. A single compromised or misbehaving agent touches everything the key permits.
4. No audit trail. Credentials used directly from a config file don't produce per-agent logs. When your Stripe dashboard shows an unexpected API call, you can't tell which agent made it, what it was doing, or whether the call was authorised.
The fix: a scoped token, not a raw key
The pattern that solves all four risks:
- Your services (Stripe, GitHub, Vercel) are connected once to a credential broker — encrypted at rest, not in any config file.
- Each agent gets its own access profile: a named identity and a list of which services it's allowed to reach.
- Your MCP config holds a single scoped bearer token for that agent, plus the broker's MCP endpoint URL. That's all.
When the agent calls get_credential, the broker validates the token, checks the access policy, logs the request against the named agent identity, and returns the credential — or denies the request if the agent isn't in scope.
Your config file now contains something like this:
{
"mcpServers": {
"gazebo": {
"url": "https://app.gazebohq.com/api/mcp",
"headers": {
"Authorization": "Bearer ag_••••••••"
}
}
}
}
The token ag_•••••••• identifies the agent. It has no service access on its own — the broker decides what it can reach based on the access profile. If someone extracts the token from your config, they get an agent identity with a defined, revocable scope, not a direct Stripe key.
Replacing raw keys in your MCP config: step by step
Step 1 — Connect your services to Gazebo. Sign in and navigate to Services. Connect the services your agent needs — Stripe, GitHub, Vercel, whatever applies. Each service is connected once and stored encrypted. Your raw API keys go in here and stay here.
Step 2 — Create an access profile. In Agents → New, name the profile after the agent (e.g. cursor-main or claude-code-work). Select only the services this agent actually needs. Gazebo generates an MCP endpoint URL and a bearer token.
Step 3 — Update your MCP config. Replace the raw service keys in your config with the MCP endpoint URL and the bearer token. Remove the raw keys from the file entirely — don't leave them commented out.
Step 4 — Remove the old keys from git history. If your config file was ever committed with real credentials, rotate those keys immediately — they're already compromised. Going forward, your MCP config is safe to commit because it contains no raw credentials.
For a full walkthrough specific to Cursor, see how to set up Cursor with Gazebo. The same pattern applies to Windsurf — see Windsurf agent credentials for the Cascade-specific setup.
What you gain
Surgical revocation. Deleting an access profile immediately invalidates the agent's token. Your underlying service keys are untouched — no rotation, no propagation, no broken pipelines. For more on why revocation is the right primitive for agents, see API key rotation vs. revocation.
Per-agent audit logs. Every get_credential call is logged against a named agent identity. When something unexpected happens, you know which agent called what, at what time, for which task.
Scope containment. A Cursor agent scoped to read-only GitHub access can't reach your Stripe keys, even if a prompt injection attack attempts to redirect it. The broker enforces the policy on every request.
Safe config sharing. Your MCP config file is now a list of endpoints and agent tokens — safe to commit, safe to share in onboarding docs, safe to include in a dotfiles repo.
For the broader access model this builds on, see MCP security: what developers need to know and the agents page.
Team setups: shared configs, separate credentials
The single-developer case is straightforward. Teams make it complicated.
The typical failure mode: one developer sets up a working MCP config, commits it to the team's dotfiles or onboarding repo, and everyone else clones it. Now every developer on the team is using the same API key. Same blast radius. No way to tell who did what. One person leaves, you rotate the key, you break everyone else's setup simultaneously.
The fix isn't "don't share configs." Shared configs are useful. The fix is that shared configs should contain no credentials worth protecting.
One access profile per developer, not per team. Each developer creates their own agent identity in Gazebo. Each gets their own bearer token. The MCP config structure is identical across the team — same endpoint URL, same gazebo server block — but each developer's token is theirs alone. The config template is shareable. The token is not.
Your onboarding doc changes from "copy this config and paste in the Stripe key" to "copy this config, create your agent profile, paste in your bearer token." That's one extra step. It's worth it.
Separate profiles for different trust levels. A junior contractor shouldn't have the same service access as a senior engineer with production Stripe permissions. Access profiles let you encode that distinction without managing multiple sets of raw API keys. The contractor gets a profile scoped to staging services only. Revoke it when the contract ends — nothing else is affected.
Team-wide services, individual agent identities. Your Stripe account is connected to Gazebo once. Everyone's agent profiles draw from that single connection. When Stripe rotates or you change keys, you update one place. Individual tokens remain valid — the broker handles the indirection.
Your config was already committed. Now what.
Rotating the key is necessary but not sufficient. Here's what to actually do, in order.
Revoke immediately, before anything else. Go to your service provider — Stripe, GitHub, wherever — and invalidate the exposed key right now. Not after you finish reading this. The key has been in your git history, potentially synced to GitHub, potentially indexed by automated scanners that crawl public repos within minutes of a push. Assume it's already been seen.
Check your service logs before you rotate. Most services let you filter API activity by key. Pull the logs for the exposed key going back to the first commit that contained it. Look for requests you didn't make — unusual volumes, calls from unexpected IP ranges, activity outside your working hours. If you see anomalies, you're dealing with an active incident, not just a hygiene problem. Treat it accordingly.
Rotate, then update everywhere atomically. Generate the new key, update all legitimate uses of it, and verify everything works before you close the old key entirely. Rotation without verification breaks pipelines and creates pressure to roll back to the compromised key — which developers sometimes do.
Scrub git history. git filter-repo is the current recommended tool. BFG Repo Cleaner also works. Both will rewrite history to remove the file content or replace the key value with a placeholder. Force-push to all remotes. Notify every collaborator — their local clones still contain the old history until they re-clone or run the equivalent reset. If it's a public repo, assume the key was harvested before you scrubbed it. Treat revocation as the actual fix; history scrubbing is hygiene.
Audit who had access to the repo. If this is a private repo with outside collaborators or CI integrations — GitHub Actions, Vercel, Netlify — check whether any of those systems cached or logged the config file. CI logs are a common secondary exposure vector that gets missed.
Add the config path to .gitignore immediately. Before you commit anything else. .cursor/mcp.json, .claude/mcp.json, and any other MCP config paths belong in .gitignore at the repo level and in your global gitignore. Both.
Document the incident internally. Not for blame — for process. What allowed a credential to reach a config file that got committed? Was it missing gitignore entries? Onboarding instructions that said "paste your key here"? Fix the process, not just the symptom.
Once you've done all of this, you're in cleanup mode. The credential broker pattern means the same incident can't recur: there are no raw service keys in your MCP config to commit.
Not sure if your current config already has raw credentials in it? Use the free Gazebo scanner — paste your MCP config and it'll flag any exposed keys in seconds, entirely in your browser.