CoreMesh

Claude Code Permissions Allow All: Rules You Can Copy and Test

How to allow all Claude Code permissions or build an allow list: allow, ask and deny rule syntax for Bash, Read, Edit and MCP, plus how to test them.

Claude Code Permissions Allow All: Rules You Can Copy and Test

There are two different things people mean by "allow all" in Claude Code. The first is "stop prompting me for the tools I use all day," which you solve with an allow list in settings.json. The second is "turn the permission system off," which you solve with the bypassPermissions mode, and that only takes effect from your user settings, managed settings, or the --permission-mode flag. It silently does nothing from a project or local file as of Claude Code 2.1.257.

The allow list is the one most people actually want. It looks like this and goes in .claude/settings.json (shared with the repo) or ~/.claude/settings.json (just you):

json
{
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(git status)",
      "Read(./src/**)",
      "Edit(./src/**)"
    ],
    "ask": [
      "Bash(git push *)"
    ],
    "deny": [
      "Read(./.env)",
      "Bash(rm -rf *)"
    ]
  }
}

The rest of this page covers where those rules live, the pattern syntax for Bash, Read, Edit and MCP tools, how to confirm a rule is doing what you think, and the handful of reasons an allow rule gets ignored.

Where allow rules live and how files combine#

Claude Code reads permission rules from several files, and it merges the lists rather than picking one. If your user file allows Bash(npm run *) and the project file allows Bash(git status), you get both. The settings page spells out which keys merge and which don't. If you're unsure which file to edit, the settings.json locations guide walks through each one.

The files you'll touch day to day:

  • ~/.claude/settings.json: your user settings. Rules here apply in every project.
  • .claude/settings.json: the project file. Commit it so teammates get the same rules.
  • .claude/settings.local.json: your local overrides for one project. This is where Claude Code writes a rule when you pick "Yes, and don't ask again" on a prompt in the CLI.
  • Managed settings: set by an administrator. Organizations can set allowManagedPermissionRulesOnly so that only their rules count and yours are ignored.

One detail that trips people up: allow rules in a project file wait for trust. Each teammate has to trust the folder before project permissions.allow and permissions.additionalDirectories kick in. Until then they still see prompts. deny and ask rules apply right away, trusted or not. If a rule you committed "doesn't work" on a colleague's machine, that's usually it.

How to write allow, ask and deny rules#

Each rule is a string. The simplest form is just a tool name, which matches every call to that tool:

json
{
  "permissions": {
    "allow": ["Read", "Glob", "Grep"]
  }
}

