SpyBara
Go Premium

agent-sdk/streaming-vs-single-mode.md 2026-08-27 23:57 UTC to 2026-08-28 23:59 UTC

This page contains 0 additions and 38 deletions.

2026
Sun 2 19:00 Mon 10 22:57 Wed 26 23:58 Fri 28 23:59

Streaming Input

Understanding the two input modes for Claude Agent SDK and when to use each

Overview

The Claude Agent SDK supports two distinct input modes for interacting with agents:

  • Streaming Input Mode: a persistent, interactive session
  • Single Message Input: one-shot queries that use session state and resuming

Streaming 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.

It allows the agent to operate as a long lived process that takes in user input, handles interruptions, surfaces permission requests, and handles session management.

Benefits

In streaming input mode, you work in a persistent session with these capabilities:

  • Image uploads: attach images directly to messages for visual analysis and understanding
  • Queued messages: send multiple messages that process sequentially, with ability to interrupt
  • Tool integration: full access to all tools and custom MCP servers during the session
  • Real-time feedback: see responses as they're generated, not just final results
  • Context persistence: maintain conversation context across multiple turns naturally

Implementation Example

These examples read an image named diagram.png from the working directory. Create one there first, or change the filename to point at your own image.

import { query, type SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
import { readFile } from "fs/promises";

async function* generateMessages(): AsyncGenerator<SDKUserMessage> {
// First message
yield {
type: "user",
message: {
role: "user",
content: "Analyze this codebase for security issues"
},
parent_tool_use_id: null
};

// Wait for conditions or user input
await new Promise((resolve) => setTimeout(resolve, 2000));

// Follow-up with image
yield {
type: "user",
message: {
role: "user",
content: [
{
type: "text",
text: "Review this architecture diagram"
},
{
type: "image",
source: {
type: "base64",
media_type: "image/png",
data: await readFile("diagram.png", "base64")
}
}
]
},
parent_tool_use_id: null
};
}

// Process streaming responses
for await (const message of query({
prompt: generateMessages(),
options: {
maxTurns: 10,
allowedTools: ["Read", "Grep"]
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}

When you run the example, the TypeScript version prints each response as it completes. The Python version's receive_response() loop ends at the first result message, so it prints the security analysis; to read both responses, use one query() and receive_response() pair per message as shown in the Python reference's example of continuing a conversation.

Single Message Input

Single message input is simpler but more limited.

When to Use Single Message Input

Use single message input when:

  • You need a one-shot response
  • You do not need image attachments or mid-session control methods
  • You need to operate in a stateless environment, such as a lambda function

Limitations

If a query ends with an error result, such as error_max_turns, a single message query() call raises an error that includes the failure text after yielding the final result message, so wrap the loop in a try block if your code needs to continue. See Handle the result for the result subtypes.

Implementation Example

import { query } from "@anthropic-ai/claude-agent-sdk";

// Simple one-shot query
// query() throws after an error result, such as error_max_turns
try {
for await (const message of query({
prompt: "Explain the authentication flow",
options: {
maxTurns: 5,
allowedTools: ["Read", "Grep"]
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
} catch (error) {
console.error(`Query failed: ${error}`);
}

// Continue conversation with session management
try {
for await (const message of query({
prompt: "Now explain the authorization process",
options: {
continue: true,
maxTurns: 5
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
} catch (error) {
console.error(`Query failed: ${error}`);
}

When you run the example, each query prints its final result text: first the authentication explanation, then the authorization explanation.