# Streaming tool calls and fine-grained tool streaming **Status:** DOCUMENTED · LIVE_VERIFIED 2026-09-18 (b1 buffered, b2 `eager_input_streaming:true`, b3 legacy header; claude-haiku-4-5-20251001). **Sources:** [Fine-grained tool streaming](https://platform.claude.com/docs/en/agents-and-tools/tool-use/fine-grained-tool-streaming) · [Streaming messages](https://platform.claude.com/docs/en/build-with-claude/streaming) · [Server tools — streaming](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools#streaming-server-tool-events). **Last verified:** 2026-09-18. ## Event sequence for a `tool_use` block (live b1, `tool_choice: any`) ``` message_start usage.input_tokens=676 content_block_start [0] {"type":"tool_use","id":"toolu_01VJ5…","name":"get_weather","input":{},"caller":{"type":"direct"}} ping content_block_delta [0] {"type":"input_json_delta","partial_json":""} content_block_delta [0] {"type":"input_json_delta","partial_json":"{\"locat"} content_block_delta [0] {"type":"input_json_delta","partial_json":"ion\": \""} content_block_delta [0] {"type":"input_json_delta","partial_json":"San Fra"} content_block_delta [0] {"type":"input_json_delta","partial_json":"ncisco,"} content_block_delta [0] {"type":"input_json_delta","partial_json":" CA\"}"} content_block_stop [0] message_delta {"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"container":null} usage.output_tokens=41 message_stop ``` Accumulate `partial_json` strings and `json.loads` at `content_block_stop`. `input: {}` in `content_block_start` is a placeholder. `message_delta.delta` carries `stop_details` and `container` fields (live; `container` is set when code execution ran). ## Fine-grained (eager) streaming Set `"eager_input_streaming": true` on a **custom** tool (per tool; `false` keeps buffered streaming even when the legacy header is present). No beta header needed since 2026-02-05; the legacy `anthropic-beta: fine-grained-tool-streaming-2025-05-14` still works and enables it for tools that leave the field unset (b3 produced the same fragments as b2). The header is **rejected** together with `computer_toolset_20260801` / `browser_toolset_20260801` entries. | Mode | Fragments seen live (same prompt) | Guarantees | |---|---|---| | buffered (default) | 6 short chunks, each key/value validated before emission; delays between events | accumulated string is valid JSON | | eager | 4 longer chunks (`{"location": "San`, ` Francisco, CA`, `"}`) | **no** server-side buffering or validation → may be partial/invalid JSON, especially at `stop_reason: max_tokens`; guard the parse and return `{"INVALID_JSON": raw}` as an `is_error` tool_result | Server tools stream the same way: `server_tool_use` gets `input_json_delta`s, the `*_tool_result` block arrives whole in a single `content_block_start` (no deltas; pause while the tool runs, `ping` keepalives ~30 s). Toolset members always arrive as one complete `input_json_delta`. Examples: `examples/anthropic/tools/fine-grained-streaming/basic.{sh,py}`. Test: `tests/anthropic/test_tools.py::test_streaming_input_json_delta`.