Before you connect an MCP server, decide three things: do you trust the code, what secrets does it need, and which of its tools Claude may call. Connecting a server hands Claude a new set of actions on a real system, so treat each connection like granting an account access. This guide shows you the questions to ask and the exact Claude Code settings that enforce your answers.
If you have not met MCP yet, start with MCP fundamentals for the mental model. This guide assumes you know what a server, tool, and transport are, and focuses on keeping access tight.
The one-line answer
Allow only the specific tools a task needs, store secrets outside your config, scope each server to the narrowest place that works, and treat any server that reads outside content as untrusted input. Everything below is how you put that into practice.
Why is connecting an MCP server a privilege grant?
A connected server gives Claude tools that act on a live system: open a pull request, run a query, send a message, delete a record. The tool runs with whatever credentials you handed the server, not with some reduced "read-only AI" identity. If you connect a database server with a write-capable login, Claude can write to that database.
Two risks follow from this:
- Over-broad access. A server with more permission than the task needs can do more damage from a mistake or a bad instruction.
- Prompt injection. A server that fetches outside content (web pages, issue text, emails) can pull attacker-controlled text into the conversation, and that text may try to redirect Claude into calling other tools. Claude Code analyses the full request and requires approval for sensitive operations, but the durable defence is to not hand the server power it does not need.
Step 1: decide if you trust the server
Claude Code asks you to verify trust the first time you run in a new codebase and the first time it sees a new server. That prompt is the moment to think, not a box to click through.
- Prefer servers you wrote or servers from a provider you already trust with the same data.
- The Anthropic directory lists connectors reviewed against listing criteria, but Anthropic does not security-audit or manage the servers themselves. Reviewed is not the same as audited.
- A stdio server runs as a local process on your machine with your file access. A remote HTTP server runs on someone else's infrastructure and you send it your data over the network. Different trust questions apply to each.
For a project-scoped server checked into version control, Claude Code holds the server as pending and asks for approval before first use. You can reset those approval choices later:
claude mcp reset-project-choicesStep 2: keep secrets out of your config
A server usually needs a credential: an API key, a database connection string, an access token. The rule is that the credential lives in your environment or a secret store, and the config references it. The config can be shared or committed. The secret cannot.
Claude Code expands environment variables inside .mcp.json, so you reference a value instead of pasting it:
{
"mcpServers": {
"billing-api": {
"type": "http",
"url": "${BILLING_BASE_URL:-https://api.example.com}/mcp",
"headers": {
"Authorization": "Bearer ${BILLING_API_KEY}"
}
}
}
}The ${VAR} form reads the variable from your environment. The ${VAR:-default} form falls back to a default when the variable is unset. If a referenced variable has no value and no default, Claude Code refuses to parse the config rather than connecting with a blank secret. That fail-closed behaviour is the point.
When you add a server from the command line, pass the secret through --env for a stdio server or --header for a remote one, so it never lands in a tracked file:
# Local stdio server, key supplied at add time
claude mcp add --env API_KEY=$MY_KEY --transport stdio reports \
-- npx -y example-reports-mcp
# Remote HTTP server, token supplied as a header
claude mcp add --transport http github https://api.githubcopilot.com/mcp/ \
--header "Authorization: Bearer $GITHUB_PAT"For OAuth servers, you do not handle a long-lived secret at all. Run /mcp inside Claude Code to complete the browser sign-in. Claude Code stores and refreshes the token, keeping it in the system keychain on macOS and behind file permissions on Windows and Linux.
How do I stop Claude from reading my secret files?
Environment references keep secrets out of the MCP config, but Claude can still read files. Add a deny rule so it never opens your env files or secret directories, whatever a server or instruction asks:
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
]
}
}Deny rules win over allow rules across every settings scope, so a deny entry cannot be undone by a broader allow elsewhere.Common gotchaA Read(./.env) deny only stops Claude's own file reads. A stdio server runs as a subprocess that inherits your whole shell environment, so it can still see every variable you exported, including secrets meant for other tools.Launch each server with only the variables it needs (pass them via --env) rather than relying on the deny rule to hide them.
Step 3: scope the server to the narrowest place
Scope controls which projects load the server and whether the config is shared. Pick the smallest scope that still works. For the mechanics of each scope and where its config lives, see MCP configuration.
| Scope | Loads in | Shared with team | Stored in |
|---|---|---|---|
| Local | Current project only | No | Your home config |
| Project | Current project only | Yes, via version control | .mcp.json in the project root |
| User | All your projects | No | Your home config |
- Local is the default and the tightest. Use it for a personal or experimental server, or one whose credentials you do not want shared.
- Project shares the server with the team through
.mcp.json. Use it for tools everyone on the repo needs. Remember the secret still comes from each person's own environment, not the committed file. - User makes a server load in every project on your machine. A user-scoped server with write access is reachable from work you have not reviewed yet, so reserve this scope for cross-project, low-risk tools.
Set the scope explicitly when you add a server:
# Default local scope, stays in this project, private to you
claude mcp add --transport http stripe https://mcp.stripe.com
# Project scope, written to .mcp.json for the team
claude mcp add --transport http paypal --scope project https://mcp.paypal.com/mcp
# User scope, every project on this machine
claude mcp add --transport http hubspot --scope user https://mcp.hubspot.com/anthropicStep 4: allow only the tools you need
This is the heart of least-privilege. A server can expose a dozen tools, and you rarely need all of them. Claude Code permission rules name an MCP tool as mcp__server__tool, so you can allow read tools and leave write tools to prompt, or deny destructive ones outright.
{
"permissions": {
"allow": [
"mcp__github__search_repositories",
"mcp__github__get_pull_request"
],
"deny": [
"mcp__github__delete_repository"
]
}
}- Anything in
allowruns without a prompt. - Anything in
denyis blocked, and deny beats allow on any conflict. - Anything matched by neither falls through to a prompt, so Claude asks before it acts. That prompt-by-default is a feature, not friction: it keeps a tool you forgot to list from running silently.
You can allow every tool from one server with a wildcard, which is convenient for a server you fully trust and risky for one you do not:
{
"permissions": {
"allow": ["mcp__notes__*"],
"deny": ["mcp__notes__delete_note"]
}
}The wildcard allows the whole notes server, and the explicit deny still carves out the one tool you never want auto-run. Start narrow and widen only when a real task needs more.Common gotchaThe mcp__server__* wildcard auto-approves tools the server adds later, not just the ones present when you wrote the rule, so a future server update can ship a delete or transfer tool that runs with no prompt.The explicit deny only covers names you already knew to block. Name each tool you use in allow, and reserve the wildcard for a server whose author you trust to add nothing destructive.
How do I limit OAuth scopes for a remote server?
For a remote OAuth server, you can also restrict access at the grant itself, not just at the tool list. Set oauth.scopes to pin the permissions Claude Code requests during sign-in to a subset your security team approves, even when the upstream server advertises more:
{
"mcpServers": {
"slack": {
"type": "http",
"url": "https://mcp.slack.com/mcp",
"oauth": {
"scopes": "channels:read chat:write search:read"
}
}
}
}This value is a single space-separated string of scope names, and it takes precedence over whatever the server discovers on its own. Pinning here means the token Claude Code holds cannot do more than the listed scopes, which is a stronger guarantee than a tool allowlist alone.
Step 5: control which servers may load at all
Two settings decide whether project servers from .mcp.json connect without per-server approval. Use them deliberately.
enableAllProjectMcpServers: trueapproves every server in the project file automatically. Convenient on a repo you fully trust, dangerous on one you do not, since a new server added by a teammate would load with no prompt.enabledMcpjsonServersis an explicit allowlist of project servers to approve by name. This is the safer default for a shared repo, because a server not on the list still needs your approval.
{
"enabledMcpjsonServers": ["github", "sentry"],
"disabledMcpjsonServers": ["filesystem"]
}If you run an organisation, managed configuration goes further. An administrator can set allowedMcpServers and deniedMcpServers, and with allowManagedMcpServersOnly only the admin-approved set connects while user additions are blocked. Managed settings sit at the top of the precedence order and cannot be overridden locally.
Step 6: contain prompt injection
Any server that reads outside content can carry instructions written by someone other than you. The defence is layered, and you control the layers.
- Least-privilege first. A server that can only read cannot be tricked into writing. Steps 1 through 5 are your strongest protection here.
- Network commands prompt by default. Claude Code does not auto-approve
curlorwget. To block them outright, add them todeny. - Sandbox risky work. Run scripts and tool calls that touch external services inside a virtual machine or a dev container, so a bad outcome stays contained.
{
"permissions": {
"deny": [
"Bash(curl *)",
"Bash(wget *)"
]
}
}Claude Code also fetches web content in a separate context window to reduce the chance that a fetched page injects instructions, and it requires approval for network requests by default. These help, but they do not replace giving each server the minimum it needs.
A quick pre-connect checklist
Run through this before you type claude mcp add:
| Question | Tight answer |
|---|---|
| Do you trust the code or provider? | Yes, or you do not connect it |
| Where does the secret live? | In your environment, referenced by ${VAR} |
| What scope does it need? | The narrowest one that works, usually local |
| Which tools may auto-run? | Only the ones a real task uses |
| What must never auto-run? | Destructive tools, listed in deny |
| Does it read outside content? | If so, keep it read-only and sandbox the work |
How do I audit which permissions I already granted?
Settings drift as projects grow. Review them on a schedule.
# See every connected server and its scope
claude mcp list
# Inspect one server's transport, scope, and auth
claude mcp get githubInside a session, run /permissions to review the active allow, deny, and ask rules, and /mcp to see connected servers, their tool counts, and their auth state. When a server has tools you never use, remove it or tighten its allowlist. Least-privilege is not a one-time setup; it is something you keep trimming.
# Remove a server you no longer need
claude mcp remove old-reportsWhere this leaves you
You now have the moves that keep MCP access tight: verify trust, reference secrets instead of pasting them, scope to the narrowest place, allow specific tools, deny destructive ones, pin OAuth scopes, and sandbox anything that reads the outside world. The mechanism behind each setting lives in MCP fundamentals; this guide is the discipline you apply on top of it.