- Choose a pattern that matches how much control you need
- Deploy a fixed server set with
managed-mcp.json, including how to disable MCP entirely - Control servers with allowlists and denylists
- Tell users what to expect when a restriction blocks a server
- Monitor which servers your organization actually uses
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.
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 amanaged-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. The file also suppresses claude.ai connectors unless you allow them alongside the managed set.
Two other settings can further filter the managed set:
allowedMcpServersanddeniedMcpServersapply to managed servers too, so a managed server that doesn’t pass them won’t load.- A user’s own
deniedMcpServersmerges in from their settings, so users can block a managed server for themselves.
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:
The file uses the same format as a project
.mcp.json file:
Authenticate with per-user credentials
Any user on the machine can read this file, so don’t store API keys or other credentials inenv blocks. Pass per-user credentials with one of these instead:
${VAR}expansion to read secrets from each user’s environment.- OAuth or per-user headers so each user authenticates as themselves.
headersHelperto generate credentials at connection time.
Validate the configuration
To confirm the file is in effect, run two checks on a managed machine:claude mcp listshows only the servers inmanaged-mcp.json. If a user’s own servers still appear, the file isn’t being read; check the path and permissions.claude mcp add --transport http test https://example.com/mcpfails withCannot 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 amanaged-mcp.json containing an empty server map to block every MCP server:
/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.
Allow claude.ai connectors alongside the managed set
Deployingmanaged-mcp.json suppresses claude.ai connectors by default, including connectors an administrator configured for the organization in the claude.ai admin console. To load those connectors alongside the servers in managed-mcp.json, set "allowAllClaudeAiMcps": true in a managed settings source. Requires Claude Code v2.1.149 or later.
With the setting enabled, Claude Code loads the same claude.ai connectors it would load if managed-mcp.json were not deployed. Allowlists and denylists still apply to those connectors, so you can block specific ones with deniedMcpServers. The setting affects only claude.ai connectors; plugin-provided servers stay suppressed.
Claude Code reads this setting only from admin-controlled policy tiers: server-managed settings, an MDM-deployed plist or HKLM registry key, or a system managed-settings.json file. Placing it in user or project settings has no effect, so users cannot re-enable connectors that exclusive control suppressed.
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, ormanaged-mcp.json before the allowlist or denylist applies to it. To deploy servers to users, use managed-mcp.json. Both lists also filter servers passed with the --mcp-config CLI flag; --strict-mcp-config limits which configuration files load and doesn’t bypass either list.
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:
Leaving
allowedMcpServers unset is different from setting it to an empty array:
See Invalid entries in managed settings for what happens when an entry fails schema validation.
The
serverName validation differs between the two lists:
- In
deniedMcpServers,serverNameaccepts any non-empty string, so you can block claude.ai connectors by their display name. For example,{ "serverName": "claude.ai Slack" }blocks the Slack connector. Prefer aserverUrlentry when you need the deny to be robust to renames, or when a connector name collides and gains a(N)suffix. - In
allowedMcpServers,serverNameis limited to letters, numbers, hyphens, and underscores. UseserverUrlto allowlist a claude.ai connector.
disableClaudeAiConnectors.
How a server is evaluated
Before loading a server, including one frommanaged-mcp.json, Claude Code runs three checks in order:
- Merge the lists. Allowlist and denylist entries from every settings source combine into one allowlist and one denylist. When
allowManagedMcpServersOnlyistrue, only the managed allowlist is kept; the denylist always merges from every source. - Check the denylist. A server that matches any denylist entry, by URL, command, or name, is blocked. Nothing overrides a denylist match.
- Check the allowlist. If
allowedMcpServersisn’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.
Three 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"]. serverCommandandserverUrlvalues expand before matching. Both the policy entry and the server’s configured value go through${VAR}and${VAR:-default}expansion, so an entry written as["${HOME}/bin/server"]matches a server config that uses either the same reference or the expanded path. On Windows, reference an environment variable that is set there, such as${USERPROFILE}instead of${HOME}.serverNamevalues match literally and never expand. The two sides read different environments; How policy entries expand covers which, and how allowlist and denylist entries differ.- URLs support
*wildcards anywhere in the pattern, including the scheme. Hostname matching is case-insensitive and ignores a trailing FQDN dot, sohttps://Mcp.Example.com/*matcheshttps://mcp.example.com/api. Paths stay case-sensitive.
How policy entries expand
The server’s configured value expands from the live process environment, like the rest of.mcp.json. A policy entry expands from a pinned environment instead, so a variable set by a project or user settings file can’t change what an allowlist entry means. Because a policy entry still depends on the launching shell’s value for any variable it references, use literal URLs and commands for entries you rely on for enforcement.
Before v2.1.219, both sides expanded from the same live process environment, which included variables set by settings files.
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:- Line 3: the first
serverUrlentry. 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
serverCommandentry. Same effect for stdio servers, so every local server must match a listed command exactly. - Line 11: a
serverNameentry in the denylist. Denylist entries always apply, so any server nameddangerous-serveris blocked regardless of its URL or command.
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.
URL-only allowlist
URL-only allowlist
Command-only allowlist
Command-only allowlist
Mixed name and command allowlist
Mixed name and command allowlist
Name-only allowlist
Name-only allowlist
Allowlist with denylist override
Allowlist with denylist override
Restrict the allowlist to managed settings only
To make the managed allowlist the only one that applies, setallowManagedMcpServersOnly in the managed settings file:
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 fromclaude 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:
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. SetOTEL_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:Related resources
- Decide what to enforce: MCP restrictions alongside permission rules, sandboxing, and the other admin controls
- Connect Claude Code to tools via MCP: the full MCP reference, including transports, scopes, and authentication
- Settings: the settings hierarchy and how managed settings take precedence
- Server-managed settings: deliver
allowedMcpServersanddeniedMcpServersfrom the Claude.ai admin console - Security: the threat model these controls defend against
- Claude Enterprise Administrator Guide: SSO, SCIM, seat management, and rollout playbook