cheat sheet
Claude Code Settings and Permissions
Complete settings.json schema for Claude Code — permission rules, tool allowlists/denylists, environment variables, hooks, MCP, statusline, model selection, and file precedence.
Claude Code Settings and Permissions
What it is
Claude Code's settings system is a JSON-based configuration layer that controls which tools Claude may use, what shell commands require confirmation, MCP server registrations, environment variables, hooks, the statusline, and behavioral toggles. Settings live at two scopes — user-global (~/.claude/settings.json, applies everywhere) and project-local (.claude/settings.json, committed for team defaults) — with a git-ignored .claude/settings.local.json layered on top for personal overrides. Reach for settings.json whenever you find yourself accepting the same permission prompt twice, or when a teammate needs to inherit the same allow-list as you.
Settings file locations
Claude Code loads settings from multiple locations and merges them. More specific files override broader ones, but list-valued keys like permissions.allow are concatenated rather than replaced.
| File | Scope | Who edits it |
|---|---|---|
~/.claude/settings.json | User-global (all projects) | Personal defaults |
<project>/.claude/settings.json | Project (all users) | Commit to repo — team defaults |
<project>/.claude/settings.local.json | Project (local only) | Git-ignored — personal overrides |
Precedence (highest to lowest): settings.local.json → project settings.json → user settings.json.
# Show effective merged settings
claude /config
Output: (none — opens active settings in $EDITOR)
Full schema
The full set of top-level keys. Every key is optional; omit any you do not need.
{
"model": "claude-opus-4-7",
"permissions": {
"allow": [],
"deny": []
},
"env": {},
"apiKeyHelper": "",
"cleanupPeriodDays": 30,
"systemPrompt": "",
"appendSystemPrompt": "",
"mcpServers": {},
"hooks": {},
"statusLine": {
"type": "command",
"command": ""
},
"outputStyle": "default",
"includeCoAuthoredBy": true,
"telemetry": true
}
model
Set the default model for all sessions opened with this settings file in scope. Override per-session with /model <id> or the --model CLI flag.
{
"model": "claude-sonnet-4-6"
}
Valid values include "claude-opus-4-7", "claude-sonnet-4-6", and dated snapshots like "claude-haiku-4-5-20251001". Unknown IDs cause claude to error out on startup.
permissions
Control which tools Claude can use without prompting. Rules in allow are auto-approved; rules in deny are auto-rejected and override allow. When a tool call matches neither list, Claude prompts the user.
Permission rule syntax
"ToolName" // exact tool name — all uses
"ToolName(subcommand)" // specific subcommand or pattern
"Bash(git *)" // glob: any git subcommand
"Bash(npm run *:npm test)" // colon-delimited list of patterns
"mcp__<server>__<tool>" // MCP-exposed tool
Allow rules
Pre-approve specific tools so Claude never prompts for them:
{
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"LS",
"Bash(git log:git diff:git status:git blame)",
"Bash(npm test:npm run lint:npm run build)",
"Bash(python -m pytest:ruff check:ruff format)"
]
}
}
Deny rules
Always block specific tools regardless of what Claude requests. Deny takes precedence over allow, so you can broadly allow Bash while still blocking destructive subcommands.
{
"permissions": {
"deny": [
"Bash(rm *:rmdir *)",
"Bash(git push *:git force-push *)",
"WebSearch",
"WebFetch"
]
}
}
Available tool names
| Tool | What it controls |
|---|---|
Bash | Shell command execution |
Read | File reading |
Edit | File editing (existing files) |
Write | File creation / overwrite |
MultiEdit | Multi-file edits in one operation |
Glob | File pattern matching |
Grep | Content search |
LS | Directory listing |
WebSearch | Web search queries |
WebFetch | Fetching URLs |
TodoRead | Reading task list |
TodoWrite | Writing task list |
Task | Spawning subagents (see Subagents) |
Skill | Invoking a registered skill |
mcp__<server>__<tool> | Any MCP server tool |
For CI/CD use
--dangerously-skip-permissionsto bypass all prompts. Only safe in isolated environments — never interactive use.
Read-only project example
Useful for code review or analysis tasks where you don't want Claude writing files:
{
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"LS",
"Bash(git log:git diff:git status:git show)"
],
"deny": [
"Edit",
"Write",
"MultiEdit",
"Bash"
]
}
}
Safe CI example
Allow tests and linting but block file writes and network:
{
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"Bash(npm test:npm run lint:python -m pytest:ruff check)"
],
"deny": [
"Write",
"Edit",
"WebSearch",
"WebFetch",
"Bash(git push *)"
]
}
}
env
Inject environment variables into every Claude Code session opened in this scope. Useful for pointing to dev tools, setting API keys for MCP servers, or configuring language runtimes.
{
"env": {
"NODE_ENV": "development",
"DATABASE_URL": "postgresql://localhost/myapp_dev",
"PYTHONPATH": "/home/alice/Code/myproject/src"
}
}
Don't put
ANTHROPIC_API_KEYin a committed settings file. Usesettings.local.json, your shell profile, orapiKeyHelperinstead.
apiKeyHelper
A shell command whose stdout is read as the API key. Useful when the key is in a secrets manager rather than an environment variable. The command runs once at startup.
{
"apiKeyHelper": "op read op://dev/anthropic/api_key"
}
{
"apiKeyHelper": "aws secretsmanager get-secret-value --secret-id anthropic --query SecretString --output text"
}
Output: (none — secret read at startup)
cleanupPeriodDays
How many days of session history to retain under ~/.claude/projects/<hash>/ before automatic cleanup. Default is 30. Bump it higher for projects you revisit infrequently; lower it to keep disk usage tight.
{
"cleanupPeriodDays": 90
}
systemPrompt and appendSystemPrompt
systemPrompt replaces the default Claude Code system prompt entirely — only use this if you know what you're doing, as you lose the built-in tool descriptions and behavior guidance. appendSystemPrompt adds text to the existing prompt, which is almost always what you actually want.
{
"appendSystemPrompt": "Always use Python 3.12+ syntax. Prefer match statements over if/elif chains where natural."
}
mcpServers
Register MCP servers persistently. The same servers can be added on the command line with claude mcp add, but settings.json is the canonical place for team-shared configuration. See MCP Servers for the full schema.
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
}
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "${DATABASE_URL}"]
}
}
}
${ENV_VAR}placeholders inmcpServers.*.argsandmcpServers.*.envare expanded at startup from the shell environment, so secrets never need to land in the JSON file.
hooks
Run shell commands at lifecycle events: PreToolUse, PostToolUse, UserPromptSubmit, Notification, Stop, SubagentStop, and PreCompact. See Hooks for the full schema.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{"type": "command", "command": "~/.claude/hooks/bash-guard.sh"}
]
}
]
}
}
statusLine
Configure the bottom statusline shown in the REPL. See Statusline for context variables, JSON input, and example scripts.
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}
outputStyle
Coarse control over Claude's response style. Default is "default"; alternatives include "concise" (terse, less narration), "explanatory" (more pedagogical), and "learning" (heavy narration suitable for pair-programming with a junior).
{
"outputStyle": "concise"
}
includeCoAuthoredBy
Whether git commit messages created by Claude should include a Co-Authored-By: trailer. Defaults to true. Set to false for projects where commit attribution policy forbids it.
{
"includeCoAuthoredBy": false
}
telemetry
Whether to send anonymous usage data to Anthropic. Defaults to true. Set to false to disable. Telemetry is also disabled when the environment variable CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 is set.
{
"telemetry": false
}
Complete example: team project settings
A realistic .claude/settings.json for a Python web project — committed to the repo so every contributor inherits the same defaults.
{
"model": "claude-sonnet-4-6",
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"LS",
"Bash(git log:git diff:git status:git blame:git stash)",
"Bash(npm install:npm test:npm run lint:npm run build)",
"Bash(python -m pytest:ruff check:ruff format:mypy)"
],
"deny": [
"Bash(git push *:git push --force *)",
"Bash(rm -rf *)",
"WebFetch"
]
},
"env": {
"NODE_ENV": "development",
"PYTHONPATH": "src"
},
"appendSystemPrompt": "This is a FastAPI + SQLAlchemy 2.0 project. Use async def for routes. Tests live in tests/.",
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "${DATABASE_URL}"]
}
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{"type": "command", "command": ".claude/hooks/bash-guard.sh"}]
}
]
},
"cleanupPeriodDays": 60
}
Complete example: personal global settings
{
"model": "claude-opus-4-7",
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"LS",
"Bash(git log:git diff:git status)",
"WebSearch"
]
},
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
},
"outputStyle": "concise",
"cleanupPeriodDays": 30
}
Environment variables (shell-level)
These variables affect Claude Code behavior when set in your shell before launching. They are the lowest-precedence layer — anything set in settings.json overrides them.
| Variable | Effect |
|---|---|
ANTHROPIC_API_KEY | API key (required unless apiKeyHelper is set) |
ANTHROPIC_BASE_URL | Override API endpoint (proxy or custom deployment) |
ANTHROPIC_MODEL | Default model (overridden by settings.json model) |
CLAUDE_CODE_MAX_OUTPUT_TOKENS | Cap output tokens per request |
CLAUDE_CODE_USE_BEDROCK | 1 to use Amazon Bedrock |
CLAUDE_CODE_USE_VERTEX | 1 to use Google Vertex AI |
AWS_REGION | Required when using Bedrock |
ANTHROPIC_VERTEX_PROJECT_ID | Required when using Vertex |
HTTP_PROXY / HTTPS_PROXY | Route traffic through a corporate proxy |
NO_PROXY | Comma-separated hosts to bypass the proxy |
NO_COLOR | Disable ANSI color output |
DISABLE_AUTOUPDATER | Set to 1 to disable automatic updates |
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC | Set to 1 to block telemetry/update checks |
BASH_DEFAULT_TIMEOUT_MS | Default Bash tool timeout in ms |
BASH_MAX_TIMEOUT_MS | Maximum allowed Bash tool timeout in ms |
export ANTHROPIC_API_KEY="sk-ant-..."
export ANTHROPIC_MODEL="claude-sonnet-4-6"
export DISABLE_AUTOUPDATER=1
claude
Output: (none — opens REPL with the specified configuration)
Permission rule grammar in depth
Permission rules use a simple grammar that combines a tool name with an optional parenthesised subcommand pattern. Patterns are not full shell globs — they match a prefix of the rendered command string.
"Bash(npm test)" // matches: npm test
// matches: npm test --watch
// does not match: pnpm test
"Bash(npm test:npm run *)" // colon separates alternatives
// matches: npm test
// matches: npm run lint
"Bash(git *)" // matches any git subcommand
// matches: git status
// matches: git push origin main ← danger!
For destructive families (rm, git push, npm publish), prefer narrow allow rules over broad ones, and pair them with deny rules.
{
"permissions": {
"allow": ["Bash(git *)"],
"deny": ["Bash(git push *:git push --force *:git reset --hard *)"]
}
}
Common pitfalls
- JSON comments —
settings.jsondoes not allow//comments; many editors highlight them as valid butclauderejects the file. Use a separate notes file instead. ~not expanded in some contexts — paths inhooks.*.commandandstatusLine.commandaccept~, but values insidemcpServers.*.argsdo not. Use absolute paths when in doubt.- Deny does not override prompts for unlisted tools — if a tool is neither allowed nor denied, Claude still prompts. To silence prompts entirely, you must list everything.
- Settings.local.json committed by mistake — verify
.claude/settings.local.jsonis in.gitignore; many templates omit it. - Merge surprises with array keys —
permissions.allowandpermissions.denyarrays are concatenated, not replaced; a personal allow-list cannot remove an entry that the project file already added. - Stale
mcpServersenv interpolation —${VAR}is only expanded at session start; changing the shell variable after launch does not propagate.
Real-world recipes
Restrict Claude to a single subdirectory
Useful for sandbox projects where Claude should never touch files outside one folder.
{
"permissions": {
"allow": [
"Read(src/*)",
"Edit(src/*)",
"Write(src/*)",
"Glob",
"Grep"
],
"deny": [
"Read(/etc/*:/home/alice/.ssh/*)",
"Edit(*)",
"Write(*)"
]
}
}
Wire a secret manager
Pull the API key from 1Password at startup so it never lands on disk.
{
"apiKeyHelper": "op read op://Personal/Anthropic/api_key"
}
Output: (none — secret resolved at session start)
Override the model only for a project
User-global says Opus, project-level says Haiku — Haiku wins because project settings have higher precedence.
{
"model": "claude-haiku-4-5-20251001"
}
Project-aware system prompt
{
"appendSystemPrompt": "This is a Cloudflare Workers project. Always prefer Workers APIs (KV, R2, D1) over Node-specific ones. The runtime is V8 isolates, not Node."
}
Quick diagnostic
claude /doctor
Output:
✅ API key found
✅ Node.js 22.4.0
✅ @anthropic-ai/claude-code 1.x.x
✅ API connectivity: OK
✅ Settings: ~/.claude/settings.json, .claude/settings.json merged cleanly