Skip to main content

Documentation Index

Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt

Use this file to discover all available pages before exploring further.

By default, anyone running Claude Code can connect any MCP server they choose. Anthropic reviews connectors against its listing criteria before adding them to the Anthropic Directory, but doesn’t security-audit or manage any MCP server. As an administrator, you can restrict which servers run in your organization, from deploying a fixed approved set to disabling MCP entirely. This page covers how to:
The Security page covers the MCP threat model and how to evaluate a server before approving it. Decide what to enforce covers MCP restrictions alongside the other administrative controls.

Choose a pattern

Claude Code supports a range of restriction levels. Each pattern uses one or both of the mechanisms covered below: managed-mcp.json for deploying a fixed set, and allowedMcpServers/deniedMcpServers for filtering what users configure.
PatternWhat it doesConfigure
Disable MCPNo servers load anywheremanaged-mcp.json with an empty server map
Fixed deploymentEvery user gets the same servers and can’t add othersmanaged-mcp.json with the servers you want
Approved catalogPublish a list of approved servers; users add the ones they want, anything else is blockedallowedMcpServers + allowManagedMcpServersOnly: true
Plugin servers onlyServers can only come from plugins; users can’t add their ownstrictPluginOnlyCustomization with mcp in the list
Soft allowlistEnforce an allowlist that users can broaden in their own settingsallowedMcpServers without allowManagedMcpServersOnly
Denylist onlyBlock known-bad servers, allow everything elsedeniedMcpServers
No restrictionsUsers add anythingDon’t deploy any managed MCP configuration
Claude Code doesn’t have a built-in MCP server registry that users can browse and install from. For the approved-catalog pattern, share the approved list and its claude mcp add commands somewhere your users will find them, such as an internal wiki, or distribute the servers as plugins through a managed plugin marketplace so users can browse and install them from /plugin.

Exclusive control with managed-mcp.json

If you deploy a managed-mcp.json file, Claude Code loads only the servers that file defines. Users cannot add, modify, or use any other MCP servers, including plugin-provided servers and claude.ai connectors. Two other settings can further filter the managed set:
  • allowedMcpServers and deniedMcpServers apply to managed servers too, so a managed server that doesn’t pass them won’t load.
  • A user’s own deniedMcpServers merges in from their settings, so users can block a managed server for themselves.
See How a server is evaluated for the full order of checks. managed-mcp.json is a standalone file, so it cannot be delivered through server-managed settings. Any process that can write to a system path with administrator privileges can deploy it. At scale, that’s usually through device management tooling, such as Jamf or a configuration profile on macOS, Group Policy or Intune on Windows, or your fleet management of choice on Linux. Claude Code looks for the file at one of these paths:
PlatformPath
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 file uses the same format as a project .mcp.json file:
{
  "mcpServers": {
    "github": {
      "type": "http",
      "url": "https://api.githubcopilot.com/mcp/"
    },
    "sentry": {
      "type": "http",
      "url": "https://mcp.sentry.dev/mcp"
    },
    "company-internal": {
      "type": "stdio",
      "command": "/usr/local/bin/company-mcp-server",
      "args": ["--config", "/etc/company/mcp-config.json"],
      "env": {
        "COMPANY_API_URL": "https://internal.example.com"
      }
    }
  }
}

Authenticate with per-user credentials

Any user on the machine can read this file, so don’t store API keys or other credentials in env blocks. Pass per-user credentials with one of these instead:

Validate the configuration

To confirm the file is in effect, run two checks on a managed machine:
  1. claude mcp list shows only the servers in managed-mcp.json. If a user’s own servers still appear, the file isn’t being read; check the path and permissions.
  2. claude mcp add --transport http test https://example.com/mcp fails with Cannot add MCP server: enterprise MCP configuration is active and has exclusive control over MCP servers. The URL doesn’t need to be a real server, since the policy check rejects the command before anything is contacted.

Disable MCP entirely

Deploy a managed-mcp.json containing an empty server map to block every MCP server:
{
  "mcpServers": {}
}
Users see no MCP servers in /mcp, and claude mcp add fails with the enterprise-policy error above. Servers users had previously configured stop loading the next time they start a session, with no warning that policy is the reason.

Policy-based control with allowlists and denylists

