Back to blog

April 19, 2026

Configuration

Best Claude Code Configuration: The Complete Settings Guide (2026)

The default Claude Code setup works. A tuned configuration ships 3x faster with half the permission prompts and none of the dangerous blind spots. Here is the exact config we run across 40+ production repos at KaiShips.

The Best Claude Code Configuration in One Paragraph

The best Claude Code configuration combines four files: a project .claude/settings.json with explicit permissions and a Sonnet 4.6 model, a root CLAUDE.md with stack context and commands, a .mcp.json registering the tools your team uses, and PreToolUse hooks in .claude/hooks/ for anything destructive. Anything less is either unsafe or annoying.

FILE 1

.claude/settings.json

Permissions, model, env vars, hooks, and statusline. The runtime config.

FILE 2

CLAUDE.md

Stack context, commands, red lines. Injected into every conversation.

FILE 3

.mcp.json

MCP server registry. GitHub, Linear, databases, Stripe, browser.

FILE 4

.claude/hooks/*

Shell scripts that hard-enforce what permissions cannot. The safety net.

The 5-Layer Configuration Hierarchy

Claude Code reads configuration from five sources, merged in a specific priority order. According to the official Claude Code docs at code.claude.com/docs/en/settings, array fields like permissions.allow merge across layers instead of overwriting, so team rules and personal rules coexist.

HIGHEST

1. Enterprise managed settings

MDM-deployed policy file, usually at /Library/Application Support/ClaudeCode/managed-settings.json. Overrides everything. Individual users cannot modify it.

2

CLI arguments

Flags like --model or --permission-mode. One-shot overrides for a session.

3

Project local: .claude/settings.local.json

Git-ignored. Personal overrides: your statusline, personal allow rules, local tool paths. Never commit this file.

4

Project shared: .claude/settings.json

Checked into git. Team rules. Required hooks, deny patterns, shared model default, MCP registry references.

LOWEST

5. User: ~/.claude/settings.json

Applies to every project. Your global allow list, preferred model, env vars, keybindings. The base layer.

The Starter settings.json (Copy This)

Drop this into .claude/settings.json at your repo root. It covers the fields that matter on day one: schema, model, permissions, env, statusline, and hooks scaffolding.

{
  "$schema": "https://code.claude.com/schemas/settings.json",
  "model": "claude-sonnet-4-6",
  "permissions": {
    "defaultMode": "acceptEdits",
    "allow": [
      "Read",
      "Write",
      "Edit",
      "Glob",
      "Grep",
      "Bash(git status)",
      "Bash(git diff:*)",
      "Bash(git log:*)",
      "Bash(npm run:*)",
      "Bash(npm test:*)",
      "Bash(bun run:*)",
      "Bash(pnpm run:*)"
    ],
    "ask": [
      "Bash(git push:*)",
      "Bash(npm publish)"
    ],
    "deny": [
      "Bash(rm -rf:*)",
      "Bash(sudo:*)",
      "Bash(curl:*| sh)",
      "Read(.env)",
      "Read(.env.*)"
    ]
  },
  "env": {
    "CLAUDE_CODE_ENABLE_TELEMETRY": "0",
    "NODE_ENV": "development"
  },
  "statusLine": {
    "type": "command",
    "command": ".claude/statusline.sh"
  },
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/guard-bash.sh"
          }
        ]
      }
    ]
  }
}

The $schema field enables autocomplete in VS Code and JetBrains. That alone catches ~90% of typos before you hit save.

Permissions: Allow, Deny, Ask, defaultMode

The permissions object is the single highest-leverage setting in Claude Code. Rules are evaluated in order: deny first, then ask, then allow. First match wins. Deny always takes precedence.

Permission Mode Comparison

ModeBehaviorWhen to use
defaultAsks for every tool callFirst-time users
acceptEditsAuto-accepts Read/Write/EditDaily driver (recommended)
planRead-only, no mutationsExploring a new codebase
bypassPermissionsSkips all promptsIsolated sandbox or CI only

Rule Patterns You Will Actually Use

  • Exact match: Bash(git status) allows only that exact command.
  • Prefix wildcard: Bash(npm run:*) allows any npm run subcommand.
  • Full tool: Read allows every file read. Scope it if you have secrets on disk.
  • File-scoped: Read(.env) in deny blocks env file reads even when Read is allowed.

Known issue (April 2026): GitHub issue #18160 and #18846 report that some Bash allow rules are not reliably enforced. The fix is a PreToolUse hook that validates Bash commands against your own allowlist. See the hooks section below.

Model Selection: Which ID to Set in 2026

The model field accepts a full model ID or an alias. For most teams in 2026, Claude Sonnet 4.6 (claude-sonnet-4-6) is the default. It costs a fraction of Opus per token while matching Opus on most coding benchmarks.

Model IDUse forRelative cost
claude-opus-4-7Complex refactors, architecture, orchestration5x Sonnet
claude-sonnet-4-6Daily coding, PRs, debugging, tests1x (baseline)
claude-haiku-4-5-20251001Status checks, log parsing, triage, fast subagents0.2x Sonnet

Pin your daily driver at the project level. Override with /model inside Claude Code when a task actually needs Opus. Do not default to Opus globally. Most tasks do not need it and the token bill piles up fast.

The complete playbook

Every setting, every permission rule, and the hook patterns that make Claude Code actually ship work.

This post covers core configuration. The KaiShips Guide to Claude Code goes deeper into production hooks, MCP orchestration, subagent routing, cron automation, and the full settings.json we run at $0 to $1M revenue scale.

Get the KaiShips Guide to Claude Code -- $29

Environment Variables and the env Key

The env key in settings.json sets environment variables that Claude Code reads on startup. Use it for feature flags, API endpoints, and telemetry toggles. Never put secrets here because the file is checked into git.

CLAUDE_CODE_ENABLE_TELEMETRY

Set to "0" to disable anonymous usage telemetry. Many teams flip this off by default.

DISABLE_AUTOUPDATER

Set to "1" to freeze your Claude Code version in CI. Prevents surprise behavior changes mid-pipeline.

ANTHROPIC_MODEL

Overrides the model field at runtime. Useful for CI jobs that should pin a cheaper model without editing settings.

CLAUDE_PROJECT_DIR

Exposed to hook scripts. Use it inside hooks to find the repo root without fragile relative paths.

Do not put trailing newlines in env values. A single "\n" at the end of a value breaks Vercel deploys and some MCP servers. This burned us for a full day in March. Strip whitespace before saving.

Hooks: The Safety Net Permissions Cannot Replace

Hooks run shell commands at specific lifecycle events. PreToolUse is the most valuable hook type because it sees every tool call before execution and can block, warn, or allow it. Unlike permission rules, hooks can do real validation: parameter inspection, path checks, risk scoring.

Minimum Viable guard-bash.sh

#!/usr/bin/env bash
# .claude/hooks/guard-bash.sh
# Read the tool call from stdin
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')

# Hard deny dangerous patterns
if echo "$cmd" | grep -qE 'rm -rf|sudo|curl.*\| ?sh|:\(\)\{'; then
  echo "{\"decision\": \"deny\", \"reason\": \"Blocked dangerous pattern\"}"
  exit 0
fi

# Ask before git push
if echo "$cmd" | grep -qE '^git push'; then
  echo "{\"decision\": \"ask\", \"reason\": \"git push is reviewed manually\"}"
  exit 0
fi

# Default: allow
echo "{\"decision\": \"allow\"}"

Make the file executable with chmod +x .claude/hooks/guard-bash.sh and register it under hooks.PreToolUse with matcher "Bash". For a full list of 26 lifecycle events, see our Claude Code Hooks Guide.

MCP Servers: .mcp.json at the Repo Root

Model Context Protocol servers extend Claude Code with tools that the built-in set does not cover. GitHub, Linear, Sentry, Stripe, browser automation, database connectors. Register them with claude mcp add <name> <command>, which writes to .mcp.json.

{
  "mcpServers": {
    "github": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
    },
    "playwright": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@playwright/mcp"]
    },
    "postgres": {
      "type": "stdio",
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-postgres",
        "postgresql://localhost/devdb"
      ]
    }
  }
}

Use environment variable interpolation for secrets instead of hardcoding them. Claude Code expands ${VAR} from the parent shell at startup. For a deeper walkthrough, see our Claude Code MCP Servers Guide.

Statusline: Show Context You Actually Care About

The statusline is a shell script that prints a one-line status bar at the bottom of Claude Code. The default one is fine. A custom one showing your current git branch, active model, and token budget is better.

#!/usr/bin/env bash
# .claude/statusline.sh
input=$(cat)
model=$(echo "$input" | jq -r '.model.id')
cwd=$(echo "$input" | jq -r '.workspace.current_dir')
branch=$(git -C "$cwd" branch --show-current 2>/dev/null || echo "-")
cost=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')

printf "[%s] %s @ %s ($%.2f)" "$branch" "$model" "$(basename "$cwd")" "$cost"

Claude Code pipes a JSON payload into the script on stdin. Fields include model.id, workspace.current_dir, and cost.total_cost_usd. Keep the output under one line. Statusline refreshes are synchronous.

The Complete .claude/ Directory Layout

A mature Claude Code project has a predictable structure under .claude/. Slash commands, hooks, subagents, and skills each have a dedicated subdirectory. This makes behavior discoverable and reviewable in PRs.

.
├── CLAUDE.md                # project context (checked in)
├── .mcp.json                # MCP server registry (checked in)
└── .claude/
    ├── settings.json        # team config (checked in)
    ├── settings.local.json  # personal config (gitignored)
    ├── statusline.sh        # custom statusline script
    ├── commands/            # custom slash commands
    │   ├── ship.md
    │   └── review.md
    ├── hooks/               # PreToolUse, PostToolUse scripts
    │   ├── guard-bash.sh
    │   └── format-on-save.sh
    ├── agents/              # custom subagents
    │   └── code-reviewer.md
    └── skills/              # custom skills (if used)
        └── seo-2026/
            └── SKILL.md

Add this to .gitignore so personal overrides stay out of the repo: .claude/settings.local.json.

5 Configuration Mistakes That Cost Us Days

Every item below burned real hours at KaiShips. Skip the tuition by copying the lesson.

#1

Trusting Bash allow rules alone

GitHub issues #18160 and #18846 document that Bash allow rules are sometimes bypassed. Always add a PreToolUse hook that enforces your Bash policy. Belt and suspenders.

#2

Leaving bypassPermissions on

Great for CI runners. Dangerous on your laptop. Set defaultMode to acceptEdits, not bypassPermissions. The difference is your deny list still runs.

#3

Committing settings.local.json

Personal allow lists and absolute paths leak into the team repo. Add the file to .gitignore the moment you create the .claude/ folder.

#4

Pinning Opus as the global default

Opus 4.7 on every task is a 5x cost multiplier against Sonnet 4.6 for negligible benefit on routine coding. Pin Sonnet. Escalate with /model.

#5

No CLAUDE.md, just settings.json

Settings control what Claude can do. CLAUDE.md tells Claude what you want it to do. Without both, you either fight prompts all day or get generic output.

Verify Your Configuration in 60 Seconds

Once your settings.json is in place, run a quick check. Do not commit and push without confirming the config actually loads.

  • Run /config inside Claude Code. It prints the merged configuration. Confirm model, permissions, and hooks look right.
  • Try a denied command. Ask Claude to run rm -rf /tmp/foo. It should refuse. If it does not, your deny list is not wired up.
  • Check MCP servers. Run claude mcp list to confirm every registered server started successfully.
  • Watch the statusline. If it is blank or shows an error, the script is missing, non-executable, or taking too long.

Claude Code Configuration FAQ

Short answers to the questions we hear most often from teams adopting Claude Code in 2026.

What is the best Claude Code configuration for a new project?

Start with a .claude/settings.json that sets model to claude-sonnet-4-6, adds a permissions.allow array with the 8 to 12 commands you run most often, and sets defaultMode to acceptEdits. Add a CLAUDE.md at the repo root with 200 to 400 lines describing your stack, commands, and red lines. This baseline eliminates 80% of permission prompts without sacrificing safety.

Where does Claude Code store its settings?

Claude Code reads five settings files in priority order. Enterprise managed settings override everything, then command-line arguments, then project local (.claude/settings.local.json), then project shared (.claude/settings.json), then user (~/.claude/settings.json). Array fields like permissions.allow merge across layers instead of overwriting, so team rules and personal rules coexist.

How do I stop Claude Code from asking permission every time?

Set permissions.defaultMode to acceptEdits for file edits and add specific commands to permissions.allow. For Bash, use patterns like Bash(npm run test:*) instead of bare Bash. Known issue: as of April 2026 some Bash allow rules are unreliable (GitHub issue #18160), so a PreToolUse hook is the fallback for hard enforcement.

Which model should I set in claude code settings.json?

Claude Sonnet 4.6 (model: 'claude-sonnet-4-6') hits the best balance of cost and capability for 90% of coding tasks in 2026. Reserve Opus 4.7 for complex refactors, architecture work, and orchestration. Use Haiku 4.5 for fast status checks, log parsing, and throwaway scripts. The full model ID for Opus is 'claude-opus-4-7'.

What is the difference between settings.json and settings.local.json?

settings.json is checked into git and shared with your team. settings.local.json is git-ignored and holds personal preferences like your statusline script, preferred model, or local hook paths. Team rules (deny dangerous Bash patterns, required hooks) belong in settings.json. Personal ergonomics belong in settings.local.json.

Do I need CLAUDE.md if I have settings.json?

Yes. settings.json controls runtime behavior (permissions, hooks, model). CLAUDE.md is loaded into every conversation as context and tells Claude how your codebase works, what commands to run, and what rules to follow. A project with both ships work 3x faster than one with either alone, based on internal KaiShips metrics across 40+ repos.

How do I configure MCP servers in Claude Code?

Add servers to .mcp.json at the repo root using claude mcp add <name> <command>. The file supports stdio, SSE, and HTTP transports. Keep MCP scope project-level for tools your team uses (Linear, Sentry, Stripe) and user-level for personal tools (local databases, scratch servers). The 10 best servers to install first are covered in our MCP guide.

Keep Going

Configuration is the foundation. These companion guides layer on top of what you just built.

The complete playbook

Every setting, every permission rule, and the hook patterns that make Claude Code actually ship work.

This post covers core configuration. The KaiShips Guide to Claude Code goes deeper into production hooks, MCP orchestration, subagent routing, cron automation, and the full settings.json we run at $0 to $1M revenue scale.

Get the KaiShips Guide to Claude Code -- $29