@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.
Files changed (102) hide show
  1. package/.docs/organized/code-examples/waterfall.md +7 -7
  2. package/.docs/organized/code-examples/with-a2a.md +8 -8
  3. package/.docs/organized/code-examples/with-ag-ui.md +12 -12
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +10 -10
  5. package/.docs/organized/code-examples/with-artifacts.md +40 -34
  6. package/.docs/organized/code-examples/with-assistant-transport.md +11 -11
  7. package/.docs/organized/code-examples/with-browser-extension.md +9 -9
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +72 -51
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +10 -10
  10. package/.docs/organized/code-examples/with-cloud.md +10 -10
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +10 -10
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +12 -12
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +12 -12
  14. package/.docs/organized/code-examples/with-expo.md +66 -31
  15. package/.docs/organized/code-examples/with-external-store.md +8 -8
  16. package/.docs/organized/code-examples/with-ffmpeg.md +13 -13
  17. package/.docs/organized/code-examples/with-generative-ui.md +98 -368
  18. package/.docs/organized/code-examples/with-google-adk.md +9 -9
  19. package/.docs/organized/code-examples/with-heat-graph.md +7 -7
  20. package/.docs/organized/code-examples/with-image-generation.md +10 -10
  21. package/.docs/organized/code-examples/with-interactables.md +10 -10
  22. package/.docs/organized/code-examples/with-langchain.md +10 -10
  23. package/.docs/organized/code-examples/with-langgraph.md +33 -29
  24. package/.docs/organized/code-examples/with-livekit.md +12 -12
  25. package/.docs/organized/code-examples/with-mcp.md +11 -11
  26. package/.docs/organized/code-examples/with-opencode.md +109 -583
  27. package/.docs/organized/code-examples/with-pi.md +2044 -0
  28. package/.docs/organized/code-examples/with-react-hook-form.md +11 -11
  29. package/.docs/organized/code-examples/with-react-ink-web.md +691 -0
  30. package/.docs/organized/code-examples/with-react-ink.md +309 -102
  31. package/.docs/organized/code-examples/with-react-router.md +14 -14
  32. package/.docs/organized/code-examples/with-resumable-stream.md +11 -11
  33. package/.docs/organized/code-examples/with-store.md +70 -66
  34. package/.docs/organized/code-examples/with-tanstack.md +25 -11
  35. package/.docs/organized/code-examples/with-tap-runtime.md +8 -8
  36. package/.docs/organized/code-examples/with-virtualized-thread.md +656 -0
  37. package/.docs/raw/docs/(docs)/architecture.mdx +65 -53
  38. package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +1 -1
  39. package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +3 -0
  40. package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +44 -1
  41. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +14 -3
  42. package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +1 -1
  43. package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +2 -23
  44. package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +22 -0
  45. package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +93 -9
  46. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +21 -86
  47. package/.docs/raw/docs/guides/chain-of-thought.mdx +1 -1
  48. package/.docs/raw/docs/guides/index.mdx +3 -0
  49. package/.docs/raw/docs/guides/input-history.mdx +55 -0
  50. package/.docs/raw/docs/guides/virtualization.mdx +63 -0
  51. package/.docs/raw/docs/ink/adapters.mdx +23 -1
  52. package/.docs/raw/docs/ink/hooks.mdx +22 -19
  53. package/.docs/raw/docs/migrations/toolkit-tools.mdx +14 -8
  54. package/.docs/raw/docs/react-native/hooks.mdx +26 -18
  55. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +39 -0
  56. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +3 -3
  57. package/.docs/raw/docs/runtimes/concepts/architecture.mdx +46 -4
  58. package/.docs/raw/docs/runtimes/concepts/stability.mdx +1 -1
  59. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +54 -12
  60. package/.docs/raw/docs/runtimes/custom/data-stream.mdx +1 -1
  61. package/.docs/raw/docs/runtimes/custom/external-store.mdx +62 -4
  62. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +120 -4
  63. package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +9 -9
  64. package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +13 -0
  65. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +5 -5
  66. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +3 -3
  67. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +6 -6
  68. package/.docs/raw/docs/tools/backend.mdx +19 -11
  69. package/.docs/raw/docs/tools/defining-tools.mdx +177 -52
  70. package/.docs/raw/docs/tools/index.mdx +7 -12
  71. package/.docs/raw/docs/tools/mcp.mdx +83 -15
  72. package/.docs/raw/docs/tools/multi-agent.mdx +5 -5
  73. package/.docs/raw/docs/tools/tool-ui.mdx +64 -28
  74. package/.docs/raw/docs/tools/user-managed-mcp.mdx +4 -4
  75. package/.docs/raw/docs/ui/dot-matrix.mdx +133 -0
  76. package/.docs/raw/docs/ui/mermaid.mdx +16 -9
  77. package/.docs/raw/docs/ui/model-selector.mdx +219 -52
  78. package/.docs/raw/docs/ui/number-roll.mdx +154 -0
  79. package/.docs/raw/docs/ui/part-grouping.mdx +2 -2
  80. package/.docs/raw/docs/ui/reasoning.mdx +3 -3
  81. package/.docs/raw/docs/ui/streamdown.mdx +2 -0
  82. package/.docs/raw/docs/ui/syntax-highlighting.mdx +1 -1
  83. package/.docs/raw/docs/ui/thread.mdx +52 -0
  84. package/.docs/raw/docs/ui/tool-fallback.mdx +16 -0
  85. package/.docs/raw/docs/utilities/react-o11y.mdx +2 -2
  86. package/dist/constants.js.map +1 -1
  87. package/dist/index.js.map +1 -1
  88. package/dist/prepare-docs/code-examples.js.map +1 -1
  89. package/dist/prepare-docs/copy-raw.js.map +1 -1
  90. package/dist/prepare-docs/prepare.js.map +1 -1
  91. package/dist/stdio.js.map +1 -1
  92. package/dist/tools/docs.js.map +1 -1
  93. package/dist/tools/examples.js.map +1 -1
  94. package/dist/tools/tests/test-setup.js.map +1 -1
  95. package/dist/utils/mdx.js.map +1 -1
  96. package/dist/utils/paths.js.map +1 -1
  97. package/package.json +4 -4
  98. /package/.docs/raw/docs/{(docs)/copilots → copilots}/assistant-frame.mdx +0 -0
  99. /package/.docs/raw/docs/{(docs)/copilots → copilots}/make-assistant-visible.mdx +0 -0
  100. /package/.docs/raw/docs/{(docs)/copilots → copilots}/model-context.mdx +0 -0
  101. /package/.docs/raw/docs/{(docs)/copilots → copilots}/motivation.mdx +0 -0
  102. /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
