@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.
- package/.docs/organized/code-examples/with-ag-ui.md +172 -1633
- package/.docs/organized/code-examples/with-ai-sdk-v6.md +42 -1640
- package/.docs/organized/code-examples/with-assistant-transport.md +40 -1743
- package/.docs/organized/code-examples/with-cloud.md +71 -1745
- package/.docs/organized/code-examples/with-custom-thread-list.md +87 -1723
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +70 -1637
- package/.docs/organized/code-examples/with-external-store.md +67 -1624
- package/.docs/organized/code-examples/with-ffmpeg.md +71 -1629
- package/.docs/organized/code-examples/with-langgraph.md +95 -1893
- package/.docs/organized/code-examples/with-parent-id-grouping.md +57 -1654
- package/.docs/organized/code-examples/with-react-hook-form.md +220 -2163
- package/.docs/organized/code-examples/with-react-router.md +66 -1318
- package/.docs/organized/code-examples/with-store.md +31 -31
- package/.docs/organized/code-examples/with-tanstack.md +77 -861
- package/.docs/organized/code-examples/with-tap-runtime.md +812 -0
- package/.docs/raw/docs/(docs)/cli.mdx +66 -0
- package/.docs/raw/docs/(docs)/copilots/make-assistant-tool-ui.mdx +0 -1
- package/.docs/raw/docs/(docs)/copilots/make-assistant-tool.mdx +0 -1
- package/.docs/raw/docs/(docs)/copilots/model-context.mdx +4 -4
- package/.docs/raw/docs/(docs)/copilots/motivation.mdx +3 -3
- package/.docs/raw/docs/(docs)/devtools.mdx +0 -1
- package/.docs/raw/docs/(docs)/guides/attachments.mdx +2 -3
- package/.docs/raw/docs/(docs)/guides/context-api.mdx +117 -117
- package/.docs/raw/docs/(docs)/guides/suggestions.mdx +296 -0
- package/.docs/raw/docs/(docs)/guides/tools.mdx +336 -513
- package/.docs/raw/docs/(docs)/index.mdx +33 -410
- package/.docs/raw/docs/(docs)/installation.mdx +450 -0
- package/.docs/raw/docs/(docs)/llm.mdx +209 -0
- package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/context-providers/text-message-part-provider.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +48 -3
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-hook-form.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/integrations/vercel-ai-sdk.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/overview.mdx +9 -3
- package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar-more.mdx +20 -52
- package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar.mdx +16 -39
- package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +49 -50
- package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-modal.mdx +3 -11
- package/.docs/raw/docs/(reference)/api-reference/primitives/attachment.mdx +0 -3
- package/.docs/raw/docs/(reference)/api-reference/primitives/branch-picker.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +5 -16
- package/.docs/raw/docs/(reference)/api-reference/primitives/composition.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/primitives/error.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +1 -2
- package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/primitives/suggestion.mdx +152 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item-more.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item.mdx +1 -2
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list.mdx +1 -2
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +28 -40
- package/.docs/raw/docs/(reference)/api-reference/runtimes/assistant-runtime.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/runtimes/attachment-runtime.mdx +1 -2
- package/.docs/raw/docs/(reference)/api-reference/runtimes/composer-runtime.mdx +2 -3
- package/.docs/raw/docs/(reference)/api-reference/runtimes/message-part-runtime.mdx +1 -2
- package/.docs/raw/docs/(reference)/api-reference/runtimes/message-runtime.mdx +1 -2
- package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-item-runtime.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-runtime.mdx +1 -2
- package/.docs/raw/docs/(reference)/legacy/styled/assistant-modal.mdx +0 -1
- package/.docs/raw/docs/(reference)/legacy/styled/decomposition.mdx +5 -5
- package/.docs/raw/docs/(reference)/legacy/styled/markdown.mdx +0 -1
- package/.docs/raw/docs/(reference)/legacy/styled/thread.mdx +0 -1
- package/.docs/raw/docs/(reference)/migrations/v0-12.mdx +207 -33
- package/.docs/raw/docs/(reference)/react-compatibility.mdx +0 -1
- package/.docs/raw/docs/cloud/persistence/ai-sdk.mdx +0 -1
- package/.docs/raw/docs/cloud/persistence/langgraph.mdx +0 -1
- package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +0 -1
- package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +118 -0
- package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +198 -0
- package/.docs/raw/docs/runtimes/assistant-transport.mdx +3 -3
- package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +5 -6
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +9 -11
- package/.docs/raw/docs/runtimes/custom/local.mdx +43 -36
- package/.docs/raw/docs/runtimes/data-stream.mdx +35 -3
- package/.docs/raw/docs/runtimes/langgraph/index.mdx +1 -2
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +0 -1
- package/.docs/raw/docs/runtimes/langserve.mdx +0 -1
- package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +0 -1
- package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +0 -1
- package/.docs/raw/docs/ui/accordion.mdx +259 -0
- package/.docs/raw/docs/ui/assistant-modal.mdx +1 -3
- package/.docs/raw/docs/ui/assistant-sidebar.mdx +1 -3
- package/.docs/raw/docs/ui/attachment.mdx +0 -2
- package/.docs/raw/docs/ui/badge.mdx +138 -0
- package/.docs/raw/docs/ui/diff-viewer.mdx +279 -0
- package/.docs/raw/docs/ui/file.mdx +152 -0
- package/.docs/raw/docs/ui/image.mdx +100 -0
- package/.docs/raw/docs/ui/markdown.mdx +0 -1
- package/.docs/raw/docs/ui/mermaid.mdx +0 -1
- package/.docs/raw/docs/ui/model-selector.mdx +224 -0
- package/.docs/raw/docs/ui/part-grouping.mdx +4 -5
- package/.docs/raw/docs/ui/reasoning.mdx +6 -5
- package/.docs/raw/docs/ui/scrollbar.mdx +26 -9
- package/.docs/raw/docs/ui/select.mdx +245 -0
- package/.docs/raw/docs/ui/sources.mdx +6 -5
- package/.docs/raw/docs/ui/streamdown.mdx +348 -0
- package/.docs/raw/docs/ui/syntax-highlighting.mdx +8 -63
- package/.docs/raw/docs/ui/tabs.mdx +259 -0
- package/.docs/raw/docs/ui/thread-list.mdx +98 -16
- package/.docs/raw/docs/ui/thread.mdx +57 -73
- package/.docs/raw/docs/ui/tool-fallback.mdx +0 -1
- package/.docs/raw/docs/ui/tool-group.mdx +1 -3
- package/README.md +3 -3
- package/package.json +4 -4
- package/src/tools/tests/examples.test.ts +1 -1
- package/.docs/raw/docs/(docs)/about-assistantui.mdx +0 -54
- package/.docs/raw/docs/(docs)/mcp-docs-server.mdx +0 -321
- 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
|
-
##
|
|
27
|
+
## Recommended: Tools() API
|
|
28
28
|
|
|
29
|
-
assistant-ui
|
|
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
|
-
|
|
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
|
-
|
|
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()
|
|
49
|
-
unit: z.enum(["celsius", "fahrenheit"]).default("celsius"),
|
|
267
|
+
location: z.string(),
|
|
50
268
|
}),
|
|
51
|
-
execute: async ({ location
|
|
52
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
###
|
|
293
|
+
### Using `useAssistantTool` Hook (Deprecated)
|
|
83
294
|
|
|
84
|
-
Register tools dynamically using React hooks
|
|
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
|
|
304
|
+
description: "Search through the data",
|
|
96
305
|
parameters: z.object({
|
|
97
306
|
query: z.string(),
|
|
98
307
|
}),
|
|
99
308
|
execute: async ({ query }) => {
|
|
100
|
-
|
|
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
|
-
|
|
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
|
-
|
|
319
|
+
### Using `makeAssistantToolUI` (Deprecated)
|
|
117
320
|
|
|
118
|
-
|
|
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
|
|
324
|
+
import { makeAssistantToolUI } from "@assistant-ui/react";
|
|
126
325
|
|
|
127
326
|
const SearchResultsUI = makeAssistantToolUI<
|
|
128
|
-
{
|
|
129
|
-
|
|
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",
|
|
330
|
+
toolName: "webSearch",
|
|
141
331
|
render: ({ args, result }) => {
|
|
142
332
|
return (
|
|
143
|
-
<div
|
|
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
|
-
|
|
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
|
|
359
|
+
Tools that execute in the browser:
|
|
219
360
|
|
|
220
361
|
```tsx
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
500
|
+
## Best Practices
|
|
566
501
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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
|
-
|
|
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
|
-
|
|
514
|
+
To migrate from legacy APIs to the `Tools()` API:
|
|
585
515
|
|
|
586
|
-
|
|
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
|
-
|
|
522
|
+
Example migration:
|
|
589
523
|
|
|
590
524
|
```tsx
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
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
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
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
|
-
|
|
551
|
+
function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
|
|
552
|
+
const runtime = useChatRuntime();
|
|
728
553
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
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
|
-
|
|
558
|
+
return (
|
|
559
|
+
<AssistantRuntimeProvider aui={aui} runtime={runtime}>
|
|
560
|
+
{children}
|
|
561
|
+
</AssistantRuntimeProvider>
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
```
|