Streaming Idioms
All CRAFT agents stream their responses via Server-Sent Events (SSE). This page covers the practical patterns for producing well-formed SSE streams, handling partial results, and implementing cooperative cancellation.A2A SSE vs. MCP Streamable HTTP: This page covers A2A streaming — the JSON-RPC events that flow between agents via Server-Sent Events (SSE). This is different from the MCP Streamable HTTP transport used to connect to the CRAFT tool gateway. A2A’s
message/stream uses SSE; MCP tool connections use the MCP Streamable HTTP transport (e.g., ADK’s StreamableHTTPConnectionParams, FastMCP’s StreamableHttpTransport, the MCP Python SDK’s streamablehttp_client, or LangGraph’s MultiServerMCPClient with transport="http"). Both A2A SSE and MCP Streamable HTTP are current standards — they serve different protocol layers.The Streaming Contract
The A2A protocol requires everymessage/stream response to follow a strict event ordering:
- First event:
Task(state =submitted) - Zero or more:
TaskStatusUpdateEvent(state =working) with intermediate progress messages - Zero or more:
TaskArtifactUpdateEventwith result artifacts - Final event:
TaskStatusUpdateEventwithfinal: trueand a terminal state (completed,failed,canceled)
data: line in the SSE stream.
Streaming Text Output
Pydantic AI — run_stream with stream_text()
The following pattern demonstrates streaming text output from a Pydantic AI agent. Each text chunk is forwarded as a TaskStatusUpdateEvent with state=working:
stream_text() returns the accumulated text at each yield, not a delta. Each chunk is the full response so far. This means the client always has a coherent partial response it can display immediately, without needing to concatenate chunks.Google ADK — SSE via to_a2a()
When using to_a2a(), ADK handles all SSE event emission automatically. Your agent code just runs normally:
ADK’s
to_a2a() uses TaskStatusUpdateEvent with state=working for streaming intermediate text chunks, then emits a TaskArtifactUpdateEvent for the final text. This differs from the Pydantic AI pattern (which uses only status events). Both are valid A2A; clients must handle both patterns.Claude Agent SDK — Streaming via A2A
The Claude Agent SDK doesn’t wrap natively in A2A. Build a thin A2A executor that streams from the Anthropic API and emits the correct A2A events:LangGraph — Streaming via A2A
Usestream_mode="messages" which yields (chunk, metadata) tuples for token-level streaming. Use stream_mode="updates" for node-completion events instead.
For graphs without a
messages state key, switch to stream_mode="updates" and iterate for node_name, payload in chunk.items(): to detect completed nodes.Streaming Intermediate Status Messages
Use intermediate status messages to keep users informed during long-running tasks. Emit an intent acknowledgment before the main agent loop:Artifact Streaming
Artifacts (charts, data files, analysis results) are emitted asTaskArtifactUpdateEvent. For multi-artifact agents (like a text-to-SQL agent that produces intermediate SQL plans followed by query results), artifacts are emitted in a specific order:
sql_plan— intermediate SQL planning artifact (TextPart, JSON)query_summary— human-readable summary of results (TextPart)sql_query— the generated SQL (DataPart with artifact URI)query_results— Parquet results file (DataPart with artifact URI,last_chunk=true)
Cooperative Cancellation
CRAFT supports cooperative cancellation: a client sendstasks/cancel, and the agent should stop its current work and emit a canceled terminal event.
Pydantic AI — asyncio.CancelledError
Implement cancellation via asyncio task cancellation. Thecancel() method updates the task state in the database before the running asyncio task receives the cancellation signal:
Cross-Instance Cancellation
In Kubernetes with multiple agent replicas, the cancel request may hit a different pod than the one running the task. Use a background polling watcher to detect cross-instance cancellation:execute() and cancel it in the finally block:
Reconnection Handling
If an SSE connection drops mid-stream, the client sends atasks/resubscribe request. The DefaultRequestHandler from the a2a library handles this automatically when used with a persistent task store.
LLM Error Retry
Transient LLM errors (HTTP 5xx, 429) should be retried with exponential backoff. A well-behaved executor retries up to 2 times:First-Token Latency
Track time-to-first-token to detect model cold starts and MCP connection overhead:Next Steps
Debugging Agents
Inspect streaming traces and diagnose SSE issues.
Eval Harness
Set up Langfuse to evaluate streaming agent behaviour.

