@assistant-ui/mcp-docs-server 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/.docs/organized/code-examples/waterfall.md +1 -1
  2. package/.docs/organized/code-examples/with-a2a.md +2 -2
  3. package/.docs/organized/code-examples/with-ag-ui.md +3 -3
  4. package/.docs/organized/code-examples/with-ai-sdk-v7.md +5 -5
  5. package/.docs/organized/code-examples/with-artifacts.md +5 -5
  6. package/.docs/organized/code-examples/with-assistant-transport.md +3 -3
  7. package/.docs/organized/code-examples/with-browser-extension.md +4 -4
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +5 -5
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +5 -5
  10. package/.docs/organized/code-examples/with-cloud.md +5 -5
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +6 -6
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +7 -7
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +8 -8
  14. package/.docs/organized/code-examples/with-eve.md +3 -3
  15. package/.docs/organized/code-examples/with-expo.md +13 -19
  16. package/.docs/organized/code-examples/with-external-store.md +3 -3
  17. package/.docs/organized/code-examples/with-ffmpeg.md +5 -5
  18. package/.docs/organized/code-examples/with-generative-ui.md +259 -23
  19. package/.docs/organized/code-examples/with-google-adk.md +2 -2
  20. package/.docs/organized/code-examples/with-heat-graph.md +1 -1
  21. package/.docs/organized/code-examples/with-image-generation.md +4 -4
  22. package/.docs/organized/code-examples/with-interactables.md +5 -5
  23. package/.docs/organized/code-examples/with-langchain.md +7 -7
  24. package/.docs/organized/code-examples/with-langgraph.md +4 -4
  25. package/.docs/organized/code-examples/with-livekit.md +7 -7
  26. package/.docs/organized/code-examples/with-mcp.md +6 -6
  27. package/.docs/organized/code-examples/with-nuxt.md +500 -564
  28. package/.docs/organized/code-examples/with-opencode.md +2 -2
  29. package/.docs/organized/code-examples/with-openui.md +449 -0
  30. package/.docs/organized/code-examples/with-pi.md +2 -2
  31. package/.docs/organized/code-examples/with-react-hook-form.md +7 -7
  32. package/.docs/organized/code-examples/with-react-ink-web.md +4 -4
  33. package/.docs/organized/code-examples/with-react-ink.md +2 -2
  34. package/.docs/organized/code-examples/with-react-router.md +4 -4
  35. package/.docs/organized/code-examples/with-resumable-stream.md +7 -7
  36. package/.docs/organized/code-examples/with-store.md +1 -1
  37. package/.docs/organized/code-examples/with-svelte.md +415 -0
  38. package/.docs/organized/code-examples/with-sveltekit.md +1061 -0
  39. package/.docs/organized/code-examples/with-tanstack.md +5 -5
  40. package/.docs/organized/code-examples/with-tap-runtime.md +2 -2
  41. package/.docs/organized/code-examples/with-virtualized-thread.md +2 -2
  42. package/.docs/organized/code-examples/with-vue.md +1 -1
  43. package/.docs/raw/docs/(docs)/cli.mdx +6 -1
  44. package/.docs/raw/docs/(docs)/installation.mdx +2 -2
  45. package/.docs/raw/docs/(reference)/api-reference/generative-ui/slack.mdx +5 -1
  46. package/.docs/raw/docs/(reference)/api-reference/generative-ui/teams.mdx +9 -2
  47. package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +2 -2
  48. package/.docs/raw/docs/(reference)/api-reference/integrations/eve.mdx +39 -1
  49. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +29 -4
  50. package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +1 -1
  51. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +29 -1
  52. package/.docs/raw/docs/(reference)/api-reference/voice/session.mdx +1 -1
  53. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +1 -1
  54. package/.docs/raw/docs/cloud/ai-sdk.mdx +2 -2
  55. package/.docs/raw/docs/cloud/index.mdx +1 -1
  56. package/.docs/raw/docs/guides/attachments.mdx +2 -2
  57. package/.docs/raw/docs/guides/context-api.mdx +15 -17
  58. package/.docs/raw/docs/guides/dictation.mdx +1 -1
  59. package/.docs/raw/docs/guides/mentions.mdx +2 -0
  60. package/.docs/raw/docs/guides/suggestions.mdx +6 -3
  61. package/.docs/raw/docs/ink/primitives.mdx +1 -1
  62. package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +146 -128
  63. package/.docs/raw/docs/migrations/v0-15.mdx +34 -0
  64. package/.docs/raw/docs/primitives/suggestion.mdx +3 -1
  65. package/.docs/raw/docs/primitives/thread.mdx +1 -1
  66. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +35 -5
  67. package/.docs/raw/docs/runtimes/claude-managed-agents.mdx +118 -0
  68. package/.docs/raw/docs/runtimes/concepts/stability.mdx +2 -1
  69. package/.docs/raw/docs/runtimes/concepts/threads.mdx +68 -28
  70. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +1 -1
  71. package/.docs/raw/docs/runtimes/custom/external-store.mdx +6 -2
  72. package/.docs/raw/docs/runtimes/langchain.mdx +1 -1
  73. package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +4 -0
  74. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +8 -1
  75. package/.docs/raw/docs/tools/defining-tools.mdx +19 -0
  76. package/.docs/raw/docs/tools/generative-ui-primitive.mdx +180 -0
  77. package/.docs/raw/docs/tools/generative-ui-slack.mdx +167 -0
  78. package/.docs/raw/docs/tools/generative-ui-teams.mdx +160 -0
  79. package/.docs/raw/docs/tools/generative-ui.mdx +224 -211
  80. package/.docs/raw/docs/tools/index.mdx +2 -1
  81. package/.docs/raw/docs/tools/interactables.mdx +5 -4
  82. package/.docs/raw/docs/tools/openui.mdx +175 -0
  83. package/.docs/raw/docs/tools/tool-ui.mdx +1 -2
  84. package/.docs/raw/docs/tools/user-managed-mcp.mdx +5 -1
  85. package/.docs/raw/docs/ui/attachment.mdx +27 -0
  86. package/.docs/raw/docs/ui/file.mdx +1 -1
  87. package/.docs/raw/docs/ui/image.mdx +1 -1
  88. package/package.json +4 -4
  89. package/.docs/raw/docs/tools/interactables-legacy.mdx +0 -423
