3.1 Claude Code — Permission Modes
Claude Code operates in one of six permission modes. The mode controls what actions Claude can take without asking for your confirmation:
| Mode | What Claude can do autonomously | Typical use |
|---|---|---|
| default | Read files; asks before writing or running commands | Day-to-day interactive use |
| acceptEdits | Read + write files; asks before running commands | Sessions where you're comfortable with file edits |
| plan | No file writes or commands; produces a plan only | Design sessions, review before committing |
| auto | Read, write, and run most commands | Trusted automated tasks |
| dontAsk | Everything except bypassing safety | Fully automated pipelines, CI with human oversight |
| bypassPermissions | Bypasses all permission checks | CI/CD systems with full automation; no human in loop |
bypassPermissions should only be used in locked-down CI/CD environments where the task is fully defined and no external content can modify Claude's behaviour. Never use it interactively.
Choosing a mode. The principle is least privilege: give Claude the minimum permissions it needs for the current task. Use plan mode first for unfamiliar tasks — review the plan, then switch to auto for execution.
3.2 Configuration Hierarchy
Claude Code has four configuration layers, applied in order (highest wins):
Enterprise policy (locked)
└── User global settings (~/.claude/settings.json)
└── Project settings (.claude/settings.json)
└── Local session overrides (--flag arguments)
What belongs in each layer:
- Enterprise policy: Enforced organisation-wide constraints — approved model IDs, blocked tool categories, compliance settings. Set by admin, cannot be overridden.
- User global settings: Personal preferences — default model, preferred permission mode, API key location.
- Project settings: Project-specific configuration — CLAUDE.md path, project-level MCP servers, hook configurations. Committed to the repo.
- Local session overrides: One-off flags (
--model,--permission-mode) that apply to the current terminal session only.
3.3 CLAUDE.md — Durable Project Context
CLAUDE.md is a Markdown file at the project root (or in .claude/) that provides persistent context read at every Claude Code session start. It is the authoritative source for:
- Project architecture and conventions
- Commands to run (build, test, lint)
- Code style rules Claude must follow
- Files or directories Claude should never modify
- APIs and external services in use
Structure for a CLAUDE.md:
# CLAUDE.md
## Project
E-commerce backend — Node.js + TypeScript + PostgreSQL. REST API, no GraphQL.
## Commands
- Build: npm run build
- Test: npm test (runs Vitest, must pass before any commit)
- Lint: npm run lint (ESLint + Prettier — fix lint errors, never suppress)
## Architecture
- src/routes/ — Express route handlers (thin; delegate to services)
- src/services/ — Business logic (no direct DB calls; use repositories)
- src/repositories/ — All database access (pg pool, never raw query strings)
- src/lib/ — Shared utilities
## Conventions
- All DB queries use parameterised queries — no string interpolation
- Errors bubble up as AppError instances (src/lib/errors.ts)
- Tests use integration test DB (TEST_DATABASE_URL env var)
## Do not modify
- src/lib/auth.ts — auth logic, requires security review
- migrations/ — migration files are append-only
A well-maintained CLAUDE.md removes the need to re-explain project conventions in every session. It is the most impactful single file for teams adopting Claude Code.
3.4 Rules and Instruction Files
Beyond CLAUDE.md, Claude Code supports scoped instruction files in a .claude/rules/ directory. These apply per-subdirectory or per-file-type:
.claude/
rules/
frontend.md # applies to src/components/**, src/pages/**
api.md # applies to api/**, src/routes/**
security.md # applies globally (sensitive patterns)
This lets you give Claude different instructions for different parts of the codebase — e.g., strict security review rules for the authentication module, different style conventions for the frontend vs backend.
3.5 Hooks
Hooks are shell commands or scripts that Claude Code executes at defined lifecycle points. They give you programmatic control over Claude's actions:
| Hook type | When it fires | Common use |
|---|---|---|
| PreToolUse | Before Claude executes a tool | Block dangerous commands, log actions, validate parameters |
| PostToolUse | After a tool completes | Capture output, trigger downstream processes |
| Notification | When Claude emits a notification | Send alerts to Slack/PagerDuty |
| Stop | When the session ends | Cleanup, summary reports |
Example: Block rm -rf commands via PreToolUse hook.
In .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python .claude/hooks/check_dangerous_commands.py"
}
]
}
]
}
}
The hook script receives the tool call details on stdin as JSON. If it exits with code 2, the tool call is blocked:
import json, sys
data = json.load(sys.stdin)
command = data.get("tool_input", {}).get("command", "")
dangerous = ["rm -rf", "DROP TABLE", "> /dev/sda"]
if any(d in command for d in dangerous):
print(f"BLOCKED: dangerous pattern detected in command")
sys.exit(2) # non-zero exit blocks the tool call
sys.exit(0) # allow
Hooks run with the same permissions as the Claude Code process. A hook that makes an outbound network call, modifies files, or reads secrets is executing with full process permissions. Audit hook scripts carefully.
3.6 Subagents
Subagents are Claude Code instances spawned by a parent Claude Code session to handle parallel or delegated work. The parent orchestrates; subagents execute.
Key security rule for subagents. Always run subagents in a sandboxed environment with least privilege. A subagent reading a file that contains attacker-controlled content could be manipulated through prompt injection embedded in that file. The sandbox limits the blast radius.
# Spawning a subagent with restricted file access
subagent = claude.spawn_agent(
task="Analyse the security vulnerabilities in src/api/",
permissions={
"read": ["src/api/**"],
"write": [], # no writes
"execute": [] # no command execution
}
)
Trust hierarchy. Parent agent receives verified instructions from the user. Subagent receives instructions from the parent — these should be treated as trusted but still validated. Tool results returned to the subagent from external systems are untrusted.
3.7 Skills and Plugins
Skills are reusable instruction sets packaged as Markdown files that can be invoked by name in any Claude Code session. They define a specific workflow (e.g., "run a security review", "generate a PR description"):
# .claude/skills/pr-review.md
## When invoked
User wants a pull request description for their changes.
## Steps
1. Run `git diff main` to see changed files
2. Summarise the purpose of changes in ≤3 bullets
3. List testing approach
4. Write a PR body in the standard template (see .claude/templates/pr.md)
Invoke with: /pr-review in the Claude Code session.
Plugins are packaged tools with code — an MCP server bundled as an npm/pip package. Install a plugin to add tools (file analysis, code indexing, API connectors) to any Claude Code session without writing custom integration code.
| Skills | Plugins | |
|---|---|---|
| Contains | Markdown instructions | Code (MCP server) |
| Adds | Workflow patterns | New tool capabilities |
| Distribution | .claude/ directory or shared repo |
npm/pip package |
| Example | Code review checklist | GitHub connector, database browser |
3.8 MCP — Model Context Protocol
MCP is a standard protocol for exposing tools, data, and prompts to Claude. It separates the concern of "what tools exist" from "who is using them" — build one MCP server and any MCP client (Claude Code, Claude Desktop, your own agent) can use it.
Three primitives.
| Primitive | What it does | Direction |
|---|---|---|
| Tools | Execute actions, return results | Client calls → Server executes → returns result |
| Resources | Provide read-only data (files, docs, schemas) | Client requests → Server returns content |
| Prompts | Parameterised prompt templates | Client requests → Server returns prompt text |
Most MCP servers expose primarily tools. Resources are used for document stores and structured reference data. Prompts are used for reusable prompt patterns.
Minimal MCP server example (TypeScript):
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server({
name: "my-tools",
version: "1.0.0"
});
server.setRequestHandler("tools/list", async () => ({
tools: [{
name: "get_issue",
description: "Retrieve a GitHub issue by number. Returns title, body, labels, and status.",
inputSchema: {
type: "object",
properties: {
issue_number: { type: "number", description: "The issue number" }
},
required: ["issue_number"]
}
}]
}));
server.setRequestHandler("tools/call", async (request) => {
if (request.params.name === "get_issue") {
const issue = await fetchGitHubIssue(request.params.arguments.issue_number);
return { content: [{ type: "text", text: JSON.stringify(issue) }] };
}
});
const transport = new StdioServerTransport();
await server.connect(transport);
3.9 MCP Transport Options
| Transport | Description | When to use |
|---|---|---|
| stdio | Server communicates over stdin/stdout | Local tools, CLI integration, CI/CD, process-local servers |
| HTTP (Streamable) | Server exposes an HTTP endpoint with streaming | Remote shared servers, multi-client deployments |
| SSE (legacy) | Server-sent events over HTTP | Legacy compatibility only; prefer Streamable HTTP for new servers |
For local-only tools in CI, stdio is the right choice: simplest setup, no port conflicts, no authentication needed.
For shared servers (e.g., a company-wide GitHub MCP server accessed by all developers), HTTP with authentication is required. The server handles the OAuth flow or API key validation; the client never sees the credentials.
3.10 MCP Scope and Configuration
MCP servers can be configured at three scopes:
| Scope | Where configured | Applies to |
|---|---|---|
| User | ~/.claude/mcp_servers.json |
All sessions for this user |
| Project | .claude/mcp_servers.json |
All users of this project (committed to repo) |
| Server-managed | Returned by server at connect time | Dynamic configuration per session |
Project-scoped MCP configuration (committed to repo):
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@github/mcp-server"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
}
},
"postgres": {
"command": "uvx",
"args": ["mcp-server-postgres", "${DATABASE_URL}"]
}
}
}
Environment variables use the ${VAR} syntax and are resolved from the current shell environment — secrets stay out of the committed config file.
3.11 Enterprise Integration Patterns
Authentication for protected APIs. The MCP server handles auth; Claude never receives the underlying credential:
Claude Code → MCP server (holds OAuth token) → Protected internal API
This is the correct pattern. Claude sees only the tool results, not the API credentials. The MCP server is responsible for refreshing tokens and handling auth failures.
Code modernisation use case. One of the highest-ROI enterprise uses of Claude Code is modernising legacy codebases:
- Configure CLAUDE.md with target coding standards (e.g., "migrate Spring Boot 2.x to 3.x")
- Set up MCP server with read access to the codebase index
- Use
planmode first — review the migration plan before applying - Run in
automode per-module — humans review each module's diff before merging
Code review with calibrated trust. AI-generated code should receive the same scrutiny as code from an unfamiliar contributor:
- Read every changed line — don't approve based on "Claude wrote it"
- Run the full test suite — AI code can pass type-checking but fail integration tests
- Verify security-sensitive paths manually — injection, auth, data validation
- Check for logic correctness on edge cases — AI code often handles happy paths well but misses boundary conditions
Calibrated trust means neither blind trust nor blanket skepticism. Apply the same bar you'd apply to any code submitted by someone who is smart but unfamiliar with your specific system's requirements.
M3 Checkpoint
bypassPermissions= CI/CD only — never interactive- CLAUDE.md is read at every session start — it's the highest-leverage file for team adoption
- Hooks exit code 2 = block the tool call — exit 0 = allow
- Subagents must run in sandboxes — attacker-controlled file content can inject instructions
- MCP tools = actions; resources = read-only data; prompts = templates
- stdio for local/CI; HTTP for shared remote servers
- MCP server handles auth — Claude never sees the underlying credentials