In multi-agent systems, giving every agent every tool is an anti-pattern. Each agent should receive exactly the tools it needs — no more, no less. This guide covers the Principle of Least Privilege for tools, the four tool_choice modes, and how coordinator vs. subagent tool sets differ by design.
In an operating room, the surgeon has a scalpel, sutures, and clamps — not a blood pressure monitor or IV pump. The anesthesiologist has the drug dispensary and vital monitoring equipment — not surgical instruments. The scrub nurse maintains the sterile instrument tray and hands tools to the surgeon on request.
Each role has exactly the tools for their function. Giving the surgeon an IV pump would create confusion and risk. Giving the anesthesiologist a scalpel adds no value and increases scope for error.
In multi-agent systems: the coordinator agent is the surgeon directing the operation. Subagents are specialists. The tool distributor (your architecture) is the scrub nurse — ensuring each agent only holds the tools they need for their specific task.
The Principle of Least Privilege (PoLP) — borrowed from security engineering — states that every agent should have access to only the tools required to perform its designated function, and nothing more.
| If You Give Too Many Tools | If You Scope Tools Correctly |
|---|---|
| Claude may choose incorrect tools for the task, leading to unexpected side effects | Claude deterministically selects from a small, relevant set |
| Token budget wasted on tool descriptions Claude never uses | Context window used efficiently; more space for actual task data |
| Security risk: write/delete tools accessible where only read is needed | Write operations gated to agents explicitly designed for mutation |
| Harder to audit what each agent is capable of doing | Clear blast radius per agent; easier to reason about failure modes |
| Tool selection ambiguity causes Claude to ask clarifying questions | Unambiguous tool set leads to more decisive, autonomous execution |
Each tool definition in the API request consumes tokens from the context window. A tool schema with a detailed description, parameter definitions, and examples can cost 100–300 tokens. In a system with 20 tools, that’s 2,000–6,000 tokens consumed before any task content. Scoping tools per agent directly reduces this overhead and leaves more context for actual work.
In a multi-agent system, the coordinator and each subagent have fundamentally different responsibilities — and therefore fundamentally different tool sets.
| Agent Type | Typical Tools | Tools it Should NOT Have |
|---|---|---|
| Coordinator |
spawn_subagent, delegate_taskaggregate_results, check_statusHigh-level orchestration verbs only |
Low-level execution tools (web_search, file_write) Domain-specific data tools (payments_api, db_query) — it delegates these, not executes them |
| Research Subagent |
web_search, fetch_urlread_document, search_knowledge_base
|
write_file, send_emailupdate_database — read-only scope
|
| Synthesis Subagent |
read_text, format_reportwrite_file (final output only)
|
web_search — synthesis uses provided data, not new fetchesspawn_subagent — not its role to orchestrate
|
| Execution Subagent |
run_command, update_databasesend_notification
|
web_search — it executes, not researchesspawn_subagent — execution is terminal, not orchestrating
|
Coordinator agents use orchestration verbs: spawn, delegate, aggregate, route, check. Subagent tools use execution verbs: search, read, write, send, run. If a coordinator needs an execution verb tool, it’s a design smell — that work should be delegated to a subagent.
The tool_choice parameter controls whether and how Claude decides to use tools in a given API call. It has four distinct modes, each with specific use cases:
Claude decides whether to call a tool or respond in text. Default behavior. Best for open-ended agentic tasks where Claude should use judgment.
Claude MUST call at least one tool, but can choose which one. Prevents pure text responses when a tool call is required by the workflow.
Claude MUST call the specific named tool. Used to force a particular structured output format, data extraction, or mandatory action.
Claude cannot call any tools. Forces a plain text response even if tools are present in the request. Used for pure summarization or explanation steps.
| Mode | Use Case | When to Use |
|---|---|---|
| auto | Open-ended agentic tasks | When Claude should autonomously decide the best approach |
| any | Forcing structured interaction | When any tool call is acceptable but text-only is not (e.g., data must be fetched) |
| tool | Mandatory structured output | Extracting JSON-structured data via a schema-tool; guaranteed call to a specific tool |
| none | Pure text generation | Summarization, explanation, or reasoning steps where no side effects should occur |
A key exam pattern: using tool_choice: {"type": "tool", "name": "extract_data"} combined with a JSON schema tool definition to guarantee structured output. Claude is forced to call the tool, producing a validated JSON response every time. This is more reliable than asking Claude to “respond in JSON” in free text, because the tool schema enforces field types at the API level.
Choosing the right tool_choice mode requires understanding what the agent step needs to guarantee:
| Situation | Correct Mode | Reasoning |
|---|---|---|
| Agent must autonomously complete a multi-step task | auto | Claude needs full discretion to choose tools or respond based on context |
| Workflow step must always produce a tool call (not text) | any | Ensures Claude doesn’t skip a required data-fetch step with a text response |
| Extracting structured data that must match a JSON schema | tool (specific) | Guarantees schema adherence; Claude forced to populate defined fields |
| Summarization, reasoning, or explanation step (no side effects) | none | Prevents accidental tool calls during pure reasoning steps |
| Onboarding/confirmation step before taking irreversible action | none | Forces explicit text response before proceeding to execution tools |
Defaulting to tool_choice: auto for every agent step is the most common mistake. For extraction steps, Claude may choose to respond in text rather than calling the schema tool, producing unstructured output. For synthesis steps, it may call a web_search tool unnecessarily, going beyond its intended scope. Match tool_choice to the guarantee you need for that specific step.
# Define a schema tool for structured extraction extract_tool = { "name": "extract_company_data", "description": "Extract structured company information from the provided text.", "input_schema": { "type": "object", "properties": { "company_name": {"type": "string"}, "founded_year": {"type": "integer"}, "revenue_usd_millions": {"type": "number"}, "headquarters_country": {"type": "string"} }, "required": ["company_name", "founded_year"] } } # Force Claude to ALWAYS call extract_company_data response = client.messages.create( model="claude-opus-4-5", max_tokens=1024, tools=[extract_tool], # Only this tool in scope tool_choice={ # FORCE this specific tool "type": "tool", "name": "extract_company_data" }, messages=[{"role": "user", "content": document_text}] ) # Response is GUARANTEED to be a tool_use block with valid JSON extracted = response.content[0].input # Always a dict matching schema
# Research subagent: scoped to read-only tools, auto choice research_tools = [ {"name": "web_search", "description": "Search the web for information", ...}, {"name": "fetch_url", "description": "Fetch content from a URL", ...}, {"name": "search_knowledge_base", "description": "Search internal KB", ...} ] # NOTE: write_file, send_email etc. deliberately excluded response = client.messages.create( model="claude-opus-4-5", max_tokens=4096, tools=research_tools, tool_choice={"type": "auto"}, # Claude decides which to call system="You are a research agent. Search for information as needed.", messages=messages )
# Synthesis phase 1: Pure reasoning, no tools allowed synthesis_response = client.messages.create( model="claude-opus-4-5", max_tokens=2048, tools=[write_report_tool], # Tool available but... tool_choice={"type": "none"}, # ...cannot be called yet system="Analyze the research findings below and identify key themes.", messages=[{"role": "user", "content": aggregated_research}] ) # Returns pure text reasoning - no tool calls possible themes = synthesis_response.content[0].text # Synthesis phase 2: Now allow write tool to produce final output final_response = client.messages.create( model="claude-opus-4-5", max_tokens=4096, tools=[write_report_tool], tool_choice={"type": "tool", "name": "write_report"}, # Force final output messages=[..., {"role": "user", "content": f"Based on themes: {themes}. Write the report."}] )
Passing the entire tool registry to every agent. Wastes tokens, creates security risk, and introduces ambiguity. Each agent should receive a purpose-built tool subset.
Giving the coordinator web_search or write_file. Coordinators orchestrate, they don’t execute domain work. This creates a single point of failure and prevents proper task specialization.
Using auto for extraction steps. Claude may produce free text instead of the required structured JSON. Extraction must use forced tool_choice: tool for reliability.
Giving a research subagent a write_file or send_email tool. Accidental execution of write operations during research can cause unintended side effects.
Allowing tool access during a pure reasoning or summarization pass. Claude may invoke tools unnecessarily, extending latency and consuming budget on unneeded operations.
Giving a leaf subagent the ability to spawn further subagents. This creates uncontrolled recursive delegation and breaks the coordinator’s ability to track and manage execution.
Define tool lists for each agent role: coordinator tools, research tools, extraction tools, synthesis tools. Never share a single list across all roles.
Ask: “What must this step guarantee?” Structured output → forced tool. Any tool call required → any. Reasoning only → none. Open-ended task → auto.
All agents that gather information (research, search, fetch) should have exclusively read-only tool sets. Mutation tools only for execution agents.
Scenario: A multi-agent research pipeline has a coordinator, a web research subagent, and a report synthesis subagent. Which tool_choice should the coordinator use when delegating to the research subagent? Which should the synthesis subagent use when producing its first reasoning pass?
Answer: Coordinator uses auto when delegating (Claude decides if/how to use delegation tools). Research subagent uses auto (Claude chooses which search tools to call and when). Synthesis subagent first pass uses none (pure reasoning, no tool calls), then tool_choice: tool for write_report in final step.
Common distractor: “Use tool_choice: any for the synthesis subagent” — wrong. any forces some tool call but doesn’t prevent the wrong tool from being called (e.g., web_search). The synthesis agent should have no web_search in its tool set at all.