@assistant-ui/mcp-docs-server 0.1.32 → 0.1.34
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 +20 -22
- package/.docs/organized/code-examples/with-a2a.md +24 -24
- package/.docs/organized/code-examples/with-ag-ui.md +30 -25
- package/.docs/organized/code-examples/with-ai-sdk-v6.md +11 -9
- package/.docs/organized/code-examples/with-artifacts.md +49 -37
- package/.docs/organized/code-examples/with-assistant-transport.md +63 -51
- package/.docs/organized/code-examples/with-browser-extension.md +22 -10
- package/.docs/organized/code-examples/with-chain-of-thought.md +406 -87
- package/.docs/organized/code-examples/with-cloud-standalone.md +25 -24
- package/.docs/organized/code-examples/with-cloud.md +11 -9
- package/.docs/organized/code-examples/with-custom-thread-list.md +14 -12
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +23 -21
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +14 -13
- package/.docs/organized/code-examples/with-expo.md +44 -29
- package/.docs/organized/code-examples/with-external-store.md +9 -7
- package/.docs/organized/code-examples/with-ffmpeg.md +322 -285
- package/.docs/organized/code-examples/with-generative-ui.md +1066 -256
- package/.docs/organized/code-examples/with-google-adk.md +10 -8
- package/.docs/organized/code-examples/with-heat-graph.md +13 -11
- package/.docs/organized/code-examples/with-image-generation.md +19 -17
- package/.docs/organized/code-examples/with-interactables.md +317 -239
- package/.docs/organized/code-examples/with-langchain.md +14 -12
- package/.docs/organized/code-examples/with-langgraph.md +91 -83
- package/.docs/organized/code-examples/with-livekit.md +24 -22
- package/.docs/organized/code-examples/with-mcp.md +18 -12
- package/.docs/organized/code-examples/with-opencode.md +66 -66
- package/.docs/organized/code-examples/with-react-hook-form.md +19 -17
- package/.docs/organized/code-examples/with-react-ink.md +297 -102
- package/.docs/organized/code-examples/with-react-router.md +17 -15
- package/.docs/organized/code-examples/with-resumable-stream.md +15 -14
- package/.docs/organized/code-examples/with-store.md +78 -76
- package/.docs/organized/code-examples/with-tanstack.md +13 -14
- package/.docs/organized/code-examples/with-tap-runtime.md +26 -24
- package/.docs/raw/docs/(docs)/architecture.mdx +94 -42
- package/.docs/raw/docs/(docs)/cli.mdx +1 -2
- package/.docs/raw/docs/(docs)/installation.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/adapters/persistence.mdx +5 -1
- package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +15 -15
- package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +14 -1
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +18 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/rendering.mdx +33 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/spec.mdx +45 -0
- package/.docs/raw/docs/(reference)/api-reference/hooks/composer-triggers.mdx +41 -41
- package/.docs/raw/docs/(reference)/api-reference/hooks/model-context.mdx +19 -1
- package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +122 -122
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +43 -2
- package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +6 -0
- package/.docs/raw/docs/(reference)/api-reference/overview.mdx +4 -6
- package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +30 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/component-tools.mdx +20 -3
- package/.docs/raw/docs/(reference)/api-reference/tools/index.mdx +8 -8
- package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +52 -4
- package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +100 -10
- package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +7 -59
- package/.docs/raw/docs/cloud/ai-sdk.mdx +0 -2
- package/.docs/raw/docs/{(docs)/copilots → copilots}/model-context.mdx +34 -26
- package/.docs/raw/docs/{(docs)/copilots → copilots}/motivation.mdx +32 -26
- package/.docs/raw/docs/guides/chain-of-thought.mdx +7 -9
- package/.docs/raw/docs/guides/context-api.mdx +2 -1
- package/.docs/raw/docs/guides/index.mdx +3 -12
- package/.docs/raw/docs/guides/mentions.mdx +4 -4
- package/.docs/raw/docs/guides/slash-commands.mdx +1 -1
- package/.docs/raw/docs/guides/suggestions.mdx +1 -1
- package/.docs/raw/docs/ink/adapters.mdx +23 -1
- package/.docs/raw/docs/ink/hooks.mdx +101 -85
- package/.docs/raw/docs/ink/migration.mdx +1 -1
- package/.docs/raw/docs/ink/primitives.mdx +2 -2
- package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +1 -1
- package/.docs/raw/docs/integrations/index.mdx +2 -2
- package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +17 -2
- package/.docs/raw/docs/migrations/toolkit-tools.mdx +232 -0
- package/.docs/raw/docs/primitives/chain-of-thought.mdx +10 -16
- package/.docs/raw/docs/primitives/message.mdx +9 -10
- package/.docs/raw/docs/react-native/hooks.mdx +62 -79
- package/.docs/raw/docs/react-native/migration.mdx +1 -1
- package/.docs/raw/docs/react-native/primitives.mdx +2 -2
- package/.docs/raw/docs/runtimes/a2a/client-and-hooks.mdx +3 -0
- package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +41 -0
- package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +122 -1
- package/.docs/raw/docs/runtimes/concepts/architecture.mdx +46 -4
- package/.docs/raw/docs/runtimes/concepts/threads.mdx +7 -1
- package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +54 -12
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +62 -4
- package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +30 -7
- package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +108 -38
- package/.docs/raw/docs/runtimes/langgraph/generative-ui.mdx +2 -2
- package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +14 -1
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +64 -50
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +98 -86
- package/.docs/raw/docs/tools/backend.mdx +144 -0
- package/.docs/raw/docs/tools/defining-tools.mdx +538 -0
- package/.docs/raw/docs/tools/dynamic-tools.mdx +110 -0
- package/.docs/raw/docs/tools/generative-ui.mdx +214 -0
- package/.docs/raw/docs/tools/index.mdx +71 -0
- package/.docs/raw/docs/{guides → tools}/interactables.mdx +1 -1
- package/.docs/raw/docs/{integrations/tools → tools}/mcp.mdx +145 -50
- package/.docs/raw/docs/{guides → tools}/multi-agent.mdx +15 -17
- package/.docs/raw/docs/tools/tool-ui.mdx +967 -0
- package/.docs/raw/docs/{integrations/tools/react-mcp.mdx → tools/user-managed-mcp.mdx} +7 -7
- package/.docs/raw/docs/ui/directive-text.mdx +3 -3
- package/.docs/raw/docs/ui/mcp-config.mdx +4 -4
- package/.docs/raw/docs/ui/mermaid.mdx +16 -9
- package/.docs/raw/docs/ui/part-grouping.mdx +84 -50
- package/.docs/raw/docs/ui/reasoning.mdx +4 -5
- package/.docs/raw/docs/ui/tool-fallback.mdx +16 -0
- package/.docs/raw/docs/ui/tool-group.mdx +5 -6
- package/.docs/raw/docs/utilities/heat-graph.mdx +1 -1
- package/.docs/raw/docs/utilities/react-o11y.mdx +2 -2
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/prepare-docs/code-examples.d.ts.map +1 -1
- package/dist/prepare-docs/code-examples.js.map +1 -1
- package/dist/prepare-docs/copy-raw.d.ts.map +1 -1
- package/dist/prepare-docs/copy-raw.js.map +1 -1
- package/dist/prepare-docs/prepare.js.map +1 -1
- package/dist/stdio.js.map +1 -1
- package/dist/tools/docs.js.map +1 -1
- package/dist/tools/examples.js.map +1 -1
- package/dist/tools/tests/test-setup.js.map +1 -1
- package/dist/utils/mdx.js.map +1 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js.map +1 -1
- package/package.json +5 -5
- package/.docs/organized/code-examples/with-parent-id-grouping.md +0 -596
- package/.docs/raw/docs/(docs)/copilots/make-assistant-tool-ui.mdx +0 -151
- package/.docs/raw/docs/(docs)/copilots/make-assistant-tool.mdx +0 -230
- package/.docs/raw/docs/guides/generative-ui.mdx +0 -142
- package/.docs/raw/docs/guides/tool-ui.mdx +0 -858
- package/.docs/raw/docs/guides/tools.mdx +0 -736
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/assistant-frame.mdx +0 -0
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/make-assistant-visible.mdx +0 -0
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/use-assistant-instructions.mdx +0 -0
- /package/.docs/raw/docs/{guides → tools}/mcp-apps.mdx +0 -0
|
@@ -117,7 +117,7 @@ For most new code, prefer `MessagePrimitive.Parts` with a `children` render func
|
|
|
117
117
|
Tool call parts resolve in this order:
|
|
118
118
|
|
|
119
119
|
1. **`tools.Override`**: if provided inline through the deprecated `components` prop, handles **all** tool calls
|
|
120
|
-
2. **Globally registered tools**: tools registered via `
|
|
120
|
+
2. **Globally registered tools**: tools registered via `Tools({ toolkit })`
|
|
121
121
|
3. **`tools.by_name[toolName]`**: per-`MessagePrimitive.Parts` inline overrides from the deprecated `components` prop
|
|
122
122
|
4. **`tools.Fallback`**: catch-all for unmatched tool calls from the deprecated `components` prop
|
|
123
123
|
5. **`part.toolUI`**: the resolved tool UI exposed directly in the children render function
|
|
@@ -250,17 +250,16 @@ Renders each content part with type-based component resolution.
|
|
|
250
250
|
|
|
251
251
|
### GroupedParts
|
|
252
252
|
|
|
253
|
-
Groups adjacent message parts into a nested tree. Use `groupBy` to
|
|
253
|
+
Groups adjacent message parts into a nested tree. Use `groupBy` to map each part to a group-key path, then switch on `part.type` in the render function. Group cases render `children`; leaf cases render their own UI. Prefer the `groupPartByType` helper for the common `part.type → path` case.
|
|
254
254
|
|
|
255
255
|
```tsx
|
|
256
|
+
import { MessagePrimitive, groupPartByType } from "@assistant-ui/react";
|
|
257
|
+
|
|
256
258
|
<MessagePrimitive.GroupedParts
|
|
257
|
-
groupBy={(
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
return ["group-chainOfThought", "group-tool"];
|
|
262
|
-
return null;
|
|
263
|
-
}}
|
|
259
|
+
groupBy={groupPartByType({
|
|
260
|
+
reasoning: ["group-chainOfThought", "group-reasoning"],
|
|
261
|
+
"tool-call": ["group-chainOfThought", "group-tool"],
|
|
262
|
+
})}
|
|
264
263
|
>
|
|
265
264
|
{({ part, children }) => {
|
|
266
265
|
switch (part.type) {
|
|
@@ -514,7 +513,7 @@ import { MessagePrimitive, AuiIf } from "@assistant-ui/react";
|
|
|
514
513
|
</MessagePrimitive.Root>;
|
|
515
514
|
```
|
|
516
515
|
|
|
517
|
-
`s.message.status` is a discriminated union of `running | requires-action | complete | incomplete`, defined only on assistant messages. The `role === "assistant"` guard keeps the predicate type-safe. For tool-call-driven generative UI that defers rendering inside the part itself, see [Deferred Rendering](/docs/
|
|
516
|
+
`s.message.status` is a discriminated union of `running | requires-action | complete | incomplete`, defined only on assistant messages. The `role === "assistant"` guard keeps the predicate type-safe. For tool-call-driven generative UI that defers rendering inside the part itself, see [Deferred Rendering](/docs/tools/tool-ui#deferred-rendering) in the Generative UI guide.
|
|
518
517
|
|
|
519
518
|
### Legacy and Unstable APIs
|
|
520
519
|
|
|
@@ -114,52 +114,54 @@ const runtime = useRemoteThreadListRuntime({
|
|
|
114
114
|
|
|
115
115
|
## Model Context Hooks
|
|
116
116
|
|
|
117
|
-
###
|
|
117
|
+
### Tools
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
Author tools in a `"use generative"` file with `defineToolkit`, the same API as on the web ([Defining Tools](/docs/tools/defining-tools)). The tool definition is forwarded to the model; when the model calls it, the `execute` function runs and the `render` component displays the result.
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
121
|
+
Add the [`@assistant-ui/metro`](https://www.npmjs.com/package/@assistant-ui/metro) plugin so Metro compiles the `"use generative"` directive:
|
|
122
|
+
|
|
123
|
+
```js title="metro.config.js"
|
|
124
|
+
const { getDefaultConfig } = require("expo/metro-config");
|
|
125
|
+
const { withAui } = require("@assistant-ui/metro");
|
|
126
|
+
|
|
127
|
+
module.exports = withAui(getDefaultConfig(__dirname));
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```tsx title="weather-toolkit.tsx"
|
|
131
|
+
"use generative";
|
|
132
|
+
|
|
133
|
+
import { defineToolkit } from "@assistant-ui/react-native";
|
|
134
|
+
import { Text, View } from "react-native";
|
|
135
|
+
import { z } from "zod";
|
|
136
|
+
|
|
137
|
+
export default defineToolkit({
|
|
138
|
+
get_weather: {
|
|
139
|
+
description: "Get the current weather for a city",
|
|
140
|
+
parameters: z.object({ city: z.string() }),
|
|
141
|
+
execute: async ({ city }) => {
|
|
142
|
+
"use client";
|
|
143
|
+
const res = await fetch(`https://api.weather.example/${city}`);
|
|
144
|
+
return res.json();
|
|
131
145
|
},
|
|
132
|
-
|
|
146
|
+
render: ({ args, result }) => (
|
|
147
|
+
<View>
|
|
148
|
+
<Text>
|
|
149
|
+
{args.city}: {result?.temperature}°F
|
|
150
|
+
</Text>
|
|
151
|
+
</View>
|
|
152
|
+
),
|
|
133
153
|
},
|
|
134
|
-
execute: async ({ city }) => {
|
|
135
|
-
const res = await fetch(`https://api.weather.example/${city}`);
|
|
136
|
-
return res.json();
|
|
137
|
-
},
|
|
138
|
-
render: ({ args, result }) => (
|
|
139
|
-
<View>
|
|
140
|
-
<Text>{args.city}: {result?.temperature}°F</Text>
|
|
141
|
-
</View>
|
|
142
|
-
),
|
|
143
154
|
});
|
|
144
155
|
```
|
|
145
156
|
|
|
146
|
-
|
|
157
|
+
```tsx title="ToolProvider.tsx"
|
|
158
|
+
import { AuiProvider, Tools, useAui } from "@assistant-ui/react-native";
|
|
159
|
+
import toolkit from "./weather-toolkit";
|
|
147
160
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
useAssistantToolUI({
|
|
154
|
-
toolName: "get_weather",
|
|
155
|
-
render: ({ args, result, status }) => (
|
|
156
|
-
<View>
|
|
157
|
-
{status?.type === "running"
|
|
158
|
-
? <Text>Loading weather for {args.city}...</Text>
|
|
159
|
-
: <Text>{args.city}: {result?.temperature}°F</Text>}
|
|
160
|
-
</View>
|
|
161
|
-
),
|
|
162
|
-
});
|
|
161
|
+
function ToolProvider({ children }: { children: React.ReactNode }) {
|
|
162
|
+
const aui = useAui({ tools: Tools({ toolkit }) });
|
|
163
|
+
return <AuiProvider value={aui}>{children}</AuiProvider>;
|
|
164
|
+
}
|
|
163
165
|
```
|
|
164
166
|
|
|
165
167
|
### useAssistantDataUI
|
|
@@ -193,49 +195,30 @@ useAssistantInstructions("You are a helpful weather assistant.");
|
|
|
193
195
|
|
|
194
196
|
Wrap a tool UI component so that inline state updates (from a parent component's render) are reflected without remounting. Use this when the render function closes over props that change over time.
|
|
195
197
|
|
|
196
|
-
```tsx
|
|
197
|
-
import { useInlineRender } from "@assistant-ui/react-native";
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
execute: async ({ city }) => ({ temperature: 72 }),
|
|
218
|
-
render: ({ args, result }) => <Text>{args.city}: {result?.temperature}°F</Text>,
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
// Mount inside AssistantRuntimeProvider to register
|
|
222
|
-
<WeatherTool />
|
|
198
|
+
```tsx title="my-tool-toolkit.tsx"
|
|
199
|
+
import { defineToolkit, useInlineRender } from "@assistant-ui/react-native";
|
|
200
|
+
import { Text } from "react-native";
|
|
201
|
+
import { useMemo } from "react";
|
|
202
|
+
|
|
203
|
+
export function useMyToolToolkit(someOuterProp: string) {
|
|
204
|
+
const stableRender = useInlineRender(({ args, result }) => (
|
|
205
|
+
<Text>{someOuterProp}: {result?.value}</Text>
|
|
206
|
+
));
|
|
207
|
+
|
|
208
|
+
return useMemo(
|
|
209
|
+
() =>
|
|
210
|
+
defineToolkit({
|
|
211
|
+
my_tool: {
|
|
212
|
+
type: "backend",
|
|
213
|
+
render: stableRender,
|
|
214
|
+
},
|
|
215
|
+
}),
|
|
216
|
+
[stableRender],
|
|
217
|
+
);
|
|
218
|
+
}
|
|
223
219
|
```
|
|
224
220
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
Create a component that registers only a tool UI renderer when mounted.
|
|
228
|
-
|
|
229
|
-
```tsx
|
|
230
|
-
import { makeAssistantToolUI } from "@assistant-ui/react-native";
|
|
231
|
-
|
|
232
|
-
const WeatherToolUI = makeAssistantToolUI({
|
|
233
|
-
toolName: "get_weather",
|
|
234
|
-
render: ({ args, result }) => <Text>{args.city}: {result?.temperature}°F</Text>,
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
<WeatherToolUI />
|
|
238
|
-
```
|
|
221
|
+
Import the toolkit hook, pass its result to `useAui({ tools: Tools({ toolkit }) })`, and provide the returned `aui` with `AuiProvider`, as shown in the [Tools](#tools) section above.
|
|
239
222
|
|
|
240
223
|
### makeAssistantDataUI
|
|
241
224
|
|
|
@@ -9,7 +9,7 @@ If you already have an assistant-ui web app, most of your code transfers directl
|
|
|
9
9
|
|
|
10
10
|
- **Runtime setup** — `useChatRuntime`, `useLocalRuntime`, `ChatModelAdapter`, and all runtime options work identically.
|
|
11
11
|
- **AI SDK integration** — `@assistant-ui/react-ai-sdk` works with React Native. Your `useChatRuntime` + `AssistantChatTransport` setup transfers directly.
|
|
12
|
-
- **Tool definitions** — `
|
|
12
|
+
- **Tool definitions** — `Tools({ toolkit })` and toolkit renderers use the same API.
|
|
13
13
|
- **State hooks** — `useAuiState`, `useAui`, and selector patterns are the same.
|
|
14
14
|
- **Backend code** — Your API routes, streaming endpoints, and server-side logic need zero changes.
|
|
15
15
|
|
|
@@ -338,7 +338,7 @@ Container `View` for a single message.
|
|
|
338
338
|
|
|
339
339
|
### MessagePrimitive.Content
|
|
340
340
|
|
|
341
|
-
Renders message content parts using render-prop functions instead of the component-map approach used by `MessagePrimitive.Parts`. Each part type receives a `part` object and its `index`. Registered
|
|
341
|
+
Renders message content parts using render-prop functions instead of the component-map approach used by `MessagePrimitive.Parts`. Each part type receives a `part` object and its `index`. Registered toolkit renderers are automatically dispatched and take priority over `renderToolCall`.
|
|
342
342
|
|
|
343
343
|
```tsx
|
|
344
344
|
<MessagePrimitive.Content
|
|
@@ -364,7 +364,7 @@ Renders message content parts using render-prop functions instead of the compone
|
|
|
364
364
|
|
|
365
365
|
### MessagePrimitive.Parts
|
|
366
366
|
|
|
367
|
-
Renders message content parts via a `components` prop. Tool call and data parts automatically render registered
|
|
367
|
+
Renders message content parts via a `components` prop. Tool call and data parts automatically render registered toolkit renderers and data UIs (via `useAssistantDataUI`), falling back to components provided here. A default `Text` component using React Native's `<Text>` is provided out of the box.
|
|
368
368
|
|
|
369
369
|
```tsx
|
|
370
370
|
<MessagePrimitive.Parts>
|
|
@@ -19,6 +19,7 @@ const client = new A2AClient({
|
|
|
19
19
|
headers: { Authorization: "Bearer <token>" },
|
|
20
20
|
tenant: "my-org",
|
|
21
21
|
extensions: ["urn:a2a:ext:my-extension"],
|
|
22
|
+
fetchOptions: { credentials: "include" },
|
|
22
23
|
});
|
|
23
24
|
```
|
|
24
25
|
|
|
@@ -37,6 +38,7 @@ const runtime = useA2ARuntime({ client });
|
|
|
37
38
|
| `headers` | `Record<string, string>` or `() => Record<string, string>` | Static or dynamic headers (e.g. for auth tokens). |
|
|
38
39
|
| `tenant` | `string` | Tenant ID for multi-tenant servers (prepended to URL paths). |
|
|
39
40
|
| `extensions` | `string[]` | Extension URIs to negotiate via `A2A-Extensions` header. |
|
|
41
|
+
| `fetchOptions` | `RequestInit` (partial) | Extra fetch options applied to every request (e.g. `{ credentials: "include" }`). `headers`, `body`, `method`, and `signal` are managed internally. |
|
|
40
42
|
|
|
41
43
|
### Client methods
|
|
42
44
|
|
|
@@ -67,6 +69,7 @@ Pass either a pre-built `client` or a `baseUrl` (the runtime creates a client fo
|
|
|
67
69
|
| `tenant` | `string` | Tenant ID for multi-tenant servers. Only used with `baseUrl`. |
|
|
68
70
|
| `headers` | `Record<string, string>` or `() => Record<string, string>` | Headers for the auto-created client. |
|
|
69
71
|
| `extensions` | `string[]` | Extension URIs to negotiate. Only used with `baseUrl`. |
|
|
72
|
+
| `fetchOptions` | `RequestInit` (partial) | Extra fetch options for the auto-created client (e.g. `{ credentials: "include" }`). `headers`, `body`, `method`, and `signal` are managed internally. Only used with `baseUrl`. |
|
|
70
73
|
| `contextId` | `string` | Initial context ID for the conversation. |
|
|
71
74
|
| `configuration` | `A2ASendMessageConfiguration` | Default send message configuration. |
|
|
72
75
|
| `onError` | `(error: Error) => void` | Error callback. |
|
|
@@ -29,6 +29,47 @@ Reference for the runtime's API surface. Start with [quickstart](/docs/runtimes/
|
|
|
29
29
|
| History | `adapters.history` | Per-thread message persistence. |
|
|
30
30
|
| Thread list | `adapters.threadList` | Multi-thread switching (experimental, see below). |
|
|
31
31
|
|
|
32
|
+
## Loading conversation history
|
|
33
|
+
|
|
34
|
+
If your backend exposes the persisted AG-UI messages of a conversation (for
|
|
35
|
+
example a `GET /agents/state` endpoint), use `fromAgUiMessages` to convert them
|
|
36
|
+
to assistant-ui messages and return them from the history adapter so the thread
|
|
37
|
+
is restored on page load:
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { fromAgUiMessages } from "@assistant-ui/react-ag-ui";
|
|
41
|
+
import { ExportedMessageRepository } from "@assistant-ui/react";
|
|
42
|
+
|
|
43
|
+
const runtime = useAgUiRuntime({
|
|
44
|
+
agent,
|
|
45
|
+
adapters: {
|
|
46
|
+
history: {
|
|
47
|
+
async load() {
|
|
48
|
+
const { messages } = await fetch("/agents/state").then((r) => r.json());
|
|
49
|
+
return ExportedMessageRepository.fromArray(fromAgUiMessages(messages));
|
|
50
|
+
},
|
|
51
|
+
async append({ message }) {
|
|
52
|
+
// persist the newly sent message on your backend
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Messages sent during the session are always forwarded to the agent through the
|
|
60
|
+
run input, independent of `append`. A no-op `append` is therefore only safe when
|
|
61
|
+
your backend already persists the conversation on its own; otherwise those
|
|
62
|
+
messages are gone on the next page load.
|
|
63
|
+
|
|
64
|
+
`fromAgUiMessages` accepts an optional second argument: pass
|
|
65
|
+
`{ showThinking: false }` to match a runtime configured with
|
|
66
|
+
`showThinking: false`, so imported reasoning messages are dropped at conversion
|
|
67
|
+
time, the same way a live run never stores them.
|
|
68
|
+
|
|
69
|
+
`fromAgUiMessages` reconstructs the text, reasoning, and tool calls of each
|
|
70
|
+
message. Non-text message content such as images and files is not restored, so a
|
|
71
|
+
backend that persists multimodal messages loads only their text on reload.
|
|
72
|
+
|
|
32
73
|
## Thread list (experimental)
|
|
33
74
|
|
|
34
75
|
<Callout type="warn">
|
|
@@ -254,7 +254,7 @@ export async function POST(req: Request) {
|
|
|
254
254
|
}
|
|
255
255
|
```
|
|
256
256
|
|
|
257
|
-
Frontend tools are registered through `useAui` (see the [tools guide](/docs/
|
|
257
|
+
Frontend tools are registered through `useAui` (see the [tools guide](/docs/tools/defining-tools)) and serialized for the backend via `frontendTools`.
|
|
258
258
|
|
|
259
259
|
## Multi-step tool calls
|
|
260
260
|
|
|
@@ -286,6 +286,127 @@ export async function POST(req: Request) {
|
|
|
286
286
|
|
|
287
287
|
Without a `stopWhen`, AI SDK runs a single inference step. Set `stepCountIs` (or one of AI SDK's other stop conditions) when your tools chain multiple calls.
|
|
288
288
|
|
|
289
|
+
## Server-side tool approval
|
|
290
|
+
|
|
291
|
+
AI SDK v6 lets a tool gate its own execution with `needsApproval`. The server pauses, emits an `approval-requested` part, and resumes once the client posts an approval response. assistant-ui surfaces the gate as `approval` on the tool part and adds a `respondToApproval` prop on the renderer, so tool components stay decoupled from `chatHelpers`.
|
|
292
|
+
|
|
293
|
+
On the backend, mark the tool with `needsApproval` (boolean or function):
|
|
294
|
+
|
|
295
|
+
```ts title="@/app/api/chat/route.ts"
|
|
296
|
+
import { openai } from "@ai-sdk/openai";
|
|
297
|
+
import { streamText, convertToModelMessages, tool } from "ai";
|
|
298
|
+
import { z } from "zod";
|
|
299
|
+
|
|
300
|
+
export async function POST(req: Request) {
|
|
301
|
+
const { messages } = await req.json();
|
|
302
|
+
|
|
303
|
+
const result = streamText({
|
|
304
|
+
model: openai("gpt-5.4-mini"),
|
|
305
|
+
messages: await convertToModelMessages(messages),
|
|
306
|
+
tools: {
|
|
307
|
+
deploy: tool({
|
|
308
|
+
description: "Deploy the current build to an environment.",
|
|
309
|
+
inputSchema: z.object({ target: z.string() }),
|
|
310
|
+
needsApproval: ({ input }) => input.target === "production",
|
|
311
|
+
execute: async ({ target }) => ({ deployed: target }),
|
|
312
|
+
}),
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
return result.toUIMessageStreamResponse();
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
On the client, configure `useChat` with `sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses` so the response is sent back to the server when the user decides:
|
|
321
|
+
|
|
322
|
+
```tsx title="@/app/page.tsx"
|
|
323
|
+
"use client";
|
|
324
|
+
|
|
325
|
+
import { useChat } from "@ai-sdk/react";
|
|
326
|
+
import {
|
|
327
|
+
AssistantRuntimeProvider,
|
|
328
|
+
useAISDKRuntime,
|
|
329
|
+
} from "@assistant-ui/react-ai-sdk";
|
|
330
|
+
import { lastAssistantMessageIsCompleteWithApprovalResponses } from "ai";
|
|
331
|
+
import { Thread } from "@/components/assistant-ui/thread";
|
|
332
|
+
|
|
333
|
+
export default function Page() {
|
|
334
|
+
const chat = useChat({
|
|
335
|
+
api: "/api/chat",
|
|
336
|
+
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,
|
|
337
|
+
});
|
|
338
|
+
const runtime = useAISDKRuntime(chat);
|
|
339
|
+
|
|
340
|
+
return (
|
|
341
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
342
|
+
<Thread />
|
|
343
|
+
</AssistantRuntimeProvider>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Render the gate with a toolkit entry. The `approval` field carries the gate state, and `respondToApproval` is the only correct way to acknowledge it (it reads the approval id from the part):
|
|
349
|
+
|
|
350
|
+
```tsx title="@/app/page.tsx"
|
|
351
|
+
"use client";
|
|
352
|
+
|
|
353
|
+
import { useChat } from "@ai-sdk/react";
|
|
354
|
+
import {
|
|
355
|
+
AssistantRuntimeProvider,
|
|
356
|
+
defineToolkit,
|
|
357
|
+
Tools,
|
|
358
|
+
useAui,
|
|
359
|
+
} from "@assistant-ui/react";
|
|
360
|
+
import { useAISDKRuntime } from "@assistant-ui/react-ai-sdk";
|
|
361
|
+
import { lastAssistantMessageIsCompleteWithApprovalResponses } from "ai";
|
|
362
|
+
import { Thread } from "@/components/assistant-ui/thread";
|
|
363
|
+
|
|
364
|
+
const toolkit = defineToolkit({
|
|
365
|
+
deploy: {
|
|
366
|
+
type: "backend",
|
|
367
|
+
render: ({ args, approval, respondToApproval, result }) => {
|
|
368
|
+
if (approval?.approved === undefined) {
|
|
369
|
+
return (
|
|
370
|
+
<div>
|
|
371
|
+
<p>Approve deploy to {args.target}?</p>
|
|
372
|
+
<button onClick={() => respondToApproval({ approved: true })}>
|
|
373
|
+
Approve
|
|
374
|
+
</button>
|
|
375
|
+
<button
|
|
376
|
+
onClick={() =>
|
|
377
|
+
respondToApproval({ approved: false, reason: "user denied" })
|
|
378
|
+
}
|
|
379
|
+
>
|
|
380
|
+
Deny
|
|
381
|
+
</button>
|
|
382
|
+
</div>
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
if (approval?.approved === false) return <p>Denied</p>;
|
|
386
|
+
if (result === undefined) return <p>Approved, deploying…</p>;
|
|
387
|
+
return <p>Deployed {result.deployed}</p>;
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
export default function Page() {
|
|
393
|
+
const chat = useChat({
|
|
394
|
+
api: "/api/chat",
|
|
395
|
+
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,
|
|
396
|
+
});
|
|
397
|
+
const runtime = useAISDKRuntime(chat);
|
|
398
|
+
const aui = useAui({ tools: Tools({ toolkit }) });
|
|
399
|
+
|
|
400
|
+
return (
|
|
401
|
+
<AssistantRuntimeProvider aui={aui} runtime={runtime}>
|
|
402
|
+
<Thread />
|
|
403
|
+
</AssistantRuntimeProvider>
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
See the [tool UI guide](/docs/tools/tool-ui#server-side-approval-gates) for the full three-state semantics of `approval.approved` and the `isAutomatic` flag.
|
|
409
|
+
|
|
289
410
|
## Quote context
|
|
290
411
|
|
|
291
412
|
assistant-ui's composer can attach quote metadata to user messages (e.g. when the user selects text and clicks "Quote"). On the server, `injectQuoteContext` flattens that metadata into a markdown blockquote prefix so the LLM sees the quoted text:
|
|
@@ -7,8 +7,8 @@ assistant-ui exposes runtime integrations at three layers. Understanding which l
|
|
|
7
7
|
|
|
8
8
|
## The three layers
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
graph TD
|
|
10
|
+
<Flow.Root
|
|
11
|
+
llm={`graph TD
|
|
12
12
|
subgraph Framework["Framework adapters"]
|
|
13
13
|
A1[react-ai-sdk]
|
|
14
14
|
A2[react-langgraph]
|
|
@@ -34,8 +34,50 @@ graph TD
|
|
|
34
34
|
A6 --> C2
|
|
35
35
|
A7 --> C2
|
|
36
36
|
P1 --> C1
|
|
37
|
-
P2 --> C2
|
|
38
|
-
|
|
37
|
+
P2 --> C2`}
|
|
38
|
+
>
|
|
39
|
+
<Flow.Canvas
|
|
40
|
+
edges={[
|
|
41
|
+
{ from: "datastream", to: "local", route: "down", midFrac: 0.4, laneOffset: -60 },
|
|
42
|
+
{ from: "transport", to: "external", route: "down", midFrac: 0.65, toOffset: -14 },
|
|
43
|
+
{ from: "adapters", to: "external", route: "down", midFrac: 0.4, toOffset: 14 },
|
|
44
|
+
]}
|
|
45
|
+
>
|
|
46
|
+
<div className="grid max-w-2xl grid-cols-[1fr_1.6fr] gap-4">
|
|
47
|
+
<Flow.Group>
|
|
48
|
+
<Flow.GroupLabel>Protocol layers</Flow.GroupLabel>
|
|
49
|
+
<Flow.Column className="items-stretch gap-5">
|
|
50
|
+
<Flow.Row className="justify-start">
|
|
51
|
+
<Flow.Node flowId="datastream">DataStream</Flow.Node>
|
|
52
|
+
</Flow.Row>
|
|
53
|
+
<Flow.Row className="justify-end">
|
|
54
|
+
<Flow.Node flowId="transport">AssistantTransport</Flow.Node>
|
|
55
|
+
</Flow.Row>
|
|
56
|
+
</Flow.Column>
|
|
57
|
+
</Flow.Group>
|
|
58
|
+
<Flow.Group flowId="adapters">
|
|
59
|
+
<Flow.GroupLabel>Framework adapters</Flow.GroupLabel>
|
|
60
|
+
<Flow.Row className="flex-wrap justify-start">
|
|
61
|
+
<Flow.Node>react-ai-sdk</Flow.Node>
|
|
62
|
+
<Flow.Node>react-langgraph</Flow.Node>
|
|
63
|
+
<Flow.Node>react-langchain</Flow.Node>
|
|
64
|
+
<Flow.Node>react-google-adk</Flow.Node>
|
|
65
|
+
<Flow.Node>react-a2a</Flow.Node>
|
|
66
|
+
<Flow.Node>react-ag-ui</Flow.Node>
|
|
67
|
+
<Flow.Node>react-opencode</Flow.Node>
|
|
68
|
+
</Flow.Row>
|
|
69
|
+
</Flow.Group>
|
|
70
|
+
</div>
|
|
71
|
+
<div className="h-14" aria-hidden />
|
|
72
|
+
<Flow.Group className="mx-auto w-fit">
|
|
73
|
+
<Flow.GroupLabel>Core runtimes</Flow.GroupLabel>
|
|
74
|
+
<Flow.Row className="gap-10">
|
|
75
|
+
<Flow.Node flowId="local">LocalRuntime</Flow.Node>
|
|
76
|
+
<Flow.Node flowId="external">ExternalStoreRuntime</Flow.Node>
|
|
77
|
+
</Flow.Row>
|
|
78
|
+
</Flow.Group>
|
|
79
|
+
</Flow.Canvas>
|
|
80
|
+
</Flow.Root>
|
|
39
81
|
|
|
40
82
|
Each upper layer is implemented in terms of a lower one. You can drop down a layer whenever you need more control, but most users start at the framework layer and never touch the others.
|
|
41
83
|
|
|
@@ -287,6 +287,12 @@ A few invariants worth knowing when wiring a custom UI on top of `loadMore()`:
|
|
|
287
287
|
description: "Persist title changes from the UI.",
|
|
288
288
|
required: true,
|
|
289
289
|
},
|
|
290
|
+
{
|
|
291
|
+
name: "updateCustom",
|
|
292
|
+
type: "(remoteId: string, custom: Record<string, unknown> | undefined) => Promise<void>",
|
|
293
|
+
description:
|
|
294
|
+
"Optional. Persist replacement custom metadata from `aui.threadListItem().updateCustom(custom)`.",
|
|
295
|
+
},
|
|
290
296
|
{
|
|
291
297
|
name: "archive",
|
|
292
298
|
type: "(remoteId: string) => Promise<void>",
|
|
@@ -355,7 +361,7 @@ function ThreadListItemMeta() {
|
|
|
355
361
|
}
|
|
356
362
|
```
|
|
357
363
|
|
|
358
|
-
`custom` is preserved across `rename`, `archive`, `unarchive`, and `generateTitle`. To
|
|
364
|
+
`custom` is preserved across `rename`, `archive`, `unarchive`, and `generateTitle`. To replace it from your UI, implement `RemoteThreadListAdapter.updateCustom` and call `aui.threadListItem().updateCustom(custom)`. The cloud adapter persists this through `cloud.threads.update(threadId, { metadata })`. If your adapter mutates thread metadata through a separate application path, return the updated values from `fetch()` or call `aui.threads().reload()` to re-run `list()`.
|
|
359
365
|
|
|
360
366
|
## ExternalStoreThreadListAdapter
|
|
361
367
|
|
|
@@ -25,11 +25,17 @@ If you only need message streaming, [DataStream](/docs/runtimes/custom/data-stre
|
|
|
25
25
|
|
|
26
26
|
## Mental model
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
graph LR
|
|
28
|
+
<Flow.Root
|
|
29
|
+
llm={`graph LR
|
|
30
30
|
Frontend -->|Commands| Agent[Agent server]
|
|
31
|
-
Agent -->|State snapshots| Frontend
|
|
32
|
-
|
|
31
|
+
Agent -->|State snapshots| Frontend`}
|
|
32
|
+
>
|
|
33
|
+
<Flow.Row>
|
|
34
|
+
<Flow.Node>Frontend</Flow.Node>
|
|
35
|
+
<Flow.Arrow label="Commands" reverseLabel="State snapshots" length={150} />
|
|
36
|
+
<Flow.Node>Agent server</Flow.Node>
|
|
37
|
+
</Flow.Row>
|
|
38
|
+
</Flow.Root>
|
|
33
39
|
|
|
34
40
|
The frontend receives state snapshots and converts them to React components. The UI is a stateless view on top of the agent state.
|
|
35
41
|
|
|
@@ -37,21 +43,57 @@ The agent server receives commands from the frontend. When a user interacts with
|
|
|
37
43
|
|
|
38
44
|
### Command lifecycle
|
|
39
45
|
|
|
40
|
-
|
|
41
|
-
graph LR
|
|
46
|
+
<Flow.Root
|
|
47
|
+
llm={`graph LR
|
|
42
48
|
queued -->|sent to backend| in_transit
|
|
43
|
-
in_transit -->|backend processes| applied
|
|
44
|
-
|
|
49
|
+
in_transit -->|backend processes| applied`}
|
|
50
|
+
>
|
|
51
|
+
<Flow.Row>
|
|
52
|
+
<Flow.Node>queued</Flow.Node>
|
|
53
|
+
<Flow.Arrow label="sent to backend" length={120} />
|
|
54
|
+
<Flow.Node>in_transit</Flow.Node>
|
|
55
|
+
<Flow.Arrow label="backend processes" length={132} />
|
|
56
|
+
<Flow.Node>applied</Flow.Node>
|
|
57
|
+
</Flow.Row>
|
|
58
|
+
</Flow.Root>
|
|
45
59
|
|
|
46
60
|
The runtime alternates between **idle** (no active backend request) and **sending** (request in flight). When a new command is created while idle, it is sent immediately; otherwise it is queued until the current request completes.
|
|
47
61
|
|
|
48
|
-
|
|
49
|
-
graph LR
|
|
62
|
+
<Flow.Root
|
|
63
|
+
llm={`graph LR
|
|
50
64
|
idle -->|new command| sending
|
|
51
65
|
sending -->|request completes| check{check queue}
|
|
52
66
|
check -->|queue has commands| sending
|
|
53
|
-
check -->|queue empty| idle
|
|
54
|
-
|
|
67
|
+
check -->|queue empty| idle`}
|
|
68
|
+
>
|
|
69
|
+
<Flow.Canvas
|
|
70
|
+
edges={[
|
|
71
|
+
{
|
|
72
|
+
from: "check",
|
|
73
|
+
to: "sending",
|
|
74
|
+
route: "loop-bottom",
|
|
75
|
+
label: "queue has commands",
|
|
76
|
+
laneOffset: 28,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
from: "check",
|
|
80
|
+
to: "idle",
|
|
81
|
+
route: "loop-bottom",
|
|
82
|
+
label: "queue empty",
|
|
83
|
+
laneOffset: 60,
|
|
84
|
+
},
|
|
85
|
+
]}
|
|
86
|
+
>
|
|
87
|
+
<Flow.Row>
|
|
88
|
+
<Flow.Node flowId="idle">idle</Flow.Node>
|
|
89
|
+
<Flow.Arrow label="new command" length={108} />
|
|
90
|
+
<Flow.Node flowId="sending">sending</Flow.Node>
|
|
91
|
+
<Flow.Arrow label="request completes" length={132} />
|
|
92
|
+
<Flow.Node flowId="check" variant="decision">check queue</Flow.Node>
|
|
93
|
+
</Flow.Row>
|
|
94
|
+
<div className="h-20" aria-hidden />
|
|
95
|
+
</Flow.Canvas>
|
|
96
|
+
</Flow.Root>
|
|
55
97
|
|
|
56
98
|
To implement this you build two pieces:
|
|
57
99
|
|