Skip to main content
AI-driven development & productivity
Claude Code
AI駆動開発
セキュリティ
AI

Using third-party MCP servers safely with Claude Code — trust decisions, allowlists and managed-mcp.json

Before you connect a third-party MCP server to Claude Code: local servers run outside the sandbox, and Anthropic does not audit MCP servers. Covers .mcp.json approval, writing allowlists and denylists correctly, managed-mcp.json, and a vetting checklist, per the official docs.

Published
Reading time
11 min read
Author
友田 陽大
Share

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.

RiskWhat it means
Local executionA stdio server is a program on the machine, so every file, environment variable and host its code can reach is in range
Where data goesA remote server receives the arguments of each tool call: code, issue text, customer names
Prompt injectionResponses 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 seeScopes 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.

ScopeLoads inShared with teamStored in
local (default)Current project onlyNo~/.claude.json
projectCurrent project onlyYes, via version control.mcp.json in the project root
userAll your projectsNo~/.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's settingSources.
  • Starting with --strict-mcp-config, so only servers passed with --mcp-config are 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.

PatternWhat happensConfigure
Disable MCPNo servers load, apart from a few exceptionsmanaged-mcp.json with an empty server map
Fixed deploymentEveryone gets the same servers and can't add othersmanaged-mcp.json
Provided serversEveryone gets the remote servers you list and keeps their ownmanagedMcpServers in managed settings
Approved catalogUsers add what they want from an approved list; everything else is blockedallowedMcpServers + allowManagedMcpServersOnly: true
Soft allowlistAn allowlist users can broaden in their own settingsallowedMcpServers without allowManagedMcpServersOnly
Denylist onlyBlock known-bad servers, allow everything elsedeniedMcpServers

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 --flag added. The env block 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 server github.

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.

SettingUnset (default)Empty array []Populated
allowedMcpServersAll servers allowedEvery server users add is blockedOnly matching servers allowed
deniedMcpServersNothing blockedNothing blockedMatching 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.

OSPath
macOS/Library/Application Support/ClaudeCode/managed-mcp.json
Linux and WSL/etc/claude-code/managed-mcp.json
WindowsC:\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 managedMcpServers instead.
  • 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 named puppeteer
  • mcp__puppeteer__*: the same, written as a wildcard
  • mcp__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.

AreaWhat to check
SourceIs the source code public? Who maintains it, and is it still being updated? Writing it in-house is the surest option
Where it runsLocal (stdio) or remote (HTTP)? If local, assume full access and ask whether you can read its code
Permissions it needsWhich tokens or API keys does it ask for? Does it want write access when read-only would do?
Token scopeCan you issue a dedicated account or least-privilege token? For OAuth, can oauth.scopes narrow it?
Network egressWhere does the server connect? Does it pull in untrusted content such as web pages or email (an injection entry point)?
Data that leavesWill code, customer data or internal documents go out as tool arguments? Does a remote provider's retention policy fit your rules?
Version pinningDoes the setup fetch the latest version every time, for example via npx? Pin the version and update only after review
How you'll control itCan 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 serverCommand entry 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__github is set to ask so that tools from a server the user named github prompt 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.

Frequently asked questions

If an MCP server is listed in Anthropic's directory, is it safe?
Safety isn't guaranteed. The official Security page says Anthropic reviews connectors against its listing criteria before adding them to the directory, but does not security-audit or manage any MCP server. It encourages writing your own servers or using servers from providers you trust, which leaves the decision and the vetting to the organization using them.
Will a server defined in a repository's .mcp.json always ask for approval?
Not always. Interactive sessions show an approval prompt, but claude -p, the Agent SDK and cloud sessions can't show it, so they load project servers without asking. In CI and similar settings, reject servers individually with disabledMcpjsonServers, or keep project settings from loading with --setting-sources or --strict-mcp-config.
If I put allowedMcpServers in managed settings, can users still use other servers?
Yes, on its own they can. allowedMcpServers can be set in any settings file, and allowlists from every scope are merged, so users can widen it in their own settings. To make only the organization's list apply, set allowManagedMcpServersOnly to true in managed settings. The denylist is always merged from every scope.
Is it enough to write the allowlist by server name (serverName)?
No. serverName is the label a user assigns with claude mcp add, and anyone can call any server github. The docs themselves say a serverName entry is not a security control. Name what actually runs instead: serverUrl for remote servers and serverCommand for local ones.
How do I lock MCP servers down completely?
Deploy managed-mcp.json to each machine's system directory. Once it is there, users can't add, modify or use any other MCP server, and an empty server map effectively turns MCP off. Because it is a standalone file, though, it can't be delivered through server-managed settings from claude.ai; place it with MDM or your file-distribution tooling.

References

友田

友田 陽大

Developer of a METI Minister's Award–winning product. With TypeScript + Python + AWS, I deliver SaaS, industry DX, and production-grade generative AI (RAG) end to end — from requirements to infrastructure and operations — single-handedly.

Before everyone on the team gets it

Permissions and governance for using Claude Code safely as an organization

Permission modes, allow / ask / deny rules, managed settings, the sandbox, MCP server allowlists, hooks and CI use — decided as team policy and turned into configuration. Built only on features in the official docs, layered with device management.

Available for both project-based (contract) and advisory engagements. Start with a free 30-minute consult.

Also worth reading