@assistant-ui/mcp-docs-server 0.1.19 → 0.1.21

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 (108) hide show
  1. package/.docs/organized/code-examples/with-ag-ui.md +172 -1633
  2. package/.docs/organized/code-examples/with-ai-sdk-v6.md +42 -1640
  3. package/.docs/organized/code-examples/with-assistant-transport.md +40 -1743
  4. package/.docs/organized/code-examples/with-cloud.md +71 -1745
  5. package/.docs/organized/code-examples/with-custom-thread-list.md +87 -1723
  6. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +70 -1637
  7. package/.docs/organized/code-examples/with-external-store.md +67 -1624
  8. package/.docs/organized/code-examples/with-ffmpeg.md +71 -1629
  9. package/.docs/organized/code-examples/with-langgraph.md +95 -1893
  10. package/.docs/organized/code-examples/with-parent-id-grouping.md +57 -1654
  11. package/.docs/organized/code-examples/with-react-hook-form.md +220 -2163
  12. package/.docs/organized/code-examples/with-react-router.md +66 -1318
  13. package/.docs/organized/code-examples/with-store.md +31 -31
  14. package/.docs/organized/code-examples/with-tanstack.md +77 -861
  15. package/.docs/organized/code-examples/with-tap-runtime.md +812 -0
  16. package/.docs/raw/docs/(docs)/cli.mdx +66 -0
  17. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool-ui.mdx +0 -1
  18. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool.mdx +0 -1
  19. package/.docs/raw/docs/(docs)/copilots/model-context.mdx +4 -4
  20. package/.docs/raw/docs/(docs)/copilots/motivation.mdx +3 -3
  21. package/.docs/raw/docs/(docs)/devtools.mdx +0 -1
  22. package/.docs/raw/docs/(docs)/guides/attachments.mdx +2 -3
  23. package/.docs/raw/docs/(docs)/guides/context-api.mdx +117 -117
  24. package/.docs/raw/docs/(docs)/guides/suggestions.mdx +296 -0
  25. package/.docs/raw/docs/(docs)/guides/tools.mdx +336 -513
  26. package/.docs/raw/docs/(docs)/index.mdx +33 -410
  27. package/.docs/raw/docs/(docs)/installation.mdx +450 -0
  28. package/.docs/raw/docs/(docs)/llm.mdx +209 -0
  29. package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +0 -1
  30. package/.docs/raw/docs/(reference)/api-reference/context-providers/text-message-part-provider.mdx +0 -1
  31. package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +48 -3
  32. package/.docs/raw/docs/(reference)/api-reference/integrations/react-hook-form.mdx +0 -1
  33. package/.docs/raw/docs/(reference)/api-reference/integrations/vercel-ai-sdk.mdx +0 -1
  34. package/.docs/raw/docs/(reference)/api-reference/overview.mdx +9 -3
  35. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar-more.mdx +20 -52
  36. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar.mdx +16 -39
  37. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +49 -50
  38. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-modal.mdx +3 -11
  39. package/.docs/raw/docs/(reference)/api-reference/primitives/attachment.mdx +0 -3
  40. package/.docs/raw/docs/(reference)/api-reference/primitives/branch-picker.mdx +0 -1
  41. package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +5 -16
  42. package/.docs/raw/docs/(reference)/api-reference/primitives/composition.mdx +0 -1
  43. package/.docs/raw/docs/(reference)/api-reference/primitives/error.mdx +0 -1
  44. package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +1 -2
  45. package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +0 -1
  46. package/.docs/raw/docs/(reference)/api-reference/primitives/suggestion.mdx +152 -0
  47. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item-more.mdx +0 -1
  48. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item.mdx +1 -2
  49. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list.mdx +1 -2
  50. package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +28 -40
  51. package/.docs/raw/docs/(reference)/api-reference/runtimes/assistant-runtime.mdx +0 -1
  52. package/.docs/raw/docs/(reference)/api-reference/runtimes/attachment-runtime.mdx +1 -2
  53. package/.docs/raw/docs/(reference)/api-reference/runtimes/composer-runtime.mdx +2 -3
  54. package/.docs/raw/docs/(reference)/api-reference/runtimes/message-part-runtime.mdx +1 -2
  55. package/.docs/raw/docs/(reference)/api-reference/runtimes/message-runtime.mdx +1 -2
  56. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-item-runtime.mdx +0 -1
  57. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +0 -1
  58. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-runtime.mdx +1 -2
  59. package/.docs/raw/docs/(reference)/legacy/styled/assistant-modal.mdx +0 -1
  60. package/.docs/raw/docs/(reference)/legacy/styled/decomposition.mdx +5 -5
  61. package/.docs/raw/docs/(reference)/legacy/styled/markdown.mdx +0 -1
  62. package/.docs/raw/docs/(reference)/legacy/styled/thread.mdx +0 -1
  63. package/.docs/raw/docs/(reference)/migrations/v0-12.mdx +207 -33
  64. package/.docs/raw/docs/(reference)/react-compatibility.mdx +0 -1
  65. package/.docs/raw/docs/cloud/persistence/ai-sdk.mdx +0 -1
  66. package/.docs/raw/docs/cloud/persistence/langgraph.mdx +0 -1
  67. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +0 -1
  68. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +118 -0
  69. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +198 -0
  70. package/.docs/raw/docs/runtimes/assistant-transport.mdx +3 -3
  71. package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +5 -6
  72. package/.docs/raw/docs/runtimes/custom/external-store.mdx +9 -11
  73. package/.docs/raw/docs/runtimes/custom/local.mdx +43 -36
  74. package/.docs/raw/docs/runtimes/data-stream.mdx +35 -3
  75. package/.docs/raw/docs/runtimes/langgraph/index.mdx +1 -2
  76. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +0 -1
  77. package/.docs/raw/docs/runtimes/langserve.mdx +0 -1
  78. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +0 -1
  79. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +0 -1
  80. package/.docs/raw/docs/ui/accordion.mdx +259 -0
  81. package/.docs/raw/docs/ui/assistant-modal.mdx +1 -3
  82. package/.docs/raw/docs/ui/assistant-sidebar.mdx +1 -3
  83. package/.docs/raw/docs/ui/attachment.mdx +0 -2
  84. package/.docs/raw/docs/ui/badge.mdx +138 -0
  85. package/.docs/raw/docs/ui/diff-viewer.mdx +279 -0
  86. package/.docs/raw/docs/ui/file.mdx +152 -0
  87. package/.docs/raw/docs/ui/image.mdx +100 -0
  88. package/.docs/raw/docs/ui/markdown.mdx +0 -1
  89. package/.docs/raw/docs/ui/mermaid.mdx +0 -1
  90. package/.docs/raw/docs/ui/model-selector.mdx +224 -0
  91. package/.docs/raw/docs/ui/part-grouping.mdx +4 -5
  92. package/.docs/raw/docs/ui/reasoning.mdx +6 -5
  93. package/.docs/raw/docs/ui/scrollbar.mdx +26 -9
  94. package/.docs/raw/docs/ui/select.mdx +245 -0
  95. package/.docs/raw/docs/ui/sources.mdx +6 -5
  96. package/.docs/raw/docs/ui/streamdown.mdx +348 -0
  97. package/.docs/raw/docs/ui/syntax-highlighting.mdx +8 -63
  98. package/.docs/raw/docs/ui/tabs.mdx +259 -0
  99. package/.docs/raw/docs/ui/thread-list.mdx +98 -16
  100. package/.docs/raw/docs/ui/thread.mdx +57 -73
  101. package/.docs/raw/docs/ui/tool-fallback.mdx +0 -1
  102. package/.docs/raw/docs/ui/tool-group.mdx +1 -3
  103. package/README.md +3 -3
  104. package/package.json +4 -4
  105. package/src/tools/tests/examples.test.ts +1 -1
  106. package/.docs/raw/docs/(docs)/about-assistantui.mdx +0 -54
  107. package/.docs/raw/docs/(docs)/mcp-docs-server.mdx +0 -321
  108. package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +0 -219
