🔒

Protected Page

Enter the password to view the architecture docs.

Claude Code Architecture

A deep dive into how Anthropic's CLI agent is built — layers, data flow, and design decisions.

Single-package Bun-compiled · React Terminal UI · Async Generator Agent Loop

At a Glance

Key numbers and characteristics of the codebase

5
Architecture Layers
20+
Built-in Tools
60+
Custom Ink Files
300+
React Components
5
Permission Modes
6
MCP Transports
LAYER 1 Presentation REPL.tsx · Components · Custom Ink Fork · Yoga Layout LAYER 2 Orchestration query.ts (async generator) · QueryEngine.ts · Permission Gateway LAYER 3 Tool Execution toolOrchestration.ts · toolExecution.ts · Hooks Pipeline · Concurrency LAYER 4 Services claude.ts (LLM API) · MCP Client · Compaction · LSP · Analytics LAYER 5 Infrastructure bootstrap/state.ts · Auth · Config · Telemetry · Proxy · Settings

Layer Architecture

Click each layer to expand details. Each layer only depends on layers below it.

01
Presentation
Terminal UI built with a heavily forked Ink (React renderer for terminals). Handles rendering, input, selection, search, scrolling, and mouse events.

Stock Ink can't handle the interaction complexity needed — text selection, transcript search, mouse hit-testing, and virtual scrolling of large conversation histories all require deep control of the terminal rendering pipeline.

Key innovations: double-buffered screen rendering with cell-level diffing, React Compiler for auto-memoized components, Yoga (flexbox) layout engine, and self-rendering tools (each tool defines its own React components for display).

screens/REPL.tsxcomponents/ink/ ink/reconciler.tsink/screen.tsink/selection.ts
02
Orchestration
The agentic query loop — an async generator that streams LLM responses, dispatches tool calls, and loops until the model stops. Decouples control flow from UI.

The async generator pattern is key: query() yields stream events that the REPL consumes for rendering, while the headless QueryEngine consumes the same events for SDK/programmatic use. Same loop, two consumers.

The permission gateway (useCanUseTool) sits here as a cross-cutting concern, intercepting every tool call before it reaches the execution layer.

query.tsQueryEngine.ts hooks/useCanUseTool.tsxquery/
03
Tool Execution
Partitions tool calls into concurrent-safe (read-only) and serial batches, runs validation, pre/post hooks, and manages the execution lifecycle.

Read-only tools like Read, Glob, Grep run in parallel (up to 10). Mutating tools like Edit, Bash run serially. This partition maximizes throughput while preserving correctness.

Each tool execution follows: validate input (Zod) → check permissions → run pre-hooks → execute → run post-hooks → budget output.

services/tools/toolOrchestration.ts services/tools/toolExecution.ts services/tools/toolHooks.ts
04
Services
External I/O: Anthropic API streaming client (multi-provider), MCP protocol connections, context compaction, and analytics.

The LLM client supports Anthropic direct, AWS Bedrock, and GCP Vertex. It manages prompt caching (1h TTL), retry logic, token budgets, and auto-continuation when output is exhausted.

Context compaction proactively summarizes conversation history before hitting limits, with reactive fallback on prompt_too_long errors.

services/api/claude.tsservices/mcp/client.ts services/compact/services/api/withRetry.ts
05
Infrastructure
Global process state, authentication, configuration, telemetry, proxy setup, and pure utilities. No dependencies on upper layers.

bootstrap/state.ts is a deliberate singleton holding session ID, model, API key, and flags — avoids threading config through every function call.

entrypoints/init.ts runs the initialization sequence: config loading, environment variables, graceful shutdown handlers, telemetry setup, and proxy configuration.

bootstrap/state.tsentrypoints/init.ts utils/state/AppStateStore.ts

Startup Flow

From binary execution to the interactive REPL — a two-stage entry for fast cold starts

Stage 1: Binary Bootstrap

entrypoints/cli.tsx — handles fast paths (--version, --dump-system-prompt) with zero module imports for instant response. Only proceeds to heavy initialization when needed.

Stage 2: CLI Entry & Configuration

main.tsx (~2000 lines) — Commander.js CLI definition, parallel prefetches (MDM settings, keychain), flag parsing, and init() call for config/env/telemetry.

Stage 3: MCP & Permissions Setup

Connects to all configured MCP servers via getMcpToolsCommandsAndResources(). Initializes permission context and runs 10+ migration scripts.

Stage 4: Launch Mode

Interactive: launchRepl()REPL.tsx via React/Ink rendering loop.
Headless: -p flag → QueryEngine.ts directly (no UI).

Stage 5: REPL Active

React component tree mounts. REPL.tsx manages message state, prompt input, permission dialogs, background tasks, and the query loop.

The Agentic Query Loop

An async generator implementing the classic LLM agent pattern — stream, act, observe, repeat

Step 1
Send to API
messages + tools → claude.ts
Step 2
Stream Response
yield token deltas to UI
Step 3
Check Stop Reason
end_turn? → done
tool_use? → continue
Step 4
Execute Tools
permission → hooks → run
Step 5
Append Results
tool results → messages
← Loop back to Step 1 (if tool_use) ←

Interactive Mode (REPL)

The REPL screen consumes yielded StreamEvents to render tokens in real-time. Permission prompts show as UI dialogs. Tool results render inline with custom components.