Allowlists and denylists filter which configured servers are allowed to load. They aren’t a registry: a server still has to be added by a user, a plugin, or managed-mcp.json before the allowlist or denylist applies to it. To deploy servers to users, use managed-mcp.json. To make the allowlist authoritative, set allowedMcpServers and allowManagedMcpServersOnly: true together in a managed settings source, such as server-managed settings or a deployed managed-settings.json file. Restrict the allowlist to managed settings only shows the configuration. Without allowManagedMcpServersOnly, allowlists from every settings source merge, including a user’s own ~/.claude/settings.json, so a user can broaden what your allowlist permits. Denylists merge from every source regardless.
allowManagedMcpServersOnly is separate from allowManagedPermissionRulesOnly, which locks down permission rules only. Setting that flag does not enforce the MCP allowlist.

Match servers by URL, command, or name

allowedMcpServers and deniedMcpServers are lists of entries. Each entry is an object with a single key that identifies servers by their URL, their command, or their name:
KeyMatchesUse for
serverUrlA remote server URL, exact or with * wildcardsHTTP and SSE servers
serverCommandThe exact command and arguments that start a stdio serverStdio servers
serverNameThe user-assigned label. Exact match only; wildcards are not expandedEither type, but see the Warning below
Leaving allowedMcpServers unset is different from setting it to an empty array:
SettingUnset (default)Empty array []Populated
allowedMcpServersAll servers allowedNo servers allowedOnly matching servers allowed
deniedMcpServersNo servers blockedNo servers blockedMatching servers blocked
An allowlist that uses only serverName entries is not a security control. The name is the label a user assigns when running claude mcp add or editing a config file, not the underlying server, so a user can call any server github. To enforce which servers actually run, add serverCommand or serverUrl entries.

How a server is evaluated

Before loading a server, including one from managed-mcp.json, Claude Code runs three checks in order:
  1. Merge the lists. Allowlist and denylist entries from every settings source combine into one allowlist and one denylist. When allowManagedMcpServersOnly is true, only the managed allowlist is kept; the denylist always merges from every source.
  2. Check the denylist. A server that matches any denylist entry, by URL, command, or name, is blocked. Nothing overrides a denylist match.
  3. Check the allowlist. If allowedMcpServers isn’t set anywhere, every server that passed the denylist loads. If it is set, what the server must match depends on its type, shown in the table below.
Server typeAllowed when it matches
Remote (HTTP or SSE)A serverUrl entry. A serverName match counts only when the allowlist contains no serverUrl entries
StdioA serverCommand entry. A serverName match counts only when the allowlist contains no serverCommand entries
Two matching rules apply inside those checks:
  • Commands match exactly. Every argument, in order. ["npx", "-y", "server"] does not match ["npx", "server"] or ["npx", "-y", "server", "--flag"].
  • URLs support * wildcards anywhere in the pattern, including the scheme. Hostname matching is case-insensitive and ignores a trailing FQDN dot, so https://Mcp.Example.com/* matches https://mcp.example.com/api. Paths stay case-sensitive.
PatternAllows
https://mcp.example.com/*All paths on a specific domain
https://mcp.example.comAlso all paths on that domain. A pattern with no path matches any path
https://*.example.com/*Any subdomain of example.com
http://localhost:*/*Any port on localhost
*://mcp.example.com/*Any scheme to a specific domain

Example configuration

The configuration below sets up a hard allowlist with a denylist. The highlighted lines change how the rest of the list is evaluated, and the callouts after the block explain each one:
{
  "allowedMcpServers": [
    { "serverUrl": "https://api.githubcopilot.com/*" },
    { "serverUrl": "https://mcp.sentry.dev/*" },
    { "serverCommand": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "."] },
    { "serverCommand": ["python", "/usr/local/bin/approved-server.py"] },
    { "serverUrl": "https://mcp.example.com/*" },
    { "serverUrl": "https://*.internal.example.com/*" }
  ],
  "deniedMcpServers": [
    { "serverName": "dangerous-server" },
    { "serverCommand": ["npx", "-y", "unapproved-package"] },
    { "serverUrl": "https://*.untrusted.example.com/*" }
  ]
}
  • Line 3: the first serverUrl entry. Once one exists, every remote server must match a URL pattern, so a user can’t get an unlisted remote server through by giving it an allowed name.
  • Line 5: the first serverCommand entry. Same effect for stdio servers, so every local server must match a listed command exactly.
  • Line 11: a serverName entry in the denylist. Denylist entries always apply, so any server named dangerous-server is blocked regardless of its URL or command.
