@assistant-ui/mcp-docs-server 0.1.33 → 0.1.35
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 +7 -7
- package/.docs/organized/code-examples/with-a2a.md +8 -8
- package/.docs/organized/code-examples/with-ag-ui.md +12 -12
- package/.docs/organized/code-examples/with-ai-sdk-v6.md +10 -10
- package/.docs/organized/code-examples/with-artifacts.md +40 -34
- package/.docs/organized/code-examples/with-assistant-transport.md +11 -11
- package/.docs/organized/code-examples/with-browser-extension.md +9 -9
- package/.docs/organized/code-examples/with-chain-of-thought.md +72 -51
- package/.docs/organized/code-examples/with-cloud-standalone.md +10 -10
- package/.docs/organized/code-examples/with-cloud.md +10 -10
- package/.docs/organized/code-examples/with-custom-thread-list.md +10 -10
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +12 -12
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +12 -12
- package/.docs/organized/code-examples/with-expo.md +66 -31
- package/.docs/organized/code-examples/with-external-store.md +8 -8
- package/.docs/organized/code-examples/with-ffmpeg.md +13 -13
- package/.docs/organized/code-examples/with-generative-ui.md +98 -368
- package/.docs/organized/code-examples/with-google-adk.md +9 -9
- package/.docs/organized/code-examples/with-heat-graph.md +7 -7
- package/.docs/organized/code-examples/with-image-generation.md +10 -10
- package/.docs/organized/code-examples/with-interactables.md +10 -10
- package/.docs/organized/code-examples/with-langchain.md +10 -10
- package/.docs/organized/code-examples/with-langgraph.md +33 -29
- package/.docs/organized/code-examples/with-livekit.md +12 -12
- package/.docs/organized/code-examples/with-mcp.md +11 -11
- package/.docs/organized/code-examples/with-opencode.md +109 -583
- package/.docs/organized/code-examples/with-pi.md +2044 -0
- package/.docs/organized/code-examples/with-react-hook-form.md +11 -11
- package/.docs/organized/code-examples/with-react-ink-web.md +691 -0
- package/.docs/organized/code-examples/with-react-ink.md +309 -102
- package/.docs/organized/code-examples/with-react-router.md +14 -14
- package/.docs/organized/code-examples/with-resumable-stream.md +11 -11
- package/.docs/organized/code-examples/with-store.md +70 -66
- package/.docs/organized/code-examples/with-tanstack.md +25 -11
- package/.docs/organized/code-examples/with-tap-runtime.md +8 -8
- package/.docs/organized/code-examples/with-virtualized-thread.md +656 -0
- package/.docs/raw/docs/(docs)/architecture.mdx +65 -53
- package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +3 -0
- package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +44 -1
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +14 -3
- package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +2 -23
- package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +22 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +93 -9
- package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +21 -86
- package/.docs/raw/docs/guides/chain-of-thought.mdx +1 -1
- package/.docs/raw/docs/guides/index.mdx +3 -0
- package/.docs/raw/docs/guides/input-history.mdx +55 -0
- package/.docs/raw/docs/guides/virtualization.mdx +63 -0
- package/.docs/raw/docs/ink/adapters.mdx +23 -1
- package/.docs/raw/docs/ink/hooks.mdx +22 -19
- package/.docs/raw/docs/migrations/toolkit-tools.mdx +14 -8
- package/.docs/raw/docs/react-native/hooks.mdx +26 -18
- package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +39 -0
- package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +3 -3
- package/.docs/raw/docs/runtimes/concepts/architecture.mdx +46 -4
- package/.docs/raw/docs/runtimes/concepts/stability.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +54 -12
- package/.docs/raw/docs/runtimes/custom/data-stream.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +62 -4
- package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +120 -4
- package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +9 -9
- package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +13 -0
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +5 -5
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +3 -3
- package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +6 -6
- package/.docs/raw/docs/tools/backend.mdx +19 -11
- package/.docs/raw/docs/tools/defining-tools.mdx +177 -52
- package/.docs/raw/docs/tools/index.mdx +7 -12
- package/.docs/raw/docs/tools/mcp.mdx +83 -15
- package/.docs/raw/docs/tools/multi-agent.mdx +5 -5
- package/.docs/raw/docs/tools/tool-ui.mdx +64 -28
- package/.docs/raw/docs/tools/user-managed-mcp.mdx +4 -4
- package/.docs/raw/docs/ui/dot-matrix.mdx +133 -0
- package/.docs/raw/docs/ui/mermaid.mdx +16 -9
- package/.docs/raw/docs/ui/model-selector.mdx +219 -52
- package/.docs/raw/docs/ui/number-roll.mdx +154 -0
- package/.docs/raw/docs/ui/part-grouping.mdx +2 -2
- package/.docs/raw/docs/ui/reasoning.mdx +3 -3
- package/.docs/raw/docs/ui/streamdown.mdx +2 -0
- package/.docs/raw/docs/ui/syntax-highlighting.mdx +1 -1
- package/.docs/raw/docs/ui/thread.mdx +52 -0
- package/.docs/raw/docs/ui/tool-fallback.mdx +16 -0
- package/.docs/raw/docs/utilities/react-o11y.mdx +2 -2
- package/dist/constants.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/prepare-docs/code-examples.js.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.js.map +1 -1
- package/package.json +4 -4
- /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}/model-context.mdx +0 -0
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/motivation.mdx +0 -0
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/use-assistant-instructions.mdx +0 -0
|
@@ -8,23 +8,25 @@ Tools let the model take actions: fetch data, call an API, query a database, dri
|
|
|
8
8
|
|
|
9
9
|
This page covers how to **author** tools. To render a tool call as a custom component, see [Tool UI](/docs/tools/tool-ui). To wire tools into your server, see [Backend tools](/docs/tools/backend).
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Define tools with `"use generative"`
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Use `"use generative"` + `defineToolkit` for toolkits. The compiler co-locates
|
|
14
|
+
the schema, executor, and renderer in one file and splits them across the
|
|
15
|
+
client/server boundary for you.
|
|
14
16
|
|
|
15
17
|
<Callout type="info">
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
elsewhere — an MCP server, a separate backend route, a LangGraph node — and you
|
|
23
|
-
only want to attach a renderer. These are render-only entries with an explicit
|
|
24
|
-
`type`.
|
|
18
|
+
You can still use the generative toolkit pattern when a tool executes
|
|
19
|
+
elsewhere:
|
|
20
|
+
|
|
21
|
+
- for MCP servers, spread `defineMcpToolkit({ ... })`;
|
|
22
|
+
- for non-MCP tools defined by another backend or runtime, write
|
|
23
|
+
`execute: externalTool()` and provide a renderer.
|
|
25
24
|
</Callout>
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
In a `"use generative"` file every tool declares an `execute`, and you never
|
|
27
|
+
write `type` yourself — the compiler infers it. For render-only external tools,
|
|
28
|
+
`externalTool()` is the escape hatch that satisfies the compiler without
|
|
29
|
+
emitting schema or executable code on the server.
|
|
28
30
|
|
|
29
31
|
## Quick start (`"use generative"`)
|
|
30
32
|
|
|
@@ -55,6 +57,17 @@ export default defineConfig({
|
|
|
55
57
|
});
|
|
56
58
|
```
|
|
57
59
|
|
|
60
|
+
For Expo, wrap your Metro config with `withAui`:
|
|
61
|
+
|
|
62
|
+
```js title="metro.config.js"
|
|
63
|
+
const { getDefaultConfig } = require("expo/metro-config");
|
|
64
|
+
const { withAui } = require("@assistant-ui/metro");
|
|
65
|
+
|
|
66
|
+
module.exports = withAui(getDefaultConfig(__dirname));
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
For a bare React Native app, import `getDefaultConfig` from `@react-native/metro-config` instead of `expo/metro-config`.
|
|
70
|
+
|
|
58
71
|
</Step>
|
|
59
72
|
<Step>
|
|
60
73
|
|
|
@@ -128,21 +141,23 @@ export function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
|
|
|
128
141
|
|
|
129
142
|
### Expose the toolkit to the model on your server
|
|
130
143
|
|
|
131
|
-
The same import resolves to the **server build** inside a route handler.
|
|
144
|
+
The same import resolves to the **server build** inside a route handler. Wrap it in an `AISDKToolkit` so the model is configured with every tool's schema:
|
|
132
145
|
|
|
133
146
|
```ts title="app/api/chat/route.ts"
|
|
134
|
-
import {
|
|
147
|
+
import { AISDKToolkit } from "@assistant-ui/react-ai-sdk";
|
|
135
148
|
import { streamText, convertToModelMessages } from "ai";
|
|
136
149
|
import { openai } from "@ai-sdk/openai";
|
|
137
150
|
import toolkit from "../../toolkit";
|
|
138
151
|
|
|
152
|
+
const aiToolkit = new AISDKToolkit({ toolkit });
|
|
153
|
+
|
|
139
154
|
export async function POST(req: Request) {
|
|
140
155
|
const { messages, tools } = await req.json();
|
|
141
156
|
|
|
142
157
|
const result = streamText({
|
|
143
158
|
model: openai("gpt-5.4-nano"),
|
|
144
159
|
messages: await convertToModelMessages(messages),
|
|
145
|
-
tools:
|
|
160
|
+
tools: await aiToolkit.tools({ frontend: tools }),
|
|
146
161
|
});
|
|
147
162
|
|
|
148
163
|
return result.toUIMessageStreamResponse();
|
|
@@ -164,9 +179,10 @@ The tool's **kind is inferred from its `execute`** and written back as a `type`
|
|
|
164
179
|
| --- | --- | --- | --- |
|
|
165
180
|
| plain `async () => …` | **backend** | schema + `execute` (guarded `server-only`) | schema + `render` |
|
|
166
181
|
| `async () => { "use client"; … }` | **frontend** | schema only | schema + `execute` + `render`/`renderText` |
|
|
167
|
-
| `
|
|
182
|
+
| `humanTool()` | **human** | schema only | schema + `render` |
|
|
168
183
|
| `stubTool()` | **frontend** (executor supplied at runtime) | schema only | schema + `render`/`renderText` |
|
|
169
184
|
| `providerTool({ … })` | **provider** | schema + provider config | schema + provider config |
|
|
185
|
+
| `externalTool()` | **backend** (defined elsewhere) | omitted | `type: "backend"` + `render`/`renderText` |
|
|
170
186
|
|
|
171
187
|
The compiler also enforces, at build time:
|
|
172
188
|
|
|
@@ -176,6 +192,26 @@ The compiler also enforces, at build time:
|
|
|
176
192
|
|
|
177
193
|
## Tool kinds
|
|
178
194
|
|
|
195
|
+
### Backend tools
|
|
196
|
+
|
|
197
|
+
Run on your server. Author a plain `execute` (no `"use client"`); the compiler moves it to the server build behind `import "server-only"` and keeps only the schema and `render` on the client. A backend tool can still carry a `render` to show its call as a trace:
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
geocode_location: {
|
|
201
|
+
description: "Geocode a location name into latitude/longitude.",
|
|
202
|
+
parameters: z.object({ query: z.string() }),
|
|
203
|
+
execute: async ({ query }) => geocodeLocation(query),
|
|
204
|
+
render: GeocodeToolUI,
|
|
205
|
+
},
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
<Callout type="tip">
|
|
209
|
+
A backend tool authored this way **has** an `execute`. To attach a renderer to
|
|
210
|
+
a tool whose execution lives entirely elsewhere (an MCP server, a different
|
|
211
|
+
backend route) — where there is no real executor to write — use
|
|
212
|
+
`externalTool()` or `defineMcpToolkit()`.
|
|
213
|
+
</Callout>
|
|
214
|
+
|
|
179
215
|
### Frontend tools
|
|
180
216
|
|
|
181
217
|
Run in the browser. Author a real `execute` with a leading `"use client"`:
|
|
@@ -196,35 +232,15 @@ copy_to_clipboard: {
|
|
|
196
232
|
},
|
|
197
233
|
```
|
|
198
234
|
|
|
199
|
-
### Backend tools
|
|
200
|
-
|
|
201
|
-
Run on your server. Author a plain `execute` (no `"use client"`); the compiler moves it to the server build behind `import "server-only"` and keeps only the schema and `render` on the client. A backend tool can still carry a `render` to show its call as a trace:
|
|
202
|
-
|
|
203
|
-
```tsx
|
|
204
|
-
geocode_location: {
|
|
205
|
-
description: "Geocode a location name into latitude/longitude.",
|
|
206
|
-
parameters: z.object({ query: z.string() }),
|
|
207
|
-
execute: async ({ query }) => geocodeLocation(query),
|
|
208
|
-
render: GeocodeToolUI,
|
|
209
|
-
},
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
<Callout type="tip">
|
|
213
|
-
A backend tool authored this way **has** an `execute`. To attach a renderer to
|
|
214
|
-
a tool whose execution lives entirely elsewhere (an MCP server, a different
|
|
215
|
-
backend route) — where there is no `execute` to write — use a [plain
|
|
216
|
-
toolkit](#render-only-tools-for-externally-executed-tools) instead.
|
|
217
|
-
</Callout>
|
|
218
|
-
|
|
219
235
|
### Human tools
|
|
220
236
|
|
|
221
|
-
Pause the run until the user supplies a result through the rendered UI. Author `execute:
|
|
237
|
+
Pause the run until the user supplies a result through the rendered UI. Author `execute: humanTool()` and a `render` that calls `addResult` exactly once:
|
|
222
238
|
|
|
223
239
|
```tsx
|
|
224
240
|
select_date: {
|
|
225
241
|
description: "Ask the user to select a date.",
|
|
226
242
|
parameters: z.object({ prompt: z.string() }),
|
|
227
|
-
execute:
|
|
243
|
+
execute: humanTool(),
|
|
228
244
|
render: ({ args, result, addResult }) => {
|
|
229
245
|
if (result) return <p>Selected {result.date}</p>;
|
|
230
246
|
return (
|
|
@@ -237,7 +253,7 @@ select_date: {
|
|
|
237
253
|
},
|
|
238
254
|
```
|
|
239
255
|
|
|
240
|
-
`
|
|
256
|
+
`humanTool` is imported from `@assistant-ui/react`. See [Tool UI → Human-in-the-loop](/docs/tools/tool-ui#user-input-collection) for the full pattern.
|
|
241
257
|
|
|
242
258
|
### Provider tools
|
|
243
259
|
|
|
@@ -252,9 +268,68 @@ web_search: {
|
|
|
252
268
|
},
|
|
253
269
|
```
|
|
254
270
|
|
|
255
|
-
###
|
|
271
|
+
### Externally defined tools
|
|
272
|
+
|
|
273
|
+
Use `externalTool()` when a non-MCP tool is already defined and executed by
|
|
274
|
+
another system (for example a separate backend route or LangGraph node), but you
|
|
275
|
+
want assistant-ui to render its tool calls. Import `externalTool` from
|
|
276
|
+
`@assistant-ui/react`:
|
|
277
|
+
|
|
278
|
+
```tsx
|
|
279
|
+
web_search: {
|
|
280
|
+
parameters: z.object({ query: z.string() }),
|
|
281
|
+
execute: externalTool(),
|
|
282
|
+
render: ({ args, result }) => (
|
|
283
|
+
<SearchResults query={args.query} results={result?.results ?? []} />
|
|
284
|
+
),
|
|
285
|
+
},
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
The compiler omits this entry from the server build, so the model still gets
|
|
289
|
+
the tool definition from the external system. The client build keeps only
|
|
290
|
+
`type: "backend"` and the renderer (or `renderText`) for matching tool-call
|
|
291
|
+
message parts.
|
|
256
292
|
|
|
257
|
-
|
|
293
|
+
### Tool stubs (supply the executor elsewhere)
|
|
294
|
+
|
|
295
|
+
Sometimes a tool's executor can't live in the build-split `"use generative"` file, usually because it has to close over React state (a `useState` setter, a ref). Declare the model-facing contract with `execute: stubTool()`, then supply the real executor at runtime with `useAuiToolOverrides` from the component that owns the state:
|
|
296
|
+
|
|
297
|
+
```tsx title="app/toolkit.tsx"
|
|
298
|
+
"use generative";
|
|
299
|
+
|
|
300
|
+
import { defineToolkit, stubTool } from "@assistant-ui/react";
|
|
301
|
+
import { manageTasksParameters } from "./state";
|
|
302
|
+
|
|
303
|
+
export default defineToolkit({
|
|
304
|
+
manage_tasks: {
|
|
305
|
+
description: "Add, toggle, or clear tasks on the board.",
|
|
306
|
+
parameters: manageTasksParameters,
|
|
307
|
+
execute: stubTool(),
|
|
308
|
+
renderText: { running: "Updating tasks…", complete: "Tasks updated" },
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
```tsx title="app/TaskBoard.tsx"
|
|
314
|
+
import { useAuiToolOverrides } from "@assistant-ui/react";
|
|
315
|
+
|
|
316
|
+
function TaskBoardToolOverrides({ setTasks }) {
|
|
317
|
+
useAuiToolOverrides({
|
|
318
|
+
manage_tasks: {
|
|
319
|
+
execute: async ({ action, title }) => {
|
|
320
|
+
// close over setTasks here, then return a payload for the model
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
`stubTool()` has no runtime implementation: it marks the executor as supplied later, while the compiler still ships the schema to the backend so the model can call the tool. The override registers above the toolkit default, so its `execute` wins for that name. To turn a tool off at runtime instead, see [Disabling a tool](#disabling-a-tool). See [Dynamic tools](/docs/tools/dynamic-tools) for the full walkthrough.
|
|
329
|
+
|
|
330
|
+
<Callout type="warn">
|
|
331
|
+
`useAuiToolOverrides` is experimental and its API may change.
|
|
332
|
+
</Callout>
|
|
258
333
|
|
|
259
334
|
## Rendering a tool call
|
|
260
335
|
|
|
@@ -271,29 +346,32 @@ If you don't provide a renderer, add the [`ToolFallback`](/docs/ui/tool-fallback
|
|
|
271
346
|
|
|
272
347
|
## Render-only tools (for externally-executed tools)
|
|
273
348
|
|
|
274
|
-
|
|
349
|
+
Prefer `"use generative"` with `externalTool()` for non-MCP tools, or
|
|
350
|
+
`defineMcpToolkit()` for MCP servers. If a file cannot go through the generative
|
|
351
|
+
compiler, declare a `"use client"` toolkit object with an explicit
|
|
352
|
+
`type: "backend"` and only a `render`:
|
|
275
353
|
|
|
276
354
|
```tsx title="app/tool-ui.tsx"
|
|
277
355
|
"use client";
|
|
278
356
|
|
|
279
|
-
import
|
|
357
|
+
import { defineToolkit } from "@assistant-ui/react";
|
|
280
358
|
|
|
281
|
-
export const toolkit = {
|
|
359
|
+
export const toolkit = defineToolkit({
|
|
282
360
|
web_search: {
|
|
283
361
|
type: "backend",
|
|
284
362
|
render: ({ args, result }) => (
|
|
285
363
|
<SearchResults query={args.query} results={result?.results ?? []} />
|
|
286
364
|
),
|
|
287
365
|
},
|
|
288
|
-
}
|
|
366
|
+
});
|
|
289
367
|
```
|
|
290
368
|
|
|
291
369
|
Register it exactly like a generative toolkit: `useAui({ tools: Tools({ toolkit }) })`. The key must match the tool name your backend or MCP server publishes. Render-only entries upload no schema and run no browser code — they only attach UI to matching tool-call message parts.
|
|
292
370
|
|
|
293
371
|
<Callout type="warn">
|
|
294
|
-
This `{ type: "backend", render }` shape is **plain-toolkit only**.
|
|
295
|
-
|
|
296
|
-
declare an `execute`, and you never author `type` there.
|
|
372
|
+
This `{ type: "backend", render }` shape is **plain-toolkit only**. Inside a
|
|
373
|
+
`"use generative"` file, use `execute: externalTool()` instead; generative
|
|
374
|
+
tools must declare an `execute`, and you never author `type` there.
|
|
297
375
|
</Callout>
|
|
298
376
|
|
|
299
377
|
## Organizing toolkits
|
|
@@ -311,12 +389,28 @@ export type GetWeatherArgs = z.infer<typeof getWeatherParameters>;
|
|
|
311
389
|
|
|
312
390
|
### Split tools across files and merge them
|
|
313
391
|
|
|
392
|
+
Each file you split into is its own `"use generative"` module that default-exports a `defineToolkit(...)`:
|
|
393
|
+
|
|
394
|
+
```tsx title="app/tools/weather.tsx"
|
|
395
|
+
"use generative";
|
|
396
|
+
|
|
397
|
+
import { defineToolkit } from "@assistant-ui/react";
|
|
398
|
+
|
|
399
|
+
export default defineToolkit({
|
|
400
|
+
get_weather: {
|
|
401
|
+
/* description, parameters, execute, render */
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Merge them by spreading their default imports into a parent toolkit:
|
|
407
|
+
|
|
314
408
|
```tsx title="app/toolkit.tsx"
|
|
315
409
|
"use generative";
|
|
316
410
|
|
|
317
411
|
import { defineToolkit } from "@assistant-ui/react";
|
|
318
|
-
import
|
|
319
|
-
import
|
|
412
|
+
import weatherTools from "./tools/weather";
|
|
413
|
+
import databaseTools from "./tools/database";
|
|
320
414
|
|
|
321
415
|
export default defineToolkit({
|
|
322
416
|
...weatherTools,
|
|
@@ -324,11 +418,26 @@ export default defineToolkit({
|
|
|
324
418
|
});
|
|
325
419
|
```
|
|
326
420
|
|
|
327
|
-
|
|
421
|
+
The compiler splits each file across the client/server boundary on its own, then checks that the spread import resolves to a `"use generative"` module before allowing it, so a backend `execute` can't leak to the client. Two rules follow:
|
|
422
|
+
|
|
423
|
+
- **Spread a default import** (`import weatherTools from "./tools/weather"`). Relative paths and `tsconfig` path aliases like `@/tools/weather` both resolve. Only the default export crosses the generative-module boundary, so a named import (or any opaque, non-generative import) is rejected.
|
|
424
|
+
- You can also spread a local `defineToolkit(...)` or `defineMcpToolkit(...)` binding declared in the same file.
|
|
328
425
|
|
|
329
426
|
### Add MCP server tools
|
|
330
427
|
|
|
331
|
-
|
|
428
|
+
`defineMcpToolkit` exposes tools from an MCP server. For an MCP-only toolkit, export it directly:
|
|
429
|
+
|
|
430
|
+
```tsx title="app/mcp-toolkit.tsx"
|
|
431
|
+
"use generative";
|
|
432
|
+
|
|
433
|
+
import { defineMcpToolkit } from "@assistant-ui/react";
|
|
434
|
+
|
|
435
|
+
export default defineMcpToolkit({
|
|
436
|
+
docs: { type: "http", url: "https://mcp.example.com/mcp" },
|
|
437
|
+
});
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
To expose MCP tools alongside your own, spread it into a `defineToolkit`:
|
|
332
441
|
|
|
333
442
|
```tsx
|
|
334
443
|
"use generative";
|
|
@@ -339,6 +448,7 @@ export default defineToolkit({
|
|
|
339
448
|
...defineMcpToolkit({
|
|
340
449
|
docs: { type: "http", url: "https://mcp.example.com/mcp" },
|
|
341
450
|
}),
|
|
451
|
+
// ...your own tools
|
|
342
452
|
});
|
|
343
453
|
```
|
|
344
454
|
|
|
@@ -408,6 +518,21 @@ While a tool runs, its arguments arrive as partial JSON. Use [`useToolArgsStatus
|
|
|
408
518
|
|
|
409
519
|
Set `disabled: true` to keep a tool known to the client but hidden from the model in the current scope.
|
|
410
520
|
|
|
521
|
+
To toggle a tool off at runtime without editing the toolkit, register the same flag through `useAuiToolOverrides`:
|
|
522
|
+
|
|
523
|
+
```tsx
|
|
524
|
+
import { useAuiToolOverrides } from "@assistant-ui/react";
|
|
525
|
+
|
|
526
|
+
function GuestModeTools() {
|
|
527
|
+
useAuiToolOverrides({
|
|
528
|
+
delete_account: { disabled: true },
|
|
529
|
+
});
|
|
530
|
+
return null;
|
|
531
|
+
}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
The override registers above the toolkit default, so the tool drops out of the set sent to the model. Mount the override only while the tool should be hidden (for example, for signed-out users); unmounting it restores the toolkit default.
|
|
535
|
+
|
|
411
536
|
## Migrating from the component APIs
|
|
412
537
|
|
|
413
538
|
`makeAssistantTool`, `useAssistantTool`, `makeAssistantToolUI`, and `useAssistantToolUI` are deprecated. See [Migrating Tools to Toolkits](/docs/migrations/toolkit-tools) for the mechanical migration.
|
|
@@ -13,7 +13,7 @@ Tools are how the model takes action: fetch data, call an API, query a database,
|
|
|
13
13
|
Author a toolkit with the `"use generative"` directive — frontend, backend, human, and provider tools, with the schema, executor, and renderer in one file.
|
|
14
14
|
</Card>
|
|
15
15
|
<Card title="Backend Tools" href="/docs/tools/backend">
|
|
16
|
-
Wire a toolkit into your AI SDK route with `
|
|
16
|
+
Wire a toolkit into your AI SDK route with `AISDKToolkit` / `frontendTools`, mix client and server tools, and round-trip multi-modal results.
|
|
17
17
|
</Card>
|
|
18
18
|
<Card title="Tool UI" href="/docs/tools/tool-ui">
|
|
19
19
|
Render tool calls as custom components — loading and result states, human-in-the-loop, approvals, and streaming.
|
|
@@ -23,22 +23,17 @@ Tools are how the model takes action: fetch data, call an API, query a database,
|
|
|
23
23
|
</Card>
|
|
24
24
|
</Cards>
|
|
25
25
|
|
|
26
|
-
##
|
|
26
|
+
## Define tools with `"use generative"`
|
|
27
27
|
|
|
28
28
|
<Callout type="info">
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
**Plain `satisfies Toolkit`** — for tools that already execute elsewhere (an
|
|
34
|
-
MCP server, a separate backend route, a LangGraph node) where you only attach a
|
|
35
|
-
renderer.
|
|
29
|
+
Use `"use generative"` + `defineToolkit` for toolkits. For tools that execute
|
|
30
|
+
elsewhere, spread `defineMcpToolkit({ ... })` for MCP servers or use
|
|
31
|
+
`execute: externalTool()` to attach a renderer to a non-MCP external tool.
|
|
36
32
|
</Callout>
|
|
37
33
|
|
|
38
34
|
In a `"use generative"` file every tool declares an `execute` and the kind is
|
|
39
|
-
**inferred** from it (you never write `type`).
|
|
40
|
-
|
|
41
|
-
[Defining Tools](/docs/tools/defining-tools#two-ways-to-define-a-toolkit).
|
|
35
|
+
**inferred** from it (you never write `type`). See
|
|
36
|
+
[Defining Tools](/docs/tools/defining-tools#define-tools-with-use-generative).
|
|
42
37
|
|
|
43
38
|
## Rendering AI output as UI
|
|
44
39
|
|
|
@@ -15,6 +15,12 @@ client ──► /api/chat ──► MCP client ──► MCP server (HTTP
|
|
|
15
15
|
|
|
16
16
|
The MCP client lives on the server inside your AI SDK route handler. It connects to one or more MCP servers, calls `tools()` to get a tool map, and hands that map to `streamText`. assistant-ui's existing tool-call UI (`ToolFallback`, or toolkit entries with `render`) renders the results.
|
|
17
17
|
|
|
18
|
+
<Callout type="info">
|
|
19
|
+
If you use a `"use generative"` toolkit, spread `defineMcpToolkit({ ... })`
|
|
20
|
+
in the toolkit and use `AISDKToolkit` in your route. It opens the MCP clients,
|
|
21
|
+
merges their tools with your toolkit, and closes them for you.
|
|
22
|
+
</Callout>
|
|
23
|
+
|
|
18
24
|
## Setup
|
|
19
25
|
|
|
20
26
|
<Steps>
|
|
@@ -71,9 +77,64 @@ const mcpClient = await createMCPClient({
|
|
|
71
77
|
</Step>
|
|
72
78
|
<Step>
|
|
73
79
|
|
|
80
|
+
### Define MCP servers in your toolkit
|
|
81
|
+
|
|
82
|
+
In a generative toolkit, spread `defineMcpToolkit({ ... })` with one entry per
|
|
83
|
+
MCP server. The entry key names the server connection; the MCP server publishes
|
|
84
|
+
the actual tool names.
|
|
85
|
+
|
|
86
|
+
```tsx title="app/toolkit.tsx"
|
|
87
|
+
"use generative";
|
|
88
|
+
|
|
89
|
+
import { defineMcpToolkit, defineToolkit } from "@assistant-ui/react";
|
|
90
|
+
|
|
91
|
+
export default defineToolkit({
|
|
92
|
+
...defineMcpToolkit({
|
|
93
|
+
github: {
|
|
94
|
+
type: "http",
|
|
95
|
+
url: "https://mcp.example.com/mcp",
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Use `AISDKToolkit` in the route. It opens the MCP clients, merges their tools
|
|
102
|
+
with the rest of your toolkit, and closes them when you call `close()`:
|
|
103
|
+
|
|
104
|
+
```ts title="app/api/chat/route.ts"
|
|
105
|
+
import { AISDKToolkit } from "@assistant-ui/react-ai-sdk";
|
|
106
|
+
import { openai } from "@ai-sdk/openai";
|
|
107
|
+
import { streamText, convertToModelMessages } from "ai";
|
|
108
|
+
import type { UIMessage } from "ai";
|
|
109
|
+
import toolkit from "../../toolkit";
|
|
110
|
+
|
|
111
|
+
export async function POST(req: Request) {
|
|
112
|
+
const { messages, tools }: { messages: UIMessage[]; tools?: Record<string, any> } =
|
|
113
|
+
await req.json();
|
|
114
|
+
|
|
115
|
+
const aiToolkit = new AISDKToolkit({ toolkit });
|
|
116
|
+
|
|
117
|
+
const result = streamText({
|
|
118
|
+
model: openai("gpt-5.4-mini"),
|
|
119
|
+
messages: await convertToModelMessages(messages),
|
|
120
|
+
tools: await aiToolkit.tools({ frontend: tools }),
|
|
121
|
+
onFinish: async () => {
|
|
122
|
+
await aiToolkit.close();
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return result.toUIMessageStreamResponse();
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
</Step>
|
|
131
|
+
<Step>
|
|
132
|
+
|
|
74
133
|
### Wire the tools into the route
|
|
75
134
|
|
|
76
|
-
`mcpClient.tools()` returns an object shaped
|
|
135
|
+
For manual MCP client control, `mcpClient.tools()` returns an object shaped
|
|
136
|
+
exactly like the `tools` argument of `streamText`. Spread it in alongside any of
|
|
137
|
+
your own tools, and close the client when the response finishes:
|
|
77
138
|
|
|
78
139
|
```ts title="app/api/chat/route.ts"
|
|
79
140
|
import { createMCPClient } from "@ai-sdk/mcp";
|
|
@@ -141,22 +202,28 @@ If two servers expose tools with the same name, the later spread wins. Rename or
|
|
|
141
202
|
|
|
142
203
|
### Render results in the UI
|
|
143
204
|
|
|
144
|
-
Tool calls flow through the existing assistant-ui tool-call rendering. With no
|
|
205
|
+
Tool calls flow through the existing assistant-ui tool-call rendering. With no
|
|
206
|
+
setup, the bundled `<ToolFallback>` component renders the call name, arguments,
|
|
207
|
+
and result. To customize the appearance for a specific tool in a generative
|
|
208
|
+
toolkit, add an `externalTool()` renderer whose key matches the MCP tool name:
|
|
145
209
|
|
|
146
210
|
<PlatformTabs>
|
|
147
211
|
<Tab value="React">
|
|
148
212
|
|
|
149
|
-
```tsx title="app/
|
|
150
|
-
"use
|
|
213
|
+
```tsx title="app/toolkit.tsx"
|
|
214
|
+
"use generative";
|
|
151
215
|
|
|
152
|
-
import
|
|
216
|
+
import { defineMcpToolkit, defineToolkit, externalTool } from "@assistant-ui/react";
|
|
153
217
|
|
|
154
218
|
type Args = { repo: string; number: number };
|
|
155
219
|
type Result = { title: string; state: string; url: string };
|
|
156
220
|
|
|
157
|
-
export
|
|
221
|
+
export default defineToolkit({
|
|
222
|
+
...defineMcpToolkit({
|
|
223
|
+
github: { type: "http", url: "https://mcp.example.com/mcp" },
|
|
224
|
+
}),
|
|
158
225
|
github_get_issue: {
|
|
159
|
-
|
|
226
|
+
execute: externalTool(),
|
|
160
227
|
render: ({ args, result }: { args: Args; result?: Result }) => (
|
|
161
228
|
<div className="rounded border p-3">
|
|
162
229
|
<div className="font-mono text-sm">{args.repo}#{args.number}</div>
|
|
@@ -168,20 +235,20 @@ export const toolkit = {
|
|
|
168
235
|
</div>
|
|
169
236
|
),
|
|
170
237
|
},
|
|
171
|
-
}
|
|
238
|
+
});
|
|
172
239
|
```
|
|
173
240
|
|
|
174
241
|
</Tab>
|
|
175
242
|
<Tab value="React Native">
|
|
176
243
|
|
|
177
244
|
```tsx title="components/GitHubIssueToolUI.tsx"
|
|
178
|
-
import
|
|
245
|
+
import { defineToolkit } from "@assistant-ui/react-native";
|
|
179
246
|
import { Linking, Pressable, Text, View } from "react-native";
|
|
180
247
|
|
|
181
248
|
type Args = { repo: string; number: number };
|
|
182
249
|
type Result = { title: string; state: string; url: string };
|
|
183
250
|
|
|
184
|
-
export const toolkit = {
|
|
251
|
+
export const toolkit = defineToolkit({
|
|
185
252
|
github_get_issue: {
|
|
186
253
|
type: "backend",
|
|
187
254
|
render: ({ args, result }: { args: Args; result?: Result }) => (
|
|
@@ -199,20 +266,20 @@ export const toolkit = {
|
|
|
199
266
|
</View>
|
|
200
267
|
),
|
|
201
268
|
},
|
|
202
|
-
}
|
|
269
|
+
});
|
|
203
270
|
```
|
|
204
271
|
|
|
205
272
|
</Tab>
|
|
206
273
|
<Tab value="React Ink">
|
|
207
274
|
|
|
208
275
|
```tsx title="components/GitHubIssueToolUI.tsx"
|
|
209
|
-
import
|
|
276
|
+
import { defineToolkit } from "@assistant-ui/react-ink";
|
|
210
277
|
import { Box, Text } from "ink";
|
|
211
278
|
|
|
212
279
|
type Args = { repo: string; number: number };
|
|
213
280
|
type Result = { title: string; state: string; url: string };
|
|
214
281
|
|
|
215
|
-
export const toolkit = {
|
|
282
|
+
export const toolkit = defineToolkit({
|
|
216
283
|
github_get_issue: {
|
|
217
284
|
type: "backend",
|
|
218
285
|
render: ({ args, result }: { args: Args; result?: Result }) => (
|
|
@@ -228,13 +295,14 @@ export const toolkit = {
|
|
|
228
295
|
</Box>
|
|
229
296
|
),
|
|
230
297
|
},
|
|
231
|
-
}
|
|
298
|
+
});
|
|
232
299
|
```
|
|
233
300
|
|
|
234
301
|
</Tab>
|
|
235
302
|
</PlatformTabs>
|
|
236
303
|
|
|
237
|
-
Register the toolkit once with `Tools({ toolkit })`.
|
|
304
|
+
Register the toolkit once with `Tools({ toolkit })`. Renderer keys such as
|
|
305
|
+
`github_get_issue` must match the tool names your MCP server publishes.
|
|
238
306
|
|
|
239
307
|
```tsx title="app/components/RuntimeProvider.tsx"
|
|
240
308
|
"use client";
|
|
@@ -25,12 +25,12 @@ Key behaviors:
|
|
|
25
25
|
|
|
26
26
|
```tsx
|
|
27
27
|
import {
|
|
28
|
+
defineToolkit,
|
|
28
29
|
Tools,
|
|
29
|
-
type Toolkit,
|
|
30
30
|
MessagePartPrimitive,
|
|
31
31
|
} from "@assistant-ui/react";
|
|
32
32
|
|
|
33
|
-
const toolkit = {
|
|
33
|
+
const toolkit = defineToolkit({
|
|
34
34
|
invoke_researcher: {
|
|
35
35
|
type: "backend",
|
|
36
36
|
render: ({ args, status }) => (
|
|
@@ -47,7 +47,7 @@ const toolkit = {
|
|
|
47
47
|
</div>
|
|
48
48
|
),
|
|
49
49
|
},
|
|
50
|
-
}
|
|
50
|
+
});
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
</Step>
|
|
@@ -154,7 +154,7 @@ When using LangGraph, subgraph events (`onSubgraphValues` / `onSubgraphUpdates`
|
|
|
154
154
|
If a sub-agent's tool calls also have nested messages, the same pattern applies recursively:
|
|
155
155
|
|
|
156
156
|
```tsx
|
|
157
|
-
const toolkit = {
|
|
157
|
+
const toolkit = defineToolkit({
|
|
158
158
|
invoke_planner: {
|
|
159
159
|
type: "backend",
|
|
160
160
|
render: () => (
|
|
@@ -189,7 +189,7 @@ const toolkit = {
|
|
|
189
189
|
</div>
|
|
190
190
|
),
|
|
191
191
|
},
|
|
192
|
-
}
|
|
192
|
+
});
|
|
193
193
|
```
|
|
194
194
|
|
|
195
195
|
## ReadonlyThreadProvider
|