Skip to main content

streaming-protocols

Use when designing or debugging streaming in AI systems — SSE, NDJSON, HTTP Streaming, WebSocket, MCP transport, or A2UI transport. Covers the three-layer mental model, bidirectionality, real-world wire formats (OpenAI, Anthropic, MCP), production pitfalls (proxy buffering, compression, mobile reconnection), and the decision guide for which protocol to use when.

Informações da origem

Repositório
kumaran-is/claude-code-onboarding
Última atividade na origem
25 de maio de 2026 às 18:14
Idioma detectado do SKILL.md
inglês
Estrelas
36
Forks
21

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
streaming-protocols
description
Use when designing or debugging streaming in AI systems — SSE, NDJSON, HTTP Streaming, WebSocket, MCP transport, or A2UI transport. Covers the three-layer mental model, bidirectionality, real-world wire formats (OpenAI, Anthropic, MCP), production pitfalls (proxy buffering, compression, mobile reconnection), and the decision guide for which protocol to use when.
allowed-tools
Read, Grep, Glob, Bash, WebFetch
metadata
{"triggers":"SSE, server-sent events, NDJSON, HTTP streaming, WebSocket, streaming protocol, MCP transport, streamable HTTP, bidirectional streaming, traceparent, streaming AI, token streaming","related-skills":"a2ui-angular, mcp-builder, agentic-ai-dev","domain":"infrastructure","role":"reference","scope":"architecture","output-format":"guidance"}
last-reviewed
2026-05-25
# Streaming Protocols for AI Systems ## Iron Law **Pick one option from each of the three layers (Transport / Framing / Message). The "which protocol?" question answers itself.** Most streaming confusion comes from treating options at different layers as alternatives. They compose — they are not competitors. --- ## 1. The Three-Layer Mental Model ``` ┌─────────────────────────────────────────────────────────┐ │ MESSAGE LAYER — what each message means │ │ JSON-RPC · A2UI envelope · OpenAI delta · plain JSON │ ├─────────────────────────────────────────────────────────┤ │ FRAMING LAYER — how messages are delimited │ │ SSE events · NDJSON lines · HTTP chunks · WS frames │ ├─────────────────────────────────────────────────────────┤ │ TRANSPORT LAYER — the connection itself │ │ HTTP · HTTP Streaming · WebSocket · stdio │ └─────────────────────────────────────────────────────────┘ ``` ``` Transport opens the pipe. Framing chops the pipe into messages. Message format gives each message meaning. ``` **The most common real-world stack:** ``` HTTP Streaming (transport) └── SSE (framing) └── JSON-RPC or provider-specific JSON (message) ``` **For tool/agent IPC:** ``` stdio (transport) └── newline-delimited JSON (framing) └── JSON-RPC 2.0 (message) ``` --- ## 2. SSE vs HTTP Streaming — The Core Confusion **SSE is not a competitor to HTTP Streaming. SSE is one *kind* of HTTP Streaming.** ``` HTTP Streaming (the behavior: server sends response gradually) │ ├── SSE ← text/event-stream, blank-line framed ├── NDJSON ← one JSON object per line ├── Chunked HTML ← progressive page rendering ├── Raw binary chunks ← video/audio, file downloads └── Custom formats ← gRPC-Web, protobuf streams ``` Asking "SSE vs HTTP Streaming" is like asking "JSON vs HTTP body" — JSON is *one of the things* you can put in a body. **Why people conflate them:** 1. In browsers, `EventSource` is the only ergonomic way to consume HTTP streams. Everything else requires `fetch()` + `ReadableStream` + manual byte parsing. 2. ChatGPT, Claude, Perplexity all use SSE — so "streaming response" colloquially means "SSE response." 3. MCP's transport naming confused everyone: the old transport was "HTTP+SSE"; the new one is "Streamable HTTP" — these sound like alternatives when they're not. **When to pick which framing:** | Scenario | Best framing | Why | |----------|-------------|-----| | Browser receives live updates | **SSE** | Native `EventSource`, auto-reconnect, simple to parse | | AI agent token streaming to a UI | **SSE** | Browser-friendly, typed event names for tool use | | Server-to-server streaming | NDJSON | No event-name overhead, easy to log/replay | | Batch processing pipelines | NDJSON | Line-addressable, replayable, strong tooling | | stdio IPC (MCP local) | NDJSON | OS-pipe-friendly newline framing | | Binary data (video, files) | Raw chunks | No text encoding overhead | | gRPC internal ML platforms | gRPC frames | Strong types, multiplexing, codegen | --- ## 3. Bidirectionality — Half-Duplex vs Full-Duplex | | HTTP/1.1 Streaming | HTTP/2 Streaming | WebSocket | |--|-------------------|-----------------|-----------| | Direction per stream | Half-duplex | Half-duplex within a stream | **Full-duplex** | | Bidirectional in practice? | Only by opening 2+ requests | Yes, via separate streams | Yes, natively | | Auto-reconnect? | Yes (SSE `Last-Event-ID`) | Per protocol | No — implement yourself | | Works through corporate proxies? | Almost always | Almost always | Often blocked or buggy | | Good for AI token streaming? | **Yes** (standard choice) | Yes | Overkill | | Good for voice / collab editing? | No | Marginal | **Yes** | | Server can initiate? | No (only respond) | Server push (limited) | Yes, anytime | **The crisp distinction:** ``` HTTP Streaming = one-direction-at-a-time per request (walkie-talkie) WebSocket = both-directions-simultaneously on one connection (phone call) ``` **Why ChatGPT's "stop generating" button closes the connection:** once the POST is sent, the client is in receive-only mode. There's no upstream channel to send a "stop" message on. The only option is to drop the connection. **Pick HTTP Streaming (SSE) when:** - Data flow is dominantly server → client - You need to traverse corporate proxies and CDNs reliably - You want browser auto-reconnect and resumability for free - You're streaming LLM tokens, agent progress, A2UI updates **Pick WebSocket when:** - Genuinely simultaneous bidirectional flow (voice, multiplayer, collab editing) - Frequent small messages in both directions - Server initiates messages as often as the client does **Practical reality for AI apps:** 95% of the time, SSE is the right choice. WebSocket earns its weight for voice and collaborative editing — not for token streaming. --- ## 4. MCP Bidirectionality Pattern (How HTTP Fakes Full-Duplex) MCP needs bidirectional communication (clients call tools; servers call back via `sampling/createMessage`, `roots/list`, elicitation). It achieves this over half-duplex HTTP using two simultaneous requests: ``` CLIENT MCP SERVER │ │ │── POST /mcp (tools/call) ──────────►│ │◄── HTTP 200, SSE response ──────────│ ← Stream A: responds to client POST │◄── data: {progress notification} │ │◄── data: {final result} │ │ │ │── GET /mcp (Accept: SSE) ─────────►│ │◄── HTTP 200, SSE response ──────────│ ← Stream B: long-lived GET for │◄── data: {server→client request} │ server-initiated messages │ │ │── POST /mcp (response to above) ───►│ ← Client replies via new POST │◄── HTTP 200 ────────────────────────│ ``` "Bidirectional messaging" in MCP = messages flow in both directions across two streams, **not** both directions on a single TCP stream. **Two headers that carry the operational glue:** | Header | Purpose | |--------|---------| | `Mcp-Session-Id` | Set by server at init. Must be echoed on every subsequent request. Missing = silent state bugs. | | `Last-Event-ID` | Standard SSE header. Client sends last received ID on reconnect; server replays missed events. | **SSE deprecation note:** SSE was deprecated as a *standalone MCP transport* (March 2025, replaced by Streamable HTTP). SSE itself is alive and well for LLM streaming, dashboards, and all browser-facing live updates. --- ## 5. Real-World Wire Formats ### OpenAI / Compatible APIs (SSE, bare JSON) ``` data: {"id":"chatcmpl-123","object":"chat.completion.chunk","choices":[{"delta":{"content":"The"},"index":0}]} data: {"id":"chatcmpl-123","object":"chat.completion.chunk","choices":[{"delta":{"content":" answer"},"index":0}]} data: {"id":"chatcmpl-123","object":"chat.completion.chunk","choices":[{"delta":{},"finish_reason":"stop","index":0}]} data: [DONE] ``` - Bare JSON (not JSON-RPC) — request/response pairing is implicit in the HTTP exchange - `[DONE]` sentinel is OpenAI's convention, not part of the SSE spec - Client extracts `choices[0].delta.content` and appends to rendered message ### Anthropic (SSE, typed events) ``` event: message_start data: {"type":"message_start","message":{"id":"msg_01...","role":"assistant"}} event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"The answer"}} event: content_block_stop data: {"type":"content_block_stop","index":0} event: message_stop data: {"type":"message_stop"} ``` - `event:` field carries semantic type — clients dispatch on type rather than inspecting payload shape - Makes tool use, thinking blocks, and multi-modal content cleaner to handle - Mid-stream errors arrive as `event: error` with HTTP status still 200 ### MCP (JSON-RPC over SSE or newline-delimited) ``` event: message data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":"t1","progress":30}} event: message data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"done"}]}} ``` ### A2UI over SSE ``` event: a2ui data: {"version":"v0.9","createSurface":{"surfaceId":"ticket_1","catalogId":"..."}} event: a2ui data: {"version":"v0.9","updateDataModel":{"surfaceId":"ticket_1","path":"/status","value":"Open"}} ``` ### A2UI over NDJSON (replay / eval / debugging) ```jsonl {"version":"v0.9","createSurface":{"surfaceId":"ticket_1","catalogId":"..."}} {"version":"v0.9","updateDataModel":{"surfaceId":"ticket_1","path":"/status","value":"Open"}} {"version":"v0.9","updateComponents":{"surfaceId":"ticket_1","components":[...]}} ``` --- ## 6. Production Pitfalls Checklist ### ❌ Proxy Buffering (Silent Stream Killer) Nginx, CloudFront, and most CDNs buffer responses by default. Your "streaming" tokens arrive in one batch. **Fix:** ```nginx location /stream { proxy_buffering off; add_header X-Accel-Buffering no; } ``` Also set `X-Accel-Buffering: no` as a response header from your app server. ### ❌ gzip / Brotli Breaks SSE Compression middleware accumulates bytes before compressing — turns smooth token flow into chunky bursts. **Fix:** Disable compression for `text/event-stream` responses. The bandwidth saving is not worth the latency cost. ```ts // Express example app.use(compression({ filter: (req, res) => { if (res.getHeader('Content-Type') === 'text/event-stream') return false; return compression.filter(req, res); }, })); ``` ### ❌ SSE Connection Timeouts on Long Agent Runs Long agent runs exceed default proxy timeouts (often 60s). Connection drops mid-stream. **Fix:** Send periodic keepalive comments: ``` : keepalive ``` (A colon-prefixed line is an SSE comment — ignored by clients, but prevents timeout.) ### ❌ Mobile Reconnection Without `Last-Event-ID` iOS kills HTTP connections when the app backgrounds. Without `Last-Event-ID`, the stream restarts from scratch on reconnect. **Fix:** 1. Include `id:` field on every SSE event 2. Buffer events server-side for ≥60 seconds after sending 3. On reconnect, client sends `Last-Event-ID` header; server replays from that point ```ts // Client: use exponential backoff — default EventSource reconnect is too aggressive const es = new EventSource('/stream'); let retryDelay = 1000; es.onerror = () => { setTimeout(() => reconnect(), retryDelay); retryDelay = Math.min(retryDelay * 2, 30000); }; ``` 4. Surface "reconnecting..." state to the user — silent staleness is worse than a visible indicator 5. For long agent runs (>10 min backgrounded): provide a polling fallback that returns current state ### ❌ Missing `traceparent` Propagation A single user action may generate 30+ network calls. Without a correlation ID, cross-service debugging is impossible. **Fix:** Generate `traceparent` (W3C Trace Context) at the edge; forward it on every outbound call. ``` User action └─► Backend generates traceparent: 00-{trace_id}-{span_id}-01 ├─► POST to LLM API — Header: traceparent ├─► POST to MCP server — Header: traceparent, Mcp-Session-Id └─► SSE response to client — echo traceparent back ``` Every modern observability tool (Langfuse, Datadog, Honeycomb, Grafana Tempo) understands `traceparent`. Include it in NDJSON logs so you can join logs across services after the fact. ### ❌ Conflating JSON-RPC `id` and SSE `id` They are unrelated and live at different layers. | Field | Layer | Purpose | |-------|-------|---------| | JSON-RPC `id` | Message | Correlates request to response within a JSON-RPC session | | SSE `id` | Framing | Enables reconnection via `Last-Event-ID` header | ### ❌ Treating Streamable HTTP and SSE as Competitors Streamable HTTP (MCP) is a *transport pattern*. SSE is *one of the response formats* Streamable HTTP can return. They compose. ### ❌ WebSocket "Because It's Realtime" If data flow is server→client only (which most LLM streaming is), WebSocket adds operational complexity without benefit. SSE is simpler, proxy-friendly, and auto-reconnecting. Reach for WebSocket only when you genuinely need bidirectional realtime. --- ## 7. Decision Guide | Your need | Transport | Framing | Message | |-----------|-----------|---------|---------| | Chat UI with token streaming | HTTP Streaming | SSE | Provider JSON (OpenAI/Anthropic) | | Local AI app calling tools | stdio | newline-delimited | JSON-RPC (MCP) | | Remote AI app calling tools | HTTP Streaming | JSON or SSE | JSON-RPC (MCP) | | Agent pushing UI updates (A2UI) | HTTP Streaming | SSE | A2UI envelope | | Recording agent runs (eval/replay) | File / HTTP POST | NDJSON | App-specific trace | | Bulk LLM inference | HTTP | NDJSON | Provider JSON (Batch API) | | Voice / collaborative editing | WebSocket | WS frames | App-specific | | High-perf server-to-server AI | HTTP/2 | gRPC frames | Protobuf | | Simple one-shot answer | HTTP | — | Plain JSON |
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub