Single Agent Model
Claude Code’s agent system starts with a simple abstraction: a single agent is a self-contained unit with its own system prompt, tool set, model preference, and behavioral constraints. Every sub-agent — whether built-in or user-defined — is an instance of this model.
The AgentDefinition Type Hierarchy
Section titled “The AgentDefinition Type Hierarchy”All agents share a common base type defined in src/tools/AgentTool/loadAgentsDir.ts:
// src/tools/AgentTool/loadAgentsDir.ts — BaseAgentDefinitionexport type BaseAgentDefinition = { agentType: string // Unique identifier (e.g., "general-purpose", "Explore") whenToUse: string // Description for the parent agent to decide when to spawn tools?: string[] // Allowed tools (undefined = all, ['*'] = all) disallowedTools?: string[] // Explicitly blocked tools skills?: string[] // Skill names to preload mcpServers?: AgentMcpServerSpec[] hooks?: HooksSettings color?: AgentColorName model?: string // "inherit" = parent's model, or specific model ID effort?: EffortValue permissionMode?: PermissionMode maxTurns?: number memory?: AgentMemoryScope // 'user' | 'project' | 'local' isolation?: 'worktree' | 'remote' background?: boolean omitClaudeMd?: boolean // Skip CLAUDE.md injection (saves tokens for read-only agents) // ...}Three concrete types extend this base:
classDiagram class BaseAgentDefinition { +agentType: string +whenToUse: string +tools?: string[] +model?: string +memory?: AgentMemoryScope +isolation?: string } class BuiltInAgentDefinition { +source: "built-in" +getSystemPrompt(params): string } class CustomAgentDefinition { +source: SettingSource +getSystemPrompt(): string +filename?: string } class PluginAgentDefinition { +source: "plugin" +getSystemPrompt(): string +plugin: string } BaseAgentDefinition <|-- BuiltInAgentDefinition BaseAgentDefinition <|-- CustomAgentDefinition BaseAgentDefinition <|-- PluginAgentDefinitionBuilt-in Agents
Section titled “Built-in Agents”Built-in agents are registered in src/tools/AgentTool/builtInAgents.ts:
export function getBuiltInAgents(): AgentDefinition[] { if ( isEnvTruthy(process.env.CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS) && getIsNonInteractiveSession() ) { return [] // SDK users can start with a blank slate }
if (feature('COORDINATOR_MODE')) { if (isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE)) { const { getCoordinatorAgents } = require('../../coordinator/workerAgent.js') return getCoordinatorAgents() // Completely different agent set } }
const agents: AgentDefinition[] = [ GENERAL_PURPOSE_AGENT, STATUSLINE_SETUP_AGENT, ] // Conditionally added... if (areExplorePlanAgentsEnabled()) { agents.push(EXPLORE_AGENT, PLAN_AGENT) } // ... return agents}The six built-in agents serve distinct roles:
| Agent | Model | Tools | Key Trait |
|---|---|---|---|
| General Purpose | default sub-agent | ['*'] (all) | Workhorse for multi-step tasks |
| Explore | haiku (external) / inherit (ant) | Read-only (no Edit/Write/Agent) | Fast codebase search, omitClaudeMd: true |
| Plan | inherit | Read-only (same as Explore) | Architecture planning, omitClaudeMd: true |
| Verification | inherit | Read-only + Bash + WebFetch | Adversarial testing, background: true |
| Claude Code Guide | haiku | Glob/Grep/Read/WebFetch/WebSearch | Documentation lookup, permissionMode: 'dontAsk' |
| Statusline Setup | sonnet | Read/Edit only | Status line configuration, color: 'orange' |
Design Patterns in Built-in Agents
Section titled “Design Patterns in Built-in Agents”Read-only enforcement. Both Explore and Plan agents achieve read-only mode through disallowedTools, not through a special “read-only” flag:
export const EXPLORE_AGENT: BuiltInAgentDefinition = { agentType: 'Explore', disallowedTools: [ AGENT_TOOL_NAME, // No spawning sub-agents EXIT_PLAN_MODE_TOOL_NAME, FILE_EDIT_TOOL_NAME, // No editing FILE_WRITE_TOOL_NAME, // No writing NOTEBOOK_EDIT_TOOL_NAME, ], omitClaudeMd: true, // Saves ~5-15 Gtok/week across 34M+ Explore spawns // ...}Token efficiency via omitClaudeMd. Read-only agents don’t need commit/PR/lint guidelines from CLAUDE.md. The omitClaudeMd flag strips this from the agent’s userContext, a significant saving at scale.
Adversarial prompting. The Verification agent has the most sophisticated system prompt (~130 lines) specifically designed to counter LLM tendencies to rubber-stamp results:
const VERIFICATION_SYSTEM_PROMPT = `You are a verification specialist.Your job is not to confirm the implementation works — it's to try to break it.
You have two documented failure patterns. First, verification avoidance:when faced with a check, you find reasons not to run it...`Custom Agent Loading
Section titled “Custom Agent Loading”Custom agents are loaded from markdown files with frontmatter or JSON definitions. The loading pipeline in getAgentDefinitionsWithOverrides():
flowchart LR A[loadMarkdownFilesForSubdir 'agents'] --> B[parseAgentFromMarkdown] C[loadPluginAgents] --> D[Plugin agents] E[getBuiltInAgents] --> F[Built-in agents] B --> G[All agents list] D --> G F --> G G --> H[getActiveAgentsFromList] H --> I[Active agents<br/>deduplicated by priority]Agent Priority System
Section titled “Agent Priority System”When multiple agents share the same agentType, the priority chain determines which one wins:
// src/tools/AgentTool/loadAgentsDir.ts — getActiveAgentsFromListconst agentGroups = [ builtInAgents, // lowest priority pluginAgents, userAgents, // ~/.claude/agents/ projectAgents, // .claude/agents/ in repo flagAgents, // feature-flag managed managedAgents, // policy/enterprise managed (highest priority)]
const agentMap = new Map<string, AgentDefinition>()for (const agents of agentGroups) { for (const agent of agents) { agentMap.set(agent.agentType, agent) // Later groups overwrite earlier }}This means a project-level agent definition can override a built-in agent, and a managed/policy agent can override everything.
Markdown Agent Format
Section titled “Markdown Agent Format”A custom agent in .claude/agents/code-reviewer.md:
---name: code-reviewerdescription: "Reviews code changes for quality and correctness"tools: - Read - Glob - Grep - Bashmodel: inheritpermissionMode: bypassPermissionsmaxTurns: 50memory: project---
You are a code review specialist. Review the provided code changes...The frontmatter is parsed with Zod validation (AgentJsonSchema), and the markdown body becomes the system prompt. When memory is set, the system prompt is dynamically appended with agent-specific memory content:
getSystemPrompt: () => { if (isAutoMemoryEnabled() && memory) { return systemPrompt + '\n\n' + loadAgentMemoryPrompt(agentType, memory) } return systemPrompt}Agent-Specific MCP Servers
Section titled “Agent-Specific MCP Servers”Agents can declare their own MCP server requirements:
// Two forms of MCP server specexport type AgentMcpServerSpec = | string // Reference by name: "slack" | { [name: string]: McpServerConfig } // Inline: { "my-server": { command: "..." } }Agents with requiredMcpServers are filtered out when their servers aren’t available:
export function hasRequiredMcpServers( agent: AgentDefinition, availableServers: string[],): boolean { return agent.requiredMcpServers.every(pattern => availableServers.some(server => server.toLowerCase().includes(pattern.toLowerCase()), ), )}The Agent Tool
Section titled “The Agent Tool”The Agent tool (src/tools/AgentTool/) is how the main agent spawns sub-agents. When the main Claude instance decides to use a sub-agent, it calls the Agent tool with a subagent_type parameter matching one of the active agent definitions. The tool then:
- Resolves the agent definition from
activeAgents - Constructs the system prompt via
getSystemPrompt() - Selects the model (inherit, specific ID, or default sub-agent model)
- Forks a new conversation with the agent’s tool set and prompt
- Returns the agent’s final response to the parent
This creates a clean parent→child relationship where each agent operates in its own context with its own constraints, while the parent retains full control over dispatch and result interpretation.