@assistant-ui/mcp-docs-server 0.1.32 → 0.1.33

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 (112) hide show
  1. package/.docs/organized/code-examples/waterfall.md +15 -17
  2. package/.docs/organized/code-examples/with-a2a.md +19 -19
  3. package/.docs/organized/code-examples/with-ag-ui.md +25 -20
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +6 -4
  5. package/.docs/organized/code-examples/with-artifacts.md +34 -28
  6. package/.docs/organized/code-examples/with-assistant-transport.md +59 -47
  7. package/.docs/organized/code-examples/with-browser-extension.md +17 -5
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +376 -78
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +20 -19
  10. package/.docs/organized/code-examples/with-cloud.md +6 -4
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +9 -7
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +18 -16
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +9 -8
  14. package/.docs/organized/code-examples/with-expo.md +23 -17
  15. package/.docs/organized/code-examples/with-external-store.md +4 -2
  16. package/.docs/organized/code-examples/with-ffmpeg.md +317 -280
  17. package/.docs/organized/code-examples/with-generative-ui.md +1018 -214
  18. package/.docs/organized/code-examples/with-google-adk.md +4 -2
  19. package/.docs/organized/code-examples/with-heat-graph.md +8 -6
  20. package/.docs/organized/code-examples/with-image-generation.md +19 -17
  21. package/.docs/organized/code-examples/with-interactables.md +312 -234
  22. package/.docs/organized/code-examples/with-langchain.md +9 -7
  23. package/.docs/organized/code-examples/with-langgraph.md +82 -78
  24. package/.docs/organized/code-examples/with-livekit.md +19 -17
  25. package/.docs/organized/code-examples/with-mcp.md +13 -7
  26. package/.docs/organized/code-examples/with-opencode.md +61 -61
  27. package/.docs/organized/code-examples/with-react-hook-form.md +14 -12
  28. package/.docs/organized/code-examples/with-react-ink.md +2 -2
  29. package/.docs/organized/code-examples/with-react-router.md +11 -9
  30. package/.docs/organized/code-examples/with-resumable-stream.md +10 -9
  31. package/.docs/organized/code-examples/with-store.md +14 -12
  32. package/.docs/organized/code-examples/with-tanstack.md +8 -9
  33. package/.docs/organized/code-examples/with-tap-runtime.md +21 -19
  34. package/.docs/raw/docs/(docs)/architecture.mdx +43 -2
  35. package/.docs/raw/docs/(docs)/cli.mdx +1 -2
  36. package/.docs/raw/docs/(docs)/copilots/model-context.mdx +34 -26
  37. package/.docs/raw/docs/(docs)/copilots/motivation.mdx +32 -26
  38. package/.docs/raw/docs/(docs)/installation.mdx +1 -1
  39. package/.docs/raw/docs/(reference)/api-reference/adapters/persistence.mdx +5 -1
  40. package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +14 -14
  41. package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +11 -1
  42. package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +18 -0
  43. package/.docs/raw/docs/(reference)/api-reference/generative-ui/rendering.mdx +33 -0
  44. package/.docs/raw/docs/(reference)/api-reference/generative-ui/spec.mdx +45 -0
  45. package/.docs/raw/docs/(reference)/api-reference/hooks/composer-triggers.mdx +41 -41
  46. package/.docs/raw/docs/(reference)/api-reference/hooks/model-context.mdx +19 -1
  47. package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +122 -122
  48. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +32 -2
  49. package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +6 -0
  50. package/.docs/raw/docs/(reference)/api-reference/overview.mdx +4 -6
  51. package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +30 -0
  52. package/.docs/raw/docs/(reference)/api-reference/tools/component-tools.mdx +20 -3
  53. package/.docs/raw/docs/(reference)/api-reference/tools/index.mdx +8 -8
  54. package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +52 -4
  55. package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +6 -7
  56. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +58 -46
  57. package/.docs/raw/docs/cloud/ai-sdk.mdx +0 -2
  58. package/.docs/raw/docs/guides/chain-of-thought.mdx +7 -9
  59. package/.docs/raw/docs/guides/context-api.mdx +2 -1
  60. package/.docs/raw/docs/guides/index.mdx +3 -12
  61. package/.docs/raw/docs/guides/mentions.mdx +4 -4
  62. package/.docs/raw/docs/guides/slash-commands.mdx +1 -1
  63. package/.docs/raw/docs/guides/suggestions.mdx +1 -1
  64. package/.docs/raw/docs/ink/hooks.mdx +98 -85
  65. package/.docs/raw/docs/ink/migration.mdx +1 -1
  66. package/.docs/raw/docs/ink/primitives.mdx +2 -2
  67. package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +1 -1
  68. package/.docs/raw/docs/integrations/index.mdx +2 -2
  69. package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +17 -2
  70. package/.docs/raw/docs/migrations/toolkit-tools.mdx +226 -0
  71. package/.docs/raw/docs/primitives/chain-of-thought.mdx +10 -16
  72. package/.docs/raw/docs/primitives/message.mdx +9 -10
  73. package/.docs/raw/docs/react-native/hooks.mdx +57 -82
  74. package/.docs/raw/docs/react-native/migration.mdx +1 -1
  75. package/.docs/raw/docs/react-native/primitives.mdx +2 -2
  76. package/.docs/raw/docs/runtimes/a2a/client-and-hooks.mdx +3 -0
  77. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +122 -1
  78. package/.docs/raw/docs/runtimes/concepts/threads.mdx +7 -1
  79. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +18 -7
  80. package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +108 -38
  81. package/.docs/raw/docs/runtimes/langgraph/generative-ui.mdx +2 -2
  82. package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +1 -1
  83. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +64 -50
  84. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +98 -86
  85. package/.docs/raw/docs/tools/backend.mdx +136 -0
  86. package/.docs/raw/docs/tools/defining-tools.mdx +413 -0
  87. package/.docs/raw/docs/tools/dynamic-tools.mdx +110 -0
  88. package/.docs/raw/docs/tools/generative-ui.mdx +214 -0
  89. package/.docs/raw/docs/tools/index.mdx +76 -0
  90. package/.docs/raw/docs/{guides → tools}/interactables.mdx +1 -1
  91. package/.docs/raw/docs/{integrations/tools → tools}/mcp.mdx +77 -50
  92. package/.docs/raw/docs/{guides → tools}/multi-agent.mdx +17 -19
  93. package/.docs/raw/docs/tools/tool-ui.mdx +967 -0
  94. package/.docs/raw/docs/{integrations/tools/react-mcp.mdx → tools/user-managed-mcp.mdx} +3 -3
  95. package/.docs/raw/docs/ui/directive-text.mdx +3 -3
  96. package/.docs/raw/docs/ui/mcp-config.mdx +4 -4
  97. package/.docs/raw/docs/ui/part-grouping.mdx +84 -50
  98. package/.docs/raw/docs/ui/reasoning.mdx +4 -5
  99. package/.docs/raw/docs/ui/tool-group.mdx +5 -6
  100. package/.docs/raw/docs/utilities/heat-graph.mdx +1 -1
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/prepare-docs/code-examples.d.ts.map +1 -1
  103. package/dist/prepare-docs/copy-raw.d.ts.map +1 -1
  104. package/dist/utils/paths.d.ts.map +1 -1
  105. package/package.json +3 -3
  106. package/.docs/organized/code-examples/with-parent-id-grouping.md +0 -596
  107. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool-ui.mdx +0 -151
  108. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool.mdx +0 -230
  109. package/.docs/raw/docs/guides/generative-ui.mdx +0 -142
  110. package/.docs/raw/docs/guides/tool-ui.mdx +0 -858
  111. package/.docs/raw/docs/guides/tools.mdx +0 -736
  112. /package/.docs/raw/docs/{guides → tools}/mcp-apps.mdx +0 -0
