How to Secure Your MCP Server Setup — Best Practices
Security best practices for MCP servers. API key management, permission scoping, network isolation, and protecting your development environment.
MCP servers are powerful because they give your AI assistant direct access to real systems — databases, cloud providers, payment processors, version control. That same power makes them a security surface you need to think about. A misconfigured MCP server can expose production data, leak API keys, or grant write access to systems that should be read-only.
This guide covers the practical security decisions you face when setting up MCP servers: how to manage API keys, scope permissions correctly, understand network exposure, and audit what your servers can actually do.
The Security Surface of MCP
When you add an MCP server to your editor, you are spawning a process on your machine that can do real things. A Supabase MCP server can query and modify your database. A GitHub MCP server can merge pull requests. A Stripe MCP server can create charges. These are not sandboxed simulations — they operate on your actual accounts with the credentials you provide.
The security considerations fall into four categories:
- Credential management — what tokens and keys you hand to MCP servers, and how you store them
- Permission scoping — how much access those credentials grant
- Network exposure — whether the server is reachable beyond your machine
- Tool auditing — knowing exactly what each server can do before you enable it
Each deserves deliberate attention, especially as you add more servers to your setup.
API Key Management
Every MCP server that connects to an external service needs credentials. How you handle those credentials is the single most important security decision in your MCP setup.
Use test and development keys, not production
Most services provide separate keys for test and production environments. Always use the test or development tier:
- Stripe: use
sk_test_keys, neversk_live_. The test key lets you build and test integrations without touching real money or real customer data. - Supabase: create a separate project for development. Use that project's access token, not your production project's.
- GitHub: create a fine-grained personal access token scoped to only the repositories you are actively developing on.
{
"mcpServers": {
"stripe-mcp": {
"command": "npx",
"args": ["-y", "@stripe/mcp", "--tools=all"],
"env": {
"STRIPE_SECRET_KEY": "sk_test_your_test_key_here"
}
}
}
}
If your AI assistant accidentally runs a destructive operation with a test key, the blast radius is limited to test data. With a production key, it is not.
Never commit secrets to version control
This should be obvious, but it still happens. Common mistakes:
- Committing
.mcp.jsonwith real tokens - Pasting tokens in a
.cursor/mcp.jsonthat was already tracked by git - Forgetting that git history preserves deleted secrets
Use placeholder values in committed configs:
{
"mcpServers": {
"github-mcp": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "YOUR_GITHUB_TOKEN"
}
}
}
}
Or add the config file to .gitignore:
# .gitignore
.mcp.json
.cursor/mcp.json
If you accidentally committed a secret, rotate the key immediately. Removing it from the current commit is not enough — anyone with access to the git history can still find it.
Rotate keys regularly
MCP tokens tend to be long-lived because you set them up once and forget about them. Schedule periodic rotation:
- GitHub tokens: rotate every 90 days. Fine-grained tokens can be set to expire automatically.
- Supabase access tokens: rotate when team members leave or roles change.
- Stripe keys: roll your test keys periodically to verify your setup still works with fresh credentials.
When you rotate a key, update it everywhere — .mcp.json, claude mcp add configs, any CI/CD pipelines.
One key per context
Do not reuse the same API key across your MCP config, your application's .env file, and your CI/CD pipeline. If the MCP key is compromised, you want to revoke it without breaking your deployment pipeline.
Create dedicated keys labeled clearly:
mcp-development— for your local MCP server configapp-production— for your deployed applicationci-testing— for your CI/CD pipeline
This separation limits the damage from any single leak.
Permission Scoping
The tokens you give to MCP servers should have the minimum permissions needed for the tools you actually use. Most services let you create tokens with fine-grained scopes.
GitHub: Fine-Grained Personal Access Tokens
Instead of a classic token with blanket repo access, create a fine-grained token:
- Go to github.com/settings/tokens?type=beta
- Click "Generate new token"
- Under "Repository access," select only the repositories you are working on — not "All repositories"
- Under "Permissions," grant only what you need:
- Contents: Read-only — if you only need the AI to read code
- Pull requests: Read and write — if you want it to create PRs
- Issues: Read and write — if you want it to manage issues
Avoid granting Administration, Actions, or Workflows permissions unless you have a specific reason.
Supabase: Row-Level Security
When connecting a Supabase MCP server, remember that it operates with the permissions of the access token you provide. For development projects:
- Enable Row-Level Security (RLS) on all tables, even in development. This way, if the AI runs an unexpected query, RLS policies limit what data is accessible.
- Use a development project separate from production. The MCP server will have access to run migrations and modify schema — you do not want that happening on your production database.
Stripe: Test Mode Only
The Stripe MCP server accepts a STRIPE_SECRET_KEY. The difference between test and live mode is a single character in the key prefix:
sk_test_...— safe for development, creates fake chargessk_live_...— real money, real customer data
There is no legitimate reason to use a live Stripe key with an MCP server during development. Test mode provides the same API surface with zero risk.
The Filesystem MCP Allowlist Model
The Filesystem MCP server from Anthropic demonstrates a best practice worth copying: explicit allowlisting. Instead of granting access to your entire filesystem, you specify exactly which directories the server can read and write:
{
"mcpServers": {
"filesystem-mcp": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/me/projects/my-app/src",
"/Users/me/projects/my-app/docs"
]
}
}
}
Each path in the args array is an allowed directory. The server will refuse to read or write anything outside these directories. This means even if the AI decides to look at /etc/passwd or ~/.ssh/id_rsa, the server blocks the request.
Apply this thinking to other servers when possible:
- Scope GitHub tokens to specific repositories
- Scope database access to specific schemas or tables
- Scope cloud provider tokens to specific services or regions
The principle is the same: define the smallest perimeter that still lets you work effectively.
Network Exposure: Stdio vs HTTP/SSE
MCP servers use one of two transport mechanisms, and they have very different security profiles.
Stdio: Local Only
Most MCP servers use stdio transport. Claude Code spawns the server as a child process and communicates through pipes. The server has no network listener, no open port, no way for external systems to connect to it.
{
"command": "npx",
"args": ["-y", "@playwright/mcp"]
}
This is the secure default. The server runs on your machine, accessible only to the client that started it. Prefer stdio transport whenever possible.
HTTP/SSE: Network Accessible
Remote MCP servers communicate over HTTP. When you connect to one, you are sending your requests (and potentially sensitive context) over the network:
{
"url": "https://mcp.example.com/sse"
}
Security considerations for HTTP/SSE servers:
- Verify the server's identity: only connect to URLs you trust. A malicious MCP server could log your conversations, steal credentials passed through tool calls, or return misleading results.
- Use HTTPS: never connect to an MCP server over plain HTTP. Your tool calls and results would be transmitted in cleartext.
- Check authentication: if the remote server requires an API key or token in headers, that credential is sent with every request. Make sure the connection is encrypted.
- Consider data sensitivity: everything you send through a remote MCP server passes through a third party's infrastructure. For highly sensitive code or data, stick to local stdio servers.
If you run your own HTTP MCP server (for team sharing, for example), restrict access by IP, require authentication, and run it behind a reverse proxy with TLS termination.
Auditing What MCP Servers Can Do
Before adding a new MCP server to your setup, understand what it is capable of. MCP servers declare their tools with names and descriptions, but the actual capability might be broader than what the name suggests.
Read the tool definitions
In Claude Code, you can list the tools a server exposes:
claude mcp list
This shows the server name and the number of tools it provides. For more detail, ask Claude Code directly: "What tools does the supabase-mcp server provide?" It will list every tool with its description and parameters.
Check the source code
Reputable MCP servers are open source. Before granting a server access to your credentials:
- Visit the repository (e.g., github.com/supabase-community/supabase-mcp)
- Look at the tool definitions to see exactly what each tool does
- Check for any data exfiltration — does the server send data to third-party endpoints?
- Review the issues and pull requests for reported security concerns
Prefer official servers
Servers maintained by the service provider (Supabase, Stripe, GitHub) are more trustworthy than third-party alternatives. They have a reputation to protect, security teams reviewing the code, and a direct interest in not compromising their customers.
Look for the official label or check the repository owner. The Supabase MCP server is maintained by supabase-community, the GitHub MCP server by modelcontextprotocol (Anthropic's org), and the Stripe MCP server by stripe.
Permission Models Across Clients
Different AI coding clients handle MCP tool permissions differently, and this matters for security:
Claude Code: Ask Before Using
Claude Code prompts you before each new tool invocation. You can allow, deny, or allow permanently for the session. This gives you granular control — you can allow read operations while blocking writes.
Cursor: Auto-Allow
Cursor generally auto-allows MCP tool calls without prompting. This is more convenient but means you are trusting the AI model's judgment about when to use potentially destructive tools. If you use Cursor, be extra careful about the permissions baked into your API tokens.
VS Code (Copilot): Configurable
VS Code lets you configure tool approval policies. You can set specific tools to always require approval while auto-allowing others.
The takeaway: if your client auto-allows tool calls, your only line of defense is the scope of the credentials you provide. Make those credentials as narrow as possible.
What NOT to Do
These are real mistakes that cause real problems:
Do not use production database credentials
Your MCP server should connect to a development or staging database, never production. If the AI runs DROP TABLE users on your production database, there is no "undo" button.
// WRONG - production credentials
"env": {
"DATABASE_URL": "postgresql://admin:password@prod-db.example.com:5432/myapp"
}
// RIGHT - development database
"env": {
"DATABASE_URL": "postgresql://dev:devpass@localhost:5432/myapp_dev"
}
Do not use admin tokens
If a service offers role-based access, create a token with the minimum role needed. An admin token that can delete organizations, manage billing, and modify security settings is far more dangerous than a developer token limited to code and issues.
Do not use live Stripe keys
Worth repeating because the consequences are severe. A live Stripe key in an MCP server means the AI can create real charges, modify real subscriptions, and access real customer payment data. Always use sk_test_ keys.
Do not run untrusted MCP servers
An MCP server is an executable that runs on your machine with your user permissions. A malicious server could:
- Read files outside its intended scope
- Exfiltrate environment variables (including other API keys)
- Execute arbitrary commands
- Install persistence mechanisms
Only run MCP servers from sources you trust. Check the repository, the maintainer, and the download count before installing.
Do not share configs with embedded secrets
Sending your .mcp.json to a colleague via Slack or email with real API keys in it creates an uncontrolled copy of your credentials. Share configs with placeholder values only.
Practical Security Checklist
Use this checklist when setting up MCP servers for a new project:
- All API keys are test/development tier, not production
- No real secrets are committed to version control
-
.mcp.jsonis either in.gitignoreor uses placeholder values - GitHub tokens use fine-grained access scoped to specific repositories
- Database credentials point to a development database, not production
- Stripe keys use the
sk_test_prefix - Filesystem MCP (if used) is scoped to specific directories only
- All servers use stdio transport (unless remote is specifically required)
- Each server's source repository has been reviewed
- Server packages are from official or well-known maintainers
- API keys are not reused across MCP config and application production environments
- A rotation schedule is set for long-lived tokens
Auditing Your Current Setup
If you already have MCP servers configured and want to audit them, here is a quick process:
1. List all configured servers
# Claude Code
claude mcp list
# Or check config files directly
cat .mcp.json
cat ~/.claude.json
2. For each server, check the credential scope
Log into each service's dashboard and review what permissions the token grants. Narrow any that are too broad.
3. Verify no production credentials
Search your config files for production URLs, live API keys, or admin tokens. Replace with development equivalents.
4. Check version control history
git log --all --oneline -- .mcp.json .cursor/mcp.json
If real secrets were ever committed, rotate those keys immediately — even if the commit was later removed.
5. Review server trust
For each MCP server you are running, visit its repository and verify it is actively maintained, has a reasonable number of stars and users, and is published by a reputable source.
Going Further
Security is not a one-time setup. As you add servers, change projects, and onboard team members, revisit your MCP security posture regularly. The checklist above works well as a recurring quarterly review.
For teams, consider documenting your MCP security policy alongside your other development guidelines. Which servers are approved? What credential scopes are acceptable? Where should secrets be stored? These decisions are easier to enforce when they are written down.
stackmcp.dev includes permission and security information for every MCP server in its directory, so you can evaluate the security implications before adding a server to your stack. The server comparison pages show what permissions each server requires and what transport it uses, making it easier to make informed choices about your setup.
Build fast, but build safely. Your MCP servers should make you more productive without making you more vulnerable.