agent-sdk/subagents.md +601 −0 added
1> ## Documentation Index
2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
3> Use this file to discover all available pages before exploring further.
4
5# Subagents in the SDK
6
7> Define and invoke subagents to isolate context, run tasks in parallel, and apply specialized instructions in your Claude Agent SDK applications.
8
9Subagents are separate agent instances that your main agent can spawn to handle focused subtasks.
10Use subagents to isolate context for focused subtasks, run multiple analyses in parallel, and apply specialized instructions without bloating the main agent's prompt.
11
12This guide explains how to define and use subagents in the SDK using the `agents` parameter.
13
14## Overview
15
16You can create subagents in three ways:
17
18* **Programmatically**: use the `agents` parameter in your `query()` options ([TypeScript](/en/agent-sdk/typescript#agent-definition), [Python](/en/agent-sdk/python#agent-definition))
19* **Filesystem-based**: define agents as markdown files in `.claude/agents/` directories (see [defining subagents as files](/en/sub-agents))
20* **Built-in general-purpose**: Claude can invoke the built-in `general-purpose` subagent at any time via the Agent tool without you defining anything
21
22This guide focuses on the programmatic approach, which is recommended for SDK applications.
23
24When you define subagents, Claude determines whether to invoke them based on each subagent's `description` field. Write clear descriptions that explain when the subagent should be used, and Claude will automatically delegate appropriate tasks. You can also explicitly request a subagent by name in your prompt (for example, "Use the code-reviewer agent to...").
25
26## Benefits of using subagents
27
28### Context isolation
29
30Each subagent runs in its own fresh conversation. Intermediate tool calls and results stay inside the subagent; only its final message returns to the parent. See [What subagents inherit](#what-subagents-inherit) for exactly what's in the subagent's context.
31
32**Example:** a `research-assistant` subagent can explore dozens of files without any of that content accumulating in the main conversation. The parent receives a concise summary, not every file the subagent read.
33
34### Parallelization
35
36Multiple subagents can run concurrently, dramatically speeding up complex workflows.
37
38**Example:** during a code review, you can run `style-checker`, `security-scanner`, and `test-coverage` subagents simultaneously, reducing review time from minutes to seconds.
39
40### Specialized instructions and knowledge
41
42Each subagent can have tailored system prompts with specific expertise, best practices, and constraints.
43
44**Example:** a `database-migration` subagent can have detailed knowledge about SQL best practices, rollback strategies, and data integrity checks that would be unnecessary noise in the main agent's instructions.
45
46### Tool restrictions
47
48Subagents can be limited to specific tools, reducing the risk of unintended actions.
49
50**Example:** a `doc-reviewer` subagent might only have access to Read and Grep tools, ensuring it can analyze but never accidentally modify your documentation files.
51
52## Creating subagents
53
54### Programmatic definition (recommended)
55
56Define subagents directly in your code using the `agents` parameter. This example creates two subagents: a code reviewer with read-only access and a test runner that can execute commands. The `Agent` tool must be included in `allowedTools` since Claude invokes subagents through the Agent tool.
57
58<CodeGroup>
59 ```python Python theme={null}
60 import asyncio
61 from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
62
63
64 async def main():
65 async for message in query(
66 prompt="Review the authentication module for security issues",
67 options=ClaudeAgentOptions(
68 # Agent tool is required for subagent invocation
69 allowed_tools=["Read", "Grep", "Glob", "Agent"],
70 agents={
71 "code-reviewer": AgentDefinition(
72 # description tells Claude when to use this subagent
73 description="Expert code review specialist. Use for quality, security, and maintainability reviews.",
74 # prompt defines the subagent's behavior and expertise
75 prompt="""You are a code review specialist with expertise in security, performance, and best practices.
76
77 When reviewing code:
78 - Identify security vulnerabilities
79 - Check for performance issues
80 - Verify adherence to coding standards
81 - Suggest specific improvements
82
83 Be thorough but concise in your feedback.""",
84 # tools restricts what the subagent can do (read-only here)
85 tools=["Read", "Grep", "Glob"],
86 # model overrides the default model for this subagent
87 model="sonnet",
88 ),
89 "test-runner": AgentDefinition(
90 description="Runs and analyzes test suites. Use for test execution and coverage analysis.",
91 prompt="""You are a test execution specialist. Run tests and provide clear analysis of results.
92
93 Focus on:
94 - Running test commands
95 - Analyzing test output
96 - Identifying failing tests
97 - Suggesting fixes for failures""",
98 # Bash access lets this subagent run test commands
99 tools=["Bash", "Read", "Grep"],
100 ),
101 },
102 ),
103 ):
104 if hasattr(message, "result"):
105 print(message.result)
106
107
108 asyncio.run(main())
109 ```
110
111 ```typescript TypeScript theme={null}
112 import { query } from "@anthropic-ai/claude-agent-sdk";
113
114 for await (const message of query({
115 prompt: "Review the authentication module for security issues",
116 options: {
117 // Agent tool is required for subagent invocation
118 allowedTools: ["Read", "Grep", "Glob", "Agent"],
119 agents: {
120 "code-reviewer": {
121 // description tells Claude when to use this subagent
122 description:
123 "Expert code review specialist. Use for quality, security, and maintainability reviews.",
124 // prompt defines the subagent's behavior and expertise
125 prompt: `You are a code review specialist with expertise in security, performance, and best practices.
126
127 When reviewing code:
128 - Identify security vulnerabilities
129 - Check for performance issues
130 - Verify adherence to coding standards
131 - Suggest specific improvements
132
133 Be thorough but concise in your feedback.`,
134 // tools restricts what the subagent can do (read-only here)
135 tools: ["Read", "Grep", "Glob"],
136 // model overrides the default model for this subagent
137 model: "sonnet"
138 },
139 "test-runner": {
140 description:
141 "Runs and analyzes test suites. Use for test execution and coverage analysis.",
142 prompt: `You are a test execution specialist. Run tests and provide clear analysis of results.
143
144 Focus on:
145 - Running test commands
146 - Analyzing test output
147 - Identifying failing tests
148 - Suggesting fixes for failures`,
149 // Bash access lets this subagent run test commands
150 tools: ["Bash", "Read", "Grep"]
151 }
152 }
153 }
154 })) {
155 if ("result" in message) console.log(message.result);
156 }
157 ```
158</CodeGroup>
159
160### AgentDefinition configuration
161
162| Field | Type | Required | Description |
163| :---------------- | :---------------------------------------------------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- |
164| `description` | `string` | Yes | Natural language description of when to use this agent |
165| `prompt` | `string` | Yes | The agent's system prompt defining its role and behavior |
166| `tools` | `string[]` | No | Array of allowed tool names. If omitted, inherits all tools |
167| `disallowedTools` | `string[]` | No | Array of tool names to remove from the agent's tool set |
168| `model` | `string` | No | Model override for this agent. Accepts an alias such as `'sonnet'`, `'opus'`, `'haiku'`, `'inherit'`, or a full model ID. Defaults to main model if omitted |
169| `skills` | `string[]` | No | List of skill names available to this agent |
170| `memory` | `'user' \| 'project' \| 'local'` | No | Memory source for this agent |
171| `mcpServers` | `(string \| object)[]` | No | MCP servers available to this agent, by name or inline config |
172| `maxTurns` | `number` | No | Maximum number of agentic turns before the agent stops |
173| `background` | `boolean` | No | Run this agent as a non-blocking background task when invoked |
174| `effort` | `'low' \| 'medium' \| 'high' \| 'xhigh' \| 'max' \| number` | No | Reasoning effort level for this agent |
175| `permissionMode` | `PermissionMode` | No | Permission mode for tool execution within this agent |
176
177In the Python SDK, these field names use camelCase to match the wire format. See the [`AgentDefinition` reference](/en/agent-sdk/python#agent-definition) for details.
178
179<Note>
180 Subagents cannot spawn their own subagents. Don't include `Agent` in a subagent's `tools` array.
181</Note>
182
183### Filesystem-based definition (alternative)
184
185You can also define subagents as markdown files in `.claude/agents/` directories. See the [Claude Code subagents documentation](/en/sub-agents) for details on this approach. Programmatically defined agents take precedence over filesystem-based agents with the same name.
186
187<Note>
188 Even without defining custom subagents, Claude can spawn the built-in `general-purpose` subagent when `Agent` is in your `allowedTools`. This is useful for delegating research or exploration tasks without creating specialized agents.
189</Note>
190
191## What subagents inherit
192
193A subagent's context window starts fresh (no parent conversation) but isn't empty. The only channel from parent to subagent is the Agent tool's prompt string, so include any file paths, error messages, or decisions the subagent needs directly in that prompt.
194
195| The subagent receives | The subagent does not receive |
196| :--------------------------------------------------------------------------- | :------------------------------------------------- |
197| Its own system prompt (`AgentDefinition.prompt`) and the Agent tool's prompt | The parent's conversation history or tool results |
198| Project CLAUDE.md (loaded via `settingSources`) | Skills (unless listed in `AgentDefinition.skills`) |
199| Tool definitions (inherited from parent, or the subset in `tools`) | The parent's system prompt |
200
201<Note>
202 The parent receives the subagent's final message verbatim as the Agent tool result, but may summarize it in its own response. To preserve subagent output verbatim in the user-facing response, include an instruction to do so in the prompt or `systemPrompt` option you pass to the **main** `query()` call.
203</Note>
204
205## Invoking subagents
206
207### Automatic invocation
208
209Claude automatically decides when to invoke subagents based on the task and each subagent's `description`. For example, if you define a `performance-optimizer` subagent with the description "Performance optimization specialist for query tuning", Claude will invoke it when your prompt mentions optimizing queries.
210
211Write clear, specific descriptions so Claude can match tasks to the right subagent.
212
213### Explicit invocation
214
215To guarantee Claude uses a specific subagent, mention it by name in your prompt:
216
217```text theme={null}
218"Use the code-reviewer agent to check the authentication module"
219```
220
221This bypasses automatic matching and directly invokes the named subagent.
222
223### Dynamic agent configuration
224
225You can create agent definitions dynamically based on runtime conditions. This example creates a security reviewer with different strictness levels, using a more powerful model for strict reviews.
226
227<CodeGroup>
228 ```python Python theme={null}
229 import asyncio
230 from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
231
232
233 # Factory function that returns an AgentDefinition
234 # This pattern lets you customize agents based on runtime conditions
235 def create_security_agent(security_level: str) -> AgentDefinition:
236 is_strict = security_level == "strict"
237 return AgentDefinition(
238 description="Security code reviewer",
239 # Customize the prompt based on strictness level
240 prompt=f"You are a {'strict' if is_strict else 'balanced'} security reviewer...",
241 tools=["Read", "Grep", "Glob"],
242 # Key insight: use a more capable model for high-stakes reviews
243 model="opus" if is_strict else "sonnet",
244 )
245
246
247 async def main():
248 # The agent is created at query time, so each request can use different settings
249 async for message in query(
250 prompt="Review this PR for security issues",
251 options=ClaudeAgentOptions(
252 allowed_tools=["Read", "Grep", "Glob", "Agent"],
253 agents={
254 # Call the factory with your desired configuration
255 "security-reviewer": create_security_agent("strict")
256 },
257 ),
258 ):
259 if hasattr(message, "result"):
260 print(message.result)
261
262
263 asyncio.run(main())
264 ```
265
266 ```typescript TypeScript theme={null}
267 import { query, type AgentDefinition } from "@anthropic-ai/claude-agent-sdk";
268
269 // Factory function that returns an AgentDefinition
270 // This pattern lets you customize agents based on runtime conditions
271 function createSecurityAgent(securityLevel: "basic" | "strict"): AgentDefinition {
272 const isStrict = securityLevel === "strict";
273 return {
274 description: "Security code reviewer",
275 // Customize the prompt based on strictness level
276 prompt: `You are a ${isStrict ? "strict" : "balanced"} security reviewer...`,
277 tools: ["Read", "Grep", "Glob"],
278 // Key insight: use a more capable model for high-stakes reviews
279 model: isStrict ? "opus" : "sonnet"
280 };
281 }
282
283 // The agent is created at query time, so each request can use different settings
284 for await (const message of query({
285 prompt: "Review this PR for security issues",
286 options: {
287 allowedTools: ["Read", "Grep", "Glob", "Agent"],
288 agents: {
289 // Call the factory with your desired configuration
290 "security-reviewer": createSecurityAgent("strict")
291 }
292 }
293 })) {
294 if ("result" in message) console.log(message.result);
295 }
296 ```
297</CodeGroup>
298
299## Detecting subagent invocation
300
301Subagents are invoked via the Agent tool. To detect when a subagent is invoked, check for `tool_use` blocks where `name` is `"Agent"`. Messages from within a subagent's context include a `parent_tool_use_id` field.
302
303<Note>
304 The tool name was renamed from `"Task"` to `"Agent"` in Claude Code v2.1.63. Current SDK releases emit `"Agent"` in `tool_use` blocks but still use `"Task"` in the `system:init` tools list and in `result.permission_denials[].tool_name`. Checking both values in `block.name` ensures compatibility across SDK versions.
305</Note>
306
307This example iterates through streamed messages, logging when a subagent is invoked and when subsequent messages originate from within that subagent's execution context.
308
309<Note>
310 The message structure differs between SDKs. In Python, content blocks are accessed directly via `message.content`. In TypeScript, `SDKAssistantMessage` wraps the Claude API message, so content is accessed via `message.message.content`.
311</Note>
312
313<CodeGroup>
314 ```python Python theme={null}
315 import asyncio
316 from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
317
318
319 async def main():
320 async for message in query(
321 prompt="Use the code-reviewer agent to review this codebase",
322 options=ClaudeAgentOptions(
323 allowed_tools=["Read", "Glob", "Grep", "Agent"],
324 agents={
325 "code-reviewer": AgentDefinition(
326 description="Expert code reviewer.",
327 prompt="Analyze code quality and suggest improvements.",
328 tools=["Read", "Glob", "Grep"],
329 )
330 },
331 ),
332 ):
333 # Check for subagent invocation. Match both names: older SDK
334 # versions emitted "Task", current versions emit "Agent".
335 if hasattr(message, "content") and message.content:
336 for block in message.content:
337 if getattr(block, "type", None) == "tool_use" and block.name in (
338 "Task",
339 "Agent",
340 ):
341 print(f"Subagent invoked: {block.input.get('subagent_type')}")
342
343 # Check if this message is from within a subagent's context
344 if hasattr(message, "parent_tool_use_id") and message.parent_tool_use_id:
345 print(" (running inside subagent)")
346
347 if hasattr(message, "result"):
348 print(message.result)
349
350
351 asyncio.run(main())
352 ```
353
354 ```typescript TypeScript theme={null}
355 import { query } from "@anthropic-ai/claude-agent-sdk";
356
357 for await (const message of query({
358 prompt: "Use the code-reviewer agent to review this codebase",
359 options: {
360 allowedTools: ["Read", "Glob", "Grep", "Agent"],
361 agents: {
362 "code-reviewer": {
363 description: "Expert code reviewer.",
364 prompt: "Analyze code quality and suggest improvements.",
365 tools: ["Read", "Glob", "Grep"]
366 }
367 }
368 }
369 })) {
370 const msg = message as any;
371
372 // Check for subagent invocation. Match both names: older SDK versions
373 // emitted "Task", current versions emit "Agent".
374 for (const block of msg.message?.content ?? []) {
375 if (block.type === "tool_use" && (block.name === "Task" || block.name === "Agent")) {
376 console.log(`Subagent invoked: ${block.input.subagent_type}`);
377 }
378 }
379
380 // Check if this message is from within a subagent's context
381 if (msg.parent_tool_use_id) {
382 console.log(" (running inside subagent)");
383 }
384
385 if ("result" in message) {
386 console.log(message.result);
387 }
388 }
389 ```
390</CodeGroup>
391
392## Resuming subagents
393
394Subagents can be resumed to continue where they left off. Resumed subagents retain their full conversation history, including all previous tool calls, results, and reasoning. The subagent picks up exactly where it stopped rather than starting fresh.
395
396When a subagent completes, Claude receives its agent ID in the Agent tool result. To resume a subagent programmatically:
397
3981. **Capture the session ID**: Extract `session_id` from messages during the first query
3992. **Extract the agent ID**: Parse `agentId` from the message content
4003. **Resume the session**: Pass `resume: sessionId` in the second query's options, and include the agent ID in your prompt
401
402<Note>
403 You must resume the same session to access the subagent's transcript. Each `query()` call starts a new session by default, so pass `resume: sessionId` to continue in the same session.
404
405 If you're using a custom agent (not a built-in one), you also need to pass the same agent definition in the `agents` parameter for both queries.
406</Note>
407
408The example below demonstrates this flow: the first query runs a subagent and captures the session ID and agent ID, then the second query resumes the session to ask a follow-up question that requires context from the first analysis.
409
410<CodeGroup>
411 ```typescript TypeScript theme={null}
412 import { query, type SDKMessage } from "@anthropic-ai/claude-agent-sdk";
413
414 // Helper to extract agentId from message content
415 // Stringify to avoid traversing different block types (TextBlock, ToolResultBlock, etc.)
416 function extractAgentId(message: SDKMessage): string | undefined {
417 if (!("message" in message)) return undefined;
418 // Stringify the content so we can search it without traversing nested blocks
419 const content = JSON.stringify(message.message.content);
420 const match = content.match(/agentId:\s*([a-f0-9-]+)/);
421 return match?.[1];
422 }
423
424 let agentId: string | undefined;
425 let sessionId: string | undefined;
426
427 // First invocation - use the Explore agent to find API endpoints
428 for await (const message of query({
429 prompt: "Use the Explore agent to find all API endpoints in this codebase",
430 options: { allowedTools: ["Read", "Grep", "Glob", "Agent"] }
431 })) {
432 // Capture session_id from ResultMessage (needed to resume this session)
433 if ("session_id" in message) sessionId = message.session_id;
434 // Search message content for the agentId (appears in Agent tool results)
435 const extractedId = extractAgentId(message);
436 if (extractedId) agentId = extractedId;
437 // Print the final result
438 if ("result" in message) console.log(message.result);
439 }
440
441 // Second invocation - resume and ask follow-up
442 if (agentId && sessionId) {
443 for await (const message of query({
444 prompt: `Resume agent ${agentId} and list the top 3 most complex endpoints`,
445 options: { allowedTools: ["Read", "Grep", "Glob", "Agent"], resume: sessionId }
446 })) {
447 if ("result" in message) console.log(message.result);
448 }
449 }
450 ```
451
452 ```python Python theme={null}
453 import asyncio
454 import json
455 import re
456 from claude_agent_sdk import query, ClaudeAgentOptions
457
458
459 def extract_agent_id(text: str) -> str | None:
460 """Extract agentId from Agent tool result text."""
461 match = re.search(r"agentId:\s*([a-f0-9-]+)", text)
462 return match.group(1) if match else None
463
464
465 async def main():
466 agent_id = None
467 session_id = None
468
469 # First invocation - use the Explore agent to find API endpoints
470 async for message in query(
471 prompt="Use the Explore agent to find all API endpoints in this codebase",
472 options=ClaudeAgentOptions(allowed_tools=["Read", "Grep", "Glob", "Agent"]),
473 ):
474 # Capture session_id from ResultMessage (needed to resume this session)
475 if hasattr(message, "session_id"):
476 session_id = message.session_id
477 # Search message content for the agentId (appears in Agent tool results)
478 if hasattr(message, "content"):
479 # Stringify the content so we can search it without traversing nested blocks
480 content_str = json.dumps(message.content, default=str)
481 extracted = extract_agent_id(content_str)
482 if extracted:
483 agent_id = extracted
484 # Print the final result
485 if hasattr(message, "result"):
486 print(message.result)
487
488 # Second invocation - resume and ask follow-up
489 if agent_id and session_id:
490 async for message in query(
491 prompt=f"Resume agent {agent_id} and list the top 3 most complex endpoints",
492 options=ClaudeAgentOptions(
493 allowed_tools=["Read", "Grep", "Glob", "Agent"], resume=session_id
494 ),
495 ):
496 if hasattr(message, "result"):
497 print(message.result)
498
499
500 asyncio.run(main())
501 ```
502</CodeGroup>
503
504Subagent transcripts persist independently of the main conversation:
505
506* **Main conversation compaction**: When the main conversation compacts, subagent transcripts are unaffected. They're stored in separate files.
507* **Session persistence**: Subagent transcripts persist within their session. You can resume a subagent after restarting Claude Code by resuming the same session.
508* **Automatic cleanup**: Transcripts are cleaned up based on the `cleanupPeriodDays` setting (default: 30 days).
509
510## Tool restrictions
511
512Subagents can have restricted tool access via the `tools` field:
513
514* **Omit the field**: agent inherits all available tools (default)
515* **Specify tools**: agent can only use listed tools
516
517This example creates a read-only analysis agent that can examine code but cannot modify files or run commands.
518
519<CodeGroup>
520 ```python Python theme={null}
521 import asyncio
522 from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
523
524
525 async def main():
526 async for message in query(
527 prompt="Analyze the architecture of this codebase",
528 options=ClaudeAgentOptions(
529 allowed_tools=["Read", "Grep", "Glob", "Agent"],
530 agents={
531 "code-analyzer": AgentDefinition(
532 description="Static code analysis and architecture review",
533 prompt="""You are a code architecture analyst. Analyze code structure,
534 identify patterns, and suggest improvements without making changes.""",
535 # Read-only tools: no Edit, Write, or Bash access
536 tools=["Read", "Grep", "Glob"],
537 )
538 },
539 ),
540 ):
541 if hasattr(message, "result"):
542 print(message.result)
543
544
545 asyncio.run(main())
546 ```
547
548 ```typescript TypeScript theme={null}
549 import { query } from "@anthropic-ai/claude-agent-sdk";
550
551 for await (const message of query({
552 prompt: "Analyze the architecture of this codebase",
553 options: {
554 allowedTools: ["Read", "Grep", "Glob", "Agent"],
555 agents: {
556 "code-analyzer": {
557 description: "Static code analysis and architecture review",
558 prompt: `You are a code architecture analyst. Analyze code structure,
559 identify patterns, and suggest improvements without making changes.`,
560 // Read-only tools: no Edit, Write, or Bash access
561 tools: ["Read", "Grep", "Glob"]
562 }
563 }
564 }
565 })) {
566 if ("result" in message) console.log(message.result);
567 }
568 ```
569</CodeGroup>
570
571### Common tool combinations
572
573| Use case | Tools | Description |
574| :----------------- | :-------------------------------------- | :-------------------------------------------------- |
575| Read-only analysis | `Read`, `Grep`, `Glob` | Can examine code but not modify or execute |
576| Test execution | `Bash`, `Read`, `Grep` | Can run commands and analyze output |
577| Code modification | `Read`, `Edit`, `Write`, `Grep`, `Glob` | Full read/write access without command execution |
578| Full access | All tools | Inherits all tools from parent (omit `tools` field) |
579
580## Troubleshooting
581
582### Claude not delegating to subagents
583
584If Claude completes tasks directly instead of delegating to your subagent:
585
5861. **Include the Agent tool**: subagents are invoked via the Agent tool, so it must be in `allowedTools`
5872. **Use explicit prompting**: mention the subagent by name in your prompt (for example, "Use the code-reviewer agent to...")
5883. **Write a clear description**: explain exactly when the subagent should be used so Claude can match tasks appropriately
589
590### Filesystem-based agents not loading
591
592Agents defined in `.claude/agents/` are loaded at startup only. If you create a new agent file while Claude Code is running, restart the session to load it.
593
594### Windows: long prompt failures
595
596On Windows, subagents with very long prompts may fail due to command line length limits (8191 chars). Keep prompts concise or use filesystem-based agents for complex instructions.
597
598## Related documentation
599
600* [Claude Code subagents](/en/sub-agents): comprehensive subagent documentation including filesystem-based definitions
601* [SDK overview](/en/agent-sdk/overview): getting started with the Claude Agent SDK