skills/claude-agent-sdk/agent-sdk-streaming/SKILL.md
Stream agent output correctly — text deltas, tool-use events, thinking deltas, progress indicators — without dropping events or blocking on long tool calls. Covers backpressure, error handling, and UX patterns. Use this skill when building agent UIs (chat, CLI), ensuring agents feel responsive, or debugging dropped/truncated streams. Activate when: agent streaming, stream events, text deltas, streaming UI, SSE agent, stream tool use.
npx skillsauth add latestaiagents/agent-skills agent-sdk-streamingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Streaming is the difference between "this agent works" and "this agent feels alive". Stream text as it generates, show tool activity, and never block the UI on a long tool call.
The SDK emits a typed stream of events. Core types:
| Event | When | UI treatment |
|---|---|---|
| assistant with text block | Model generates text | Append to current message bubble |
| assistant with thinking | Extended thinking | Show "thinking..." spinner |
| assistant with tool_use | Model decides to call tool | Show tool badge ("calling web_fetch...") |
| user with tool_result | Tool returned | Show result or just acknowledge |
| system with progress | Tool progress notification | Update progress bar |
| result | Turn complete | Remove spinner, finalize |
import { query } from "@anthropic-ai/claude-agent-sdk";
const ui = new ChatUI();
for await (const msg of query({ prompt, options: { model: "claude-sonnet-4-6" } })) {
switch (msg.type) {
case "assistant": {
for (const block of msg.message.content) {
if (block.type === "text") ui.appendText(block.text);
if (block.type === "thinking") ui.showThinking(block.thinking);
if (block.type === "tool_use") ui.showToolCall(block.name, block.input);
}
break;
}
case "user": {
for (const block of msg.message.content) {
if (block.type === "tool_result") ui.showToolResult(block.tool_use_id, block.content);
}
break;
}
case "result": {
ui.finalize(msg.result);
break;
}
}
}
Some SDK versions emit full messages (with cumulative text) rather than deltas. Handle both:
let lastText = "";
// on assistant text:
const newText = block.text.slice(lastText.length);
ui.appendText(newText);
lastText = block.text;
Check your SDK version's docs; deltas reduce UI work but complete messages simplify state.
For web UIs, bridge Node stream → SSE:
app.get("/chat", async (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.flushHeaders();
for await (const msg of query({ prompt: req.query.q, options: {...} })) {
res.write(`data: ${JSON.stringify(msg)}\n\n`);
if (typeof (res as any).flush === "function") (res as any).flush();
}
res.end();
});
Gotchas:
X-Accel-Buffering: no) if behind nginx: ping\n\n) every 15s to prevent proxy idle timeoutsfunction useAgent() {
const [messages, setMessages] = useState([]);
const [streaming, setStreaming] = useState(false);
const send = async (prompt: string) => {
setStreaming(true);
const res = await fetch("/chat?q=" + encodeURIComponent(prompt));
const reader = res.body!.getReader();
const decoder = new TextDecoder();
let buf = "";
while (true) {
const { value, done } = await reader.read();
if (done) break;
buf += decoder.decode(value, { stream: true });
const events = buf.split("\n\n");
buf = events.pop()!;
for (const evt of events) {
if (evt.startsWith("data: ")) {
const msg = JSON.parse(evt.slice(6));
setMessages((m) => reduceAgentMsg(m, msg));
}
}
}
setStreaming(false);
};
return { messages, streaming, send };
}
If the UI renders slower than events arrive:
Users change their mind. Support interrupting a running agent:
const controller = new AbortController();
for await (const msg of query({
prompt,
options: { abortController: controller },
})) { /* ... */ }
// elsewhere:
cancelButton.onclick = () => controller.abort();
Always cancel gracefully — show "Stopped by user" instead of a broken state.
try {
for await (const msg of query({ prompt, options })) { /* ... */ }
} catch (err) {
if (err.name === "AbortError") ui.showStopped();
else if (err.status === 429) ui.showRateLimit();
else ui.showError(err.message);
}
Don't silently fail — users need to know whether to retry.
Raw tool calls are noisy. Map them to user-friendly UI:
const TOOL_LABELS: Record<string, string> = {
web_fetch: "Looking up the web",
code_execution: "Running code",
Task: "Delegating research",
};
ui.showToolCall = (name, input) => {
ui.addStatusLine(TOOL_LABELS[name] ?? `Running ${name}...`);
};
Collapse details into an expandable disclosure — power users want to see the raw call; others don't.
X-Accel-Buffering: no at proxiesdevelopment
Test skills for correct activation, content quality, and regression — both automated checks (frontmatter validity, lint) and manual verification (query-suite activation testing). Covers CI integration and how to catch skill regressions before users do. Use this skill when adding skills to a repo, setting up CI for a skill library, or debugging "the skill exists but doesn't work". Activate when: test skills, validate skills, skill CI, skill linting, skill activation test, skill regression.
documentation
Write the YAML frontmatter for a SKILL.md file so it activates reliably — name, description, and activation keywords that the model matches against. Covers length, tone, and the most common frontmatter mistakes. Use this skill when authoring a new skill, fixing a skill that isn't auto-activating, or reviewing skills for publication. Activate when: SKILL.md frontmatter, skill description, skill activation, skill YAML, write a skill, author a skill.
development
Design skills that fire at the right moment — neither over-eager (noise) nor under-eager (silent). Covers activation specificity, trigger phrases, disambiguation between overlapping skills, and debugging activation. Use this skill when multiple skills could fire on the same query, a skill never fires, or a skill fires too often. Activate when: skill won't activate, skill over-activates, overlapping skills, skill triggers, skill selection, skill disambiguation.
development
Structure SKILL.md content so the model reads just enough — concise summary up front, progressively deeper detail, examples on demand. Covers section ordering, length budgets, when to split into multiple skills. Use this skill when writing or refactoring a skill body, one skill has grown too long, or a skill is wordy but not useful. Activate when: SKILL.md structure, skill content, skill too long, split skill, progressive disclosure, skill body.