Claude Code settings.json: Every Field Explained for 2026

Last updated: April 15, 2026

Claude Code settings.json Reference

Quick answer

settings.json is the configuration file for Claude Code. Put it at ~/.claude/settings.json for global rules or .claude/settings.json in the repo root for project rules. Project values override global values. The six main fields are model, hooks, mcpServers, permissions, env, and apiKeyHelper.

Overview

settings.json controls how Claude Code behaves: which model to use by default, what hooks to run, which MCP servers to attach, which tools are allowed, and what environment variables to inject. This page is a field-by-field reference with types, defaults, and a working example.

File locations

Claude Code reads settings.json from two paths:

  • ~/.claude/settings.json: global settings, apply to every project.
  • .claude/settings.json: project-level, committed to the repo.

Project settings override global settings field by field. If ~/.claude/settings.json sets model: claude-opus-4-5 and the project .claude/settings.json sets model: claude-sonnet-4-5, the active model inside that repo is Sonnet.

For per-user overrides that you do not want to commit, use .claude/settings.local.json. Claude Code reads it last and it is git-ignored by default.

All fields

model

Type: string. Default: claude-sonnet-4-5.

Which Claude model to use for every session in this scope. Accepted values include claude-sonnet-4-5, claude-opus-4-5, claude-haiku-4-5, and any pinned model ID from the Anthropic console.

{ "model": "claude-sonnet-4-5" }

Switching models mid-session is done with /model. The model field sets the starting model only.

hooks

Type: object. Default: {}.

An object with four arrays, one per lifecycle event. Each array contains hook definitions with matcher and command fields. The matcher is a regex against the tool name; the command is a shell command that runs with the tool input piped to stdin.

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Bash", "command": ".claude/hooks/block-dangerous.sh" }
    ],
    "PostToolUse": [
      { "matcher": "Edit|Write", "command": "pnpm lint --fix" }
    ],
    "Notification": [],
    "Stop": [
      { "command": ".claude/hooks/log-cost.sh" }
    ]
  }
}

See the hooks reference page for exit-code behavior and full examples.

mcpServers

Type: object. Default: {}.

An object whose keys are server names and whose values describe how to launch the MCP server. Two common shapes: stdio (subprocess) and HTTP (remote URL).

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/projects"]
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_TOKEN" }
    },
    "remote-api": {
      "url": "https://mcp.example.com/sse",
      "headers": { "Authorization": "Bearer $API_KEY" }
    }
  }
}

Environment variable references in the form $VAR_NAME are expanded at session start against your shell environment. See the MCP servers page for more.

permissions

Type: object. Default: allow all except dangerous patterns.

Two arrays control tool access:

  • allowedTools: tools that never prompt for approval.
  • deniedTools: tools that are blocked outright.

Patterns use tool-name prefixes and wildcards:

{
  "permissions": {
    "allowedTools": [
      "Read",
      "Grep",
      "Glob",
      "Bash(git status)",
      "Bash(git diff)",
      "Bash(pnpm test*)"
    ],
    "deniedTools": [
      "Bash(rm -rf *)",
      "Bash(git push --force*)"
    ]
  }
}

Bash(git status) matches the exact command git status. Bash(pnpm test*) matches any command starting with pnpm test. More specific patterns win.

env

Type: object. Default: {}.

Environment variables injected into every shell command Claude Code runs. Use this to set paths, flags, or feature toggles that should apply to every Bash tool call.

{
  "env": {
    "NODE_ENV": "development",
    "CLAUDE_PROJECT_ROOT": "$PWD",
    "NEXT_TELEMETRY_DISABLED": "1"
  }
}

Do not put secrets here. The file is committed. Use .env.local or apiKeyHelper for secrets.

apiKeyHelper

Type: string. Default: none.

A shell command that prints an API key to stdout. Claude Code runs it at session start and uses the output as the active Anthropic key. Useful for keys stored in a vault or rotated on a schedule.

{
  "apiKeyHelper": "op read op://Private/anthropic-api/credential"
}