@@ -24,16 +24,236 @@ When tools are executed, you can display custom generative UI components that pr
24
24
  creating your own Tool UI component for the tool's name.
25
25
  </Callout>
26
26
 
27
- ## Tool Creation Methods
27
+ ## Recommended: Tools() API
28
28
 
29
- assistant-ui offers multiple ways to create and register tools, each suited for different use cases:
29
+ The `Tools()` API is the recommended way to register tools in assistant-ui. It provides centralized tool registration that prevents duplicate registrations and works seamlessly with all runtimes.
30
30
 
31
- - **`makeAssistantTool`**: Register client-defined tools with the assistant context
32
- - **`useAssistantTool`**: Hook-based dynamic tool registration
33
- - **`makeAssistantToolUI`**: UI-only components for existing tools
34
- - **Direct context registration**: Advanced registration with full model context control
31
+ ### Quick Start
35
32
 
36
- ### 1. Using `makeAssistantTool`
33
+ Create a toolkit object containing all your tools, then register it using `useAui()`:
34
+
35
+ ```tsx
36
+ import { useAui, Tools, type Toolkit } from "@assistant-ui/react";
37
+ import { z } from "zod";
38
+
39
+ // Define your toolkit
40
+ const myToolkit: Toolkit = {
41
+ getWeather: {
42
+ description: "Get current weather for a location",
43
+ parameters: z.object({
44
+ location: z.string().describe("City name or zip code"),
45
+ unit: z.enum(["celsius", "fahrenheit"]).default("celsius"),
46
+ }),
47
+ execute: async ({ location, unit }) => {
48
+ const weather = await fetchWeatherAPI(location, unit);
49
+ return weather;
50
+ },
51
+ render: ({ args, result }) => {
52
+ if (!result) return <div>Fetching weather for {args.location}...</div>;
53
+ return (
54
+ <div className="weather-card">
55
+ <h3>{args.location}</h3>
56
+ <p>{result.temperature}° {args.unit}</p>
57
+ <p>{result.conditions}</p>
58
+ </div>
59
+ );
60
+ },
61
+ },
62
+ // Add more tools here
63
+ };
64
+
65
+ // Register tools in your runtime provider
66
+ function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
67
+ const runtime = useChatRuntime();
68
+
69
+ // Register all tools
70
+ const aui = useAui({
71
+ tools: Tools({ toolkit: myToolkit }),
72
+ });
73
+
74
+ return (
75
+ <AssistantRuntimeProvider aui={aui} runtime={runtime}>
76
+ {children}
77
+ </AssistantRuntimeProvider>
78
+ );
79
+ }
80
+ ```
81
+
82
+ ### Benefits
83
+
84
+ - **No Duplicate Registrations**: Tools are registered once, preventing the "tool already exists" error
85
+ - **Centralized Definition**: All your tools in one place, easier to manage and test
86
+ - **Type-Safe**: Full TypeScript support with proper type inference
87
+ - **Flexible**: Works with all runtimes (AI SDK, LangGraph, custom, etc.)
88
+ - **Composable**: Easily split toolkits across files and merge them
89
+
90
+ ### Tool Definition
91
+
92
+ Each tool in the toolkit is a `ToolDefinition` object with these properties:
93
+
94
+ ```tsx
95
+ type ToolDefinition = {
96
+ description: string;
97
+ parameters: z.ZodType; // Zod schema for parameters
98
+ execute: (args, context) => Promise<any>;
99
+ render?: (props) => React.ReactNode; // Optional UI component
100
+ };
101
+ ```
102
+
103
+ ### Organizing Large Toolkits
104
+
105
+ For larger applications, split tools across multiple files:
106
+
107
+ ```tsx
108
+ // lib/tools/weather.tsx
109
+ export const weatherTools: Toolkit = {
110
+ getWeather: { /* ... */ },
111
+ getWeatherForecast: { /* ... */ },
112
+ };
113
+
114
+ // lib/tools/database.tsx
115
+ export const databaseTools: Toolkit = {
116
+ queryData: { /* ... */ },
117
+ insertData: { /* ... */ },
118
+ };
119
+
120
+ // lib/toolkit.tsx
121
+ import { weatherTools } from "./tools/weather";
122
+ import { databaseTools } from "./tools/database";
123
+
124
+ export const appToolkit: Toolkit = {
125
+ ...weatherTools,
126
+ ...databaseTools,
127
+ };
128
+
129
+ // App.tsx
130
+ import { appToolkit } from "./lib/toolkit";
131
+
132
+ function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
133
+ const runtime = useChatRuntime();
134
+
135
+ const aui = useAui({
136
+ tools: Tools({ toolkit: appToolkit }),
137
+ });
138
+
139
+ return (
140
+ <AssistantRuntimeProvider aui={aui} runtime={runtime}>
141
+ {children}
142
+ </AssistantRuntimeProvider>
143
+ );
144
+ }
145
+ ```
146
+
147
+ ### UI-Only Tools
148
+
149
+ For tools where execution happens elsewhere (e.g., backend MCP tools), omit the `execute` function:
150
+
151
+ ```tsx
152
+ const uiOnlyToolkit: Toolkit = {
153
+ webSearch: {
154
+ description: "Search the web",
155
+ parameters: z.object({
156
+ query: z.string(),
157
+ }),
158
+ // No execute - handled by backend
159
+ render: ({ args, result }) => {
160
+ return (
161
+ <div>
162
+ <h3>Search: {args.query}</h3>
163
+ {result?.results.map((item) => (
164
+ <div key={item.id}>
165
+ <a href={item.url}>{item.title}</a>
166
+ </div>
167
+ ))}
168
+ </div>
169
+ );
170
+ },
171
+ },
172
+ };
173
+ ```
174
+
175
+ ### Tool Execution Context
176
+
177
+ Tools receive additional context during execution:
178
+
179
+ ```tsx
180
+ execute: async (args, context) => {
181
+ // context.abortSignal - AbortSignal for cancellation
182
+ // context.toolCallId - Unique identifier for this invocation
183
+ // context.human - Function to request human input
184
+
185
+ // Example: Respect cancellation
186
+ const response = await fetch(url, { signal: context.abortSignal });
187
+
188
+ // Example: Request user confirmation
189
+ const userResponse = await context.human({
190
+ message: "Are you sure?",
191
+ });
192
+ };
193
+ ```
194
+
195
+ ### Human-in-the-Loop
196
+
197
+ Tools can pause execution to request user input or approval:
198
+
199
+ ```tsx
200
+ const confirmationToolkit: Toolkit = {
201
+ sendEmail: {
202
+ description: "Send an email with confirmation",
203
+ parameters: z.object({
204
+ to: z.string(),
205
+ subject: z.string(),
206
+ body: z.string(),
207
+ }),
208
+ execute: async ({ to, subject, body }, { human }) => {
209
+ // Request user confirmation before sending
210
+ const confirmed = await human({
211
+ type: "confirmation",
212
+ action: "send-email",
213
+ details: { to, subject },
214
+ });
215
+
216
+ if (!confirmed) {
217
+ return { status: "cancelled" };
218
+ }
219
+
220
+ await sendEmail({ to, subject, body });
221
+ return { status: "sent" };
222
+ },
223
+ render: ({ args, result, interrupt, resume }) => {
224
+ // Show confirmation dialog when waiting for user input
225
+ if (interrupt) {
226
+ return (
227
+ <div>
228
+ <h3>Confirm Email</h3>
229
+ <p>Send to: {interrupt.payload.details.to}</p>
230
+ <p>Subject: {interrupt.payload.details.subject}</p>
231
+ <button onClick={() => resume(true)}>Confirm</button>
232
+ <button onClick={() => resume(false)}>Cancel</button>
233
+ </div>
234
+ );
235
+ }
236
+
237
+ // Show result
238
+ if (result) {
239
+ return <div>Status: {result.status}</div>;
240
+ }
241
+
242
+ return <div>Preparing email...</div>;
243
+ },
244
+ },
245
+ };
246
+ ```
247
+
248
+ ## Alternative Methods (Legacy)
249
+
250
+ <Callout type="warning">
251
+ The following methods are supported for backwards compatibility but are not
252
+ recommended for new code. They can cause duplicate registration errors and are
253
+ harder to maintain. Use the `Tools()` API instead.
254
+ </Callout>
255
+
256
+ ### Using `makeAssistantTool` (Deprecated)
37
257
 
