Delegating Work to Subagents in the Claude Agent SDK
A subagent is a child agent with its own isolated context that a parent agent can hand a subtask to.
Instead of a single agent trying to hold an entire large task, including every intermediate step of every sub-problem, in one context window, a parent can delegate self-contained pieces to subagents and only receive back their final results.
Summary
Subagents exist for two related reasons: context isolation and parallelism.
Isolation means a subagent's exploratory reasoning, failed attempts, and intermediate tool calls never pollute the parent's context; the parent sees a clean result.
Parallelism means independent subagents can run concurrently, since neither depends on the other's intermediate state.
This page covers how to define subagents, when a task is a good candidate for delegation, and how tool scoping and results flow between parent and child.
Recipe
Quick-reference recipe card - copy-paste ready.
from claude_agent_sdk import query, AgentOptions, SubagentConfig
options = AgentOptions(
allowed_tools=["file_edit", "bash"],
subagents=[
SubagentConfig(
name="frontend-reviewer",
description="Reviews React component changes for correctness and style.",
allowed_tools=["file_edit"],
),
SubagentConfig(
name="backend-reviewer",
description="Reviews API route changes for correctness and security.",
allowed_tools=["file_edit", "bash"],
),
],
)
async for message in query(
prompt="Review this PR's frontend and backend changes using the two reviewer subagents.",
options=options,
):
print(message)When to reach for this:
- A task splits cleanly into two or more independent pieces that don't need to see each other's intermediate steps.
- One piece would otherwise flood the parent's context with exploratory reasoning irrelevant to the rest of the task.
- You want a narrower, differently-scoped tool set for one part of the work than another.
- You want to run independent pieces concurrently instead of one after another.
Working Example
import asyncio
from claude_agent_sdk import query, AgentOptions, SubagentConfig
async def audit_monorepo_packages(repo_path: str, packages: list[str]) -> None:
subagents = [
SubagentConfig(
name=f"audit-{pkg}",
description=f"Audits the {pkg} package for outdated dependencies and lint errors.",
allowed_tools=["bash", "file_edit"],
tool_config={
"bash": {"allowed_commands": ["npm outdated", "npm run lint"]},
"file_edit": {"allowed_paths": [f"packages/{pkg}/**"]},
},
)
for pkg in packages
]
options = AgentOptions(
cwd=repo_path,
allowed_tools=["file_edit"],
subagents=subagents,
)
prompt = (
f"Use the audit subagents for each of {packages} to check for outdated "
"dependencies and lint errors, then write a combined AUDIT.md summary "
"at the repo root with one section per package."
)
async for message in query(prompt=prompt, options=options):
if message.get("type") == "text":
print(message["text"], end="", flush=True)
elif message.get("type") == "subagent_result":
print(f"\n[{message['subagent_name']} finished]")
asyncio.run(audit_monorepo_packages("/repo", ["billing", "auth", "search"]))What this demonstrates:
- Generating one subagent per package so each audit runs in its own isolated context, scoped to that package's files only.
- Scoping each subagent's
bashandfile_editaccess independently of the parent and of each other. - The parent agent only needing
file_editfor itself, since its own job is just writing the combined summary. - Independent, same-shaped subtasks (auditing unrelated packages) as the clearest case for delegation.
Deep Dive
How It Works
- A subagent is defined with a
name, adescriptionthe parent model uses to decide when to invoke it, and its ownallowed_tools/tool_config. - From the parent's tool-use loop, invoking a subagent looks like any other tool call: the parent decides to call it, the SDK runs the subagent's own internal loop to completion, and the result comes back as an observation.
- The subagent runs its own decide-act-observe cycle internally, with its own context window that starts fresh for that invocation and does not carry the parent's full history by default.
- Only the subagent's final result crosses back into the parent's context, not its intermediate tool calls or reasoning; this is the isolation property that keeps the parent's context clean.
- Because each subagent invocation is self-contained, independent subagents can be dispatched and awaited concurrently, while a subagent whose task depends on another subagent's output must run after it.
When to Delegate vs. Add a Tool
| Signal | Favors | Reason |
|---|---|---|
| Task needs one more capability, no independent exploration | Adding a tool | Simpler; no extra context boundary needed |
| Task is a self-contained subtask with its own exploration | Subagent | Keeps exploratory noise out of the parent's context |
| Multiple similar subtasks across independent inputs | Subagents (one per input) | Enables parallel execution |
| Subtask needs a narrower or different tool scope than the parent | Subagent | Tool scope is per-subagent, not shared |
Python Notes
# A subagent's description is what the parent model reads to decide
# when to invoke it - treat it like a tool description, not a comment.
SubagentConfig(
name="test-runner",
description="Runs the project's test suite and reports only failing tests.",
allowed_tools=["bash"],
tool_config={"bash": {"allowed_commands": ["pytest"]}},
)Parameters & Return Values
| Parameter | Type | Description |
|---|---|---|
name | str | Identifier the parent uses to invoke this subagent |
description | str | Tells the parent model when this subagent applies |
allowed_tools | list[str] | Tool scope for this subagent, independent of the parent |
tool_config | dict | Per-tool scoping for this subagent's tools |
Gotchas
- Delegating a task that isn't actually independent. Splitting a task with real dependencies between two subagents forces you to sequence them manually and pass results between them yourself. Fix: only split tasks along genuinely independent boundaries; keep dependent steps in one agent or in sequence.
- Writing a vague subagent description. A description like "helper agent" gives the parent model no signal for when to invoke it. Fix: write descriptions the way you'd write a tool description: specific, action-oriented, and scoped to one job.
- Assuming a subagent sees the parent's full history. Subagents typically start with a fresh context for their invocation, not the parent's entire conversation. Fix: pass any context the subagent actually needs explicitly in the prompt you give it.
- Over-splitting a simple task into many subagents. Each subagent invocation adds overhead (a fresh context, a full internal loop); a task simple enough for one tool call doesn't need a subagent. Fix: reserve subagents for genuinely independent, non-trivial subtasks.
- Granting a subagent the parent's full tool scope by default. Copying the parent's
allowed_toolsinto every subagent defeats the purpose of scoping work narrowly. Fix: scope each subagent to only what its specific job requires. - Not handling a subagent failure. If a subagent's internal loop fails or times out, the parent needs to decide what to do with an incomplete result. Fix: treat subagent results like any other tool result that can fail, and handle that case in your prompt or application logic.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| A single agent with more tools | The task needs one more capability, not independent exploration | The subtask would otherwise flood context with irrelevant reasoning |
| Sequential tool calls in one agent | Steps genuinely depend on each other's output | Steps are independent and could run concurrently |
| Separate top-level query() calls | Subtasks are fully unrelated, no shared parent task | Subtasks are part of one coherent parent task that needs a combined result |
FAQs
What exactly does a subagent isolate?
- Its own context window, separate from the parent's.
- Its own tool scope, independent of the parent's
allowed_tools. - Its intermediate reasoning and tool calls, which never surface in the parent's context, only the final result does.
How does the parent decide when to invoke a subagent?
The same way it decides to call any tool: the model reads the subagent's description alongside the conversation and chooses to invoke it when the task matches.
Can subagents run in parallel?
Yes, when they're independent of each other. Subagents whose tasks don't depend on one another's output can be dispatched and awaited concurrently.
Does a subagent see the parent's conversation history?
Not by default; it typically starts with a fresh context for its own invocation. Pass any needed context explicitly in the prompt you give the subagent.
Can a subagent have its own subagents?
The SDK's subagent model supports nesting in principle, since each subagent runs its own internal tool-use loop, but deep nesting adds real overhead and debugging difficulty; most tasks are well served by one level.
What tools can a subagent use?
Whatever you scope it to via its own allowed_tools and tool_config, independent of the parent's tool scope; a subagent is not automatically granted the parent's tools.
How is delegating to a subagent different from just calling a tool?
A tool call executes one discrete action and returns a result. A subagent invocation runs a full internal tool-use loop, potentially calling several tools and reasoning across multiple steps, before returning one final result.
When is a task too small for a subagent?
When it's a single, simple action that a direct tool call would handle just as well; the overhead of a fresh context and internal loop isn't worth it for trivial steps.
What happens if a subagent's task fails?
The failure (or an incomplete/error result) comes back to the parent as the observation from that invocation, and the parent's own loop has to decide how to proceed, the same way it would handle any failed tool call.
Can I limit how many subagents run at once?
Concurrency limits are an application-level concern; you control how many subagent invocations you dispatch at once in your own orchestration code around query().
Related
- The Claude Agent SDK Mental Model - how subagents fit into the loop conceptually
- Enabling File Edit, Bash, and Web Tools in the Agent SDK - scoping tools, applicable per subagent too
- Claude Agent SDK Configuration Options Reference - comparing subagent and other options
- Adding Human-in-the-Loop Checkpoints to an Agent SDK Workflow - checkpoints inside delegated work
- Claude Agent SDK Best Practices - production guidance on scoping and delegation
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the Claude Agent SDK (latest release, Python and TypeScript). Model names, SDK versions, and pricing move quickly - verify current specifics at platform.claude.com/docs before relying on them.