⚙️Claude CodeLesson 3.4

Settings & Permission Management

Configuring settings.json, allowed tools, and MCP servers.

20 min

Learning Objectives

  • Configure settings.json and settings.local.json
  • Manage tool permissions and trust levels
  • Add MCP servers to Claude Code

Settings & Permission Management

Claude Code's behavior is configured through a layered settings system that controls everything from tool permissions to MCP server connections. The settings.jsonfile is the central configuration mechanism, and understanding its structure and precedence rules is essential for the CCA-F exam.

Settings.json Overview

Claude Code uses JSON configuration files at multiple levels to control permissions, behavior, and integrations. Like CLAUDE.md, settings follow a hierarchy:

Settings File Locations

  • User settings: ~/.claude/settings.json — personal settings that apply to all projects.
  • Project settings: .claude/settings.json — project-specific settings committed to version control.
  • Local project settings: .claude/settings.local.json — personal overrides for a specific project (not committed to version control).
# Settings file hierarchy (least to most specific):
~/.claude/settings.json              # User (all projects)
.claude/settings.json                # Project (shared via git)
.claude/settings.local.json          # Local project (personal, gitignored)
Exam Tip: Know the three settings levels and their purposes.settings.json in the project is committed and shared;settings.local.json is personal and gitignored. User-level settings apply globally. More specific settings override less specific ones.

Tool Permission Configuration

The most important section of settings.json is tool permissions. This controls which tools Claude Code can use without asking for approval:

{
  "permissions": {
    "allow": [
      "Edit",
      "Write",
      "Bash(npm test)",
      "Bash(npm run build)",
      "Bash(npm run lint)",
      "Bash(npx tsc --noEmit)",
      "Bash(git status)",
      "Bash(git diff)",
      "Bash(git log)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(git push --force)",
      "Bash(npm publish)"
    ]
  }
}

Permission Patterns

Permissions support several matching patterns:

  • Tool name only: "Edit" — allows all uses of the Edit tool.
  • Tool with argument: "Bash(npm test)" — allows only this specific bash command.
  • Tool with prefix match: "Bash(npm run *)" — allows any npm run command.
  • MCP tool: "mcp__servername__toolname" — allows a specific MCP tool.
{
  "permissions": {
    "allow": [
      "Edit",
      "Write",
      "Bash(npm *)",
      "Bash(npx *)",
      "Bash(git status)",
      "Bash(git diff *)",
      "Bash(git log *)",
      "Bash(git add *)",
      "Bash(git commit *)",
      "mcp__postgres__query"
    ],
    "deny": [
      "Bash(git push *)",
      "Bash(rm -rf *)",
      "Bash(curl *)",
      "mcp__postgres__drop_table"
    ]
  }
}
Exam Tip: The deny list takes precedence overallow. If a tool matches both an allow rule and a deny rule, it is denied. This is a critical security principle tested on the exam.

Permission Precedence

When evaluating whether a tool call is permitted, Claude Code checks in this order:

  1. Check the deny list — if the tool matches any deny rule, it is blocked.
  2. Check the allow list — if the tool matches an allow rule, it is permitted.
  3. If no rule matches, prompt the user for approval.

The deny-first evaluation means you can use a broad allow pattern and then carve out specific exceptions:

{
  "permissions": {
    "allow": [
      "Bash(git *)"       
    ],
    "deny": [
      "Bash(git push --force *)",
      "Bash(git reset --hard *)",
      "Bash(git clean *)"          
    ]
  }
}

MCP Server Configuration

MCP (Model Context Protocol) servers extend Claude Code's capabilities by connecting it to external tools and services. MCP servers are configured in settings.json:

{
  "mcpServers": {
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres"],
      "env": {
        "DATABASE_URL": "postgresql://localhost:5432/mydb"
      }
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_TOKEN": "${GITHUB_TOKEN}"
      }
    },
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/path/to/allowed/directory"
      ]
    }
  }
}

MCP Server Configuration Fields

  • command: The executable to run (e.g., npx, node,python).
  • args: Arguments passed to the command. For npx-based servers, this typically includes -y (auto-confirm) and the package name.
  • env: Environment variables passed to the MCP server process. Supports variable expansion with ${VAR_NAME} syntax to reference environment variables from your shell.
