nikolas.sapa
April 16, 2026

02 — Settings & Permissions

settings.json is where Claude Code gets its boundaries. What tools it can run freely, what needs a confirmation, and what's off-limits. Done right, you get autonomous work on routine tasks and a prompt before anything destructive.


1. The Three-Layer Config

Claude Code reads settings from three places, in order of increasing specificity:

LayerPathScope
Global~/.claude/settings.jsonApplies everywhere
Project.claude/settings.jsonThis repo only
Local.claude/settings.local.jsonYour machine, not committed

Each layer merges on top of the previous. Use global for behavior you want everywhere, project for team-shared rules, and local for machine-specific overrides (API keys, paths, tooling that isn't on every dev's machine).


2. Permission Modes

Every tool call falls into one of three modes:

  • allow — runs without prompting
  • ask — Claude pauses and waits for confirmation
  • deny — blocked entirely

You configure these as arrays in settings.json:

{
  "permissions": {
    "allow": [
      "Bash(git log*)",
      "Bash(npm run build*)",
      "mcp__myserver__some_tool"
    ],
    "ask": [
      "Bash(rm *)",
      "Bash(git push --force*)"
    ],
    "deny": []
  }
}

The pattern syntax uses glob-style wildcards. Bash(git log*) allows any git log invocation. Bash(rm *) stops Claude before any file deletion and asks you first.

A practical split: auto-approve read operations and common build commands; require confirmation for anything destructive (deletion, force pushes, database drops).


3. The defaultMode Flag

{
  "defaultMode": "bypassPermissions"
}

bypassPermissions removes the generic per-call prompt. Claude still respects your allow/ask/deny lists — this flag removes the runtime nag for things not explicitly listed.

Pair it with skipDangerousModePermissionPrompt: true to suppress the startup warning. Use this only once you trust your ask list covers the dangerous cases.


4. The env Block

Environment variables you want available in every Claude session:

{
  "env": {
    "CLAUDE_CODE_DISABLE_AUTO_MEMORY": "1",
    "CLAUDE_CODE_SUBAGENT_MODEL": "claude-sonnet"
  }
}

Two flags worth knowing:

  • CLAUDE_CODE_DISABLE_AUTO_MEMORY — prevents Claude from auto-summarizing sessions into its built-in memory. Useful if you manage context manually.
  • CLAUDE_CODE_SUBAGENT_MODEL — sets which model spawned subagents use by default. Keeps costs predictable.

Set whichever variables your tooling or workflow needs. This is also where you expose things like NODE_ENV or a custom API base URL without having to set them in every terminal session.


5. Enabling MCP Tools

MCP tool calls follow the same allow/ask/deny system. The tool name format is mcp__<server>__<tool>:

{
  "permissions": {
    "allow": [
      "mcp__notion__notion-search",
      "mcp__playwright__browser_navigate"
    ]
  }
}

If you're using enableAllProjectMcpServers: true in your local settings, all project-defined MCP servers activate without per-server approval. Useful on your own machine; don't commit it to shared project settings.


6. Hooks

Hooks let you run shell commands at lifecycle events. Three useful moments:

{
  "hooks": {
    "Stop": [{ "type": "command", "command": "echo 'Claude finished'" }],
    "Notification": [{ "type": "command", "command": "echo 'Claude needs input'" }],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{ "type": "command", "command": "my-rewrite-script" }]
      }
    ]
  }
}

Stop fires on session end. Notification fires when Claude pauses for input. PreToolUse with a matcher lets you intercept specific tool calls before they execute — useful for logging, rewriting commands, or enforcing patterns at the harness level rather than in the prompt.

Adapt these to your OS and workflow. macOS users often wire them to osascript for system notifications.


FAQ

How do I give Claude Code all permissions?

Set "defaultMode": "bypassPermissions" in settings.json. That drops the per-call prompt for anything not explicitly listed, but your allow, ask, and deny arrays still apply. Add "skipDangerousModePermissionPrompt": true to suppress the startup warning. Only do this once your ask list covers the destructive cases.

What are Claude Code permission wildcards?

The pattern syntax is glob-style. Bash(git log*) matches any git log invocation; Bash(rm *) matches any deletion. Wildcards let you approve a whole family of commands with one rule instead of listing every variant — auto-approve Bash(npm run build*) and send Bash(git push --force*) to ask.

Where do global and project permissions go?

Three layers, each merging on top of the last. ~/.claude/settings.json applies everywhere. .claude/settings.json is committed and shared with the repo. .claude/settings.local.json stays on your machine and isn't committed. Global for personal defaults, project for team rules, local for machine-specific paths and keys.

How do I block a tool entirely?

Add the pattern to the deny array under permissions. Denied calls are blocked outright — Claude doesn't prompt, it simply can't run them. Use ask when you want a confirmation instead of a hard block. deny is for what should never run in this repo, whatever the context.

How do I allow MCP tools?

MCP calls use the same allow/ask/deny lists. The tool name format is mcp__<server>__<tool> — for example, mcp__playwright__browser_navigate. To activate every project-defined MCP server without approving each one, set enableAllProjectMcpServers: true in your local settings. Don't commit that to shared project settings.

What does skipDangerousModePermissionPrompt do?

It suppresses the startup warning shown when you run with bypassPermissions. It changes nothing about what Claude is allowed to do — it only removes the banner. Pair it with defaultMode: bypassPermissions once you trust your ask list to catch anything destructive.