@assistant-ui/mcp-docs-server 0.1.36 → 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.
Files changed (246) hide show
  1. package/.docs/organized/code-examples/waterfall.md +8 -9
  2. package/.docs/organized/code-examples/with-a2a.md +16 -11
  3. package/.docs/organized/code-examples/with-ag-ui.md +16 -11
  4. package/.docs/organized/code-examples/{with-ai-sdk-v6.md → with-ai-sdk-v7.md} +32 -21
  5. package/.docs/organized/code-examples/with-artifacts.md +17 -10
  6. package/.docs/organized/code-examples/with-assistant-transport.md +15 -8
  7. package/.docs/organized/code-examples/with-browser-extension.md +15 -8
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +17 -10
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +10 -9
  10. package/.docs/organized/code-examples/with-cloud.md +18 -13
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +17 -10
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +20 -13
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +20 -13
  14. package/.docs/organized/code-examples/with-eve.md +16 -9
  15. package/.docs/organized/code-examples/with-expo.md +26 -23
  16. package/.docs/organized/code-examples/with-external-store.md +16 -11
  17. package/.docs/organized/code-examples/with-ffmpeg.md +18 -13
  18. package/.docs/organized/code-examples/with-generative-ui.md +20 -15
  19. package/.docs/organized/code-examples/with-google-adk.md +16 -9
  20. package/.docs/organized/code-examples/with-heat-graph.md +8 -9
  21. package/.docs/organized/code-examples/with-image-generation.md +17 -10
  22. package/.docs/organized/code-examples/with-interactables.md +19 -15
  23. package/.docs/organized/code-examples/with-langchain.md +17 -10
  24. package/.docs/organized/code-examples/with-langgraph.md +17 -10
  25. package/.docs/organized/code-examples/with-livekit.md +21 -14
  26. package/.docs/organized/code-examples/with-mcp.md +40 -18
  27. package/.docs/organized/code-examples/with-opencode.md +22 -17
  28. package/.docs/organized/code-examples/with-pi.md +47 -12
  29. package/.docs/organized/code-examples/with-react-hook-form.md +19 -14
  30. package/.docs/organized/code-examples/with-react-ink-web.md +6 -6
  31. package/.docs/organized/code-examples/with-react-ink.md +2 -2
  32. package/.docs/organized/code-examples/with-react-router.md +20 -15
  33. package/.docs/organized/code-examples/with-resumable-stream.md +19 -12
  34. package/.docs/organized/code-examples/with-store.md +8 -9
  35. package/.docs/organized/code-examples/with-tanstack.md +17 -11
  36. package/.docs/organized/code-examples/with-tap-runtime.md +16 -11
  37. package/.docs/organized/code-examples/with-virtualized-thread.md +17 -12
  38. package/.docs/raw/docs/(docs)/base-ui.mdx +39 -0
  39. package/.docs/raw/docs/(docs)/cli.mdx +18 -2
  40. package/.docs/raw/docs/(docs)/installation.mdx +15 -1
  41. package/.docs/raw/docs/(docs)/rtl.mdx +2 -4
  42. package/.docs/raw/docs/(reference)/api-reference/generative-ui/actions.mdx +56 -0
  43. package/.docs/raw/docs/(reference)/api-reference/generative-ui/components.mdx +86 -0
  44. package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +19 -1
  45. package/.docs/raw/docs/(reference)/api-reference/generative-ui/json-generative-ui.mdx +42 -0
  46. package/.docs/raw/docs/(reference)/api-reference/generative-ui/rendering.mdx +53 -2
  47. package/.docs/raw/docs/(reference)/api-reference/generative-ui/slack.mdx +81 -0
  48. package/.docs/raw/docs/(reference)/api-reference/generative-ui/teams.mdx +86 -0
  49. package/.docs/raw/docs/(reference)/api-reference/generative-ui/tokens.mdx +62 -0
  50. package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +4 -1
  51. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +4 -2
  52. package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +37 -0
  53. package/.docs/raw/docs/(reference)/api-reference/overview.mdx +1 -0
  54. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +29 -29
  55. package/.docs/raw/docs/(reference)/api-reference/primitives/composition.mdx +1 -0
  56. package/.docs/raw/docs/(reference)/api-reference/primitives/index.mdx +3 -0
  57. package/.docs/raw/docs/(reference)/api-reference/primitives/selection-toolbar.mdx +2 -0
  58. package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +0 -2
  59. package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +12 -9
  60. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +4 -0
  61. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +7 -1
  62. package/.docs/raw/docs/cloud/langgraph.mdx +4 -2
  63. package/.docs/raw/docs/guides/chatgpt-subscription.mdx +108 -0
  64. package/.docs/raw/docs/guides/dictation.mdx +185 -257
  65. package/.docs/raw/docs/guides/index.mdx +10 -0
  66. package/.docs/raw/docs/guides/mentions.mdx +31 -3
  67. package/.docs/raw/docs/guides/resumable-streams.mdx +12 -1
  68. package/.docs/raw/docs/guides/speech.mdx +47 -29
  69. package/.docs/raw/docs/guides/suggestions.mdx +70 -1
  70. package/.docs/raw/docs/guides/voice.mdx +197 -267
  71. package/.docs/raw/docs/ink/primitives.mdx +35 -1
  72. package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +13 -7
  73. package/.docs/raw/docs/integrations/frameworks/cloudflare-agents/overview.mdx +3 -3
  74. package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +1 -1
  75. package/.docs/raw/docs/integrations/frameworks/mastra/overview.mdx +3 -3
  76. package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +1 -1
  77. package/.docs/raw/docs/integrations/gateways/index.mdx +2 -2
  78. package/.docs/raw/docs/integrations/index.mdx +1 -1
  79. package/.docs/raw/docs/integrations/observability/helicone.mdx +1 -1
  80. package/.docs/raw/docs/integrations/observability/langfuse.mdx +1 -1
  81. package/.docs/raw/docs/integrations/observability/langsmith.mdx +3 -3
  82. package/.docs/raw/docs/migrations/index.mdx +50 -0
  83. package/.docs/raw/docs/migrations/react-langgraph-v0-7.mdx +2 -2
  84. package/.docs/raw/docs/migrations/toolkit-tools.mdx +4 -2
  85. package/.docs/raw/docs/primitives/chain-of-thought.mdx +6 -1
  86. package/.docs/raw/docs/primitives/composer.mdx +16 -0
  87. package/.docs/raw/docs/primitives/selection-toolbar.mdx +25 -0
  88. package/.docs/raw/docs/primitives/thread-list.mdx +30 -6
  89. package/.docs/raw/docs/react-native/primitives.mdx +23 -0
  90. package/.docs/raw/docs/runtimes/a2a/quickstart.mdx +1 -1
  91. package/.docs/raw/docs/runtimes/ag-ui/agent-state.mdx +124 -0
  92. package/.docs/raw/docs/runtimes/ag-ui/quickstart.mdx +1 -1
  93. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +13 -2
  94. package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +13 -4
  95. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +11 -11
  96. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +5 -5
  97. package/.docs/raw/docs/runtimes/ai-sdk/{v6.mdx → v6-legacy.mdx} +8 -6
  98. package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +717 -0
  99. package/.docs/raw/docs/runtimes/concepts/adapters.mdx +1 -1
  100. package/.docs/raw/docs/runtimes/concepts/architecture.mdx +1 -1
  101. package/.docs/raw/docs/runtimes/custom/data-stream.mdx +10 -19
  102. package/.docs/raw/docs/runtimes/custom/external-store.mdx +1 -1
  103. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +2 -0
  104. package/.docs/raw/docs/runtimes/eve/overview.mdx +1 -1
  105. package/.docs/raw/docs/runtimes/google-adk/quickstart.mdx +1 -1
  106. package/.docs/raw/docs/runtimes/langchain.mdx +2 -2
  107. package/.docs/raw/docs/runtimes/langgraph/agent-state.mdx +181 -0
  108. package/.docs/raw/docs/runtimes/opencode/hooks.mdx +1 -1
  109. package/.docs/raw/docs/tools/backend.mdx +6 -3
  110. package/.docs/raw/docs/tools/defining-tools.mdx +12 -1
  111. package/.docs/raw/docs/tools/generative-ui.mdx +95 -0
  112. package/.docs/raw/docs/tools/mcp-apps.mdx +37 -10
  113. package/.docs/raw/docs/tools/mcp.mdx +105 -3
  114. package/.docs/raw/docs/tools/tool-ui.mdx +6 -4
  115. package/.docs/raw/docs/tools/user-managed-mcp.mdx +41 -4
  116. package/.docs/raw/docs/ui/accordion.mdx +16 -10
  117. package/.docs/raw/docs/ui/assistant-modal.mdx +8 -4
  118. package/.docs/raw/docs/ui/attachment.mdx +5 -1
  119. package/.docs/raw/docs/ui/badge.mdx +23 -12
  120. package/.docs/raw/docs/ui/follow-up-suggestions.mdx +80 -0
  121. package/.docs/raw/docs/ui/model-selector.mdx +69 -6
  122. package/.docs/raw/docs/ui/select.mdx +22 -14
  123. package/.docs/raw/docs/ui/sources.mdx +1 -1
  124. package/.docs/raw/docs/ui/tabs.mdx +25 -14
  125. package/.docs/raw/docs/utilities/heat-graph.mdx +2 -2
  126. package/README.md +1 -1
  127. package/dist/constants.d.ts +2 -1
  128. package/dist/constants.d.ts.map +1 -1
  129. package/dist/constants.js +2 -1
  130. package/dist/constants.js.map +1 -1
  131. package/dist/index.d.ts +0 -1
  132. package/dist/index.d.ts.map +1 -1
  133. package/dist/index.js +51 -0
  134. package/dist/index.js.map +1 -1
  135. package/dist/prepare-docs/code-examples.d.ts.map +1 -1
  136. package/dist/prepare-docs/copy-raw.d.ts.map +1 -1
  137. package/dist/prepare-docs/copy-raw.js +0 -4
  138. package/dist/prepare-docs/copy-raw.js.map +1 -1
  139. package/dist/prompts/xulux-playground.d.ts +12 -0
  140. package/dist/prompts/xulux-playground.d.ts.map +1 -0
  141. package/dist/prompts/xulux-playground.js +33 -0
  142. package/dist/prompts/xulux-playground.js.map +1 -0
  143. package/dist/tools/docs.d.ts +2 -4
  144. package/dist/tools/docs.d.ts.map +1 -1
  145. package/dist/tools/docs.js +38 -10
  146. package/dist/tools/docs.js.map +1 -1
  147. package/dist/tools/examples.d.ts +5 -5
  148. package/dist/tools/examples.d.ts.map +1 -1
  149. package/dist/tools/examples.js +10 -7
  150. package/dist/tools/examples.js.map +1 -1
  151. package/dist/tools/resources.d.ts +6 -0
  152. package/dist/tools/resources.d.ts.map +1 -0
  153. package/dist/tools/resources.js +74 -0
  154. package/dist/tools/resources.js.map +1 -0
  155. package/dist/tools/search.d.ts +30 -0
  156. package/dist/tools/search.d.ts.map +1 -0
  157. package/dist/tools/search.js +39 -0
  158. package/dist/tools/search.js.map +1 -0
  159. package/dist/tools/tests/test-setup.d.ts.map +1 -1
  160. package/dist/tools/tests/test-setup.js +7 -1
  161. package/dist/tools/tests/test-setup.js.map +1 -1
  162. package/dist/tools/xulux-templates.d.ts +72 -0
  163. package/dist/tools/xulux-templates.d.ts.map +1 -0
  164. package/dist/tools/xulux-templates.js +82 -0
  165. package/dist/tools/xulux-templates.js.map +1 -0
  166. package/dist/utils/cache.d.ts +5 -0
  167. package/dist/utils/cache.d.ts.map +1 -0
  168. package/dist/utils/cache.js +18 -0
  169. package/dist/utils/cache.js.map +1 -0
  170. package/dist/utils/logger.d.ts.map +1 -1
  171. package/dist/utils/mcp-format.d.ts +1 -0
  172. package/dist/utils/mcp-format.d.ts.map +1 -1
  173. package/dist/utils/mcp-format.js +7 -4
  174. package/dist/utils/mcp-format.js.map +1 -1
  175. package/dist/utils/mdx.d.ts +2 -1
  176. package/dist/utils/mdx.d.ts.map +1 -1
  177. package/dist/utils/mdx.js +19 -2
  178. package/dist/utils/mdx.js.map +1 -1
  179. package/dist/utils/paths.d.ts +2 -1
  180. package/dist/utils/paths.d.ts.map +1 -1
  181. package/dist/utils/paths.js +20 -1
  182. package/dist/utils/paths.js.map +1 -1
  183. package/dist/utils/search.d.ts +10 -0
  184. package/dist/utils/search.d.ts.map +1 -0
  185. package/dist/utils/search.js +97 -0
  186. package/dist/utils/search.js.map +1 -0
  187. package/dist/utils/security.d.ts.map +1 -1
  188. package/dist/xulux/catalog-client.d.ts +14 -0
  189. package/dist/xulux/catalog-client.d.ts.map +1 -0
  190. package/dist/xulux/catalog-client.js +67 -0
  191. package/dist/xulux/catalog-client.js.map +1 -0
  192. package/dist/xulux/fallback-catalog.d.ts +7 -0
  193. package/dist/xulux/fallback-catalog.d.ts.map +1 -0
  194. package/dist/xulux/fallback-catalog.js +47 -0
  195. package/dist/xulux/fallback-catalog.js.map +1 -0
  196. package/dist/xulux/fetch-sandbox.d.ts +5 -0
  197. package/dist/xulux/fetch-sandbox.d.ts.map +1 -0
  198. package/dist/xulux/fetch-sandbox.js +40 -0
  199. package/dist/xulux/fetch-sandbox.js.map +1 -0
  200. package/dist/xulux/template-service.d.ts +84 -0
  201. package/dist/xulux/template-service.d.ts.map +1 -0
  202. package/dist/xulux/template-service.js +223 -0
  203. package/dist/xulux/template-service.js.map +1 -0
  204. package/dist/xulux/types.d.ts +55 -0
  205. package/dist/xulux/types.d.ts.map +1 -0
  206. package/dist/xulux/types.js +6 -0
  207. package/dist/xulux/types.js.map +1 -0
  208. package/package.json +5 -5
  209. package/src/constants.ts +2 -0
  210. package/src/index.ts +67 -0
  211. package/src/prepare-docs/copy-raw.ts +0 -5
  212. package/src/prompts/xulux-playground.ts +36 -0
  213. package/src/tools/docs.ts +52 -4
  214. package/src/tools/examples.ts +18 -11
  215. package/src/tools/resources.ts +114 -0
  216. package/src/tools/search.ts +46 -0
  217. package/src/tools/tests/completions.test.ts +46 -0
  218. package/src/tools/tests/directory-size-cap.test.ts +50 -0
  219. package/src/tools/tests/docs.test.ts +20 -0
  220. package/src/tools/tests/examples.test.ts +5 -5
  221. package/src/tools/tests/listings-cache.test.ts +19 -0
  222. package/src/tools/tests/mcp-protocol.test.ts +92 -1
  223. package/src/tools/tests/resources.test.ts +102 -0
  224. package/src/tools/tests/search.test.ts +37 -0
  225. package/src/tools/tests/test-setup.ts +10 -0
  226. package/src/tools/tests/xulux-templates.test.ts +262 -0
  227. package/src/tools/xulux-templates.ts +141 -0
  228. package/src/utils/cache.ts +20 -0
  229. package/src/utils/mcp-format.ts +8 -6
  230. package/src/utils/mdx.ts +21 -1
  231. package/src/utils/paths.ts +25 -0
  232. package/src/utils/search.ts +131 -0
  233. package/src/utils/tests/cache.test.ts +51 -0
  234. package/src/utils/tests/mcp-format.test.ts +22 -0
  235. package/src/utils/tests/security.test.ts +1 -1
  236. package/src/xulux/catalog-client.ts +105 -0
  237. package/src/xulux/fallback-catalog.ts +63 -0
  238. package/src/xulux/fetch-sandbox.ts +56 -0
  239. package/src/xulux/template-service.ts +406 -0
  240. package/src/xulux/types.ts +60 -0
  241. package/.docs/raw/blog/2024-07-29-hello/index.mdx +0 -64
  242. package/.docs/raw/blog/2024-09-11/index.mdx +0 -10
  243. package/.docs/raw/blog/2024-12-15/index.mdx +0 -10
  244. package/.docs/raw/blog/2025-01-31-changelog/index.mdx +0 -127
  245. package/.docs/raw/blog/2026-03-launch-week/index.mdx +0 -258
  246. 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/v6) for the full pattern.
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 v6 (`useChat`) |
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 via its `protocol` option:
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 | Default | Matching backend |
26
+ | Protocol | Detection | Matching backend |
25
27
  | --- | --- | --- |
