Stream tool call results in real time by combining LangChain/LangGraph's astream_events API to capture tool lifecycle events and Server-Sent Events (SSE) or WebSockets to push structured updates to the client, using the useStream hook on the frontend to receive and render typed tool calls and their results.
Streaming tool call results to the client in real time involves two key parts: capturing granular events on the backend as the agent executes tools, and pushing those events to the frontend over a persistent connection. On the backend, LangGraph's astream_events API provides a detailed stream of events, including on_tool_start and on_tool_end, which you can forward to the client via Server-Sent Events (SSE) or WebSockets. On the frontend, the useStream hook from @langchain/react automatically consumes this stream and provides a reactive toolCalls array that updates in real time as tools are invoked and completed. This approach gives you a smooth, token-by-token feel for tool execution without the complexity of managing raw WebSocket connections manually .
The core of a real-time tool-call streaming implementation is LangGraph's astream_events API. This method streams every granular event that occurs during agent execution, including when a tool call starts (on_tool_start), when it ends (on_tool_end), and even individual text tokens from the LLM (on_chat_model_stream). To capture tool calls, your agent must be created with a model that supports tool calling and is configured with streaming=True (for providers like Anthropic or OpenAI) to ensure that events are emitted in a timely fashion .
On the frontend, the useStream hook from @langchain/react integrates seamlessly with the streaming backend. It consumes the SSE stream and automatically populates a reactive toolCalls array. Each entry in this array has a state (pending, completed, or error) and contains both the tool call request (with its name and args) and its result. This allows you to build a UI that updates in real time, showing a loading indicator the moment a tool call is emitted and then displaying the result as soon as it arrives .
The useStream hook returns a toolCalls array of objects with a specific structure that gives you full control over the rendering of each tool's lifecycle. For optimal user experience, every tool should handle the three lifecycle states .
call.id: A unique ID for the tool call, used to match calls to results and filter them per message.
call.name: The name of the tool (e.g., get_weather, calculator).
call.args: The typed arguments the agent passed to the tool, inferred from the tool's Zod schema for full type safety.
result: The ToolMessage response, available once the tool finishes execution.
state: The lifecycle state, which can be "pending" (tool is running), "completed" (success), or "error" (failure).
A key UX feature of a streaming agent is that tool calls and their results are interleaved with the LLM's text output. The useStream hook maintains the correct order, allowing you to render the conversational flow naturally .
Use Server-Sent Events (SSE) over WebSockets for simpler implementations, as SSE works seamlessly with HTTP and avoids issues with session affinity in serverless or autoscaling environments .
Always provide fallback UI for the three tool call states: pending, completed, and error .
For type safety, define your tools with Zod schemas in TypeScript; the useStream hook will infer the argument types, ensuring your UI components receive correctly typed data .
When using models like GPT-5, note that they may aggregate response chunks, which can prevent token-level streaming in astream_events. For token-by-token text streaming, models like Anthropic's Claude Sonnet are more reliable .
For multi-agent systems (deep agents), LangGraph provides stream.subagents, allowing you to stream tool calls and results from subagents independently, providing a richer view of the entire decision-making process .