AI agents
Ren Okabe10 min read92 views

Claude Agent SDK Subagents: Isolate Context and Run Tasks in Parallel (2026)

Define subagents in the Claude Agent SDK with the agents parameter and AgentDefinition: isolate context, run focused subtasks in parallel, restrict each one's tools, and confirm delegation. Runnable Python and TypeScript, August 2026.

Updated on August 21, 2026

Rows of dark server racks lit with blue accents, representing multiple Claude subagents running in parallel and in isolation
Rows of dark server racks lit with blue accents, representing multiple Claude subagents running in parallel and in isolation
On this page

Quick answer

(August 2026) Subagents in the Claude Agent SDK are separate agent instances your main agent spawns to handle focused subtasks. You define them programmatically with the agents parameter on ClaudeAgentOptions, where each entry is an AgentDefinition with a description, a prompt, and an optional tools list. Include "Agent" in allowed_tools so Claude can invoke them without a permission prompt. Each subagent runs in its own fresh context and returns only its final message to the parent, which is how you keep a long research or review task from flooding the main conversation, and how you run several subtasks in parallel. Every snippet below is copy-paste runnable against the 2026 SDK.

A single Claude agent works fine until one turn has to do three unrelated things at once: read forty files, run the test suite, and check the docs. Stuff all of that into one conversation and two things break. The context window fills with intermediate tool output nobody needs later, and the model starts losing the thread. Subagents fix both. Each one gets a clean context, does its job, and hands back a short answer.

This tutorial is a focused deep dive on subagents alone. It assumes you already have a working agent (the Python quickstart covers that) and that you have wired custom tools before. Here you learn to define subagents in code, control what each one can touch, run them in parallel, confirm delegation actually happened, and decide when a subagent is the wrong tool.

Anthropic logo All code is verified against the current claude-agent-sdk (Python) and @anthropic-ai/claude-agent-sdk (TypeScript) references and the Anthropic subagents documentation as of August 2026.

What a subagent actually is

A subagent is a separate agent instance with its own conversation. The main agent calls it through the built-in Agent tool, waits (or does not wait) for a result, and receives that subagent's final message back as the tool result. Three properties are the whole reason they exist:

  • Context isolation. Each subagent starts with a fresh context window. Its intermediate tool calls and results stay inside it, and only the final message returns to the parent. A research-assistant can read dozens of files without any of that content piling up in the main conversation.
  • Parallelization. Independent subagents run concurrently, so three subtasks finish in the time of the slowest one instead of the sum of all three.
  • Specialization. Each subagent has its own system prompt and its own tool set, so you can give a security-scanner deep, specific instructions and read-only tools without adding that noise to the main agent.

You can define subagents three ways: programmatically through the agents parameter, as markdown files in .claude/agents/, or by leaning on the built-in general-purpose subagent Claude can already invoke. This guide uses the programmatic approach, which Anthropic recommends for SDK applications because the definition lives in your code next to everything else. If you landed here looking for the Claude Code CLI rather than the SDK, the markdown-file route is the one you want, and it has its own moving parts (frontmatter fields, which definition wins when a project and a personal file collide, nesting depth): our Claude Code subagents walkthrough covers that surface end to end. The rest of this page is SDK-only.

Prerequisites

You need Python 3.10+ (or Node 20+ for the TypeScript snippets), an Anthropic API key exported as ANTHROPIC_API_KEY, and the SDK installed:

bash
# Python
pip install claude-agent-sdk

# TypeScript
npm install @anthropic-ai/claude-agent-sdk

Define one subagent

Start with a single code-reviewer subagent that has read-only tools. The description is not a comment. Claude reads it to decide when to delegate, so write it for the model. The prompt is the subagent's own system prompt.

python
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition


async def main():
    async for message in query(
        prompt="Review the authentication module for security issues",
        options=ClaudeAgentOptions(
            # "Agent" must be here or the subagent call falls to a permission prompt
            allowed_tools=["Read", "Grep", "Glob", "Agent"],
            agents={
                "code-reviewer": AgentDefinition(
                    description="Expert code review specialist. Use for quality, security, and maintainability reviews.",
                    prompt=(
                        "You are a code review specialist. Identify security "
                        "vulnerabilities, check for performance issues, and "
                        "suggest specific improvements. Be thorough but concise."
                    ),
                    # tools restricts what this subagent can do: read-only here
                    tools=["Read", "Grep", "Glob"],
                ),
            },
        ),
    ):
        if hasattr(message, "result"):
            print(message.result)


asyncio.run(main())

The TypeScript shape is identical, using a plain object instead of AgentDefinition:

typescript
import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "Review the authentication module for security issues",
  options: {
    allowedTools: ["Read", "Grep", "Glob", "Agent"],
    agents: {
      "code-reviewer": {
        description:
          "Expert code review specialist. Use for quality, security, and maintainability reviews.",
        prompt:
          "You are a code review specialist. Identify security vulnerabilities, check for performance issues, and suggest specific improvements.",
        tools: ["Read", "Grep", "Glob"],
      },
    },
  },
})) {
  if ("result" in message) console.log(message.result);
}

