Follow Discord
Sweep 22 Oct 2026 · 04:32Z Build v2.1.308 527 read Stable v2.1.308 Latest v2.1.308 Next v2.1.308 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One change · claude-code

Stream responses in real-time changedagent-sdk/streaming-output

Nearest release: v2.1.296, published 6 hours before upstream edited the page. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.

Upstream edited this page at 9 Oct 2026 23:17 UTC, give or take a minute or two: the time comes from Anthropic’s own sitemap rather than from a commit. This site recorded the change at 20 Oct 2026 18:37 UTC.

Upstream edited
Recorded here
Lines+228added
Lines−234removed
From line 4 where the diff opens
First seen 14 Aug 2026 this site's first read of the page
Recorded edits8to this page, all time

The whole hunk

from line 4, old and new numbered
/
lines
from line 4
44 
55By default, the Agent SDK yields a complete `AssistantMessage` for each non-empty content block, such as a text block or a tool call, after Claude finishes generating that block. To receive incremental updates as text and tool calls are generated, enable partial message streaming.
66 
7<Tip>
8 This page covers output streaming (receiving tokens in real-time). For input modes (how you send messages), see [Send messages to agents](/docs/en/agent-sdk/streaming-vs-single-mode). You can also [stream responses using the Agent SDK via the CLI](/docs/en/headless).
9</Tip>
7> [!TIP]
8>
9> This page covers output streaming (receiving tokens in real-time). For input modes (how you send messages), see [Send messages to agents](/docs/en/agent-sdk/streaming-vs-single-mode). You can also [stream responses using the Agent SDK via the CLI](/docs/en/headless).
1010 
1111## Enable streaming output
1212 
from line 20
2020 
2121The example below enables streaming and prints text chunks as they arrive. Notice the nested type checks: first for `StreamEvent`, then for `content_block_delta`, then for `text_delta`:
2222 
23<CodeGroup>
24 ```python Python theme={null}
25 from claude_agent_sdk import query, ClaudeAgentOptions
26 from claude_agent_sdk.types import StreamEvent
27 import asyncio
23```python Python theme={null}
24from claude_agent_sdk import query, ClaudeAgentOptions
25from claude_agent_sdk.types import StreamEvent
26import asyncio
2827 
2928 
30 async def stream_response():
31 options = ClaudeAgentOptions(
32 include_partial_messages=True,
33 allowed_tools=["Bash", "Read"],
34 )
29async def stream_response():
30 options = ClaudeAgentOptions(
31 include_partial_messages=True,
32 allowed_tools=["Bash", "Read"],
33 )
3534 
36 async for message in query(prompt="List the files in my project", options=options):
37 if isinstance(message, StreamEvent):
38 event = message.event
39 if event.get("type") == "content_block_delta":
40 delta = event.get("delta", {})
41 if delta.get("type") == "text_delta":
42 print(delta.get("text", ""), end="", flush=True)
35 async for message in query(prompt="List the files in my project", options=options):
36 if isinstance(message, StreamEvent):
37 event = message.event
38 if event.get("type") == "content_block_delta":
39 delta = event.get("delta", {})
40 if delta.get("type") == "text_delta":
41 print(delta.get("text", ""), end="", flush=True)
4342 
4443 
45 asyncio.run(stream_response())
46 ```
44asyncio.run(stream_response())
45```
4746 
48 ```typescript TypeScript theme={null}
49 import { query } from "@anthropic-ai/claude-agent-sdk";
47```typescript TypeScript theme={null}
48import { query } from "@anthropic-ai/claude-agent-sdk";
5049 
51 for await (const message of query({
52 prompt: "List the files in my project",
53 options: {
54 includePartialMessages: true,
55 allowedTools: ["Bash", "Read"]
56 }
57 })) {
58 if (message.type === "stream_event") {
59 const event = message.event;
60 if (event.type === "content_block_delta") {
61 if (event.delta.type === "text_delta") {
62 process.stdout.write(event.delta.text);
63 }
50for await (const message of query({
51 prompt: "List the files in my project",
52 options: {
53 includePartialMessages: true,
54 allowedTools: ["Bash", "Read"]
55 }
56})) {
57 if (message.type === "stream_event") {
58 const event = message.event;
59 if (event.type === "content_block_delta") {
60 if (event.delta.type === "text_delta") {
61 process.stdout.write(event.delta.text);
6462 }
6563 }
6664 }
67 ```
68</CodeGroup>
65}
66```
6967 
7068## StreamEvent reference
7169 
from line 126
128126* `content_block_delta` with `input_json_delta`: input chunks arrive
129127* `content_block_stop`: tool call complete
130128 
131<CodeGroup>
132 ```python Python theme={null}
133 from claude_agent_sdk import query, ClaudeAgentOptions
134 from claude_agent_sdk.types import StreamEvent
135 import asyncio
129```python Python theme={null}
130from claude_agent_sdk import query, ClaudeAgentOptions
131from claude_agent_sdk.types import StreamEvent
132import asyncio
136133 
137134 
138 async def stream_tool_calls():
139 options = ClaudeAgentOptions(
140 include_partial_messages=True,
141 allowed_tools=["Read", "Bash"],
142 )
135async def stream_tool_calls():
136 options = ClaudeAgentOptions(
137 include_partial_messages=True,
138 allowed_tools=["Read", "Bash"],
139 )
143140 
144 # Track the current tool and accumulate its input JSON
145 current_tool = None
146 tool_input = ""
141 # Track the current tool and accumulate its input JSON
142 current_tool = None
143 tool_input = ""
147144 
148 async for message in query(prompt="Read the README.md file", options=options):
149 if isinstance(message, StreamEvent):
150 event = message.event
151 event_type = event.get("type")
145 async for message in query(prompt="Read the README.md file", options=options):
146 if isinstance(message, StreamEvent):
147 event = message.event
148 event_type = event.get("type")
152149 
153 if event_type == "content_block_start":
154 # New tool call is starting
155 content_block = event.get("content_block", {})
156 if content_block.get("type") == "tool_use":
157 current_tool = content_block.get("name")
158 tool_input = ""
159 print(f"Starting tool: {current_tool}")
150 if event_type == "content_block_start":
151 # New tool call is starting
152 content_block = event.get("content_block", {})
153 if content_block.get("type") == "tool_use":
154 current_tool = content_block.get("name")
155 tool_input = ""
156 print(f"Starting tool: {current_tool}")
160157 
161 elif event_type == "content_block_delta":
162 delta = event.get("delta", {})
163 if delta.get("type") == "input_json_delta":
164 # Accumulate JSON input as it streams in
165 chunk = delta.get("partial_json", "")
166 tool_input += chunk
167 print(f" Input chunk: {chunk}")
158 elif event_type == "content_block_delta":
159 delta = event.get("delta", {})
160 if delta.get("type") == "input_json_delta":
161 # Accumulate JSON input as it streams in
162 chunk = delta.get("partial_json", "")
163 tool_input += chunk
164 print(f" Input chunk: {chunk}")
168165 
169 elif event_type == "content_block_stop":
170 # Tool call complete - show final input
171 if current_tool:
172 print(f"Tool {current_tool} called with: {tool_input}")
173 current_tool = None
166 elif event_type == "content_block_stop":
167 # Tool call complete - show final input
168 if current_tool:
169 print(f"Tool {current_tool} called with: {tool_input}")
170 current_tool = None
174171 
175172 
176 asyncio.run(stream_tool_calls())
177 ```
173asyncio.run(stream_tool_calls())
174```
178175 
179 ```typescript TypeScript theme={null}
180 import { query } from "@anthropic-ai/claude-agent-sdk";
176```typescript TypeScript theme={null}
177import { query } from "@anthropic-ai/claude-agent-sdk";
181178 
182 // Track the current tool and accumulate its input JSON
183 let currentTool: string | null = null;
184 let toolInput = "";
179// Track the current tool and accumulate its input JSON
180let currentTool: string | null = null;
181let toolInput = "";
185182 
186 for await (const message of query({
187 prompt: "Read the README.md file",
188 options: {
189 includePartialMessages: true,
190 allowedTools: ["Read", "Bash"]
191 }
192 })) {
193 if (message.type === "stream_event") {
194 const event = message.event;
183for await (const message of query({
184 prompt: "Read the README.md file",
185 options: {
186 includePartialMessages: true,
187 allowedTools: ["Read", "Bash"]
188 }
189})) {
190 if (message.type === "stream_event") {
191 const event = message.event;
195192 
196 if (event.type === "content_block_start") {
197 // New tool call is starting
198 if (event.content_block.type === "tool_use") {
199 currentTool = event.content_block.name;
200 toolInput = "";
201 console.log(`Starting tool: ${currentTool}`);
202 }
203 } else if (event.type === "content_block_delta") {
204 if (event.delta.type === "input_json_delta") {
205 // Accumulate JSON input as it streams in
206 const chunk = event.delta.partial_json;
207 toolInput += chunk;
208 console.log(` Input chunk: ${chunk}`);
209 }
210 } else if (event.type === "content_block_stop") {
211 // Tool call complete - show final input
212 if (currentTool) {
213 console.log(`Tool ${currentTool} called with: ${toolInput}`);
214 currentTool = null;
215 }
193 if (event.type === "content_block_start") {
194 // New tool call is starting
195 if (event.content_block.type === "tool_use") {
196 currentTool = event.content_block.name;
197 toolInput = "";
198 console.log(`Starting tool: ${currentTool}`);
216199 }
200 } else if (event.type === "content_block_delta") {
201 if (event.delta.type === "input_json_delta") {
202 // Accumulate JSON input as it streams in
203 const chunk = event.delta.partial_json;
204 toolInput += chunk;
205 console.log(` Input chunk: ${chunk}`);
206 }
207 } else if (event.type === "content_block_stop") {
208 // Tool call complete - show final input
209 if (currentTool) {
210 console.log(`Tool ${currentTool} called with: ${toolInput}`);
211 currentTool = null;
212 }
217213 }
218214 }
219 ```
220</CodeGroup>
215}
216```
221217 
222218## Build a streaming UI
223219 
224220This example combines text and tool streaming into a cohesive UI. It tracks whether the agent is currently executing a tool (using an `in_tool` flag) to show status indicators like `[Using Read...]` while tools run. Text streams normally when not in a tool, and tool completion triggers a "done" message. This pattern is useful for chat interfaces that need to show progress during multi-step agent tasks.
225221 
226<CodeGroup>
227 ```python Python theme={null}
228 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
229 from claude_agent_sdk.types import StreamEvent
230 import asyncio
231 import sys
222```python Python theme={null}
223from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
224from claude_agent_sdk.types import StreamEvent
225import asyncio
226import sys
232227 
233228 
234 async def streaming_ui():
235 options = ClaudeAgentOptions(
236 include_partial_messages=True,
237 allowed_tools=["Read", "Bash", "Grep"],
238 )
229async def streaming_ui():
230 options = ClaudeAgentOptions(
231 include_partial_messages=True,
232 allowed_tools=["Read", "Bash", "Grep"],
233 )
239234 
240 # Track whether we're currently in a tool call
241 in_tool = False
235 # Track whether we're currently in a tool call
236 in_tool = False
242237 
243 async for message in query(
244 prompt="Find all TODO comments in the codebase", options=options
245 ):
246 if isinstance(message, StreamEvent):
247 event = message.event
248 event_type = event.get("type")
238 async for message in query(
239 prompt="Find all TODO comments in the codebase", options=options
240 ):
241 if isinstance(message, StreamEvent):
242 event = message.event
243 event_type = event.get("type")
249244 
250 if event_type == "content_block_start":
251 content_block = event.get("content_block", {})
252 if content_block.get("type") == "tool_use":
253 # Tool call is starting - show status indicator
254 tool_name = content_block.get("name")
255 print(f"\n[Using {tool_name}...]", end="", flush=True)
256 in_tool = True
245 if event_type == "content_block_start":
246 content_block = event.get("content_block", {})
247 if content_block.get("type") == "tool_use":
248 # Tool call is starting - show status indicator
249 tool_name = content_block.get("name")
250 print(f"\n[Using {tool_name}...]", end="", flush=True)
251 in_tool = True
257252 
258 elif event_type == "content_block_delta":
259 delta = event.get("delta", {})
260 # Only stream text when not executing a tool
261 if delta.get("type") == "text_delta" and not in_tool:
262 sys.stdout.write(delta.get("text", ""))
263 sys.stdout.flush()
253 elif event_type == "content_block_delta":
254 delta = event.get("delta", {})
255 # Only stream text when not executing a tool
256 if delta.get("type") == "text_delta" and not in_tool:
257 sys.stdout.write(delta.get("text", ""))
258 sys.stdout.flush()
264259 
265 elif event_type == "content_block_stop":
266 if in_tool:
267 # Tool call finished
268 print(" done", flush=True)
269 in_tool = False
260 elif event_type == "content_block_stop":
261 if in_tool:
262 # Tool call finished
263 print(" done", flush=True)
264 in_tool = False
270265 
271 elif isinstance(message, ResultMessage):
272 # Agent finished all work
273 print(f"\n\n--- Complete ---")
266 elif isinstance(message, ResultMessage):
267 # Agent finished all work
268 print(f"\n\n--- Complete ---")
274269 
275270 
276 asyncio.run(streaming_ui())
277 ```
271asyncio.run(streaming_ui())
272```
278273 
279 ```typescript TypeScript theme={null}
280 import { query } from "@anthropic-ai/claude-agent-sdk";
274```typescript TypeScript theme={null}
275import { query } from "@anthropic-ai/claude-agent-sdk";
281276 
282 // Track whether we're currently in a tool call
283 let inTool = false;
277// Track whether we're currently in a tool call
278let inTool = false;
284279 
285 for await (const message of query({
286 prompt: "Find all TODO comments in the codebase",
287 options: {
288 includePartialMessages: true,
289 allowedTools: ["Read", "Bash", "Grep"]
290 }
291 })) {
292 if (message.type === "stream_event") {
293 const event = message.event;
280for await (const message of query({
281 prompt: "Find all TODO comments in the codebase",
282 options: {
283 includePartialMessages: true,
284 allowedTools: ["Read", "Bash", "Grep"]
285 }
286})) {
287 if (message.type === "stream_event") {
288 const event = message.event;
294289 
295 if (event.type === "content_block_start") {
296 if (event.content_block.type === "tool_use") {
297 // Tool call is starting - show status indicator
298 process.stdout.write(`\n[Using ${event.content_block.name}...]`);
299 inTool = true;
300 }
301 } else if (event.type === "content_block_delta") {
302 // Only stream text when not executing a tool
303 if (event.delta.type === "text_delta" && !inTool) {
304 process.stdout.write(event.delta.text);
305 }
306 } else if (event.type === "content_block_stop") {
307 if (inTool) {
308 // Tool call finished
309 console.log(" done");
310 inTool = false;
311 }
290 if (event.type === "content_block_start") {
291 if (event.content_block.type === "tool_use") {
292 // Tool call is starting - show status indicator
293 process.stdout.write(`\n[Using ${event.content_block.name}...]`);
294 inTool = true;
312295 }
313 } else if (message.type === "result") {
314 // Agent finished all work
315 console.log("\n\n--- Complete ---");
296 } else if (event.type === "content_block_delta") {
297 // Only stream text when not executing a tool
298 if (event.delta.type === "text_delta" && !inTool) {
299 process.stdout.write(event.delta.text);
300 }
301 } else if (event.type === "content_block_stop") {
302 if (inTool) {
303 // Tool call finished
304 console.log(" done");
305 inTool = false;
306 }
316307 }
308 } else if (message.type === "result") {
309 // Agent finished all work
310 console.log("\n\n--- Complete ---");
317311 }
318 ```
319</CodeGroup>
312}
313```
320314 
321315## Known limitations
322316 
Feedback