Skip to main content

Anatomy of .claude/settings.json

Every key, matcher, permission, and gotcha explained.

.claude/settings.json

The control tower for Claude Code โ€” every key, every matcher, every gotcha.


TL;DRโ€‹

  • .claude/settings.json is the single JSON file that controls Claude's behavior in a workspace.
  • It governs permissions, hooks, env vars, MCP servers, model choice, telemetry, UI โ€” and more.
  • Three locations stack with precedence: Local > Workspace > Global > Defaults.
  • Commit settings.json; never commit settings.local.json (personal/secret overrides).
  • Treat it like production code: review, version, test before deploying.

Why this mattersโ€‹

Think of settings.json as the circuit breaker panel for your AI teammate. Flip the right switches and you get a safe, predictable collaborator. Flip the wrong ones (or none) and you've handed it the master key to your repo.

Bottom line: This one file decides what Claude can, can't, and must do.


File Location & Precedenceโ€‹

ScopePathCommitted?Purpose
DefaultsBuilt-inn/aSensible factory defaults
Global~/.claude/settings.jsonNoYour personal cross-project defaults
Workspace.claude/settings.jsonโœ… YesTeam-shared rules
Local.claude/settings.local.jsonโŒ No (.gitignore it)Per-developer overrides, secrets

Bottom line: Lower in the table = higher precedence.


Top-Level Keys (the full map)โ€‹

KeyPurposeRequiredDefault
versionConfig versionNo"1.0"
modelModel selection & fallback behaviorNoWorkspace default
permissionsSandbox allow/deny rulesNoStrict deny
hooksLifecycle event automationNoNone
mcpServersExternal tool connectionsNoNone
envEnvironment variablesNo{}
featuresFeature flagsNoBuilt-in defaults
telemetryAnalytics & usage opt-inNoEnabled
uiUI & display preferencesNoBuilt-in defaults
loggingLogging configurationNoinfo
securitySecurity & complianceNoSafe defaults

1. versionโ€‹

{ "version": "1.0" }

โš ๏ธ Gotcha: A missing version is silently treated as "1.0". Set it explicitly so future schema changes don't bite you.


2. model โ€” selection & fallback chainโ€‹

"model": {
"default": "claude-opus-4",
"fallback": ["claude-sonnet-4", "claude-haiku-4"],
"temperature": 0.2,
"max_tokens": 8000
}
FieldPurpose
defaultPrimary model
fallbackOrdered list tried on error/budget cap
temperature0.0โ€“1.0; lower = more deterministic
max_tokensOutput cap per turn

๐Ÿ’ก Tip: Set temperature: 0.2 for code; 0.7 for ideation.


3. permissions โ€” sandbox rules โญ the big oneโ€‹

"permissions": {
"allow": [
"Read",
"Edit",
"Bash(npm run *)",
"Bash(git *)"
],
"deny": [
"WriteOutsideWorkspace",
"Bash(rm -rf *)",
"Bash(sudo *)"
],
"ask": [
"Bash(curl *)",
"WebSearch"
]
}

How matching worksโ€‹

  1. Rules are matched in order, first match wins.
  2. deny overrides allow only if it appears earlier or is more specific.
  3. Wildcards use glob-style patterns, not full regex.
  4. Unmatched actions fall through to a default deny.

Permission categoriesโ€‹

CategoryExamples
File opsRead, Write, Edit, WriteOutsideWorkspace
ShellBash, Bash(npm *), Bash(git *)
NetworkWebSearch, WebFetch, WebFetch(domain:github.com)
MCP toolsMCP:*, MCP:github, MCP:postgres(SELECT *)
Built-in toolsGrep, Glob, Task, TodoWrite

Common gotchas โš ๏ธโ€‹

  • Bash(sudo *) in allow = privilege escalation. Never.
  • Bash(*) is way too broad โ€” prefer Bash(npm run *).
  • WriteOutsideWorkspace blocks edits outside the repo root โ€” keep it in deny.
  • Specific patterns override wildcards: list Bash(rm -rf *) in deny before any Bash(*) in allow.
  • ask prompts the user โ€” great for "risky but sometimes needed" actions like curl.

