@assistant-ui/mcp-docs-server 0.1.33 → 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.
Files changed (81) hide show
  1. package/.docs/organized/code-examples/waterfall.md +5 -5
  2. package/.docs/organized/code-examples/with-a2a.md +5 -5
  3. package/.docs/organized/code-examples/with-ag-ui.md +9 -9
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +7 -7
  5. package/.docs/organized/code-examples/with-artifacts.md +37 -31
  6. package/.docs/organized/code-examples/with-assistant-transport.md +8 -8
  7. package/.docs/organized/code-examples/with-browser-extension.md +5 -5
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +68 -47
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +7 -7
  10. package/.docs/organized/code-examples/with-cloud.md +7 -7
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +7 -7
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +8 -8
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +8 -8
  14. package/.docs/organized/code-examples/with-expo.md +33 -24
  15. package/.docs/organized/code-examples/with-external-store.md +5 -5
  16. package/.docs/organized/code-examples/with-ffmpeg.md +10 -10
  17. package/.docs/organized/code-examples/with-generative-ui.md +70 -64
  18. package/.docs/organized/code-examples/with-google-adk.md +6 -6
  19. package/.docs/organized/code-examples/with-heat-graph.md +5 -5
  20. package/.docs/organized/code-examples/with-image-generation.md +7 -7
  21. package/.docs/organized/code-examples/with-interactables.md +7 -7
  22. package/.docs/organized/code-examples/with-langchain.md +7 -7
  23. package/.docs/organized/code-examples/with-langgraph.md +30 -26
  24. package/.docs/organized/code-examples/with-livekit.md +8 -8
  25. package/.docs/organized/code-examples/with-mcp.md +8 -8
  26. package/.docs/organized/code-examples/with-opencode.md +6 -6
  27. package/.docs/organized/code-examples/with-react-hook-form.md +7 -7
  28. package/.docs/organized/code-examples/with-react-ink.md +295 -100
  29. package/.docs/organized/code-examples/with-react-router.md +11 -11
  30. package/.docs/organized/code-examples/with-resumable-stream.md +8 -8
  31. package/.docs/organized/code-examples/with-store.md +64 -64
  32. package/.docs/organized/code-examples/with-tanstack.md +8 -8
  33. package/.docs/organized/code-examples/with-tap-runtime.md +5 -5
  34. package/.docs/raw/docs/(docs)/architecture.mdx +52 -41
  35. package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +1 -1
  36. package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +3 -0
  37. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +14 -3
  38. package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +94 -3
  39. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +5 -69
  40. package/.docs/raw/docs/ink/adapters.mdx +23 -1
  41. package/.docs/raw/docs/ink/hooks.mdx +20 -17
  42. package/.docs/raw/docs/migrations/toolkit-tools.mdx +14 -8
  43. package/.docs/raw/docs/react-native/hooks.mdx +25 -17
  44. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +41 -0
  45. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +3 -3
  46. package/.docs/raw/docs/runtimes/concepts/architecture.mdx +46 -4
  47. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +54 -12
  48. package/.docs/raw/docs/runtimes/custom/external-store.mdx +62 -4
  49. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +12 -0
  50. package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +9 -9
  51. package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +13 -0
  52. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +5 -5
  53. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +3 -3
  54. package/.docs/raw/docs/tools/backend.mdx +19 -11
  55. package/.docs/raw/docs/tools/defining-tools.mdx +177 -52
  56. package/.docs/raw/docs/tools/index.mdx +7 -12
  57. package/.docs/raw/docs/tools/mcp.mdx +83 -15
  58. package/.docs/raw/docs/tools/multi-agent.mdx +5 -5
  59. package/.docs/raw/docs/tools/tool-ui.mdx +27 -27
  60. package/.docs/raw/docs/tools/user-managed-mcp.mdx +4 -4
  61. package/.docs/raw/docs/ui/mermaid.mdx +16 -9
  62. package/.docs/raw/docs/ui/part-grouping.mdx +2 -2
  63. package/.docs/raw/docs/ui/tool-fallback.mdx +16 -0
  64. package/.docs/raw/docs/utilities/react-o11y.mdx +2 -2
  65. package/dist/constants.js.map +1 -1
  66. package/dist/index.js.map +1 -1
  67. package/dist/prepare-docs/code-examples.js.map +1 -1
  68. package/dist/prepare-docs/copy-raw.js.map +1 -1
  69. package/dist/prepare-docs/prepare.js.map +1 -1
  70. package/dist/stdio.js.map +1 -1
  71. package/dist/tools/docs.js.map +1 -1
  72. package/dist/tools/examples.js.map +1 -1
  73. package/dist/tools/tests/test-setup.js.map +1 -1
  74. package/dist/utils/mdx.js.map +1 -1
  75. package/dist/utils/paths.js.map +1 -1
  76. package/package.json +4 -4
  77. /package/.docs/raw/docs/{(docs)/copilots → copilots}/assistant-frame.mdx +0 -0
  78. /package/.docs/raw/docs/{(docs)/copilots → copilots}/make-assistant-visible.mdx +0 -0
  79. /package/.docs/raw/docs/{(docs)/copilots → copilots}/model-context.mdx +0 -0
  80. /package/.docs/raw/docs/{(docs)/copilots → copilots}/motivation.mdx +0 -0
  81. /package/.docs/raw/docs/{(docs)/copilots → copilots}/use-assistant-instructions.mdx +0 -0
