Skip to content

Commit 231bc91

Browse files
authored
blog: TanStack AI now fully speaks AG-UI (#927)
* blog: TanStack AI now fully speaks AG-UI Announce client-to-server AG-UI 0.0.52 compliance for TanStack AI. Server-to-client AG-UI events were already supported; this release completes the round trip with the request-side wire format (RunAgentInput) and adds chatParamsFromRequest + mergeAgentTools server helpers. Migration is fully backward compatible via three deprecation bridges and a jscodeshift codemod. * blog(ag-ui-compliance): clarify "TanStack server" → "@tanstack/ai endpoint" The original phrasing read as "a server from TanStack" when the real meaning is "any server endpoint built with @tanstack/ai" (which is a library you drop into TanStack Start, Next.js, Hono, raw Node, Bun, etc). Rephrased in five places: intro, why-this-matters asymmetry paragraph, the cross-vendor framing, the nothing-breaks paragraph, and the bidirectional-interop bold lead.
1 parent a8809e8 commit 231bc91

2 files changed

Lines changed: 156 additions & 0 deletions

File tree

2.81 MB
Loading

src/blog/ag-ui-compliance.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
---
2+
title: 'TanStack AI now fully speaks AG-UI'
3+
published: 2026-05-17
4+
excerpt: 'Server-to-client AG-UI events already worked. TanStack AI now completes the round trip with client-to-server AG-UI compliance. Fully backward compatible.'
5+
library: ai
6+
authors:
7+
- Alem Tuzlak
8+
---
9+
10+
![TanStack AI now fully speaks AG-UI](/blog-assets/ag-ui-compliance/header.png)
11+
12+
Half the protocol was already there.
13+
14+
For a while now, endpoints built with `@tanstack/ai` have emitted [AG-UI](https://ag-ui.com) events on the wire going out. The streaming side of the conversation (`RUN_STARTED`, tool-call events, run finish, errors) was already a compliant AG-UI event stream. The piece that was still proprietary was the _other_ direction: the request body going from client to server. The TanStack client POSTed `{ messages, data }`, not AG-UI's `RunAgentInput`.
15+
16+
That last half is what this release fixes. **TanStack AI is now fully AG-UI compliant in both directions.** Server-to-client events were AG-UI before. Client-to-server requests are AG-UI now. The round trip is done.
17+
18+
The same `@tanstack/ai-client` can hit any AG-UI server. Any AG-UI client can hit an endpoint built with `@tanstack/ai`, wherever you host it (TanStack Start, Next.js, Hono, raw Node, Bun, anywhere). And nothing about your existing code breaks.
19+
20+
## Why this matters
21+
22+
AG-UI is an open protocol for agent-to-frontend communication. It defines a single wire format, `RunAgentInput`, that carries the conversation, the tools, the thread and run IDs, and arbitrary forwarded properties. Servers that speak AG-UI can be addressed by any compliant client. Clients that emit AG-UI can talk to any compliant server.
23+
24+
With server-to-client AG-UI already in place, a `@tanstack/ai` endpoint could _stream_ to a compliant client. But the client-to-server side was a one-way mirror: only the TanStack client could _send_ requests that endpoint understood. The asymmetry meant true cross-vendor interop was still gated on rewriting your request layer.
25+
26+
Closing that gap is what this release does. The whole ecosystem (CopilotKit, CrewAI, LangGraph adapters, and now TanStack AI) gets to share the same plumbing in both directions.
27+
28+
## What changed on the wire
29+
30+
Before this release, `@tanstack/ai-client` POSTed:
31+
32+
```json
33+
{
34+
"messages": [...],
35+
"data": { ... }
36+
}
37+
```
38+
39+
After:
40+
41+
```json
42+
{
43+
"threadId": "thread-7f2a",
44+
"runId": "run-a91",
45+
"state": {},
46+
"messages": [...],
47+
"tools": [...],
48+
"context": [],
49+
"forwardedProps": { ... },
50+
"data": { ... }
51+
}
52+
```
53+
54+
The new envelope is the full AG-UI `RunAgentInput`. The old `data` field is still emitted as a mirror of `forwardedProps` so legacy servers reading `body.data.X` keep working unchanged. `threadId` persists per session, `runId` is fresh per send, and `tools` carries the client's `clientTools` declarations so the server can dispatch tool calls without a static registry.
55+
56+
Server-to-client events haven't changed shape, because they were already AG-UI compliant. They just now carry the matching `threadId` and `runId` you sent in.
57+
58+
## What changed in the API
59+
60+
Three new things to know about, all opt-in.
61+
62+
### `chat()` accepts `threadId`, `runId`, `parentRunId`
63+
64+
These were always part of the AG-UI event semantics on the way out. They're now first-class options on `chat()` and flow through every provider adapter into `RUN_STARTED` events for observability and run correlation.
65+
66+
```ts
67+
import { chat } from '@tanstack/ai'
68+
import { openaiText } from '@tanstack/ai-openai/adapters'
69+
70+
const stream = chat({
71+
adapter: openaiText('gpt-4o'),
72+
threadId: 'thread-7f2a',
73+
runId: 'run-a91',
74+
messages: [...],
75+
})
76+
```
77+
78+
If you don't pass them, the runtime auto-generates a stable `threadId` per request and a fresh `runId` per call. Existing code that didn't know about them keeps working.
79+
80+
### `chatParamsFromRequest` for the server
81+
82+
A one-import helper that reads `req.json()`, validates the body against the AG-UI `RunAgentInputSchema`, and gives you a clean params object. On invalid input it throws a `400 Response` that frameworks like TanStack Start, SolidStart, Remix, and React Router 7 return to the client automatically.
83+
84+
```ts
85+
import {
86+
chat,
87+
chatParamsFromRequest,
88+
toServerSentEventsResponse,
89+
} from '@tanstack/ai'
90+
import { openaiText } from '@tanstack/ai-openai/adapters'
91+
92+
export async function POST(req: Request) {
93+
const params = await chatParamsFromRequest(req)
94+
const stream = chat({
95+
adapter: openaiText('gpt-4o'),
96+
messages: params.messages,
97+
threadId: params.threadId,
98+
tools: serverTools,
99+
})
100+
return toServerSentEventsResponse(stream)
101+
}
102+
```
103+
104+
That's the whole server. No body shape to remember, no manual validation, and a typed `params.forwardedProps` if you want client-driven options like provider, model, or temperature.
105+
106+
### `forwardedProps` replaces `body` on the client
107+
108+
`useChat({ body: {...} })` still works, but `body` is now `@deprecated`. The canonical name is `forwardedProps`, which is what the new wire format calls the field. A jscodeshift codemod ships in the repo to flip every site:
109+
110+
```bash
111+
npx jscodeshift \
112+
--parser=tsx \
113+
-t https://raw.githubusercontent.com/TanStack/ai/main/codemods/ag-ui-compliance/transform.ts \
114+
"src/**/*.{ts,tsx}"
115+
```
116+
117+
It's import-source gated, so files that don't import from `@tanstack/ai*` are left alone.
118+
119+
## Nothing breaks
120+
121+
This is the part most "wire format change" releases get wrong. The upgrade ships three compatibility bridges so old code keeps running:
122+
123+
| Surface | Legacy (still works) | Canonical |
124+
| ---------------------- | ---------------------------------------- | ------------------------- |
125+
| Client option | `body: { ... }` | `forwardedProps: { ... }` |
126+
| Server wire field | `body.data.X` (mirror of forwardedProps) | `body.forwardedProps.X` |
127+
| Server `chat()` option | `conversationId` | `threadId` |
128+
129+
An existing endpoint reading `body.data.provider` keeps reading `body.data.provider` because the client emits both `data` and `forwardedProps` with the same content. A `chat({ conversationId })` call keeps working because `conversationId` is now a deprecated alias of `threadId`. Mix old and new freely. The bridges will be removed in the next major release, so migrate at your convenience.
130+
131+
## Bidirectional interop in practice
132+
133+
With both halves of the protocol compliant, the boundaries between AI SDKs get a lot blurrier.
134+
135+
**A pure AG-UI client (no TanStack code) hitting a `@tanstack/ai` endpoint** works end-to-end. Tool messages pass through as `ModelMessage` entries with `role: 'tool'`. AG-UI `reasoning` and `activity` messages with no TanStack equivalent are dropped at the boundary. `developer` messages collapse to `system` role. The outbound event stream was already AG-UI, so the foreign client renders it natively.
136+
137+
**A TanStack client hitting a foreign AG-UI server** works for the common cases. Single-turn user messages mirror to AG-UI's `content` field. Server-emitted events stream and render. Multi-turn history with tool results from prior turns survives because the client sends AG-UI fan-out duplicates alongside the TanStack anchor messages.
138+
139+
The practical upshot: if you've been waiting to try a different inference provider, a different framework's agent runtime, or a different orchestrator, the wire is no longer the thing standing in your way. Both directions speak the same language.
140+
141+
## What's not in this release
142+
143+
A few things were intentionally left out:
144+
145+
- **Reasoning replay to LLM providers.** TanStack still drops `ThinkingPart` at the `UIMessage``ModelMessage` boundary. Providers like Anthropic that require thinking blocks to be replayed for extended thinking continuation are a separate track.
146+
- **AG-UI `state` and `context` fields.** Surfaced on the params object but not yet wired into `chat()`. They're available for your endpoint to inspect or forward.
147+
- **PHP and Python server packages.** No `chatParamsFromRequest` parity yet. Those examples temporarily lag on the old shape until the matching helpers ship.
148+
149+
## Try it
150+
151+
Upgrade `@tanstack/ai` and `@tanstack/ai-client` to the latest. If you're using one of the framework wrappers (`@tanstack/ai-react`, `-vue`, `-svelte`, `-solid`, `-preact`), bump those too so the client wire stays in lockstep.
152+
153+
- [Migration guide](https://tanstack.com/ai/migration/ag-ui-compliance) walks through the three deprecation bridges and the codemod
154+
- [Star the repo](https://github.com/TanStack/ai) if this saved you an adapter
155+
156+
The AI stack is supposed to be the part you compose, not the part that locks you in. AG-UI is how that starts being true across vendors. With this release, TanStack AI is the first SDK to ship full bidirectional client-to-server _and_ server-to-client compliance against the AG-UI 0.0.52 spec. The next agent runtime you adopt should not be the one that finally forces you to rewrite your wire layer.

0 commit comments

Comments
 (0)