4. hooks โ€” lifecycle automationโ€‹

"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{ "type": "command", "command": "./scripts/pre-tool.sh" }
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{ "type": "command", "command": "npm run lint" },
{ "type": "command", "command": "npm test" }
]
}
],
"PreCommit": [
{
"matcher": "*",
"hooks": [
{ "type": "command", "command": "./scripts/secret-scan.sh" }
]
}
],
"Notification": [
{
"matcher": "*",
"hooks": [
{ "type": "webhook", "url": "https://hooks.slack.com/...", "timeoutMs": 5000 }
]
}
]
}

Hook eventsโ€‹

EventFires whenTypical uses
PreToolUseBefore any tool callBlock unsafe commands, secret scan, policy checks
PostToolUseAfter a tool callLint, tests, format, notify
PreCommitBefore git commitSecret scan, policy, licence check
NotificationImportant eventsSlack/webhook, audit
SessionStartSession opensLoad context, env summary
SessionEndSession closesSave summary, audit
ErrorHook or tool errorAlert, rollback, capture logs

Hook entry formatโ€‹

{
"matcher": "<regex>", // when this hook runs
"hooks": [
{ "type": "command", "command": "./scripts/x.sh" },
{ "type": "webhook", "url": "https://...", "timeoutMs": 5000 },
{ "type": "script", "script": "./scripts/policy.js", "env": { "FOO": "bar" } },
{ "type": "builtin", "action": "lint" },
{ "type": "custom", "name": "my-integration", "config": { } }
],
"concurrency": "sequential" // or "parallel"
}

Hook type cheat sheetโ€‹

TypeWhat it does
commandRuns a shell command
scriptRuns a script file (Node/Python/Bash)
webhookSends an HTTP request
builtinCalls an internal Claude action
customHooks a registered custom integration

Matcher language โ€” what can they actually match?โ€‹

Matchers are regex-like patterns matched against the tool name (and sometimes args).