38
258
  Register tools with the assistant context. Returns a React component that registers the tool when rendered:
39
259
 
@@ -41,27 +261,23 @@ Register tools with the assistant context. Returns a React component that regist
41
261
  import { makeAssistantTool, tool } from "@assistant-ui/react";
42
262
  import { z } from "zod";
43
263
 
44
- // Define the tool
45
264
  const weatherTool = tool({
46
265
  description: "Get current weather for a location",
47
266
  parameters: z.object({
48
- location: z.string().describe("City name or zip code"),
49
- unit: z.enum(["celsius", "fahrenheit"]).default("celsius"),
267
+ location: z.string(),
50
268
  }),
51
- execute: async ({ location, unit }) => {
52
- // Tool execution logic
53
- const weather = await fetchWeatherAPI(location, unit);
269
+ execute: async ({ location }) => {
270
+ const weather = await fetchWeatherAPI(location);
54
271
  return weather;
55
272
  },
56
273
  });
57
274
 
58
- // Create the component
59
275
  const WeatherTool = makeAssistantTool({
60
276
  ...weatherTool,
61
277
  toolName: "getWeather",
62
278
  });
63
279
 
64
- // Place the tool component inside AssistantRuntimeProvider
280
+ // Place inside AssistantRuntimeProvider
65
281
  function App() {
66
282
  return (
67
283
  <AssistantRuntimeProvider runtime={runtime}>
@@ -72,88 +288,58 @@ function App() {
72
288
  }
73
289
  ```
74
290
 
75
- <Callout type="tip">
76
- When using server-side runtimes like Vercel AI SDK, you can pass
77
- client-defined tools to your backend using `frontendTools`. See the
78
- [Client-Defined Tools with
79
- frontendTools](#client-defined-tools-with-frontendtools) section below.
80
- </Callout>
291
+ **Why this is deprecated**: Component-based registration can lead to duplicate registrations if components are remounted or if the same tool is defined in multiple places.
81
292
 
82
- ### 2. Using `useAssistantTool` Hook
293
+ ### Using `useAssistantTool` Hook (Deprecated)
83
294
 
84
- Register tools dynamically using React hooks. Useful for conditional tools or when tool availability depends on component state:
295
+ Register tools dynamically using React hooks:
85
296
 
86
297
  ```tsx
87
298
  import { useAssistantTool } from "@assistant-ui/react";
88
299
  import { z } from "zod";
89
300
 
90
301
  function DynamicTools() {
91
- const [dataSource, setDataSource] = useState<"local" | "cloud">("local");
92
-
93
302
  useAssistantTool({
94
303
  toolName: "searchData",
95
- description: "Search through the selected data source",
304
+ description: "Search through the data",
96
305
  parameters: z.object({
97
306
  query: z.string(),
98
307
  }),
99
308
  execute: async ({ query }) => {
100
- if (dataSource === "local") {
101
- return await searchLocalDatabase(query);
102
- } else {
103
- return await searchCloudDatabase(query);
104
- }
309
+ return await searchDatabase(query);
105
310
  },
106
- // Re-register when data source changes
107
- enabled: true,
108
311
  });
109
312
 
110
313
  return null;
111
314
  }
112
315
  ```
113
316
 
114
- ### 3. Using `makeAssistantToolUI`
317
+ **Why this is deprecated**: Hook-based registration ties tool definitions to component lifecycle, making them harder to test and potentially causing duplicate registrations.
115
318
 
116
- Create generative UI components for tools that are defined elsewhere. This is UI-only - the tool's execution logic must be registered separately (e.g., in your backend, MCP server, or another component):
319
+ ### Using `makeAssistantToolUI` (Deprecated)
117
320
 
118
- <Callout type="note">
119
- This creates only the UI component. The actual tool execution happens where
120
- you've defined it (typically in your API route with server-based runtimes like
121
- Vercel AI SDK).
122
- </Callout>
321
+ Create UI-only components for tools defined elsewhere:
123
322
 
124
323
  ```tsx
125
- import { makeAssistantToolUI, AssistantToolUI } from "@assistant-ui/react";
324
+ import { makeAssistantToolUI } from "@assistant-ui/react";
126
325
 
127
326
  const SearchResultsUI = makeAssistantToolUI<
128
- {
129
- query: string;
130
- },
131
- {
132
- results: Array<{
133
- id: string;
134
- url: string;
135
- title: string;
136
- snippet: string;
137
- }>;
138
- }
327
+ { query: string },
328
+ { results: Array<any> }
139
329
  >({
140
- toolName: "webSearch", // Must match the registered tool's name
330
+ toolName: "webSearch",
141
331
  render: ({ args, result }) => {
142
332
  return (
143
- <div className="search-results">
333
+ <div>
144
334
  <h3>Search: {args.query}</h3>
145
335
  {result.results.map((item) => (
146
- <div key={item.id}>
147
- <a href={item.url}>{item.title}</a>
148
- <p>{item.snippet}</p>
149
- </div>
336
+ <div key={item.id}>{item.title}</div>
150
337
  ))}
151
338
  </div>
152
339
  );
153
340
  },
154
341
  });
155
342
 
156
- // Place the tool component inside AssistantRuntimeProvider
157
343
  function App() {
158
344
  return (
159
345
  <AssistantRuntimeProvider runtime={runtime}>
@@ -164,81 +350,33 @@ function App() {
164
350
  }
165
351
  ```
166
352
 
167
- ### 4. Advanced: Direct Context Registration
168
-
169
- Use `api.modelContext().register()` when you need to configure more than just tools:
170
-
171
- ```tsx
172
- import { tool, useAssistantApi } from "@assistant-ui/react";
173
- import { useEffect, useState } from "react";
174
- import { z } from "zod";
175
-
176
- function MyComponent() {
177
- const api = useAssistantApi();
178
- const [isCreativeMode, setIsCreativeMode] = useState(false);
179
-
180
- useEffect(() => {
181
- const calculateTool = tool({
182
- description: "Perform mathematical calculations",
183
- parameters: z.object({
184
- expression: z.string(),
185
- }),
186
- execute: async ({ expression }) => {
187
- return eval(expression); // Note: Use proper math parser in production
188
- },
189
- });
190
-
191
- // Register tools with model configuration
192
- return api.modelContext().register({
193
- getModelContext: () => ({
194
- tools: { calculate: calculateTool },
195
- callSettings: {
196
- temperature: isCreativeMode ? 0.9 : 0.2,
197
- maxTokens: 1000,
198
- },
199
- priority: 10, // Higher priority overrides other providers
200
- }),
201
- });
202
- }, [api, isCreativeMode]);
203
-
204
- return <div>{/* Your component */}</div>;
205
- }
206
- ```
207
-
208
- Use this approach when you need:
209
-
210
- - Dynamic model parameters (temperature, maxTokens, etc.)
211
- - Priority-based context merging
212
- - Multiple context types in one registration
353
+ **Why this is deprecated**: Component-based UI registration can cause issues with tool UI not appearing or appearing multiple times.
213
354
 
214
355
  ## Tool Paradigms
215
356
 
216
357
  ### Frontend Tools
217
358
 
218
- Tools that execute in the browser, accessing client-side resources:
359
+ Tools that execute in the browser:
219
360
 
220
361
  ```tsx
221
- const screenshotTool = tool({
222
- description: "Capture a screenshot of the current page",
223
- parameters: z.object({
224
- selector: z.string().optional(),
225
- }),
226
- execute: async ({ selector }) => {
227
- const element = selector ? document.querySelector(selector) : document.body;
228
- const screenshot = await captureElement(element);
229
- return { dataUrl: screenshot };
362
+ const frontendToolkit: Toolkit = {
363
+ screenshot: {
364
+ description: "Capture a screenshot of the current page",
365
+ parameters: z.object({
366
+ selector: z.string().optional(),
367
+ }),
368
+ execute: async ({ selector }) => {
369
+ const element = selector ? document.querySelector(selector) : document.body;
370
+ const screenshot = await captureElement(element);
371
+ return { dataUrl: screenshot };
372
+ },
230
373
  },
231
- });
232
-
233
- const ScreenshotTool = makeAssistantTool({
234
- ...screenshotTool,
235
- toolName: "screenshot",
236
- });
374
+ };
237
375
  ```
238
376
 
239
377
  ### Backend Tools
240
378
 
241
- Tools that trigger server-side operations:
379
+ Tools executed server-side:
242
380
 
243
381
  ```tsx
244
382
  // Backend route (AI SDK)
@@ -258,7 +396,6 @@ export async function POST(req: Request) {
258
396
  }),
259
397
  ),
260
398
  execute: async ({ query, table }) => {
261
- // Server-side database access
262
399
  const results = await db.query(query, { table });
263
400
  return results;
264
401
  },
@@ -272,26 +409,35 @@ export async function POST(req: Request) {
272
409
 
273
410
  ### Client-Defined Tools with frontendTools
274
411
 
275
- Currently, the Vercel AI SDK adapter implements automatic serialization of client-defined tools. When using this adapter, tools registered via `makeAssistantTool`, `useAssistantTool`, or `registerModelContextProvider` are automatically included in API requests. The `frontendTools` utility helps you use these tools server-side:
412
+ The Vercel AI SDK adapter implements automatic serialization of client-defined tools. Tools registered via the `Tools()` API are automatically included in API requests:
276
413
 
277
414
  ```tsx
278
- // Frontend: Define tool with makeAssistantTool
279
- import { makeAssistantTool, tool } from "@assistant-ui/react";
280
-
281
- const calculateTool = tool({
282
- description: "Perform calculations",
283
- parameters: z.object({
284
- expression: z.string(),
285
- }),
286
- execute: async ({ expression }) => {
287
- return eval(expression); // Note: Use proper math parser in production
415
+ // Frontend: Define tools with Tools() API
416
+ const clientToolkit: Toolkit = {
417
+ calculate: {
418
+ description: "Perform calculations",
419
+ parameters: z.object({
420
+ expression: z.string(),
421
+ }),
422
+ execute: async ({ expression }) => {
423
+ return eval(expression); // Use proper parser in production
424
+ },
288
425
  },
289
- });
426
+ };
290
427
 
291
- const CalculateTool = makeAssistantTool({
292
- ...calculateTool,
293
- toolName: "calculate",
294
- });
428
+ function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
429
+ const runtime = useChatRuntime();
430
+
431
+ const aui = useAui({
432
+ tools: Tools({ toolkit: clientToolkit }),
433
+ });
434
+
435
+ return (
436
+ <AssistantRuntimeProvider aui={aui} runtime={runtime}>
437
+ {children}
438
+ </AssistantRuntimeProvider>
439
+ );
440
+ }
295
441
 
296
442
  // Backend: Use frontendTools to receive client tools
297
443
  import { frontendTools } from "@assistant-ui/react-ai-sdk";
@@ -306,7 +452,7 @@ export async function POST(req: Request) {
306
452
  ...frontendTools(tools), // Client-defined tools
307
453
  // Additional server-side tools
308
454
  queryDatabase: {
309
- description: "Query the application database",
455
+ description: "Query the database",
310
456
  inputSchema: zodSchema(z.object({ query: z.string() })),
311
457
  execute: async ({ query }) => {
312
458
  return await db.query(query);
@@ -319,219 +465,15 @@ export async function POST(req: Request) {
319
465
  }
320
466
  ```
321
467
 
322
- <Callout type="note">
323
- The `frontendTools` utility is currently only available for the Vercel AI SDK
324
- integration. Other adapters like LangGraph follow a server-side tool
325
- definition model and don't yet implement client tool serialization. Learn more
326
- in the [Vercel AI SDK integration guide](/docs/runtimes/ai-sdk/use-chat-hook).
327
- </Callout>
328
-
329
- ### Human-in-the-Loop Tools
330
-
331
- Tools that require human approval or input:
332
-
333
- ```tsx
334
- import { makeAssistantTool, tool } from "@assistant-ui/react";
335
- import { z } from "zod";
336
-
337
- const refundTool = tool({
338
- description: "Process a customer refund",
339
- parameters: z.object({
340
- orderId: z.string(),
341
- amount: z.number(),
342
- reason: z.string(),
343
- }),
344
- execute: async ({ orderId, amount, reason }) => {
345
- // Wait for human approval
346
- const approved = await requestHumanApproval({
347
- action: "refund",
348
- details: { orderId, amount, reason },
349
- });
350
-
351
- if (!approved) {
352
- throw new Error("Refund rejected by administrator");
353
- }
354
-
355
- return await processRefund(orderId, amount);
356
- },
357
- });
358
-
359
- const RefundTool = makeAssistantTool({
360
- ...refundTool,
361
- toolName: "requestRefund",
362
- });
363
- ```
364
-
365
- ### Tool Human Input
366
-
367
- Tools can pause their execution to request user input or approval before continuing. This is useful for:
368
-
369
- - Requesting user confirmation for sensitive operations
370
- - Collecting additional information mid-execution
371
- - Implementing progressive disclosure workflows
372
- - Building interactive, multi-step tool experiences
373
-
374
- ```tsx
375
- import { makeAssistantTool, tool } from "@assistant-ui/react";
376
- import { z } from "zod";
377
-
378
- const confirmationTool = tool({
379
- description: "Send an email with confirmation",
380
- parameters: z.object({
381
- to: z.string(),
382
- subject: z.string(),
383
- body: z.string(),
384
- }),
385
- execute: async ({ to, subject, body }, { human }) => {
386
- // Request user confirmation before sending
387
- const confirmed = await human({
388
- type: "confirmation",
389
- action: "send-email",
390
- details: { to, subject },
391
- });
392
-
393
- if (!confirmed) {
394
- return {
395
- status: "cancelled",
396
- message: "Email sending cancelled by user",
397
- };
398
- }
399
-
400
- // Proceed with sending the email
401
- await sendEmail({ to, subject, body });
402
- return { status: "sent", message: `Email sent to ${to}` };
403
- },
404
- });
405
-
406
- const EmailTool = makeAssistantTool({
407
- ...confirmationTool,
408
- toolName: "sendEmail",
409
- render: ({ args, result, interrupt, resume }) => {
410
- // The interrupt payload is available when the tool is waiting for user input
411
- if (interrupt) {
412
- return (
413
- <div className="confirmation-dialog">
414
- <h3>Confirm Email</h3>
415
- <p>Send email to: {interrupt.payload.details.to}</p>
416
- <p>Subject: {interrupt.payload.details.subject}</p>
417
- <div className="actions">
418
- <button onClick={() => resume(true)}>Confirm</button>
419
- <button onClick={() => resume(false)}>Cancel</button>
420
- </div>
421
- </div>
422
- );
423
- }
424
-
425
- // Show the result after completion
426
- if (result) {
427
- return (
428
- <div className="email-result">
429
- <p>{result.message}</p>
430
- </div>
431
- );
432
- }
433
-
434
- // Show loading state
435
- return <div>Preparing email...</div>;
436
- },
437
- });
438
- ```
439
-
440
- #### Human Input Behavior
441
-
442
- - **Payload**: The object passed to `human()` is available in the `render` function as `interrupt.payload`
443
- - **Type**: The `interrupt` object has the structure `{ type: "human", payload: ... }`
444
- - **Resume**: Call `resume(payload)` to continue execution - the payload becomes the resolved value of the `human()` call
445
- - **Multiple Requests**: If `human()` is called multiple times, previous requests are automatically rejected with an error
446
- - **Cancellation**: If the tool execution is aborted (e.g., user cancels the message), all pending requests are rejected
447
-
448
- #### Advanced Human Input Patterns
449
-
450
- You can use human input for complex multi-step interactions:
451
-
452
- ```tsx
453
- const wizardTool = tool({
454
- description: "Multi-step data processing wizard",
455
- parameters: z.object({
456
- dataSource: z.string(),
457
- }),
458
- execute: async ({ dataSource }, { human }) => {
459
- // Step 1: Load data
460
- const data = await loadData(dataSource);
461
-
462
- // Step 2: Request user to select columns
463
- const selectedColumns = await human({
464
- type: "column-selection",
465
- availableColumns: data.columns,
466
- });
467
-
468
- // Step 3: Request processing options
469
- const options = await human({
470
- type: "processing-options",
471
- columns: selectedColumns,
472
- });
473
-
474
- // Step 4: Process data with user selections
475
- const result = await processData(data, selectedColumns, options);
476
- return result;
477
- },
478
- });
479
-
480
- const WizardTool = makeAssistantTool({
481
- ...wizardTool,
482
- toolName: "dataWizard",
483
- render: ({ args, result, interrupt, resume }) => {
484
- if (interrupt?.payload.type === "column-selection") {
485
- return (
486
- <ColumnSelector
487
- columns={interrupt.payload.availableColumns}
488
- onSelect={(cols) => resume(cols)}
489
- />
490
- );
491
- }
492
-
493
- if (interrupt?.payload.type === "processing-options") {
494
- return (
495
- <ProcessingOptions
496
- columns={interrupt.payload.columns}
497
- onConfirm={(opts) => resume(opts)}
498
- />
499
- );
500
- }
501
-
502
- if (result) {
503
- return <ResultDisplay data={result} />;
504
- }
505
-
506
- return <div>Loading...</div>;
507
- },
508
- });
509
- ```
510
-
511
- <Callout type="note">
512
- When a tool calls `human()` multiple times (e.g., for multi-step
513
- workflows), each new request automatically rejects any previous pending
514
- request. Make sure to handle potential errors if you need to support
515
- cancellation of earlier steps.
516
- </Callout>
517
-
518
468
  ### MCP (Model Context Protocol) Tools
519
469
 
520
- Integration with MCP servers using AI SDK v5's experimental MCP support:
521
-
522
- <Callout type="warning">
523
- MCP support in AI SDK v5 is experimental. The API may change in future
524
- releases. Make sure to install the MCP SDK: `npm install
525
- @modelcontextprotocol/sdk`
526
- </Callout>
470
+ Integration with MCP servers using AI SDK's experimental MCP support:
527
471
 
528
472
  ```tsx
529
- // Server-side usage (e.g., in your API route)
530
473
  import { experimental_createMCPClient, streamText } from "ai";
531
474
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
532
475
 
533
476
  export async function POST(req: Request) {
534
- // Create MCP client with stdio transport
535
477
  const client = await experimental_createMCPClient({
536
478
  transport: new StdioClientTransport({
537
479
  command: "npx",
@@ -540,7 +482,6 @@ export async function POST(req: Request) {
540
482
  });
541
483
 
542
484
  try {
543
- // Get tools from the MCP server
544
485
  const tools = await client.tools();
545
486
 
546
487
  const result = streamText({
@@ -551,191 +492,73 @@ export async function POST(req: Request) {
551
492
 
552
493
  return result.toUIMessageStreamResponse();
553
494
  } finally {
554
- // Always close the client to release resources
555
495
  await client.close();
556
496
  }
557
497
  }
558
-
559
- // Frontend usage with assistant-ui
560
- const runtime = useChatRuntime({
561
- api: "/api/chat", // Your API route that uses MCP tools
562
- });
563
498
  ```
564
499
 
565
- Alternative transport options:
500
+ ## Best Practices
566
501
 
567
- ```tsx
568
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
569
- import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
570
-
571
- // HTTP transport
572
- const httpClient = await experimental_createMCPClient({
573
- transport: new StreamableHTTPClientTransport(
574
- new URL("http://localhost:3000/mcp"),
575
- ),
576
- });
502
+ 1. **Use Tools() API**: Always prefer the `Tools()` API over legacy component/hook-based registration
503
+ 2. **Centralize Definitions**: Keep all tools in a toolkit file for easy management
504
+ 3. **Clear Descriptions**: Write descriptive tool descriptions that help the LLM understand when to use each tool
505
+ 4. **Parameter Validation**: Use Zod schemas to ensure type safety
506
+ 5. **Error Handling**: Handle errors gracefully with user-friendly messages
507
+ 6. **Loading States**: Provide visual feedback during tool execution
508
+ 7. **Security**: Validate permissions and sanitize inputs
509
+ 8. **Performance**: Use abort signals for cancellable operations
510
+ 9. **Testing**: Test tools in isolation and with the full assistant flow
577
511
 
578
- // Server-Sent Events transport
579
- const sseClient = await experimental_createMCPClient({
580
- transport: new SSEClientTransport(new URL("http://localhost:3000/sse")),
581
- });
582
- ```
512
+ ## Migration from Legacy APIs
583
513
 
584
- ## Advanced Patterns
514
+ To migrate from legacy APIs to the `Tools()` API:
585
515
 
586
- ### Tool Composition
516
+ 1. **Create a toolkit object** with all your tools
517
+ 2. **Move tool definitions** from `makeAssistantTool`/`useAssistantTool` calls into the toolkit
518
+ 3. **Register once** using `useAui({ tools: Tools({ toolkit }) })` in your runtime provider
519
+ 4. **Remove component registrations** (`<WeatherTool />`, etc.)
520
+ 5. **Test** to ensure all tools work as expected
587
521
 
588
- Combining multiple tools for complex workflows:
522
+ Example migration:
589
523
 
590
524
  ```tsx
591
- const travelPlannerTool = tool({
592
- description: "Plan a complete trip itinerary",
593
- parameters: z.object({
594
- destination: z.string(),
595
- dates: z.object({
596
- start: z.string(),
597
- end: z.string(),
598
- }),
599
- }),
600
- execute: async ({ destination, dates }) => {
601
- // Execute multiple operations
602
- const weather = await getWeatherAPI(destination);
603
- const hotels = await searchHotelsAPI({
604
- location: destination,
605
- dates,
606
- });
607
- const activities = await findActivitiesAPI({
608
- location: destination,
609
- weather: weather.forecast,
610
- });
611
-
612
- return {
613
- weather,
614
- hotels,
615
- activities,
616
- itinerary: generateItinerary({ weather, hotels, activities }),
617
- };
618
- },
619
- });
620
-
621
- const TravelPlannerTool = makeAssistantTool({
622
- ...travelPlannerTool,
623
- toolName: "planTrip",
525
+ // Before (Legacy)
526
+ const WeatherTool = makeAssistantTool({
527
+ toolName: "getWeather",
528
+ description: "Get weather",
529
+ parameters: z.object({ location: z.string() }),
530
+ execute: async ({ location }) => { /* ... */ },
624
531
  });
625
- ```
626
-
627
- ### Conditional Tool Availability
628
-
629
- Tools that appear based on context:
630
532
 
631
- ```tsx
632
- function ConditionalTools() {
633
- const { user } = useAuth();
634
- const { subscription } = useSubscription();
635
-
636
- // Premium features
637
- useAssistantTool({
638
- toolName: "advancedAnalysis",
639
- description: "Perform advanced data analysis",
640
- parameters: z.object({
641
- dataset: z.string(),
642
- }),
643
- execute: async (args) => {
644
- // Premium analysis logic
645
- },
646
- enabled: subscription?.tier === "premium",
647
- });
648
-
649
- // Role-based tools
650
- useAssistantTool({
651
- toolName: "adminPanel",
652
- description: "Access admin controls",
653
- parameters: z.object({}),
654
- execute: async () => {
655
- // Admin actions
656
- },
657
- enabled: user?.role === "admin",
658
- });
533
+ function App() {
534
+ return (
535
+ <AssistantRuntimeProvider runtime={runtime}>
536
+ <WeatherTool />
537
+ <Thread />
538
+ </AssistantRuntimeProvider>
539
+ );
659
540
  }
660
- ```
661
541
 
662
- ### Tool Error Handling
663
-
664
- Robust error handling and recovery:
665
-
666
- ```tsx
667
- const resilientTool = tool({
668
- description: "Fetch data with retry logic",
669
- parameters: z.object({
670
- endpoint: z.string(),
671
- }),
672
- execute: async ({ endpoint }, { abortSignal }) => {
673
- const maxRetries = 3;
674
- let lastError;
675
-
676
- for (let i = 0; i < maxRetries; i++) {
677
- try {
678
- const response = await fetch(endpoint, { signal: abortSignal });
679
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
680
- return await response.json();
681
- } catch (error) {
682
- lastError = error;
683
- if (abortSignal.aborted) throw error; // Don't retry on abort
684
- await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)));
685
- }
686
- }
687
-
688
- throw new Error(
689
- `Failed after ${maxRetries} attempts: ${lastError.message}`,
690
- );
542
+ // After (Recommended)
543
+ const toolkit: Toolkit = {
544
+ getWeather: {
545
+ description: "Get weather",
546
+ parameters: z.object({ location: z.string() }),
547
+ execute: async ({ location }) => { /* ... */ },
691
548
  },
692
- });
693
-
694
- const ResilientTool = makeAssistantTool({
695
- ...resilientTool,
696
- toolName: "fetchWithRetries",
697
- });
698
- ```
699
-
700
- ## Best Practices
701
-
702
- 1. **Clear Descriptions**: Write descriptive tool descriptions that help the LLM understand when to use each tool
703
- 2. **Parameter Validation**: Use Zod schemas to ensure type safety and provide clear parameter descriptions
704
- 3. **Error Handling**: Always handle potential errors gracefully with user-friendly messages
705
- 4. **Loading States**: Provide visual feedback during tool execution
706
- 5. **Security**: Validate permissions and sanitize inputs, especially for destructive operations
707
- 6. **Performance**: Use abort signals for cancellable operations and implement timeouts
708
- 7. **Testing**: Test tools in isolation and with the full assistant flow
709
-
710
- ## Tool Execution Context
711
-
712
- Tools receive additional context during execution:
713
-
714
- ```tsx
715
- execute: async (args, context) => {
716
- // context.abortSignal - AbortSignal for cancellation
717
- // context.toolCallId - Unique identifier for this invocation
718
- // context.human - Function to request human input
719
-
720
- // Example: Request user confirmation
721
- const userResponse = await context.human({
722
- message: "Are you sure?",
723
- });
724
549
  };
725
- ```
726
550
 
727
- The execution context provides:
551
+ function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
552
+ const runtime = useChatRuntime();
728
553
 
729
- - **`abortSignal`**: An `AbortSignal` that triggers when the tool execution is cancelled
730
- - **`toolCallId`**: A unique identifier for this specific tool invocation
731
- - **`human`**: A function that pauses execution and requests user input. The payload passed to `human()` becomes available in the render function, and the value passed to `resume()` becomes the resolved value of the `human()` call
732
-
733
- ## Runtime Integration
734
-
735
- Each integration handles tools differently:
736
-
737
- - **Vercel AI SDK**: Tools defined in API routes with `streamText({ tools: {...} })`. Also supports client-defined tools via `frontendTools`.
738
- - **LangGraph**: Tools defined in your LangGraph graph configuration.
739
- - **Mastra**: Tools defined as typed functions used by agents and workflows.
554
+ const aui = useAui({
555
+ tools: Tools({ toolkit }),
556
+ });
740
557
 
741
- All integrations support tool UI customization via `makeAssistantToolUI`.
558
+ return (
559
+ <AssistantRuntimeProvider aui={aui} runtime={runtime}>
560
+ {children}
561
+ </AssistantRuntimeProvider>
562
+ );
563
+ }
564
+ ```