Skip to content
LinkedInX

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

About 5 minutes

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.


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.

FilePurposeGit Management
.claude/settings.jsonSettings shared with the teamCommit it
.claude/settings.local.jsonPersonal settings and overridesExclude 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.

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

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:

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.


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"]

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)"]

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

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"
}

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

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.


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.


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.


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

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]

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