The example above pulls the key from 1Password via its CLI. Any command that prints the key on stdout works: aws secretsmanager get-secret-value ..., a custom Python script, pass show anthropic-key, and so on.

Complete working settings.json

Here is a full project .claude/settings.json used on a production Next.js repo:

{
  "model": "claude-sonnet-4-5",
  "env": {
    "NODE_ENV": "development",
    "NEXT_TELEMETRY_DISABLED": "1"
  },
  "permissions": {
    "allowedTools": [
      "Read",
      "Grep",
      "Glob",
      "Bash(git status)",
      "Bash(git diff*)",
      "Bash(git log*)",
      "Bash(pnpm test*)",
      "Bash(pnpm lint*)",
      "Bash(pnpm typecheck)"
    ],
    "deniedTools": [
      "Bash(rm -rf *)",
      "Bash(git push --force*)",
      "Bash(npm publish*)"
    ]
  },
  "hooks": {
    "PreToolUse": [
      { "matcher": "Bash", "command": ".claude/hooks/pre-bash.sh" }
    ],
    "PostToolUse": [
      { "matcher": "Edit|Write", "command": ".claude/hooks/post-edit-lint.sh" }
    ],
    "Stop": [
      { "command": ".claude/hooks/log-session.sh" }
    ]
  },
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
    }
  }
}

This example keeps read-only bash operations frictionless, blocks the three most common foot-guns, runs lint after every edit, and logs session cost on exit.

Override order

When multiple settings.json files exist, Claude Code merges them in this order (later overrides earlier):

  1. ~/.claude/settings.json
  2. .claude/settings.json in the repo root
  3. .claude/settings.local.json in the repo root

Arrays merge by concatenation; objects merge field-by-field. If you want to reset an inherited array in a project settings.json, start it empty.

Secrets in settings.json

Three patterns work safely:

  1. Reference environment variables with $VAR_NAME. Claude Code expands them against your shell environment at session start.
  2. Use apiKeyHelper with a password manager CLI.
  3. Put real secrets in .claude/settings.local.json, which is git-ignored.

Never hardcode a key. Anyone cloning the repo gets your key otherwise.

settings.json vs CLAUDE.md

The split is simple:

  • settings.json: how the tooling behaves (model, hooks, MCP, permissions).
  • CLAUDE.md: what rules Claude should follow when writing code (conventions, test commands, banned patterns).

If you find yourself writing English sentences in settings.json, move them to CLAUDE.md. If you find yourself putting shell commands in CLAUDE.md, move them to settings.json as hooks.

Validating the file

Before committing, validate the JSON:

jq empty .claude/settings.json

Claude Code shows a warning at startup if settings.json has a syntax error and falls back to defaults. Check /status to confirm the file is loaded and has the values you expect.

Common configuration patterns

Here are five settings.json recipes drawn from real projects.

Recipe 1: read-only exploration

Allow Claude to explore but block any writes. Useful when reviewing an unfamiliar repo.

{
  "permissions": {
    "allowedTools": ["Read", "Grep", "Glob", "Bash(git log*)", "Bash(git diff*)"],
    "deniedTools": ["Write", "Edit", "Bash(rm*)", "Bash(git commit*)", "Bash(git push*)"]
  }
}

Recipe 2: CI-friendly auto-accept

For running Claude Code in GitHub Actions with a narrow toolset, combined with hooks to catch failures.

{
  "model": "claude-sonnet-4-5",
  "permissions": {
    "allowedTools": ["Read", "Write", "Edit", "Grep", "Glob", "Bash(pnpm *)", "Bash(git *)"]
  },
  "hooks": {
    "PostToolUse": [
      { "matcher": "Edit|Write", "command": "pnpm lint --fix && pnpm typecheck" }
    ]
  }
}

Recipe 3: Opus for planning, Sonnet for execution

Run Opus for the initial plan and switch to Sonnet after. The /model command handles the switch mid-session, but set the default to Opus so planning starts rich.

{ "model": "claude-opus-4-5" }

Followed by /model claude-sonnet-4-5 once the plan is approved. Opus tokens cost roughly 5x Sonnet tokens; keeping Sonnet for long execution phases cuts cost about 70%.