Exam Tip: MCP servers in Claude Code settings use the same MCP protocol discussed in Domain 2 (Model Context Protocol). The exam will test whether you know how to configure MCP servers via settings.json, including the command, args, and env fields. Know that MCP tools appear with the mcp__servername__toolname format in permission rules.

Environment Variable Handling

For security, sensitive values like API keys and tokens should not be hardcoded in settings.json. Instead, use environment variable references:

{
  "mcpServers": {
    "slack": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-slack"],
      "env": {
        "SLACK_BOT_TOKEN": "${SLACK_BOT_TOKEN}",
        "SLACK_TEAM_ID": "${SLACK_TEAM_ID}"
      }
    }
  }
}

The ${VAR_NAME} syntax reads the value from your shell environment at runtime. This means the actual token is never stored in the settings file and never committed to version control.

Project Settings for Teams

A well-configured project settings file establishes shared permissions and tool access for the entire team:

// .claude/settings.json (committed to git)
{
  "permissions": {
    "allow": [
      "Edit",
      "Write",
      "Bash(npm test *)",
      "Bash(npm run *)",
      "Bash(npx tsc *)",
      "Bash(npx prisma *)",
      "Bash(git status)",
      "Bash(git diff *)",
      "Bash(git log *)",
      "Bash(git add *)",
      "Bash(git commit *)"
    ],
    "deny": [
      "Bash(git push --force *)",
      "Bash(rm -rf *)",
      "Bash(npm publish *)",
      "Bash(npx prisma migrate reset *)"
    ]
  },
  "mcpServers": {
    "project-db": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres"],
      "env": {
        "DATABASE_URL": "${DATABASE_URL}"
      }
    }
  }
}
// .claude/settings.local.json (personal, gitignored)
{
  "permissions": {
    "allow": [
      "Bash(git push *)"
    ]
  },
  "mcpServers": {
    "my-notes": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/notes"]
    }
  }
}

Hooks Configuration

Claude Code supports hooks — custom scripts that run before or after specific tool invocations. Hooks are configured in settings.json and provide a way to enforce policies or trigger side effects:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "echo "Running bash command validation""
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write $CLAUDE_FILE_PATH"
          }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "terminal-notifier -message "$CLAUDE_NOTIFICATION""
          }
        ]
      }
    ]
  }
}

Hook Types

  • PreToolUse: Runs before a tool is executed. Can be used for validation or logging. If the hook exits with a non-zero status, the tool call is blocked.
  • PostToolUse: Runs after a tool completes. Useful for formatting, logging, or triggering follow-up actions.
  • Notification: Runs when Claude Code emits a notification (e.g., when waiting for user input).
Exam Tip: Hooks run at the system level and are not controlled by Claude itself. PreToolUse hooks can block tool execution by returning a non-zero exit code. This is a key mechanism for enforcing policies beyond what permission rules alone can do.

Complete Settings Reference

Here is a comprehensive settings.json showing all major configuration sections:

{
  "permissions": {
    "allow": [],
    "deny": []
  },
  "mcpServers": {},
  "hooks": {
    "PreToolUse": [],
    "PostToolUse": [],
    "Notification": []
  }
}
Key Takeaway: Claude Code is configured through a three-level settings hierarchy: user (~/.claude/settings.json), project (.claude/settings.json), and local (.claude/settings.local.json). The permission system uses allow/deny lists where deny takes precedence. MCP servers are configured with command, args, and env fields. Hooks (PreToolUse, PostToolUse, Notification) provide system-level automation around tool execution. Project settings are committed for team sharing; local settings are personal and gitignored.

Exam-Relevant Concepts Summary

  • Three settings levels: user, project (committed), local project (gitignored).
  • Permission rules: deny takes precedence over allow; unmatched tools prompt the user.
  • Bash permissions support pattern matching: "Bash(npm *)".
  • MCP tools use mcp__servername__toolname format in permission rules.
  • Environment variables use ${VAR_NAME} syntax for secure secret handling.
  • Hooks: PreToolUse (can block), PostToolUse (post-processing), Notification.
  • Project settings.json is committed to git; settings.local.json is not.