Deploy managed settings
Put organisation policy on every developer machine, understand how Claude Code picks between managed sources, and diagnose a policy that is not applying.
Managed settings are the configuration an organisation pushes to developer machines. They sit above every other layer: nothing in a user file, project file, local file or --settings flag overrides them, apart from a short list of security-sensitive keys where a stricter lower value is still honoured (see settings).
This page is for the person deploying that policy, or the person trying to work out why it is not taking effect. If you have not yet decided what to enforce, start at organisation setup. For the console-based route, see server-managed settings.
The fastest route: one file
If you just want a policy on a handful of machines, drop a file in place.
1. Write the policy. It uses exactly the same JSON shape as settings.json. This example keeps Claude away from a credentials folder and a local database dump, removes bypass mode, and means only managed permission rules count:
{
"permissions": {
"deny": [
"Read(./config/credentials/**)",
"Read(./*.sqlite)"
],
"disableBypassPermissionsMode": "disable"
},
"allowManagedPermissionRulesOnly": true
}
The settings reference says, per key, whether a managed source may set it, and settings examples has a fuller organisational file.
2. Place it. Save as managed-settings.json in the system directory:
| OS | Path |
|---|---|
| macOS | /Library/Application Support/ClaudeCode/managed-settings.json |
| Linux and WSL | /etc/claude-code/managed-settings.json |
| Windows | C:\Program Files\ClaudeCode\managed-settings.json |
The old Windows location under C:\ProgramData\ClaudeCode is no longer read.
3. Check it. On one machine, run /status. The Setting sources line should include Enterprise managed settings (file). Only then roll out to everyone.
Choosing a delivery mechanism
The file is one of four channels. They all carry the same keys; what differs is how you push them and how often Claude Code looks.
| Channel | You deploy it via | Claude Code reads it | Good fit |
|---|---|---|---|
| Server-managed | The claude.ai admin console, or a self-hosted Claude apps gateway | At startup, then polled hourly | One central place for a claude.ai organisation, no device tooling |
| MDM / OS policy | macOS configuration profile or Windows HKLM registry, through Jamf, Intune, Group Policy and the like | At startup, then every 30 minutes | You already manage devices centrally |
| Managed file | managed-settings.json in the system directory | At startup, and reloaded when the file changes | No MDM, Linux servers, golden images |
| HKCU registry | A Windows HKCU value | At startup, then every 30 minutes, and only if nothing above it is present | You cannot write to HKLM |
The MDM and file channels together are called endpoint-managed settings, because the policy lives on the device. Anthropic publishes starter templates for Jamf, Iru, Intune and Group Policy in the examples/mdm folder of the anthropics/claude-code GitHub repository.
How each channel stores the policy:
- Server-managed: held remotely; a local cache is applied at startup and replaced on each successful fetch.
- macOS profile: the
com.anthropic.claudecodemanaged preferences domain. Top-level keys match the JSON, nested objects become dictionaries and lists become arrays. - Windows HKLM / HKCU: the JSON as a string (
REG_SZorREG_EXPAND_SZ) in a value namedSettingsunderSOFTWARE\Policies\ClaudeCode. - File:
managed-settings.json, an optionalmanaged-settings.d/folder, andmanaged-mcp.json, all in the system directory.
Every channel applies one policy to everyone it reaches. Different groups need different files or profiles. The claude.ai console cannot target groups yet; a Claude apps gateway can deliver per IdP group.
A handful of keys are tied to particular sources: the delivery controls policyHelper, wslInheritsWindowsSettings and managedSourcesBehavior, and the gateway login keys forceLoginGatewayUrl, gatewayInternalNetworks and the "gateway" value of forceLoginMethod.
Splitting a file policy between teams
If security, platform and data teams each own a slice of policy, give each a drop-in file in managed-settings.d/ rather than fighting over one file. Claude Code reads managed-settings.json first, then every *.json in the folder alphabetically, skipping hidden files. Prefix with numbers to control order:
/etc/claude-code/
managed-settings.json
managed-settings.d/
10-platform.json
20-security.json
30-data-team.json
When two files set the same key:
| Kind of value | Result |
|---|---|
Scalar ("model", "cleanupPeriodDays") | Later file wins |
List (permissions.deny, sandbox.network.allowedDomains) | Combined, duplicates removed |
Object (env, sandbox) | Merged key by key using these same rules |
fallbackModel, modelPicker | Later file replaces the whole thing |
extraKnownMarketplaces, managedMcpServers | An entry with the same name is replaced whole |
Which sessions see the policy
- Local surfaces. The terminal, VS Code and JetBrains extensions, the desktop app's Code tab and Agent SDK sessions all read every channel. SDK sessions load managed settings even if
settingSourcesleaves out user, project and local. - Anthropic-hosted cloud sessions cannot see a device's file or MDM profile, so their policy must come from server-managed settings. Sessions in self-hosted environments also read the file in their runner image, by default only when server-managed settings supply no policy key.
- Claude Tag sessions do not get server-managed settings, though in self-hosted environments they read the runner image's file.
- Cowork (in Claude Desktop) never fetches server-managed settings from the admin console. On the user's machine it reads the local MDM policy and file. Inside a full VM sandbox (
requireCoworkFullVmSandbox) or in remote Cowork, there is no device policy to read. claude.ai does still enforce the console'sstrictKnownMarketplacesandblockedMarketplaceswhen anyone adds a marketplace from claude.ai or Cowork's Customize. - Running sessions pick up most changes on the schedule above without restarting. Exceptions that wait for the next start:
forceRemoteSettingsRefresh,requiredMinimumVersion, a new or changedpolicyHelper, and some user-editable keys. Server-managed changes that need approval (hooks, certainenvvariables) wait for the developer to accept a dialog in interactive sessions. - Very long sessions can lag a rollout.
requiredMinimumVersionstops an old binary starting; it does not kill one already running.
How Claude Code combines several managed sources
If a machine receives policy from more than one channel, managedSourcesBehavior decides what happens:
"first-wins"(default): use the highest-ranked source that contains at least one policy key, and ignore the rest. No warning is shown, but/statuslists what was skipped."merge"(v2.1.242+): apply every admin source that has a policy key, combined by kind of key.
The ranking, highest first:
- Remote: server-managed settings from claude.ai or a gateway. Only fetched when the session talks to Anthropic's API directly with an eligible login, or signs into a gateway. On other providers, or with
ANTHROPIC_BASE_URLpointing elsewhere, Claude Code starts at step 2. - OS policy: the macOS profile or the
HKLMkey. - Files:
managed-settings.jsonplusmanaged-settings.d/*.json, merged. - HKCU on Windows (and on WSL when inheritance is turned on and the HKCU value also sets it).
Two definitions matter here. A policy key is any key other than the control keys wslInheritsWindowsSettings and managedSourcesBehavior; a source carrying only those is passed over. An admin source is one of the first three; HKCU is user-writable so it never counts.
HKCU is never used if an admin document is present above it. A document is present when it sets any policy key to a non-null value (even an unreadable value), or when an HKLM value, file or drop-in folder exists but cannot be read.
Keys read from every admin source
Even under "first-wins", a few keys are gathered from all admin sources (HKCU excluded), so a lower MDM profile or file can still supply them:
- The sandbox locks
sandbox.network.allowManagedDomainsOnlyandsandbox.filesystem.allowManagedReadPathsOnly. Atrueanywhere switches the lock on, and while it is on the locked allowlists (domains plusWebFetch(domain:...)rules, orfilesystem.allowRead) are unioned across admin sources. allowManagedMcpServersOnly(v2.1.273+). Atrueanywhere turns the MCP allowlist lock on; theallowedMcpServerslist then comes from the highest admin source that sets one, with a server-managed list replacing lower lists.deniedMcpServersanddisableClaudeAiConnectors(v2.1.273+), andallowAllClaudeAiMcps.- Sandbox binaries and switches:
sandbox.bwrapPath,sandbox.socatPath,sandbox.ripgrep,sandbox.filesystem.disabled,sandbox.network.strictAllowlist. - Deny-only toggles:
useAutoModeDuringPlan,syncClaudeAiSkills,syncClaudeAiPluginsandenableArtifact(v2.1.242+). Afalseanywhere, including the developer's own files, turns the feature off. maxEffortLevel(v2.1.267+): the lowest cap wins, and a developer can only lower it further.- A commit-trailer opt-out in
attributionor the olderincludeCoAuthoredBy. forceRemoteSettingsRefresh.env(v2.1.223+), merged per variable: each variable comes from the highest source that defines it.
The gateway login keys are never read from server-managed settings, so the highest admin source on the machine supplies them. allowedProviders (v2.1.285+) has its own combination rule, described in the settings reference.
Turning on merge
Set "managedSourcesBehavior": "merge" in the highest-ranked source you deploy; lower sources cannot opt themselves in. If some machines never receive server-managed settings, put it in their MDM profile too. HKCU never merges. Only use merge when every lower source is admin-controlled, because their allow rules and hooks get added.
| Kind of key | Under merge | Examples |
|---|---|---|
| Lists | Union of all sources | permissions.allow, hooks, deniedMcpServers, deniedModels |
| Locks | Strictest value wins; a looser value only counts from the top source | allowManagedHooksOnly, permissions.disableBypassPermissionsMode, crossSessionInbound, availableModelsMatch |
| Restriction allowlists | Taken whole from the top source that sets one | availableModels, allowedMcpServers, strictKnownMarketplaces, allowedChannelPlugins, fallbackModel |
| Whole values | Taken whole from the top source that sets one | sandbox.credentials.awsPairs, sandbox.ripgrep |
| Provided MCP servers | Union of names; same name takes the higher source's entry | managedMcpServers |
| Top-source-only keys | Ignored in lower sources even when the top leaves them unset | apiKeyHelper, forceLoginOrgUUID, modelPicker, permissions.defaultMode |
env | Per variable, as above | |
| Anything else | Highest source that sets it | model, cleanupPeriodDays |
Policy helpers
A policyHelper is an executable named in your MDM policy or file. Claude Code runs it at startup, and if it emits a managedSettings object, that object becomes the entire managed policy for the session, including the cross-source keys above (with a special startup rule for forceRemoteSettingsRefresh). Useful when policy depends on something only known at runtime, such as the user's group.
Policy supplied by a host application
When Claude Desktop, an IDE extension or an Agent SDK app launches Claude Code, it can pass its own managed settings via the SDK managedSettings option. These are parent settings. By default they are ignored whenever any admin source exists. Set parentSettingsBehavior: "merge" in your top source to combine them; Claude Code then keeps only the host's restrictive values, although the host's allow rules and sandbox allowlists still apply unless you also set the allowManaged*Only locks. A policy helper can veto parent merging.
Some checks apply to parent values regardless: allowManagedPermissionRulesOnly in any admin source strips the host's allow rules and additionalDirectories; your applied forceLoginOrgUUID, allowedMcpServers, availableModels, strictKnownMarketplaces (v2.1.282+) and allowedProviders (v2.1.285+) block the host's versions; and a host's blockedMarketplaces adds to yours.
Cowork and allowManagedPermissionRulesOnly. Cowork grants folder access with allow rules it passes at launch. Under that lock those rules vanish, so Cowork writes fail as "blocked". Add allow rules for the folders your users connect to the managed source that is actually selected (on an MDM fleet, the MDM profile):
{
"allowManagedPermissionRulesOnly": true,
"permissions": {
"allow": ["Edit(~/Documents/ClientWork/**)"]
}
}
What developers can still change
- The session model. A managed
modelis a default.--modelandANTHROPIC_MODELstill override it per session; useavailableModelsto restrict. - The auto-compact window. A managed
autoCompactWindowis a default;--autocompactandCLAUDE_CODE_AUTO_COMPACT_WINDOWoverride per session. - The source itself, if they are local admins. That is why MDM tools reapply profiles and why
HKLMand the managed preferences domain exist. - The server-managed cache, but only until the next fetch.
- Other tools. Managed settings only bind Claude Code.
Checking that a policy is in force
Reading /status
Run /status and look at Setting sources. With a managed source active it shows Enterprise managed settings and, in brackets, which one:
| Label | Meaning |
|---|---|
(remote) | Server-managed settings from claude.ai or a gateway |
(plist) / (HKLM) | OS policy |
(file), (drop-ins), (file + drop-ins) | The managed file, the drop-in folder, or both |
(remote + file, merged) and similar | Merge is on; these sources were combined |
(HKCU) | The user registry fallback |
(parent process) | A host application supplied restrictive settings |
(helper) | A policy helper produced the policy |
From v2.1.242, a Skipped sources line lists anything found but not selected. So:
- No managed entry at all? Nothing delivered a policy key. Check the path and that the file contains a real policy key. For server-managed settings, run
claude doctorto see the fetch result. - A different source than you expected? Something higher-ranked won.
Skipped sourceswill show yours.
Owners can also switch Remote Control and cloud sessions on or off for the organisation in the claude.ai Claude Code admin settings. When Remote Control is turned off, connected sessions on v2.1.286+ disconnect at their next hourly policy refresh. disableRemoteControl turns it off per device. claude doctor (v2.1.261+) prints an Organization policy line showing where that organisation policy came from or why it failed.
Finding entries that were dropped
Managed sources are validated forgivingly. Claude Code repairs what it can (dropping one bad permission rule, for instance), warns, and then discards values that still fail, except for keys that fail closed. You will see the problems in a startup dialog, on stderr in -p runs, and in claude doctor with source and field.
Some failures stop Claude Code launching altogether. If a managed file, drop-in, plist or HKLM value exists but is not a JSON object (invalid JSON, a plutil failure, an empty or non-string registry value), Claude Code refuses to start and names the source, even if another source is fine. Absent sources, an empty file (treated as {}) and a malformed HKCU value do not block launch.
If a source exists but cannot be read and no other admin source supplies policy: an OS permission denial lets sessions start without it (and records the failure); any other read error stops every session with a "contact your administrator" message.
Fail-closed keys
From v2.1.282, a top-level key with a single restrictive value (such as allowManagedPermissionRulesOnly, disableAutoMode or skipDangerousModePermissionPrompt) that is set to something unreadable is treated as that restrictive value, and reported as was present but invalid. A null simply removes the key. An invalid disableAllHooks is dropped rather than enforced, so your own managed hooks keep running. Quoted booleans like "true" are accepted with a notice asking you to remove the quotes.
The permissions, autoMode, worktree and attribution blocks are repaired field by field. Locks inside them go restrictive, an invalid permissions.defaultMode becomes default, and an unreadable deny or ask list withholds allow and additionalDirectories so you never get grants without their matching restrictions. The same applies in autoMode: a bad soft_deny or hard_deny withholds allow and environment.
Keys with their own fallback when invalid:
| Key | Fallback |
|---|---|
allowedMcpServers | Empty allowlist: no user-added servers. managedMcpServers and qualifying managed-mcp.json servers still load. Bad entries are stripped. |
allowedProviders | Empty allowlist: every provider refused, so Claude Code will not start. Unknown entries are dropped. |
allowedHttpHookUrls, httpHookAllowedEnvVars | Empty managed allowlist (v2.1.267+). These merge with other files, so their entries still count. |
allowedChannelPlugins | Empty allowlist (v2.1.267+) |
strictKnownMarketplaces | Empty allowlist; uncompilable hostPattern entries stripped (v2.1.277+) |
availableModels | Empty allowlist: only the Default model |
availableModelsMatch | Treated as exact |
forceLoginOrgUUID | Nobody can log in |
gatewayInternalNetworks | New gateway sign-ins refused, if it came from the top source |
crossSessionInbound | Treated as refuse |
deniedMcpServers, deniedModels, blockedMarketplaces | Bad entries stripped; a wholly invalid value is dropped with a warning rather than blocking everything |
strictPluginOnlyCustomization | Treated as true if neither boolean nor array (v2.1.282+) |
enabledPlugins | Bad entries dropped; wholly invalid value dropped (v2.1.282+) |
sandbox.credentials | Recoverable entries downgraded to mode: "deny"; others stripped |
requiredMinimumVersion and requiredMaximumVersion deliberately fail open. This tolerance is for managed sources only; user, project and local files are rejected whole if their top-level shape is wrong.
Inside sandbox (v2.1.283+), each field is validated separately. Invalid booleans go to whichever value is strictest (enabled reads as true, allowUnsandboxedCommands as false), except failIfUnavailable, which is dropped so a typo cannot stop your whole fleet launching. Bad list entries are removed. An invalid network.deniedDomains withholds network.allowedDomains, and an invalid filesystem.denyRead or denyWrite withholds both filesystem allow lists. Before v2.1.283 one bad field dropped the whole block apart from credentials.
Keys only a managed source can set
These are ignored in user and project files. Most are locks that tell Claude Code to honour only the managed version of an ordinary key.
| Key | What it does |
|---|---|
allowAllClaudeAiMcps | Load claude.ai connectors alongside a deployed managed-mcp.json |
allowedChannelPlugins | Which channel plugins may push messages (needs channelsEnabled: true) |
allowManagedHooksOnly | Restrict which hooks run |
allowManagedMcpServersOnly | Only the managed allowedMcpServers list counts |
allowManagedPermissionRulesOnly | Only managed permission rules count |
blockedMarketplaces | Marketplace sources to refuse before download |
channelsEnabled | Allow channels for the organisation |
disableCommandPluginSources | Block command plugin sources and most marketplace headersHelper commands (v2.1.229+) |
disableSideloadFlags | Reject --plugin-dir, --plugin-url, --agents and --mcp-config (v2.1.193+) |
forceRemoteSettingsRefresh | Block startup until server-managed settings are freshly fetched |
managedMcpServers | Remote MCP servers provided to everyone (v2.1.259+) |
managedSourcesBehavior | "first-wins" or "merge" |
parentSettingsBehavior | Whether host-supplied settings merge in |
pluginSuggestionMarketplaces | Marketplaces whose plugins may be suggested |
pluginTrustMessage | Extra text on the plugin trust warning |
policyHelper | Executable that computes the policy |
sandbox.filesystem.allowManagedReadPathsOnly | Only managed allowRead paths count |
sandbox.network.allowManagedDomainsOnly | Only managed domains count; others blocked without a prompt |
strictKnownMarketplaces | Which marketplace sources users may add |
strictPluginOnlyCustomization | Skills, agents, hooks and MCP only from plugins or managed settings |
wslInheritsWindowsSettings | Let WSL read the Windows policy chain |
Turning telemetry off for everyone
Sessions that use the Anthropic API (directly, via a gateway or a custom ANTHROPIC_BASE_URL) send operational telemetry by default. To switch it off fleet-wide:
{
"env": { "DISABLE_TELEMETRY": "1" }
}
A value of 1 applies without the usual approval dialog. Be aware it also stops the data that feeds your analytics dashboard and disables feature-flag fetching for those users.