To narrow a rule, put a pattern in parentheses after the tool name: Bash(npm run *), Read(./docs/**), Edit(./src/**). The three lists mean what they say:

List Effect Applies from a project file before trust?
allow Runs without a prompt No
ask Always prompts, even if an allow rule also matches Yes
deny Blocked, no prompt Yes

When more than one rule matches the same call, deny wins over ask, and ask wins over allow. That ordering holds across files too, so an allow rule in your local file can't outrank an ask rule in the project or managed file. The permissions documentation has the full precedence description.

Bash(npm run *) is a reasonable thing to allow for a whole team. A bare Bash is not, because it hands over every shell command.

Bash patterns and wildcards#

Bash rules match on the command string. There are two useful shapes:

  • Exact match: Bash(git status) matches git status and nothing else. It doesn't match git status --short.
  • Prefix match: Bash(git status *) matches git status followed by anything. The space before the * matters. Bash(npm run *) matches npm run build and npm run test -- --watch.

You'll also see the older Bash(npm run test:*) form in the wild. It still works, but the space-and-star form is what the current docs show, so use that for new rules.

A practical starter set for a Node project:

json
{
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(npm test *)",
      "Bash(npx prettier *)",
      "Bash(git status *)",
      "Bash(git diff *)",
      "Bash(git log *)",
      "Bash(git add *)",
      "Bash(git commit *)"
    ],
    "ask": [
      "Bash(git push *)",
      "Bash(npm publish *)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(git push --force *)"
    ]
  }
}

Notice that git push lands in ask while git push --force lands in deny. Because deny wins over ask, a force push is blocked even though the broader push rule would have prompted.

Prefix matching also means Bash(ls) doesn't cover lsof. Exact rules match whole commands, and prefix rules match on the text before the *. That's the behavior you want, and it's the part people most often get backwards when they write a rule and then wonder why a similar command still prompts.

Read, Edit and MCP patterns#

Read and Edit rules take file paths with gitignore-style globs. ** crosses directories and * matches within one path segment. The path forms:

  • Read(./src/**): relative to the project. Anything under src.
  • Read(~/.zshrc): relative to your home directory.
  • Edit(//etc/**): an absolute path, written with a double leading slash.

A common pattern is to allow edits inside the code tree and deny reads of anything secret:

json
{
  "permissions": {
    "allow": [
      "Read(./src/**)",
      "Read(./docs/**)",
      "Edit(./src/**)",
      "Edit(./docs/**)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",
      "Edit(./package-lock.json)"
    ]
  }
}

MCP tools follow a naming convention: mcp__<server>__<tool>. Two levels are useful:

json
{
  "permissions": {
    "allow": [
      "mcp__github",
      "mcp__playwright__browser_navigate"
    ]
  }
}

The first line allows every tool from the server configured as github. The second allows one tool from the playwright server and leaves the rest prompting. Allowing a whole server is the MCP version of "allow all," so reserve it for servers whose every tool is read-only or otherwise harmless. The server name must match what's in your MCP config, so check /mcp if a rule like this isn't matching.

Allow everything with bypassPermissions#

If you really want no prompts at all, that's a mode, not a rule. Set permissions.defaultMode to bypassPermissions in your user settings:

json
{
  "permissions": {
    "defaultMode": "bypassPermissions"
  }
}

Or pass it for a single session:

bash
claude --permission-mode bypassPermissions

The --dangerously-skip-permissions flag does the same thing for one run and has been the usual choice for containers and CI. Either way, this only makes sense inside an isolated environment where a bad command can't reach anything that matters. On your laptop, an allow list is the better tool.

Two scope rules matter here, both from the settings troubleshooting section:

  • bypassPermissions and auto don't take effect from .claude/settings.json or .claude/settings.local.json. Put them in ~/.claude/settings.json or managed settings, or use the flag. Before version 2.1.257, bypassPermissions worked from any file, which is why older blog posts and READMEs show it in project files.
  • Managed settings can override your choice. If /status shows a managed source, your administrator may have pinned the mode.

If you're running non-interactive sessions, the headless Claude Code setup covers how this fits with claude -p, and the GitHub Actions workflow shows a CI-shaped example.

How to test your permission rules#

You don't have to guess whether a rule matches. A short loop:

  1. Run /status inside Claude Code. It lists which settings files loaded. If your file isn't there, the JSON is probably invalid, and Claude Code skips a broken file rather than loading part of it. Run it through a JSON validator or jq . .claude/settings.json.
  2. Run /permissions to see the rules Claude Code actually assembled from all files, including which file each came from.
  3. Ask Claude to run a command that should match, like "run npm run build". No prompt means the allow rule fired. A prompt means either the pattern didn't match or a higher-priority ask rule did.
  4. Try a near miss on purpose. If you allowed Bash(git status *), ask for git stash and confirm it still prompts.

It helps to have a mental model of the matching before you edit files. This script mimics the documented order for Bash prefix rules. The real matcher does more (it inspects compound commands, for example), so treat it as a sketch rather than the implementation:

javascript
// Simplified model of Bash rule matching: deny > ask > allow.
const rules = {
  deny: ["Bash(rm -rf *)", "Bash(git push --force *)"],
  ask: ["Bash(git push *)"],
  allow: ["Bash(npm run *)", "Bash(git *)", "Bash(ls)"],
};

function matches(rule, cmd) {
  const m = rule.match(/^Bash\((.*)\)$/);
  if (!m) return false;
  const pat = m[1];
  if (pat === "*") return true;
  if (pat.endsWith(" *")) {
    const prefix = pat.slice(0, -2);
    return cmd === prefix || cmd.startsWith(prefix + " ");
  }
  return cmd === pat;
}

function decide(cmd) {
  for (const kind of ["deny", "ask", "allow"]) {
    const hit = rules[kind].find((r) => matches(r, cmd));
    if (hit) return `${kind.padEnd(5)} via ${hit}`;
  }
  return "ask   (no rule matched, default prompt)";
}

const commands = [
  "npm run build",
  "git status",
  "git push origin main",
  "git push --force origin main",
  "rm -rf node_modules",
  "ls",
  "lsof -i :3000",
];
for (const cmd of commands) console.log(cmd.padEnd(30), decide(cmd));

The ls versus lsof row is the one to look at. Exact rules only match the whole command.

For the file itself, a quick lint catches the mistakes that make Claude Code skip it or make rules fight each other:

python
import json

raw = '''{
  "permissions": {
    "allow": ["Bash(npm run *)", "Read(./src/**)", "Edit(./src/**)", "Bash(cat *)"],
    "ask": ["Bash(git push *)"],
    "deny": ["Read(./.env)", "Bash(cat *)", "Bash(rm -rf *"]
  }
}'''

cfg = json.loads(raw)
perms = cfg.get("permissions", {})
for kind in ("allow", "ask", "deny"):
    print(f"{kind}: {perms.get(kind, [])}")

overlap = set(perms.get("allow", [])) & set(perms.get("deny", []))
for rule in sorted(overlap):
    print(f"warning: {rule!r} is in both allow and deny; deny wins")

for kind in ("allow", "ask", "deny"):
    for rule in perms.get(kind, []):
        if "(" in rule and not rule.endswith(")"):
            print(f"warning: {kind} rule {rule!r} is missing its closing parenthesis")

Swap the embedded string for the contents of your own file and you have a pre-commit check that takes a second to run.

Claude Code permissions allow not working: common causes#

Most "my allow rule is ignored" reports come down to one of these.

  • The folder isn't trusted. Project allow rules wait until each person trusts the folder. deny and ask don't. Trust the folder and the prompts stop.
  • An ask rule outranks it. You clicked "Yes, and don't ask again," which wrote an allow rule to .claude/settings.local.json, but the project or managed file has an ask rule for the same tool. Ask wins. Remove the ask rule from the shared file or accept the prompt.
  • Wrong file for the key. defaultMode: "bypassPermissions" in a project file does nothing since 2.1.257. Move it to user settings or use --permission-mode.
  • Invalid JSON. A trailing comma or a missing bracket makes Claude Code skip the whole file. /status won't list it. Validate the file.
  • Pattern shape. Bash(npm run*) without the space, or Bash(npm run) without the star, won't match npm run build. Use Bash(npm run *).
  • MCP name mismatch. mcp__github only works if the server is registered under exactly github. Check /mcp for the real name.
  • Managed rules only. If your organization set allowManagedPermissionRulesOnly, your own allow rules are dropped. /status shows whether managed settings are present.
  • Wrong destination in VS Code. The VS Code extension's approval card lets you pick which file the rule goes to, including the project's shared file. Picking the shared file changes the rule for everyone on the repo, so look at the selector before you confirm.

If you want prompts for a category of commands without writing a dozen rules, a PreToolUse hook can make the decision in code. The hooks walkthrough has copy-paste examples. Hooks and rules compose, so you can allow broadly and let a hook catch the exceptions.

Quick reference#

Goal Rule or setting File
Allow one command Bash(git status) any
Allow a command prefix Bash(npm run *) any
Allow every Bash command Bash user, with care
Allow reads under a directory Read(./src/**) any
Allow edits under a directory Edit(./src/**) any
Block a file from being read deny: ["Read(./.env)"] any, applies before trust
Always prompt for something ask: ["Bash(git push *)"] any, applies before trust
Allow all tools from an MCP server mcp__servername any
Allow one MCP tool mcp__servername__toolname any
Turn prompts off entirely "defaultMode": "bypassPermissions" user or managed only
Same thing for one session claude --permission-mode bypassPermissions flag

Before you commit a rules file:

  1. Run the file through a JSON validator.
  2. Start Claude Code and check /status lists the file.
  3. Check /permissions shows your rules with the expected source.
  4. Trigger one allowed command, one asked command and one denied command.
  5. Confirm teammates know to trust the folder, or the allow rules won't reach them.
  6. Keep bypassPermissions out of the project file. It won't apply there, and it signals the wrong intent to anyone who reads it.

For every key mentioned above, the settings reference lists its scope column, which tells you at a glance whether a repository file can set it.

Quick answers

How do I allow all Claude Code permissions?

Set permissions.defaultMode to bypassPermissions in ~/.claude/settings.json or managed settings, or start a session with claude --permission-mode bypassPermissions. As of version 2.1.257 it doesn't take effect from project or local settings files.

How do I always allow a specific command without a prompt?

Add a rule like "Bash(npm run *)" to the permissions.allow list in your user, project or local settings.json. Picking "Yes, and don't ask again" on a prompt writes the same kind of rule to .claude/settings.local.json for you.

Why is my Claude Code allow rule not working?

The usual causes are an untrusted project folder (project allow rules wait for trust), an ask or deny rule in another file that outranks it, invalid JSON that makes Claude Code skip the file, or a pattern missing the space before the asterisk. Run /status and /permissions to see what actually loaded.

Can I use wildcards in Claude Code permission rules?

Yes. Bash rules use a trailing " *" for prefix matches, such as Bash(git *), and Read and Edit rules use gitignore-style globs like Read(./src/**).

How do I allow MCP tools in Claude Code?

Use mcp__servername to allow every tool from a server, or mcp__servername__toolname for a single tool. The server name must match the one shown by /mcp.

Did this solve it?

One tap. No account.

Saved you an hour? Send a tip.

USDC or ETH on Base, Ethereum, Arbitrum and more. Keeps the notes free and the examples running.

Base

Network

0xc783bc5c3fa14cEC3f09A22284bC91C5AF0d59e1
Open in wallet · 5 USDC

On a computer? Scan the code with your phone wallet, or copy the address.

Pick the network your wallet or exchange uses. USDC works on all six; ETH on Base, Ethereum, Arbitrum and Optimism. Don't send on Tron or Solana: this address doesn't exist there. Tips are optional and unlock nothing; every guide stays free.

Discussion

Start the discussion

Indra Wijaya

Builds and runs AI agents, bots and content pipelines, and writes down what works and what broke.

About · Email · RSS

Keep reading

New notes, no spam.

One post at a time, only when there's something worth reading. Follow in any feed reader.

Subscribe via RSS