Skip to content
docs
UseBuildChangelog
Open Lumin

Guide

Stream responses

Two different things in this stack are called streaming, and they are easy to conflate. The MCP wire between your backend and Lumin is SSE. Whether YOUR user sees a typing effect is a separate choice you make in your own UI.

The MCP wire is SSE, always

Lumin runs the MCP SDK's stateless HTTP handler, which answers every POST /mcp as a Server-Sent-Events stream: one or more data: lines carrying the JSON-RPC payload, not a plain JSON body. There is no JSON-only mode to opt into.

This is why a bare curl | jq pipe fails

Two consequences follow directly. The request needs Accept: application/json, text/event-stream, both values, on every call, or the server answers 406. And a plain curl ... | jq pipe fails on the response, because jq is handed an SSE frame, not JSON: strip the data: prefix first. See quickstart for the working idiom.

One handler, two protocol revisions

The same handler serves both eras the MCP spec has had recently: 2025-11-25 and 2026-07-28. A 2025-11-25 client gets the familiar initialize handshake and a session. A 2026-07-28 client gets the newer stateless wire instead: protocol version, client info and capabilities travel in _meta on every request, there is no Mcp-Session-Id header, and there is no session state kept between calls. Clients negotiate whichever revision they speak, so nothing about this changes for anyone on a fixed date.

Lumin's birth-data model already assumed the stateless shape: every tool call carries its own birth fields, because nothing set in an earlier call is remembered. The 2026-07-28 wire made that the protocol's own default rather than only this server's convention.

Streaming YOUR user's experience is a separate layer

A reading is 12 to 40-plus sequential tool calls behind the scenes. Nothing about the MCP wire streams that to an end user, that is the model layer, one level up. If you are driving the reading with the Anthropic SDK, stream the model's own output (its narration between tool calls, and its final answer) to your chat UI as it arrives, using the SDK's own streaming helper. See integrate into your app for a worked example, including the model call shape and the two Lumin-specific gotchas in it: max_tokens as a whole-turn budget the tool calls count against, and handling stop_reason: "pause_turn" on a long tool-calling turn.