# Tool Call Lifecycle Complete documentation of how tool invocations flow through Claude JSONL logs. ## Lifecycle Overview ``` 1. Assistant message with tool_use block ↓ 2. PreToolUse hook fires (optional) ↓ 3. Tool executes ↓ 4. PostToolUse hook fires (optional) ↓ 5. User message with tool_result block ↓ 6. Assistant processes result ``` ## Phase 1: Tool Invocation Claude requests a tool via `tool_use` content block: ```json { "type": "assistant", "message": { "role": "assistant", "content": [ { "type": "text", "text": "I'll read that file for you." }, { "type": "tool_use", "id": "toolu_01ReadFile123", "name": "Read", "input": { "file_path": "/src/auth/login.ts", "limit": 200 } } ], "stop_reason": null }, "uuid": "msg-001" } ``` ### Tool Use Block Structure | Field | Type | Required | Description | |-------|------|----------|-------------| | `type` | `"tool_use"` | Yes | Block type identifier | | `id` | string | Yes | Unique tool call ID (format: `toolu_*`) | | `name` | string | Yes | Tool name | | `input` | object | Yes | Tool parameters | ### Common Tool Names | Tool | Purpose | Key Input Fields | |------|---------|------------------| | `Read` | Read file | `file_path`, `offset`, `limit` | | `Edit` | Edit file | `file_path`, `old_string`, `new_string` | | `Write` | Create file | `file_path`, `content` | | `Bash` | Run command | `command`, `timeout` | | `Glob` | Find files | `pattern`, `path` | | `Grep` | Search content | `pattern`, `path`, `type` | | `WebFetch` | Fetch URL | `url`, `prompt` | | `WebSearch` | Search web | `query` | | `Task` | Spawn subagent | `prompt`, `subagent_type` | | `AskUserQuestion` | Ask user | `questions` | ## Phase 2: Hook Execution (Optional) If hooks are configured, progress events are logged: ### PreToolUse Hook Input ```json { "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../session.jsonl", "cwd": "/Users/dev/myproject", "permission_mode": "default", "hook_event_name": "PreToolUse", "tool_name": "Read", "tool_input": { "file_path": "/src/auth/login.ts" } } ``` ### Hook Progress Event ```json { "type": "progress", "data": { "type": "hook_progress", "hookEvent": "PreToolUse", "hookName": "security_check", "status": "running" }, "parentToolUseID": "toolu_01ReadFile123", "toolUseID": "toolu_01ReadFile123", "uuid": "prog-001" } ``` ### Hook Output (Decision) ```json { "decision": "allow", "reason": "File read permitted", "additionalContext": "Note: This file was recently modified" } ``` ## Phase 3: Tool Result Tool output is wrapped in a user message: ```json { "type": "user", "message": { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_01ReadFile123", "content": "1\texport async function login(email: string, password: string) {\n2\t const user = await db.users.findByEmail(email);\n..." } ] }, "uuid": "msg-002", "parentUuid": "msg-001" } ``` ### Tool Result Block Structure | Field | Type | Required | Description | |-------|------|----------|-------------| | `type` | `"tool_result"` | Yes | Block type identifier | | `tool_use_id` | string | Yes | Matches `tool_use.id` | | `content` | string | Yes | Tool output | | `is_error` | boolean | No | True if tool failed | ### Error Results ```json { "type": "tool_result", "tool_use_id": "toolu_01ReadFile123", "content": "Error: File not found: /src/auth/login.ts", "is_error": true } ``` ## Phase 4: Result Processing Claude processes the result and continues: ```json { "type": "assistant", "message": { "content": [ { "type": "thinking", "thinking": "The login function looks correct. The issue might be in the middleware..." }, { "type": "text", "text": "I see the login function. Let me check the middleware next." }, { "type": "tool_use", "id": "toolu_01ReadMiddleware", "name": "Read", "input": {"file_path": "/src/auth/middleware.ts"} } ] }, "uuid": "msg-003", "parentUuid": "msg-002" } ``` ## Parallel Tool Calls Multiple tools can be invoked in a single message: ```json { "type": "assistant", "message": { "content": [ {"type": "tool_use", "id": "toolu_01A", "name": "Grep", "input": {"pattern": "TODO"}}, {"type": "tool_use", "id": "toolu_01B", "name": "Grep", "input": {"pattern": "FIXME"}}, {"type": "tool_use", "id": "toolu_01C", "name": "Glob", "input": {"pattern": "**/*.test.ts"}} ] } } ``` Results come back in the same user message: ```json { "type": "user", "message": { "content": [ {"type": "tool_result", "tool_use_id": "toolu_01A", "content": "Found 15 TODOs"}, {"type": "tool_result", "tool_use_id": "toolu_01B", "content": "Found 3 FIXMEs"}, {"type": "tool_result", "tool_use_id": "toolu_01C", "content": "tests/auth.test.ts\ntests/api.test.ts"} ] } } ``` ## Codex Tool Format Codex uses a different structure: ### Function Call ```json { "type": "response_item", "payload": { "type": "function_call", "call_id": "call_abc123", "name": "Read", "arguments": "{\"file_path\": \"/src/auth/login.ts\"}" } } ``` **Note:** `arguments` is a JSON string that needs parsing. ### Function Result ```json { "type": "response_item", "payload": { "type": "function_call_result", "call_id": "call_abc123", "result": "File contents..." } } ``` ## Tool Call Pairing To reconstruct tool call history: 1. **Find tool_use blocks** in assistant messages 2. **Match by ID** to tool_result blocks in following user messages 3. **Handle parallel calls** — multiple tool_use can have multiple tool_result ```python # Example: Pairing tool calls with results tool_calls = {} for line in jsonl_file: event = json.loads(line) if event['type'] == 'assistant': for block in event['message']['content']: if block['type'] == 'tool_use': tool_calls[block['id']] = { 'name': block['name'], 'input': block['input'], 'timestamp': event['timestamp'] } elif event['type'] == 'user': content = event['message']['content'] if isinstance(content, list): for block in content: if block['type'] == 'tool_result': call_id = block['tool_use_id'] if call_id in tool_calls: tool_calls[call_id]['result'] = block['content'] tool_calls[call_id]['is_error'] = block.get('is_error', False) ``` ## Missing Tool Results Edge cases where tool results may be absent: 1. **Session interrupted** — User closed session mid-tool 2. **Tool timeout** — Long-running tool exceeded limits 3. **Hook blocked** — PreToolUse hook returned `block` 4. **Permission denied** — User denied tool permission Handle by checking if tool_use has matching tool_result before assuming completion. ## Tool-Specific Formats ### Bash Tool ```json { "type": "tool_use", "name": "Bash", "input": { "command": "npm test -- --coverage", "timeout": 120000, "description": "Run tests with coverage" } } ``` Result includes exit code context: ```json { "type": "tool_result", "content": "PASS src/auth.test.ts\n...\nCoverage: 85%\n\n[Exit code: 0]" } ``` ### Task Tool (Subagent) ```json { "type": "tool_use", "name": "Task", "input": { "description": "Research auth patterns", "prompt": "Explore authentication implementations...", "subagent_type": "Explore" } } ``` Result returns subagent output: ```json { "type": "tool_result", "content": "## Research Findings\n\n1. JWT patterns...\n\nagentId: agent-abc123" } ```