.claude/settings.json is not a small file anymore. Claude Code has shipped enough settings — sandboxing, auto mode, hooks, remote control, managed policy — that no one keeps the full field list in their head. Most guides cover the five or six keys people actually set by hand (model, permissions, env, hooks) and stop there.
This is the field list itself: every documented key, grouped by what it actually controls, plus the precedence order that decides which file wins when two of them disagree.
If you’re trying to understand where a specific setting came from — or why one teammate’s Claude Code behaves differently from another’s — start with the precedence table below, not the field list.
Where settings.json lives
Claude Code reads configuration from five sources, checked in this order (highest priority first):
| Priority | Scope | Location | Git-tracked |
|---|---|---|---|
| 1 | Managed / enterprise | /Library/Application Support/ClaudeCode/ (macOS), /etc/claude-code/ (Linux/WSL), or MDM policy | N/A — deployed by admins |
| 2 | CLI arguments | Flags like --model, --permission-mode | N/A — session only |
| 3 | Local | .claude/settings.local.json | No — auto-gitignored |
| 4 | Project | .claude/settings.json | Yes — commit this |
| 5 | User | ~/.claude/settings.json | No — machine-local |
One exception matters more than the rest of the table: permissions.allow, permissions.deny, and permissions.ask don’t override across scopes — they merge. A deny rule in managed settings and an allow rule in your local settings both apply; Claude Code doesn’t silently drop one. Every other key follows strict override (higher scope wins, full stop).
Managed settings can’t be overridden by anything below them. If your org pushes forceLoginMethod: "oauth", no .claude/settings.local.json on any laptop changes that.
Fields by category
Model and session behavior
| Field | Type | What it does |
|---|---|---|
model | string | Default model ID or alias ("sonnet", "opus", "haiku") |
fallbackModel | array | Fallback chain if the primary model is unavailable (max 3 entries) |
availableModels | array | Restricts which models can be selected at all |
enforceAvailableModels | boolean | Extends that restriction to the default model too |
effortLevel | string | Persists reasoning effort: "low", "medium", "high", "xhigh" |
fastMode | boolean | Enables fast mode where the model supports it |
alwaysThinkingEnabled | boolean | Turns on extended thinking by default |
agent | string | Runs the main thread as a named subagent |
Permissions and sandboxing
| Field | Type | What it does |
|---|---|---|
permissions.allow / .deny / .ask | array | The three permission rule lists — these merge across scopes |
permissions.disableAutoMode | string | Set "disable" to turn off auto mode from within permissions |
sandbox.filesystem.disabled | boolean | Disables filesystem isolation (managed only) |
sandbox.credentials.files | array | Masks or blocks specific files: [{"path": "...", "mode": "mask|deny", "extract": "..."}] |
sandbox.credentials.envVars | array | Same, for environment variables by name |
handlePermissionError | boolean | Auto-skips (rather than halts on) permission errors during batch operations |
autoMode | object | Customizes the auto-mode classifier: environment, allow, soft_deny, hard_deny arrays |
autoMode.classifyAllShell | boolean | Routes every shell command through the classifier, not just flagged ones |
disableAutoMode | string | Set "disable" to prevent auto mode activating at all |
This is the part worth reading twice if you’ve deployed sandbox.credentials — it’s the newest and least-documented category, and it’s the one that actually stops a leaked .env from reaching a tool call rather than just warning about it after the fact.
Hooks and automation
| Field | Type | What it does |
|---|---|---|
hooks.beforeToolCall | array | Runs before each tool invocation |
hooks.afterToolCall | array | Runs after each tool invocation |
hooks.beforeTurn / .afterTurn | array | Runs before/after each conversation turn |
hooks.configChange | array | Fires when settings change |
disableAllHooks | boolean | Disables all hooks and the custom status line |
allowManagedHooksOnly | boolean | Only managed/SDK/force-enabled plugin hooks run (managed only) |
allowedHttpHookUrls | array | Allowlist of URL patterns HTTP hooks may target (supports *) |
disableSkillShellExecution | boolean | Disables inline shell execution inside skills/commands |
Hook syntax and payloads are documented separately — see our Hooks reference for the event schema. This table only covers the settings.json keys that turn hooks on or off.
MCP and plugins
| Field | Type | What it does |
|---|---|---|
enableAllProjectMcpServers | boolean | Auto-approves every server in the project’s .mcp.json |
enabledMcpjsonServers / disabledMcpjsonServers | array | Approve or reject specific .mcp.json servers by name |
allowedMcpServers / deniedMcpServers | array | Managed allow/deny lists (deny wins) |
allowManagedMcpServersOnly | boolean | Blocks users from adding servers outside the managed list |
allowAllClaudeAiMcps | boolean | Loads claude.ai connectors alongside managed-mcp.json |
strictKnownMarketplaces | boolean | Restricts plugins to the official Anthropic marketplace |
blockedMarketplaces | array | Denylist of plugin marketplace sources |
pluginDevelopmentMode | boolean | Enables local plugin development/testing |
Worth flagging since it trips people up: MCP servers are not configured in settings.json. They live in ~/.claude.json (user), .mcp.json (project), or managed-mcp.json (managed). The keys above only control which of those servers are allowed to load — not what they connect to.
Memory and context
| Field | Type | What it does |
|---|---|---|
claudeMd | string | Organization-managed memory content, in CLAUDE.md format (managed only) |
claudeMdExcludes | array | Glob patterns of CLAUDE.md files to skip when loading hierarchy |
autoMemoryEnabled | boolean | Enables auto memory read/write (default true) |
autoMemoryDirectory | string | Custom path for auto memory storage |
autoCompactEnabled | boolean | Auto-compacts the conversation as context fills (default true) |
autoCompactWindow | number | Token threshold that triggers auto-compact (100,000–1,000,000) |
Git and attribution
| Field | Type | What it does |
|---|---|---|
includeCoAuthoredBy | boolean | Adds a Co-authored-by: trailer to commits made by Claude Code |
attribution.commit / .pr | string | Customizes the commit/PR attribution text |
gitAttributeName / gitAttributeEmail | string | Overrides git identity for commits Claude Code makes |
gitDiffUnified | number | Lines of unified diff context shown (default 3) |
gitMaxDiffSize | number | Max diff size in bytes before Claude Code stops reading it (default 1,000,000) |
Auth and identity
| Field | Type | What it does |
|---|---|---|
apiKeyHelper | string | Shell command that generates the value for X-Api-Key / Authorization: Bearer |
forceLoginMethod | string | Requires "api-key", "oauth", "device-code", or "anthropic-api-key" (managed only) |
forceLoginOrgUUID | string | Forces login to a specific org (managed only) |
awsAuthRefresh / awsCredentialExport | string | Custom scripts for AWS credential handling (Bedrock users) |
Interface and notifications
| Field | Type | What it does |
|---|---|---|
outputStyle | string | "auto", "fullscreen", or "compact" |
tui | string | Terminal renderer: "auto", "classic", "modern" |
statusLine | string | Custom status line template ({{model}}, {{tokens}}, etc.) |
showStatusBar | boolean | Shows token/cost info in the status bar (default true) |
notificationLevel | string | "silent", "minimal", "normal", "verbose" |
editorMode | string | "normal" or "vim" key bindings for the input prompt |
textWrapColumn | number | Hard-wraps output at a column; 0 disables |
axScreenReader | boolean | Renders flat, screen-reader-friendly text |
askUserQuestionTimeout | string | Idle time before an unanswered AskUserQuestion auto-continues (default "never") |
Cleanup, updates, and misc
| Field | Type | What it does |
|---|---|---|
cleanupPeriodDays | number | Auto-deletes session files older than this (default 30, min 1) |
autoUpdatesChannel | string | "latest" or "stable" |
requiredMinimumVersion / requiredMaximumVersion | string | Version gate, e.g. "2.1.0" |
env | object | Environment variables applied to every session |
disableRemoteControl | boolean | Turns off Remote Control entirely |
agentPushNotifEnabled | boolean | Allows push notifications when Remote Control is connected (default false) |
disableArtifact / enableArtifact | boolean | Toggles the Artifact publishing tool |
taskCaptureMode | string | "auto" (Claude decides what’s a task) or "manual" |
A worked example: what actually wins
Say your org’s managed settings deny Bash(curl *). Your project .claude/settings.json doesn’t mention curl at all. Your local .claude/settings.local.json has "allow": ["Bash(curl *)"] because you added it while debugging.
What happens when Claude Code tries to run curl?
It’s blocked. Managed deny rules can’t be overridden by allow rules anywhere below them — remember, permission rules merge rather than override, and a deny always wins over an allow when both match the same pattern. Your local allow entry does nothing here except confuse you into thinking it should have worked.
This is the single most common support question in Claude Code permission threads: “I added an allow rule and it’s still blocked.” The answer is almost always a managed or project-level deny sitting above it in the merge.
Minimal example that touches most categories
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"model": "claude-sonnet-5",
"outputStyle": "fullscreen",
"cleanupPeriodDays": 30,
"includeCoAuthoredBy": true,
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1"
},
"permissions": {
"allow": ["Bash(npm run lint)", "Bash(npm run test *)"],
"deny": ["Bash(curl *)", "Read(./.env)", "Read(./.env.*)"]
},
"sandbox": {
"credentials": {
"files": [{ "path": ".env", "mode": "deny" }]
}
},
"hooks": {
"beforeToolCall": [
{ "type": "http", "url": "https://hooks.example.com/before" }
]
}
}
Commit this as .claude/settings.json. Anything personal — your preferred editorMode, a local allow rule for a command only you run — belongs in .claude/settings.local.json, which should already be in your .gitignore.
FAQ
Where do I put MCP server config?
Not in settings.json. Use .mcp.json (project, git-tracked) or ~/.claude.json (user). settings.json only holds allow/deny lists for which servers are permitted to load.
Which file should I commit to git?
.claude/settings.json (project scope). Never commit .claude/settings.local.json — it’s meant to be machine-specific and Claude Code auto-adds it to .gitignore when it’s created.
Do permission rules override or merge?
They merge. allow, deny, and ask arrays from every applicable scope are concatenated, and deny wins over allow on a matching pattern.
What’s the difference between disableAutoMode and permissions.disableAutoMode?
Same effect (turns off auto mode), different location — one is a top-level key, the other lives inside the permissions object. Both accept the string "disable".
Can I restrict which models a user can pick?
Yes — availableModels restricts the picker, and enforceAvailableModels: true extends that restriction to the default model as well.
How do I stop a settings.local.json field from being accidentally committed?
Claude Code gitignores it automatically when the file is first created. If it’s already tracked, remove it with git rm --cached .claude/settings.local.json and add the pattern to .gitignore manually.
Browse real project configurations — not just field lists — in our rules gallery, including settings.json examples pulled from open-source repos that use hooks, sandboxing, and managed permission rules in production.