@@ -1,736 +0,0 @@
1
- ---
2
- title: Tool Calling
3
- description: Add API calls, database queries, and custom actions to your AI chat with assistant-ui's tool calling. Real-time visualization, type safety, and streaming.
4
- platforms: ["react"]
5
- ---
6
-
7
- Tools enable LLMs to take actions and interact with external systems. assistant-ui provides a comprehensive toolkit for creating, managing, and visualizing tool interactions in real-time.
8
-
9
- ## Overview
10
-
11
- Tools in assistant-ui are functions that the LLM can call to perform specific tasks. They bridge the gap between the LLM's reasoning capabilities and real-world actions like:
12
-
13
- - Fetching data from APIs
14
- - Performing calculations
15
- - Interacting with databases
16
- - Controlling UI elements
17
- - Executing workflows
18
-
19
- When tools are executed, you can display custom generative UI components that provide rich, interactive visualizations of the tool's execution and results. Learn more in the [Generative UI guide](/docs/guides/tool-ui).
20
-
21
- <Callout type="tip">
22
- If you haven't provided a custom UI for a tool, assistant-ui offers a
23
- [`ToolFallback`](/docs/ui/tool-fallback) component that you can add to your
24
- codebase to render a default UI for tool executions. You can customize this by
25
- creating your own Tool UI component for the tool's name.
26
- </Callout>
27
-
28
- ## Tools() API
29
-
30
- The `Tools()` API is the recommended starting point for registering tools in assistant-ui. It provides centralized tool registration that prevents duplicate registrations and works seamlessly with all runtimes. For tools whose availability depends on a specific part of your UI being mounted, see the [component-based APIs](#component-based-apis) below; both styles are supported and can be mixed in the same app.
31
-
32
- ### Quick Start
33
-
34
- Create a toolkit object containing all your tools, then register it using `useAui()`:
35
-
36
- ```tsx
37
- import { useAui, Tools, type Toolkit } from "@assistant-ui/react";
38
- import { z } from "zod";
39
-
40
- // Define your toolkit
41
- const myToolkit: Toolkit = {
42
- getWeather: {
43
- description: "Get current weather for a location",
44
- parameters: z.object({
45
- location: z.string().describe("City name or zip code"),
46
- unit: z.enum(["celsius", "fahrenheit"]).default("celsius"),
47
- }),
48
- execute: async ({ location, unit }) => {
49
- const weather = await fetchWeatherAPI(location, unit);
50
- return weather;
51
- },
52
- render: ({ args, result }) => {
53
- if (!result) return <div>Fetching weather for {args.location}...</div>;
54
- return (
55
- <div className="weather-card">
56
- <h3>{args.location}</h3>
57
- <p>{result.temperature}° {args.unit}</p>
58
- <p>{result.conditions}</p>
59
- </div>
60
- );
61
- },
62
- },
63
- // Add more tools here
64
- };
65
-
66
- // Register tools in your runtime provider
67
- function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
68
- const runtime = useChatRuntime();
69
-
70
- // Register all tools
71
- const aui = useAui({
72
- tools: Tools({ toolkit: myToolkit }),
73
- });
74
-
75
- return (
76
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
77
- {children}
78
- </AssistantRuntimeProvider>
79
- );
80
- }
81
- ```
82
-
83
- ### Benefits
84
-
85
- - **No Duplicate Registrations**: Tools are registered once, preventing the "tool already exists" error
86
- - **Centralized Definition**: All your tools in one place, easier to manage and test
87
- - **Type-Safe**: Full TypeScript support with proper type inference
88
- - **Flexible**: Works with all runtimes (AI SDK, LangGraph, custom, etc.)
89
- - **Composable**: Easily split toolkits across files and merge them
90
-
91
- ### Tool Definition
92
-
93
- Each tool in the toolkit is a `ToolDefinition` object with these properties:
94
-
95
- ```tsx
96
- type ToolDefinition =
97
- | {
98
- // Frontend tool: executes in the browser
99
- type?: "frontend";
100
- description?: string;
101
- parameters: StandardSchemaV1 | JSONSchema7; // e.g. a Zod schema
102
- execute: (args, context) => Promise<any>;
103
- toModelOutput?: (opts) => ToolModelContentPart[]; // see "Multi-modal tool results"
104
- render?: (props) => React.ReactNode;
105
- }
106
- | {
107
- // Human tool: pauses for user input (render is required)
108
- type: "human";
109
- description?: string;
110
- parameters: StandardSchemaV1 | JSONSchema7;
111
- render: (props) => React.ReactNode;
112
- }
113
- | {
114
- // Backend tool: execution happens server-side (no execute/parameters needed)
115
- type: "backend";
116
- render?: (props) => React.ReactNode;
117
- };
118
- ```
119
-
120
- ### Multi-modal Tool Results
121
-
122
- By default, the value returned from `execute` is sent to the model as a single JSON blob. That is fine for most tools, but it does not work for tools whose useful output is a file or image: a "read PDF" tool, an OCR tool, a chart-rendering tool, etc.
123
-
124
- `toModelOutput` is an optional callback that maps the developer-facing `execute` result into the multi-modal content the model actually sees. Your `render` function still receives the rich, typed `result`; the model receives the projection.
125
-
126
- ```tsx
127
- import { tool } from "@assistant-ui/react";
128
- import { convertUint8ArrayToBase64 } from "@ai-sdk/provider-utils";
129
- import { z } from "zod";
130
-
131
- const readPdfTool = tool({
132
- description: "Fetch a PDF from a URL and return it",
133
- parameters: z.object({ url: z.string().url() }),
134
- execute: async ({ url }) => {
135
- const res = await fetch(url);
136
- const buf = new Uint8Array(await res.arrayBuffer());
137
- const base64 = convertUint8ArrayToBase64(buf);
138
- return { mediaType: "application/pdf", base64, byteLength: buf.byteLength };
139
- },
140
- toModelOutput: ({ output }) => [
141
- { type: "text", text: "PDF contents:" },
142
- {
143
- type: "file",
144
- data: output.base64,
145
- mediaType: output.mediaType,
146
- },
147
- ],
148
- });
149
- ```
150
-
151
- `ToolModelContentPart` is a union of `{ type: "text"; text }` and `{ type: "file"; data; mediaType; filename? }`. Use `mediaType` (e.g. `image/png`, `application/pdf`) to tell the model how to interpret the bytes.
152
-
153
- When using the AI SDK runtime, frontend tool results round-trip through the AI SDK chat protocol back to your route handler on the next turn. For `toModelOutput` to fire on those round-tripped results, your route handler must also pass the tool registry to `convertToModelMessages`. This is the [same pattern AI SDK documents](https://ai-sdk.dev/docs/reference/ai-sdk-ui/convert-to-model-messages#multi-modal-tool-responses) for any multi-modal tool response:
154
-
155
- ```ts
156
- import { frontendTools } from "@assistant-ui/react-ai-sdk";
157
- import { convertToModelMessages, streamText } from "ai";
158
-
159
- const aiSDKTools = { ...frontendTools(tools ?? {}) };
160
-
161
- const result = streamText({
162
- model,
163
- // Pass tools to both calls. convertToModelMessages reads `toModelOutput`
164
- // from `tools[toolName]` to project prior tool results.
165
- messages: await convertToModelMessages(messages, { tools: aiSDKTools }),
166
- tools: aiSDKTools,
167
- });
168
- ```
169
-
170
- If you skip the `{ tools: aiSDKTools }` argument, prior tool results will be sent to the model as a plain JSON blob (the AI SDK default), and your `toModelOutput` will be silently ignored. Tools that do not declare `toModelOutput` are unaffected either way.
171
-
172
- <Callout type="warn">
173
- **Reserved property name.** When `toModelOutput` is set, the runtime wraps the AI SDK chat output as `{ __aui_modelContent: ToolModelContentPart[], value: <your result> }` before persisting. Do not return objects whose top-level key is literally `__aui_modelContent` from any tool's `execute`, or it will be misread as the envelope. The prefix is namespaced for this reason; any other property name is fine.
174
- </Callout>
175
-
176
- <Callout type="warn">
177
- **Read/write compatibility for persisted threads.** The `__aui_modelContent` envelope is recognized by `@assistant-ui/react-ai-sdk` from this version onward. If you persist UI messages (thread history adapter, cloud, etc.) and read them from multiple environments, upgrade every reader before any writer starts producing `toModelOutput`. Older readers will treat the entire envelope as the `result`, which breaks tool `render` functions for those messages.
178
- </Callout>
179
-
180
- ### Organizing Large Toolkits
181
-
182
- For larger applications, split tools across multiple files:
183
-
184
- ```tsx
185
- // lib/tools/weather.tsx
186
- export const weatherTools: Toolkit = {
187
- getWeather: { /* ... */ },
188
- getWeatherForecast: { /* ... */ },
189
- };
190
-
191
- // lib/tools/database.tsx
192
- export const databaseTools: Toolkit = {
193
- queryData: { /* ... */ },
194
- insertData: { /* ... */ },
195
- };
196
-
197
- // lib/toolkit.tsx
198
- import { weatherTools } from "./tools/weather";
199
- import { databaseTools } from "./tools/database";
200
-
201
- export const appToolkit: Toolkit = {
202
- ...weatherTools,
203
- ...databaseTools,
204
- };
205
-
206
- // App.tsx
207
- import { appToolkit } from "./lib/toolkit";
208
-
209
- function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
210
- const runtime = useChatRuntime();
211
-
212
- const aui = useAui({
213
- tools: Tools({ toolkit: appToolkit }),
214
- });
215
-
216
- return (
217
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
218
- {children}
219
- </AssistantRuntimeProvider>
220
- );
221
- }
222
- ```
223
-
224
- ### UI-Only Tools
225
-
226
- For tools where execution happens elsewhere (e.g., backend MCP tools), omit the `execute` function:
227
-
228
- ```tsx
229
- const uiOnlyToolkit: Toolkit = {
230
- webSearch: {
231
- description: "Search the web",
232
- parameters: z.object({
233
- query: z.string(),
234
- }),
235
- // No execute - handled by backend
236
- render: ({ args, result }) => {
237
- return (
238
- <div>
239
- <h3>Search: {args.query}</h3>
240
- {result?.results.map((item) => (
241
- <div key={item.id}>
242
- <a href={item.url}>{item.title}</a>
243
- </div>
244
- ))}
245
- </div>
246
- );
247
- },
248
- },
249
- };
250
- ```
251
-
252
- ### Tool Execution Context
253
-
254
- Tools receive additional context during execution:
255
-
256
- ```tsx
257
- execute: async (args, context) => {
258
- // context.abortSignal - AbortSignal for cancellation
259
- // context.toolCallId - Unique identifier for this invocation
260
- // context.human - Function to request human input
261
-
262
- // Example: Respect cancellation
263
- const response = await fetch(url, { signal: context.abortSignal });
264
-
265
- // Example: Request user confirmation
266
- const userResponse = await context.human({
267
- message: "Are you sure?",
268
- });
269
- };
270
- ```
271
-
272
- ### Cancellation
273
-
274
- `context.abortSignal` is an `AbortSignal` that fires when the user stops the run. Pass it to any async I/O so the work stops immediately:
275
-
276
- ```tsx
277
- execute: async ({ query }, { abortSignal }) => {
278
- const res = await fetch(`/api/search?q=${query}`, { signal: abortSignal });
279
- return res.json();
280
- },
281
- ```
282
-
283
- When using LangGraph with `unstable_createLangGraphStream`, the default `onDisconnect` value is already `"cancel"`, which tells the LangGraph server to cancel the run on abort:
284
-
285
- ```ts
286
- import { unstable_createLangGraphStream } from "@assistant-ui/react-langgraph";
287
-
288
- const stream = unstable_createLangGraphStream({
289
- client,
290
- assistantId,
291
- // onDisconnect defaults to "cancel"; the server cancels the run when the
292
- // client disconnects or the user stops the message.
293
- });
294
- ```
295
-
296
- See the [LangGraph quickstart](/docs/runtimes/langgraph/quickstart) for full setup.
297
-
298
- ### Human-in-the-Loop
299
-
300
- Tools can pause execution to request user input or approval:
301
-
302
- ```tsx
303
- const confirmationToolkit: Toolkit = {
304
- sendEmail: {
305
- description: "Send an email with confirmation",
306
- parameters: z.object({
307
- to: z.string(),
308
- subject: z.string(),
309
- body: z.string(),
310
- }),
311
- execute: async ({ to, subject, body }, { human }) => {
312
- // Request user confirmation before sending
313
- const confirmed = await human({
314
- type: "confirmation",
315
- action: "send-email",
316
- details: { to, subject },
317
- });
318
-
319
- if (!confirmed) {
320
- return { status: "cancelled" };
321
- }
322
-
323
- await sendEmail({ to, subject, body });
324
- return { status: "sent" };
325
- },
326
- render: ({ args, result, interrupt, resume }) => {
327
- // Show confirmation dialog when waiting for user input
328
- if (interrupt) {
329
- return (
330
- <div>
331
- <h3>Confirm Email</h3>
332
- <p>Send to: {interrupt.payload.details.to}</p>
333
- <p>Subject: {interrupt.payload.details.subject}</p>
334
- <button onClick={() => resume(true)}>Confirm</button>
335
- <button onClick={() => resume(false)}>Cancel</button>
336
- </div>
337
- );
338
- }
339
-
340
- // Show result
341
- if (result) {
342
- return <div>Status: {result.status}</div>;
343
- }
344
-
345
- return <div>Preparing email...</div>;
346
- },
347
- },
348
- };
349
- ```
350
-
351
- ### Streaming Tool Args
352
-
353
- While a tool is running, its arguments arrive as partial JSON. Use `useToolArgsStatus` inside a tool UI render function to react to each top-level field as it streams in. The hook is exported from `@assistant-ui/react`.
354
-
355
- ```tsx
356
- import { useToolArgsStatus } from "@assistant-ui/react";
357
-
358
- const SearchToolUI = makeAssistantToolUI<{ query: string; limit: number }, unknown>({
359
- toolName: "search",
360
- render: ({ args }) => {
361
- const { propStatus } = useToolArgsStatus<{ query: string; limit: number }>();
362
-
363
- return (
364
- <div>
365
- <span className={propStatus.query === "streaming" ? "animate-pulse" : ""}>
366
- {args.query ?? "..."}
367
- </span>
368
- {propStatus.limit === "complete" && <span> (limit: {args.limit})</span>}
369
- </div>
370
- );
371
- },
372
- });
373
- ```
374
-
375
- `propStatus` maps each top-level key in the args object to `"streaming"` while it is still being parsed and to `"complete"` once that field is fully present.
376
-
377
- ## Component-Based APIs
378
-
379
- `makeAssistantTool`, `useAssistantTool`, and `makeAssistantToolUI` are component-and-hook-based APIs that coexist with the [`Tools()`](#tools-api) toolkit pattern. They are fully supported and the natural fit for the [intelligent components](/docs/copilots/motivation) pattern, where each part of your UI registers the tools it owns when it is mounted, for example a product-specific tool that should only be exposed while that product's screen is open.
380
-
381
- <Callout type="info">
382
- Be careful not to register the same tool from both APIs at once: each API
383
- registers under `toolName`, and duplicate registrations will be rejected.
384
- </Callout>
385
-
386
- <Callout type="warn">
387
- Tool **execution** can be registered dynamically (when a component mounts),
388
- but tool **UI** should generally be pre-registered. A `render` function that
389
- is only registered while a specific component is mounted will not render
390
- when chat history is replayed or during server-side rendering. Either
391
- declare the tool's `render` in a `Tools()` toolkit, or mount
392
- `makeAssistantToolUI` near the root of your tree.
393
- </Callout>
394
-
395
- ### Using `makeAssistantTool`
396
-
397
- Register tools with the assistant context. Returns a React component that registers the tool when rendered:
398
-
399
- ```tsx
400
- import { makeAssistantTool, tool } from "@assistant-ui/react";
401
- import { z } from "zod";
402
-
403
- const weatherTool = tool({
404
- description: "Get current weather for a location",
405
- parameters: z.object({
406
- location: z.string(),
407
- }),
408
- execute: async ({ location }) => {
409
- const weather = await fetchWeatherAPI(location);
410
- return weather;
411
- },
412
- });
413
-
414
- const WeatherTool = makeAssistantTool({
415
- ...weatherTool,
416
- toolName: "getWeather",
417
- });
418
-
419
- // Place inside AssistantRuntimeProvider
420
- function App() {
421
- return (
422
- <AssistantRuntimeProvider runtime={runtime}>
423
- <WeatherTool />
424
- <Thread />
425
- </AssistantRuntimeProvider>
426
- );
427
- }
428
- ```
429
-
430
- Tradeoff: component-based registration is tied to React lifecycle, so the tool is registered when the component mounts and unregistered when it unmounts. Take care not to remount it accidentally if you also register the same tool elsewhere.
431
-
432
- ### Using the `useAssistantTool` Hook
433
-
434
- Register tools dynamically using React hooks:
435
-
436
- ```tsx
437
- import { useAssistantTool } from "@assistant-ui/react";
438
- import { z } from "zod";
439
-
440
- function DynamicTools() {
441
- useAssistantTool({
442
- toolName: "searchData",
443
- description: "Search through the data",
444
- parameters: z.object({
445
- query: z.string(),
446
- }),
447
- execute: async ({ query }) => {
448
- return await searchDatabase(query);
449
- },
450
- });
451
-
452
- return null;
453
- }
454
- ```
455
-
456
- Tradeoff: like `makeAssistantTool`, the registration follows the component lifecycle. Useful for dynamic tools that depend on component state or props.
457
-
458
- ### Using `makeAssistantToolUI`
459
-
460
- Create UI-only components for tools defined elsewhere:
461
-
462
- ```tsx
463
- import { makeAssistantToolUI } from "@assistant-ui/react";
464
-
465
- const SearchResultsUI = makeAssistantToolUI<
466
- { query: string },
467
- { results: Array<any> }
468
- >({
469
- toolName: "webSearch",
470
- render: ({ args, result }) => {
471
- return (
472
- <div>
473
- <h3>Search: {args.query}</h3>
474
- {result.results.map((item) => (
475
- <div key={item.id}>{item.title}</div>
476
- ))}
477
- </div>
478
- );
479
- },
480
- });
481
-
482
- function App() {
483
- return (
484
- <AssistantRuntimeProvider runtime={runtime}>
485
- <SearchResultsUI />
486
- <Thread />
487
- </AssistantRuntimeProvider>
488
- );
489
- }
490
- ```
491
-
492
- Tradeoff: like the other component-based APIs, the UI is registered while the component is mounted. Useful when the tool UI needs access to surrounding component state or context.
493
-
494
- ## Tool Paradigms
495
-
496
- ### Frontend Tools
497
-
498
- Tools that execute in the browser:
499
-
500
- ```tsx
501
- const frontendToolkit: Toolkit = {
502
- screenshot: {
503
- description: "Capture a screenshot of the current page",
504
- parameters: z.object({
505
- selector: z.string().optional(),
506
- }),
507
- execute: async ({ selector }) => {
508
- const element = selector ? document.querySelector(selector) : document.body;
509
- const screenshot = await captureElement(element);
510
- return { dataUrl: screenshot };
511
- },
512
- },
513
- };
514
- ```
515
-
516
- ### Backend Tools
517
-
518
- Tools executed server-side live in your API route. A minimal example with the AI SDK:
519
-
520
- ```ts title="@/app/api/chat/route.ts"
521
- import { openai } from "@ai-sdk/openai";
522
- import { streamText, convertToModelMessages, tool, zodSchema } from "ai";
523
- import { z } from "zod";
524
-
525
- export async function POST(req: Request) {
526
- const { messages } = await req.json();
527
- const result = streamText({
528
- model: openai("gpt-5.4-nano"),
529
- messages: await convertToModelMessages(messages),
530
- tools: {
531
- queryDatabase: tool({
532
- description: "Query the application database",
533
- inputSchema: zodSchema(z.object({ query: z.string(), table: z.string() })),
534
- execute: async ({ query, table }) => db.query(query, { table }),
535
- }),
536
- },
537
- });
538
- return result.toUIMessageStreamResponse();
539
- }
540
- ```
541
-
542
- For the full AI SDK v6 backend setup including multi-step tool calls, frontend tools, history persistence with `withFormat`, and more, see the [AI SDK v6 guide](/docs/runtimes/ai-sdk/v6).
543
-
544
- ### Client-Defined Tools with frontendTools
545
-
546
- The Vercel AI SDK adapter implements automatic serialization of client-defined tools. Tools registered via the `Tools()` API are automatically included in API requests:
547
-
548
- ```tsx
549
- // Frontend: Define tools with Tools() API
550
- const clientToolkit: Toolkit = {
551
- calculate: {
552
- description: "Perform calculations",
553
- parameters: z.object({
554
- expression: z.string(),
555
- }),
556
- execute: async ({ expression }) => {
557
- return eval(expression); // Use proper parser in production
558
- },
559
- },
560
- };
561
-
562
- function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
563
- const runtime = useChatRuntime();
564
-
565
- const aui = useAui({
566
- tools: Tools({ toolkit: clientToolkit }),
567
- });
568
-
569
- return (
570
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
571
- {children}
572
- </AssistantRuntimeProvider>
573
- );
574
- }
575
-
576
- // Backend: Use frontendTools to receive client tools
577
- import { frontendTools } from "@assistant-ui/react-ai-sdk";
578
-
579
- export async function POST(req: Request) {
580
- const { messages, tools } = await req.json();
581
-
582
- const result = streamText({
583
- model: openai("gpt-5.4-nano"),
584
- messages: await convertToModelMessages(messages),
585
- tools: {
586
- ...frontendTools(tools), // Client-defined tools
587
- // Additional server-side tools
588
- queryDatabase: {
589
- description: "Query the database",
590
- inputSchema: zodSchema(z.object({ query: z.string() })),
591
- execute: async ({ query }) => {
592
- return await db.query(query);
593
- },
594
- },
595
- },
596
- });
597
-
598
- return result.toUIMessageStreamResponse();
599
- }
600
- ```
601
-
602
- ### MCP (Model Context Protocol) Tools
603
-
604
- Integration with MCP servers using AI SDK's experimental MCP support:
605
-
606
- ```tsx
607
- import { experimental_createMCPClient, streamText } from "ai";
608
- import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
609
-
610
- export async function POST(req: Request) {
611
- const client = await experimental_createMCPClient({
612
- transport: new StdioClientTransport({
613
- command: "npx",
614
- args: ["@modelcontextprotocol/server-github"],
615
- }),
616
- });
617
-
618
- try {
619
- const tools = await client.tools();
620
-
621
- const result = streamText({
622
- model: openai("gpt-5.4-nano"),
623
- tools,
624
- messages: await convertToModelMessages(messages),
625
- });
626
-
627
- return result.toUIMessageStreamResponse();
628
- } finally {
629
- await client.close();
630
- }
631
- }
632
- ```
633
-
634
- ## LangGraph subgraph events
635
-
636
- When a LangGraph graph contains sub-agents (nested subgraphs), events from those subgraphs arrive with a `metadata.namespace` field identifying the originating subgraph. Pass event handlers to `useLangGraphRuntime` (or `useLangGraphMessages`) to react to them:
637
-
638
- ```ts
639
- const runtime = useLangGraphRuntime({
640
- stream,
641
- eventHandlers: {
642
- onSubgraphValues: (namespace, values) => {
643
- console.log("subgraph", namespace, "state:", values);
644
- },
645
- onSubgraphUpdates: (namespace, updates) => {
646
- console.log("subgraph", namespace, "updates:", updates);
647
- },
648
- onSubgraphError: (namespace, error) => {
649
- console.error("subgraph", namespace, "error:", error);
650
- },
651
- },
652
- });
653
- ```
654
-
655
- `namespace` is a pipe-separated string like `"parent|child_agent"`. Messages emitted by a subgraph include `metadata.namespace` so you can attribute tool results to the correct sub-agent.
656
-
657
- ## `useLangChainState`
658
-
659
- When using `@assistant-ui/react-langchain` (`useStreamRuntime`), the `useLangChainState` hook lets you read any key from the current LangChain/LangGraph state on the client without a separate API call:
660
-
661
- ```tsx
662
- import { useLangChainState } from "@assistant-ui/react-langchain";
663
-
664
- function TodoSidebar() {
665
- const todos = useLangChainState<string[]>("todos", []);
666
- return <ul>{todos.map((t) => <li key={t}>{t}</li>)}</ul>;
667
- }
668
- ```
669
-
670
- The second argument is an optional default value. The hook re-renders whenever the state key changes during a stream.
671
-
672
- ## Best Practices
673
-
674
- 1. **Pick one registration style per tool**: avoid registering the same tool through both the `Tools()` toolkit and a component-based API; both routes will register, and duplicates are rejected
675
- 2. **Centralize Definitions**: Keep all tools in a toolkit file for easy management
676
- 3. **Clear Descriptions**: Write descriptive tool descriptions that help the LLM understand when to use each tool
677
- 4. **Parameter Validation**: Use Zod schemas to ensure type safety
678
- 5. **Error Handling**: Handle errors gracefully with user-friendly messages
679
- 6. **Loading States**: Provide visual feedback during tool execution
680
- 7. **Security**: Validate permissions and sanitize inputs
681
- 8. **Performance**: Use abort signals for cancellable operations
682
- 9. **Testing**: Test tools in isolation and with the full assistant flow
683
-
684
- ## Switching from Component-Based to Toolkit
685
-
686
- If you prefer the toolkit shape, switching is mechanical:
687
-
688
- 1. **Create a toolkit object** with all your tools
689
- 2. **Move tool definitions** from `makeAssistantTool`/`useAssistantTool` calls into the toolkit
690
- 3. **Register once** using `useAui({ tools: Tools({ toolkit }) })` in your runtime provider
691
- 4. **Remove component registrations** (`<WeatherTool />`, etc.)
692
- 5. **Test** to ensure all tools work as expected
693
-
694
- Example migration:
695
-
696
- ```tsx
697
- // Component-based API
698
- const WeatherTool = makeAssistantTool({
699
- toolName: "getWeather",
700
- description: "Get weather",
701
- parameters: z.object({ location: z.string() }),
702
- execute: async ({ location }) => { /* ... */ },
703
- });
704
-
705
- function App() {
706
- return (
707
- <AssistantRuntimeProvider runtime={runtime}>
708
- <WeatherTool />
709
- <Thread />
710
- </AssistantRuntimeProvider>
711
- );
712
- }
713
-
714
- // Toolkit API
715
- const toolkit: Toolkit = {
716
- getWeather: {
717
- description: "Get weather",
718
- parameters: z.object({ location: z.string() }),
719
- execute: async ({ location }) => { /* ... */ },
720
- },
721
- };
722
-
723
- function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
724
- const runtime = useChatRuntime();
725
-
726
- const aui = useAui({
727
- tools: Tools({ toolkit }),
728
- });
729
-
730
- return (
731
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
732
- {children}
733
- </AssistantRuntimeProvider>
734
- );
735
- }
736
- ```