What it is
An allowedTools list is the SDK-enforced whitelist of tools a subagent can call. It is declared at spawn time as part of the subagent's config: research subagents typically get [Read, WebSearch, Bash], verification gets [Read, WebSearch, Bash] plus a fact-check rubric, and synthesis gets [Read] only. The SDK rejects any tool call outside the list at the runtime level. You don't have to hope the subagent respects a prompt suggestion; the contract is tool-based, not language-based.
Isolation means each subagent runs in a fresh context window with no inherited messages, no parent chat history, and no shared memory between invocations. The coordinator passes context explicitly in the subagent's task prompt: User asked: [query]. Key context: [pinned facts]. Your task: [focused goal]. The subagent's intermediate work (file reads, searches, tool calls) lives in that nested context and is discarded when the subagent returns. Only the final structured summary comes back.
These two mechanisms together are the architectural payoff of the multi-agent pattern. Tool scoping prevents accidental side effects (a reviewer that should only read cannot Edit; a synthesis subagent cannot re-research mid-narrative). Context isolation prevents bloat (50 file reads in a research subagent cost zero tokens in the coordinator). The SDK enforces both at the contract level, which is why the exam treats allowedTools and isolation as load-bearing primitives, not best-effort guidelines.
How it works
At spawn time, the coordinator constructs a messages.create (or equivalent) call with three scoping fields: system (role + behavior), tools (the whitelist), and messages: [{role: 'user', content: task_prompt}] (the self-contained task). The SDK initializes a fresh agent context with only those inputs. Inside the subagent's loop, every tool call is checked against allowedTools; calls outside the list are rejected before execution. stop_reason signals when the subagent terminates, and the SDK extracts the final message as the summary.
The synthesis subagent is the canonical example. Its allowedTools is [Read] only. No WebSearch. No Bash. This isn't a prompt instruction; it's an SDK-enforced restriction. Even if the synthesis prompt drifts and the model decides it wants to verify a fact mid-sentence, the WebSearch call is rejected at runtime. The model is forced to either render from the verified-claims JSON or acknowledge a gap. That single restriction caps synthesis latency, prevents re-research, and removes a whole class of fabrication paths.
Isolation is enforced by the spawn semantics, not by the SDK alone. Every subagent's messages array starts empty (except for the coordinator-constructed task prompt). There is no resumption, no continue this conversation mode, no second turn. If the coordinator needs more work, it spawns a brand-new subagent with a fresh task. The subagent is stateless by design. The mistake junior teams make is passing the entire coordinator chat history into the subagent for more context. That actively confuses the subagent (it wasn't part of that conversation) and inflates per-subagent cost without benefit.
The 4 decisions
Each row pairs the right answer with the most-tested distractor. The Why column explains the failure mode behind the wrong choice.
| Decision | Right answer | Wrong answer | Why |
|---|---|---|---|
What allowedTools does the synthesis subagent get? | [Read] only. No WebSearch, no Bash, no Edit | Same as research: [Read, WebSearch, Bash] | Synthesis stitches verified findings into a narrative. WebSearch enables re-research mid-narrative (3-5x latency, fabrication risk). Read-only restriction is the architectural detail. |
| How does the coordinator pass context to a subagent? | Explicitly in the task prompt as plain text | By passing the entire coordinator chat history | Subagents do not inherit history. Passing prior messages confuses them (they weren't part of that conversation) and inflates token cost. Embed only what the subagent needs. |
Coordinator's allowedTools is ['web_search', 'read_document']. Coordinator cannot spawn subagents. Why? | Task is missing from the allowedTools list. The SDK requires it for spawning | Subagents need a separate subagent_endpoint config | The Task tool is how the coordinator spawns. If it's not in allowedTools, the SDK blocks the spawn call. This is a verbatim CCA-F practice exam question. |
A code-review subagent has [Read, Grep, Bash, Edit, Write]. It accidentally modifies a config file. What was the design error? | Tool overscoping. A reviewer should never have Edit or Write | The model misunderstood the prompt. Add stronger prose constraints | Tool scope must match the role. Prose can drift; SDK-enforced whitelists cannot. Restrict to the minimum needed for the role. |
Where it breaks
5 failure pairs. Each one is one exam pattern. The fix is always architectural, never a prose plea to the model.
Retriever subagent has [Read, Grep, WebSearch, Bash, Edit] and accidentally writes a file mid-search. Side effect leaks into the host workspace.
Restrict to the minimum needed per role. Retriever gets [Read, Grep, Glob, WebSearch]. Reviewer gets [Read, Grep, Bash]. Synthesis gets [Read]. Lint the configs.
Coordinator passes the full chat history into the subagent for context. Subagent gets confused, references conversations it wasn't part of, and produces incoherent output.
Embed only the facts the subagent needs in a self-contained task prompt. Treat each subagent as starting from zero. Inheritance is not supported and not desirable.
Synthesis subagent has WebSearch in allowedTools. Mid-narrative it decides to re-verify a fact. Latency triples; fabrication risk reappears.
Synthesis allowedTools = [Read] only. SDK rejects WebSearch at runtime. Forces synthesis to render from verified-claims or acknowledge a gap.
Coordinator's allowedTools is ['web_search', 'read_document']. Spawning a subagent throws tool not allowed. The whole architecture is dead on launch.
Add Task to the coordinator's allowedTools. The Task tool is the spawn primitive; without it, no fan-out is possible.
Subagent has no defined output schema. Wanders for 40 turns, returns a plausible essay. Token bill is huge; coordinator can't aggregate cleanly.
Define a structured output format in the subagent's system prompt ({findings: [{claim, sources}]}). The schema doubles as a stopping cue and an aggregation contract.
Exam patterns
5 V2 questions wired to this deep dive. Each shows all 4 options with rationale, the mental model under test, and the priority order across distractors.
Concepts wired
4 primitives compose this sub-pattern. Each card links to the concept page where the primitive is taught in isolation.
Continue the parent
2 more sub-patterns under Multi-Agent Research System. Each one drills into a different load-bearing decision.
Coordinator Routing
How the coordinator decomposes a research query and dispatches subtasks across the hub-and-spoke topology.
The coordinator owns semantic decomposition (not lexical), enumerates every relevant sub-domain before spawning anything, and dispatches research subagents in parallel via a synchronous-fork-then-join pattern. Coverage gaps live in the decomposition step. Not in the subagents.
Read deep dive ↗Structured Claim-Source Mapping
How verified claims get pinned to their source documents in a structured-output schema so the final report cannot fabricate.
Every verified claim emits as a JSON object with claim_id, claim_text, source_url, source_passage, confidence, and notes. The schema is enforced at the verification step. Synthesis renders citations from the schema and cannot invent attributions because the source text is pinned in-record.
Frequently asked
Can a subagent call another subagent (nested spawn)?
How do I pass a large document to a subagent if history isn't inherited?
Read tool can fetch. The coordinator decides which approach. For documents under ~5k tokens, embed directly. For larger, write to disk and pass the path. Either way, the subagent starts with only what's in its task prompt and tools.Are allowedTools enforced at runtime or at config-load time?
tool not allowed. There is no soft-enforcement path; it's hard-rejected.