Anatomy of .claude/settings.json
Every key, matcher, permission, and gotcha explained.

The control tower for Claude Code โ every key, every matcher, every gotcha.
TL;DRโ
.claude/settings.jsonis 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 commitsettings.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โ
| Scope | Path | Committed? | Purpose |
|---|---|---|---|
| Defaults | Built-in | n/a | Sensible factory defaults |
| Global | ~/.claude/settings.json | No | Your personal cross-project defaults |
| Workspace | .claude/settings.json | โ Yes | Team-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)โ
| Key | Purpose | Required | Default |
|---|---|---|---|
version | Config version | No | "1.0" |
model | Model selection & fallback behavior | No | Workspace default |
permissions | Sandbox allow/deny rules | No | Strict deny |
hooks | Lifecycle event automation | No | None |
mcpServers | External tool connections | No | None |
env | Environment variables | No | {} |
features | Feature flags | No | Built-in defaults |
telemetry | Analytics & usage opt-in | No | Enabled |
ui | UI & display preferences | No | Built-in defaults |
logging | Logging configuration | No | info |
security | Security & compliance | No | Safe defaults |
1. versionโ
{ "version": "1.0" }
โ ๏ธ Gotcha: A missing
versionis 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
}
| Field | Purpose |
|---|---|
default | Primary model |
fallback | Ordered list tried on error/budget cap |
temperature | 0.0โ1.0; lower = more deterministic |
max_tokens | Output cap per turn |
๐ก Tip: Set
temperature: 0.2for code;0.7for 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โ
- Rules are matched in order, first match wins.
denyoverridesallowonly if it appears earlier or is more specific.- Wildcards use glob-style patterns, not full regex.
- Unmatched actions fall through to a default deny.
Permission categoriesโ
| Category | Examples |
|---|---|
| File ops | Read, Write, Edit, WriteOutsideWorkspace |
| Shell | Bash, Bash(npm *), Bash(git *) |
| Network | WebSearch, WebFetch, WebFetch(domain:github.com) |
| MCP tools | MCP:*, MCP:github, MCP:postgres(SELECT *) |
| Built-in tools | Grep, Glob, Task, TodoWrite |
Common gotchas โ ๏ธโ
Bash(sudo *)inallow= privilege escalation. Never.Bash(*)is way too broad โ preferBash(npm run *).WriteOutsideWorkspaceblocks edits outside the repo root โ keep it indeny.- Specific patterns override wildcards: list
Bash(rm -rf *)indenybefore anyBash(*)inallow. askprompts the user โ great for "risky but sometimes needed" actions likecurl.
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โ
| Event | Fires when | Typical uses |
|---|---|---|
PreToolUse | Before any tool call | Block unsafe commands, secret scan, policy checks |
PostToolUse | After a tool call | Lint, tests, format, notify |
PreCommit | Before git commit | Secret scan, policy, licence check |
Notification | Important events | Slack/webhook, audit |
SessionStart | Session opens | Load context, env summary |
SessionEnd | Session closes | Save summary, audit |
Error | Hook or tool error | Alert, 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โ
| Type | What it does |
|---|---|
command | Runs a shell command |
script | Runs a script file (Node/Python/Bash) |
webhook | Sends an HTTP request |
builtin | Calls an internal Claude action |
custom | Hooks a registered custom integration |
Matcher language โ what can they actually match?โ
Matchers are regex-like patterns matched against the tool name (and sometimes args).
| Matcher | Matches | Example |
|---|---|---|
* | Everything | All tools & all actions |
Read | Read operations | Read file, list dir, search |
Write | Write operations | Create file, overwrite |
Edit | Edits to existing files | Patch, replace, apply diff |
Write|Edit | OR pattern | Either Write or Edit |
Bash | All Bash commands | Includes sub-matchers |
Bash(npm *) | Shell with arg pattern | npm test, npm install |
Bash(git *) | Git commands | git status, git commit |
Bash(rm -rf *) | Dangerous deletion | rm -rf /tmp/* |
WebSearch / WebFetch | Network ops | WebSearch(...) |
MCP:* | Any MCP tool | All MCP calls |
MCP:github | Only GitHub MCP | Issues, PR actions |
Tool:fs | Filesystem tools | fs:read, fs:write |
Tool:browser | Browser automation | browser:navigate, click |
Hook gotchas โ ๏ธโ
- Hooks block the agent until they finish โ keep them under 5 s.
- A
PreToolUsehook that exits non-zero blocks the tool call (useexit 1to deny). - Hooks are NOT sandboxed โ they run with your user's permissions. Audit script contents.
webhookhooks should have shorttimeoutMs(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.jsonis preferred for cleanliness. If both exist,.mcp.jsontypically wins โ verify withclaude 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โ
| Variable | Purpose | Example |
|---|---|---|
MAX_THINKING_TOKENS | Cap reasoning tokens per turn | "100000" |
CLAUDE_AUTOCOMPACT_PCT_OVERRIDE | Auto-compact context at % usage | "50" |
NO_COLOR | Disable ANSI colors | "1" |
CLAUDE_CODE_TELEMETRY_DISABLED | Disable telemetry | "1" |
NODE_OPTIONS | Node runtime options | "--max-old-space-size=4096" |
โ ๏ธ Gotcha: Values must be strings โ
"50", not50. 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"]
}
| Flag | What it does |
|---|---|
autoCompact | Auto-manage context size |
skipPermissionsConfirm | Skip confirm prompts (dangerous) |
codebaseIndexing | Index codebase for search |
fastApply | Apply patches faster |
allowedDomains | Restrict web fetch to a list |
โ ๏ธ Gotcha:
skipPermissionsConfirm: trueremoves 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; gitignoresettings.local.json. - Start strict (
deny: ["*"]+ explicitallow), 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.jsonbefore promoting to workspace.
Anti-Patterns โโ
Bash(*)inallowโ too broad.- Hard-coded tokens / secrets in the file.
skipPermissionsConfirm: trueoutside of CI.- Hooks that block for > 10 s.
- Editing
settings.jsondirectly without team review. - Mixing personal preferences (theme, model) with team rules.
Troubleshootingโ
| Symptom | Likely cause | Fix |
|---|---|---|
| Tool unexpectedly denied | Deny pattern too broad | Inspect with claude config explain |
| Hook doesn't fire | Matcher regex mismatch | Test with matcher: "*" first |
${VAR} not interpolated | Var not exported | export VAR=... then restart |
| Slow agent | Hooks too long | Profile + move to async |
| Settings ignored | Wrong file path/scope | Confirm precedence (Local > Workspace > Global) |
| Webhook hangs session | Missing timeoutMs | Set timeoutMs: 3000 |
| MCP tool missing | Server failed to start | claude 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"