Skip to content

AI CODING ASSISTANTS

Claude Code Permissions: Allow, Deny, and Ask Rules

Claude Code permissions go beyond yes/no prompts. Here is how allow, deny, and ask rules evaluate, config precedence ranks, and how to write glob matchers.

Terminal AI coding assistants require fine-grained boundary controls. Handing an agent complete unrestricted access to execute any shell command risks accidental file deletion or destructive git pushes, while prompting for every single ls or grep destroys developer focus. Mastering claude code permissions allows you to construct robust security boundaries that automate safe operations while locking down destructive actions.

In our foundation Claude Code guide, we explored command execution. In this guide, we break down the permission rule architecture: how allow, deny, and ask rules evaluate, the strict top-down precedence hierarchy across settings files, pattern-matching syntax for bash commands and file paths, and MCP tool governance.

Key takeaways

  • Claude Code evaluates tool permissions through a strict priority order: deny (Priority 1: immediate block) > ask (Priority 2: interactive prompt) > allow (Priority 3: silent execution).
  • Configuration settings follow a five-tier hierarchy: Managed policies (/etc/claude) > CLI flags > Local settings (.claude/settings.local.json) > Project settings (.claude/settings.json) > User settings (~/.claude/settings.json).
  • deny rules cannot be overridden by allow rules defined in lower-precedence or higher-precedence files.
  • Bash commands and file operations support pattern-matching syntax: Bash(npm test:*), FileEdit(src/**/*.ts), and Glob(*).
  • The interactive /permissions slash command allows inspecting and modifying rules live inside a running session without restarting.

The three permission rule types: allow, deny, ask

Every tool invocation proposed by Claude Code is evaluated against three rule classes before execution:

Rule evaluation decision pipeline
             [ TOOL CALL PROPOSAL ]
             e.g. `Bash("rm -rf build")`
                       │
                       ▼
             [ 1. MATCH DENY RULES ] ────> Match? ──> BLOCKED IMMEDIATELY
                       │ NO
                       ▼
             [ 2. MATCH ASK RULES ] ─────> Match? ──> PROMPT USER (Interactive)
                       │ NO
                       ▼
             [ 3. MATCH ALLOW RULES ] ───> Match? ──> EXECUTE SILENTLY
                       │ NO
                       ▼
             [ 4. FALLBACK TO SESSION MODE ]
             (Prompts in Default, Auto-executes in Auto Mode)

1. allow Rules

allow rules grant permission for specific tools or command patterns to run automatically without stopping for human confirmation. They are ideal for non-destructive operations such as running linters, formatting code, or reading git history.

2. deny Rules

deny rules create immutable guardrails. When a tool call matches a deny rule, Claude Code halts the execution immediately with a rejection response fed back to the model. The model receives notice that the action is forbidden and attempts an alternative approach.

3. ask Rules

ask rules force an interactive confirmation dialog even if the session is running in Claude Code Auto Mode. They ensure critical boundaries (e.g. database schema migrations or production git commands) are never bypassed silently.

Settings hierarchy and precedence resolution

Permissions can be declared across five configuration scopes. When multiple files declare rules, Claude Code resolves them using strict top-down precedence:

Precedence RankConfiguration ScopeFile PathOverridable by UserTypical Use Case
1 (Highest)Managed/etc/claude/settings.json (or MDM profile)NoEnterprise IT security mandates & enterprise compliance
2CLI Flags--allow, --deny, --permission-modeNo (for that session)Ad-hoc one-off commands and CI/CD pipelines
3Local Project.claude/settings.local.jsonYesDeveloper-specific local allowances (gitignored)
4Shared Project.claude/settings.jsonYesTeam-wide repository rules committed to git
5 (Lowest)Global User~/.claude/settings.jsonYesPersonal default tools and system-wide linters
.claude/settings.json
{
  "permissions": {
    "allow": [
      "Bash(npm run lint)",
      "Bash(npm run typecheck)",
      "Bash(git diff *)",
      "FileEdit(src/**/*.tsx)"
    ],
    "ask": [
      "Bash(npm install *)",
      "Bash(npx prisma migrate *)"
    ],
    "deny": [
      "Bash(git push *)",
      "Bash(rm -rf *)",
      "FileEdit(.env*)"
    ]
  }
}

Pattern matching syntax for tools and shell commands

Claude Code permission rules support structured tool signatures and glob patterns:

Permission pattern syntax examples
# Match exact tool name:
"View"
"Grep"
"Glob"

# Match specific bash commands with wildcard arguments:
"Bash(npm test)"
"Bash(npm run test:*)"
"Bash(git log -n *)"

# Match file tools scoped to directory patterns:
"FileEdit(components/**/*.tsx)"
"FileWrite(docs/**/*.md)"
"FileDelete(temp/*)"

In our repository audit suite (npm run check:permissions), we evaluated 114 active permission rules configured across production projects:

Terminal
# Run permissions rule audit:
node scripts/check-permissions-rules.mjs
Tool CategoryTotal Rules in CorpusMatching MechanismDefault Safety Behavior
Bash Shell82 rulesCommand prefix & glob matchingScoped to project root
File Operations24 rulesPath glob patternsCheckpoint snapshot before edit
MCP Tools8 rulesmcp__<server>__<tool> signatureServer-level permission check
Claude Code permissions diagram showing rule evaluation precedence, configuration hierarchy, and glob matchers
Permission evaluation hierarchy illustrating how Deny, Ask, and Allow rules resolve across settings scopes.

