Where a setting wins
highest precedence at the top
- Managed settingsadmin console, MDM or system file — organisation
- Command line
claude --settings, flags — this session - Project local
.claude/settings.local.json— you, this repo - Shared project
.claude/settings.json— committed, whole team - User
~/.claude/settings.json— you, every repo
Four scopes, four jobs
Claude Code reads settings from files at several scopes, and each scope answers a different question: who should this apply to? The settings documentation’s own guidance is clear. User settings are personal preferences for every project. Shared project settings in .claude/settings.json are for the team: permissions, hooks, plugins and the environment variables the project needs, committed so every clone gets them. Project local settings in .claude/settings.local.json are your personal overrides for one repo and stay out of git. Managed settings are for security policy and compliance, and nothing a developer sets overrides them, apart from a few keys where Claude Code honours a stricter value from a lower level.
| Put it in… | When the setting is… | Example |
|---|---|---|
| Managed settings | Organisation policy that must hold everywhere | Deny reading .env files; only approved MCP servers |
.claude/settings.json (committed) | How this repo works, for everyone | Allow npm run test *; enable the team’s lint plugin |
.claude/settings.local.json | Your preference for this repo only | Allow a local debug script you run often |
~/.claude/settings.json | Your preference everywhere | Default model, editor mode, theme |
Two combining rules matter for design. First, lists merge: permissions.allow and permissions.deny collect entries from every file, so a team can add allow rules on top of an organisation’s but can’t delete a managed deny. Second, permission rules are evaluated deny, then ask, then allow, across all scopes, and the first match wins. A deny rule in any file beats an allow rule in any other. That makes deny rules the natural tool for policy, and allow rules the natural tool for removing friction.
{
"permissions": {
"allow": [
"Bash(npm run lint)",
"Bash(npm run test *)",
"Bash(git commit *)"
],
"deny": [
"Read(./.env)",
"Read(./secrets/**)",
"Bash(git push *)"
]
},
"sandbox": { "enabled": true },
"extraKnownMarketplaces": {
"acme-tools": {
"source": { "source": "github", "repo": "acme/claude-plugins" }
}
},
"enabledPlugins": { "service-conventions@acme-tools": true }
}Managed settings: organisation policy that sticks
Managed settings are how an organisation makes policy non-negotiable. They can be delivered four ways, checked in priority order: server-managed settings from the claude.ai admin console (Teams or Enterprise plans; fetched at startup and refreshed hourly), a macOS plist or Windows HKLM registry policy, a file named managed-settings.json in a system directory, and the Windows user registry. The plist and HKLM locations need admin rights to write, so they resist tampering. The docs describe the user-registry location as a convenience default rather than an enforcement channel, because users can write to it without elevation.
| Control | Managed setting keys | What it achieves |
|---|---|---|
| Permission lockdown | allowManagedPermissionRulesOnly, permissions.disableBypassPermissionsMode | Only the organisation’s permission rules count; nobody skips permission checks |
| MCP control | allowedMcpServers, deniedMcpServers, allowManagedMcpServersOnly | Only approved servers can be added or connected |
| Plugin sources | strictKnownMarketplaces, blockedMarketplaces | Plugins come only from approved marketplaces |
| Hooks | allowManagedHooksOnly | Only hooks the organisation deploys run |
| Customisation lockdown | strictPluginOnlyCustomization | Skills, agents, hooks and MCP servers only from plugins or managed settings |
| Models and versions | availableModels, minimumVersion | Approved models only; no downgrade below a floor |
Organisation instructions have a managed home too. A managed-policy CLAUDE.md at the system path loads in every session, and the memory docs say it can’t be excluded by individual settings. But it is still context, not enforcement. The same docs say Claude treats CLAUDE.md as context, not enforced configuration, and point to a PreToolUse hook when an action must be blocked whatever Claude decides.
Sharing the team toolkit: CLAUDE.md, MCP and plugins
Beyond settings, three shared assets make a team’s Claude Code consistent. The project CLAUDE.md (in the repo root or .claude/) carries commands, conventions and gotchas; personal notes go in a gitignored CLAUDE.local.md. How to structure and scope those files is covered in the CCAR-F Claude Code lessons. Project-scoped MCP servers live in .mcp.json at the repo root, committed so the team shares them. Interactive sessions ask each developer to approve a project server before using it. Plugins bundle skills, hooks, subagents and MCP servers into one installable unit, and a marketplace is a catalogue of them. Listing a marketplace under extraKnownMarketplaces and plugins under enabledPlugins in the committed settings file means teammates get them automatically once they trust the folder.
Reviewing a repo’s Claude Code setup before it is shared
- Passes: Build and test commands in
.claude/settings.jsonallow rules - Fails:
.mcp.jsonheaders contain a literal API tokenuse${VAR}expansion from each user’s environment - Check: “Never read .env” written only in CLAUDE.mdadvisory; add a deny rule or managed policy
- Passes:
.claude/settings.local.jsonexcluded from git - Passes: Team plugin from an approved marketplace
- Missing: Sandbox network allowlist for Bashdeny rules alone don’t stop
curl
.mcp.json supports environment-variable expansion (${VAR} and ${VAR:-default}) in commands, arguments, URLs and headers. That lets the committed file name the server while each developer supplies their own token. As a safeguard, Claude Code expands a set of well-known credential variables, such as ANTHROPIC_API_KEY, as empty in remote server URLs and headers so they don’t leak to third parties. When the same MCP server name is defined in several places, local scope wins over project, and project over user.
Traps the wrong answers are built from
| Tempting but wrong | Do this instead |
|---|---|
| Writing a security requirement only in CLAUDE.md | Enforce it with managed deny rules, hooks, the sandbox or an MCP allowlist; CLAUDE.md is guidance. |
Committing API tokens in .mcp.json or .claude/settings.json | Reference ${VAR} and have each developer supply credentials locally. |
| Putting organisation policy in the shared project settings file | Deliver it as managed settings, which developers and repos can’t override. |
| Relying on the Windows user registry to enforce policy | Use server-managed settings, HKLM/plist policy or the system managed-settings.json file. |
| Locking every rule to managed-only on day one | Enforce the few true policies centrally and let repos add their own allow rules for build and test commands. |
You should now be able to
- Place a setting at the right scope: managed, command line, project local, shared project or user.
- Predict the effective configuration when values override and lists merge across scopes.
- Use deny rules, hooks, the sandbox and managed MCP or plugin controls for rules that must be enforced.
- Choose a managed-settings delivery mechanism that fits the provider and device fleet.
- Share CLAUDE.md, project MCP servers and plugins through the repository without committing secrets.