@@ -94,14 +94,14 @@ This simply displays the tool name and arguments passed to it, but not the resul
94
94
 
95
95
  import { Thread } from "@/components/assistant-ui/thread";
96
96
  import { PriceSnapshotToolUI } from "@/components/tools/price-snapshot/PriceSnapshotTool";
97
- import { AuiProvider, Tools, type Toolkit, useAui } from "@assistant-ui/react";
97
+ import { AuiProvider, defineToolkit, Tools, useAui } from "@assistant-ui/react";
98
98
 
99
- const toolkit = {
99
+ const toolkit = defineToolkit({
100
100
  price_snapshot: {
101
101
  type: "backend",
102
102
  render: PriceSnapshotToolUI,
103
103
  },
104
- } satisfies Toolkit;
104
+ });
105
105
 
106
106
  export default function Home() {
107
107
  const aui = useAui({ tools: Tools({ toolkit }) });
@@ -326,12 +326,12 @@ export const ToolFallback: ToolCallMessagePartComponent = ({
326
326
  ### Bind fallback UI
327
327
 
328
328
  ```tsx title="@/app/page.tsx"
329
- const toolkit = {
329
+ const toolkit = defineToolkit({
330
330
  price_snapshot: {
331
331
  type: "backend",
332
332
  render: PriceSnapshotToolUI,
333
333
  },
334
- } satisfies Toolkit;
334
+ });
335
335
 
336
336
  export default function Home() {
337
337
  const aui = useAui({ tools: Tools({ toolkit }) });
@@ -212,9 +212,9 @@ export function TransactionConfirmationPending(props: TransactionConfirmation) {
212
212
  import { Thread } from "@/components/assistant-ui/thread";
213
213
  import { PriceSnapshotToolUI } from "@/components/tools/price-snapshot/PriceSnapshotTool";
214
214
  import { PurchaseStockToolUI } from "@/components/tools/purchase-stock/PurchaseStockTool";
215
- import { AuiProvider, Tools, type Toolkit, useAui } from "@assistant-ui/react";
215
+ import { AuiProvider, defineToolkit, Tools, useAui } from "@assistant-ui/react";
216
216
 
217
- const toolkit = {
217
+ const toolkit = defineToolkit({
218
218
  price_snapshot: {
219
219
  type: "backend",
220
220
  render: PriceSnapshotToolUI,
@@ -223,7 +223,7 @@ const toolkit = {
223
223
  type: "backend",
224
224
  render: PurchaseStockToolUI,
225
225
  },
226
- } satisfies Toolkit;
226
+ });
227
227
 
228
228
  export default function Home() {
229
229
  const aui = useAui({ tools: Tools({ toolkit }) });
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Backend Tools
3
- description: Wire assistant-ui toolkits into your server with the AI SDK — generativeTools, frontendTools, mixing client and server tools, and multi-modal results.
3
+ description: Wire assistant-ui toolkits into your server with the AI SDK — AISDKToolkit, frontendTools, mixing client and server tools, and multi-modal results.
4
4
  platforms: ["react"]
5
5
  ---
6
6
 
@@ -24,16 +24,18 @@ const {
24
24
  } = await req.json();
25
25
  ```
26
26
 
27
- ## Generative toolkits: `generativeTools`
27
+ ## Generative toolkits: `AISDKToolkit`
28
28
 
29
- When you author tools in a [`"use generative"` file](/docs/tools/defining-tools#quick-start-use-generative), the same import resolves to the **server build** inside a route handler — schema plus any backend `execute`, with renderers stripped. Pass it to `generativeTools` together with the uploaded `tools`:
29
+ When you author tools in a [`"use generative"` file](/docs/tools/defining-tools#quick-start-use-generative), the same import resolves to the **server build** inside a route handler — schema plus any backend `execute`, with renderers stripped. Wrap it in an `AISDKToolkit` and call `.tools()` with the uploaded `tools`:
30
30
 
31
31
  ```ts title="app/api/chat/route.ts"
32
- import { generativeTools } from "@assistant-ui/react-ai-sdk";
32
+ import { AISDKToolkit } from "@assistant-ui/react-ai-sdk";
33
33
  import { streamText, convertToModelMessages, type UIMessage } from "ai";
34
34
  import { openai } from "@ai-sdk/openai";
35
35
  import toolkit from "../../toolkit";
36
36
 
37
+ const aiToolkit = new AISDKToolkit({ toolkit });
38
+
37
39
  export async function POST(req: Request) {
38
40
  const { messages, system, tools } = await req.json();
39
41
 
@@ -41,24 +43,29 @@ export async function POST(req: Request) {
41
43
  model: openai("gpt-5.4-nano"),
42
44
  system,
43
45
  messages: await convertToModelMessages(messages),
44
- tools: generativeTools({ toolkit, frontendTools: tools }),
46
+ tools: await aiToolkit.tools({ frontend: tools }),
45
47
  });
46
48
 
47
49
  return result.toUIMessageStreamResponse();
48
50
  }
49
51
  ```
50
52
 
51
- `generativeTools` registers every toolkit tool with the model using its schema, wires the backend `execute` where the server build carries one, and merges in the uploaded `frontendTools`. A server `execute` wins over an uploaded entry of the same name. Frontend and human tools (no server `execute`) are exposed schema-only and left for the client and the user to fulfill.
53
+ `AISDKToolkit.tools()` registers every toolkit tool with the model using its schema, wires the backend `execute` where the server build carries one, and merges in the uploaded frontend tools. A server `execute` wins over an uploaded entry of the same name. Frontend and human tools (no server `execute`) are exposed schema-only and left for the client and the user to fulfill.
52
54
 
53
55
  <Callout type="info">
54
- If your toolkit spreads in MCP server tools (`defineMcpToolkit`), use
55
- `new AISDKToolkit({ toolkit }).tools({ frontend })` instead of
56
- `generativeTools` — it also opens the MCP connections. See [MCP](/docs/tools/mcp).
56
+ If your toolkit spreads in MCP server tools (`defineMcpToolkit`), `.tools()`
57
+ also opens those connections. A module-scope `aiToolkit` pools them across
58
+ requests; see [MCP](/docs/tools/mcp) for the connection lifecycle and when to
59
+ call `aiToolkit.close()`. The older
60
+ `generativeTools({ toolkit, frontendTools })` is deprecated, MCP-less, and
61
+ superseded by `AISDKToolkit`.
57
62
  </Callout>
58
63
 
59
64
  ## Client-defined tools: `frontendTools`
60
65
 
61
- If you don't use the generative compiler — for example a plain [`satisfies Toolkit`](/docs/tools/defining-tools#render-only-tools-for-externally-executed-tools) with browser-executed tools — the AI SDK adapter still serializes those tools into the request `tools`. Convert them to the AI SDK shape with `frontendTools` and spread your own server tools alongside:
66
+ If a toolkit cannot go through the generative compiler, the AI SDK adapter still
67
+ serializes browser-executed tools into the request `tools`. Convert them to the
68
+ AI SDK shape with `frontendTools` and spread your own server tools alongside:
62
69
 
63
70
  ```ts title="app/api/chat/route.ts"
64
71
  import { frontendTools } from "@assistant-ui/react-ai-sdk";
@@ -86,7 +93,8 @@ export async function POST(req: Request) {
86
93
  }
87
94
  ```
88
95
 
89
- `generativeTools` calls `frontendTools` for you under the hood; reach for `frontendTools` directly when you're not on the generative build.
96
+ `AISDKToolkit.tools()` calls `frontendTools` for you under the hood; reach for
97
+ `frontendTools` directly when you're not on the generative build.
90
98
 
91
99
  <Callout type="tip">
92
100
  `toToolsJSONSchema` emits the uploaded tools in alphabetical order, so two
@@ -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