Skip to content
LinkedInX

Settings JSON Design — Permissions, Environment Variables, and Hook Configuration

Target audience: Those who understand Claude Code basic operations (Level 3–4 equivalent) and want to build a harness that can be shared with a team.
Prerequisites:
  • 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

The Difference Between settings.json and settings.local.json

Claude Code settings are managed in two files.

FilePurposeGit Management
.claude/settings.jsonSettings shared with the teamCommit it
.claude/settings.local.jsonPersonal settings and overridesExclude with .gitignore
This table scrolls horizontally. Keyboard users can focus the table and use the left and right arrow keys.

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

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 execute
  • env: Environment variables that become active during Claude’s session
  • hooks: Scripts that run before and after tool execution, and at stop
  • mcpServers: Connection settings for Model Context Protocol (MCP) servers

The allow/deny Pattern for permissions

Patterns can be written in three formats.

FormatExampleMeaning
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:
This table scrolls horizontally. Keyboard users can focus the table and use the left and right arrow keys.

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

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

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

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.json

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

Step 1: Check the Current settings.json

Run the following command from the project root to check the current settings.

cat .claude/settings.json

Read 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

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

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.json

Use 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

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

This page covered the following.

  • Manage settings split between settings.json for shared team use and settings.local.json for personal use
  • Specify allow concretely with only the commands you use, and always include npm run build in deny
  • Consolidate environment variables in the env section, and write actual values in settings.local.json
  • Exclude settings.local.json from Git management with .gitignore

Next Steps


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

  1. Anthropic, Claude Code documentation
  2. Anthropic, Claude API documentation

For the latest releases and updates, check the official website and official documentation.

Quiz