That is the entire minimum: a name, a description, a prompt, and "Agent" in your allowed tools.

Know the AgentDefinition fields

description and prompt are the only required fields. The rest are optional, and a few of them are the difference between a demo and a system you can run in production.

Scroll to see more

FieldTypeRequiredWhat it does
descriptionstringYesNatural-language description of when to use this agent. Claude matches tasks against it.
promptstringYesThe subagent's system prompt: its role and behavior.
toolsstring[]NoAllowed tool names. Omit to inherit every tool available to subagents.
disallowedToolsstring[]NoTools to remove. Accepts MCP patterns like mcp__server__*.
modelstringNoModel override. Accepts an alias (opus, sonnet, haiku, inherit) or a full id.
mcpServers(string | object)[]NoMCP servers available to this agent, by name or inline config.
maxTurnsnumberNoCap on agentic turns before the agent stops.
backgroundbooleanNoForce this agent to run as a non-blocking background task.
permissionModePermissionModeNoPermission mode for tool execution inside this agent.

One Python gotcha worth stating plainly: multi-word field names keep their camelCase spelling (disallowedTools, mcpServers, maxTurns) to match the wire format, rather than the snake_case you would expect in Python. tools, prompt, description, and model are single words and read normally.

Run subagents in parallel

This is the payoff. Define three specialists and let the main agent fan out. Because independent subagents run concurrently, a review that touches style, security, and tests finishes in the time of the slowest lane.

python
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition

REVIEW_AGENTS = {
    "style-checker": AgentDefinition(
        description="Checks code style and formatting conventions.",
        prompt="You review code for style, naming, and formatting consistency only.",
        tools=["Read", "Grep", "Glob"],
    ),
    "security-scanner": AgentDefinition(
        description="Finds security vulnerabilities. Use for any auth or input-handling code.",
        prompt="You are a security auditor. Report injection, auth, and data-exposure risks with file and line.",
        tools=["Read", "Grep", "Glob"],
        model="opus",  # spend the capable model where the stakes are highest
    ),
    "test-runner": AgentDefinition(
        description="Runs the test suite and analyzes failures.",
        prompt="You run tests and summarize which failed and why.",
        tools=["Bash", "Read", "Grep"],
    ),
}


async def main():
    async for message in query(
        prompt=(
            "Review the payments module. Run the style-checker, security-scanner, "
            "and test-runner agents, then summarize all three."
        ),
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Grep", "Glob", "Bash", "Agent"],
            agents=REVIEW_AGENTS,
        ),
    ):
        if hasattr(message, "result"):
            print(message.result)


asyncio.run(main())

Naming the three agents in the prompt is explicit invocation: it guarantees Claude uses those subagents instead of leaving delegation to chance. Leave the names out and Claude decides on its own from each description (that is automatic invocation). Explicit is the right default when you know exactly which specialists a task needs.

Understand what a subagent inherits (the number-one gotcha)

A subagent starts with a fresh context window, but it is not empty. The single most common subagent bug is assuming the child can see the parent's conversation. It cannot. This table is worth pinning above your desk:

Scroll to see more

The subagent receivesThe subagent does NOT receive
Its own system prompt (AgentDefinition.prompt)The parent's conversation history or tool results
The Agent tool's prompt string you passed when invoking itThe parent's system prompt
Tool definitions (inherited, or the subset in tools)Preloaded skill content, unless listed in skills
Project CLAUDE.md, when loaded via settingSourcesAnything you assumed was "obvious from earlier"

The practical rule that falls out of this: put everything the subagent needs into the Agent tool's prompt string. File paths, error messages, the decision it is acting on. If it is not in that prompt or the subagent's own system prompt, the subagent has never seen it. Half of "why is my subagent making things up" turns out to be a parent that never told it the file path.

Confirm delegation actually happened

If you only print the final result you cannot tell whether Claude delegated or just answered directly. Claude invokes subagents through the Agent tool, so watch for tool_use blocks whose name is Agent. One versioning wrinkle: the tool was renamed from Task to Agent in Claude Code v2.1.63, and current releases still emit Task in the system:init tools list, so check both names.

python
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition, ToolUseBlock


async def main():
    async for message in query(
        prompt="Use the code-reviewer agent to review this codebase",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Glob", "Grep", "Agent"],
            agents={
                "code-reviewer": AgentDefinition(
                    description="Expert code reviewer.",
                    prompt="Analyze code quality and suggest improvements.",
                    tools=["Read", "Glob", "Grep"],
                )
            },
        ),
    ):
        if hasattr(message, "content") and message.content:
            for block in message.content:
                # Match both names for cross-version safety
                if isinstance(block, ToolUseBlock) and block.name in ("Task", "Agent"):
                    print(f"Subagent invoked: {block.input.get('subagent_type')}")

        # Messages produced inside a subagent carry a parent_tool_use_id
        if getattr(message, "parent_tool_use_id", None):
            print("  (running inside subagent)")

        if hasattr(message, "result"):
            print(message.result)


