- MCP Server ToolsUpdated
- Managed MCP with chat()Updated
- Manual MCPUpdated
- MCP Type GenerationUpdated
- MCP Client InputUpdated
- MCP Server Tools: AdvancedNew
- MCP EventsNew
- MCP AppsUpdated
- Serve Tools over HTTPUpdated
- Serve Resources and PromptsUpdated
- Ask for InputUpdated
- Draft Text from a ToolUpdated
- MCP Server TasksUpdated
- MCP Server AuthUpdated
- MCP Server SessionsUpdated
- MCP Server on stdioUpdated
Persistence (Advanced)
- GenerationsUpdated
- Realtime Voice ChatUpdated
- Text-to-SpeechUpdated
- TranscriptionUpdated
- Transcription: AdvancedNew
- Voice CreationUpdated
- Audio RecordingUpdated
- Audio GenerationUpdated
- Image GenerationUpdated
- Image Generation: AdvancedNew
- Video GenerationUpdated
- Video Generation: AdvancedNew
- Live GenerationUpdated
- World GenerationUpdated
- Generation HooksUpdated
- OverviewUpdated
- Quick StartUpdated
- ProvidersUpdated
- HarnessesUpdated
- Harness AuthUpdated
- WorkspaceUpdated
- ToolsUpdated
- PolicyUpdated
- Lifecycle & SnapshotsUpdated
- Portable SnapshotsUpdated
- Keep Files After ReloadUpdated
- Save a Named VersionUpdated
- Branch From a VersionUpdated
- Send a Frozen FileUpdated
- Let the Agent Save and ForkUpdated
- Pick Which Files to KeepUpdated
- What a Snapshot StoresUpdated
- Instance DurabilityUpdated
- Durable RunsUpdated
- EventsUpdated
Sandboxes (Advanced)
- Runtime ContextUpdated
- Bring Your Own KeyUpdated
- Debug LoggingUpdated
- Multimodal ContentUpdated
- Files APIUpdated
- Per-Model Type SafetyUpdated
- Runtime Adapter SwitchingUpdated
- Tree-ShakingUpdated
- Extend AdapterUpdated
- Extend Adapter: AdvancedNew
- Typed Pre-Configured OptionsUpdated
- Test with a Fake ModelUpdated
- Prompt CachingUpdated
- Mid-Conversation ChangesUpdated
- Approval Flow ProcessingUpdated
HTTP Stream
Some places do not let SSE through: some edge runtimes, some mobile WebViews, or a proxy that strips text/event-stream. Stream newline-delimited JSON (NDJSON) instead. Each line is one JSON StreamChunk.
1. Return NDJSON from the server#
Wrap the chat() stream with toHttpResponse():
import { chat, chatParamsFromRequest, toHttpResponse } from "@tanstack/ai";
import { openaiText } from "@tanstack/ai-openai";
export async function POST(request: Request) {
const { messages, threadId, runId } = await chatParamsFromRequest(request);
const stream = chat({
adapter: openaiText("gpt-5.6"),
messages,
threadId,
runId,
});
return toHttpResponse(stream);
}To write the response yourself, write each chunk as JSON.stringify(chunk) + "\n" to the response body.
2. Connect from the client#
Pass fetchHttpStream() to useChat:
import { useChat, fetchHttpStream } from "@tanstack/ai-react";
const { messages } = useChat({
connection: fetchHttpStream("/api/chat"),
});fetchHttpStream takes the same options as fetchServerSentEvents: url, headers, body, fetchClient, and dynamic functions. See Request Options.
On React Native, use xhrHttpStream with the same server. See React Native.
Resume a dropped stream#
Pass a durability adapter to toHttpResponse. Then each line becomes an { id, chunk } envelope, and the client can continue a reply:
- Reconnect. A dropped connection reconnects with Last-Event-ID. The adapter removes the replayed chunks that it already has.
- joinRun(runId). It attaches to a run that already exists.
Add a GET handler for joinRun, with resumeHttpResponse:
import {
chat,
chatParamsFromRequest,
memoryStream,
resumeHttpResponse,
toHttpResponse,
} from "@tanstack/ai";
import { openaiText } from "@tanstack/ai-openai";
export async function POST(request: Request) {
const { messages, threadId, runId } = await chatParamsFromRequest(request);
const stream = chat({ adapter: openaiText("gpt-5.6"), messages, threadId, runId });
return toHttpResponse(stream, {
durability: { adapter: memoryStream(request) },
});
}
// joinRun hits GET ?offset=-1&runId=... (replay only, no messages sent).
export async function GET(request: Request) {
return resumeHttpResponse({ adapter: memoryStream(request) });
}xhrHttpStream resumes the same way. The guarantees are the same as resumable SSE, over NDJSON.
Send a message. Each chunk arrives as one JSON line, and the reply streams into messages.