ailiteracynepal 🇳🇵
Text size

Chapter 06 · Section I · 12 min read

Permission modes explained

Claude Code prompts for approval on tool calls by default. Permission modes are the knobs that decide which calls prompt, which are auto-approved, and which are denied outright. Understand them once, tune once, and stop being surprised.

Every tool call Claude Code proposes goes through the permission system before it runs. Whether you see a prompt, get an auto-approval, or hit a hard “no” is controlled by the permission mode you have selected. Getting this right at the start saves a lot of clicking, and — more importantly — saves you from accidentally allowing something you did not mean to.

The modes

Claude Code ships with a small set of built-in modes. The exact names may vary by version, but the shape is the same:

  • prompt-on-tool-use (the default) — every tool call asks. Read this file? Approve. Edit this line? Approve. Run this command? Approve.
  • allowlist — a subset of tools are auto-approved (typically read-only ones — Read, Grep, Glob). Everything else prompts.
  • accept-all — no prompts. Claude runs its tool calls freely. Only appropriate for very contained environments (a scratch VM, a sandboxed container).
  • plan — Claude proposes but does not execute. You see the plan and can approve it as a whole.

You can switch modes mid-session with /config or the equivalent slash command in your version.

Tool-level allowlisting

Beyond the mode, you can allowlist specific tools or specific command patterns in settings.json:

{
  "permissions": {
    "allow": [
      "Bash(npm run test:*)",
      "Bash(git status)",
      "Bash(git diff)",
      "Read",
      "Grep"
    ]
  }
}

That configuration says: those Bash patterns and those tools are auto-approved. Everything else still prompts. This is the sweet spot for most people.

What “prompt” actually shows you

When a prompt appears, three pieces of information are worth reading:

  1. The tool. What kind of action — Read, Edit, Bash, an MCP call.
  2. The target. Which file, or which command, or which URL.
  3. The payload. For Edit, the exact old-string / new-string. For Bash, the exact command line.

The default habit worth building: read all three before approving anything.

The “deny” side

Just as you can allowlist, you can denylist. In particular, if there are Bash patterns you never want to run — rm -rf, git push --force, anything hitting production — put them in the deny list. That way even a slip of the finger cannot approve them.

When to loosen

  • In a throwaway environment. A container, a scratch repo, a VM you can nuke.
  • For clearly-read-only tools. Read, Grep, Glob will not damage anything.
  • For narrow, well-defined command patterns you have vetted. npm run test:unit is fine; npm run * is not.

When to tighten

  • Anything that touches production.
  • Anything running as root, or with admin credentials.
  • First time using a new MCP server. Read a lot; auto-approve nothing.

Check your understanding

Quick check

Which is the safest sane default for daily work in a real codebase?