asyncio.run(main())

Messages that originate inside a subagent's execution carry a parent_tool_use_id, so you can tell parent output from child output in the same stream.

One 2026 behavior change to know: background by default

Two subagent behaviors changed in Claude Code v2.1.198, and the first one surprises people who wrote agent code earlier in the year. Subagents now run in the background by default. An Agent tool call that omits run_in_background launches a background subagent, and Claude sets run_in_background: false only when it needs the result before continuing. Before v2.1.198, omitting that field ran the subagent synchronously.

If you need a specific subagent to always run in the background regardless of what Claude requests, set background=True on its AgentDefinition. The second change: a subagent now inherits the main session's extended-thinking configuration.

When a subagent is the wrong tool

Subagents are not free. Each one is a fresh model context and a round trip. Reach for one only when the isolation or parallelism pays for that cost. Three quick decisions:

  • One big prompt vs. a subagent. If a task is small and its intermediate output is short, keep it in the main agent. Spin out a subagent when the intermediate work is large (dozens of files, a noisy tool log) and the parent only needs the conclusion.
  • Restrict tools instead of adding a subagent. If your only goal is "this step must not write files," you may just want a tighter tools list, not a whole second agent.
  • Dozens or hundreds of agents. Subagents are built for a handful of delegated tasks per turn. For orchestration at real scale, Anthropic points you at the Workflow tool, which moves the coordination into a script outside the conversation. If you are considering how much orchestration to hand-roll at all, the tradeoff is the same one covered in the SDK versus writing the agent loop yourself.

If you use the LangChain stack, its Deep Agents comparison lays out how that harness makes different tradeoffs on subagents and execution environment, which is useful context before you commit to one approach.

Common mistakes

  • Forgetting "Agent" in allowed_tools. Without it, subagent invocations fall through to a permission prompt (or get denied in a non-interactive mode), so Claude quietly answers the task itself. This is the number-one "my subagents never run" cause.
  • A vague description. Automatic invocation matches tasks against the description. "Helper agent" delegates nothing useful. "Security reviewer for auth and input-handling code" delegates the right tasks.
  • Assuming the subagent can see the chat. It cannot. Pass file paths, errors, and decisions in the Agent tool prompt string (see Step 4).
  • Pinning a dated model snapshot in model. Use an alias like opus or sonnet so the subagent does not break when a snapshot ages out.
  • Reaching for subagents to save tokens on a tiny task. The extra context and round trip can cost more than they save. Isolation and parallelism are the reasons to use them, not raw token thrift.

Where to go next

You can now define subagents, restrict their tools, run them in parallel, verify delegation, and tell when a plain prompt would have been better. The natural next step is giving those specialists real capabilities to call, which is exactly what custom tools in the Agent SDK covers. The full field reference and the latest behavior notes live in the Anthropic subagents docs and the Python SDK repo.

Ren Okabe

Written by

Ren Okabe

Ren builds agent infrastructure and writes copy-paste tutorials for engineers shipping LLM tool-use systems.

Frequently asked questions

How do I create a subagent in the Claude Agent SDK?

Add an entry to the agents parameter on ClaudeAgentOptions, mapping a name to an AgentDefinition with a description, a prompt, and an optional tools list. Include "Agent" in allowed_tools so Claude can invoke the subagent without a permission prompt. Claude then delegates to it automatically based on the description, or on demand when you name the agent in your prompt.

What is AgentDefinition in the Claude Agent SDK?

AgentDefinition is the object that configures one programmatic subagent. Only description (when to use it) and prompt (its system prompt) are required. Optional fields include tools, disallowedTools, model, mcpServers, maxTurns, background, and permissionMode. In the Python SDK, multi-word fields such as disallowedTools and mcpServers keep camelCase to match the wire format.

Why does Claude answer directly instead of delegating to my subagent?

The most common cause is leaving "Agent" out of allowed_tools. Without it, subagent invocations fall through to a permission prompt or are denied in a non-interactive mode, so Claude quietly handles the task itself. Add "Agent" to allowed_tools, and write a specific description so automatic invocation can match the task, or name the agent explicitly in your prompt.

Do subagents share context with the main agent?

No. Each subagent starts in a fresh context window. It receives its own system prompt and the Agent tool's prompt string, but not the parent's conversation history, tool results, or system prompt. Put every file path, error message, and decision the subagent needs directly into the Agent tool's prompt string, since it has seen nothing else from the parent.

Do Claude Agent SDK subagents run in parallel and in the background?

Yes to both. Independent subagents run concurrently, so several subtasks finish in the time of the slowest one. As of Claude Code v2.1.198 subagents also run in the background by default: an Agent tool call that omits run_in_background launches a background subagent, and Claude sets run_in_background to false only when it needs the result before continuing. Set background=True on an AgentDefinition to force background execution.