Claude Code Managed Settings: Enterprise Policy Guide
How Claude Code managed settings enforce enterprise policy: managed-settings.json anatomy, allow/deny/ask rules, MDM deployment, and safe rollout.
Claude Code managed settings are the enterprise policy layer for Claude Code: a managed-settings.json file that administrators deploy to developer machines to enforce permission rules, MCP server allowlists, and environment configuration that individual users cannot override. The file sits at the very top of Claude Code's settings precedence -- above command-line flags, project settings, and personal settings -- which makes it the right home for any policy that actually needs to hold.
If you're running a Claude Code enterprise rollout, this guide walks through what the file can and can't control, the permission allow/deny/ask model, how to ship the file through MDM or config management, and how to test a policy on a pilot group before pushing it to every engineer.
What Claude Code managed settings can and can't control
Claude Code reads settings from several places and merges them by precedence. Managed settings win every conflict:
| Precedence | Source | Who controls it |
|---|---|---|
| 1 (highest) | managed-settings.json | IT / security admins |
| 2 | Command-line arguments | Developer, per session |
| 3 | .claude/settings.local.json | Developer, per project (gitignored) |
| 4 | .claude/settings.json | Team, checked into the repo |
| 5 (lowest) | ~/.claude/settings.json | Developer, machine-wide |
A deny rule in the managed file cannot be removed by anything a developer writes into their user or project settings. That one-way merge is the whole point.
What managed settings can enforce:
- Permission rules -- allow, deny, and ask lists for every tool Claude Code can invoke:
Bash,Read,Edit,WebFetch,WebSearch, and individual MCP tools. - Permission modes -- set
defaultModeand, critically, setdisableBypassPermissionsModeto"disable"so--dangerously-skip-permissionsstops working org-wide. - MCP server approval -- pin which
.mcp.jsonservers are enabled or disabled, and turn off blanket auto-approval of project-defined servers. - Environment and login -- force environment variables into every session and pin the login method so developers authenticate through the right account type.
What managed settings can't do:
- They don't review the code Claude writes. Policy controls what the agent may do on the machine, not the quality or security of its output.
- They don't apply to other tools. Cursor, Windsurf, and Copilot each have their own configuration surfaces;
managed-settings.jsonis Claude Code only. - They are not a sandbox.
Bashrules are pattern matching on command strings, not an OS-level boundary. Treat them as guardrails and pair them with real network egress controls and OS permissions for anything adversarial. - They don't distribute your prompts, skills, or coding conventions. Policy says "may not"; it never says "here's how we do things." That gap is covered in the last section.
Managed settings are still the strongest of the Claude Code admin controls available, precisely because they're enforced client-side at the highest precedence and deployed through channels developers don't own.
managed-settings.json anatomy: allow, deny, and ask
Here is a realistic policy file:
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(~/.aws/**)",
"Read(~/.ssh/**)",
"Bash(curl:*)",
"Bash(wget:*)",
"WebFetch"
],
"ask": [
"Bash(git push:*)",
"Bash(npm publish:*)",
"Bash(terraform apply:*)"
],
"allow": [
"Bash(npm run build:*)",
"Bash(npm run test:*)",
"WebFetch(domain:docs.internal.example.com)"
],
"defaultMode": "default",
"disableBypassPermissionsMode": "disable"
},
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1"
},
"forceLoginMethod": "console",
"enableAllProjectMcpServers": false,
"enabledMcpjsonServers": ["internal-docs"]
}
Rule syntax
Each rule is Tool or Tool(specifier):
| Rule | Matches |
|---|---|
Bash(npm run test:*) | Any shell command starting with npm run test |
Read(~/.aws/**) | Reading any file under ~/.aws/ |
Edit(src/**) | Edits to files inside src/ |
WebFetch(domain:example.com) | Fetches to example.com only |
WebFetch | Every fetch, any domain |
mcp__github | Every tool exposed by the github MCP server |
mcp__github__create_issue | One specific MCP tool |
Two syntax details trip people up. First, Bash prefix rules use :*, not a bare * -- Bash(curl:*) matches curl plus anything after it. Second, MCP rules don't support wildcards like mcp__github__*; to cover a whole server, use the bare server form mcp__github.
Evaluation order
The permission allow/deny/ask lists resolve in a fixed order:
- Deny is checked first. A match blocks the action, full stop -- no lower-precedence file and no allow rule can rescue it.
- Ask is checked next. A match forces an interactive confirmation even if an allow rule also matches.
- Allow runs the action without prompting.
- No match falls through to the default behavior for that tool, which for anything consequential means prompting the developer.
Design your managed file accordingly: deny the things that must never happen, ask for the irreversible-but-legitimate operations, and allow the high-frequency safe ones so developers aren't trained to mash "approve."
Deploying policy via MDM and config management
Claude Code looks for the managed file at a fixed system path per OS:
| OS | Path |
|---|---|
| macOS | /Library/Application Support/ClaudeCode/managed-settings.json |
| Linux / WSL | /etc/claude-code/managed-settings.json |
| Windows | C:\ProgramData\ClaudeCode\managed-settings.json |
These locations require admin rights to write, which is what keeps the policy out of developers' hands. The deployment loop:
-
Author the policy in version control. Treat
managed-settings.jsonlike any other piece of infrastructure config: PR review, an owner, a changelog. -
Validate the JSON before shipping. A malformed file is a fleet-wide support ticket:
node -e "JSON.parse(require('fs').readFileSync('managed-settings.json','utf8'))" \
&& echo "valid"
- Distribute through the channel you already have. On macOS, package the file and push it with Jamf or your MDM of choice. On Windows, deploy a Win32 app or remediation script through Intune that writes to
ProgramData. On Linux fleets, config management is the natural fit:
- name: Deploy Claude Code managed settings
ansible.builtin.copy:
src: files/managed-settings.json
dest: /etc/claude-code/managed-settings.json
owner: root
group: root
mode: "0644"
-
Lock down file ownership. The file should be owned by root (or the Windows equivalent) and writable only by admins --
0644on Unix-likes. If a developer's account can edit the file, it isn't a managed setting; it's a suggestion. -
Verify on a real endpoint. Open Claude Code on a managed machine and run
/permissions. Managed rules appear alongside the settings file they came from, so you can confirm the policy actually landed.
Policies that matter: tools, MCP servers, and network access
You could write hundreds of rules. In practice, three policy areas carry most of the risk.
Secrets and destructive commands
Deny reads of credential material (.env files, ~/.ssh, ~/.aws, cloud CLI config directories) and deny or ask-gate the commands that can't be undone: force pushes, package publishes, infrastructure applies, bulk deletes. Setting disableBypassPermissionsMode to "disable" belongs in every enterprise file -- without it, a single --dangerously-skip-permissions flag turns your carefully tuned lists into decoration.
MCP servers
MCP servers are arbitrary code with tool access, so treat server approval like dependency approval. Set enableAllProjectMcpServers to false so a .mcp.json checked into a cloned repo doesn't auto-enable, then explicitly list vetted servers in enabledMcpjsonServers. For finer control, use permission rules: allow mcp__internal-docs wholesale, but ask-gate a specific risky tool like mcp__github__create_issue if you want a human in the loop.
Network access
The default posture for most orgs: deny WebFetch and WebSearch broadly, then allow specific domains -- internal docs, your package registry, vendor documentation -- via WebFetch(domain:...) rules. Back that up by denying Bash(curl:*) and Bash(wget:*) so the shell isn't a trivial side door. Remember the caveat from earlier: string matching on shell commands is a guardrail, not an egress firewall. If data exfiltration is in your threat model, enforce it at the network layer too, and run a security audit on the skills you install -- a malicious skill can instruct the agent toward exfiltration in ways no permission list anticipates.
Testing Claude Code managed settings before org-wide rollout
An over-broad deny list is how you teach developers to work around tooling. Test policy like you'd test code.
-
Stage it on one machine. Copy the file to the managed path on your own laptop and restart Claude Code.
-
Inspect the merged result. Run
/permissionsand confirm every managed rule is present and attributed to the managed policy, not to a user or project file. -
Try to break it. Ask Claude to
cat .env, to fetch an unapproved domain, to run with--dangerously-skip-permissions. Then add a conflicting allow rule to~/.claude/settings.jsonand confirm the managed deny still wins. -
Pilot with one team for a week. Real workflows surface what your threat modeling missed -- usually an allow rule that's too narrow (
Bash(npm run test:*)doesn't coverpnpm test) or a deny that blocks a legitimate daily task. Collect the friction and revise. -
Roll out in waves with a rollback path. Ship by group through your MDM and keep the previous file version ready to redeploy. Because the policy is a single JSON artifact in version control, rollback is a file replacement, not an incident.
The failure mode to watch for isn't "policy too loose" -- it's a pilot group that quietly stops using Claude Code because every third action prompts. Prompt fatigue is a security problem: it trains reflexive approval. Move high-frequency safe operations into allow until the prompt rate is boring.
Pairing policy with approved skills and rules
Managed settings define what Claude Code may do. They say nothing about how it should work: your architecture conventions, security requirements, testing standards, or the internal APIs it should reach for. A locked-down agent with no guidance still writes non-compliant code -- politely, and with permission.
The complement to a policy file is a curated library of approved skills and rules, distributed from a private skill registry rather than pasted into random repos. That's the layer localskills.sh provides: skills are versioned packages with rollback, organized into org folders with folder-level access restrictions, so the security team can own security/ skills while platform teams own their own namespaces. Enterprise controls -- SAML SSO, SCIM directory sync, and audit logs -- mean the "who changed our AI guidance and when" question has an answer, which pairs naturally with the "what may the agent do" question managed settings answer.
Distribution is one command per developer machine (or one line in a bootstrap script):
localskills install acme/security/secure-coding --target claude
The CLI writes the skill into .claude/skills/ in Claude Code's native format -- and the same published skill can target Cursor, Windsurf, and other tools with one install command. Policy from managed-settings.json, guidance from the registry: two halves of the same governance story. See the docs for setting up an org library.
FAQ
Where is managed-settings.json located?
macOS: /Library/Application Support/ClaudeCode/managed-settings.json. Linux and WSL: /etc/claude-code/managed-settings.json. Windows: C:\ProgramData\ClaudeCode\managed-settings.json. All three paths require administrator rights to write, which is what makes the file enforceable.
Can developers override Claude Code managed settings?
No. Managed settings hold the highest precedence in Claude Code's configuration hierarchy -- above CLI flags, .claude/settings.local.json, project .claude/settings.json, and user ~/.claude/settings.json. A managed deny rule cannot be neutralized by an allow rule in any lower-precedence file.
What's the difference between allow, deny, and ask?
Deny blocks the action unconditionally and is evaluated first. Ask permits the action only after an interactive confirmation. Allow runs the action without prompting. Anything unmatched falls back to the tool's default behavior, which for consequential actions means prompting the developer.
Do managed settings control MCP servers?
Yes, two ways: server enablement keys (enableAllProjectMcpServers, enabledMcpjsonServers, disabledMcpjsonServers) control which servers load, and permission rules (mcp__server for a whole server, mcp__server__tool for one tool) control what loaded servers may do. Note that MCP permission rules don't accept wildcards -- use the bare server name to cover all its tools.
Do managed settings apply to Cursor or Windsurf?
No. managed-settings.json is specific to Claude Code. Other AI coding tools have their own configuration mechanisms, so multi-tool orgs need a per-tool policy story -- and a tool-agnostic way to distribute the guidance layer, like a shared skill registry.
Ready to pair your Claude Code policy with a governed library of approved skills? Create your org on localskills.sh and publish your first approved skill in minutes.
npm install -g @localskills/cli
localskills login
localskills publish