When you read about agentic frameworks using a “Task tool,” it can sound like a built-in Claude capability. It isn’t. The Task tool is a pattern you implement.
Here’s what that means in practice:
- You define a tool called “Task” in your tools array with a schema
- Claude learns from the description when to call this tool
- When Claude calls “Task,” your orchestration code runs a new agent loop
- The result of that agent loop becomes the tool_result you send back
You control everything: how subagents are initialized, what tools they have access to, how long they can run, and what happens when they fail.
TASK_TOOL = {
"name": "Task",
"description": """Spawn a specialized subagent to handle a specific component of work.
Use this when:
- A subtask requires specialized tools or focus
- A subtask can run independently and in parallel with others
- You need to isolate a complex operation for better accuracy
The subagent will receive its own context and run independently.
You will receive its final result when complete.""",
"input_schema": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "Brief description of what this subagent should accomplish"
},
"prompt": {
"type": "string",
"description": "Complete context and instructions for the subagent. Include everything it needs — it has no access to your context."
},
"allowed_tools": {
"type": "array",
"items": {"type": "string"},
"description": "List of tool names this subagent is permitted to use"
},
"max_tokens": {
"type": "integer",
"description": "Maximum tokens for subagent response (default: 4096)"
}
},
"required": ["description", "prompt"]
}
}
When Claude calls the Task tool, your executor runs:
async def execute_task_tool(task_input: dict) -> str:
"""
This function runs when Claude calls the Task tool.
It spawns a complete subagent with its own agentic loop.
"""
prompt = task_input["prompt"]
allowed_tool_names = task_input.get("allowed_tools", [])
max_tokens = task_input.get("max_tokens", 4096)
# Get the tool definitions for allowed tools
subagent_tools = [
tool for tool in ALL_AVAILABLE_TOOLS
if tool["name"] in allowed_tool_names
]
# Run the subagent's complete agentic loop
try:
result = await run_agent_loop(
messages=[{"role": "user", "content": prompt}],
tools=subagent_tools,
max_tokens=max_tokens,
max_iterations=20
)
return result
except Exception as e:
return f"Subagent failed: {type(e).__name__}: {str(e)}"
Each subagent should have access to only the tools it needs:
# Tool registry
ALL_TOOLS = {
"web_search": web_search_tool,
"database_query": database_query_tool,
"file_read": file_read_tool,
"file_write": file_write_tool,
"send_email": send_email_tool,
"process_payment": payment_tool,
}
# Search subagent — read-only external access only
SEARCH_AGENT_TOOLS = ["web_search"]
# Analysis subagent — can read files and query database
ANALYSIS_AGENT_TOOLS = ["file_read", "database_query"]
# Summary subagent — no external tools needed, just reasoning
SUMMARY_AGENT_TOOLS = []
# Never give a search agent file_write or payment access
The exam specifically tests this: giving agents more tool access than needed is an anti-pattern. It increases the blast radius of errors and reduces reliability of tool selection.
Key Takeaways
- Task tool is your implementation — define the schema, write the executor
- Subagents run complete agentic loops — they can use tools and make decisions
- Coordinator sees only final output — not internal reasoning or intermediate steps
- Scope tool access tightly — minimum necessary for each subagent role
- Handle subagent failures — return error as tool_result, don’t crash