agent-sdk/streaming-vs-single-mode.md +295 −0 added
1> ## Documentation Index
2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
3> Use this file to discover all available pages before exploring further.
4
5# Streaming Input
6
7> Understanding the two input modes for Claude Agent SDK and when to use each
8
9## Overview
10
11The Claude Agent SDK supports two distinct input modes for interacting with agents:
12
13* **Streaming Input Mode** (Default & Recommended) - A persistent, interactive session
14* **Single Message Input** - One-shot queries that use session state and resuming
15
16This guide explains the differences, benefits, and use cases for each mode to help you choose the right approach for your application.
17
18## Streaming Input Mode (Recommended)
19
20Streaming input mode is the **preferred** way to use the Claude Agent SDK. It provides full access to the agent's capabilities and enables rich, interactive experiences.
21
22It allows the agent to operate as a long lived process that takes in user input, handles interruptions, surfaces permission requests, and handles session management.
23
24### How It Works
25
26```mermaid theme={null}
27sequenceDiagram
28 participant App as Your Application
29 participant Agent as Claude Agent
30 participant Tools as Tools/Hooks
31 participant FS as Environment/<br/>File System
32
33 App->>Agent: Initialize with AsyncGenerator
34 activate Agent
35
36 App->>Agent: Yield Message 1
37 Agent->>Tools: Execute tools
38 Tools->>FS: Read files
39 FS-->>Tools: File contents
40 Tools->>FS: Write/Edit files
41 FS-->>Tools: Success/Error
42 Agent-->>App: Stream partial response
43 Agent-->>App: Stream more content...
44 Agent->>App: Complete Message 1
45
46 App->>Agent: Yield Message 2 + Image
47 Agent->>Tools: Process image & execute
48 Tools->>FS: Access filesystem
49 FS-->>Tools: Operation results
50 Agent-->>App: Stream response 2
51
52 App->>Agent: Queue Message 3
53 App->>Agent: Interrupt/Cancel
54 Agent->>App: Handle interruption
55
56 Note over App,Agent: Session stays alive
57 Note over Tools,FS: Persistent file system<br/>state maintained
58
59 deactivate Agent
60```
61
62### Benefits
63
64<CardGroup cols={2}>
65 <Card title="Image Uploads" icon="image">
66 Attach images directly to messages for visual analysis and understanding
67 </Card>
68
69 <Card title="Queued Messages" icon="stack">
70 Send multiple messages that process sequentially, with ability to interrupt
71 </Card>
72
73 <Card title="Tool Integration" icon="wrench">
74 Full access to all tools and custom MCP servers during the session
75 </Card>
76
77 <Card title="Hooks Support" icon="link">
78 Use lifecycle hooks to customize behavior at various points
79 </Card>
80
81 <Card title="Real-time Feedback" icon="lightning">
82 See responses as they're generated, not just final results
83 </Card>
84
85 <Card title="Context Persistence" icon="database">
86 Maintain conversation context across multiple turns naturally
87 </Card>
88</CardGroup>
89
90### Implementation Example
91
92<CodeGroup>
93 ```typescript TypeScript theme={null}
94 import { query } from "@anthropic-ai/claude-agent-sdk";
95 import { readFile } from "fs/promises";
96
97 async function* generateMessages() {
98 // First message
99 yield {
100 type: "user" as const,
101 message: {
102 role: "user" as const,
103 content: "Analyze this codebase for security issues"
104 }
105 };
106
107 // Wait for conditions or user input
108 await new Promise((resolve) => setTimeout(resolve, 2000));
109
110 // Follow-up with image
111 yield {
112 type: "user" as const,
113 message: {
114 role: "user" as const,
115 content: [
116 {
117 type: "text",
118 text: "Review this architecture diagram"
119 },
120 {
121 type: "image",
122 source: {
123 type: "base64",
124 media_type: "image/png",
125 data: await readFile("diagram.png", "base64")
126 }
127 }
128 ]
129 }
130 };
131 }
132
133 // Process streaming responses
134 for await (const message of query({
135 prompt: generateMessages(),
136 options: {
137 maxTurns: 10,
138 allowedTools: ["Read", "Grep"]
139 }
140 })) {
141 if (message.type === "result") {
142 console.log(message.result);
143 }
144 }
145 ```
146
147 ```python Python theme={null}
148 from claude_agent_sdk import (
149 ClaudeSDKClient,
150 ClaudeAgentOptions,
151 AssistantMessage,
152 TextBlock,
153 )
154 import asyncio
155 import base64
156
157
158 async def streaming_analysis():
159 async def message_generator():
160 # First message
161 yield {
162 "type": "user",
163 "message": {
164 "role": "user",
165 "content": "Analyze this codebase for security issues",
166 },
167 }
168
169 # Wait for conditions
170 await asyncio.sleep(2)
171
172 # Follow-up with image
173 with open("diagram.png", "rb") as f:
174 image_data = base64.b64encode(f.read()).decode()
175
176 yield {
177 "type": "user",
178 "message": {
179 "role": "user",
180 "content": [
181 {"type": "text", "text": "Review this architecture diagram"},
182 {
183 "type": "image",
184 "source": {
185 "type": "base64",
186 "media_type": "image/png",
187 "data": image_data,
188 },
189 },
190 ],
191 },
192 }
193
194 # Use ClaudeSDKClient for streaming input
195 options = ClaudeAgentOptions(max_turns=10, allowed_tools=["Read", "Grep"])
196
197 async with ClaudeSDKClient(options) as client:
198 # Send streaming input
199 await client.query(message_generator())
200
201 # Process responses
202 async for message in client.receive_response():
203 if isinstance(message, AssistantMessage):
204 for block in message.content:
205 if isinstance(block, TextBlock):
206 print(block.text)
207
208
209 asyncio.run(streaming_analysis())
210 ```
211</CodeGroup>
212
213## Single Message Input
214
215Single message input is simpler but more limited.
216
217### When to Use Single Message Input
218
219Use single message input when:
220
221* You need a one-shot response
222* You do not need image attachments, hooks, etc.
223* You need to operate in a stateless environment, such as a lambda function
224
225### Limitations
226
227<Warning>
228 Single message input mode does **not** support:
229
230 * Direct image attachments in messages
231 * Dynamic message queueing
232 * Real-time interruption
233 * Hook integration
234 * Natural multi-turn conversations
235</Warning>
236
237### Implementation Example
238
239<CodeGroup>
240 ```typescript TypeScript theme={null}
241 import { query } from "@anthropic-ai/claude-agent-sdk";
242
243 // Simple one-shot query
244 for await (const message of query({
245 prompt: "Explain the authentication flow",
246 options: {
247 maxTurns: 1,
248 allowedTools: ["Read", "Grep"]
249 }
250 })) {
251 if (message.type === "result") {
252 console.log(message.result);
253 }
254 }
255
256 // Continue conversation with session management
257 for await (const message of query({
258 prompt: "Now explain the authorization process",
259 options: {
260 continue: true,
261 maxTurns: 1
262 }
263 })) {
264 if (message.type === "result") {
265 console.log(message.result);
266 }
267 }
268 ```
269
270 ```python Python theme={null}
271 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
272 import asyncio
273
274
275 async def single_message_example():
276 # Simple one-shot query using query() function
277 async for message in query(
278 prompt="Explain the authentication flow",
279 options=ClaudeAgentOptions(max_turns=1, allowed_tools=["Read", "Grep"]),
280 ):
281 if isinstance(message, ResultMessage):
282 print(message.result)
283
284 # Continue conversation with session management
285 async for message in query(
286 prompt="Now explain the authorization process",
287 options=ClaudeAgentOptions(continue_conversation=True, max_turns=1),
288 ):
289 if isinstance(message, ResultMessage):
290 print(message.result)
291
292
293 asyncio.run(single_message_example())
294 ```
295</CodeGroup>