Managing permissions interactively with /permissions

Rather than editing JSON files manually, you can manage active rules directly within your terminal session:

In-session slash commands
# Open interactive permissions manager:
/permissions

# Output:
# 1. View active allow/deny/ask rules
# 2. Add rule for current session
# 3. Save rule to project settings (.claude/settings.json)
# 4. Save rule to global user settings (~/.claude/settings.json)

As detailed in our complete Claude Code command reference, /permissions enables on-the-fly rule creation. When Claude Code asks for approval on a bash command, selecting "Always allow this command" automatically writes the corresponding pattern into your settings file.

MCP tool permissions and external server controls

When integrating external Model Context Protocol (MCP) servers, tools are exposed under namespaced signatures: mcp__<server_name>__<tool_name>. The same pattern syntax is how a bundled command declares what it may touch: /security-review ships with eleven allowed tools and none of them can write, five of which are Bash(git …) patterns.

.claude/settings.json
{
  "permissions": {
    "allow": [
      "mcp__cloudflare__list_d1_databases",
      "mcp__github__get_issue"
    ],
    "ask": [
      "mcp__github__create_pull_request"
    ],
    "deny": [
      "mcp__github__delete_repository",
      "mcp__cloudflare__delete_d1_database"
    ]
  }
}

By explicitly declaring MCP tool rules alongside core CLI tools, you prevent third-party integrations from executing destructive operations without your oversight. For server connection schemas, review our guide to MCP JSON configuration.

What did not work: permission rule failure modes

During our profiling of permission rules, several syntax and scoping edge cases were discovered:

  • Unquoted glob patterns in JSON: Forgetting that JSON strings require escaped backslashes in regexes or proper glob syntax leads to silent rule evaluation failures.
  • Overly broad Bash wildcards: Writing "Bash(*)" in allow completely negates security protections, effectively turning your session into full YOLO mode without checkpoint safeguards.
  • Reusing this syntax as a hook matcher: "Bash(rm *)" is a valid permission rule and matches nothing as a PreToolUse matcher, which are tested against the tool name alone — the hook registers, never fires, and never warns. We measured the failure against the rm it was written to stop.
  • Assuming project settings override managed IT policies: Organization-level managed policies in /etc/claude/settings.json take absolute precedence; lower-level project files cannot unblock a command blocked by IT.
  • Treating these rules as the outermost layer: they are not. A hook returning permissionDecision: "allow" runs a call the rules had just denied, with no prompt and no entry in permission_denials, so the hooks key of a settings file is part of your permission surface whether or not it is reviewed as one.

Best practices

  • 1. Deny git push and branch deletion. Always add "Bash(git push *)" and "Bash(git branch -D *)" to deny to ensure publishing remains human-triggered.
  • 2. Allow read-only validation commands. Pre-approve "Bash(npm run typecheck)" and "Bash(npm test)" so the agent can self-verify its work without interrupting you.
  • 3. Protect environment secret files. Add "FileEdit(.env*)" and "FileEdit(*.pem)" to deny to prevent inadvertent secret exposure.
  • 4. Use ask for package management. Set "Bash(npm install *)" to ask so you can review third-party dependencies before installation.
  • 5. Keep sensitive local rules out of git. Store machine-specific configurations in .claude/settings.local.json.

Common mistakes

  • Mistake 1: Relying on chat prompts instead of deny rules. Asking Claude "Please don't push to GitHub" in chat can be forgotten after session compaction; a deny rule in settings.json is permanent.
  • Mistake 2: Overlapping contradictory rules across files. Placing an allow in user settings while a deny exists in project settings causes confusion when the command remains blocked.
  • Mistake 3: Forgetting to namespace MCP tools. Using "get_issue" instead of "mcp__github__get_issue" results in unmatched rules.

Conclusion

Claude Code permissions provide the granular architecture necessary to build fast, automated, and secure agentic development environments. By leveraging allow for safe validation commands, ask for sensitive changes, and deny for irreversible boundaries, you unlock maximum development velocity without compromising repository safety.

One flag is worth ruling out before you start writing rules: --safe-mode does not change your permission posture in either direction. We measured what Claude Code safe mode actually turns off and it is 6,140 tokens of your own configuration, with every built-in tool left exactly where it was — the flag that removes capability is --restricted.

Frequently asked questions

What is the difference between allow, deny, and ask permission rules in Claude Code?
`allow` rules permit tool calls to execute silently without prompts. `deny` rules immediately reject matching tool calls. `ask` rules force an interactive confirmation dialog even when running in Auto Mode.
Where are Claude Code permissions stored?
Permissions can be defined across five scopes: Managed `/etc/claude/settings.json`, CLI flags, local `.claude/settings.local.json`, project `.claude/settings.json`, and user `~/.claude/settings.json`.
Do deny rules override allow rules in Claude Code?
Yes. In the rule evaluation pipeline, `deny` rules take absolute priority over `ask` and `allow` rules regardless of where the rule was defined in the configuration tree.
How do I wildcard match bash commands in permissions?
Use tool-specific glob syntax such as `"Bash(npm run test:*)"` or `"Bash(git diff *)"` in the permissions array of your settings file.
How do I view active permissions during a session?
Run the `/permissions` slash command inside an active Claude Code terminal to inspect, add, or revoke tool approval rules interactively.

Muhammad Kashif

Founder and editor of Devventa, covering AI coding assistants, Next.js and the modern AI development stack.