| name | claude-code-agent-architecture |
| description | Research repository analyzing Claude Code CLI Agent architecture, tools, telemetry, and internal mechanisms |
| triggers | ["how does claude code agent work internally","analyze claude code architecture","what tools does claude code use","claude code agent system design","understanding coding agent architecture","claude code tool permissions and execution","implement coding agent like claude code","claude code feature flags and telemetry"] |
Claude Code Agent Architecture
Skill by ara.so — AI Agent Skills collection.
Overview
sanbuphy/learn-coding-agent is a research repository that reverse-engineers and documents the architecture of Claude Code (the CLI coding agent). It provides deep technical analysis of:
- Core agent loop: How messages, tools, and API calls form the execution cycle
- Tool system: 40+ built-in tools with permission flows
- Telemetry & privacy: What data is collected and how
- Hidden features: Undercover mode, killswitches, remote control
- Architecture patterns: Production-grade harness mechanisms
This is a learning resource for understanding production coding agents, not the actual Claude Code source code.
Installation
git clone https://github.com/sanbuphy/learn-coding-agent.git
cd learn-coding-agent
npm install
bun install
Note: This is a documentation/research project. The TypeScript files are reconstructed architecture examples, not a runnable agent.
Core Agent Loop Pattern
The fundamental agent pattern documented in this research:
async function agentLoop(userMessage: string, messages: Message[]) {
messages.push({ role: "user", content: userMessage });
while (true) {
const response = await callClaudeAPI(messages);
if (response.stop_reason === "tool_use") {
const toolResults = await executeTools(response.content);
messages.push({ role: "assistant", content: response.content });
messages.push({ role: "user", content: toolResults });
} else {
return response.content;
}
}
}
The 12 Progressive Harness Mechanisms
Claude Code wraps the basic loop with production features:
- Permission System: User approval for file writes, command execution
- Streaming: Real-time token streaming with UI updates
- Concurrency: Parallel tool execution via
StreamingToolExecutor
- Context Compaction: Automatic message history compression
- Sub-agents: Specialized agents (verification, review, planning)
- Persistence: Session state saved to disk
- MCP Integration: Model Context Protocol server support
- Cost Tracking: Token usage and API cost accumulation
- Analytics: Telemetry events (OpenTelemetry + Datadog)
- Remote Control: Feature flags and killswitches from server
- Error Recovery: Retry logic with exponential backoff
- Multi-transport: CLI, bridge, SDK interfaces
Tool System Architecture
Built-in Tool Categories
The research documents 40+ tools across categories:
const toolRegistry = {
"read_file": { category: "file", risk: "low" },
"write_to_file": { category: "file", risk: "high", requiresPermission: true },
"list_files": { category: "file", risk: "low" },
"execute_command": { category: "command", risk: "high", requiresPermission: true },
"spawn_agent": { category: "agent", risk: "medium" },
"search_files": { category: "search", risk: "low" },
"list_code_definition_names": { category: "code", risk: "low" },
"browser_navigate": { category: "browser", risk: "medium" },
"browser_click": { category: "browser", : },
: { : , : },
: { : , : }
};
Permission Flow
interface ToolPermission {
toolName: string;
approved: boolean;
autoApprove?: boolean;
risk: "low" | "medium" | "high";
}
async function checkPermission(tool: Tool, args: any): Promise<boolean> {
if (config.autoApprove?.[tool.name]) {
return true;
}
if (tool.risk === "low") {
return true;
}
const approved = await showPermissionDialog({
tool: tool.name,
description: tool.description,
args: sanitizeArgs(args),
risk: tool.risk
});
analytics.track("tool_permission_decision", {
: tool.,
approved,
:
});
approved;
}
Tool Execution with Streaming
class StreamingToolExecutor {
async executeTools(toolCalls: ToolUse[]): Promise<ToolResult[]> {
const results: ToolResult[] = [];
const parallel = toolCalls.filter(t => this.canRunInParallel(t));
const sequential = toolCalls.filter(t => !this.canRunInParallel(t));
if (parallel.length > 0) {
const parallelResults = await Promise.all(
parallel.map(tc => this.executeSingleTool(tc))
);
results.push(...parallelResults);
}
for (const toolCall of sequential) {
const result = await this.executeSingleTool(toolCall);
results.(result);
}
results;
}
(: ): <> {
tool = toolRegistry.(toolCall.);
approved = (tool, toolCall.);
(!approved) {
{
: toolCall.,
: ,
:
};
}
{
output = tool.(toolCall.);
{
: toolCall.,
: output
};
} (error) {
{
: toolCall.,
: error.,
:
};
}
}
}
Key Architectural Components
1. Query Engine
class QueryEngine {
private messages: Message[] = [];
private state: TaskState;
async runQuery(input: string, options?: QueryOptions): Promise<QueryResult> {
this.messages.push({ role: "user", content: input });
if (this.shouldCompact()) {
await this.compactContext();
}
while (true) {
const response = await this.callAPI();
if (response.stop_reason === "tool_use") {
const toolResults = await this.toolExecutor.executeTools(
response.content.filter(c => c. === )
);
..({ : , : response. });
..({ : , : toolResults });
} {
{ : response., : . };
}
}
}
(): <> {
compactedMessages = ..(
.,
{ : }
);
. = compactedMessages;
}
}
2. MCP Integration
interface MCPServer {
name: string;
command: string;
args: string[];
env?: Record<string, string>;
}
class MCPManager {
private servers: Map<string, MCPServerInstance> = new Map();
async connectServer(config: MCPServer): Promise<void> {
const instance = await this.spawn({
command: config.command,
args: config.args,
env: { ...process.env, ...config.env }
});
const tools = await instance.listTools();
tools.forEach(tool => {
toolRegistry.register(`mcp_${config.name}_${tool.name}`, tool);
});
this..(config., instance);
}
(: , : , : ): <> {
server = ..(serverName);
server.(toolName, args);
}
}
mcpConfig = {
: {
: {
: ,
: [, ]
},
: {
: ,
: [],
: {
:
}
}
}
};
3. Telemetry System
interface AnalyticsEvent {
event: string;
properties: Record<string, any>;
userId?: string;
sessionId: string;
timestamp: number;
}
class AnalyticsService {
private sinks = {
firstParty: new OpenTelemetryClient(),
datadog: new DatadogClient()
};
track(event: string, properties: Record<string, any>): void {
const payload: AnalyticsEvent = {
event,
properties: {
...properties,
os: process.platform,
arch: process.arch,
nodeVersion: process.version,
appVersion: packageJson.version,
repoHash: this.getRepoHash(),
},
sessionId: .,
: .()
};
...(payload);
...(payload);
(process.. === ) {
.(payload);
}
}
(: , : , : ): {
.(, {
tool,
: .(args).,
: .(result).,
: !result.
});
}
}
4. Feature Flags & Remote Control
interface RemoteSettings {
killswitches: {
disable_bypass_permissions?: boolean;
disable_fast_mode?: boolean;
disable_analytics?: boolean;
};
modelOverrides?: {
defaultModel?: string;
enabledModels?: string[];
};
experimentFlags?: Record<string, any>;
}
class SettingsSyncService {
private pollInterval = 60 * 60 * 1000;
async poll(): Promise<void> {
const settings = await fetch(
"https://api.claude.ai/api/claude_code/settings",
{ headers: { Authorization: `Bearer ${this.token}` } }
).then(r => r.json());
if (settings.killswitches?.disable_bypass_permissions) {
config.bypassPermissions = false;
}
(.(settings)) {
accepted = ({
: ,
: ,
: [, ]
});
(!accepted) {
process.();
}
}
.(settings.);
}
}
Common Patterns
Building a Custom Tool
import { buildTool } from "./Tool";
const customTool = buildTool({
name: "analyze_code_quality",
description: "Analyzes code quality metrics for a given file",
parameters: {
type: "object",
properties: {
filePath: { type: "string", description: "Path to file" },
metrics: {
type: "array",
items: { type: "string" },
description: "Metrics to analyze (complexity, coverage, etc.)"
}
},
required: ["filePath"]
},
async execute({ filePath, metrics = ["complexity"] }) {
const content = await fs.readFile(filePath, "utf-8");
const results = await analyzeCode(content, metrics);
return {
file: filePath,
metrics: results,
summary: `Analyzed ${metrics.length} metrics for `
};
},
: ,
: ,
:
});
toolRegistry.(, customTool);
Implementing Sub-agents
class SubAgent {
constructor(
private role: string,
private systemPrompt: string
) {}
async run(task: string, context: any): Promise<string> {
const messages = [
{ role: "system", content: this.systemPrompt },
{ role: "user", content: this.formatTask(task, context) }
];
const response = await callClaudeAPI({
messages,
model: "claude-sonnet-4.0",
max_tokens: 4096
});
return response.content[0].text;
}
private formatTask(task: string, context: any): string {
return `Task: ${task}\n\nContext:\n`;
}
}
reviewAgent = (
,
);
reviewResult = reviewAgent.(
,
{ diff, files, description }
);
Context Compaction Strategy
class CompactionService {
async compact(messages: Message[], options: CompactOptions): Promise<Message[]> {
const totalTokens = this.estimateTokens(messages);
if (totalTokens < options.maxTokens) {
return messages;
}
const recentMessages = messages.slice(-10);
const oldMessages = messages.slice(0, -10);
const summary = await this.summarizeMessages(oldMessages);
return [
{ role: "system", content: `Previous conversation summary:\n${summary}` },
...recentMessages
];
}
private async summarizeMessages(messages: Message[]): Promise<string> {
const response = await ({
: [
{ : , : }
],
: ,
:
});
response.[].;
}
}
Configuration
Environment Variables
ANTHROPIC_API_KEY=sk-ant-xxx
OTEL_LOG_TOOL_DETAILS=1
ANALYTICS_ENABLED=true
ENABLE_FAST_MODE=true
ENABLE_VOICE_MODE=false
ENABLE_UNDERCOVER_MODE=false
MCP_SERVER_PATH=/path/to/mcp-servers
MCP_ALLOWED_PATHS=/workspace,/home/user/projects
DEBUG=claude:*
LOG_LEVEL=info
Configuration File
{
"defaultModel": "claude-sonnet-4.0",
"autoApprove": {
"read_file": true,
"list_files": true,
"search_files": true
},
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"]
}
},
"maxTokens": 8192,
"compactionThreshold": 100000,
"telemetry": {
Research Reports
The repository includes deep-dive reports in 4 languages:
Key Reports
01-telemetry-and-privacy: Documents two analytics sinks (OpenTelemetry + Datadog), environment fingerprinting, and why opt-out isn't exposed in UI.
02-hidden-features-and-codenames: Reveals animal codenames (Capybara, Tengu, Numbat), feature flags using random word pairs, and internal vs external user differences.
03-undercover-mode: Official employees auto-enter mode that instructs the model to hide AI authorship in public repos ("Do not blow your cover").
04-remote-control-and-killswitches: Hourly polling of settings API, blocking dialogs that exit the app on rejection, 6+ killswitches.
05-future-roadmap: Upcoming Numbat model, KAIROS autonomous mode with <tick> heartbeats, voice mode, 17 unreleased tools.
Access reports in docs/[en|ja|ko|zh]/ directories.
Troubleshooting
Understanding Agent Behavior
process.env.DEBUG = "claude:*";
process.env.LOG_LEVEL = "debug";
Analyzing Tool Permissions
const highRiskTools = Array.from(toolRegistry.entries())
.filter(([_, tool]) => tool.risk === "high")
.map(([name, _]) => name);
console.log("High-risk tools:", highRiskTools);
Debugging MCP Connections
const mcpManager = new MCPManager();
try {
await mcpManager.connectServer({
name: "test-server",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-puppeteer"]
});
const tools = await mcpManager.listTools("test-server");
console.log("Available MCP tools:", tools);
} catch (error) {
console.error("MCP connection failed:", error.message);
}
Inspecting Telemetry Events
export OTEL_LOG_TOOL_DETAILS=1
claude-code | grep "tool_executed"
Additional Resources
Note: This is a research and educational repository. It documents publicly available information about coding agent architecture. For production coding agent development, refer to official Anthropic documentation and Claude API guides.