Issue 00 · Autumn 2026日本語 · RSS
AgentKoji

Claude Code settings.json Permissions: A Cheat Sheet

Claude Code permissions: allow/deny/ask rules, defaultMode values and rule syntax in one place. claude doctor rejected defaultMode "readonly" as invalid.

TL;DR

  • Permission rules are evaluated deny, then ask, then allow; the first match decides, regardless of how specific a rule is.
  • defaultMode accepts default (manual), acceptEdits, plan, auto, dontAsk and bypassPermissions; bypassPermissions is only for isolated environments.
  • A misspelled defaultMode value isn't used; claude doctor reports it, and a bare deny on a tool like Bash removes that tool entirely.

This is a quick reference for the permissions block in Claude Code’s settings.json. It covers allow, ask and deny, the defaultMode values, additionalDirectories, rule syntax, which settings file wins, and three example configs you can paste in. We checked every format against the official docs. In our test environment (macOS, Claude Code 2.1.303 and 2.1.304, jq 1.7.1, on 2026-10-19 and 2026-10-20), we only checked syntax, using jq and claude doctor. We did not test how the rules behave in a live session. For why rules are evaluated in this order and how to avoid over-broad allows, see Permission Design for AI Agents.

allow, ask and deny

KeyWhat it doesWatch out for
allowLets Claude use the tool without askingHas no effect if a deny or ask rule also matches
askPrompts every timeStill prompts even when a narrower allow rule matches
denyBlocks the toolA bare tool name such as Bash removes the tool from Claude entirely

Rules are evaluated deny first, then ask, then allow. The first match decides. How specific a rule is doesn’t change that order. If you deny Bash(aws *), allowing Bash(aws s3 ls) won’t get it through.

Source: https://code.claude.com/docs/en/permissions (checked 2026-10-20)

defaultMode values

ValueBehavior
defaultAsks the first time each tool is used. Shown as Manual in the CLI; manual works as an alias
acceptEditsAuto-approves file edits and commands such as mkdir, touch, mv and cp inside the working directory
planReads files and runs read-only commands to explore. Doesn’t edit source
autoNo routine prompts; a classifier checks each action against your request before it runs
dontAskAuto-denies anything that would prompt. Allowed tools and actions that need no approval still run
bypassPermissionsSkips prompts. Use only in isolated environments such as containers or VMs

Source: https://code.claude.com/docs/en/permissions (checked 2026-10-20)

A misspelled value is not used. With "defaultMode": "readonly", claude doctor printed:

- <project>/.claude/settings.json › permissions.defaultMode: Invalid value. Expected one of: "acceptEdits", "auto", "bypassPermissions", "default", "dontAsk", "plan"

"manual" raised no error.

Rule syntax

You writeMatchesWatch out for
Bash / Bash(*)Every Bash commandAs a deny rule, Bash becomes unavailable
Bash(npm run build)That exact commandDoesn’t match npm run build --watch
Bash(npm run *)Commands starting with npm runPut * after the subcommand. Bash(git *) matches every git command
Bash(npm run test:*)Same as Bash(npm run test *):* works only at the end
Read(./.env).env in the current directoryA bare filename pattern matches at any depth
Edit(/src/**)src/ relative to the settings file’s locationIn project settings, that means <working directory>/src/
Read(//etc/**)An absolute filesystem pathA single leading / is not absolute
Read(~/.ssh/**)A path under your home directoryUse this in user settings to cover every project
WebFetch(domain:example.com)example.com onlyAdd domain:*.example.com for subdomains
mcp__github__*Every tool from the github servermcp__github means the same
mcp__github__create_issueThat one toolMCP rules with parentheses are skipped

Only Read(...) and Edit(...) apply to files. Write(docs/**) is accepted but never consulted; write Edit(docs/**) instead. A Read deny also blocks Edit and Write on the same path. It doesn’t cover commands that read files without naming them, such as grep -r.

Source: https://code.claude.com/docs/en/permissions (checked 2026-10-20)

claude doctor reported the MCP rule with parentheses like this:

- <project>/.claude/settings.json › permissions.allow: Invalid permission rule "mcp__github__create_issue(repo:x)" was skipped: MCP rules do not support patterns in parentheses. ...

claude doctor did not flag Write(docs/**) or Bash(git * main). The docs say both produce a warning at session startup (unverified: we didn’t check the startup warning).

Adding working directories

Claude can read files inside the directory it was started in without asking. There are three ways to add more: --add-dir <path> at launch, /add-dir during a session, and additionalDirectories in settings. Added directories follow the same rules as the original one. Most of the .claude/ configuration inside an added directory is not loaded.

Source: https://code.claude.com/docs/en/permissions (checked 2026-10-20)

Settings files and precedence

PrecedenceLocationScope
1 (highest)Managed settingsDeployed by your organization
2--settings on the command lineThat session only
3.claude/settings.local.jsonThis project, just you
4.claude/settings.jsonThis project, shared through Git
5~/.claude/settings.jsonAll your projects

List keys such as permissions.allow merge across files instead of replacing each other. A deny applies from any level: an allow in user settings won’t override a deny in project settings. In a session, /permissions lists every rule and the file it came from. /status shows which settings files loaded, on the Setting sources line.

Source: https://code.claude.com/docs/en/settings and https://code.claude.com/docs/en/permissions (checked 2026-10-20)

Configs you can paste in

All three passed jq empty, and claude doctor reported no invalid settings.

Read-mostly and cautious (how plan mode works is in Claude Code Plan Mode: Plan Before It Edits):

{
  "permissions": {
    "defaultMode": "plan",
    "allow": ["Bash(git status)", "Bash(git diff *)", "Bash(git log *)"],
    "deny": ["Edit", "WebFetch"],
    "additionalDirectories": ["../shared-docs"]
  }
}

Tests and builds only:

{
  "permissions": {
    "allow": [
      "Bash(npm run test:*)",
      "Bash(npm run build)",
      "Bash(npm run lint)",
      "Edit(/src/**)",
      "Edit(/tests/**)"
    ],
    "ask": ["Bash(npm install *)", "Bash(git push *)"],
    "deny": ["Bash(npm publish *)", "Bash(curl *)"]
  }
}

Keep secret files out of reach:

{
  "permissions": {
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",
      "Read(~/.ssh/**)",
      "Read(~/.aws/**)",
      "Bash(printenv *)"
    ]
  }
}

A command spelled differently, such as git -C . push, doesn’t match Bash(git push *). For anything that must never run, back the rule with a hook; see Claude Code Hooks in Practice. For handling secrets, see Keep Secrets Away from Claude Code.

Summary