Claude Academy
Sign in

Claude Code in Action · lesson 3 of 6

Claude Code Settings & Permissions

settings.json controls permissions, env vars, hooks, MCP servers, output format, and model selection.

File locations (precedence: top wins)

  1. Command-line flags
  2. <repo>/.claude/settings.local.json — local-only, gitignored
  3. <repo>/.claude/settings.json — project-shared, committed
  4. ~/.claude/settings.json — user-global
  5. Enterprise managed settings

Shape

{
  "model": "claude-opus-4-7",
  "permissions": {
    "allow": [
      "Read(./**)",
      "Bash(npm test:*)",
      "Bash(git status)",
      "Bash(git diff:*)"
    ],
    "deny": [
      "Bash(rm -rf:*)",
      "Bash(git push --force:*)"
    ]
  },
  "env": {
    "ANTHROPIC_API_KEY": "${env:ANTHROPIC_API_KEY}"
  },
  "hooks": {
    "PreToolUse": [...],
    "PostToolUse": [...]
  }
}

Permission match rules

  • Permission strings: Tool(arg-pattern).
  • Arg pattern: glob-like (Bash(npm test:*) matches any npm test ... invocation).
  • deny is evaluated first. A denied call never proceeds.
  • allow skips the user-confirmation prompt.
  • Anything not allowed nor denied → user is prompted.

Useful patterns

CI / non-interactive:

{"permissions": {"allow": ["*"], "deny": ["Bash(rm:*)", "Bash(git push:*)"]}}

Combine with claude --print so unattended runs don't block on prompts.

Read-only review:

{"permissions": {"allow": ["Read(./**)", "Grep(*)", "Glob(*)"], "deny": ["Edit(*)", "Write(*)", "Bash(*)"]}}

Per-project tools:

{"permissions": {"allow": ["Bash(./scripts/deploy.sh:*)", "Bash(make:*)"]}}

Output format (for scripting)

claude -p "task" --output-format json returns a JSON envelope you can pipe. --output-format stream-json for streaming events.

CCA-F angle

The exam tests where to put a permission so it applies in CI but not locally (or vice versa), and how to safely auto-approve a narrow set of tools without opening the floodgates. Memorize the precedence order and that deny always wins.

See also