Claude Code subagents, and why your tools list gets ignored
Short answer: a subagent is a markdown file with YAML frontmatter that gets its own context window — and since v2.1.198 it runs in the background by default, where it keeps only 19 built-in tools no matter what you wrote in tools.
The docs say the quiet part out loud, one sentence deep in a paragraph nowhere near the tools field: "the same definition can resolve to different tools in the foreground and the background. The removal reports no error." Most "my subagent ignores its tool list" reports are that, not a bug. Here's the rest of what only shows up once you run these in anger.
On this page
What a subagent is, in one screen
A file in .claude/agents/ (project) or ~/.claude/agents/ (personal). YAML frontmatter for configuration, and the markdown body is the system prompt.
---
name: db-migration-checker
description: Reviews SQL migrations for destructive operations.
Use proactively before any migration is applied.
tools: Read, Grep, Glob, Bash
model: inherit
---
You review database migrations. Flag anything that drops a column,
drops a table, or rewrites data in place without a backup step.
Report findings as a list. Do not modify files.
Only name and description are required. The optional fields that matter in practice: tools, disallowedTools, model (defaults to inherit), permissionMode, maxTurns, skills, mcpServers, hooks, memory, background, effort and isolation.
Four loading behaviours worth knowing before you debug anything:
- Precedence, highest first: managed settings →
--agents→ project.claude/agents/→ personal~/.claude/agents/→ plugins. - Duplicate names within one tree are resolved by filesystem read order, and which one wins is not documented. Don't rely on it — rename.
- Plugin subagents silently drop
hooks,mcpServersandpermissionMode. That's a deliberate security boundary, not a bug, but it is silent. - Agent files hot-reload — but a newly created
agents/directory isn't watched until you restart. If your first-ever subagent doesn't appear, that's why.
One more that decides which field to reach for: disallowedTools is applied first, and tools is resolved against what's left. Set both and the overlap is gone.
isolation: worktree gives the subagent a temporary git worktree — but it branches from your default branch, not the parent session's HEAD. If you're working on a feature branch and delegate with worktree isolation, the subagent doesn't see your work.Your tools list goes through two filters
This is the section that resolves most confused bug reports. A subagent inherits the main conversation's tools, then two filters run.
Filter one — removed from every subagent
Present even if you name them explicitly in tools:
AskUserQuestion, EndConversation, EnterPlanMode, ExitPlanMode (unless permissionMode: plan), ScheduleWakeup, TaskOutput, WaitForMcpServers, Workflow, and Agent once the subagent is at the depth limit.
The first one explains a common surprise: a subagent cannot ask you a question. If the delegated task is ambiguous, it guesses. Resolve ambiguity in the prompt, not by hoping it'll check in.
Filter two — background subagents, which is the default
A background subagent keeps every MCP tool, but only these built-ins:
Read, Grep, Glob, Bash, PowerShell, Edit, Write, NotebookEdit, WebFetch, WebSearch, TodoWrite, Skill, ToolSearch, EnterWorktree, ExitWorktree, Monitor, TaskStop, SendMessage, Artifact.
"Claude Code removes every other built-in tool from a background subagent, whether inherited or listed in the tools field, so the same definition can resolve to different tools in the foreground and the background. The removal reports no error unless it leaves the tools list resolving to nothing."
So: no error, no warning, and the same file behaves differently depending on a decision Claude makes at spawn time. Since v2.1.198 the default is background — foreground happens only when Claude needs the result before it can continue.
The fix is one line of frontmatter:
---
name: my-agent
description: ...
background: false # force foreground; full tool set
---
Forks are the exception: /subtask skips both filters and gets the main conversation's exact tool pool. Teammates in agent teams additionally keep TaskCreate, TaskGet, TaskList, TaskUpdate, CronCreate, CronDelete and CronList.
background: false and run it again. If the behaviour changes, filter two was your problem — and the transcript will never have said so.The three limits, and why community advice is stale
These are three separate limits, all added recently, all with environment variables. Most blog posts and Reddit threads predate them and say something like "about ten, not configurable, no recursion." That's out of date.
| Limit | Default | Environment variable |
|---|---|---|
| Concurrently running | 20 | CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS (v2.1.217+) |
| Total per session | 200 | CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSION (v2.1.212+) |
| Nesting depth | 3 | CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH (v2.1.217+) |
// .claude/settings.json
{ "env": {
"CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS": "8",
"CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH": "2"
} }
Behaviour at the edges, which is where the practical differences are:
- Hitting the concurrency limit is a failure, not a queue. Spawning fails with
Concurrent subagent limit reachedand the error tells Claude not to retry. Spawning works again once something finishes. - Finished subagents still count toward the session total. 200 is a budget for the whole conversation, not a live count.
/clearresets it. - At the depth limit, Claude Code withholds the
Agenttool — a subagent at the bottom does the work itself and returns one summary. Set depth to1to turn nesting off entirely. - Only the top-level subagent's summary reaches you. Nested output never surfaces in your main conversation.
- Sessions with ultracode active are exempt from the concurrency limit.
And one that costs people an afternoon: you cannot control concurrency from a prompt or from CLAUDE.md. Telling Claude "run at most three at a time" is a suggestion to a language model. The environment variable is the only thing that enforces it.
What a subagent can actually see
Each subagent gets an independent context window. It cannot see your conversation, the files you've read, or the skills you invoked. That isolation is the point — it's why delegation keeps your main context clean — but it means everything the subagent needs has to be in the prompt you send it. The most common failure with subagents isn't configuration, it's a prompt written as if the reader had been in the room.
What it does receive:
- Its own system prompt (the markdown body)
CLAUDE.mdfrom every level- A snapshot of
git status - The full text of any skills named in
skills: - A roster of its siblings, when
SendMessageis available (v2.1.206+)
Two exceptions: the built-in Explore and Plan agents skip CLAUDE.md and the git snapshot, and that isn't configurable. If your project conventions live in CLAUDE.md, an Explore agent doesn't know them.
Also worth planning around: the context window is sized by the subagent's own model. Delegating a large-file review to a cheaper model gives you a smaller window as well as a weaker reader, which is usually the opposite of what you wanted.
The parent only ever receives the subagent's final message. The full transcript is on disk if you need to see what actually happened:
~/.claude/projects/<project>/<session-id>/subagents/agent-<agent-id>.jsonl
What fan-out costs
Parallel subagents are not free, and the multiplier is worse than the headcount suggests. Each subagent resends its system prompt and tool schemas every turn, and simultaneous cold spawns compete for cache writes rather than sharing them.
The one public measurement we're aware of — Systima's "The Subagent Tax", July 2026 — put the same task at roughly 2.6× the tokens with two subagents and 3.2× with five, against a sequential baseline. Treat the direction as sound and the exact numbers as soft: the authors note their runs went through a caching proxy.
The practical takeaways survive the uncertainty:
- Stagger cold fan-outs rather than starting ten at once. Sequential spawns can reuse cache that simultaneous ones destroy.
- Parallelism buys wall-clock time, not tokens. If you're rate-limited rather than impatient, delegating is the wrong move.
- Narrow
toolslists genuinely help, because the schemas are re-sent per turn. - There is no per-subagent cost accounting API today (#11008 is the open request), and #43945 reports
/costomitting background subagent spend. Budget with a margin.
Known problems
Open issues with reaction counts as of 2026-07-31. The first one is the most consequential thing on this page after the tool filter.
| Issue | 👍 | What happens |
|---|---|---|
| #80988 | 37 | v2.1.219 injects a system prompt saying "Do not call the AgentTool unless the user requested it." Reported by extracting strings from the binary. It quietly overrides delegation policy written in CLAUDE.md, and there is no opt-out. Open, filed 2026-07-24. |
| #18950 | 69 | Skills and subagents don't inherit user permissions from settings.json |
| #43869 | 29 | Model routing ignores the documented precedence and falls back to the parent's model |
| #37793 | 23 | prompt is too long when many MCP servers are connected |
| #66023 | — | 46 Opus subagents spawned in one go, roughly 3M tokens, no cost confirmation |
#80988 is the one to check your version against. If your subagents stopped being invoked automatically and nothing in your configuration changed, that prompt injection is the likeliest explanation — and asking explicitly ("use the db-migration-checker subagent") still works.
Rules of thumb
What we'd tell someone starting today, in order of how much time it saves:
- Write
descriptionfor the dispatcher, not for humans. It is the only thing Claude reads when deciding whether to delegate. "Use proactively when X" works; a job title doesn't. - Set
background: falseon anything with a specific tool list. Otherwise filter two edits your configuration without telling you. - Put the context in the prompt. The subagent has never seen your conversation and cannot ask.
- Cap concurrency with the env var if you care about spend. Prompts don't bind.
- Delegate for context isolation first, speed second. Reading 40 files to answer one question is the case that pays; a task you could do inline usually isn't.
- Pin your version when writing anything down. Half the advice online is not wrong, it's from March.
Where we fit, briefly
We build UFO, so weigh this accordingly. Nothing here needs us — subagents are a Claude Code feature and every fix above is a frontmatter or env change.
We know this corner in detail because UFO's agents run on subagents in production, on machines people have paired, unattended. The tool filter cost us an afternoon before we found that sentence in the docs, which is most of why this page exists.
Where UFO is a genuinely different shape: subagents are ephemeral and invisible — one summary comes back, the transcript is a file on disk, and nobody else on your team ever sees it. UFO is a team chat where agents are members of channels alongside people, on machines you pair, with task cards for work that outlives a session. If what you want is one Claude Code session delegating work, stay where you are; subagents already do that well.
Sources
Behaviour, quotes, defaults and version numbers are from Anthropic's official documentation, read 2026-07-31 against Claude Code v2.1.219. Issue links go to the original reports.
- code.claude.com/docs/en/sub-agents — the reference, and the source of every quote above
- code.claude.com/docs/en/settings — where the environment variables go
- code.claude.com/docs/en/hooks —
SubagentStart/SubagentStop, covered in our hooks guide - github.com/anthropics/claude-code/issues
Not affiliated with Anthropic. If something here is wrong or has gone stale, tell us and we'll fix it.