Claude Code settings.json, explained and tested
Paste your settings file. The doctor flags what Claude Code ignores, shows the fix, and tells you whether a command runs, asks first or gets blocked.
Findings
1 error · 2 warnings · 1 note- errorComments are not allowed in settings.json. Claude Code reads strict JSON and reports this file as a Settings Error.Delete the comment. Use a _comment key or a nearby README if you need a note.
- warning"Bash(git * main)": the * stands in for the subcommand, so this allows every git subcommand that ends that way (for git that includes options that run other programs). Claude Code warns about it at startup.Put the * after the subcommand, for example Bash(git log *).
- warning"Write(/src/**)": Claude Code checks file paths against Edit(...) and Read(...) rules only. A Write(path) rule is accepted but never consulted.Use Edit(/src/**).
- noteThe 5 allow rules in a committed project file apply only after each teammate accepts the workspace trust dialog. Deny and ask rules apply right away.
Test a tool call
as Project fileA deny rule matches. Deny is checked first, so nothing else can allow it.
It also matches the allow rule Bash(git * main), but deny is checked first.
Tested as if the JSON errors were fixed. Until they are, Claude Code skips this file.
Where is the Claude Code settings.json file?
Your personal settings live in ~/.claude/settings.json (on Windows %USERPROFILE%\.claude\settings.json). Each project can have a shared .claude/settings.json, committed for the team, and a personal .claude/settings.local.json that stays out of git. Organizations deploy managed-settings.json in a system folder. Installing Claude Code creates none of them.
Paths for macOS. On Windows, ~/.claude means %USERPROFILE%\.claude. Set CLAUDE_CONFIG_DIR to keep the home files somewhere else.
Claude Code writes two of these files for you. ~/.claude/settings.json appears the first time you change a /config option it stores there, such as the theme. .claude/settings.local.json appears the first time you pick "Yes, and don't ask again" on a permission prompt, and in a git repository Claude Code adds it to your global git excludes so it never lands in a commit.
It reads the shared .claude/settings.json from the folder you start in, so start Claude Code at the repository root to use a file committed there. The local file lives at the repository root even when you start in a subfolder.
Which settings file should I use?
Use ~/.claude/settings.json for preferences you want everywhere, such as theme and model. Put team rules, hooks and plugins in .claude/settings.json and commit it. Put personal overrides for one repository in .claude/settings.local.json. Anything your organization must enforce belongs in managed settings.
~/.claude/settings.json
Your user file applies in every project on this machine. Theme, model, notifications and your own deny rules go here.
To try a value for one session without saving it, pass it at start: claude --settings '{"model": "opus"}'. It sits above every file except managed settings and is gone when the session ends.
Which Claude Code setting wins?
Managed settings win, then command-line flags such as --settings, then .claude/settings.local.json, then .claude/settings.json, then ~/.claude/settings.json. A key set higher overrides the same key lower down, but lists such as permissions.allow merge across files instead of replacing each other.
model key from any file.{
"permissions": {
"allow": [
"Bash(npm run lint)",
"Bash(npm run test *)",
"Bash(git push *)",
"Bash(docker compose up *)"
],
"deny": [
"Bash(git push *)",
"Read(./.env)"
]
}
}The order for a single value, from the settings docs. Lists combine from every file instead of picking one.
Lists merge, and deny beats allow across files
Each file can add permissions.allow entries without removing another file's. That merge cuts both ways: a deny rule in your user file blocks an allow rule in the project file, and the reverse. A few model keys follow their own rules: fallbackModel takes its whole value from one file, modelPicker is read only from managed, --settings and user files, and a managed availableModels list replaces the lists in other files.
Keys where the stricter value wins
For a few security keys, Claude Code honors a restrictive value from a lower file even over managed settings:
| Key | Stricter value honored |
|---|---|
disableClaudeAiConnectors | true from any file |
isolatePeerMachines | true from any file |
enableArtifact | false from any file |
disableArtifact | true from any file |
maxEffortLevel | The lowest cap from any file, including --settings |
remoteControlAtStartup | false from the project or local file |
syncClaudeAiSkills, syncClaudeAiPlugins, useAutoModeDuringPlan | false from any file except the shared .claude/settings.json |
How do Claude Code permission rules work?
Rules look like Tool or Tool(specifier) and sit in permissions.allow, ask or deny. Claude Code checks deny first, then ask, then allow, and the first match decides, so a broad deny beats a narrow allow. In Bash rules, * matches any text, and each part of a compound command must match on its own.
Lit for the call in the doctor: Bash git push origin main. Blocked by Bash(git push *) on line 15. Change the call above and this updates.
Deny, then ask, then allow. Specificity does not matter: Bash(aws *) in deny blocks aws s3 ls even with Bash(aws s3 ls) in allow.
The doctor tests the file in the editor. Claude Code merges every file, so a deny elsewhere still wins.
The matcher passes 92 checks, most taken from the example tables on the official permissions page.
Does it match? The documented wildcard examples
Type a command and every rule from the docs' wildcard table says whether it matches. Click an example to load it.
| You write | Your command | Docs: matches | Docs: does not match |
|---|---|---|---|
Bash(npm run build) | no match | ||
Bash(npm run *) | no match | ||
Bash(git log * main) | no match | ||
Bash(git * main) | matches | ||
Bash(* --version) | no match | ||
Bash(ls *) | no match | ||
Bash(ls*) | no match | ||
Bash(* --help *) | no match |
* counts: Bash(ls *) skips lsof, Bash(ls*) does not. Bash(ls:*) equals Bash(ls *), but :* only works at the end.Compound commands and wrappers
Claude Code splits commands on &&, ||, ;, |, |&, & and newlines. An allow rule must match every part; a deny or ask rule fires on any part, including commands inside $( ). Before matching it strips timeout, time, nice, nohup, stdbuf, command, builtin, noglob and bare xargs, so Bash(npm test *) also covers timeout 30 npm test. Runners such as npx, devbox run and docker exec are not stripped, so Bash(devbox run *) also matches devbox run rm -rf ..
This allowlisting approach is often defeated by the model's own proclivity to get fancy with inline scripting.
What a Bash rule does not stop
A Bash rule matches the command text Claude writes. The same program called another way slips past it:
| Rule in deny | Stops | Does not stop |
|---|---|---|
Bash(curl *) | curl https://example.com | /usr/bin/curl https://example.com, sh -c 'curl https://example.com' |
Bash(rm *) | rm -rf build/ | /bin/rm -rf build/, bash -c 'rm -rf build/' |
Bash(git push *) | git push origin main | git -C . push origin main, git 'push' origin main |
I had to write my own auto approval system and plug it in as a hook.
Where a path rule points
Read and Edit rules use gitignore patterns with four anchors. A single leading slash is the trap: it anchors at the project root in a project file and at ~/.claude in your user file, never at the filesystem root.
permission enforcement that lives inside the tool it's supposed to police will always be one bug away from failing open
What should a starter settings.json contain?
Start small: the $schema line for autocomplete, deny rules for .env files and secrets, allow rules for the lint and test commands you run all day, and a rule that stops git push. Keep the default mode, notifications and model in your user file, not the one your team shares.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(npm run lint)",
"Bash(npm run test *)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
],
"defaultMode": "acceptEdits"
},
"preferredNotifChannel": "terminal_bell",
"cleanupPeriodDays": 90,
"statusLine": {
"type": "command",
"command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
}
}
Save to ~/.claude/settings.json
Applies in every project. model is read at start, so restart after saving.
Which Claude Code settings are worth changing first?
Ten keys do most of the work: cleanupPeriodDays to keep transcripts past 30 days, autoMemoryEnabled, preferredNotifChannel for a bell when a task ends, attribution, statusLine, model, spinnerTipsEnabled, env, enabledPlugins with extraKnownMarketplaces, and sandbox.enabled.
cleanupPeriodDays01
"cleanupPeriodDays": 90
Claude Code deletes transcripts older than this, silently, so old sessions vanish from /resume. The default is 30 days; 0 fails validation.
"cleanupPeriodDays": 99999SyneRyder · Hacker News · Jun 2026
autoMemoryEnabled02
"autoMemoryEnabled": false
Auto memory is on by default. Turn it off if you would rather keep project notes in CLAUDE.md yourself.
I have this in ~/.claude/settings.json "autoMemoryEnabled": falsefluidcruft · Hacker News · Aug 2026
preferredNotifChannel03
"preferredNotifChannel": "terminal_bell"
A bell or desktop notification when a task finishes. Values: auto, terminal_bell, iterm2, iterm2_with_bell, kitty, ghostty, notifications_disabled.
In claude code it's `"preferredNotifChannel": "terminal_bell"` in the settings.jsonzavec · Hacker News · Jun 2026
attribution04
"attribution": false
Drops the attribution Claude Code adds to commits and pull requests. false works from 2.1.281; earlier versions reject it and skip the file. An object with commit and pr strings customizes it instead.
statusLine05
"statusLine": {
"type": "command",
"command": "jq -r '...'"
}Runs your command and prints its output below the prompt. The docs example shows the model name and how full the context window is; it needs jq.
model06
"model": "opus"
The model Claude Code starts with, read only at start: use /model mid-session. Aliases such as opus, sonnet and haiku follow the newest version. For effort, use /effort: it saves a level per model, and Opus 5.5 and newer ignore effortLevel in the user file.
spinnerTipsEnabled07
"spinnerTipsEnabled": false
Hides the tips shown in the spinner while Claude works. On by default.
env08
"env": {
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
}Sets environment variables for every session and its subprocesses, so you stop exporting them in your shell profile.
I added: "env": { "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1" }, to my .claude/settings.json file, and the /rc in the corner went away.ebcode · Hacker News · Sep 2026
enabledPlugins + extraKnownMarketplaces09
"enabledPlugins": {
"code-formatter@team-tools": true
}Commit both in the project file and everyone who opens the repo gets the marketplace and the plugin. Marketplaces wait for workspace trust.
i just define the marketplaces and plugins in my .claude/settings.json for the project.pdantix · Hacker News · Sep 2026
sandbox.enabled10
"sandbox": {
"enabled": true
}OS-level limits on what Bash can touch, on macOS, Linux and WSL2. Linux and WSL2 need bubblewrap and socat. Sandboxed commands run without prompts by default.
Which copied settings do nothing?
Seven show up again and again: bash.deniedCommands and an autoCompactThreshold key do not exist, .claudeignore has no effect, a // comment breaks the whole file, Write(path) rules are never consulted, bypassPermissions in a committed project file is ignored, and mcp__server__tool(arg) rules are skipped.
bash.deniedCommands does not exist
"bash": {
"deniedCommands": ["rm"]
}"permissions": {
"deny": ["Bash(rm *)"]
}There is no bash key. Claude Code ignores it, and every rm still runs or prompts as before. Blocked commands are Bash rules in permissions.deny.
There is no auto-compact threshold key
"autoCompactThreshold": 50
"env": {
"CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "50"
}The variable takes 1 to 100 and can only make compaction happen earlier: values above the default are ignored. autoCompactEnabled turns it off.
.claudeignore has no effect
# .claudeignore .env node_modules/
"permissions": {
"deny": ["Read(./.env)"]
}Claude Code does not read a .claudeignore file. Read deny rules keep files out, and a Read deny also blocks Edit on the same path.
A // comment breaks the whole file
{
// my rules
"model": "opus"
}{
"model": "opus"
}Settings files are strict JSON. One comment or trailing comma is a Settings Error, and Claude Code skips the entire file, not just that line.
Write(path) rules are never consulted
"allow": ["Write(/src/**)"]
"allow": ["Edit(/src/**)"]
Edit rules cover every built-in tool that writes files. Path rules for Write, NotebookEdit, MultiEdit and Glob are never checked.
bypassPermissions in a committed project file
"defaultMode": "bypassPermissions" (in .claude/settings.json)
"defaultMode": "acceptEdits" or bypassPermissions in your user file, in a container
auto and bypassPermissions do not take effect from project or local files (bypassPermissions since 2.1.257), so a cloned repo cannot switch off your prompts.
mcp__server__tool(arg) rules are skipped
"deny": ["mcp__github__create_issue(repo:prod)"]
"deny": ["mcp__github__create_issue"]
MCP rules with parentheses are skipped when settings load. Deny the whole tool, or pass the pattern on the command line with --disallowedTools.
How do I stop Claude Code asking for permission, safely?
Add allow rules for the commands you approve every day, use acceptEdits mode for file edits, and turn on the sandbox so sandboxed Bash commands run without a prompt. Auto mode hands routine approvals to a classifier. Keep bypassPermissions for containers and VMs, and set disableBypassPermissionsMode to "disable" to lock it out.
I setup an alias in my shell for --dangerously-skip-permissions after about a week of the constant y, y, y
| Mode | What it does | Set it from |
|---|---|---|
default | Asks the first time each tool is used. Shown as Manual; manual is an alias. | Any file |
acceptEdits | Accepts file edits and mkdir, touch, mv, cp, rm, rmdir and sed inside your working directories. | Any file |
plan | Reads and runs read-only commands; no edits to your source files. | Any file |
auto | No routine prompts; a background classifier checks shell commands and network requests. | User or managed file, --settings or --permission-mode |
dontAsk | Denies anything that would prompt. Allowed tools and reads still run: good for CI. | Any file |
bypassPermissions | Skips prompts, including writes to .git and .claude. Ask rules still ask. Containers and VMs only. | User or managed file, or --dangerously-skip-permissions |
- Allow what you approve every day.
Bash(npm run test *)andBash(npm run lint)remove most prompts on their own. - Never allow an interpreter or an encoder.
Bash(python *)runs any script, andBash(base64 *)prints any file. - Turn on the sandbox. With
sandbox.enabledand the defaultautoAllowBashIfSandboxed, sandboxed Bash runs without asking, inside OS-level limits. Deny rules still apply. - Audit the local file. Every "Yes, and don't ask again" adds an allow rule to
.claude/settings.local.json.
give Claude Code permission to run base64 without review without realizing this lets it read any fileany time it found it was blocked from using a tool, e.g. git, it would write a powershell script that did the same thing
I've been running Claude Code with --dangerously-skip-permissions in a Docker container
What settings does Claude Code have?
The settings reference lists 243 keys. 156 work in any settings file, 45 only in managed settings, 26 only in user or managed settings, 4 in user, local or managed settings, and 12 global config keys live in ~/.claude.json. Filter by file and topic below, or download the whole list.
| Key | What it does | Works in |
|---|---|---|
| advisorModel | Pick which model answers when Claude asks the advisor tool | Any file |
| agent | Start every session as a named subagent with its prompt, tools, and model | Any file |
| agentPushNotifEnabled | Let Claude send a push notification to your phone when it decides to | Any file |
| allowAllClaudeAiMcps | Load the claude.ai connectors Claude Code fetches itself alongside a deployed managed-mcp.json | Managed |
| allowClaudeInChromeWithManagedMcp | Let the built-in Claude in Chrome server run alongside a deployed managed-mcp.json | Managed |
| allowedChannelPlugins | Replace the default allowlist of channel plugins that can push messages | Managed |
| allowedHttpHookUrls | Limit which URLs HTTP hooks can target | Any file |
| allowedMcpServers | Allowlist which MCP servers users can add | Any file |
| allowedProviders | Limit which API providers a machine may use | Managed |
| allowManagedHooksOnly | Run only the hooks your organization deploys | Managed |
| allowManagedMcpServersOnly | Make the managed MCP allowlist the only one that applies | Managed |
| allowManagedPermissionRulesOnly | Make managed settings the only settings source of permission rules | Managed |
| alwaysThinkingEnabled | Turn extended thinking off for every session | Any file |
| apiKeyHelper | Generate the API credential with your own command | Any file |
| askUserQuestionTimeout | Let an unanswered question auto-continue after idle time | User or managed |
No key matches. Clear the search or pick All.
Why is my Claude Code setting ignored?
Usually one of five things: a higher level sets the same key, the file cannot set that key, broken JSON makes Claude Code skip the file, project allow rules are waiting for workspace trust, or the key is read only at start, like model. Run /status to see which files loaded, then claude doctor.
- Run /status. The Setting sources line lists every file Claude Code loaded. A file missing from it was not read.
- Run claude doctor. It lists entries Claude Code rejected and why.
- Check the higher levels. A key in managed, --settings, local or project settings beats the same key in your user file.
- Check the file can set that key. Managed-only keys do nothing elsewhere, and global config keys live in ~/.claude.json. The doctor flags both.
- Accept workspace trust. Project allow rules, additionalDirectories, marketplaces and most env values wait for it. claude -p shows no dialog, so in a folder you never trusted it skips project allow rules.
- Fix the JSON. A Settings Error means the whole file was skipped. A Settings Warning means only some entries were.
- Restart for start-only keys. model is read at start. Use /model mid-session.
- Look for ANTHROPIC_MODEL. An exported ANTHROPIC_MODEL beats the model key in every file, managed included.
- Audit settings.local.json. Every "Yes, and don't ask again" adds an allow rule there, and old ones can outlive the reason you added them.
these permissions strings can contain substantive risks: API keys once pasted into a command, unrestricted git push
settings.json vs ~/.claude.json vs CLAUDE.md vs .mcp.json: what goes where?
settings.json holds rules and preferences that Claude Code enforces. ~/.claude.json is Claude Code's own state: your sign-in, MCP servers at user and local scope, and trust decisions. CLAUDE.md holds instructions Claude reads but nothing enforces. .mcp.json lists the project MCP servers you commit for the team.
| File | Holds | Who writes it | Commit it? |
|---|---|---|---|
settings.json | Permissions, hooks, env, model, plugins. Enforced by Claude Code. | You, or /config and permission prompts | The project file, yes |
~/.claude.json | Sign-in, user and local MCP servers, per-project trust, global config keys | Claude Code; you rarely edit it | Never |
CLAUDE.md | Standing instructions. Shapes what Claude tries; enforces nothing | You | The project one, yes |
.mcp.json | Project-scope MCP servers; asks before first use | You, or claude mcp add --scope project | Yes |
.mcp.json: see MCP servers worth adding.One more place: the env block in settings points Claude Code at a different endpoint with ANTHROPIC_BASE_URL. If you plan to run a model on your own machine, check what your hardware can run first.
Questions people ask
How do I access Claude Code settings?
Run /config in a session and open the Config tab for personal options such as theme and editor mode. For everything else, edit the settings file in your editor. /status shows which files loaded.
Where is settings.json on Windows and macOS?
macOS and Linux: ~/.claude/settings.json. Windows: %USERPROFILE%\.claude\settings.json. Managed settings sit in /Library/Application Support/ClaudeCode/ on macOS and C:\Program Files\ClaudeCode\ on Windows.
Does settings.json allow comments?
No. Settings files are strict JSON. A // comment or a trailing comma is a syntax error, and Claude Code shows a Settings Error at the next start and skips the file.
What is settings.local.json for?
Your own overrides for one repository. Claude Code saves "Yes, and don't ask again" approvals there and keeps the file out of git through your global excludes.
How do I make Claude Code stop asking for permission?
Allow the commands you trust, use acceptEdits for file edits, or turn on the sandbox. See the safe options above.
How do I allow all Bash commands?
Add "Bash" to permissions.allow. Deny and ask rules still apply, and a PreToolUse hook can reject the commands you never want.
Can I prevent Claude Code from running dangerous commands?
Deny rules such as Bash(rm *) stop the usual form, not /bin/rm or sh -c. For a real boundary, enable the sandbox.
How do I change the default model?
Set "model" in ~/.claude/settings.json, or pick one with /model. --model changes one session, and an exported ANTHROPIC_MODEL overrides the key in every file.
Is there a JSON schema for settings.json?
Yes. Add "$schema": "https://json.schemastore.org/claude-code-settings.json" for autocomplete in VS Code and Cursor. It can lag behind the newest releases.
Why is my setting ignored?
A higher level sets it, the file cannot set it, the JSON is broken, or it waits for trust. Work through the checklist.
Which MCP servers are worth adding?
Twenty servers, each with one verdict and its context cost, plus a config builder for .mcp.json.