·11 min read

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:

PrecedenceSourceWho controls it
1 (highest)managed-settings.jsonIT / security admins
2Command-line argumentsDeveloper, per session
3.claude/settings.local.jsonDeveloper, per project (gitignored)
4.claude/settings.jsonTeam, checked into the repo
5 (lowest)~/.claude/settings.jsonDeveloper, 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 defaultMode and, critically, set disableBypassPermissionsMode to "disable" so --dangerously-skip-permissions stops working org-wide.
  • MCP server approval -- pin which .mcp.json servers 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.json is Claude Code only.
  • They are not a sandbox. Bash rules 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):

RuleMatches
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
WebFetchEvery fetch, any domain
mcp__githubEvery tool exposed by the github MCP server
mcp__github__create_issueOne 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:

  1. Deny is checked first. A match blocks the action, full stop -- no lower-precedence file and no allow rule can rescue it.
  2. Ask is checked next. A match forces an interactive confirmation even if an allow rule also matches.
  3. Allow runs the action without prompting.
  4. 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:

OSPath
macOS/Library/Application Support/ClaudeCode/managed-settings.json
Linux / WSL/etc/claude-code/managed-settings.json
WindowsC:\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:

  1. Author the policy in version control. Treat managed-settings.json like any other piece of infrastructure config: PR review, an owner, a changelog.

  2. 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"
  1. 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"
  1. Lock down file ownership. The file should be owned by root (or the Windows equivalent) and writable only by admins -- 0644 on Unix-likes. If a developer's account can edit the file, it isn't a managed setting; it's a suggestion.

  2. 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.

  1. Stage it on one machine. Copy the file to the managed path on your own laptop and restart Claude Code.

  2. Inspect the merged result. Run /permissions and confirm every managed rule is present and attributed to the managed policy, not to a user or project file.

  3. 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.json and confirm the managed deny still wins.

  4. 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 cover pnpm test) or a deny that blocks a legitimate daily task. Collect the friction and revise.

  5. 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