The short answer first. Connecting an MCP server gives Claude Code new hands. Claude's calls to those tools still pass through your permission rules (allow, ask, deny), but a locally started server process runs outside the sandbox with your full access, and what it does inside that process is not something those rules see. So the question for an organization is not "which useful servers can we find" but which servers, with how much access, approved by whom.
The official Security page states that Anthropic reviews connectors against its listing criteria before adding them to the directory, but does not security-audit or manage any MCP server. Deciding whether to adopt one is the user's job.
This article goes deeper into the MCP part of security governance for rolling Claude Code out to an organization (permissions, managed settings, sandboxing). Setting keys and behaviors were checked against the official documentation as retrieved on 2026-10-12. Some details change between versions, so confirm them against the version you deploy.
1. What an MCP server can do
A local MCP server is a separate process Claude Code starts, and it runs with the user's full access. The sandboxing docs list local MCP servers alongside command hooks and LSP servers as processes that "run with your full access." The sandbox wraps the shell commands Claude runs, not MCP server processes.
An organization should plan for four risks.
| Risk | What it means |
|---|---|
| Local execution | A stdio server is a program on the machine, so every file, environment variable and host its code can reach is in range |
| Where data goes | A remote server receives the arguments of each tool call: code, issue text, customer names |
| Prompt injection | Responses from a server that fetches outside content land in Claude's context. The docs warn that such servers can expose you to prompt injection risk |
| Additions you don't see | Scopes other than .mcp.json, claude.ai connectors and plugins can all add servers. In the docs' words, reviewing .mcp.json doesn't show every server a session can load |
Before everyone on the team gets it
Permissions and governance for using Claude Code safely as an organization
2. Scopes: local, project, user and precedence
MCP server configuration can live in three scopes. When the same server is defined in more than one, the entry from the highest-precedence source is used whole.
| Scope | Loads in | Shared with team | Stored in |
|---|---|---|---|
| local (default) | Current project only | No | ~/.claude.json |
| project | Current project only | Yes, via version control | .mcp.json in the project root |
| user | All your projects | No | ~/.claude.json |
Precedence runs local → project → user → plugin-provided servers → claude.ai connectors, and fields are never merged across scopes. Duplicates across the three scopes are matched by name; plugins and connectors are matched by endpoint. Servers the organization provides with managedMcpServers in managed settings rank above all of these.
One confusing point: MCP "local scope" is stored in ~/.claude.json in the home directory, and has nothing to do with the general .claude/settings.local.json.
3. Project .mcp.json approval, and the -p and SDK exception
In interactive sessions, Claude Code asks for approval before using servers from a project's .mcp.json. claude -p, the Agent SDK and cloud sessions can't show that prompt, so they load those servers without asking.
On the interactive side, there is a guard against a cloned repository approving itself: enableAllProjectMcpServers or enabledMcpjsonServers committed to the repository's .claude/settings.json is ignored in an untrusted folder, and the server stays pending approval.
The problem is non-interactive runs. The table in the permissions docs says that under claude -p or the SDK, in a folder never trusted, .mcp.json servers are "connected without asking, approved or not." If CI runs Claude against someone else's repository or an outside pull request, close this with one of:
- Listing the server by name in
disabledMcpjsonServers, which blocks it in every permission mode. - Excluding project settings with
--setting-sources, or the SDK'ssettingSources. - Starting with
--strict-mcp-config, so only servers passed with--mcp-configare used. - Starting with
--bare, which stops auto-discovery, including the project's.mcp.json.
The approval prompt is also skipped in a session started in bypassPermissions mode with skipDangerousModePermissionPrompt set in user or managed settings.
4. Choosing an organizational control pattern
The docs describe patterns by strength of control. Decide which one you want first.
| Pattern | What happens | Configure |
|---|---|---|
| Disable MCP | No servers load, apart from a few exceptions | managed-mcp.json with an empty server map |
| Fixed deployment | Everyone gets the same servers and can't add others | managed-mcp.json |
| Provided servers | Everyone gets the remote servers you list and keeps their own | managedMcpServers in managed settings |
| Approved catalog | Users add what they want from an approved list; everything else is blocked | allowedMcpServers + allowManagedMcpServersOnly: true |
| Soft allowlist | An allowlist users can broaden in their own settings | allowedMcpServers without allowManagedMcpServersOnly |
| Denylist only | Block known-bad servers, allow everything else | deniedMcpServers |
For most organizations the realistic choice is the approved catalog. Claude Code has no built-in MCP registry users can browse, so share the approved servers and their claude mcp add commands on an internal wiki, or distribute them as plugins through a managed plugin marketplace.
5. Writing allowedMcpServers and deniedMcpServers correctly
Each entry in either list is an object with exactly one key: serverUrl, serverCommand or serverName. Only serverUrl and serverCommand work as controls; serverName doesn't.
serverUrl: the URL of a remote (HTTP or SSE) server, with*wildcards. A pattern with the hostname written out in full matches only the scheme's default port (443 for https), while a hostname containing*matches every port.serverCommand: the command and arguments that start a stdio server, as an array, matched exactly and in order.["npx", "-y", "server"]matches neither["npx", "server"]nor the same command with--flagadded. Theenvblock isn't compared.serverName: the label a user assigns. The docs say it "is not a security control" in either list, because anyone can name any servergithub.
Evaluation runs merge the lists → check the denylist → check the allowlist. A server that matches the denylist is blocked, and nothing overrides that. Once the allowlist contains any serverUrl entry, every remote server must match a URL, so a name alone no longer gets one through (the same goes for stdio servers and serverCommand).
Know the difference between unset and an empty array.
| Setting | Unset (default) | Empty array [] | Populated |
|---|---|---|---|
allowedMcpServers | All servers allowed | Every server users add is blocked | Only matching servers allowed |
deniedMcpServers | Nothing blocked | Nothing blocked | Matching servers blocked |
And the most commonly misunderstood point: allowedMcpServers and deniedMcpServers are not managed-only keys; any settings file can set them. Without allowManagedMcpServersOnly, allowlists from every scope, including a user's own ~/.claude/settings.json, are merged, and users can widen what you permit. Setting allowManagedPermissionRulesOnly, the lock for permission rules, doesn't enforce the MCP allowlist either. They are separate locks.
6. Pinning servers with managed-mcp.json
Deploy managed-mcp.json and the only MCP servers usable on that machine are the ones the organization chose. Users can't add, modify or use any other server, including plugin-provided servers and servers passed with --mcp-config, and claude mcp add fails with an enterprise-policy error.
It lives in the same system directory as managed settings.
| OS | Path |
|---|---|
| macOS | /Library/Application Support/ClaudeCode/managed-mcp.json |
| Linux and WSL | /etc/claude-code/managed-mcp.json |
| Windows | C:\Program Files\ClaudeCode\managed-mcp.json |
The format matches a project .mcp.json. Three cautions:
- It can't be delivered through server-managed settings. It is a standalone file, so place it with anything that can write system paths with administrator rights: MDM, Group Policy, configuration management. If you don't need exclusive control and want to deliver from claude.ai, use
managedMcpServersinstead. - An empty server map effectively disables MCP. Servers you provide through
managedMcpServers, and anything else you allow alongside the managed set, still load. - Entries that use
${VAR}are checked against the allowlist. The organization's own servers skip the allowlist check only when they use no variable expansion.
Blocked servers disappear from /mcp and claude mcp list without telling the user why. When you introduce a new restriction, tell people beforehand which servers will stop.
7. Narrowing MCP tools with permission rules
After allowing a server, you can still apply ask or deny per tool. Permission rules name MCP tools like this:
mcp__puppeteer: every tool from the server namedpuppeteermcp__puppeteer__*: the same, written as a wildcardmcp__puppeteer__puppeteer_navigate: one specific tool from that server
Deny and ask rules accept "mcp__*" to mean every tool from every server. Allow rules, by contrast, accept wildcards only after a literal mcp__<server>__ prefix; "mcp__*" in allow is skipped with a warning. The design prevents blanket auto-approval of every server.
A working rule of thumb: allow read-only tools, ask on tools that write or send data out, and deny tools you don't want used. For remote servers using OAuth, oauth.scopes pins the scopes Claude Code requests. The docs present it as the way to restrict a server to a security-team-approved subset when the authorization server advertises more scopes than you want to grant.
8. A vetting checklist for third-party MCP servers
Judge a server not by how useful it is, but by what you hand it and how far it reaches. Turning these items into an intake template makes the process manageable.
| Area | What to check |
|---|---|
| Source | Is the source code public? Who maintains it, and is it still being updated? Writing it in-house is the surest option |
| Where it runs | Local (stdio) or remote (HTTP)? If local, assume full access and ask whether you can read its code |
| Permissions it needs | Which tokens or API keys does it ask for? Does it want write access when read-only would do? |
| Token scope | Can you issue a dedicated account or least-privilege token? For OAuth, can oauth.scopes narrow it? |
| Network egress | Where does the server connect? Does it pull in untrusted content such as web pages or email (an injection entry point)? |
| Data that leaves | Will code, customer data or internal documents go out as tool arguments? Does a remote provider's retention policy fit your rules? |
| Version pinning | Does the setup fetch the latest version every time, for example via npx? Pin the version and update only after review |
| How you'll control it | Can you write it into the allowlist with serverUrl or serverCommand? Can you allow only the tools you need? |
If you're worried about environment secrets reaching local servers, CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 strips credentials from the environments of child processes, including stdio MCP servers (GitHub tokens and proxy settings are left in place).
9. An example allowlist configuration
The example below is a starting point for the approved-catalog pattern, built only from keys confirmed in the official docs. The URLs and package name are placeholders; replace them with what you have vetted.
{
"allowManagedMcpServersOnly": true,
"allowedMcpServers": [
{ "serverUrl": "https://api.githubcopilot.com/*" },
{ "serverUrl": "https://mcp.example.internal/*" },
{ "serverCommand": ["npx", "-y", "@example-org/docs-mcp-server@1.4.2"] }
],
"deniedMcpServers": [
{ "serverUrl": "https://*.untrusted.example.com/*" }
],
"permissions": {
"ask": [
"mcp__github"
]
}
}
Some notes on the intent.
- With
allowManagedMcpServersOnly, any widening in user or project settings is ignored. The denylist still merges from every scope, so users can always block more for themselves. - The
serverCommandentry spells out the full arguments, version included. A single differing argument means no match, so a server started with another version or other arguments is blocked. - The denylist entry
https://*.untrusted.example.com/*contains*in the hostname, so it matches every port on those subdomains. A pattern with the hostname written out in full and no port matches only the default port, so watch how you write ports when blocking one specific host. mcp__githubis set to ask so that tools from a server the user namedgithubprompt on every call. Since users choose names, the allowlist above is what controls which server runs; the ask rule only adds a confirmation step.- Because this goes in managed settings, you can deliver it by file, MDM or server-managed settings (unlike
managed-mcp.json, it works through server-managed settings).
10. What to watch after rollout
MCP governance doesn't end at deployment. If you've set up OpenTelemetry, OTEL_LOG_TOOL_DETAILS=1 adds MCP server and tool names to tool events, so you can count which servers people actually connect to (the variable is off by default). Drop allowlisted servers nobody uses, and send frequently requested ones through vetting.
Summary: MCP is about turning trust decisions into settings
MCP servers greatly extend what Claude Code can do, but a local server's process runs outside the sandbox with the user's full access, and servers bring outside content into Claude's context. Permission rules govern only Claude's calls to their tools (section 7), not what the server process does. Since Anthropic doesn't audit them, the organizational baseline is four things: vet them, write the allowlist with serverUrl and serverCommand, make only the organization's list apply with allowManagedMcpServersOnly, and keep CI from loading .mcp.json.
Controls beyond MCP, such as permission modes and the sandbox, are covered in security governance for rolling Claude Code out to an organization. If you'd like outside help designing your MCP vetting criteria or allowlist, I take enquiries at Claude Code security governance for organizations.