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.

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):
{
"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
allowManagedPermissionRulesOnlyso 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:
{
"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)matchesgit statusand nothing else. It doesn't matchgit status --short. - Prefix match:
Bash(git status *)matchesgit statusfollowed by anything. The space before the*matters.Bash(npm run *)matchesnpm run buildandnpm 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:
{
"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 undersrc.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:
{
"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:
{
"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:
{
"permissions": {
"defaultMode": "bypassPermissions"
}
}
Or pass it for a single session:
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:
bypassPermissionsandautodon't take effect from.claude/settings.jsonor.claude/settings.local.json. Put them in~/.claude/settings.jsonor managed settings, or use the flag. Before version 2.1.257,bypassPermissionsworked from any file, which is why older blog posts and READMEs show it in project files.- Managed settings can override your choice. If
/statusshows 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:
- Run
/statusinside 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 orjq . .claude/settings.json. - Run
/permissionsto see the rules Claude Code actually assembled from all files, including which file each came from. - 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
askrule did. - Try a near miss on purpose. If you allowed
Bash(git status *), ask forgit stashand 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:
// 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:
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
allowrules wait until each person trusts the folder.denyandaskdon't. Trust the folder and the prompts stop. - An ask rule outranks it. You clicked "Yes, and don't ask again," which wrote an
allowrule to.claude/settings.local.json, but the project or managed file has anaskrule 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.
/statuswon't list it. Validate the file. - Pattern shape.
Bash(npm run*)without the space, orBash(npm run)without the star, won't matchnpm run build. UseBash(npm run *). - MCP name mismatch.
mcp__githubonly works if the server is registered under exactlygithub. Check/mcpfor the real name. - Managed rules only. If your organization set
allowManagedPermissionRulesOnly, your own allow rules are dropped./statusshows 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:
- Run the file through a JSON validator.
- Start Claude Code and check
/statuslists the file. - Check
/permissionsshows your rules with the expected source. - Trigger one allowed command, one asked command and one denied command.
- Confirm teammates know to trust the folder, or the allow rules won't reach them.
- Keep
bypassPermissionsout 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.
Sorry about that. What was missing?
from 0 readers
Saved you an hour? Send a tip.
USDC or ETH on Base, Ethereum, Arbitrum and more. Keeps the notes free and the examples running.
Network
0xc783bc5c3fa14cEC3f09A22284bC91C5AF0d59e1
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
Next note