A serverName entry in this allowlist would never match anything, since both transport types already have stricter entries. The accordions below walk through how a server is evaluated against other allowlist and denylist combinations.
{
  "allowedMcpServers": [
    { "serverUrl": "https://mcp.example.com/*" },
    { "serverUrl": "https://*.internal.example.com/*" }
  ]
}
ServerResult
HTTP server at https://mcp.example.com/apiAllowed: matches URL pattern
HTTP server at https://api.internal.example.com/mcpAllowed: matches wildcard subdomain
HTTP server at https://external.example.com/mcpBlocked: doesn’t match any URL pattern
Stdio server with any commandBlocked: no name or command entries to match
{
  "allowedMcpServers": [
    { "serverCommand": ["npx", "-y", "approved-package"] }
  ]
}
ServerResult
Stdio server with ["npx", "-y", "approved-package"]Allowed: matches command
Stdio server with ["node", "server.js"]Blocked: doesn’t match command
HTTP server named my-apiBlocked: no name entries to match
{
  "allowedMcpServers": [
    { "serverName": "github" },
    { "serverCommand": ["npx", "-y", "approved-package"] }
  ]
}
ServerResult
Stdio server named local-tool with ["npx", "-y", "approved-package"]Allowed: matches command
Stdio server named local-tool with ["node", "server.js"]Blocked: command entries exist but doesn’t match
Stdio server named github with ["node", "server.js"]Blocked: stdio servers must match commands when command entries exist
HTTP server named githubAllowed: matches name
HTTP server named other-apiBlocked: name doesn’t match
{
  "allowedMcpServers": [
    { "serverName": "github" },
    { "serverName": "internal-tool" }
  ]
}
ServerResult
Stdio server named github with any commandAllowed: no command restrictions
Stdio server named internal-tool with any commandAllowed: no command restrictions
HTTP server named githubAllowed: matches name
Any server named otherBlocked: name doesn’t match
{
  "allowedMcpServers": [
    { "serverUrl": "https://*.example.com/*" }
  ],
  "deniedMcpServers": [
    { "serverUrl": "https://staging.example.com/*" }
  ]
}
ServerResult
HTTP server at https://mcp.example.com/apiAllowed: matches allowlist URL pattern, no denylist match
HTTP server at https://staging.example.com/apiBlocked: matches both, but the denylist takes precedence
HTTP server at https://other.com/mcpBlocked: doesn’t match the allowlist

Restrict the allowlist to managed settings only

To make the managed allowlist the only one that applies, set allowManagedMcpServersOnly in the managed settings file:
{
  "allowManagedMcpServersOnly": true,
  "allowedMcpServers": [
    { "serverUrl": "https://api.githubcopilot.com/*" },
    { "serverUrl": "https://*.internal.example.com/*" }
  ]
}
When allowManagedMcpServersOnly is true, allowlists from user, project, and local settings are ignored. The denylist still merges from all sources, so users can always block servers for themselves.

How restrictions appear to users

When a restriction blocks a server, the user either sees an error from claude mcp add or the server silently stops loading. Use this table to recognize those reports and to tell users what to expect before you roll out a change:
RestrictionWhat the user sees
managed-mcp.json is present and the user runs claude mcp addCannot add MCP server: enterprise MCP configuration is active and has exclusive control over MCP servers
The server is on a denylist and the user runs claude mcp addCannot add MCP server "<name>": server is explicitly blocked by enterprise policy
The server isn’t on the allowlist and the user runs claude mcp addCannot add MCP server "<name>": not allowed by enterprise policy
A previously configured server is now blocked by policyThe server silently disappears from /mcp and claude mcp list with no warning
In the last case, the user gets no signal that policy is the reason their server disappeared, so tell affected users which servers are blocked when you roll out a new restriction.

Monitor MCP usage

When OpenTelemetry export is configured, Claude Code can record which MCP servers and tools users invoke. Set OTEL_LOG_TOOL_DETAILS=1 to include MCP server and tool names in tool events, then aggregate them in your collector to see which servers your users actually connect to. See Monitoring to set up the exporter and for the full event schema.

Configuration summary

Every file and setting this page covers, what it controls, and how to deliver it:
SurfaceWhat it controlsWhere it livesHow to deliver
managed-mcp.jsonFixed server set, exclusive controlSystem path: /Library/Application Support/ClaudeCode/, /etc/claude-code/, or C:\Program Files\ClaudeCode\MDM, GPO, fleet management, or any process with administrator privileges. Cannot be set through server-managed settings
allowedMcpServersAllowlist of permitted serversAny settings file; entries from every source merge unless allowManagedMcpServersOnly is setFor enforcement, a managed settings source: server-managed settings, managed-settings.json, MDM profile, or registry
deniedMcpServersDenylist of blocked serversAny settings file; entries from every source mergeSame as allowedMcpServers
allowManagedMcpServersOnlyLocks the allowlist to managed sources onlyManaged settings sources only; the setting has no effect elsewhereSame as allowedMcpServers