AI
AI building blocks for TypeScript. We build the hard parts, you keep the stack.
TanStack AI is a TypeScript library for building AI features and agents. It ships the agent loop, provider adapters, durability, interrupts, sandboxes, and tools, and plugs into the server, database, and UI you already have.
Skills
Install the agent skills from the skills folder of https://github.com/TanStack/ai for my user, read them, then ask me what AI features I want to build, or suggest some.
import { toolDefinition } from '@tanstack/ai'
import { z } from 'zod'
export const lookupInvoice = toolDefinition({
name: 'lookup_invoice',
description: 'Find an invoice by id',
inputSchema: z.object({ id: z.string() }),
outputSchema: z.object({
total: z.number(),
status: z.enum(['draft', 'sent', 'paid']),
}),
})This file never changes. Everything on the right is a destination for it.
import { chat, toServerSentEventsResponse } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
import { createFileRoute } from '@tanstack/react-router'
import { lookupInvoice } from './tools'
export const Route = createFileRoute('/api/chat')({
server: {
handlers: {
POST: async ({ request }) => {
const { messages } = await request.json()
const stream = chat({
adapter: openaiText('gpt-5.5'),
messages,
tools: [lookupInvoice.server(findInvoice)],
})
return toServerSentEventsResponse(stream)
},
},
},
})Open protocol
AG-UI compliant, in both directions.
The client sends AG-UI requests and consumes AG-UI events, so the agent on the other end is replaceable: point the same client at a Python, Go, or PHP runtime and it keeps working. Bring your own transport.
AG-UI sits between your web app and your AI endpoint, with traffic in both directions. The server then talks to a provider such as OpenAI or Anthropic.
CLIENT
your web app
AG-UI
communication protocol
Server
your ai endpoint
Provider
openai, anthropic
Typesafe models
Typed options for every model.
Pick a model and TypeScript narrows the fields to what it supports. Input parts for chat. Pixel sizes on one image model and aspect ratio plus resolution on the next. Durations and tiers for video. Resolution for world models. The wrong value fails in the editor, not in production.
import { openaiText } from '@tanstack/ai-openai'
const result = await chat({
adapter: openaiText('gpt-6-astra'),
messages: [{ role: 'user', content: [{ type: 'image', source: receiptUrl }] }],
})
input for gpt-6-astra
Input parts are typed per model.
✓ no errors. 'image' is a valid input for gpt-6-astra.
We handle tools
Define a tool once. Run it on either side.
One schema gives you the input and output types on the server and the client. The loop calls the tool, waits for approval when asked, applies the user's edits, and feeds the result back to the model.
const lookupInvoice = toolDefinition({
name: 'lookup_invoice',
inputSchema: z.object({ id: z.string() }),
outputSchema: invoiceSchema,
needsApproval: true,
})
lookupInvoice.server(async ({ id }) => {
return db.invoices.update({
where: { id },
data: { lastViewedAt: new Date() },
})
})
The server implementation uses the same typed id to update a row in your database. The model never sees your credentials.
You own the UI
Messages are parts. Render however you like.
Text, thinking, tool calls and results all arrive as typed parts with their own state. Loop over the parts and render each one, or hand a component per part type to createChatHook and it picks the right one for you.
A message is a list of parts. A thinking part, then a tool call that moves from awaiting input through approval to complete, then the tool result and the streamed text reply. Below the list, the component registered for the active part.
tool-call lifecycle
You own persistence
Your database. Your schema.
Persistence is two functions: loading and saving a thread. With the ai-persistence skill shipped with the package, your coding agent can wire them to your tables and ORM in one pass.
import { defineAIPersistence, defineMessageStore } from '@tanstack/ai-persistence'
// The whole contract. Your tables, your columns, your types.
export const persistence = defineAIPersistence({
stores: {
messages: defineMessageStore({
loadThread: (threadId) =>
sql`select messages from threads where id = ${threadId}`.then((rows) => rows[0]?.messages ?? []),
saveThread: async (threadId, messages) => {
await sql`insert into threads (id, messages) values (${threadId}, ${sql.json(messages)})
on conflict (id) do update set messages = excluded.messages`
},
}),
},
})
// chat({ ..., middleware: [withPersistence(persistence)] })Durability you can move
Refresh mid-answer and nothing is lost.
Every chunk is written to a log before it is delivered. Drop the socket or refresh the page and the client replays from the last offset instead of losing the model's answer.
import { chat, memoryStream, toServerSentEventsResponse } from '@tanstack/ai'
// Development and single-process apps. Zero setup.
export async function POST(request: Request) {
const stream = chat({ /* ... */ })
return toServerSentEventsResponse(stream, {
durability: { adapter: memoryStream(request) },
})
}We handle the hard parts
Sandboxes, Code Mode, MCP, memory, compaction.
With each feature as its own package, load what the task needs and leave out the rest.
@tanstack/ai-code-mode
The model chains your tools into one script and runs it in an isolate, instead of one round-trip per call.
@tanstack/ai-sandbox
Run Claude Code, Codex, or any ACP agent as a chat backend in a local process or a sandbox. Its activity streams back as events your UI already renders.
@tanstack/ai-mcp
A typed MCP client with a CLI that generates the types, plus interactive widgets rendered from tool results.
@tanstack/ai-memory · @tanstack/ai-compaction
Recall across sessions through Redis, mem0, Honcho, or Hindsight. Compaction keeps long threads inside the model window.
Beyond chat
Images, video, speech, voice, and live worlds.
The same adapters and the same persistence cover every modality, with progress updates and cost tracking built in.
chat · outputSchema · summarize
Structured output that matches your schema exactly.
generateSpeech · generateTranscription · generateAudio
Transcription with word timestamps and diarization, plus music and sound effects.
openaiRealtimeToken · RealtimeClient
OpenAI, Grok, and ElevenLabs with VAD modes and tool calling inside a live session.
generateWorld · generateLiveVideo
Mint a session on the server and stream an explorable world or live video into the browser over WebRTC.
Devtools
See every action on both sides.
Every tool call, interrupt, memory recall, and finish reason, on the server and in the client, in one timeline.
hooks
run timeline
thread_7f2 · run_3
Start here
Pick the page that matches your next hour.
Each one is a short guide with copyable code, not a tour.
Partners
Sponsors get special perks like private discord channels, priority issue requests, and direct support!