Settings JSON Design — Permissions, Environment Variables, and Hook Configuration
About 5 minutes
- Understanding the role of the `.claude/` folder
- Ability to read and write basic JSON syntax
Claude Code behavior can be controlled in detail via .claude/settings.json. By correctly configuring permissions, environment variables, and hooks, you can safely operate a harness as a team. This page explains the structure of the settings file and practical design guidelines in order.
How It Works
Section titled “How It Works”The Difference Between settings.json and settings.local.json
Section titled “The Difference Between settings.json and settings.local.json”Claude Code settings are managed in two files.
| File | Purpose | Git Management |
|---|---|---|
.claude/settings.json | Settings shared with the team | Commit it |
.claude/settings.local.json | Personal settings and overrides | Exclude with .gitignore |
settings.json is committed to the repository so all team members operate with the same permissions and hook settings. On the other hand, settings.local.json is a place for personal API keys and experimental settings, and is not included in Git.
Main Configuration Items
Section titled “Main Configuration Items”settings.json has the following structure.
{
"permissions": {
"allow": [
"Bash(npm run dev)",
"Bash(npm run harness:*)",
"Read",
"Write",
"Edit"
],
"deny": [
"Bash(npm run build)"
]
},
"env": {
"NODE_ENV": "development"
},
"hooks": {
"PreToolUse": [],
"PostToolUse": [],
"Stop": []
},
"mcpServers": {
"github": {}
}
}The role of each section is as follows.
permissions: Allow list (allow) and deny list (deny) of tools and commands Claude can executeenv: Environment variables that become active during Claude’s sessionhooks: Scripts that run before and after tool execution, and at stopmcpServers: Connection settings for Model Context Protocol (MCP) servers
The allow/deny Pattern for permissions
Section titled “The allow/deny Pattern for permissions”Patterns can be written in three formats.
| Format | Example | Meaning |
|---|---|---|
| Tool name only | "Read" | Allow the entire Read tool |
| Command specification | "Bash(npm run dev)" | Allow only a specific command |
| Glob pattern | "Bash(npm run harness:*)" | Allow all commands starting with harness: |
Commands registered in allow are executed automatically without displaying a permission prompt (confirmation dialog) when Claude runs them. Commands registered in deny are always blocked even if Claude tries to execute them.
Best Practices
Section titled “Best Practices”Follow the Principle of Least Privilege
Section titled “Follow the Principle of Least Privilege”Keep the allow list limited to “only what is necessary.” Allowing all of "Bash" means Claude can execute any shell command. By listing only the specific commands used in the project, you can prevent unintended operations.
// Setting to avoid: allows all of Bash
"allow": ["Bash", "Read", "Write"]
// Recommended: explicitly specify only the commands used
"allow": ["Bash(npm run dev)", "Bash(npm run harness:*)", "Read", "Write", "Edit"]Always Include npm run build in deny
Section titled “Always Include npm run build in deny”If npm run build is linked to a hosting pipeline, it can trigger a production deployment. By explicitly registering it in deny, you prevent Claude from accidentally running it.
"deny": ["Bash(npm run build)"]Add settings.local.json to .gitignore
Section titled “Add settings.local.json to .gitignore”To prevent personal ANTHROPIC_API_KEY or experimental settings from being mixed into settings.json, add the following to the project’s .gitignore:
.claude/settings.local.jsonConsolidate Environment Variables in the env Section
Section titled “Consolidate Environment Variables in the env Section”Instead of hardcoding environment variable values in code or configuration files, manage them centrally in the env section.
"env": {
"NODE_ENV": "development",
"LOG_LEVEL": "info"
}Hands-on Exercise
Section titled “Hands-on Exercise”Step 1: Check the Current settings.json
Section titled “Step 1: Check the Current settings.json”Run the following command from the project root to check the current settings.
cat .claude/settings.jsonRead what is registered in allow and deny under permissions.
✅ Verify: Confirm that "Bash(npm run build)" is included in deny. If it is not, add it in the next step.
Step 2: Add npm run harness:check to allow
Section titled “Step 2: Add npm run harness:check to allow”If "Bash(npm run harness:check)" is not in allow, add it. Add the following line to the permissions.allow array in settings.json.
"allow": [
"Bash(npm run dev)",
"Bash(npm run harness:check)",
"Bash(npm run harness:*)",
"Read",
"Write",
"Edit"
]✅ Verify: Confirm there are no JSON syntax errors. Check that there is no trailing comma on the last element of the array.
Step 3: Create settings.local.json
Section titled “Step 3: Create settings.local.json”Create a personal settings file in the project root. Do not write actual API keys; use placeholders to check the structure.
touch .claude/settings.local.jsonUse the following structure as a reference for the file contents.
{
"env": {
"ANTHROPIC_API_KEY": "YOUR_API_KEY_HERE"
}
}Then check whether the exclusion setting has been added to .gitignore.
grep "settings.local.json" .gitignore✅ Verify: Confirm that .claude/settings.local.json is listed in .gitignore. If not, add it.
Step 4: Verify Settings Are Applied
Section titled “Step 4: Verify Settings Are Applied”After changing settings.json, restart Claude Code to apply the settings. Exit Claude Code once, then restart it and run the following command.
npm run harness:check✅ Verify: Confirm that Claude can run npm run harness:check without a permission prompt.
Summary
Section titled “Summary”This page covered the following.
- Manage settings split between
settings.jsonfor shared team use andsettings.local.jsonfor personal use - Specify allow concretely with only the commands you use, and always include
npm run buildin deny - Consolidate environment variables in the
envsection, and write actual values insettings.local.json - Exclude
settings.local.jsonfrom Git management with.gitignore
Next Steps
Section titled “Next Steps”- Rules File Design — Writing Safety Rules Declaratively — How to declaratively manage “rules” that cannot be controlled by settings
- CLAUDE.md Design — Deep dive into context design for Claude
Frequently Asked Questions
Section titled “Frequently Asked Questions”Q: Can’t commands that are not in allow be executed?
A: They can be executed, but a permission prompt will be displayed every time Claude tries to run them. allow is a list of commands to execute automatically without displaying a prompt. Commands registered in deny are always blocked without a prompt.
Q: What happens if settings.local.json does not exist?
A: Only the settings in settings.json are applied. settings.local.json is an optional file.
Q: How flexible can glob patterns be?
A: * matches any string that does not include a path separator. "Bash(npm run harness:*)" matches npm run harness:check and npm run harness:sync. However, avoid overly broad patterns like "Bash(*)".
Q: What can be done with the hooks section?
A: PreToolUse runs an arbitrary script just before tool execution, PostToolUse just after execution, and Stop at session end. For example, it can be used to automatically run a formatter after a file is updated.
See the references for the external specifications and background sources used on this page.[1][2]
References
Section titled “References”- Anthropic, Claude Code documentation
- Anthropic, Claude API documentation