@assistant-ui/mcp-docs-server 0.1.38 → 0.1.39
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/waterfall.md +8 -9
- package/.docs/organized/code-examples/with-a2a.md +16 -11
- package/.docs/organized/code-examples/with-ag-ui.md +16 -11
- package/.docs/organized/code-examples/{with-ai-sdk-v6.md → with-ai-sdk-v7.md} +32 -21
- package/.docs/organized/code-examples/with-artifacts.md +17 -10
- package/.docs/organized/code-examples/with-assistant-transport.md +15 -8
- package/.docs/organized/code-examples/with-browser-extension.md +15 -8
- package/.docs/organized/code-examples/with-chain-of-thought.md +17 -10
- package/.docs/organized/code-examples/with-cloud-standalone.md +10 -9
- package/.docs/organized/code-examples/with-cloud.md +18 -13
- package/.docs/organized/code-examples/with-custom-thread-list.md +17 -10
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +20 -13
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +20 -13
- package/.docs/organized/code-examples/with-eve.md +16 -9
- package/.docs/organized/code-examples/with-expo.md +27 -24
- package/.docs/organized/code-examples/with-external-store.md +16 -11
- package/.docs/organized/code-examples/with-ffmpeg.md +18 -13
- package/.docs/organized/code-examples/with-generative-ui.md +20 -15
- package/.docs/organized/code-examples/with-google-adk.md +15 -8
- package/.docs/organized/code-examples/with-heat-graph.md +8 -9
- package/.docs/organized/code-examples/with-image-generation.md +17 -10
- package/.docs/organized/code-examples/with-interactables.md +19 -15
- package/.docs/organized/code-examples/with-langchain.md +17 -10
- package/.docs/organized/code-examples/with-langgraph.md +17 -10
- package/.docs/organized/code-examples/with-livekit.md +21 -14
- package/.docs/organized/code-examples/with-mcp.md +25 -12
- package/.docs/organized/code-examples/with-opencode.md +22 -17
- package/.docs/organized/code-examples/with-pi.md +47 -12
- package/.docs/organized/code-examples/with-react-hook-form.md +19 -14
- package/.docs/organized/code-examples/with-react-ink-web.md +6 -6
- package/.docs/organized/code-examples/with-react-ink.md +2 -2
- package/.docs/organized/code-examples/with-react-router.md +20 -15
- package/.docs/organized/code-examples/with-resumable-stream.md +19 -12
- package/.docs/organized/code-examples/with-store.md +8 -9
- package/.docs/organized/code-examples/with-tanstack.md +16 -10
- package/.docs/organized/code-examples/with-tap-runtime.md +16 -11
- package/.docs/organized/code-examples/with-virtualized-thread.md +17 -12
- package/.docs/raw/docs/(docs)/base-ui.mdx +39 -0
- package/.docs/raw/docs/(docs)/cli.mdx +17 -1
- package/.docs/raw/docs/(docs)/installation.mdx +15 -1
- package/.docs/raw/docs/(docs)/rtl.mdx +2 -4
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/actions.mdx +56 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/components.mdx +86 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +19 -1
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/json-generative-ui.mdx +42 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/rendering.mdx +53 -2
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/slack.mdx +81 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/teams.mdx +86 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/tokens.mdx +62 -0
- package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +4 -1
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +4 -2
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +37 -0
- package/.docs/raw/docs/(reference)/api-reference/overview.mdx +1 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +29 -29
- package/.docs/raw/docs/(reference)/api-reference/primitives/composition.mdx +1 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/index.mdx +3 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/selection-toolbar.mdx +2 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +0 -2
- package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +12 -9
- package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +4 -0
- package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +7 -1
- package/.docs/raw/docs/cloud/langgraph.mdx +3 -1
- package/.docs/raw/docs/guides/chatgpt-subscription.mdx +108 -0
- package/.docs/raw/docs/guides/dictation.mdx +185 -257
- package/.docs/raw/docs/guides/index.mdx +10 -0
- package/.docs/raw/docs/guides/mentions.mdx +31 -3
- package/.docs/raw/docs/guides/resumable-streams.mdx +12 -1
- package/.docs/raw/docs/guides/speech.mdx +47 -29
- package/.docs/raw/docs/guides/suggestions.mdx +70 -1
- package/.docs/raw/docs/guides/voice.mdx +197 -267
- package/.docs/raw/docs/ink/primitives.mdx +35 -1
- package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +10 -4
- package/.docs/raw/docs/integrations/frameworks/cloudflare-agents/overview.mdx +3 -3
- package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +1 -1
- package/.docs/raw/docs/integrations/frameworks/mastra/overview.mdx +3 -3
- package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +1 -1
- package/.docs/raw/docs/integrations/gateways/index.mdx +2 -2
- package/.docs/raw/docs/integrations/observability/helicone.mdx +1 -1
- package/.docs/raw/docs/integrations/observability/langfuse.mdx +1 -1
- package/.docs/raw/docs/integrations/observability/langsmith.mdx +1 -1
- package/.docs/raw/docs/migrations/index.mdx +50 -0
- package/.docs/raw/docs/migrations/toolkit-tools.mdx +4 -2
- package/.docs/raw/docs/primitives/chain-of-thought.mdx +6 -1
- package/.docs/raw/docs/primitives/composer.mdx +16 -0
- package/.docs/raw/docs/primitives/selection-toolbar.mdx +25 -0
- package/.docs/raw/docs/react-native/primitives.mdx +23 -0
- package/.docs/raw/docs/runtimes/ag-ui/agent-state.mdx +124 -0
- package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +10 -1
- package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +13 -4
- package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +11 -11
- package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +5 -5
- package/.docs/raw/docs/runtimes/ai-sdk/{v6.mdx → v6-legacy.mdx} +8 -6
- package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +717 -0
- package/.docs/raw/docs/runtimes/concepts/adapters.mdx +1 -1
- package/.docs/raw/docs/runtimes/concepts/architecture.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/data-stream.mdx +10 -19
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +2 -0
- package/.docs/raw/docs/runtimes/eve/overview.mdx +1 -1
- package/.docs/raw/docs/runtimes/langgraph/agent-state.mdx +181 -0
- package/.docs/raw/docs/tools/backend.mdx +6 -3
- package/.docs/raw/docs/tools/defining-tools.mdx +7 -1
- package/.docs/raw/docs/tools/generative-ui.mdx +60 -2
- package/.docs/raw/docs/tools/mcp-apps.mdx +29 -11
- package/.docs/raw/docs/tools/mcp.mdx +100 -3
- package/.docs/raw/docs/tools/tool-ui.mdx +6 -4
- package/.docs/raw/docs/tools/user-managed-mcp.mdx +26 -3
- package/.docs/raw/docs/ui/accordion.mdx +16 -10
- package/.docs/raw/docs/ui/assistant-modal.mdx +8 -4
- package/.docs/raw/docs/ui/attachment.mdx +5 -1
- package/.docs/raw/docs/ui/badge.mdx +23 -12
- package/.docs/raw/docs/ui/follow-up-suggestions.mdx +2 -2
- package/.docs/raw/docs/ui/model-selector.mdx +32 -2
- package/.docs/raw/docs/ui/select.mdx +22 -14
- package/.docs/raw/docs/ui/sources.mdx +1 -1
- package/.docs/raw/docs/ui/tabs.mdx +25 -14
- package/.docs/raw/docs/utilities/heat-graph.mdx +2 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -1
- package/dist/prepare-docs/code-examples.d.ts.map +1 -1
- package/dist/prepare-docs/copy-raw.d.ts.map +1 -1
- package/dist/prompts/xulux-playground.d.ts +12 -0
- package/dist/prompts/xulux-playground.d.ts.map +1 -0
- package/dist/prompts/xulux-playground.js +33 -0
- package/dist/prompts/xulux-playground.js.map +1 -0
- package/dist/tools/docs.d.ts +2 -4
- package/dist/tools/docs.d.ts.map +1 -1
- package/dist/tools/docs.js +24 -8
- package/dist/tools/docs.js.map +1 -1
- package/dist/tools/examples.d.ts +2 -4
- package/dist/tools/examples.d.ts.map +1 -1
- package/dist/tools/examples.js +9 -6
- package/dist/tools/examples.js.map +1 -1
- package/dist/tools/resources.d.ts +0 -1
- package/dist/tools/resources.d.ts.map +1 -1
- package/dist/tools/search.d.ts +2 -5
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/tests/test-setup.d.ts.map +1 -1
- package/dist/tools/tests/test-setup.js +5 -1
- package/dist/tools/tests/test-setup.js.map +1 -1
- package/dist/tools/xulux-templates.d.ts +72 -0
- package/dist/tools/xulux-templates.d.ts.map +1 -0
- package/dist/tools/xulux-templates.js +82 -0
- package/dist/tools/xulux-templates.js.map +1 -0
- package/dist/utils/cache.d.ts +5 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +18 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/mcp-format.d.ts +1 -0
- package/dist/utils/mcp-format.d.ts.map +1 -1
- package/dist/utils/mcp-format.js +7 -4
- package/dist/utils/mcp-format.js.map +1 -1
- package/dist/utils/mdx.d.ts.map +1 -1
- package/dist/utils/paths.d.ts +1 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +3 -1
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/search.d.ts.map +1 -1
- package/dist/utils/security.d.ts.map +1 -1
- package/dist/xulux/catalog-client.d.ts +14 -0
- package/dist/xulux/catalog-client.d.ts.map +1 -0
- package/dist/xulux/catalog-client.js +67 -0
- package/dist/xulux/catalog-client.js.map +1 -0
- package/dist/xulux/fallback-catalog.d.ts +7 -0
- package/dist/xulux/fallback-catalog.d.ts.map +1 -0
- package/dist/xulux/fallback-catalog.js +47 -0
- package/dist/xulux/fallback-catalog.js.map +1 -0
- package/dist/xulux/fetch-sandbox.d.ts +5 -0
- package/dist/xulux/fetch-sandbox.d.ts.map +1 -0
- package/dist/xulux/fetch-sandbox.js +40 -0
- package/dist/xulux/fetch-sandbox.js.map +1 -0
- package/dist/xulux/template-service.d.ts +84 -0
- package/dist/xulux/template-service.d.ts.map +1 -0
- package/dist/xulux/template-service.js +223 -0
- package/dist/xulux/template-service.js.map +1 -0
- package/dist/xulux/types.d.ts +55 -0
- package/dist/xulux/types.d.ts.map +1 -0
- package/dist/xulux/types.js +6 -0
- package/dist/xulux/types.js.map +1 -0
- package/package.json +5 -5
- package/src/index.ts +53 -0
- package/src/prompts/xulux-playground.ts +36 -0
- package/src/tools/docs.ts +25 -3
- package/src/tools/examples.ts +15 -10
- package/src/tools/tests/docs.test.ts +20 -0
- package/src/tools/tests/examples.test.ts +5 -5
- package/src/tools/tests/listings-cache.test.ts +19 -0
- package/src/tools/tests/mcp-protocol.test.ts +81 -1
- package/src/tools/tests/test-setup.ts +8 -0
- package/src/tools/tests/xulux-templates.test.ts +262 -0
- package/src/tools/xulux-templates.ts +141 -0
- package/src/utils/cache.ts +20 -0
- package/src/utils/mcp-format.ts +8 -6
- package/src/utils/paths.ts +4 -1
- package/src/utils/tests/cache.test.ts +51 -0
- package/src/utils/tests/mcp-format.test.ts +22 -0
- package/src/utils/tests/security.test.ts +1 -1
- package/src/xulux/catalog-client.ts +105 -0
- package/src/xulux/fallback-catalog.ts +63 -0
- package/src/xulux/fetch-sandbox.ts +56 -0
- package/src/xulux/template-service.ts +406 -0
- package/src/xulux/types.ts +60 -0
- package/.docs/raw/docs/(reference)/api-reference/hooks/utilities.mdx +0 -464
|
@@ -196,7 +196,7 @@ type ThreadHistoryAdapter = {
|
|
|
196
196
|
`load` runs when a thread opens. `append` runs after each message completes.
|
|
197
197
|
|
|
198
198
|
<Callout type="info">
|
|
199
|
-
`react-ai-sdk` requires `withFormat` so messages round-trip as AI SDK `UIMessage` objects. An adapter without `withFormat` throws at runtime in the AI SDK path. See the [AI SDK history docs](/docs/runtimes/ai-sdk/
|
|
199
|
+
`react-ai-sdk` requires `withFormat` so messages round-trip as AI SDK `UIMessage` objects. An adapter without `withFormat` throws at runtime in the AI SDK path. See the [AI SDK history docs](/docs/runtimes/ai-sdk/v7) for the full pattern.
|
|
200
200
|
</Callout>
|
|
201
201
|
|
|
202
202
|
## Suggestion adapter
|
|
@@ -118,7 +118,7 @@ The fastest path. Each adapter wraps one of the core or protocol layers and adds
|
|
|
118
118
|
|
|
119
119
|
| Adapter | Layered on | Targets |
|
|
120
120
|
| --- | --- | --- |
|
|
121
|
-
| `react-ai-sdk` | `ExternalStoreRuntime` | Vercel AI SDK
|
|
121
|
+
| `react-ai-sdk` | `ExternalStoreRuntime` | Vercel AI SDK v7 (`useChat`) |
|
|
122
122
|
| `react-langgraph` | `ExternalStoreRuntime` | LangGraph Cloud via `@langchain/langgraph-sdk` |
|
|
123
123
|
| `react-langchain` | `ExternalStoreRuntime` | LangGraph Cloud via `@langchain/react`'s `useStream` |
|
|
124
124
|
| `react-google-adk` | `ExternalStoreRuntime` | Google ADK JS or Python agents |
|
|
@@ -19,14 +19,17 @@ If your backend exposes a richer state surface, consider [`AssistantTransport`](
|
|
|
19
19
|
|
|
20
20
|
## Wire protocols
|
|
21
21
|
|
|
22
|
-
`useDataStreamRuntime` accepts two wire formats
|
|
22
|
+
`useDataStreamRuntime` accepts two wire formats. When `protocol` is omitted, it
|
|
23
|
+
detects known Vercel AI response markers before falling back to UI message
|
|
24
|
+
stream for compatibility.
|
|
23
25
|
|
|
24
|
-
| Protocol |
|
|
26
|
+
| Protocol | Detection | Matching backend |
|
|
25
27
|
| --- | --- | --- |
|
|
26
|
-
| `"ui-message-stream"` |
|
|
27
|
-
| `"data-stream"` |
|
|
28
|
+
| `"ui-message-stream"` | `x-vercel-ai-ui-message-stream: v1`, otherwise fallback | AI SDK v5+'s `result.toUIMessageStreamResponse()` (SSE) |
|
|
29
|
+
| `"data-stream"` | `x-vercel-ai-data-stream: v1` | `createAssistantStreamResponse` from `assistant-stream`, or AI SDK v4's `toDataStreamResponse()` (Vercel data stream v1) |
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
Set `protocol` explicitly only for custom endpoints that do not preserve or
|
|
32
|
+
expose the response marker.
|
|
30
33
|
|
|
31
34
|
## Install
|
|
32
35
|
|
|
@@ -66,10 +69,7 @@ import { AssistantRuntimeProvider } from "@assistant-ui/react";
|
|
|
66
69
|
import { Thread } from "@/components/assistant-ui/thread";
|
|
67
70
|
|
|
68
71
|
export default function ChatPage() {
|
|
69
|
-
const runtime = useDataStreamRuntime({
|
|
70
|
-
api: "/api/chat",
|
|
71
|
-
protocol: "data-stream",
|
|
72
|
-
});
|
|
72
|
+
const runtime = useDataStreamRuntime({ api: "/api/chat" });
|
|
73
73
|
return (
|
|
74
74
|
<AssistantRuntimeProvider runtime={runtime}>
|
|
75
75
|
<Thread />
|
|
@@ -90,10 +90,7 @@ import { Thread } from "@/components/assistant-ui/thread";
|
|
|
90
90
|
const API_URL = process.env.EXPO_PUBLIC_API_URL ?? "http://localhost:3000";
|
|
91
91
|
|
|
92
92
|
export default function ChatPage() {
|
|
93
|
-
const runtime = useDataStreamRuntime({
|
|
94
|
-
api: `${API_URL}/api/chat`,
|
|
95
|
-
protocol: "data-stream",
|
|
96
|
-
});
|
|
93
|
+
const runtime = useDataStreamRuntime({ api: `${API_URL}/api/chat` });
|
|
97
94
|
return (
|
|
98
95
|
<AssistantRuntimeProvider runtime={runtime}>
|
|
99
96
|
<View style={{ flex: 1 }}>
|
|
@@ -116,7 +113,6 @@ import { Thread } from "./components/thread.js";
|
|
|
116
113
|
export function App() {
|
|
117
114
|
const runtime = useDataStreamRuntime({
|
|
118
115
|
api: "http://localhost:3000/api/chat",
|
|
119
|
-
protocol: "data-stream",
|
|
120
116
|
});
|
|
121
117
|
return (
|
|
122
118
|
<AssistantRuntimeProvider runtime={runtime}>
|
|
@@ -167,7 +163,6 @@ The request body includes `messages`, `tools`, `system` (if configured), and `th
|
|
|
167
163
|
```tsx
|
|
168
164
|
const runtime = useDataStreamRuntime({
|
|
169
165
|
api: "/api/chat",
|
|
170
|
-
protocol: "data-stream",
|
|
171
166
|
headers: { Authorization: `Bearer ${token}`, "X-Custom-Header": "value" },
|
|
172
167
|
credentials: "include",
|
|
173
168
|
});
|
|
@@ -178,7 +173,6 @@ Evaluate per-request:
|
|
|
178
173
|
```tsx
|
|
179
174
|
const runtime = useDataStreamRuntime({
|
|
180
175
|
api: "/api/chat",
|
|
181
|
-
protocol: "data-stream",
|
|
182
176
|
headers: async () => ({
|
|
183
177
|
Authorization: `Bearer ${await getAuthToken()}`,
|
|
184
178
|
}),
|
|
@@ -195,7 +189,6 @@ const runtime = useDataStreamRuntime({
|
|
|
195
189
|
```tsx
|
|
196
190
|
const runtime = useDataStreamRuntime({
|
|
197
191
|
api: "/api/chat",
|
|
198
|
-
protocol: "data-stream",
|
|
199
192
|
onResponse: (response) => console.log("status:", response.status),
|
|
200
193
|
onFinish: (message) => console.log("done:", message),
|
|
201
194
|
onError: (error) => console.error(error),
|
|
@@ -230,7 +223,6 @@ const myTools = {
|
|
|
230
223
|
|
|
231
224
|
const runtime = useDataStreamRuntime({
|
|
232
225
|
api: "/api/chat",
|
|
233
|
-
protocol: "data-stream",
|
|
234
226
|
body: { tools: toToolsJSONSchema(myTools) },
|
|
235
227
|
});
|
|
236
228
|
```
|
|
@@ -291,7 +283,6 @@ const runtime = useCloudRuntime({
|
|
|
291
283
|
```tsx
|
|
292
284
|
const runtime = useDataStreamRuntime({
|
|
293
285
|
api: "/api/chat",
|
|
294
|
-
protocol: "data-stream",
|
|
295
286
|
initialMessages: [
|
|
296
287
|
{ role: "user", content: [{ type: "text", text: "Hello" }] },
|
|
297
288
|
{ role: "assistant", content: [{ type: "text", text: "Hi!" }] },
|
|
@@ -796,7 +796,7 @@ useExternalStoreRuntime({
|
|
|
796
796
|
name: "onResume",
|
|
797
797
|
type: "(config: ResumeRunConfig) => Promise<void>",
|
|
798
798
|
description:
|
|
799
|
-
"Handler for resuming an interrupted run (e.g. after a page reload mid-generation).",
|
|
799
|
+
"Handler for resuming an interrupted run (e.g. after a page reload mid-generation). For AI SDK reload-safe streaming, see the [Resumable Streams](/docs/guides/resumable-streams) guide.",
|
|
800
800
|
},
|
|
801
801
|
{
|
|
802
802
|
name: "onResumeToolCall",
|
|
@@ -698,6 +698,8 @@ const OpenAIAdapter: ChatModelAdapter = {
|
|
|
698
698
|
};
|
|
699
699
|
```
|
|
700
700
|
|
|
701
|
+
For local development on a ChatGPT Plus or Pro plan, the same client can run without a real API key by pointing `baseURL` at a local OAuth proxy and passing a placeholder `apiKey`; see [ChatGPT Subscription](/docs/guides/chatgpt-subscription#openai-compatible-proxy).
|
|
702
|
+
|
|
701
703
|
### Custom REST API
|
|
702
704
|
|
|
703
705
|
```tsx
|
|
@@ -56,7 +56,7 @@ export default function Home() {
|
|
|
56
56
|
- An Eve app mounted with `eve/next`.
|
|
57
57
|
- A model credential for the model configured in `agent/agent.ts`.
|
|
58
58
|
|
|
59
|
-
Eve's default gateway model ids route through the [Vercel AI Gateway](https://vercel.com/docs/ai-gateway). Use `AI_GATEWAY_API_KEY` or Vercel OIDC, or configure a direct provider `LanguageModel`.
|
|
59
|
+
Eve's default gateway model ids route through the [Vercel AI Gateway](https://vercel.com/docs/ai-gateway). Use `AI_GATEWAY_API_KEY` or Vercel OIDC, or configure a direct provider `LanguageModel`. For local development, a direct `LanguageModel` can also run on a ChatGPT Plus or Pro plan without any API key; see [ChatGPT Subscription](/docs/guides/chatgpt-subscription).
|
|
60
60
|
|
|
61
61
|
## Install
|
|
62
62
|
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Agent state
|
|
3
|
+
description: Read and optimistically update graph state with useLangGraphState and useLangGraphSetState in LangGraph.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`useLangGraphState` mirrors the graph's values object that LangGraph streams to the client. It updates live while the agent runs (when the `values` stream mode is enabled). `useLangGraphSetState` lets you apply optimistic local updates that ride the next send.
|
|
7
|
+
|
|
8
|
+
## Enable the `values` stream mode
|
|
9
|
+
|
|
10
|
+
<Callout type="warn">
|
|
11
|
+
The default stream setup does **not** include the `values` stream mode. Without it, `useLangGraphState` never receives live graph state.
|
|
12
|
+
</Callout>
|
|
13
|
+
|
|
14
|
+
When you call `client.runs.stream` yourself, request `values` alongside the modes you already use:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
client.runs.stream(threadId, assistantId, {
|
|
18
|
+
input,
|
|
19
|
+
streamMode: ["messages", "updates", "custom", "values"],
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
If you use `unstable_createLangGraphStream`, its default stream modes do **not** include `values` either. Pass the option explicitly:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
const stream = unstable_createLangGraphStream({
|
|
27
|
+
client,
|
|
28
|
+
assistantId: ASSISTANT_ID,
|
|
29
|
+
streamMode: ["messages", "updates", "custom", "values"],
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Basic usage
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import {
|
|
37
|
+
useLangGraphState,
|
|
38
|
+
useLangGraphSetState,
|
|
39
|
+
} from "@assistant-ui/react-langgraph";
|
|
40
|
+
import { useAuiState } from "@assistant-ui/react";
|
|
41
|
+
|
|
42
|
+
type GraphState = {
|
|
43
|
+
messages: unknown[];
|
|
44
|
+
filters: { region: string; maxResults: number };
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const state = useLangGraphState<GraphState>();
|
|
48
|
+
// state: GraphState | undefined (latest agent state; updates live while the agent runs)
|
|
49
|
+
|
|
50
|
+
const setState = useLangGraphSetState<GraphState>();
|
|
51
|
+
// setState(next | (prev) => next) (optimistic local update; sent with the NEXT run)
|
|
52
|
+
|
|
53
|
+
const isRunning = useAuiState((s) => s.thread.isRunning);
|
|
54
|
+
// isRunning: boolean (whether the thread is currently running)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Example
|
|
58
|
+
|
|
59
|
+
Render graph state beside the chat and stage an optimistic filter update:
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
"use client";
|
|
63
|
+
|
|
64
|
+
import {
|
|
65
|
+
useLangGraphState,
|
|
66
|
+
useLangGraphSetState,
|
|
67
|
+
} from "@assistant-ui/react-langgraph";
|
|
68
|
+
import { useAuiState } from "@assistant-ui/react";
|
|
69
|
+
import { Thread } from "@/components/assistant-ui/thread";
|
|
70
|
+
|
|
71
|
+
type GraphState = {
|
|
72
|
+
filters: { region: string; maxResults: number };
|
|
73
|
+
lastQuery?: string;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export function CatalogAssistant() {
|
|
77
|
+
const state = useLangGraphState<GraphState>();
|
|
78
|
+
const setState = useLangGraphSetState<GraphState>();
|
|
79
|
+
const isRunning = useAuiState((s) => s.thread.isRunning);
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div className="flex h-full">
|
|
83
|
+
<aside className="w-72 border-r p-4">
|
|
84
|
+
<h2>Graph state</h2>
|
|
85
|
+
{state ? (
|
|
86
|
+
<ul>
|
|
87
|
+
<li>Region: {state.filters.region}</li>
|
|
88
|
+
<li>Max results: {state.filters.maxResults}</li>
|
|
89
|
+
{state.lastQuery && <li>Last query: {state.lastQuery}</li>}
|
|
90
|
+
</ul>
|
|
91
|
+
) : (
|
|
92
|
+
<p>No graph state yet. Ensure streamMode includes "values".</p>
|
|
93
|
+
)}
|
|
94
|
+
<button
|
|
95
|
+
type="button"
|
|
96
|
+
disabled={isRunning}
|
|
97
|
+
onClick={() =>
|
|
98
|
+
setState((prev) => ({
|
|
99
|
+
...prev,
|
|
100
|
+
filters: {
|
|
101
|
+
region: "eu",
|
|
102
|
+
maxResults: prev?.filters.maxResults ?? 10,
|
|
103
|
+
},
|
|
104
|
+
}))
|
|
105
|
+
}
|
|
106
|
+
>
|
|
107
|
+
Prefer EU region
|
|
108
|
+
</button>
|
|
109
|
+
{isRunning && <p>Agent is running…</p>}
|
|
110
|
+
</aside>
|
|
111
|
+
<main className="flex-1">
|
|
112
|
+
<Thread />
|
|
113
|
+
</main>
|
|
114
|
+
</div>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The panel tracks the latest `values` events from the graph. The button updates the local overlay immediately; that update object is merged into the run `input` on the next send so LangGraph applies it through the graph's state reducers and input schema.
|
|
120
|
+
|
|
121
|
+
## How state is synced
|
|
122
|
+
|
|
123
|
+
LangGraph state is the graph's values object. When `streamMode` includes `"values"`, each `values` event updates the client-side snapshot that `useLangGraphState` exposes.
|
|
124
|
+
|
|
125
|
+
The setter from `useLangGraphSetState` overlays that snapshot locally. On the next send, the runtime merges the update object into the run `input`, so LangGraph reduces it through the graph's state reducers and input schema.
|
|
126
|
+
|
|
127
|
+
If you supply a custom `stream` callback, the staged update is available as `config.state`. Forward it into your run input yourself:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
const runtime = useLangGraphRuntime({
|
|
131
|
+
stream: async (messages, { initialize, ...config }) => {
|
|
132
|
+
const { externalId } = await initialize();
|
|
133
|
+
if (!externalId) throw new Error("Thread not found");
|
|
134
|
+
|
|
135
|
+
const client = createClient();
|
|
136
|
+
return client.runs.stream(externalId, ASSISTANT_ID, {
|
|
137
|
+
input: {
|
|
138
|
+
...(config.state ?? {}),
|
|
139
|
+
messages,
|
|
140
|
+
},
|
|
141
|
+
streamMode: ["messages", "updates", "custom", "values"],
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The built-in path that uses the package helpers performs this merge for you. Custom `stream` callbacks must do it explicitly.
|
|
148
|
+
|
|
149
|
+
## Write-back timing
|
|
150
|
+
|
|
151
|
+
`useLangGraphSetState` is optimistic and local first. The value reaches the agent only when the next run starts. It is not a live channel into a run that is already in progress. Stage filters, preferences, or other graph fields that the next turn should apply through reducers; do not expect an in-flight run to observe mid-run setter calls.
|
|
152
|
+
|
|
153
|
+
## Relationship to other state
|
|
154
|
+
|
|
155
|
+
Keep the three state layers distinct:
|
|
156
|
+
|
|
157
|
+
- **Your app state** stays yours (React state, URL, a store). assistant-ui does not own it.
|
|
158
|
+
- **`useAuiState`** reads assistant-ui's client state (messages, composer, thread status).
|
|
159
|
+
- **`useLangGraphState` / `useLangGraphSetState`** mirror state the **agent** (your LangGraph graph) owns, synced over the wire via `values` events.
|
|
160
|
+
|
|
161
|
+
Use `useLangGraphState` and `useLangGraphSetState` for fields that live in the graph state schema. Use `useAuiState` for UI that depends on the chat thread itself. Use your own state for everything else.
|
|
162
|
+
|
|
163
|
+
## Next
|
|
164
|
+
|
|
165
|
+
<Cards>
|
|
166
|
+
<Card
|
|
167
|
+
title="Streaming"
|
|
168
|
+
description="Event handlers, message metadata, generative UI."
|
|
169
|
+
href="/docs/runtimes/langgraph/streaming"
|
|
170
|
+
/>
|
|
171
|
+
<Card
|
|
172
|
+
title="Quickstart"
|
|
173
|
+
description="From-template and manual setup paths."
|
|
174
|
+
href="/docs/runtimes/langgraph/quickstart"
|
|
175
|
+
/>
|
|
176
|
+
<Card
|
|
177
|
+
title="Generative UI"
|
|
178
|
+
description="Structured UI components emitted by your graph."
|
|
179
|
+
href="/docs/runtimes/langgraph/generative-ui"
|
|
180
|
+
/>
|
|
181
|
+
</Cards>
|
|
@@ -13,6 +13,8 @@ For authoring tools, see [Defining Tools](/docs/tools/defining-tools). For MCP s
|
|
|
13
13
|
`@assistant-ui/react-ai-sdk` posts `{ messages, system, tools }` to your route. `tools` is the map of **frontend** tools the client serialized for this request (the model needs their schemas to call them, even though they run in the browser):
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
|
+
import type { FrontendTools } from "@assistant-ui/react-ai-sdk";
|
|
17
|
+
|
|
16
18
|
const {
|
|
17
19
|
messages,
|
|
18
20
|
system,
|
|
@@ -20,7 +22,7 @@ const {
|
|
|
20
22
|
}: {
|
|
21
23
|
messages: UIMessage[];
|
|
22
24
|
system?: string;
|
|
23
|
-
tools?:
|
|
25
|
+
tools?: FrontendTools;
|
|
24
26
|
} = await req.json();
|
|
25
27
|
```
|
|
26
28
|
|
|
@@ -136,9 +138,10 @@ To let the model see a frontend tool's result and continue, configure the runtim
|
|
|
136
138
|
import { lastAssistantMessageIsCompleteWithToolCalls } from "ai";
|
|
137
139
|
|
|
138
140
|
const runtime = useChatRuntime({
|
|
139
|
-
api: "/api/chat",
|
|
140
141
|
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
|
|
141
142
|
});
|
|
142
143
|
```
|
|
143
144
|
|
|
144
|
-
|
|
145
|
+
`useChatRuntime()` targets `/api/chat` by default. To point at a different endpoint or customize requests, see [Custom transport](/docs/runtimes/ai-sdk/v7#custom-transport).
|
|
146
|
+
|
|
147
|
+
For the full AI SDK v7 backend setup — history persistence, reasoning, server-side approvals — see the [AI SDK v7 guide](/docs/runtimes/ai-sdk/v7).
|
|
@@ -125,7 +125,7 @@ import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
|
|
|
125
125
|
import toolkit from "./toolkit";
|
|
126
126
|
|
|
127
127
|
export function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
|
|
128
|
-
const runtime = useChatRuntime(
|
|
128
|
+
const runtime = useChatRuntime();
|
|
129
129
|
const aui = useAui({ tools: Tools({ toolkit }) });
|
|
130
130
|
|
|
131
131
|
return (
|
|
@@ -136,6 +136,8 @@ export function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
|
|
|
136
136
|
}
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
+
`useChatRuntime()` targets `/api/chat` by default. To point at a different endpoint or customize requests, see [Custom transport](/docs/runtimes/ai-sdk/v7#custom-transport).
|
|
140
|
+
|
|
139
141
|
</Step>
|
|
140
142
|
<Step>
|
|
141
143
|
|
|
@@ -461,6 +463,10 @@ export default defineToolkit({
|
|
|
461
463
|
});
|
|
462
464
|
```
|
|
463
465
|
|
|
466
|
+
When two MCP servers expose the same tool name, use `{ server, prefix }` on an
|
|
467
|
+
entry so the model sees distinct names such as `docs_search` and
|
|
468
|
+
`github_search`.
|
|
469
|
+
|
|
464
470
|
See [MCP](/docs/tools/mcp) for the full server-side and user-managed MCP flows.
|
|
465
471
|
|
|
466
472
|
## Advanced
|
|
@@ -173,6 +173,8 @@ action registry to let interactive nodes call back into your app. This path uses
|
|
|
173
173
|
the flat `{ "$type": ... }` node shape; the model puts an `$action` object on
|
|
174
174
|
the node, and its `type` is matched against your registered handlers.
|
|
175
175
|
|
|
176
|
+
Browse the [gallery](/gallery) to see every default vocabulary component rendered live, next to its IR JSON, generated React code, and a usage snippet.
|
|
177
|
+
|
|
176
178
|
```tsx
|
|
177
179
|
import {
|
|
178
180
|
JSONGenerativeUI,
|
|
@@ -200,8 +202,7 @@ const generative = new JSONGenerativeUI({
|
|
|
200
202
|
}
|
|
201
203
|
```
|
|
202
204
|
|
|
203
|
-
`Select`, `Input`, and `
|
|
204
|
-
fire the action. Unknown action types are ignored and warn in development.
|
|
205
|
+
`Select`, `Input`, `DatePicker`, `Checkbox`, and `RadioGroup` add the user's value as `$input` when they fire the action; `Form` and a `Card` with `asForm` set add an object keyed by each control's `name` instead. On a `Card` there is no Card-level `$action`: the collected object is dispatched through `confirm.$action`, while `cancel.$action` always fires without `$input`. Unknown action types are ignored and warn in development.
|
|
205
206
|
|
|
206
207
|
## Streaming
|
|
207
208
|
|
|
@@ -249,3 +250,60 @@ Tool-call UI is great when the agent already invoked a known tool. Generative
|
|
|
249
250
|
UI flips it: the agent _composes_ UI from a vocabulary you ship. Useful for
|
|
250
251
|
dashboards, status panels, and structured layouts — not for collecting user
|
|
251
252
|
input (use Tool UI for that).
|
|
253
|
+
|
|
254
|
+
## Slack Block Kit
|
|
255
|
+
|
|
256
|
+
`toSlackBlocks` from `@assistant-ui/react-generative-ui/slack` converts a
|
|
257
|
+
generative-UI tree into Slack's Block Kit JSON. It is pure and React-free, so
|
|
258
|
+
it runs equally well in a server action, a queue worker, or a webhook
|
|
259
|
+
handler. Components the converter doesn't recognize are skipped and reported
|
|
260
|
+
as warnings instead of throwing, and content that exceeds Slack's published
|
|
261
|
+
size and count budgets is clamped or downgraded to a simpler block rather
|
|
262
|
+
than producing an invalid payload.
|
|
263
|
+
|
|
264
|
+
```tsx
|
|
265
|
+
import { WebClient } from "@slack/web-api";
|
|
266
|
+
import { toSlackBlocks } from "@assistant-ui/react-generative-ui/slack";
|
|
267
|
+
|
|
268
|
+
const slack = new WebClient(process.env.SLACK_BOT_TOKEN);
|
|
269
|
+
|
|
270
|
+
const { blocks } = toSlackBlocks({
|
|
271
|
+
$type: "Card",
|
|
272
|
+
title: "Order #48213",
|
|
273
|
+
children: [{ $type: "Text", value: "Shipped, arriving Thursday." }],
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
await slack.chat.postMessage({
|
|
277
|
+
channel: "#orders",
|
|
278
|
+
blocks,
|
|
279
|
+
});
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Slack posts interactive elements back as a `block_actions` payload.
|
|
283
|
+
`decodeBlockAction` takes one entry from that payload's `actions` array and
|
|
284
|
+
decodes it back into the `$action` shape your tree dispatched, with the
|
|
285
|
+
user's runtime selection (a picked option, a typed value) carried under
|
|
286
|
+
`$input`. Receiving the webhook, verifying its signature, and routing the
|
|
287
|
+
decoded action to your handler stay the host app's responsibility; the
|
|
288
|
+
converter only speaks JSON in and JSON out.
|
|
289
|
+
|
|
290
|
+
The inverse direction is `fromSlackBlocks`: it maps a Block Kit payload back into vocabulary nodes, back-mapping each element's `action_id` to `$action.type` and reparsing serialized payload values. The round trip is faithful on the plain building blocks (text, images, facts, controls, tables, simple cards) and documented-lossy elsewhere: context elements all return as `Caption`, button styles beyond `primary` and `danger` are dropped, an alert's title and description come back as one description, and card layouts flatten to the fields the `card` block carries.
|
|
291
|
+
|
|
292
|
+
A few conversion caveats worth knowing:
|
|
293
|
+
|
|
294
|
+
- `Alert` has no message-surface equivalent upstream (Slack only supports it
|
|
295
|
+
in modals), so messages get a context block plus section block fallback
|
|
296
|
+
instead.
|
|
297
|
+
- `Card` and `Carousel` have tight text budgets; a card whose content
|
|
298
|
+
overflows its budget falls back to plain blocks rather than the card
|
|
299
|
+
layout.
|
|
300
|
+
- `Chart` has no Slack Block Kit mapping and is replaced by a note block.
|
|
301
|
+
- The Block Kit Builder deep link format
|
|
302
|
+
(`https://app.slack.com/block-kit-builder/#<payload>`) is an observed
|
|
303
|
+
convention, not an officially documented API.
|
|
304
|
+
|
|
305
|
+
## Microsoft Teams
|
|
306
|
+
|
|
307
|
+
The same tree converts to an Adaptive Card with `toAdaptiveCard` from `@assistant-ui/react-generative-ui/teams`: pure, React-free, pinned to Adaptive Cards 1.5 (the Teams desktop ceiling; mobile clients cap at 1.2), and total in the same way as the Slack converter, so unknown components degrade with warnings instead of failing. Interactive components encode their `$action` inside the submit payload's reserved `aui` key, and `decodeSubmitData` splits a bot's incoming `activity.value` back into the `$action` shape with the card's input values under `$input`. A root `Carousel` is an activity-level construct on Teams, so `toTeamsAttachments` returns up to ten card attachments with `attachmentLayout: "carousel"` instead of one card.
|
|
308
|
+
|
|
309
|
+
Caveats mirror the platform: Teams ignores positive and destructive action styling, TextBlock markdown is a subset (no headings, tables, or images), `Divider` and `Spacer` become `separator` and `spacing` properties on the following element, and `Chart` has no Teams mapping and is replaced by a note.
|
|
@@ -69,20 +69,32 @@ The route accepts `POST` requests with `{ method, params }` JSON bodies. Dispatc
|
|
|
69
69
|
// app/api/mcp-apps/route.ts
|
|
70
70
|
import { createMCPClient } from "@ai-sdk/mcp";
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
const serverUrls = new Map([
|
|
73
|
+
["search", process.env.SEARCH_MCP_SERVER_URL!],
|
|
74
|
+
["calendar", process.env.CALENDAR_MCP_SERVER_URL!],
|
|
75
|
+
]);
|
|
76
|
+
const fallbackServerUrl = process.env.MCP_SERVER_URL!;
|
|
77
|
+
const clientPromises = new Map<string, ReturnType<typeof createMCPClient>>();
|
|
78
|
+
|
|
79
|
+
const getClient = (serverId?: string) => {
|
|
80
|
+
const url = serverId ? serverUrls.get(serverId) : fallbackServerUrl;
|
|
81
|
+
if (!url) throw new Error(`Unknown MCP server: ${serverId}`);
|
|
82
|
+
let clientPromise = clientPromises.get(url);
|
|
83
|
+
if (!clientPromise) {
|
|
84
|
+
clientPromise = createMCPClient({
|
|
85
|
+
transport: { type: "sse", url },
|
|
86
|
+
}).catch((error) => {
|
|
87
|
+
clientPromises.delete(url);
|
|
88
|
+
throw error;
|
|
89
|
+
});
|
|
90
|
+
clientPromises.set(url, clientPromise);
|
|
91
|
+
}
|
|
80
92
|
return clientPromise;
|
|
81
93
|
};
|
|
82
94
|
|
|
83
95
|
export async function POST(req: Request) {
|
|
84
96
|
const { method, params } = await req.json();
|
|
85
|
-
const client = await getClient();
|
|
97
|
+
const client = await getClient(params?.serverId);
|
|
86
98
|
|
|
87
99
|
switch (method) {
|
|
88
100
|
case "mcp-apps/read-resource": {
|
|
@@ -109,8 +121,10 @@ export async function POST(req: Request) {
|
|
|
109
121
|
}
|
|
110
122
|
case "resources/read":
|
|
111
123
|
return Response.json(await client.readResource({ uri: params.uri }));
|
|
112
|
-
case "resources/list":
|
|
113
|
-
|
|
124
|
+
case "resources/list": {
|
|
125
|
+
const { serverId: _, ...listParams } = params ?? {};
|
|
126
|
+
return Response.json(await client.listResources(listParams));
|
|
127
|
+
}
|
|
114
128
|
default:
|
|
115
129
|
return Response.json({ error: "Unsupported method" }, { status: 400 });
|
|
116
130
|
}
|
|
@@ -119,6 +133,10 @@ export async function POST(req: Request) {
|
|
|
119
133
|
|
|
120
134
|
The renderer POSTs four method names: `mcp-apps/read-resource`, `tools/call`, `resources/read`, `resources/list`. Reject anything else server-side and apply your own auth / rate limiting in the route.
|
|
121
135
|
|
|
136
|
+
### Multiple MCP servers
|
|
137
|
+
|
|
138
|
+
When a tool part carries `mcp.app.serverId`, the renderer forwards it to the host operations as `params.serverId` so the route can select the MCP client that owns the resource or tool. The agent stack emits this routable identity in part metadata. For `@ag-ui/mcp-apps-middleware`, assistant-ui uses its configured `serverId`, falling back to `serverHash` when `serverId` is absent or empty. Match the route's map keys to whichever identity your setup emits: configure an explicit `serverId` on each middleware server, or key the map by the emitted hashes. Omitting `serverId` preserves the single-server behavior, as shown by the fallback client in the route example.
|
|
139
|
+
|
|
122
140
|
Per-name `setToolUI` registrations always win over the MCP fallback — you can still customize specific tools.
|
|
123
141
|
|
|
124
142
|
## AI SDK integration
|
|
@@ -81,7 +81,8 @@ const mcpClient = await createMCPClient({
|
|
|
81
81
|
|
|
82
82
|
In a generative toolkit, spread `defineMcpToolkit({ ... })` with one entry per
|
|
83
83
|
MCP server. The entry key names the server connection; the MCP server publishes
|
|
84
|
-
the actual tool names.
|
|
84
|
+
the actual tool names. Use a readable key because it appears in connection,
|
|
85
|
+
tool-listing, and close errors for debugging.
|
|
85
86
|
|
|
86
87
|
```tsx title="app/toolkit.tsx"
|
|
87
88
|
"use generative";
|
|
@@ -99,6 +100,62 @@ export default defineToolkit({
|
|
|
99
100
|
});
|
|
100
101
|
```
|
|
101
102
|
|
|
103
|
+
Use `{ server, disabled }` when a whole MCP server should stay configured but
|
|
104
|
+
not expose tools for the current request, such as missing credentials, feature
|
|
105
|
+
flags, or plan gating:
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
defineMcpToolkit({
|
|
109
|
+
docs: {
|
|
110
|
+
server: {
|
|
111
|
+
type: "http",
|
|
112
|
+
url: process.env.DOCS_MCP_URL!,
|
|
113
|
+
},
|
|
114
|
+
disabled: !process.env.DOCS_MCP_URL,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Use `tools` when the server should stay enabled but specific MCP tools should
|
|
120
|
+
be hidden from the model:
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
defineMcpToolkit({
|
|
124
|
+
docs: {
|
|
125
|
+
server: {
|
|
126
|
+
type: "http",
|
|
127
|
+
url: process.env.DOCS_MCP_URL!,
|
|
128
|
+
},
|
|
129
|
+
tools: {
|
|
130
|
+
deleteDocument: {
|
|
131
|
+
disabled: !userCanDelete,
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
If multiple MCP servers expose the same tool name, wrap the entry with
|
|
139
|
+
`{ server, prefix }` to give each server's tools distinct model-visible names:
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
export default defineToolkit({
|
|
143
|
+
...defineMcpToolkit({
|
|
144
|
+
docs: {
|
|
145
|
+
server: { type: "http", url: "https://docs.example.com/mcp" },
|
|
146
|
+
prefix: "docs_",
|
|
147
|
+
},
|
|
148
|
+
github: {
|
|
149
|
+
server: { type: "http", url: "https://github.example.com/mcp" },
|
|
150
|
+
prefix: "github_",
|
|
151
|
+
},
|
|
152
|
+
}),
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
If both servers publish `search`, the model receives `docs_search` and
|
|
157
|
+
`github_search` instead of an ambiguous duplicate.
|
|
158
|
+
|
|
102
159
|
Use `AISDKToolkit` in the route. It opens the MCP clients, merges their tools
|
|
103
160
|
with the rest of your toolkit, and closes them when you call `close()`:
|
|
104
161
|
|
|
@@ -319,7 +376,7 @@ import type { ReactNode } from "react";
|
|
|
319
376
|
import { toolkit } from "./GitHubIssueToolUI";
|
|
320
377
|
|
|
321
378
|
export function MyRuntimeProvider({ children }: { children: ReactNode }) {
|
|
322
|
-
const runtime = useChatRuntime(
|
|
379
|
+
const runtime = useChatRuntime();
|
|
323
380
|
const aui = useAui({ tools: Tools({ toolkit }) });
|
|
324
381
|
|
|
325
382
|
return (
|
|
@@ -330,6 +387,46 @@ export function MyRuntimeProvider({ children }: { children: ReactNode }) {
|
|
|
330
387
|
}
|
|
331
388
|
```
|
|
332
389
|
|
|
390
|
+
`useChatRuntime()` targets `/api/chat` by default. To point at a different endpoint or customize requests, see [Custom transport](/docs/runtimes/ai-sdk/v7#custom-transport).
|
|
391
|
+
|
|
392
|
+
</Step>
|
|
393
|
+
<Step>
|
|
394
|
+
|
|
395
|
+
### Require approval before an MCP tool runs
|
|
396
|
+
|
|
397
|
+
MCP tools execute on the server, so approval is a server-side tool gate, not a `humanTool()` result. Gate the call with AI SDK v7's call-level `toolApproval` option, keyed by the tool's model-visible name. The tool name stays the same, so your custom renderer or the default `ToolFallback` receives `approval` and `respondToApproval` like any other backend tool:
|
|
398
|
+
|
|
399
|
+
```ts title="app/api/chat/route.ts"
|
|
400
|
+
const tools = await mcpClient.tools();
|
|
401
|
+
|
|
402
|
+
const result = streamText({
|
|
403
|
+
model: openai("gpt-5.4-mini"),
|
|
404
|
+
messages: await convertToModelMessages(messages),
|
|
405
|
+
tools,
|
|
406
|
+
toolApproval: {
|
|
407
|
+
github_delete_repository: "user-approval",
|
|
408
|
+
},
|
|
409
|
+
onFinish: async () => {
|
|
410
|
+
await mcpClient.close();
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
With `AISDKToolkit`, pass the same `toolApproval` option alongside the tools returned by `await aiToolkit.tools(...)`; key it by the prefixed name when the entry sets one.
|
|
416
|
+
|
|
417
|
+
On the client, let the AI SDK send the recorded approval decision back to the
|
|
418
|
+
route:
|
|
419
|
+
|
|
420
|
+
```tsx title="app/components/RuntimeProvider.tsx"
|
|
421
|
+
import { lastAssistantMessageIsCompleteWithApprovalResponses } from "ai";
|
|
422
|
+
|
|
423
|
+
const runtime = useChatRuntime({
|
|
424
|
+
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,
|
|
425
|
+
});
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Use this pattern for backend-owned actions such as deleting, writing, deploying, or calling privileged MCP tools. Use `humanTool()` only when the user supplies the tool result itself. For custom approval UIs, see [Server-side approval gates](/docs/tools/tool-ui#server-side-approval-gates); for the full wire setup, see [Server-side tool approval](/docs/runtimes/ai-sdk/v7#server-side-tool-approval).
|
|
429
|
+
|
|
333
430
|
</Step>
|
|
334
431
|
<Step>
|
|
335
432
|
|
|
@@ -357,7 +454,7 @@ Start the app and trigger a tool call (e.g., ask the assistant to do something t
|
|
|
357
454
|
<Card
|
|
358
455
|
title="AI SDK runtime"
|
|
359
456
|
description="The runtime that ferries MCP tool calls to the chat UI."
|
|
360
|
-
href="/docs/runtimes/ai-sdk/
|
|
457
|
+
href="/docs/runtimes/ai-sdk/v7"
|
|
361
458
|
/>
|
|
362
459
|
<Card
|
|
363
460
|
title="Tools and tool UI"
|