Claude Code settings.json permissions, explained.

Guides

Claude Code decides whether to run a tool call from a list of permission rules in its settings files. Understanding three things stops most surprises: how a rule is written, in which order rules are checked, and which settings file wins.

The three rule lists

  • allow: the call runs without asking.
  • ask: Claude Code stops and asks you each time.
  • deny: the call is refused.

Rules live under permissions in a settings file. Each rule names a tool, and optionally a pattern in brackets.

{
  "permissions": {
    "defaultMode": "default",
    "allow": [
      "Bash(npm run test *)",
      "Bash(npm run lint)",
      "Bash(git diff *)"
    ],
    "ask": [
      "Bash(git push *)"
    ],
    "deny": [
      "Bash(git push --force *)",
      "Read(./.env)",
      "Read(./.env.*)",
      "Edit(migrations/**)"
    ]
  }
}

Order matters: deny, then ask, then allow

Rules are checked in that order and the first match decides. Specificity does not change it, so a narrow allow rule cannot carve an exception out of a broader deny. In the example above, git push origin main prompts you, while git push --force origin main is denied because the deny list is checked first. A deny in any settings file beats an allow in any other file.

Writing the patterns

  • Bash: Bash(npm run test *) matches the command text. A trailing * (space, then star) also matches the bare command, and the space matters: Bash(ls *) does not match lsof.
  • Read and Edit: gitignore-style paths. Read(./.env) blocks the file tools from reading that file; Edit(migrations/**) covers writes. Edit rules cover the write tools, so write one Edit rule rather than separate Write ones.
  • WebFetch: WebFetch(domain:example.com). A star prefix covers subdomains.
  • MCP: mcp__servername__toolname, or the server name alone for every tool on it.

Which file wins

Highest precedence first: managed settings from an organisation, command-line settings, .claude/settings.local.json, .claude/settings.json, then ~/.claude/settings.json. List settings such as permissions.allow merge across files rather than replace each other. Allow rules in a committed project file only apply after the person who cloned it accepts the workspace trust prompt; deny and ask rules apply straight away. Choosing "Yes, and don't ask again" on a prompt writes an allow rule to the local file, which is where surprise allows come from.

The permission mode

defaultMode sets how a session starts. The documented values include default (prompts on first use of each tool), acceptEdits, plan, auto, dontAsk and bypassPermissions. Be deliberate with the last one: it skips most prompts. Run /permissions in a session and /status to see what actually loaded.

Where Bash rules stop

  • They match text, not intent. A rule for git push * does not match git -C . push or a command wrapped in sh -c. Rules that try to constrain arguments, such as limiting curl to one URL, are fragile.
  • Chains are split. An allow rule for one command does not approve that-command && something-else; each part has to match.
  • Read rules cover the file tools. A shell command that reads a file is judged by your Bash rules, so protect secrets with Bash rules or a sandbox as well.

For the few operations that must never run, add a PreToolUse hook, which can check the command however you like. CommonRig's shell guard covers the same ground for both Claude Code and Codex and is described in stopping destructive git commands.

Check your own files

Paste your CLAUDE.md or AGENTS.md into the free footprint scorer, or get the free sample and run rig doctor --lite locally. The hosted doctor check is A$0.50 a run and scores the whole setup, hooks included. The kit (A$10 once) has the rule source, generator, hooks and protocols these guides describe.

More guides