Example settings files
Three realistic settings.json files, one personal, one for a team repository and one managed policy, with a note on every key so you can copy what you need.
Reading a list of keys only gets you so far. It is usually quicker to look at a complete file, delete what you do not want and change the values. This page gives three, one for each place a setting can live:
- a personal
~/.claude/settings.json - a team's
.claude/settings.json, committed to the repository - an organisation's
managed-settings.json
None of them is a recommended baseline. They are plausible files for the person who would own them. Each is shown first as valid JSON you can paste, followed by a key-by-key explanation. Claude Code rejects comments in settings files, so always copy the JSON block, not the notes. Types, defaults and allowed scopes for every key are in the settings reference, and settings files and precedence explains how the three files combine.
Your own settings
This is close to what I run on my own laptop. It picks a default model and effort, tweaks the terminal, adds a status line and pre-approves a couple of harmless actions. Anything not listed keeps its default, and because it lives in ~/.claude/settings.json it applies in every project.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"model": "claude-opus-5-5",
"modelSettings": {
"claude-opus-5-5": { "effortLevel": "medium" }
},
"editorMode": "vim",
"theme": "dark",
"statusLine": {
"type": "command",
"command": "jq -r '\"\\(.model.display_name) | ctx \\(.context_window.used_percentage // 0)%\"'"
},
"spinnerTipsEnabled": false,
"preferredNotifChannel": "terminal_bell",
"permissions": {
"allow": [
"Bash(git status)",
"Bash(git log *)",
"Read(~/.gitconfig)"
]
},
"autoUpdatesChannel": "stable",
"cleanupPeriodDays": 30
}
What each key is doing:
| Key | Effect here |
|---|---|
$schema | Gives editors autocomplete and validation. Optional, ignored by Claude Code |
model | Every new session starts on Opus 5.5 |
modelSettings | Per-model options. Here Opus runs at medium effort to keep everyday turns quick. /effort also saves a level per model, and --effort overrides it for one session |
editorMode | Vim-style editing in the prompt box |
theme | The dark colour theme. The daltonised themes (such as light-daltonized) are there if you need colour-blind-friendly palettes |
statusLine | Runs a command after each turn and prints its output under the prompt. This one shows the model name and context used; see status line |
spinnerTipsEnabled | Hides the rotating tips under the spinner |
preferredNotifChannel | Rings the terminal bell when a task finishes or a prompt is waiting |
permissions.allow | Lets Claude run git status, any git log, and read ~/.gitconfig without asking |
autoUpdatesChannel | Takes updates from the stable channel rather than the newest build |
cleanupPeriodDays | Deletes transcripts and other local session data older than 30 days |
A team's shared settings
This file sits at .claude/settings.json at the top of a repository and is committed, so everyone who clones it gets the same guardrails, the same pre-commit-style hook and the same plugin marketplace. It is modelled on a TypeScript monorepo I look after.
Before you commit something like it, know these five things:
- Cloud sessions use it too. A cloud session starts from a clone, so the committed file applies there.
- Telemetry does not belong here. Claude Code ignores the OpenTelemetry exporter variables in a repository's settings files, apart from values that switch telemetry off. Put them in managed settings or each person's user file (see monitoring usage).
- Allow rules wait for trust.
allowrules andextraKnownMarketplacesonly take effect once each person has trusted this exact folder, not merely a parent.denyandaskrules apply straight away, trusted or not. - The hook is a script in the repo. Commit
.claude/hooks/guard-migrations.shalongside this file. The hooks guide shows how to write one. - Rules match what Claude actually types.
Bash(git push *)does not catchgit -C . push.Read(./.env)stops the file tools and commands that name the file, such ascat .env, but notgrep -rover the directory. Enabling the sandbox closes that gap, because yourReaddeny paths are added to what sandboxed commands cannot read. See permissions.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(pnpm run *)",
"Bash(pnpm test *)",
"Bash(pnpm tsc *)"
],
"ask": [
"Bash(git push *)",
"Bash(pnpm publish *)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./infra/secrets/**)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/guard-migrations.sh"
}
]
}
]
},
"extraKnownMarketplaces": {
"northwind-tools": {
"source": {
"source": "github",
"repo": "northwind-dev/claude-plugins"
}
}
},
"enabledPlugins": {
"changelog-writer@northwind-tools": true
},
"sandbox": {
"enabled": true,
"filesystem": {
"allowWrite": ["/tmp/northwind-build"]
},
"network": {
"allowedDomains": [
"registry.npmjs.org",
"*.northwind.dev"
]
}
},
"plansDirectory": "./docs/plans"
}
What each key is doing:
| Key | Effect here |
|---|---|
permissions.allow | Any pnpm run, pnpm test or pnpm tsc command runs without a prompt |
permissions.ask | Pushing and publishing always need a human to confirm, even if someone has a broader allow rule |
permissions.deny | The file tools and file-reading commands cannot open .env files or anything under infra/secrets/ |
hooks.PreToolUse | Before every edit or write, a repo script runs and can block changes to applied database migrations. ${CLAUDE_PROJECT_DIR} keeps the path working from any subfolder |
extraKnownMarketplaces | Registers the team's plugin marketplace on every clone |
enabledPlugins | Switches on one plugin from that marketplace. A plugin from an external source still needs each person to install it once |
sandbox.enabled | Runs Bash commands inside the OS sandbox |
sandbox.filesystem.allowWrite | Adds a scratch build directory to the paths sandboxed commands may write |
sandbox.network.allowedDomains | Pre-approves npm and the company's own domains for sandboxed network access |
plansDirectory | Saves plan-mode plan files inside the repo so they can be reviewed in pull requests |
Read more in sandboxing and plugins.
An organisation's managed settings
A managed-settings.json that shows the shape of most of the managed-only keys, each with one plausible value. Treat it as a menu, not a policy: choose the keys that match your own requirements. Administrators deploy the same JSON as a file, through MDM, or as server-managed settings from the claude.ai console. One document applies to everyone it reaches; for different groups, deploy different files or profiles, because server-managed settings do not yet support per-group policy.
{
"forceLoginMethod": "claudeai",
"forceLoginOrgUUID": [
"00000000-0000-0000-0000-000000000000"
],
"availableModels": ["opus", "sonnet"],
"enforceAvailableModels": true,
"permissions": {
"deny": [
"Bash(curl *)",
"Bash(wget *)",
"Read(./.env)",
"Read(~/.aws/**)"
],
"disableBypassPermissionsMode": "disable"
},
"allowManagedPermissionRulesOnly": true,
"allowedMcpServers": [
{ "serverUrl": "https://mcp.linear.app/*" }
],
"allowManagedMcpServersOnly": true,
"strictKnownMarketplaces": [
{ "source": "github", "repo": "northwind-dev/approved-plugins" }
],
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"allowUnsandboxedCommands": false,
"network": {
"allowedDomains": ["registry.npmjs.org", "pypi.org", "github.com"],
"allowManagedDomainsOnly": true
}
},
"requiredMinimumVersion": "2.1.200",
"cleanupPeriodDays": 14,
"companyAnnouncements": [
"Claude Code is approved for client work. Read the AI usage policy on the intranet before your first session."
]
}
What each key is doing:
| Key | Effect here |
|---|---|
forceLoginMethod | Only claude.ai logins are allowed (no API key login) |
forceLoginOrgUUID | And only into this organisation. Replace the placeholder with your organisation's UUID |
availableModels | Sessions can use Opus and Sonnet only |
enforceAvailableModels | Makes the Default model option obey that list as well |
permissions.deny | Blocks curl and wget as Claude would normally write them, the project .env, and the user's AWS credentials folder, on every machine |
permissions.disableBypassPermissionsMode | Removes bypass-permissions mode entirely |
allowManagedPermissionRulesOnly | Ignores permission rules from user, project and local files, so only this file's rules apply |
allowedMcpServers | Only an MCP server at this URL may load. Matching by URL rather than name matters, because anyone can name a server anything. With only URL entries, every stdio server is blocked |
allowManagedMcpServersOnly | Makes this managed list the only MCP allowlist that counts |
strictKnownMarketplaces | Plugins can come from this one marketplace only |
sandbox.enabled | Every shell command runs sandboxed |
sandbox.failIfUnavailable | Claude Code refuses to start where the sandbox cannot be set up |
sandbox.allowUnsandboxedCommands | A command blocked by the sandbox cannot be retried outside it |
sandbox.network.allowManagedDomainsOnly | Users cannot add their own allowed domains on top of npm, PyPI and GitHub |
requiredMinimumVersion | Claude Code will not start on versions older than 2.1.200 |
cleanupPeriodDays | Local transcripts and session data are kept for two weeks |
companyAnnouncements | A message every user sees at startup |
Remember that a Bash(curl *) deny is not a hard boundary: /usr/bin/curl or sh -c 'curl ...' would slip past the rule. The sandbox's network allowlist is what actually enforces it, which is why this file pairs the two. Managed settings covers delivery, and admin setup walks through choosing what to enforce.