MatcherMatchesExample
*EverythingAll tools & all actions
ReadRead operationsRead file, list dir, search
WriteWrite operationsCreate file, overwrite
EditEdits to existing filesPatch, replace, apply diff
Write|EditOR patternEither Write or Edit
BashAll Bash commandsIncludes sub-matchers
Bash(npm *)Shell with arg patternnpm test, npm install
Bash(git *)Git commandsgit status, git commit
Bash(rm -rf *)Dangerous deletionrm -rf /tmp/*
WebSearch / WebFetchNetwork opsWebSearch(...)
MCP:*Any MCP toolAll MCP calls
MCP:githubOnly GitHub MCPIssues, PR actions
Tool:fsFilesystem toolsfs:read, fs:write
Tool:browserBrowser automationbrowser:navigate, click

Hook gotchas โš ๏ธโ€‹

  • Hooks block the agent until they finish โ€” keep them under 5 s.
  • A PreToolUse hook that exits non-zero blocks the tool call (use exit 1 to deny).
  • Hooks are NOT sandboxed โ€” they run with your user's permissions. Audit script contents.
  • webhook hooks should have short timeoutMs (1โ€“5 s) โ€” avoid hanging the session.
  • concurrency: "parallel" is faster but unsafe if hooks share state.
  • Matchers are case-sensitive.
  • A * matcher fires on every event โ€” use sparingly.

5. mcpServers โ€” external tool connectionsโ€‹

"mcpServers": {
"github": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@anthropic/mcp-github"],
"env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
},
"postgres": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@anthropic/mcp-postgres"],
"env": { "DATABASE_URL": "${DATABASE_URL}" }
}
}

โš ๏ธ Gotcha: You can define MCP servers here, but a dedicated .mcp.json is preferred for cleanliness. If both exist, .mcp.json typically wins โ€” verify with claude mcp list.

Other gotchas

  • Never hard-code tokens โ€” always ${ENV_VAR} interpolation.
  • The server name is the alias Claude uses (@github).
  • A failed server start = the tool silently becomes unavailable. Check claude mcp status.

6. env โ€” environment variablesโ€‹

"env": {
"MAX_THINKING_TOKENS": "100000",
"CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "50",
"NO_COLOR": "1",
"NODE_OPTIONS": "--max-old-space-size=4096"
}

Common variablesโ€‹

VariablePurposeExample
MAX_THINKING_TOKENSCap reasoning tokens per turn"100000"
CLAUDE_AUTOCOMPACT_PCT_OVERRIDEAuto-compact context at % usage"50"
NO_COLORDisable ANSI colors"1"
CLAUDE_CODE_TELEMETRY_DISABLEDDisable telemetry"1"
NODE_OPTIONSNode runtime options"--max-old-space-size=4096"

โš ๏ธ Gotcha: Values must be strings โ€” "50", not 50. Numbers will silently coerce or be rejected.


7. features โ€” feature flagsโ€‹

"features": {
"strictComplexity": true,
"skipPermissionsConfirm": false,
"codebaseIndexing": true,
"fastApply": true,
"enableTelemetry": false,
"autoCompact": true,
"codebaseSearch": true,
"showToolUseNotifications": true,
"allowedDomains": ["github.com", "docs.anthropic.com"]
}
FlagWhat it does
autoCompactAuto-manage context size
skipPermissionsConfirmSkip confirm prompts (dangerous)
codebaseIndexingIndex codebase for search
fastApplyApply patches faster
allowedDomainsRestrict web fetch to a list

โš ๏ธ Gotcha: skipPermissionsConfirm: true removes the human safety net. Only use in CI or isolated containers.


8. telemetryโ€‹

"telemetry": {
"enabled": false,
"level": "minimal",
"endpoint": null
}

Set enabled: false for regulated environments.


9. ui โ€” display preferencesโ€‹

"ui": {
"theme": "auto", // auto | light | dark
"diffMode": "unified", // unified | split | side-by-side
"showToolUseNotifications": true,
"expandThoughts": false
}

10. loggingโ€‹

"logging": {
"level": "info", // trace | debug | info | warn | error
"file": ".claude/logs/claude-code.log",
"maxSizeMB": 20,
"maxFiles": 5
}

โš ๏ธ Gotchas:

  • Logs may contain file paths and command args โ€” review before sharing.
  • Rotate logs to avoid disk bloat.
  • Never log secrets โ€” scrub env values in custom hooks.

11. securityโ€‹

"security": {
"secretScan": true,
"blockSecretCommit": true,
"scanPatterns": ["api[_-]?key", "secret", "token"],
"auditLog": ".claude/logs/audit.log"
}

Complete Annotated Exampleโ€‹

{
"version": "1.0",
"name": "My App Workspace",

// โ”€โ”€ Model selection & fallback chain โ”€โ”€
"model": {
"default": "claude-opus-4",
"fallback": ["claude-sonnet-4", "claude-haiku-4"],
"temperature": 0.2
},

// โ”€โ”€ Sandbox permissions โ”€โ”€
"permissions": {
"allow": ["Read", "Edit", "Bash(npm run *)", "Bash(git *)"],
"deny": ["WriteOutsideWorkspace", "Bash(rm -rf *)", "Bash(sudo *)"],
"ask": ["Bash(curl *)", "WebSearch"]
},

// โ”€โ”€ Lifecycle hooks โ”€โ”€
"hooks": {
"PreToolUse": [
{ "matcher": "Write|Edit",
"hooks": [{ "type": "command", "command": "./scripts/pre-tool.sh" }] }
],
"PostToolUse": [
{ "matcher": "Write|Edit",
"hooks": [
{ "type": "command", "command": "npm run lint" },
{ "type": "command", "command": "npm test" }
] }
],
"PreCommit": [
{ "matcher": "*",
"hooks": [{ "type": "command", "command": "./scripts/secret-scan.sh" }] }
],
"SessionEnd": [
{ "matcher": "*",
"hooks": [{ "type": "command", "command": "./scripts/session-end.sh" }] }
]
},

// โ”€โ”€ MCP servers โ”€โ”€
"mcpServers": {
"github": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@anthropic/mcp-github"],
"env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
},
"postgres": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@anthropic/mcp-postgres"],
"env": { "DATABASE_URL": "${DATABASE_URL}" }
}
},

// โ”€โ”€ Environment variables โ”€โ”€
"env": {
"MAX_THINKING_TOKENS": "100000",
"CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "50",
"NO_COLOR": "1"
},

// โ”€โ”€ Feature flags โ”€โ”€
"features": {
"autoCompact": true,
"codebaseIndexing": true,
"fastApply": true,
"allowedDomains": ["github.com", "docs.anthropic.com"]
},

// โ”€โ”€ Telemetry โ”€โ”€
"telemetry": { "enabled": false },

// โ”€โ”€ UI โ”€โ”€
"ui": { "theme": "auto", "diffMode": "unified" },

// โ”€โ”€ Logging โ”€โ”€
"logging": {
"level": "info",
"file": ".claude/logs/claude-code.log",
"maxSizeMB": 20,
"maxFiles": 5
},

// โ”€โ”€ Security โ”€โ”€
"security": {
"secretScan": true,
"blockSecretCommit": true,
"auditLog": ".claude/logs/audit.log"
}
}

Variants for Different Team Settingsโ€‹

๐Ÿ‘ค Solo developer (permissive but safe)โ€‹

{
"permissions": {
"allow": ["Read", "Edit", "Bash(npm *)", "Bash(git *)", "WebSearch"],
"deny": ["WriteOutsideWorkspace", "Bash(rm -rf *)", "Bash(sudo *)"]
},
"hooks": {
"PostToolUse": [
{ "matcher": "Write|Edit",
"hooks": [{ "type": "command", "command": "npm run lint -- --fix" }] }
]
}
}

๐Ÿ‘ฅ Small team (shared standards)โ€‹

{
"permissions": {
"allow": ["Read", "Edit", "Bash(npm run *)", "Bash(git *)"],
"deny": ["WriteOutsideWorkspace", "Bash(rm -rf *)", "Bash(sudo *)"],
"ask": ["WebSearch"]
},
"hooks": {
"PreToolUse": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "./scripts/policy.sh" }] }],
"PostToolUse": [{ "matcher": "Write|Edit", "hooks": [{ "type": "command", "command": "npm run lint" }] }],
"PreCommit": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "./scripts/secret-scan.sh" }] }]
}
}

๐Ÿข Enterprise / regulated (fail closed)โ€‹

{
"permissions": {
"allow": ["Read", "Bash(npm test)"],
"deny": ["Write", "Edit", "WriteOutsideWorkspace",
"Bash(curl *)", "Bash(sudo *)", "WebSearch", "WebFetch"]
},
"features": { "skipPermissionsConfirm": false, "enableTelemetry": false },
"telemetry": { "enabled": false },
"hooks": {
"PreToolUse": [
{ "matcher": "*",
"hooks": [
{ "type": "command", "command": "./scripts/policy-check.sh" },
{ "type": "webhook", "url": "https://siem.corp/audit", "timeoutMs": 3000 }
] }
],
"SessionEnd": [
{ "matcher": "*",
"hooks": [{ "type": "command", "command": "./scripts/audit-flush.sh" }] }
]
},
"security": { "secretScan": true, "blockSecretCommit": true, "auditLog": "/var/log/claude/audit.log" }
}

๐ŸŒ Open-source (welcoming + safe)โ€‹

{
"permissions": {
"allow": ["Read", "Edit", "Bash(npm test)", "WebSearch"],
"deny": ["WriteOutsideWorkspace", "Bash(rm -rf *)"]
},
"features": { "allowedDomains": ["github.com", "docs.anthropic.com", "npmjs.com"] }
}

Bottom line: Start permissive solo, tighten as team & risk grow.


Common Patterns & Recipesโ€‹

Pattern 1 โ€” Auto-lint after writesโ€‹

"PostToolUse": [{ "matcher": "Write|Edit",
"hooks": [{ "type": "command", "command": "npx prettier --write ." }] }]

Pattern 2 โ€” Block dangerous deletes (specific before broad)โ€‹

"permissions": {
"deny": ["Bash(rm -rf /*)", "Bash(rm -rf *)"],
"allow": ["Bash(rm *)"]
}

Pattern 3 โ€” Domain allowlist for webโ€‹

"features": { "allowedDomains": ["github.com", "docs.anthropic.com"] }

Pattern 4 โ€” Per-developer overrides (settings.local.json)โ€‹

{
"model": { "default": "claude-haiku-4" },
"ui": { "theme": "dark" }
}

Pattern 5 โ€” Slack on every session endโ€‹

"SessionEnd": [{ "matcher": "*",
"hooks": [{ "type": "webhook", "url": "https://hooks.slack.com/...", "timeoutMs": 3000 }] }]

Best Practices โœ…โ€‹

  • Commit settings.json; gitignore settings.local.json.
  • Start strict (deny: ["*"] + explicit allow), then loosen with intent.
  • List specific deny rules before broad allow rules.
  • Keep hooks under 5 s โ€” they block the agent.
  • Use ${ENV_VAR} for all secrets.
  • Review changes in PR like production code.
  • Test in settings.local.json before promoting to workspace.

Anti-Patterns โŒโ€‹

  • Bash(*) in allow โ€” too broad.
  • Hard-coded tokens / secrets in the file.
  • skipPermissionsConfirm: true outside of CI.
  • Hooks that block for > 10 s.
  • Editing settings.json directly without team review.
  • Mixing personal preferences (theme, model) with team rules.

Troubleshootingโ€‹

SymptomLikely causeFix
Tool unexpectedly deniedDeny pattern too broadInspect with claude config explain
Hook doesn't fireMatcher regex mismatchTest with matcher: "*" first
${VAR} not interpolatedVar not exportedexport VAR=... then restart
Slow agentHooks too longProfile + move to async
Settings ignoredWrong file path/scopeConfirm precedence (Local > Workspace > Global)
Webhook hangs sessionMissing timeoutMsSet timeoutMs: 3000
MCP tool missingServer failed to startclaude mcp status to debug

Minimal Starter Template (copy-paste)โ€‹

{
"version": "1.0",
"permissions": {
"allow": ["Read", "Edit", "Bash(npm run *)"],
"deny": ["WriteOutsideWorkspace", "Bash(rm -rf *)"]
},
"hooks": {
"PreToolUse": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "./scripts/pre-tool.sh" }] }],
"PostToolUse": [{ "matcher": "Write|Edit", "hooks": [{ "type": "command", "command": "npm run lint" }] }]
}
}

๐Ÿ’ก Tip: This file is powerful โ€” treat it like production code.


Glossaryโ€‹

  • Allow / Deny / Ask โ€” Permission verdicts: green, red, prompt-user.
  • Matcher โ€” A glob/regex-like pattern that scopes a hook or permission.
  • Precedence โ€” The order in which configs override each other.
  • Sandbox โ€” The set of allowed operations Claude may perform.
  • Hook โ€” Shell/webhook command bound to a lifecycle event.
  • Interpolation โ€” ${VAR} substitution from the process environment.
  • Fail closed โ€” Block on error (safe). Fail open โ€” Continue on error (convenient).

Further Readingโ€‹

  • Search: "Claude Code settings.json reference"
  • Search: "Claude Code permissions allow deny patterns"
  • Search: "Claude Code hooks PreToolUse PostToolUse"
  • Search: "glob vs regex permission matchers"