GistUI Format says what a program means. A binding says how one travels. The bindings are drafts and non-normative; the texts are in spec/bindings/.
HTTP and SSE streams
The simplest binding, and the one the library uses.
- A plain text response. The body is the program,
Content-Type: text/vnd.gistui; charset=utf-8(ortext/plainuntil the type is registered), sent in chunks as the model produces it. - Server-sent events. A provider’s own SSE format (OpenAI, Anthropic, the AI SDK’s UI message stream) carries the program as its text deltas. The client, or your server, concatenates them.
- Inline mode. The text is Markdown chat with programs in
gistuifences.
// Server: any provider's events to text
return new Response(toReadable(textDeltas(res, "openai")));
// Client: the body is the program
<GistUI library={ui} stream={res.body} />MCP
Three ways to use GistUI with the Model Context Protocol.
A program as a resource
{
"contents": [
{ "uri": "gistui://reports/q3", "mimeType": "text/vnd.gistui", "text": "root = Page(…)\n…" }
]
}A program as a tool result
A tool returns a program as an embedded resource next to a plain-text fallback, so a host that does not know GistUI still has something to show.
{
"content": [
{ "type": "text", "text": "Q3 revenue was $1.2M, up 8%." },
{ "type": "resource",
"resource": { "uri": "gistui://result/1", "mimeType": "text/vnd.gistui", "text": "root = Page(…)\n…" } }
]
}A GistUI view as an MCP App
The MCP Apps extension lets a server ship an HTML view that the host shows in a sandboxed iframe. A GistUI renderer fits in such a view, and the host needs no GistUI support. This is the most practical MCP binding today.
MCP tools as GistUI tools
In the other direction, an MCP client can be the tools of a renderer: any object with callTool({ name, arguments }) works. Keep the read/write split: only tools without side effects belong under tools.
AG-UI
AG-UI carries an agent’s text as TEXT_MESSAGE_CONTENT events. The program is the text of a message.
return new Response(toReadable(textDeltas(res, "agui")));A person’s action goes back as the next user message, marked as coming from the UI. See AG-UI agents.
A2UI
A2UI and GistUI both keep components in a flat, id-addressed set that can be filled in any order, so a static mapping is direct.
| GistUI Format | A2UI 0.9.1 | |
|---|---|---|
| What the model writes | line-based text | JSON messages |
| Units | statements with ids; nested calls allowed | a flat list of components with ids |
| Root | the statement root |
the component with id root |
| Component set | a catalog manifest | a catalog named by catalogId |
| Data | tables, inlined into props | a data model per surface, bound by path |
| State and logic | client-side state and expressions | data model updates from the agent; client functions |
| Updates | later statements replace earlier ones | updateComponents, updateDataModel |
| User input | action envelopes | action messages to the agent |
Compiling a tree to A2UI: createSurface, then updateComponents with one entry per node (the node id, the mapped component name, converted props, child ids). Tables go into the data model with updateDataModel, and the component binds to that path.
What does not map statically: runtime expressions and @each need either the compiler’s own GistUI runtime or a mapping to A2UI bindings for the simple cases; @set and @run need a runtime or A2UI client functions; diagnostics have no A2UI counterpart.
So a GistUI program can be shown by an A2UI renderer, and an A2UI catalog can be used from GistUI, with documented losses on both sides.