- ## Two ways to define a toolkit
11
+ ## Define tools with `"use generative"`
12
12
 
13
- There are two authoring models. Most apps use the first; the second is for attaching UI to tools that execute somewhere you don't control.
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
- **Use `"use generative"` + `defineToolkit`** when you author the tool's
17
- behavior yourself (it runs in the browser or in your own backend code). The
18
- compiler co-locates the schema, the executor, and the renderer in one file and
19
- splits them across the client/server boundary for you.
20
-
21
- **Use a plain `satisfies Toolkit` object** when the tool already executes
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
- The difference matters because of one rule: **in a `"use generative"` file every tool must declare an `execute`, and you never write `type` yourself** — the compiler infers it. A render-only `{ type: "backend", render }` entry (no `execute`) is a plain-toolkit construct and will **not** compile inside a `"use generative"` file.
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. Pass it to `generativeTools` so the model is configured with every tool's schema:
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 { generativeTools } from "@assistant-ui/react-ai-sdk";
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: generativeTools({ toolkit, frontendTools: 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
- | `hitlTool()` | **human** | schema only | schema + `render` |
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: hitlTool()` and a `render` that calls `addResult` exactly once:
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: hitlTool(),
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
- `hitlTool` is imported from `@assistant-ui/react`. See [Tool UI → Human-in-the-loop](/docs/tools/tool-ui#user-input-collection) for the full pattern.
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
- ### Dynamic (stateful) tools
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
- When a tool's executor must close over React state, declare its contract with `execute: stubTool()` and supply the real executor at runtime with `useAuiToolOverrides`. See [Dynamic tools](/docs/tools/dynamic-tools).
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
- When the tool already runs somewhere you don't control — an MCP server, a separate backend route, a LangGraph node — there is no `execute` to author. Declare a **plain** toolkit (a `"use client"` file, no `"use generative"`, no `defineToolkit`) with an explicit `type: "backend"` and only a `render`:
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 type { Toolkit } from "@assistant-ui/react";
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
- } satisfies Toolkit;
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**. Putting it
295
- inside a `"use generative"` file is a compile error — a generative tool must
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 { weatherTools } from "./tools/weather";
319
- import { databaseTools } from "./tools/database";
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
- Only spreads the compiler can see through are allowed — a local `defineToolkit(...)` / `defineMcpToolkit(...)` binding, or an inline `...defineMcpToolkit({ … })`. Spreading an opaque import is rejected, because the compiler can't verify a backend `execute` won't leak to the client.
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
- Spread `defineMcpToolkit` to expose tools from an MCP server alongside your own:
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 `generativeTools` / `frontendTools`, mix client and server tools, and round-trip multi-modal results.
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
- ## Two ways to define a toolkit
26
+ ## Define tools with `"use generative"`
27
27
 
28
28
  <Callout type="info">
29
- **`"use generative"` + `defineToolkit`** — for tools you author yourself
30
- (browser or your own backend). The compiler co-locates the schema, executor,
31
- and renderer in one file and splits them across the client/server boundary.
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`). In a plain toolkit you author
40
- `type` and write render-only `{ type: "backend", render }` entries. See
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 exactly like the `tools` argument of `streamText`. Spread it in alongside any of your own tools, and close the client when the response finishes:
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 setup, the bundled `<ToolFallback>` component renders the call name, arguments, and result. To customize the appearance for a specific tool, add a backend entry to your toolkit:
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/components/GitHubIssueToolUI.tsx"
150
- "use client";
213
+ ```tsx title="app/toolkit.tsx"
214
+ "use generative";
151
215
 
152
- import type { Toolkit } from "@assistant-ui/react";
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 const toolkit = {
221
+ export default defineToolkit({
222
+ ...defineMcpToolkit({
223
+ github: { type: "http", url: "https://mcp.example.com/mcp" },
224
+ }),
158
225
  github_get_issue: {
159
- type: "backend",
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
- } satisfies Toolkit;
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 type { Toolkit } from "@assistant-ui/react-native";
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
- } satisfies Toolkit;
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 type { Toolkit } from "@assistant-ui/react-ink";
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
- } satisfies Toolkit;
298
+ });
232
299
  ```
233
300
 
234
301
  </Tab>
235
302
  </PlatformTabs>
236
303
 
237
- Register the toolkit once with `Tools({ toolkit })`. The toolkit key must match the name your MCP server publishes.
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
- } satisfies Toolkit;
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
- } satisfies Toolkit;
192
+ });
193
193
  ```
194
194
 
195
195
  ## ReadonlyThreadProvider