@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.
Files changed (207) 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 +27 -24
  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 +15 -8
  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 +25 -12
  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 +16 -10
  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 +17 -1
  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 +3 -1
  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 +10 -4
  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/observability/helicone.mdx +1 -1
  79. package/.docs/raw/docs/integrations/observability/langfuse.mdx +1 -1
  80. package/.docs/raw/docs/integrations/observability/langsmith.mdx +1 -1
  81. package/.docs/raw/docs/migrations/index.mdx +50 -0
  82. package/.docs/raw/docs/migrations/toolkit-tools.mdx +4 -2
  83. package/.docs/raw/docs/primitives/chain-of-thought.mdx +6 -1
  84. package/.docs/raw/docs/primitives/composer.mdx +16 -0
  85. package/.docs/raw/docs/primitives/selection-toolbar.mdx +25 -0
  86. package/.docs/raw/docs/react-native/primitives.mdx +23 -0
  87. package/.docs/raw/docs/runtimes/ag-ui/agent-state.mdx +124 -0
  88. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +10 -1
  89. package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +13 -4
  90. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +11 -11
  91. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +5 -5
  92. package/.docs/raw/docs/runtimes/ai-sdk/{v6.mdx → v6-legacy.mdx} +8 -6
  93. package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +717 -0
  94. package/.docs/raw/docs/runtimes/concepts/adapters.mdx +1 -1
  95. package/.docs/raw/docs/runtimes/concepts/architecture.mdx +1 -1
  96. package/.docs/raw/docs/runtimes/custom/data-stream.mdx +10 -19
  97. package/.docs/raw/docs/runtimes/custom/external-store.mdx +1 -1
  98. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +2 -0
  99. package/.docs/raw/docs/runtimes/eve/overview.mdx +1 -1
  100. package/.docs/raw/docs/runtimes/langgraph/agent-state.mdx +181 -0
  101. package/.docs/raw/docs/tools/backend.mdx +6 -3
  102. package/.docs/raw/docs/tools/defining-tools.mdx +7 -1
  103. package/.docs/raw/docs/tools/generative-ui.mdx +60 -2
  104. package/.docs/raw/docs/tools/mcp-apps.mdx +29 -11
  105. package/.docs/raw/docs/tools/mcp.mdx +100 -3
  106. package/.docs/raw/docs/tools/tool-ui.mdx +6 -4
  107. package/.docs/raw/docs/tools/user-managed-mcp.mdx +26 -3
  108. package/.docs/raw/docs/ui/accordion.mdx +16 -10
  109. package/.docs/raw/docs/ui/assistant-modal.mdx +8 -4
  110. package/.docs/raw/docs/ui/attachment.mdx +5 -1
  111. package/.docs/raw/docs/ui/badge.mdx +23 -12
  112. package/.docs/raw/docs/ui/follow-up-suggestions.mdx +2 -2
  113. package/.docs/raw/docs/ui/model-selector.mdx +32 -2
  114. package/.docs/raw/docs/ui/select.mdx +22 -14
  115. package/.docs/raw/docs/ui/sources.mdx +1 -1
  116. package/.docs/raw/docs/ui/tabs.mdx +25 -14
  117. package/.docs/raw/docs/utilities/heat-graph.mdx +2 -2
  118. package/dist/constants.d.ts.map +1 -1
  119. package/dist/index.d.ts +0 -1
  120. package/dist/index.d.ts.map +1 -1
  121. package/dist/index.js +39 -0
  122. package/dist/index.js.map +1 -1
  123. package/dist/prepare-docs/code-examples.d.ts.map +1 -1
  124. package/dist/prepare-docs/copy-raw.d.ts.map +1 -1
  125. package/dist/prompts/xulux-playground.d.ts +12 -0
  126. package/dist/prompts/xulux-playground.d.ts.map +1 -0
  127. package/dist/prompts/xulux-playground.js +33 -0
  128. package/dist/prompts/xulux-playground.js.map +1 -0
  129. package/dist/tools/docs.d.ts +2 -4
  130. package/dist/tools/docs.d.ts.map +1 -1
  131. package/dist/tools/docs.js +24 -8
  132. package/dist/tools/docs.js.map +1 -1
  133. package/dist/tools/examples.d.ts +2 -4
  134. package/dist/tools/examples.d.ts.map +1 -1
  135. package/dist/tools/examples.js +9 -6
  136. package/dist/tools/examples.js.map +1 -1
  137. package/dist/tools/resources.d.ts +0 -1
  138. package/dist/tools/resources.d.ts.map +1 -1
  139. package/dist/tools/search.d.ts +2 -5
  140. package/dist/tools/search.d.ts.map +1 -1
  141. package/dist/tools/tests/test-setup.d.ts.map +1 -1
  142. package/dist/tools/tests/test-setup.js +5 -1
  143. package/dist/tools/tests/test-setup.js.map +1 -1
  144. package/dist/tools/xulux-templates.d.ts +72 -0
  145. package/dist/tools/xulux-templates.d.ts.map +1 -0
  146. package/dist/tools/xulux-templates.js +82 -0
  147. package/dist/tools/xulux-templates.js.map +1 -0
  148. package/dist/utils/cache.d.ts +5 -0
  149. package/dist/utils/cache.d.ts.map +1 -0
  150. package/dist/utils/cache.js +18 -0
  151. package/dist/utils/cache.js.map +1 -0
  152. package/dist/utils/logger.d.ts.map +1 -1
  153. package/dist/utils/mcp-format.d.ts +1 -0
  154. package/dist/utils/mcp-format.d.ts.map +1 -1
  155. package/dist/utils/mcp-format.js +7 -4
  156. package/dist/utils/mcp-format.js.map +1 -1
  157. package/dist/utils/mdx.d.ts.map +1 -1
  158. package/dist/utils/paths.d.ts +1 -1
  159. package/dist/utils/paths.d.ts.map +1 -1
  160. package/dist/utils/paths.js +3 -1
  161. package/dist/utils/paths.js.map +1 -1
  162. package/dist/utils/search.d.ts.map +1 -1
  163. package/dist/utils/security.d.ts.map +1 -1
  164. package/dist/xulux/catalog-client.d.ts +14 -0
  165. package/dist/xulux/catalog-client.d.ts.map +1 -0
  166. package/dist/xulux/catalog-client.js +67 -0
  167. package/dist/xulux/catalog-client.js.map +1 -0
  168. package/dist/xulux/fallback-catalog.d.ts +7 -0
  169. package/dist/xulux/fallback-catalog.d.ts.map +1 -0
  170. package/dist/xulux/fallback-catalog.js +47 -0
  171. package/dist/xulux/fallback-catalog.js.map +1 -0
  172. package/dist/xulux/fetch-sandbox.d.ts +5 -0
  173. package/dist/xulux/fetch-sandbox.d.ts.map +1 -0
  174. package/dist/xulux/fetch-sandbox.js +40 -0
  175. package/dist/xulux/fetch-sandbox.js.map +1 -0
  176. package/dist/xulux/template-service.d.ts +84 -0
  177. package/dist/xulux/template-service.d.ts.map +1 -0
  178. package/dist/xulux/template-service.js +223 -0
  179. package/dist/xulux/template-service.js.map +1 -0
  180. package/dist/xulux/types.d.ts +55 -0
  181. package/dist/xulux/types.d.ts.map +1 -0
  182. package/dist/xulux/types.js +6 -0
  183. package/dist/xulux/types.js.map +1 -0
  184. package/package.json +5 -5
  185. package/src/index.ts +53 -0
  186. package/src/prompts/xulux-playground.ts +36 -0
  187. package/src/tools/docs.ts +25 -3
  188. package/src/tools/examples.ts +15 -10
  189. package/src/tools/tests/docs.test.ts +20 -0
  190. package/src/tools/tests/examples.test.ts +5 -5
  191. package/src/tools/tests/listings-cache.test.ts +19 -0
  192. package/src/tools/tests/mcp-protocol.test.ts +81 -1
  193. package/src/tools/tests/test-setup.ts +8 -0
  194. package/src/tools/tests/xulux-templates.test.ts +262 -0
  195. package/src/tools/xulux-templates.ts +141 -0
  196. package/src/utils/cache.ts +20 -0
  197. package/src/utils/mcp-format.ts +8 -6
  198. package/src/utils/paths.ts +4 -1
  199. package/src/utils/tests/cache.test.ts +51 -0
  200. package/src/utils/tests/mcp-format.test.ts +22 -0
  201. package/src/utils/tests/security.test.ts +1 -1
  202. package/src/xulux/catalog-client.ts +105 -0
  203. package/src/xulux/fallback-catalog.ts +63 -0
  204. package/src/xulux/fetch-sandbox.ts +56 -0
  205. package/src/xulux/template-service.ts +406 -0
  206. package/src/xulux/types.ts +60 -0
  207. 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
 
@@ -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?: 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
 
@@ -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 `DatePicker` add the user's value as `$input` when they
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
- let clientPromise: ReturnType<typeof createMCPClient> | undefined;
73
- const getClient = () => {
74
- clientPromise ??= createMCPClient({
75
- transport: { type: "sse", url: process.env.MCP_SERVER_URL! },
76
- }).catch((error) => {
77
- clientPromise = undefined;
78
- throw error;
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
- 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
+ }
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({ api: "/api/chat" });
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/v6"
457
+ href="/docs/runtimes/ai-sdk/v7"
361
458
  />
362
459
  <Card
363
460
  title="Tools and tool UI"