A close read of subagents — isolated Claude instances the main conversation spawns for focused work, and everything that governs what they see, what they can touch, and what comes back.
A subagent isn't a shortcut for calling a tool — it's a separate agent instance, and that separation is the entire point.
A subagent can read dozens of files; only its final message reaches the parent, not every file it opened.
Style, security, and coverage checks run at once instead of one after another.
Domain expertise — SQL migration rules, security checklists — lives in the subagent, not cluttering the main prompt.
A doc reviewer given only Read and Grep literally cannot edit a file, by omission — not by asking politely.
Unless it's a fork, a subagent's context starts fresh — but "fresh" isn't "empty." Here's exactly what it does and doesn't get.
| Receives | Does not receive | |
|---|---|---|
| Prompt | Its own system prompt + the Agent tool's task string | The parent's system prompt |
| History | — | The parent's conversation history or tool results |
| Project context | CLAUDE.md hierarchy (Explore/Plan skip this) | — |
| Skills | Only what's listed in its skills field | Any skill content already loaded in the parent |
| Tools | Parent's set, or the subset in tools | Anything omitted — no prompt, no error, just absent |
Filesystem-based at .claude/agents/<name>.md, or defined inline via the SDK's agents option — same shape either way.
--- name: code-reviewer description: Security and quality review specialist. tools: Read, Grep, Glob model: sonnet --- You are a code review specialist. Identify security issues, check performance, verify standards.
AgentDefinition( description="Expert code review...", prompt="You are a code review...", tools=["Read", "Grep", "Glob"], model="sonnet", ) # a programmatic agent overrides # a filesystem one with the same name
Before writing a single custom agent, three types are already available.
Skips CLAUDE.md and git status to stay fast. Write and Edit are denied outright. One-shot — no agentId, can't be resumed.
Same read-only tool set and the same CLAUDE.md/git-status skip as Explore. Also one-shot.
Every subagent tool available. Loads the full CLAUDE.md hierarchy and a git status snapshot. Resumable via its agentId.
Claude reads a subagent's description the same way it reads a skill's — as the entire basis for an autonomous decision.
Since v2.1.198, omitting the mode defaults to background — the reverse of earlier behavior.
| Foreground | Background | |
|---|---|---|
| Main conversation | Blocks until the subagent finishes | Continues immediately; result lands as a later notification |
| Permission prompts | Passed straight through to you | Surface in the main session, naming the requesting subagent |
| Built-in tool set | Same as the main session | A fixed, reduced list — MCP tools are unaffected |
A tool left out of tools isn't refused with a prompt or an error — it simply isn't in the subagent's session at all.
Can examine code but structurally cannot modify or execute anything.
Can run commands and parse their output, nothing more.
A subagent's skills field injects full skill content at startup — the opposite of the main session's on-demand matching.
Every skill's description sits in context; Claude decides at runtime whether one matches, then loads its body.
Listed skills' full bodies are already there when the subagent's first turn begins — unlisted skills stay reachable through the Skill tool.
A subagent can spawn subagents of its own — one prompt really can grow into a tree. Three caps keep that tree bounded.
Default 3 layers below the main agent. At the limit, the bottom subagent just does the work itself instead of delegating further.
Default 20 running at once. Past it: "Concurrent subagent limit reached" — ultracode sessions are exempt.
No default limit. At the cap: no new subagents, running background ones stop, query ends with error_max_budget_usd.
A resumed subagent keeps its full history — every prior tool call, result, and reasoning step, not just its final summary.
A subagent's final message could contain text engineered to look like a system instruction. Since v2.1.210, Claude Code neutralizes that before the parent ever reads it.
A backslash goes in right after the opening angle bracket. Nothing is deleted.
Gets a backslash before the colon, so it can't fake a conversation-turn boundary.
settings.json, bypassPermissions — kept as written, not touched.
Everything here — frontmatter, built-in types, foreground vs. background, depth and spend caps, output scanning — exists to make that one exchange safe to repeat at scale: a parent that stays lean, and a subagent whose mess never leaves the room it made it in.