Claude Code asks permission before running commands, writing files, or calling MCP tools. This guide covers every way to configure that behavior, from surgical allowlists to full skip mode.
TL;DR
For interactive development, use the allowlist in settings.json. It auto-approves your common tools while still gating dangerous operations. For CI, use --dangerously-skip-permissions inside a sandboxed container. Never use skip mode interactively on a production machine.
Permission Modes
| Mode | Configured Via | Behavior | Best For |
|---|---|---|---|
| Prompt (default) | No config needed | Asks before every tool call | New users, untrusted code |
| Allowlist | allowedTools in settings.json | Auto-approves listed tools, prompts for others | Daily development |
| Skip All | --dangerously-skip-permissions | No prompts for any tool | CI, sandboxed environments |
| Deny (read-only) | Restrict allowedTools to reads | Only allows read operations freely | Code review, exploration |
Allowlist Mode (Recommended)
The allowlist is the right choice for daily development. You auto-approve the tools Claude uses every session (file reads, git, test runners) while keeping manual gates on destructive operations.
Recommended allowlist for a typical project
// .claude/settings.json
{
"allowedTools": [
"Read",
"Write",
"Edit",
"Glob",
"Grep",
"Bash(git *)",
"Bash(npm test)",
"Bash(npm run lint)",
"Bash(npm run build)",
"Bash(npx tsc --noEmit)",
"Bash(npx prettier --write *)"
]
}With this config, Claude reads and writes files, runs git commands, tests, lints, and builds without asking. It still asks before running npm install, rm, curl, or anything else not on the list.
Glob Patterns for Bash
Pattern matching examples
Bash(git *) # any command starting with "git"
Bash(npm run *) # any npm script
Bash(docker compose *) # any docker compose subcommand
Bash(python -m pytest) # exactly this command, no args
Bash(make *) # any make target
Bash(cargo *) # any cargo commandPatterns match the full command string. Bash(git *) matches git status, git commit -m "fix", and git push origin main. It does not match echo git because the command does not start with git.
Skip All Permissions
The --dangerously-skip-permissions flag disables every permission check. Claude executes all tools immediately without asking.
Skip permissions (CI usage)
# In a CI pipeline or sandboxed container
claude --dangerously-skip-permissions --print "run the test suite and fix any failures"
# Non-interactive with output
claude --dangerously-skip-permissions --print "review this PR for security issues" > review.mdWhen Skip Mode is Appropriate
Use --dangerously-skip-permissions only when all of these are true: (1) running in an ephemeral environment (Docker, CI runner), (2) no production credentials are accessible, (3) network access is restricted or monitored, (4) the environment is destroyed after the run. If any of these are not true, use the allowlist instead.
The flag name includes "dangerously" because it removes Claude's built-in safety layer. In an interactive session on your development machine, Claude could run rm -rf, install packages, or send network requests without asking. The allowlist achieves the same speed benefits for common operations without this risk.
Deny Mode (Read-Only)
For code review or exploration, restrict Claude to read-only operations:
Read-only configuration
{
"allowedTools": [
"Read",
"Glob",
"Grep",
"Bash(git log *)",
"Bash(git diff *)",
"Bash(git show *)"
]
}Claude can explore the codebase, search files, and read git history. Any write operation (Edit, Write, Bash commands that modify files) requires manual approval. This is useful for onboarding to unfamiliar codebases or security reviews where you want Claude's analysis but not its edits.
Settings.json Permissions Deep Dive
The allowedTools array accepts three formats:
AllowedTools format reference
{
"allowedTools": [
// 1. Bare tool names (file operations, search)
"Read",
"Write",
"Edit",
"Glob",
"Grep",
// 2. Bash with glob patterns
"Bash(git *)",
"Bash(npm test)",
// 3. MCP tools with server prefix
"mcp__github__create_issue",
"mcp__github__*", // all tools from github server
"mcp__filesystem__read_file"
]
}The mcp__* pattern is powerful for MCP servers. You can allowlist all tools from a trusted server (mcp__github__*) while requiring approval for tools from less trusted servers.
Settings files at different levels interact: project allowedTools replaces (not extends) global allowedTools. See the settings.json guide for the full merge behavior.
Hooks as Permission Guards
Hooks run shell commands before or after tool calls. They can act as an additional permission layer, rejecting operations that the allowlist alone cannot filter.
Hook that blocks writes to production configs
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"command": "python3 check_protected_files.py $CLAUDE_FILE_PATH"
}
]
}
}If check_protected_files.py exits with a non-zero status, Claude's write is blocked. This lets you allow writes in general but protect specific files (production configs, migration scripts, CI pipelines) with custom logic.
CI/CD Permission Patterns
GitHub Actions with skip permissions + Docker sandbox
jobs:
claude-review:
runs-on: ubuntu-latest
container:
image: ghcr.io/anthropics/claude-code:latest
options: --network=none # no network access
steps:
- uses: actions/checkout@v4
- name: Claude review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
claude --dangerously-skip-permissions \
--print "review the diff for bugs" > review.md
- uses: actions/upload-artifact@v4
with:
name: review
path: review.mdThe --network=none flag on the container removes network access, so even with skip permissions, Claude cannot exfiltrate data or install packages from the internet. The only network request it makes is to the Anthropic API (which Docker routes through the host).
Note on --network=none
With --network=none, the Anthropic API call also fails. In practice, you need a more targeted network policy (e.g., allow only api.anthropic.com) or pass the API response via a sidecar.
Security Considerations
The permission system protects against two threats: Claude executing unintended actions, and malicious prompt injections in code/docs that try to trick Claude into running commands.
- Allowlist reduces attack surface. A prompt injection in a README cannot trigger
curlifcurlis not inallowedTools. - Skip mode removes all guardrails. In skip mode, a prompt injection embedded in a code comment could execute arbitrary commands. This is why skip mode should only run in sandboxed environments.
- Hooks add defense in depth. Even with a broad allowlist, hooks can block writes to sensitive paths or log all commands for audit.
- Project settings are team-enforced.
.claude/settings.jsonis committed to the repo. If the team restrictsallowedTools, every team member gets those restrictions (though individuals can locally override viasettings.local.json).
FAQ
Can I allowlist all tools at once?
There is no "allowedTools": ["*"] wildcard. You need to list each tool explicitly. If you want to skip all permission prompts, use --dangerously-skip-permissions. The design is intentional: allowlists force you to think about what you're allowing.
Do permissions persist between sessions?
Settings.json permissions persist. One-time approvals during a session (clicking "allow" on a prompt) apply only to that session by default. To make them permanent, select "always allow" which adds the tool to your settings.json automatically.
How do I see what permissions are currently active?
Run /permissions in a Claude Code session to see the merged permissions from all three settings levels plus any session-level approvals.
Use Morph with Your Claude Code Permissions
Morph works with all permission modes. Set apiBaseUrl in your settings.json and get faster file edits at lower cost. No permission changes needed.