@assistant-ui/mcp-docs-server 0.2.1 → 0.2.3
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.
- package/.docs/organized/code-examples/waterfall.md +3 -3
- package/.docs/organized/code-examples/with-a2a.md +5 -4
- package/.docs/organized/code-examples/with-ag-ui.md +7 -6
- package/.docs/organized/code-examples/with-ai-sdk-v7.md +14 -13
- package/.docs/organized/code-examples/with-artifacts.md +12 -11
- package/.docs/organized/code-examples/with-assistant-transport.md +6 -5
- package/.docs/organized/code-examples/with-browser-extension.md +6 -6
- package/.docs/organized/code-examples/with-chain-of-thought.md +16 -14
- package/.docs/organized/code-examples/with-cloud-standalone.md +7 -7
- package/.docs/organized/code-examples/with-cloud.md +11 -10
- package/.docs/organized/code-examples/with-custom-thread-list.md +11 -10
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +14 -13
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +15 -14
- package/.docs/organized/code-examples/with-eve.md +6 -5
- package/.docs/organized/code-examples/with-expo.md +213 -36
- package/.docs/organized/code-examples/with-external-store.md +6 -5
- package/.docs/organized/code-examples/with-ffmpeg.md +11 -10
- package/.docs/organized/code-examples/with-generative-ui.md +263 -29
- package/.docs/organized/code-examples/with-google-adk.md +5 -4
- package/.docs/organized/code-examples/with-heat-graph.md +3 -3
- package/.docs/organized/code-examples/with-image-generation.md +8 -7
- package/.docs/organized/code-examples/with-interactables.md +11 -10
- package/.docs/organized/code-examples/with-langchain.md +10 -9
- package/.docs/organized/code-examples/with-langgraph.md +7 -6
- package/.docs/organized/code-examples/with-livekit.md +15 -14
- package/.docs/organized/code-examples/with-mcp.md +14 -12
- package/.docs/organized/code-examples/with-nuxt.md +511 -575
- package/.docs/organized/code-examples/with-opencode.md +4 -4
- package/.docs/organized/code-examples/with-openui.md +450 -0
- package/.docs/organized/code-examples/with-pi.md +6 -5
- package/.docs/organized/code-examples/with-react-hook-form.md +16 -13
- package/.docs/organized/code-examples/with-react-ink-web.md +65 -15
- package/.docs/organized/code-examples/with-react-ink.md +5 -4
- package/.docs/organized/code-examples/with-react-router.md +8 -7
- package/.docs/organized/code-examples/with-resumable-stream.md +13 -12
- package/.docs/organized/code-examples/with-store.md +3 -3
- package/.docs/organized/code-examples/with-svelte.md +415 -0
- package/.docs/organized/code-examples/with-sveltekit.md +1061 -0
- package/.docs/organized/code-examples/with-tanstack.md +10 -9
- package/.docs/organized/code-examples/with-tap-runtime.md +5 -4
- package/.docs/organized/code-examples/with-virtualized-thread.md +6 -5
- package/.docs/organized/code-examples/with-vue.md +2 -2
- package/.docs/raw/docs/{(docs) → (getting-started)}/cli.mdx +6 -1
- package/.docs/raw/docs/{(docs) → (getting-started)}/devtools.mdx +1 -1
- package/.docs/raw/docs/{(docs) → (getting-started)}/index.mdx +4 -2
- package/.docs/raw/docs/{(docs) → (getting-started)}/installation.mdx +26 -26
- package/.docs/raw/docs/(reference)/api-reference/adapters/suggestions.mdx +6 -0
- package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +6 -1
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/slack.mdx +5 -1
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/teams.mdx +9 -2
- package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +2 -2
- package/.docs/raw/docs/(reference)/api-reference/integrations/{react-ai-sdk.mdx → ai-sdk.mdx} +43 -18
- package/.docs/raw/docs/(reference)/api-reference/integrations/eve.mdx +61 -2
- package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +3 -3
- package/.docs/raw/docs/(reference)/api-reference/overview.mdx +2 -2
- package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +20 -2
- package/.docs/raw/docs/(reference)/api-reference/tools/interactables.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/transport/frame.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +32 -1
- package/.docs/raw/docs/(reference)/api-reference/voice/session.mdx +1 -1
- package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +10 -10
- package/.docs/raw/docs/cloud/ai-sdk.mdx +4 -2
- package/.docs/raw/docs/cloud/index.mdx +1 -1
- package/.docs/raw/docs/copilots/assistant-frame.mdx +19 -8
- package/.docs/raw/docs/guides/attachments.mdx +4 -4
- package/.docs/raw/docs/guides/branching.mdx +1 -1
- package/.docs/raw/docs/guides/chatgpt-subscription.mdx +1 -1
- package/.docs/raw/docs/guides/context-api.mdx +15 -17
- package/.docs/raw/docs/guides/dictation.mdx +2 -2
- package/.docs/raw/docs/guides/electron.mdx +1 -1
- package/.docs/raw/docs/guides/latex.mdx +1 -1
- package/.docs/raw/docs/guides/mentions.mdx +2 -0
- package/.docs/raw/docs/guides/message-timing.mdx +11 -5
- package/.docs/raw/docs/guides/quoting.mdx +1 -1
- package/.docs/raw/docs/guides/resumable-streams.mdx +3 -3
- package/.docs/raw/docs/guides/speech.mdx +1 -1
- package/.docs/raw/docs/guides/suggestions.mdx +8 -5
- package/.docs/raw/docs/guides/voice.mdx +1 -1
- package/.docs/raw/docs/ink/hooks.mdx +1 -1
- package/.docs/raw/docs/ink/index.mdx +3 -2
- package/.docs/raw/docs/ink/migration.mdx +1 -1
- package/.docs/raw/docs/ink/primitives.mdx +1 -1
- package/.docs/raw/docs/integrations/attachments/custom-adapter.mdx +3 -3
- package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +3 -3
- package/.docs/raw/docs/integrations/frameworks/{cloudflare-agents/overview.mdx → cloudflare-agents.mdx} +4 -3
- package/.docs/raw/docs/integrations/frameworks/mastra/overview.mdx +1 -1
- package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +1 -1
- package/.docs/raw/docs/integrations/index.mdx +2 -2
- package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +149 -131
- package/.docs/raw/docs/migrations/toolkit-tools.mdx +2 -2
- package/.docs/raw/docs/migrations/v0-15.mdx +34 -0
- package/.docs/raw/docs/primitives/action-bar.mdx +1 -1
- package/.docs/raw/docs/primitives/assistant-modal.mdx +1 -1
- package/.docs/raw/docs/primitives/attachment.mdx +1 -1
- package/.docs/raw/docs/primitives/branch-picker.mdx +1 -1
- package/.docs/raw/docs/primitives/chain-of-thought.mdx +3 -3
- package/.docs/raw/docs/primitives/composer.mdx +1 -1
- package/.docs/raw/docs/primitives/error.mdx +1 -1
- package/.docs/raw/docs/primitives/message.mdx +1 -1
- package/.docs/raw/docs/primitives/selection-toolbar.mdx +1 -1
- package/.docs/raw/docs/primitives/suggestion.mdx +4 -2
- package/.docs/raw/docs/primitives/thread-list.mdx +1 -1
- package/.docs/raw/docs/primitives/thread.mdx +2 -2
- package/.docs/raw/docs/react-native/adapters.mdx +1 -1
- package/.docs/raw/docs/react-native/index.mdx +2 -2
- package/.docs/raw/docs/react-native/migration.mdx +2 -2
- package/.docs/raw/docs/react-native/primitives.mdx +142 -5
- package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +48 -6
- package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +3 -3
- package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +4 -4
- package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +31 -16
- package/.docs/raw/docs/runtimes/claude-managed-agents.mdx +118 -0
- package/.docs/raw/docs/runtimes/concepts/adapters.mdx +3 -3
- package/.docs/raw/docs/runtimes/concepts/architecture.mdx +3 -3
- package/.docs/raw/docs/runtimes/concepts/stability.mdx +2 -1
- package/.docs/raw/docs/runtimes/concepts/threads.mdx +73 -33
- package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +6 -2
- package/.docs/raw/docs/runtimes/langchain.mdx +2 -2
- package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +4 -0
- package/.docs/raw/docs/runtimes/opencode/quickstart.mdx +1 -1
- package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +9 -2
- package/.docs/raw/docs/tools/backend.mdx +4 -4
- package/.docs/raw/docs/tools/defining-tools.mdx +21 -2
- package/.docs/raw/docs/tools/generative-ui-primitive.mdx +180 -0
- package/.docs/raw/docs/tools/generative-ui-slack.mdx +167 -0
- package/.docs/raw/docs/tools/generative-ui-teams.mdx +160 -0
- package/.docs/raw/docs/tools/generative-ui.mdx +224 -211
- package/.docs/raw/docs/tools/index.mdx +2 -1
- package/.docs/raw/docs/tools/interactables.mdx +8 -7
- package/.docs/raw/docs/tools/mcp-apps.mdx +18 -1
- package/.docs/raw/docs/tools/mcp.mdx +2 -2
- package/.docs/raw/docs/tools/openui.mdx +175 -0
- package/.docs/raw/docs/tools/tool-ui.mdx +18 -7
- package/.docs/raw/docs/tools/user-managed-mcp.mdx +7 -3
- package/.docs/raw/docs/ui/assistant-modal.mdx +3 -3
- package/.docs/raw/docs/ui/assistant-sidebar.mdx +1 -1
- package/.docs/raw/docs/ui/attachment.mdx +50 -3
- package/.docs/raw/docs/ui/composer-trigger-popover.mdx +36 -1
- package/.docs/raw/docs/ui/context-display.mdx +1 -1
- package/.docs/raw/docs/ui/directive-text.mdx +1 -1
- package/.docs/raw/docs/ui/file.mdx +2 -2
- package/.docs/raw/docs/ui/follow-up-suggestions.mdx +1 -1
- package/.docs/raw/docs/ui/image.mdx +2 -2
- package/.docs/raw/docs/ui/markdown.mdx +40 -1
- package/.docs/raw/docs/ui/mermaid.mdx +1 -1
- package/.docs/raw/docs/ui/message-timing.mdx +1 -1
- package/.docs/raw/docs/ui/model-selector.mdx +4 -4
- package/.docs/raw/docs/ui/part-grouping.mdx +1 -1
- package/.docs/raw/docs/ui/quote.mdx +3 -3
- package/.docs/raw/docs/ui/reasoning.mdx +31 -3
- package/.docs/raw/docs/ui/scrollbar.mdx +1 -1
- package/.docs/raw/docs/ui/sources.mdx +10 -1
- package/.docs/raw/docs/ui/streamdown.mdx +2 -2
- package/.docs/raw/docs/ui/syntax-highlighting.mdx +1 -1
- package/.docs/raw/docs/ui/thread-list.mdx +40 -1
- package/.docs/raw/docs/ui/thread.mdx +49 -2
- package/.docs/raw/docs/ui/tool-fallback.mdx +23 -9
- package/.docs/raw/docs/ui/tool-group.mdx +1 -1
- package/.docs/raw/docs/ui/voice.mdx +1 -1
- package/.docs/raw/docs/utilities/react-o11y.mdx +2 -2
- package/dist/xulux/catalog-client.d.ts.map +1 -1
- package/dist/xulux/catalog-client.js +51 -6
- package/dist/xulux/catalog-client.js.map +1 -1
- package/dist/xulux/types.d.ts +7 -7
- package/dist/xulux/types.d.ts.map +1 -1
- package/dist/xulux/types.js.map +1 -1
- package/package.json +5 -5
- package/src/tools/tests/docs.test.ts +10 -7
- package/src/tools/tests/path-traversal.test.ts +5 -2
- package/src/tools/tests/xulux-templates.test.ts +63 -0
- package/src/xulux/catalog-client.ts +68 -23
- package/src/xulux/types.ts +17 -13
- package/.docs/raw/docs/tools/interactables-legacy.mdx +0 -423
- package/.docs/raw/docs/ui/accordion.mdx +0 -266
- package/.docs/raw/docs/ui/badge.mdx +0 -150
- package/.docs/raw/docs/ui/diff-viewer.mdx +0 -280
- package/.docs/raw/docs/ui/dot-matrix.mdx +0 -133
- package/.docs/raw/docs/ui/number-roll.mdx +0 -154
- package/.docs/raw/docs/ui/select.mdx +0 -254
- package/.docs/raw/docs/ui/tabs.mdx +0 -271
- /package/.docs/raw/docs/{(docs) → (getting-started)}/architecture.mdx +0 -0
- /package/.docs/raw/docs/{(docs) → (getting-started)}/base-ui.mdx +0 -0
- /package/.docs/raw/docs/{(docs) → (getting-started)}/llm.mdx +0 -0
- /package/.docs/raw/docs/{(docs) → (getting-started)}/rtl.mdx +0 -0
|
@@ -1,179 +1,240 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Generative UI
|
|
3
|
-
description:
|
|
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
|
-
|
|
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
|
-
|
|
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"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
28
|
+
<Steps>
|
|
42
29
|
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
|
|
58
|
-
```
|
|
34
|
+
<InstallCommand npm={["@assistant-ui/react-generative-ui"]} />
|
|
59
35
|
|
|
60
|
-
|
|
36
|
+
The vocabulary renders as unstyled semantic HTML with `data-aui` attributes. Add the styled library to get the shipped look:
|
|
61
37
|
|
|
62
|
-
|
|
38
|
+
<InstallCommand shadcn={["generative-ui"]} />
|
|
63
39
|
|
|
64
|
-
|
|
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
|
-
|
|
42
|
+
</Step>
|
|
67
43
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
60
|
+
</Step>
|
|
87
61
|
|
|
88
|
-
|
|
62
|
+
<Step>
|
|
89
63
|
|
|
90
|
-
|
|
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
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
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/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
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
/>
|
|
117
|
+
<AssistantRuntimeProvider runtime={runtime} config={config}>
|
|
118
|
+
{children}
|
|
119
|
+
</AssistantRuntimeProvider>
|
|
116
120
|
);
|
|
121
|
+
}
|
|
117
122
|
```
|
|
118
123
|
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
128
|
+
</Step>
|
|
123
129
|
|
|
124
|
-
|
|
130
|
+
<Step>
|
|
125
131
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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/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
|
-
|
|
192
|
+
]
|
|
193
|
+
}
|
|
141
194
|
```
|
|
142
195
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
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
|
-
|
|
202
|
+
```tsx title="app/toolkit.tsx"
|
|
203
|
+
"use generative";
|
|
150
204
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
162
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
268
|
+
Without a registry, the tree still renders and model-emitted actions degrade to a no-op.
|
|
218
269
|
|
|
219
|
-
|
|
270
|
+
## Styling
|
|
220
271
|
|
|
221
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
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
|
-
|
|
250
|
-
|
|
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
|
-
|
|
290
|
+
const markdown = defaultGenerativeUILibrary.Markdown!;
|
|
255
291
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
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
|
-
|
|
309
|
+
## Security
|
|
291
310
|
|
|
292
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
315
|
+
## Beyond the browser
|
|
306
316
|
|
|
307
|
-
The
|
|
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
|
-
|
|
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
|
|
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
|
|
|
@@ -4,7 +4,7 @@ description: Build stateful components and tool UIs that both the user and the m
|
|
|
4
4
|
platforms: ["react"]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import { InteractableSample } from "@/components/docs/samples/interactable";
|
|
7
|
+
import { InteractableSample } from "@/components/pages/docs/samples/interactable";
|
|
8
8
|
|
|
9
9
|
Interactables allow both agents and users to read and edit tool UIs and components. They can be in-thread tool UIs like an email composer, or app-scoped components, like artifacts, task boards, or settings panels.
|
|
10
10
|
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
AssistantRuntimeProvider,
|
|
47
47
|
Tools,
|
|
48
48
|
} from "@assistant-ui/react";
|
|
49
|
-
import { useChatRuntime } from "@assistant-ui/
|
|
49
|
+
import { useChatRuntime } from "@assistant-ui/ai-sdk";
|
|
50
50
|
|
|
51
51
|
function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
|
|
52
52
|
const runtime = useChatRuntime();
|
|
@@ -66,8 +66,9 @@ function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
|
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
<Callout type="idea">
|
|
69
|
-
The
|
|
70
|
-
`
|
|
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
|
|
|
@@ -195,7 +196,7 @@ Each user message carries its state snapshots in its metadata, but the AI SDK's
|
|
|
195
196
|
```ts title="app/api/chat/route.ts"
|
|
196
197
|
import { openai } from "@ai-sdk/openai";
|
|
197
198
|
import { convertToModelMessages, streamText } from "ai";
|
|
198
|
-
import { unstable_injectInteractableContext as injectInteractableContext } from "@assistant-ui/
|
|
199
|
+
import { unstable_injectInteractableContext as injectInteractableContext } from "@assistant-ui/ai-sdk";
|
|
199
200
|
|
|
200
201
|
export async function POST(req: Request) {
|
|
201
202
|
const { messages } = await req.json();
|
|
@@ -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.
|
|
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.
|
|
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
|
|
|
@@ -90,6 +90,23 @@ const mcpApp = McpAppRenderer({
|
|
|
90
90
|
|
|
91
91
|
`openLink` is auto-wired to `window.open(url, "_blank", "noopener,noreferrer")`. `sendMessage` is auto-wired to append a user message to the current thread (accepts `string`, `{ prompt }`, `{ text }`, or `{ message }`).
|
|
92
92
|
|
|
93
|
+
Pass `handlers` when the widget needs host-side UI or lifecycle callbacks. The capabilities are advertised during mount, so provide the handlers before the widget initializes:
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
McpAppRenderer({
|
|
97
|
+
host: McpAppsRemoteHost({ url: "/api/mcp-apps" }),
|
|
98
|
+
handlers: {
|
|
99
|
+
requestDisplayMode: ({ mode }) => applyHostDisplayMode(mode),
|
|
100
|
+
updateModelContext: applyHostModelContext,
|
|
101
|
+
openLink: ({ url }) => window.open(url, "_blank", "noopener,noreferrer"),
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`requestDisplayMode` should update host chrome and return the mode that was actually honored. Echoing `{ mode }` without applying it tells the widget the change succeeded while the host stays `inline`.
|
|
107
|
+
|
|
108
|
+
Caller handlers override the default `openLink` and `sendMessage` behavior and can provide `requestDisplayMode`, `updateModelContext`, and lifecycle hooks such as `onInitialized` and `onSizeChange`. `callTool`, `readResource`, and `listResources` remain bound to the configured `host`; use a custom host to change those data-plane operations.
|
|
109
|
+
|
|
93
110
|
### Route handler
|
|
94
111
|
|
|
95
112
|
The route accepts `POST` requests with `{ method, params }` JSON bodies. Dispatch by method name and return the result as JSON. Example for Next.js App Router:
|
|
@@ -170,7 +187,7 @@ Per-name `setToolUI` registrations always win over the MCP fallback — you can
|
|
|
170
187
|
|
|
171
188
|
## AI SDK integration
|
|
172
189
|
|
|
173
|
-
`@assistant-ui/
|
|
190
|
+
`@assistant-ui/ai-sdk` forwards `callProviderMetadata.mcp.app` from AI SDK tool UI parts into `ToolCallMessagePart.mcp.app`. With AI SDK 5.x and an MCP-Apps-capable MCP server, no extra wiring is required on the part shape.
|
|
174
191
|
|
|
175
192
|
The rich UI comes from the MCP server's metadata, not from the model, so the path is identical whichever provider drives the conversation. Running Claude is just a different `model:` in `streamText` (`anthropic("claude-sonnet-4-6")` via `@ai-sdk/anthropic`); the MCP server, `splitMcpAppTools`, and the renderer are unchanged. MCP Apps is an open standard in the MCP ecosystem (Claude is one of its hosts), so a standard MCP-Apps server renders out of the box. The bridge below is only needed for servers that use OpenAI's `openai/outputTemplate` convention, again independent of which model you run.
|
|
176
193
|
|