← BlogAI Agents

Agent teams in Claude Code, and subagents in the Agent SDK

Isaac Kargar4 min read

  • AI Agents
  • Claude
  • SDK
  • Multi-Agent

Introduction

Claude Code has an experimental Agent Teams feature for interactive sessions. A lead session can delegate independent work to teammates, and the teammates can communicate with the lead and with one another. The Claude Agent SDK provides a different building block: a program can start focused subagent queries and combine their results.

These two modes solve related problems but have different lifecycle rules. Use subagents for focused work that reports back to a coordinator. Use teams when workers need to discuss findings and coordinate directly.

When a team is useful

A single agent is a good fit for a short, linear task. A team is useful when independent investigations can run in parallel or when one worker’s finding should change another worker’s work. Code review, competing bug hypotheses, and work that spans separate layers are common examples.

Teams use more context and tokens than a single session, so give every worker a bounded role and a concrete deliverable. A lead should also decide which files each worker may edit before the work starts.

Current team lifecycle

The current Claude Code documentation describes Agent Teams as an interactive feature. Enable the experiment, start an interactive Claude Code session, and ask the lead in ordinary language to create a team and delegate work.

export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
claude

The current lifecycle is:

  1. Start an interactive lead session and describe the team task.
  2. Ask the lead to delegate independent assignments to teammates.
  3. Teammates work in separate contexts and send findings to the lead or to one another.
  4. The lead reviews the evidence and synthesizes the result.
  5. Claude Code manages team setup and cleanup when the session ends.

The current workflow does not require users to issue lifecycle commands.

Agent Teams require an interactive session. A non-interactive claude -p invocation and an Agent SDK query do not spawn teammates. In those environments, an instruction to use a team should be implemented as ordinary subagent work or handled by the coordinator.

A bounded interactive task

The following prompt gives each role a separate deliverable and limits file overlap. It leaves permission decisions to the normal Claude Code approval flow.

Act as the lead for this repository task. Delegate three independent reviews:

1. Ask an analyst to map the current command interface and report the public
   functions that a small task CLI should expose.
2. Ask an implementer to make the CLI changes in cli.py after reading the
   analyst's interface report. The implementer owns cli.py only.
3. Ask a tester to add and run tests after reading the interface report and
   the implementation. The tester owns test_cli.py only.

Have the workers message the lead when they finish. Ask the tester to report
the exact test command and result. Review the changed files yourself before
you summarize the work. Do not claim a test passed without its output.

The lead may ask workers to coordinate directly when a dependency appears. For example, the tester can send a failing case to the implementer and the implementer can report the fix to the lead. The role instructions match the work the workers can actually perform; they do not require a teammate to wait for a lifecycle command that no longer exists.

A representative task with this shape produced a CLI with list, add, done, and stats commands. The tester reported a passing test suite after the implementation was available, and the documentation writer described the resulting interface from the files. That is an observed workflow outcome, not a benchmark for team quality.

Permissions and file ownership

Teammates inherit the lead’s permission settings. Use the narrowest permissions that let a role complete its assignment, and keep unrelated files outside its ownership. A setting that skips permission checks gives every teammate the same broad authority as the lead, which is useful only in a deliberately isolated environment.

Before delegating edits, state the ownership boundary in the task. The lead remains responsible for reviewing the diff, running the relevant checks, and deciding whether a worker’s result is ready to use.

Subagents with the Claude Agent SDK

The SDK is a programmatic way to run a focused Claude query. A Python coordinator can start two independent subagents and then combine their reports. This is delegation, not Agent Teams: the SDK session does not create interactive teammates or provide a shared team mailbox.

Install the SDK in the environment used by the coordinator and keep the version in the application’s dependency file.

uv add claude-agent-sdk

The message stream has distinct types. Assistant messages contain text and tool-use blocks. User messages contain tool-result blocks returned by tools. A result message contains the final result and accounting fields such as total_cost_usd.

import asyncio
from pathlib import Path

from claude_agent_sdk import (
    AssistantMessage,
    ClaudeAgentOptions,
    ResultMessage,
    TextBlock,
    ToolResultBlock,
    ToolUseBlock,
    UserMessage,
    query,
)


def block_text(content) -> str:
    """Extract text from a string or a list of SDK content blocks."""
    if isinstance(content, str):
        return content
    if not isinstance(content, list):
        return str(content or "")
    parts = []
    for block in content:
        value = getattr(block, "text", None)
        if value:
            parts.append(value)
        elif isinstance(block, dict) and block.get("text"):
            parts.append(block["text"])
    return "\n".join(parts)


async def run_subagent(prompt: str, cwd: str = ".") -> dict:
    """Run a focused, read-only subagent and return its report and cost."""
    report_parts = []
    tool_results = []
    tool_calls = []
    result_message = None
    options = ClaudeAgentOptions(
        cwd=str(Path(cwd).resolve()),
        allowed_tools=["Read", "Glob", "Grep"],
        permission_mode="default",
        max_turns=20,
    )

    async for message in query(prompt=prompt, options=options):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if isinstance(block, TextBlock):
                    report_parts.append(block.text)
                elif isinstance(block, ToolUseBlock):
                    tool_calls.append(block.name)
        elif isinstance(message, UserMessage):
            for block in message.content:
                if isinstance(block, ToolResultBlock):
                    tool_results.append(block_text(block.content))
        elif isinstance(message, ResultMessage):
            result_message = message

    return {
        "report": "\n".join(part for part in report_parts if part),
        "tool_calls": tool_calls,
        "tool_results": tool_results,
        "result": result_message.result if result_message else "",
        "total_cost_usd": result_message.total_cost_usd if result_message else None,
    }


async def review_repository(cwd: str = ".") -> list[dict]:
    prompts = [
        "Inspect the command interface and report the public functions and risks. Do not edit files.",
        "Inspect the test layout and report missing cases for the command interface. Do not edit files.",
    ]
    return await asyncio.gather(*(run_subagent(prompt, cwd) for prompt in prompts))


if __name__ == "__main__":
    reports = asyncio.run(review_repository())
    for report in reports:
        print(report["report"])
        if report["total_cost_usd"] is not None:
            print(f"total cost: ${report['total_cost_usd']:.4f}")

The two branches in the stream are intentional. Tool use is read from AssistantMessage; tool results are read from UserMessage. Reading tool results from the assistant branch loses the returned content. The final accounting value is ResultMessage.total_cost_usd, not cost_usd.

The coordinator can use the returned reports as input to a final synthesis query. Keep the report’s source file and line references when the final answer makes a claim about the repository. If an implementer needs to edit, give that call a separate permission policy and a narrower file scope, then review its diff before using the result.

Choosing the mode

Choose an SDK subagent when one coordinator owns the workflow and each worker can return a self-contained report. Choose an interactive team when workers need to exchange findings while the task is in progress. For either mode, define the task boundary, limit permissions, preserve evidence, and test the resulting work.

References

Work with Nazmi

Build your AI system with Nazmi.

Tell us what you are building, what exists today, and where your team needs help.

Start a conversation or book a 20-minute call →