26
- | `"ui-message-stream"` | yes | AI SDK v6's `result.toUIMessageStreamResponse()` (SSE) |
27
- | `"data-stream"` | no | `createAssistantStreamResponse` from `assistant-stream`, or AI SDK v4's `toDataStreamResponse()` (Vercel data stream v1) |
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
- Pairing the default decoder with a data stream backend throws `Stream ended abruptly without receiving [DONE] marker` at flush; pass `protocol: "data-stream"` to switch decoders.
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
 
@@ -3,7 +3,7 @@ title: Quickstart
3
3
  description: Minimal API route and client setup with createAdkApiRoute.
4
4
  ---
5
5
 
6
- Four steps to a working ADK chat. Assumes you have already installed the package and have ADK ready on the server side; if not, start at [overview](/docs/runtimes/google-adk).
6
+ Four steps to a working ADK chat. Assumes you have already installed the package and have ADK ready on the server side; if not, start at [overview](/docs/runtimes/google-adk/overview).
7
7
 
8
8
  <Steps>
9
9
  <Step>
@@ -5,7 +5,7 @@ description: Use LangChain's useStream hook with a React chat UI through assista
5
5
 
6
6
  import { LangGraphIcon } from "@/components/icons/langgraph";
7
7
 
8
- `@assistant-ui/react-langchain` wraps [`useStream`](https://docs.langchain.com/oss/javascript/langgraph-sdk/react-stream) from `@langchain/react` and exposes it as an assistant-ui runtime. It targets the same backend as [`@assistant-ui/react-langgraph`](/docs/runtimes/langgraph) (LangGraph Cloud) but at a higher level, delegating stream plumbing to the upstream hook.
8
+ `@assistant-ui/react-langchain` wraps [`useStream`](https://docs.langchain.com/oss/javascript/langgraph-sdk/react-stream) from `@langchain/react` and exposes it as an assistant-ui runtime. It targets the same backend as [`@assistant-ui/react-langgraph`](/docs/runtimes/langgraph/overview) (LangGraph Cloud) but at a higher level, delegating stream plumbing to the upstream hook.
9
9
 
10
10
  ## When to use it
11
11
 
@@ -891,7 +891,7 @@ Regenerate works without configuration. Clicking regenerate resolves the server
891
891
  icon={<LangGraphIcon width={20} height={20} className="text-[#1C3C3C] dark:text-[#5b9595]" />}
892
892
  title="LangGraph"
893
893
  description="The original LangGraph adapter, built on the raw SDK."
894
- href="/docs/runtimes/langgraph"
894
+ href="/docs/runtimes/langgraph/overview"
895
895
  />
896
896
  <Card
897
897
  title="ExternalStoreRuntime"
@@ -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>
@@ -175,7 +175,7 @@ These are only needed for advanced cases; most apps should use `useOpenCodeRunti
175
175
  icon={<OpenCodeIcon width={20} height={20} />}
176
176
  title="OpenCode overview"
177
177
  description="What OpenCode is and when to pick it."
178
- href="/docs/runtimes/opencode"
178
+ href="/docs/runtimes/opencode/overview"
179
179
  />
180
180
  <Card
181
181
  title="Quickstart"
@@ -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?: Record<string, { description?: string; parameters: JSONSchema7 }>;
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
- For the full AI SDK v6 backend setup — history persistence, reasoning, server-side approvals — see the [AI SDK v6 guide](/docs/runtimes/ai-sdk/v6).
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({ api: "/api/chat" });
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
 
@@ -427,6 +429,11 @@ The compiler checks static tool names across inline entries and compiler-visible
427
429
  spreads. If two fragments define the same tool name, the build warns that
428
430
  JavaScript object spread will keep the later tool definition.
429
431
 
432
+ If the compiler reports that `tool "weather" cannot be makeTool()`, that entry
433
+ came from an opaque factory call. Rewrite it as an inline tool object, or spread
434
+ a compiler-visible `defineToolkit(...)` / `defineMcpToolkit(...)` fragment
435
+ instead.
436
+
430
437
  ### Add MCP server tools
431
438
 
432
439
  `defineMcpToolkit` exposes tools from an MCP server. For an MCP-only toolkit, export it directly:
@@ -456,6 +463,10 @@ export default defineToolkit({
456
463
  });
457
464
  ```
458
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
+
459
470
  See [MCP](/docs/tools/mcp) for the full server-side and user-managed MCP flows.
460
471
 
461
472
  ## Advanced
@@ -166,6 +166,44 @@ type GenerativeUISpec = {
166
166
  The spec is plain JSON — easy for any agent to emit, and easy to validate
167
167
  on the server before delivery.
168
168
 
169
+ ## Actions with `JSONGenerativeUI`
170
+
171
+ When you expose a component library through `new JSONGenerativeUI(...)`, pass an
172
+ action registry to let interactive nodes call back into your app. This path uses
173
+ the flat `{ "$type": ... }` node shape; the model puts an `$action` object on
174
+ the node, and its `type` is matched against your registered handlers.
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
+
178
+ ```tsx
179
+ import {
180
+ JSONGenerativeUI,
181
+ createActionRegistry,
182
+ defaultGenerativeUILibrary,
183
+ } from "@assistant-ui/react-generative-ui";
184
+
185
+ const actions = createActionRegistry({
186
+ purchase: async ({ payload }) => {
187
+ await checkout(payload);
188
+ },
189
+ });
190
+
191
+ const generative = new JSONGenerativeUI({
192
+ library: defaultGenerativeUILibrary,
193
+ actions,
194
+ });
195
+ ```
196
+
197
+ ```json
198
+ {
199
+ "$type": "Button",
200
+ "label": "Buy",
201
+ "$action": { "type": "purchase", "sku": "pro-plan" }
202
+ }
203
+ ```
204
+
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.
206
+
169
207
  ## Streaming
170
208
 
171
209
  When a message contains native `generative-ui` parts whose `spec` updates
@@ -212,3 +250,60 @@ Tool-call UI is great when the agent already invoked a known tool. Generative
212
250
  UI flips it: the agent _composes_ UI from a vocabulary you ship. Useful for
213
251
  dashboards, status panels, and structured layouts — not for collecting user
214
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.
@@ -24,6 +24,12 @@ Capability presence is determined at mount time by which handlers you provide. U
24
24
 
25
25
  The renderer talks to a backend route you expose — the MCP client lives server-side so credentials and transport stay out of the browser. The route receives `{ method, params }` POSTs and dispatches to your MCP client.
26
26
 
27
+ ### Install the MCP client
28
+
29
+ Install the AI SDK MCP client package used by the route handler:
30
+
31
+ <InstallCommand npm={["@ai-sdk/mcp"]} />
32
+
27
33
  ### Client
28
34
 
29
35
  Compose `McpAppRenderer({...})` into your `Tools` resource. Provide `host.url` pointing at your route. Any tool-call part carrying `mcp.app` metadata renders the MCP App widget automatically.
@@ -61,19 +67,34 @@ The route accepts `POST` requests with `{ method, params }` JSON bodies. Dispatc
61
67
 
62
68
  ```ts
63
69
  // app/api/mcp-apps/route.ts
64
- import { experimental_createMCPClient } from "ai";
65
-
66
- let clientPromise: ReturnType<typeof experimental_createMCPClient> | undefined;
67
- const getClient = () => {
68
- clientPromise ??= experimental_createMCPClient({
69
- transport: { type: "sse", url: process.env.MCP_SERVER_URL! },
70
- });
70
+ import { createMCPClient } from "@ai-sdk/mcp";
71
+
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
+ }
71
92
  return clientPromise;
72
93
  };
73
94
 
74
95
  export async function POST(req: Request) {
75
96
  const { method, params } = await req.json();
76
- const client = await getClient();
97
+ const client = await getClient(params?.serverId);
77
98
 
78
99
  switch (method) {
79
100
  case "mcp-apps/read-resource": {
@@ -100,8 +121,10 @@ export async function POST(req: Request) {
100
121
  }
101
122
  case "resources/read":
102
123
  return Response.json(await client.readResource({ uri: params.uri }));
103
- case "resources/list":
104
- return Response.json(await client.listResources(params));
124
+ case "resources/list": {
125
+ const { serverId: _, ...listParams } = params ?? {};
126
+ return Response.json(await client.listResources(listParams));
127
+ }
105
128
  default:
106
129
  return Response.json({ error: "Unsupported method" }, { status: 400 });
107
130
  }
@@ -110,6 +133,10 @@ export async function POST(req: Request) {
110
133
 
111
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.
112
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
+
113
140
  Per-name `setToolUI` registrations always win over the MCP fallback — you can still customize specific tools.
114
141
 
115
142
  ## AI SDK integration