Recipe 4: multi-MCP productivity setup

Attach filesystem, GitHub, and Postgres MCP servers for a data-engineering workflow.

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_TOKEN" }
    },
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres", "$DATABASE_URL"]
    }
  }
}

Recipe 5: hardened secrets via apiKeyHelper

No API key in plain env. Fetch from a password manager on every session start.

{
  "apiKeyHelper": "op read op://Dev/anthropic-api/credential"
}

This is safer than exporting ANTHROPIC_API_KEY because the key never sits in shell history or a .env file.

Field precedence cheat sheet

When three files disagree on a value, Claude Code picks the most specific:

  • .claude/settings.local.json beats
  • .claude/settings.json beats
  • ~/.claude/settings.json

For arrays like permissions.allowedTools, all three are concatenated and deduped. For scalars like model, the most specific wins outright. For objects like mcpServers, keys merge; a key present in a more specific file replaces the same key from a less specific file.

Debugging a broken settings.json

If a hook is not firing, an MCP server is missing, or permissions feel off, work through this checklist:

  1. Run /status inside a session. It lists every settings file loaded and the effective values.
  2. Run jq empty .claude/settings.json to confirm valid JSON.
  3. Check environment variable expansion. $DATABASE_URL only works if DATABASE_URL is set in the shell where you launched Claude Code.
  4. Check file paths in hooks. Relative paths are relative to the CWD, not the settings.json file.
  5. For MCP server failures, try running the command plus args manually in a terminal. The MCP server should print a protocol handshake on stdout.

Keep settings.json small

As with CLAUDE.md, smaller is better. Every hook adds latency to tool calls. Every MCP server adds startup time to sessions. Ten well-chosen permissions beat 50 permissive ones. Review settings.json every few months and prune entries you no longer use.

Versioning settings.json

Commit settings.json. Put .claude/settings.local.json in .gitignore. When a teammate pulls the repo, they get the agreed-on model, hooks, and MCP servers without any extra setup. A one-line change to settings.json that affects every future session is reviewed in a PR like any other code change, which is exactly what you want for something that can run shell commands on everyone's machine.

Short checklist before merging a settings.json change:

  • Does any new hook command reference a file that might not exist on every teammate's checkout?
  • Does any env value leak an absolute path tied to your machine?
  • Does any allowedTools entry open up more than it should?

Skip the change if any of those are yes.

Migrating from older versions

Older Claude Code builds used separate files for hooks and MCP. If you are upgrading from a version before 1.0, move entries:

  1. Hooks from .claude/hooks.json into settings.json under the hooks key.
  2. MCP config from .mcp.json into settings.json under the mcpServers key.
  3. Delete the old files after confirming /status shows the values in the new location.

Claude Code will keep reading the old files for one release cycle, then ignore them. Running /doctor highlights any file the new version no longer reads.

Frequently asked questions

Where is the default location of settings.json?

Global settings live at `~/.claude/settings.json` and project settings live at `.claude/settings.json` in the repo root. Project values override global values field by field.

How do I validate my settings.json JSON?

Run `jq empty .claude/settings.json`. Claude Code also shows a warning at startup if the file has a syntax error and falls back to defaults. Run `/status` inside a session to confirm what was loaded.

Can I have per-branch settings.json?

Yes, indirectly. Commit different settings.json to different branches and Claude Code reads whichever branch is checked out. For user-specific non-committed overrides, use `.claude/settings.local.json`.

What happens if settings.json has a syntax error?

Claude Code prints a warning at session start and uses defaults. Fix the JSON and either restart the session or run `/status` after the fix to reload.

How do I change the default model for every session?

Set the `model` field in settings.json to the desired model ID, for example `claude-sonnet-4-5`, `claude-opus-4-5`, or `claude-haiku-4-5`. Mid-session overrides with `/model` take effect only for that session.

How do I configure MCP servers in settings.json?

Add an `mcpServers` object with one entry per server. Each entry needs a `command` and `args` array for stdio transport, or a `url` for HTTP transport. Reference env vars with `$VAR_NAME` syntax.