Streaming Tool Results
We previously covered Streaming API Responses, which creates that beautiful, real-time typing effect on the frontend.
We also covered Function Calling (Tools).
But what happens when you combine them? If your Agent uses a tool, how does the frontend know what is happening while the backend is executing the tool?
The Problem with Tools and Streaming
If you use tool_choice: auto and enable stream: true, the stream behaves differently.
When Claude decides to use a tool, it doesn't stream the final text answer (because it doesn't know the answer yet). Instead, Claude streams the JSON arguments for the tool!
If you blindly write the stream chunks to the frontend UI, the user will see a bunch of raw JSON appear on their screen:
{"city": "L
on
don"}
This is a terrible user experience.
Handling the Event Types
To build a professional streaming Agent, your backend must carefully inspect the type of each chunk arriving in the stream, and route it appropriately.
Anthropic's streaming SDK emits several event types. The two most important for this scenario are:
text_delta: These chunks contain the human-readable text (e.g., "The weather is sunny."). You should forward these chunks directly to the frontend via WebSockets.input_json_delta: These chunks contain the fragments of the tool arguments. You should NOT send these to the frontend. Instead, your backend should silently accumulate them in a variable.
The Streaming Tool Architecture
Here is how a production backend handles this:
- Start the Stream: The user asks a question. The backend starts the stream with Anthropic.
- Intercept: As chunks arrive, the backend checks the type.
- Accumulate: If it is an
input_json_delta, the backend silently joins the chunks together:{"city":+"London"}. - Execute: Once the stream emits a
message_stopevent (meaning the JSON is finished), the backend parses the accumulated JSON and executes the local tool (myWeatherFunction("London")). - Resume the Stream: The backend builds the
tool_resultpayload, sends it to Anthropic in a new streaming request, and this time, Claude streamstext_deltachunks containing the final answer, which are forwarded to the frontend UI.
By intercepting and hiding the JSON chunks, you provide a seamless, magical experience for the user. They only see the conversational text, completely unaware of the complex tool negotiations happening on your server.