Claude Code /permissions: Fine-Grained Control Over What AI Can Do
Table of Contents
- What is /permissions
- Permission System Basics
- Three Behaviors
- Rule Format
- Rule Sources
- How to Use /permissions
- Configuring Permissions in settings.json
- Basic Structure
- Allow Rule Examples
- Deny Rule Examples
- Ask Rule Examples
- Wildcard Matching Explained
- Exact Match
- Prefix Match (:* syntax)
- Wildcard Match (* syntax)
- MCP Tool Matching
- Permission Modes
- Permission Check Flow
- Practical Tips
- Tip 1: Start Loose, Tighten Gradually
- Tip 2: Use Layered Management
- Tip 3: Use Deny Rules as a Safety Net
- Tip 4: Use /permissions to Debug Permission Issues
- Security Recommendations
- Final Thoughts
What is /permissions
If you’ve used Claude Code, you’ve definitely experienced this:
- Claude Code wants to run
npm install— a popup asks “Allow?” - You click Allow, then it wants to run
npm test— another popup - By the end of a task, you’ve clicked “Allow” a dozen times
The other extreme: you get tired of popups and enable bypass permissions mode — but feel uneasy, wondering what if it runs something dangerous?
/permissions solves exactly this problem. It gives you fine-grained control over every tool in Claude Code: which actions auto-approve, which must ask you first, and which are outright blocked.
Not too loose, not too tight — just right.
Permission System Basics
Before diving into /permissions, let’s understand Claude Code’s permission model.
Three Behaviors
Every permission rule maps to one of three behaviors:
| Behavior | Meaning |
|---|---|
| allow | Auto-approve, no popup |
| deny | Reject outright, Claude Code won’t execute |
| ask | Force a popup, even in permissive modes |
Rule Format
Permission rules use the format ToolName or ToolName(content):
Bash → allow/deny/ask all Bash commands
Bash(npm install) → only the exact "npm install" command
Bash(npm:*) → all commands starting with "npm" (prefix match)
Bash(npm *) → same, using wildcard syntax
Edit → all file edit operations
mcp__server1 → all tools from an MCP server
mcp__server1__tool1 → a specific MCP server tool
Rule Sources
Permission rules can come from multiple places, with later sources overriding earlier ones:
| Source | File Path | Description |
|---|---|---|
| User | ~/.claude/settings.json | Global rules, affect all projects |
| Project | <project>/.claude/settings.json | Project-level, can be committed to Git |
| Local | <project>/.claude/settings.local.json | Project-level local, gitignored |
| CLI | --allowedTools and similar flags | Temporarily specified |
| Managed | Enterprise managed settings | Pushed by admins, highest priority |
| Session | Your choices in the current session | Only valid for this session |
How to Use /permissions
In Claude Code’s interactive mode, type:
/permissions
An interactive panel appears listing all active permission rules. Its alias is /allowed-tools.
In this panel you can:
- View all allow, deny, and ask rules, along with which config source they come from
- Delete rules you no longer need
- Retry denied operations — if an action was previously denied, you can remove the restriction and retry here
Configuring Permissions in settings.json
The /permissions panel is great for viewing and deleting rules. But for bulk-adding rules, editing settings.json directly is more efficient.
Basic Structure
{
"permissions": {
"allow": ["Bash(npm:*)", "Bash(git:*)", "Edit", "Read", "Glob", "Grep"],
"deny": ["Bash(rm -rf:*)", "Bash(sudo:*)"],
"ask": ["Bash(git push:*)"]
}
}
Allow Rule Examples
{
"permissions": {
"allow": [
"Bash(npm:*)",
"Bash(npx:*)",
"Bash(node:*)",
"Bash(git:*)",
"Bash(ls:*)",
"Bash(cat:*)",
"Bash(echo:*)",
"Bash(mkdir:*)",
"Bash(cp:*)",
"Bash(mv:*)",
"Edit",
"Write",
"Read",
"Glob",
"Grep",
"WebFetch",
"WebSearch",
"Agent",
"TodoWrite",
"mcp__github"
]
}
}
The effect of these rules:
- Common dev commands (npm, git, node, file operations) auto-approve
- File read/write and search tools auto-approve
- All tools from the GitHub MCP server auto-approve
- Any unlisted operations still prompt for confirmation
Deny Rule Examples
{
"permissions": {
"deny": ["Bash(rm -rf:*)", "Bash(sudo:*)", "Bash(curl * | sh:*)", "Bash(wget * | sh:*)"]
}
}
These rules outright block dangerous operations: recursive deletion, sudo escalation, and downloading-and-executing scripts from the internet.
Ask Rule Examples
{
"permissions": {
"ask": ["Bash(git push:*)", "Bash(git checkout:*)", "Bash(docker:*)"]
}
}
These commands always prompt for confirmation even in permissive modes — pushing code, switching branches, and Docker operations are things you probably want to review.
Wildcard Matching Explained
Permission rules support three matching modes:
Exact Match
Bash(npm install)
Only matches the exact npm install command. npm install express won’t match.
Prefix Match (:* syntax)
Bash(npm:*)
Matches all commands starting with npm: npm install, npm test, npm run build all match.
This is the most common format. The :* after the prefix means “anything can follow.”
Wildcard Match (* syntax)
Bash(git * --force)
* can be placed anywhere, matching any content. The rule above matches git push --force, git push origin main --force, etc.
If your rule ends with space + * (like git *), then bare git itself also matches — consistent with prefix match behavior.
MCP Tool Matching
mcp__github → all tools from the GitHub MCP server
mcp__github__* → same, wildcard syntax
mcp__github__create_pr → only the create_pr tool
Permission Modes
Beyond rules, Claude Code has global “permission modes” that determine default behavior when no rule matches:
| Mode | Behavior |
|---|---|
| default | Standard mode. Read operations auto-approve, writes and command execution prompt |
| plan | Planning mode. Claude Code drafts a plan first, prompts before execution |
| acceptEdits | Accept edits mode. File edits auto-approve, Bash commands still prompt |
| bypassPermissions | Skip permissions. All operations auto-approve (dangerous, use with caution) |
Permission modes can be switched in /config or set as defaults in settings.json:
{
"permissions": {
"defaultMode": "default"
}
}
Permission Check Flow
When Claude Code wants to execute a tool, the complete permission check flow is:
Tool call request
|
Input validation (validateInput)
|
PreToolUse Hooks check
|
Permission rule matching
|-- Hit deny rule -> Reject
|-- Hit allow rule -> Approve
|-- Hit ask rule -> Prompt
+-- No rule match -> Check permission mode
|
Prompt user to choose
|-- Allow Once -> Approve this time
|-- Allow Always -> Add to allow rules
+-- Deny -> Reject
|
Tool-level check (checkPermissions)
|
Execute
Key points:
- Deny rules take priority over allow rules — if an action matches both allow and deny, deny wins
- Hooks are checked before rules — hooks can intercept or even modify tool calls
- Choosing “Allow Always” in the prompt automatically writes a rule — the same action won’t ask again
Practical Tips
Tip 1: Start Loose, Tighten Gradually
When you first start using Claude Code, don’t rush to configure permissions. Use default mode and observe which operations Claude Code needs day-to-day. Once you’ve built up experience, gradually add allow and deny rules based on your needs.
Selecting “Allow Always” in popups is the simplest way to add rules — over time, your permission configuration naturally fills out.
Tip 2: Use Layered Management
- Global rules (
~/.claude/settings.json) — Universal rules needed across all projects, likeEdit,Read,Glob,Grep - Project shared rules (
.claude/settings.json) — Team-unified project-level rules, like allowing project-specific build commands - Personal local rules (
.claude/settings.local.json) — Your personal preferences, like allowing tools only you use
Tip 3: Use Deny Rules as a Safety Net
Even if your allow rules are generous, add a few deny rules as a backstop:
{
"permissions": {
"deny": ["Bash(rm -rf /)", "Bash(sudo:*)", "Bash(:(){ :|:& };:)"]
}
}
Deny rules take priority over everything — they’re your last line of defense.
Tip 4: Use /permissions to Debug Permission Issues
If Claude Code keeps failing on a certain operation, or does something it shouldn’t, open /permissions to check the active rule list. Each rule shows its source (user / project / local / session), making it easy to identify which config file is causing the issue.
Security Recommendations
- Don’t put overly permissive allow rules in shared project settings.json — team members have different environments; commands you trust might be risky for others
- Periodically check your rule list with
/permissions— over time, you may accumulate allow rules you no longer need - Use managed settings in enterprise environments — admins can force-deny certain operations through managed settings for compliance
- Only use bypassPermissions mode when you fully trust the current task — it skips all prompts, including for dangerous operations
Final Thoughts
Permission management seems like a hassle, but it’s really the trust foundation for collaborating with AI.
If every action triggers a popup, you get interrupted and lose efficiency. If nothing triggers a popup, you worry and feel uneasy. Good permission configuration is the key to being both smooth and secure — auto-approve what’s safe, intercept what’s risky.
Spend ten minutes adding common operations to the allow list and dangerous operations to the deny list, then you can focus on what truly matters — letting Claude Code write code for you, instead of constantly clicking “Allow.”
Related Articles
Claude Code Agent Loop: Dissecting the Heart of an AI Coding Assistant
How does Claude Code understand your requests, invoke tools, and self-recover step by step? A source-code deep dive into the Agent Loop's core architecture — streaming responses, parallel tool execution, auto-compaction, and error recovery.
Claude Code settings.json Explained (1): Where Config Files Live and Who Wins
A complete guide to Claude Code's configuration file system — five config sources, their file paths, priority rules, array merging vs value overriding, and enterprise managed settings delivery.
Claude Code settings.json Deep Dive (Part 2): The Permissions System
A thorough breakdown of Claude Code's permissions configuration — allow/deny/ask rule arrays, wildcard syntax, MCP tool permissions, defaultMode options, and additionalDirectories.
Claude Code settings.json Deep Dive (Part 3): The Hooks System
A thorough breakdown of Claude Code's hooks configuration — four hook types, core events (PreToolUse/PostToolUse/Stop/Notification), stdin/stdout protocol, exit code semantics, and practical examples.
Claude Code settings.json Deep Dive (4): env, Models, Auth, and Other Useful Fields
A comprehensive guide to the remaining settings.json fields in Claude Code — env variable injection, model configuration, authentication helpers, Git attribution, session cleanup, language and UI, thinking depth, auto-updates, memory system, and more.