@@ -1,179 +1,240 @@
1
1
  ---
2
- title: Generative UI (JSON spec)
3
- description: Render agent-described React UI from a JSON spec with a consumer-provided component allowlist.
2
+ title: Generative UI
3
+ description: Let the model compose an interface at runtime from a component vocabulary you ship, using the present tool from @assistant-ui/react-generative-ui.
4
+ platforms: ["react"]
4
5
  ---
5
6
 
6
- `MessagePrimitive.GenerativeUI` is a first-class primitive for rendering UI
7
- described by the agent at runtime as a JSON spec. Instead of hard-coding a
8
- component per tool, the agent emits a `generative-ui` message part containing
9
- a tree of components by name. assistant-ui resolves each name against a
10
- **consumer-provided allowlist** and renders the result.
7
+ Generative UI inverts the usual tool-rendering relationship. Instead of writing one component per tool, you ship a **vocabulary** of components and let the model assemble them. The model calls a single `present` tool whose arguments are a JSON tree of component names and props, and assistant-ui renders that tree.
11
8
 
12
- > The allowlist controls **which** components the agent may render: any name
13
- > not in it throws a typed `GenerativeUIRenderError` (no implicit fallback). It
14
- > does not constrain the props passed to those components; see [Security](#security).
15
-
16
- > **Opt-in feature:** The default shadcn `Thread` does **not** render
17
- > `generative-ui` parts. You must wire the primitive explicitly — see
18
- > [Opt-in wiring](#opt-in-wiring).
9
+ The package ships a default vocabulary of 27 components (cards, facts, tables, charts, forms, controls), so the shortest useful setup registers one tool and writes no components at all.
19
10
 
20
11
  ## Which generative UI pattern?
21
12
 
22
- assistant-ui uses "generative UI" in three different places. Pick the one that
23
- matches your integration:
24
-
25
- | Pattern | API | Best for | Streaming |
26
- |---------|-----|----------|-----------|
27
- | **Generative UI primitive** | `MessagePrimitive.GenerativeUI` + allowlist | Composing dashboards, cards, and layouts from a component vocabulary you ship | Native `generative-ui` parts update progressively when the part spec changes incrementally |
28
- | **Tool UI** | `Tools({ toolkit })` with `render` | Interactive widgets tied to a known tool (forms, pickers, charts) | Tool **args** stream while the model fills them in |
29
- | **LangGraph data UI** | `makeAssistantDataUI` + `ui_message` | LangGraph agents emitting UI via the LangGraph stream | UI messages arrive on the LangGraph custom channel |
13
+ assistant-ui uses "generative UI" for more than one thing. Two questions separate them: does the **model** compose the layout or do you bind it ahead of time, and does the UI originate from a **tool call** or from a part your backend emits. Pick the row that matches what you are building:
30
14
 
31
- See also: [Tool UI guide](/docs/tools/tool-ui), [LangGraph generative UI](/docs/runtimes/langgraph/generative-ui).
15
+ | Pattern | API | Best for |
16
+ |---------|-----|----------|
17
+ | **The `present` tool** (this page) | `JSONGenerativeUI` + `present` | The model composes dashboards, cards, and layouts from a vocabulary you ship |
18
+ | **[Tool UI](/docs/tools/tool-ui)** | toolkit `render` | A widget tied to a tool you already know about (forms, pickers, charts) |
19
+ | **[Generative UI primitive](/docs/tools/generative-ui-primitive)** | `MessagePrimitive.GenerativeUI` + allowlist | A backend that already emits `generative-ui` message parts |
20
+ | **[LangGraph data UI](/docs/runtimes/langgraph/generative-ui)** | `makeAssistantDataUI` + `ui_message` | LangGraph agents emitting UI on the LangGraph stream |
32
21
 
33
- ## When not to use the primitive
22
+ The first two are tool-driven, so the model decides when UI appears; the last two are backend-driven, so your agent does. The backend-driven pair splits on transport rather than on either axis: the primitive reads a `generative-ui` message part, while LangGraph data UI reads `push_ui_message` off the LangGraph stream. `present` and the primitive both take a JSON component tree, but in [non-interchangeable shapes](/docs/tools/generative-ui-primitive#spec-shape). A third-party option also exists in the tool-driven space: [OpenUI](/docs/tools/openui) publishes an integration that streams OpenUI Lang through the same shape.
34
23
 
35
- - **User input and two-way interaction** → [Tool UI](/docs/tools/tool-ui) or [Interactables](/docs/tools/interactables)
36
- - **LangGraph `push_ui_message`** → [LangGraph data UI](/docs/runtimes/langgraph/generative-ui)
37
- - **Untrusted HTML or third-party widgets** → [MCP Apps](/docs/tools/mcp-apps) (sandboxed frames)
24
+ Browse every component the default vocabulary offers in the [component vocabulary reference](/elements/vocabulary), and see finished compositions in the [Generative section of Elements](/elements).
38
25
 
39
26
  ## Quick start
40
27
 
41
- ### 1. Define your component allowlist
28
+ <Steps>
42
29
 
43
- ```tsx title="components/gui.tsx"
44
- const Card = ({ title, children }) => (
45
- <div className="rounded-xl border bg-card p-4 shadow-sm">
46
- <div className="text-base font-semibold">{title}</div>
47
- <div className="mt-2">{children}</div>
48
- </div>
49
- );
30
+ <Step>
50
31
 
51
- const Button = ({ label }) => (
52
- <button className="rounded-md bg-primary px-3 py-1.5 text-primary-foreground">
53
- {label}
54
- </button>
55
- );
32
+ ### Install the package
56
33
 
57
- export const componentsAllowlist = { Card, Button };
58
- ```
34
+ <InstallCommand npm={["@assistant-ui/react-generative-ui"]} />
59
35
 
60
- ### 2. Wire the primitive into your message renderer
36
+ The vocabulary renders as unstyled semantic HTML with `data-aui` attributes. Add the styled library to get the shipped look:
61
37
 
62
- See [Opt-in wiring](#opt-in-wiring) for all three integration patterns.
38
+ <InstallCommand shadcn={["generative-ui"]} />
63
39
 
64
- ### 3. Have the agent emit UI
40
+ This merges the vocabulary stylesheet and theme variables into your CSS, which is what the rest of this page assumes, and lands `components/assistant-ui/generative-ui.tsx`. That file exports `styledGenerativeUILibrary`, whose only difference from the default vocabulary is a real markdown renderer; [Styling](#styling) wires it in.
65
41
 
66
- **ExternalStore / manual messages** attach a native part:
42
+ </Step>
67
43
 
68
- ```ts
69
- {
70
- type: "generative-ui",
71
- spec: {
72
- root: {
73
- component: "Card",
74
- props: { title: "Welcome" },
75
- children: [
76
- { component: "Button", props: { label: "Get started" } },
77
- ],
78
- },
79
- },
80
- }
44
+ <Step>
45
+
46
+ ### Enable the compiler
47
+
48
+ The `"use generative"` directive lets one file declare tools that both the browser and your server route can import: the compiler strips the browser-only halves out of the server build and the schemas out of the client build.
49
+
50
+ ```ts title="next.config.ts"
51
+ import { withAui } from "@assistant-ui/next";
52
+
53
+ export default withAui({
54
+ /* your Next config */
55
+ });
81
56
  ```
82
57
 
83
- **AI SDK (`useChatRuntime`)** — the adapter maps tool results to `tool-call`
84
- parts, not `generative-ui` parts. Use the [AI SDK interim bridge](#pattern-3--ai-sdk-interim-bridge) until a native emission helper ships.
58
+ Vite and TanStack Start use `aui()` from `@assistant-ui/vite`; Expo and bare React Native use `withAui` from `@assistant-ui/metro`.
85
59
 
86
- Live examples in [`examples/with-generative-ui`](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-generative-ui): Tool UI demo (`/`), static primitive (`/primitive`), GUI chat (`/gui-chat`).
60
+ </Step>
87
61
 
88
- ## Opt-in wiring
62
+ <Step>
89
63
 
90
- The stock `@assistant-ui/ui` `Thread` switch returns `null` for unknown part
91
- types — including `generative-ui`. Add one of these patterns in **your**
92
- assistant message renderer (~15 lines).
64
+ ### Expose the vocabulary as a tool
93
65
 
94
- ### Pattern 1 — `MessagePrimitive.Parts`
66
+ `JSONGenerativeUI` turns a component library into the model-facing schema for `present`. Register the result on a toolkit like any other tool.
95
67
 
96
- ```tsx
97
- <MessagePrimitive.Parts
98
- components={{
99
- generativeUI: {
100
- components: componentsAllowlist,
101
- Fallback: UnknownComponentFallback,
102
- },
103
- }}
104
- />
68
+ ```tsx title="app/toolkit.tsx"
69
+ "use generative";
70
+
71
+ import { defineToolkit } from "@assistant-ui/react";
72
+ import {
73
+ JSONGenerativeUI,
74
+ defaultGenerativeUILibrary,
75
+ } from "@assistant-ui/react-generative-ui";
76
+
77
+ const generative = new JSONGenerativeUI({
78
+ library: defaultGenerativeUILibrary,
79
+ });
80
+
81
+ export default defineToolkit({
82
+ present: generative.present({ display: "standalone" }),
83
+ });
105
84
  ```
106
85
 
107
- ### Pattern 2 — `GroupedParts` case (shadcn Thread fork)
86
+ `display: "standalone"` renders the result on its own surface, outside the chain-of-thought trace. Omit it to render inline.
87
+
88
+ </Step>
89
+
90
+ <Step>
91
+
92
+ ### Register the toolkit on the client
93
+
94
+ ```tsx title="app/MyRuntimeProvider.tsx"
95
+ "use client";
96
+
97
+ import {
98
+ AssistantRuntimeProvider,
99
+ AuiConfig,
100
+ Tools,
101
+ } from "@assistant-ui/react";
102
+ import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
103
+ import { lastAssistantMessageIsCompleteWithToolCalls } from "ai";
104
+ import toolkit from "./toolkit";
105
+
106
+ export function MyRuntimeProvider({
107
+ children,
108
+ }: {
109
+ children: React.ReactNode;
110
+ }) {
111
+ const runtime = useChatRuntime({
112
+ sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
113
+ });
114
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
108
115
 
109
- ```tsx
110
- case "generative-ui":
111
116
  return (
112
- <MessagePrimitive.GenerativeUI
113
- components={componentsAllowlist}
114
- Fallback={UnknownComponentFallback}
115
- />
117
+ <AssistantRuntimeProvider runtime={runtime} config={config}>
118
+ {children}
119
+ </AssistantRuntimeProvider>
116
120
  );
121
+ }
117
122
  ```
118
123
 
119
- Also exclude `render_gui` from tool-group chrome in `groupBy` if you use the
120
- AI SDK bridge (return `null` for that tool name).
124
+ <Callout type="warn">
125
+ `present` is a frontend tool: it resolves in the browser, and the run only continues once its result is sent back. Without `sendAutomaticallyWhen`, the UI renders and the conversation then stops.
126
+ </Callout>
121
127
 
122
- ### Pattern 3 — AI SDK interim bridge
128
+ </Step>
123
129
 
124
- When using `useChatRuntime`, map a dedicated tool result to the renderer:
130
+ <Step>
125
131
 
126
- ```tsx
127
- case "tool-call":
128
- if (part.toolName === "render_gui") {
129
- const spec = parseRenderGuiResult(part.result);
130
- if (spec) {
131
- return (
132
- <MessagePrimitive.GenerativeUI
133
- spec={spec}
134
- components={componentsAllowlist}
135
- Fallback={UnknownComponentFallback}
136
- />
137
- );
132
+ ### Serve the schema
133
+
134
+ The route imports the same toolkit module. The compiler resolves that import to the server build, so only the schemas cross over and no browser code enters your server bundle.
135
+
136
+ ```ts title="app/api/chat/route.ts"
137
+ import { openai } from "@ai-sdk/openai";
138
+ import { AISDKToolkit } from "@assistant-ui/react-ai-sdk";
139
+ import { convertToModelMessages, stepCountIs, streamText } from "ai";
140
+ import toolkit from "@/app/toolkit";
141
+
142
+ const aiToolkit = new AISDKToolkit({ toolkit });
143
+
144
+ export async function POST(req: Request) {
145
+ const { messages, tools } = await req.json();
146
+
147
+ const result = streamText({
148
+ model: openai("gpt-5.6-luna"),
149
+ messages: await convertToModelMessages(messages),
150
+ stopWhen: stepCountIs(10),
151
+ tools: await aiToolkit.tools({ frontend: tools }),
152
+ });
153
+
154
+ return result.toUIMessageStreamResponse();
155
+ }
156
+ ```
157
+
158
+ `stopWhen` matters here for the same reason `sendAutomaticallyWhen` does: rendering the UI is one step, and the model needs another to say anything after it.
159
+
160
+ </Step>
161
+
162
+ </Steps>
163
+
164
+ Ask for something the vocabulary can express, for example "show me a sales dashboard for the last six months", and the model will answer with a rendered composition. The complete setup runs in [`examples/with-generative-ui`](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-generative-ui).
165
+
166
+ ## What the model emits
167
+
168
+ Every node is a flat object: `$type` names the component, `children` nests, and every other key is a prop.
169
+
170
+ ```json
171
+ {
172
+ "$type": "Card",
173
+ "title": "Q3 revenue",
174
+ "children": [
175
+ {
176
+ "$type": "Row",
177
+ "children": [
178
+ { "$type": "Fact", "label": "Bookings", "value": "$1.2M" },
179
+ { "$type": "Fact", "label": "Growth", "value": "+18%" }
180
+ ]
181
+ },
182
+ {
183
+ "$type": "Chart",
184
+ "variant": "bar",
185
+ "showAxis": true,
186
+ "data": [
187
+ { "label": "Jul", "value": 22 },
188
+ { "label": "Aug", "value": 26 },
189
+ { "label": "Sep", "value": 31 }
190
+ ]
138
191
  }
139
- }
140
- return part.toolUI ?? <ToolFallback {...part} />;
192
+ ]
193
+ }
141
194
  ```
142
195
 
143
- The message store still holds a `tool-call` on this path — not a
144
- `generative-ui` part. See `examples/with-generative-ui/app/gui-chat` for a
145
- working reference.
196
+ Keys beginning with `$` are reserved by the framework (`$type`, `$key`, `$action`, and the injected `$status`), and `children` follows the JSX convention. Every other key is yours, so a component is free to declare props named `type`, `status`, or `variant` without colliding.
197
+
198
+ ## Extending the vocabulary
146
199
 
147
- Bare strings act as inline text leaves.
200
+ `defineGenerativeComponents` adds your own components. Each one declares a zod schema for its props, a description the model reads, and a render function.
148
201
 
149
- ## Spec shape
202
+ ```tsx title="app/toolkit.tsx"
203
+ "use generative";
150
204
 
151
- ```ts
152
- type GenerativeUINode =
153
- | string
154
- | {
155
- component: string; // resolved against the allowlist
156
- props?: Record<string, unknown>;
157
- children?: GenerativeUINode[];
158
- key?: string; // optional stable React key
159
- };
205
+ import { z } from "zod";
206
+ import {
207
+ JSONGenerativeUI,
208
+ defaultGenerativeUILibrary,
209
+ defineGenerativeComponents,
210
+ } from "@assistant-ui/react-generative-ui";
211
+ import { WeatherCard } from "@/components/weather-card";
160
212
 
161
- type GenerativeUISpec = {
162
- root: GenerativeUINode | GenerativeUINode[];
163
- };
213
+ const generative = new JSONGenerativeUI({
214
+ library: {
215
+ ...defaultGenerativeUILibrary,
216
+ ...defineGenerativeComponents({
217
+ Weather: {
218
+ description: "Show a weather card for a `get_weather` result.",
219
+ properties: z.object({
220
+ id: z.string().describe("The `id` returned by `get_weather`."),
221
+ }),
222
+ render: (props) => <WeatherCard {...props} />,
223
+ },
224
+ }),
225
+ },
226
+ });
164
227
  ```
165
228
 
166
- The spec is plain JSON — easy for any agent to emit, and easy to validate
167
- on the server before delivery.
229
+ Set `streamProperties: true` alongside `properties` to receive partially-filled props while the model is still writing them. `render` then sees `Partial<P>` and an injected `$status` of `"streaming"`, and the full props once it turns `"done"`. Components opt out by default and render only once their props are complete.
168
230
 
169
- ## Actions with `JSONGenerativeUI`
231
+ <Callout type="warn">
232
+ Inside a `"use generative"` module, a `"use client"` module may be referenced **only** as the `render` value of an inline `defineGenerativeComponents` literal. The compiler drops `render` and the imports only it uses from the server build; referencing such a module from `properties`, `description`, a spread, or a top-level constant leaks a client reference into the server graph and breaks schema generation. `defaultGenerativeUILibrary` is safe on both builds.
233
+ </Callout>
170
234
 
171
- When you expose a component library through `new JSONGenerativeUI(...)`, pass an
172
- action registry to let interactive nodes call back into your app. This path uses
173
- the flat `{ "$type": ... }` node shape; the model puts an `$action` object on
174
- the node, and its `type` is matched against your registered handlers.
235
+ ## Actions
175
236
 
176
- Browse the [gallery](/gallery) to see every default vocabulary component rendered live, next to its IR JSON, generated React code, and a usage snippet.
237
+ Pass an action registry to let rendered nodes call back into your app. The model puts an `$action` object on a node, and its `type` is matched against your handlers.
177
238
 
178
239
  ```tsx
179
240
  import {
@@ -202,108 +263,60 @@ const generative = new JSONGenerativeUI({
202
263
  }
203
264
  ```
204
265
 
205
- `Select`, `Input`, `DatePicker`, `Checkbox`, and `RadioGroup` add the user's value as `$input` when they fire the action; `Form` and a `Card` with `asForm` set add an object keyed by each control's `name` instead. On a `Card` there is no Card-level `$action`: the collected object is dispatched through `confirm.$action`, while `cancel.$action` always fires without `$input`. Unknown action types are ignored and warn in development.
206
-
207
- ## Streaming
208
-
209
- When a message contains native `generative-ui` parts whose `spec` updates
210
- incrementally (for example via ExternalStore), the primitive renders
211
- progressively as nodes and props arrive.
212
-
213
- The AI SDK `render_gui` tool path returns the full spec at **tool completion**
214
- — not incrementally during the tool execute step. For args streaming during
215
- generation, use [Tool UI](/docs/tools/tool-ui) instead.
266
+ `Select`, `Input`, `DatePicker`, `Checkbox`, and `RadioGroup` add the user's value as `$input` when they fire the action. `Form`, and a `Card` with `asForm` set, add an object keyed by each control's `name` instead. On a `Card` there is no card-level `$action`: the collected object is dispatched through `confirm.$action`, while `cancel.$action` always fires without `$input`. Unknown action types are ignored and warn in development.
216
267
 
217
- ## Security
268
+ Without a registry, the tree still renders and model-emitted actions degrade to a no-op.
218
269
 
219
- The allowlist is the boundary on **which** components render: a spec can only instantiate components you put in the registry, with no `eval` and no dynamic import (names are looked up in the registry and nothing else). An unknown name throws `GenerativeUIRenderError` or invokes your `Fallback`.
270
+ ## Styling
220
271
 
221
- It does **not** constrain the `props` the agent supplies. Spec props are spread directly onto your allowlisted components, so treat every allowlisted component as receiving untrusted input: never forward agent-supplied props into `dangerouslySetInnerHTML`, validate or reject `href` / `src` values (for example block `javascript:` URLs), and avoid passing spec props anywhere they become executable. The safest allowlisted components accept only primitive, display-oriented props.
272
+ The vocabulary renders semantic HTML tagged with `data-aui` and `data-aui-*` attributes and ships no styles of its own, so it inherits nothing and collides with nothing. The `generative-ui` registry item above installs the shipped stylesheet, which is written entirely against your existing theme variables: change `--radius` or `--primary` in your own CSS and the rendered widgets follow.
222
273
 
223
- ## Error handling
274
+ A `Card` renders as a plain section by default, so several in a row read as one answer rather than a stack of boxes. It takes on a framed surface only where one is warranted: when the model sets a `background`, when `confirm` or `cancel` add a footer whose buttons need a delimited target, or when it is a carousel slot.
224
275
 
225
- Unknown component names throw `GenerativeUIRenderError` with a typed
226
- `componentName` field. Catch it with a React error boundary, or pass a
227
- `Fallback` component to opt into a soft-fail UX:
276
+ To restyle a single component, target its attribute:
228
277
 
229
- ```tsx
230
- <MessagePrimitive.GenerativeUI
231
- components={componentsAllowlist}
232
- Fallback={({ component }) => (
233
- <span className="rounded bg-muted px-1.5 py-0.5 font-mono text-xs">
234
- unknown component: {component}
235
- </span>
236
- )}
237
- />
278
+ ```css
279
+ [data-aui="fact-value"] {
280
+ font-variant-numeric: tabular-nums;
281
+ font-size: 1.125rem;
282
+ }
238
283
  ```
239
284
 
240
- ## Composing with other primitives
241
-
242
- `generative-ui` is a regular `MessagePart` type, so it composes cleanly with
243
- `MessagePrimitive.Parts`, `MessagePrimitive.PartByIndex`, and
244
- `MessagePrimitive.GroupedParts`. Render it alongside text, tool calls, and
245
- reasoning in the same message.
246
-
247
- ## Why a primitive (not just a tool)
285
+ Overriding a component's markup rather than its appearance is a library-level change: spread `defaultGenerativeUILibrary` and replace that one entry through `defineGenerativeComponents`. The default `Markdown` renders its source as plain text, which is why the registry item ships a replacement. Wiring it is the same override:
248
286
 
249
- Tool-call UI is great when the agent already invoked a known tool. Generative
250
- UI flips it: the agent _composes_ UI from a vocabulary you ship. Useful for
251
- dashboards, status panels, and structured layouts — not for collecting user
252
- input (use Tool UI for that).
287
+ ```tsx title="app/toolkit.tsx"
288
+ import { styledGenerativeUILibrary } from "@/components/assistant-ui/generative-ui";
253
289
 
254
- ## Slack Block Kit
290
+ const markdown = defaultGenerativeUILibrary.Markdown!;
255
291
 
256
- `toSlackBlocks` from `@assistant-ui/react-generative-ui/slack` converts a
257
- generative-UI tree into Slack's Block Kit JSON. It is pure and React-free, so
258
- it runs equally well in a server action, a queue worker, or a webhook
259
- handler. Components the converter doesn't recognize are skipped and reported
260
- as warnings instead of throwing, and content that exceeds Slack's published
261
- size and count budgets is clamped or downgraded to a simpler block rather
262
- than producing an invalid payload.
263
-
264
- ```tsx
265
- import { WebClient } from "@slack/web-api";
266
- import { toSlackBlocks } from "@assistant-ui/react-generative-ui/slack";
267
-
268
- const slack = new WebClient(process.env.SLACK_BOT_TOKEN);
269
-
270
- const { blocks } = toSlackBlocks({
271
- $type: "Card",
272
- title: "Order #48213",
273
- children: [{ $type: "Text", value: "Shipped, arriving Thursday." }],
274
- });
275
-
276
- await slack.chat.postMessage({
277
- channel: "#orders",
278
- blocks,
292
+ const generative = new JSONGenerativeUI({
293
+ library: {
294
+ ...defaultGenerativeUILibrary,
295
+ ...defineGenerativeComponents({
296
+ Markdown: {
297
+ properties: markdown.properties,
298
+ streamProperties: markdown.streamProperties,
299
+ description: "A markdown string, rendered with GitHub-flavored markdown.",
300
+ render: styledGenerativeUILibrary.Markdown!.render,
301
+ },
302
+ }),
303
+ },
279
304
  });
280
305
  ```
281
306
 
282
- Slack posts interactive elements back as a `block_actions` payload.
283
- `decodeBlockAction` takes one entry from that payload's `actions` array and
284
- decodes it back into the `$action` shape your tree dispatched, with the
285
- user's runtime selection (a picked option, a typed value) carried under
286
- `$input`. Receiving the webhook, verifying its signature, and routing the
287
- decoded action to your handler stay the host app's responsibility; the
288
- converter only speaks JSON in and JSON out.
307
+ `styledGenerativeUILibrary` is a `"use client"` module, so this inline `render` is the only place a `"use generative"` file may name it. Passing it directly as `library` works only in a file without the directive.
289
308
 
290
- The inverse direction is `fromSlackBlocks`: it maps a Block Kit payload back into vocabulary nodes, back-mapping each element's `action_id` to `$action.type` and reparsing serialized payload values. The round trip is faithful on the plain building blocks (text, images, facts, controls, tables, simple cards) and documented-lossy elsewhere: context elements all return as `Caption`, button styles beyond `primary` and `danger` are dropped, an alert's title and description come back as one description, and card layouts flatten to the fields the `card` block carries.
309
+ ## Security
291
310
 
292
- A few conversion caveats worth knowing:
311
+ The library is the boundary on **which** components can render: the model can only name components you put in it, resolved by lookup with no `eval` and no dynamic import. Unknown names are dropped.
293
312
 
294
- - `Alert` has no message-surface equivalent upstream (Slack only supports it
295
- in modals), so messages get a context block plus section block fallback
296
- instead.
297
- - `Card` and `Carousel` have tight text budgets; a card whose content
298
- overflows its budget falls back to plain blocks rather than the card
299
- layout.
300
- - `Chart` has no Slack Block Kit mapping and is replaced by a note block.
301
- - The Block Kit Builder deep link format
302
- (`https://app.slack.com/block-kit-builder/#<payload>`) is an observed
303
- convention, not an officially documented API.
313
+ Props are a separate question. Each component's zod schema validates what the model sends, so a component that declares only primitive, display-oriented props cannot receive anything else. When you add a component that takes a URL, a raw HTML string, or anything that becomes executable, validate it inside that component; the schema constrains shape, not intent.
304
314
 
305
- ## Microsoft Teams
315
+ ## Beyond the browser
306
316
 
307
- The same tree converts to an Adaptive Card with `toAdaptiveCard` from `@assistant-ui/react-generative-ui/teams`: pure, React-free, pinned to Adaptive Cards 1.5 (the Teams desktop ceiling; mobile clients cap at 1.2), and total in the same way as the Slack converter, so unknown components degrade with warnings instead of failing. Interactive components encode their `$action` inside the submit payload's reserved `aui` key, and `decodeSubmitData` splits a bot's incoming `activity.value` back into the `$action` shape with the card's input values under `$input`. A root `Carousel` is an activity-level construct on Teams, so `toTeamsAttachments` returns up to ten card attachments with `attachmentLayout: "carousel"` instead of one card.
317
+ The tree is plain JSON, so it is not tied to the browser. Four React-free subpaths let a server action, queue worker, or webhook handler consume it without pulling React:
308
318
 
309
- Caveats mirror the platform: Teams ignores positive and destructive action styling, TextBlock markdown is a subset (no headings, tables, or images), `Divider` and `Spacer` become `separator` and `spacing` properties on the following element, and `Chart` has no Teams mapping and is replaced by a note.
319
+ - `@assistant-ui/react-generative-ui/ir` holds the tree types, normalization, and token enums.
320
+ - `@assistant-ui/react-generative-ui/slack` converts the tree into Block Kit and decodes `block_actions` back into `$action`. See [Generative UI on Slack](/docs/tools/generative-ui-slack).
321
+ - `@assistant-ui/react-generative-ui/teams` converts it into an Adaptive Card and decodes the submit payload. See [Generative UI on Microsoft Teams](/docs/tools/generative-ui-teams).
322
+ - `@assistant-ui/react-generative-ui/a2ui` consumes [A2UI surfaces over AG-UI](/docs/tools/a2ui).
@@ -43,8 +43,9 @@ assistant-ui has a few ways to turn model output into React UI. Pick by **who de
43
43
  | --- | --- | --- |
44
44
  | A custom component for a known tool call (form, picker, chart, status) | [Tool UI](/docs/tools/tool-ui) — `render` on a toolkit entry | the **model**, by calling the tool |
45
45
  | Persistent, out-of-thread state the AI can read and write | [Interactables](/docs/tools/interactables) | the **model + the user**, bidirectionally |
46
- | UI composed from a component vocabulary you ship, described as a JSON spec | [Generative UI (JSON spec)](/docs/tools/generative-ui) — `MessagePrimitive.GenerativeUI` | the **model**, composing a tree |
46
+ | UI composed from a component vocabulary you ship | [Generative UI](/docs/tools/generative-ui) — the `present` tool | the **model**, composing a tree |
47
47
  | UI pushed by a LangGraph node alongside messages | [LangGraph Generative UI](/docs/runtimes/langgraph/generative-ui) — `makeAssistantDataUI` | the **backend / orchestrator** |
48
+ | UI composed in OpenUI Lang, rendered by OpenUI's kit (third-party) | [OpenUI](/docs/tools/openui) — `@openuidev/assistant-ui` | the **model**, composing a tree |
48
49
 
49
50
  ## Connect external tools
50
51
 
@@ -66,8 +66,9 @@ function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
66
66
  ```
67
67
 
68
68
  <Callout type="idea">
69
- The legacy `interactables: Interactables()` scope and the new
70
- `unstable_interactables: unstable_Interactables()` scope are mutually
69
+ The deprecated [`interactables:
70
+ Interactables()`](/docs/api-reference/tools/interactables-legacy) scope and the
71
+ new `unstable_interactables: unstable_Interactables()` scope are mutually
71
72
  exclusive. Mount only one interactables API in a single provider config.
72
73
  </Callout>
73
74
 
@@ -629,7 +630,7 @@ function MyRuntimeProvider({ children }) {
629
630
 
630
631
  `load` is called when the adapter is attached and may be async. Loaded state seeds interactables as they register; a local edit made while a slow `load` is still in flight wins over the loaded value. Thread-scoped interactables are not touched by the adapter; they persist via thread history.
631
632
 
632
- For dynamic setups (an adapter that depends on auth), call `aui.interactables.setPersistenceAdapter(adapter)` imperatively instead.
633
+ For dynamic setups (an adapter that depends on auth), call `aui.unstable_interactables.setPersistenceAdapter(adapter)` imperatively instead. Replacing or removing an adapter flushes queued changes through the outgoing adapter before the new persistence context is attached.
633
634
 
634
635
  ### Sync Status
635
636
 
@@ -711,7 +712,7 @@ Inside a thread-scoped `render`, `streaming: true` carries the same live state:
711
712
 
712
713
  ## Multiple Instances
713
714
 
714
- A `name` can have many live instances at once. They all share one `update_{name}` tool: the model addresses an instance with the tool's `id` parameter, which it reads from the state snapshots in the conversation. The tool's name, schema, and description never change as instances mount and unmount, so the model's tool list (and provider prompt caches) stay stable. While exactly one instance exists, the model may omit `id`; a call with an unknown `id` returns an error listing the valid ids, so the model can recover.
715
+ A `name` can have many live instances at once. They all share one `update_{name}` tool: the model addresses an instance with the tool's `id` parameter, which it reads from the state snapshots in the conversation. The tool's name, schema, and description never change as instances mount and unmount, so the model's tool list (and provider prompt caches) stay stable. The schema marks `id` as required, so the model is expected to send it on every call; the runtime still resolves an id-less call while exactly one instance exists. A call with an unknown `id` returns an error listing the valid ids, so the model can recover.
715
716
 
716
717
  How instances come into being differs by scope.
717
718