What are async iterator tools?
Standard tools execute and return a single result. Async iterator tools are different — they yield intermediate progress updates during execution before producing a final result. This is implemented using async generators on the server side. Consider a web search tool that crawls multiple pages:- Start searching → yield
{ status: "starting", completed: 0, total: 5 } - First page crawled → yield
{ status: "searching", completed: 1, total: 5, latest: "example.com" } - Second page crawled → yield
{ status: "searching", completed: 2, total: 5, latest: "docs.org" } - …and so on until complete
toolProgress property on useStream.
Use cases
- Web scraping — crawling multiple URLs with per-page progress
- File processing — batch transformations on uploaded documents
- Database queries — scanning large datasets with row-count progress
- API aggregation — calling multiple third-party APIs sequentially
- Code analysis — linting or testing across multiple files
- Data pipelines — ETL steps with stage-level progress
The toolProgress property
The useStream hook exposes toolProgress, an array of ToolProgress objects representing active and recently completed tool executions:
Accessing toolProgress from useStream
Define a TypeScript interface matching your agent’s state schema and pass it as a type parameter to useStream for type-safe access to state values. In the examples below, replace typeof myAgent with your interface name:
Building a ToolProgressCard
TheToolProgressCard component renders a visual progress bar with status information for each active tool execution:
Status mapping
Map the tool’s state to user-friendly labels and colors:Styling the progress card
Calculating progress percentage
The percentage calculation is straightforward but needs guard checks for missing data:Some tools may not report
total upfront (e.g., when the number of items isn’t known until partway through execution). In these cases, consider showing an indeterminate progress indicator instead of a bar at 0%.Indeterminate progress fallback
Filtering completed progress
Once a tool completes, you typically want to remove its progress card and let the final result appear in the message stream. Filter by state:Combining with regular tool calls
Async iterator tools and standard tool calls can coexist in the same agent. The final result of an async iterator tool appears in the message stream as a regular tool response, while progress updates come throughtoolProgress.
Handling multiple concurrent tools
An agent may execute multiple async iterator tools in parallel. Each has its owntoolCallId, so they render as separate progress cards:
Complete example
Best practices
Progress updates arrive at the rate the server yields them. If updates are too frequent (e.g., per-row in a million-row scan), throttle rendering on the client side to avoid DOM thrashing.
- Show the latest item — displaying
data.latestgives users a sense of what is being processed, not just how much - Use transitions on the progress bar —
transition: width 0.3s easemakes the bar animate smoothly between updates - Handle missing totals — fall back to an indeterminate progress bar when
data.totalis not yet known - Clean up completed cards — remove or fade out progress cards once the tool finishes to keep the UI uncluttered
- Throttle rapid updates — if progress updates arrive faster than 60fps, debounce the state updates to maintain smooth rendering
- Provide cancel affordance — if the agent supports interruption, add a cancel button to the progress card so users can abort long-running tools
Connect these docs to Claude, VSCode, and more via MCP for real-time answers.