Headless Mode (SDK)

QueryEngine.ts consumes the same async generator without a UI. Used for -p flag, SDK integrations, and programmatic access. Same loop, different consumer.

Tool System

Every tool implements the Tool<Input, Output, Progress> interface. Self-describing, self-rendering, permission-aware.

Core Tools

BashShell commands
📄ReadFile reading
EditString replacement
📝WriteFile creation
🔍GlobFile patterns
🔎GrepContent search
🌐WebFetchHTTP requests
🕵WebSearchWeb search
🤖AgentSub-agents
AskUserUser prompts
📋TodoWriteTask tracking
🎯SkillSlash commands
📚NotebookJupyter editing
🛠MCP ToolsExternal servers

Tool Execution Pipeline

Validate Zod schema Permissions rules + user prompt Pre-Hooks user shell scripts Execute tool.call() Post-Hooks modify output Budget truncate if large

Concurrency Model

Concurrent Batch (up to 10)

Read-only tools: Read, Glob, Grep, WebFetch. Safe to parallelize because they don't mutate state.

Serial Execution

Mutating tools: Edit, Write, Bash, Agent. Must run one at a time to prevent race conditions.

Permission Model

Layered security: mode → rules → tool-specific checks → user confirmation

ModeBehaviorRisk LevelUse Case
default Prompts for most operations Safe Normal interactive use
acceptEdits Auto-approves file edits, prompts for bash Safe Trusted editing sessions
plan Read-only — no writes permitted Safe Architecture review / exploration
auto AI classifier auto-approves based on patterns Medium Autonomous agentic work
bypassPermissions Skips all permission prompts High CI/CD, sandboxed environments

Rule Evaluation Order

alwaysDeny then alwaysAsk then alwaysAllow then Mode Default

MCP Integration

Model Context Protocol — extending Claude Code with external tool servers

Claude Code (Client)

  • Discovers & connects to servers
  • Lists tools, resources, prompts
  • Wraps each as MCPTool
  • Handles OAuth authentication
  • Manages elicitation dialogs
stdio / HTTP / SSE / WebSocket

MCP Servers

  • Expose tools via standard protocol
  • Provide resources (files, data)
  • Define prompts (slash commands)
  • Scopes: local, user, project, enterprise
  • 6 transport types supported

Tool Name Normalization

MCP tools are normalized to API-safe names: mcp__<server>__<tool>
Example: Figma's get_design_context becomes mcp__claude_ai_Figma__get_design_context
buildMcpToolName() constructs them; mcpInfoFromString() parses them back.

Directory Map

Color-coded by architectural layer

screens/Top-level React screens
components/300+ UI components
ink/Custom terminal renderer
vim/Vim emulation mode
query/Query loop modules
hooks/React hooks (permissions)
coordinator/Multi-agent coordinator
tools/Tool implementations
commands/Slash commands
skills/Bundled skills
services/API, MCP, compact, LSP
tasks/Background task types
plugins/Plugin system
bootstrap/Global process state
utils/Auth, config, telemetry
entrypoints/CLI, init, MCP, SDK
state/AppStateStore
types/Shared type defs
migrations/Settings migrations
bridge/Remote control relay
keybindings/Key binding system
memdir/CLAUDE.md memory
context/React contexts
outputStyles/Custom output styles
Presentation Orchestration Tool Execution Services Infrastructure Cross-cutting

Key Design Decisions

The "why" behind the architecture

React for Terminal UI

Uses a heavily forked Ink framework to render React components in the terminal. Enables declarative, component-based UI with the same mental model as web development.

Why: Tools self-render their own output via React components, keeping the tool system extensible without modifying core UI code.

Async Generator Agent Loop

query() is an async generator that yields stream events. The REPL and SDK consume the same generator differently.

Why: Clean separation between agentic control flow and consumers. One loop serves interactive UI, headless CLI, and SDK programmatic access.

Build-time Feature Flags

Bun's feature() macro replaces flags with boolean literals at compile time, enabling dead-code elimination.

Why: One codebase produces different binaries — internal Anthropic builds with extra tools vs external public builds, with zero runtime cost.

Tool Interface as Central Contract

Every tool (built-in, MCP, agent) implements the same Tool<I, O, P> interface with schema, permissions, rendering.

Why: New tools plug in without changing core logic. MCP external tools get the same permission/hook treatment as built-in tools.

Permission as Cross-cutting Concern

Rather than embedding checks inside tools, the permission gateway sits at the orchestration layer and applies uniformly to all tool calls.

Why: Prevents tools from accidentally bypassing security. Rule evaluation (deny > ask > allow > mode) is consistent and auditable.

Two-stage Startup

cli.tsx handles fast paths with zero imports. Heavy initialization only loads when actually needed.

Why: --version responds instantly. Cold start for the full REPL is optimized with parallel prefetches (keychain, MDM settings).

Proactive Context Compaction

Automatically summarizes conversation history before hitting context limits, with a reactive fallback on prompt_too_long errors.

Why: Long agentic sessions can generate enormous context. Rather than failing, the system preserves the most important information and continues.

Global Singleton State

bootstrap/state.ts holds session ID, model, API key, and flags as a deliberate mutable singleton.

Why: Avoids threading configuration through every function call in a single-process CLI app. Pragmatic trade-off for a non-concurrent runtime.