@assistant-ui/mcp-docs-server 0.1.33 → 0.1.35

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 (102) hide show
  1. package/.docs/organized/code-examples/waterfall.md +7 -7
  2. package/.docs/organized/code-examples/with-a2a.md +8 -8
  3. package/.docs/organized/code-examples/with-ag-ui.md +12 -12
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +10 -10
  5. package/.docs/organized/code-examples/with-artifacts.md +40 -34
  6. package/.docs/organized/code-examples/with-assistant-transport.md +11 -11
  7. package/.docs/organized/code-examples/with-browser-extension.md +9 -9
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +72 -51
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +10 -10
  10. package/.docs/organized/code-examples/with-cloud.md +10 -10
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +10 -10
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +12 -12
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +12 -12
  14. package/.docs/organized/code-examples/with-expo.md +66 -31
  15. package/.docs/organized/code-examples/with-external-store.md +8 -8
  16. package/.docs/organized/code-examples/with-ffmpeg.md +13 -13
  17. package/.docs/organized/code-examples/with-generative-ui.md +98 -368
  18. package/.docs/organized/code-examples/with-google-adk.md +9 -9
  19. package/.docs/organized/code-examples/with-heat-graph.md +7 -7
  20. package/.docs/organized/code-examples/with-image-generation.md +10 -10
  21. package/.docs/organized/code-examples/with-interactables.md +10 -10
  22. package/.docs/organized/code-examples/with-langchain.md +10 -10
  23. package/.docs/organized/code-examples/with-langgraph.md +33 -29
  24. package/.docs/organized/code-examples/with-livekit.md +12 -12
  25. package/.docs/organized/code-examples/with-mcp.md +11 -11
  26. package/.docs/organized/code-examples/with-opencode.md +109 -583
  27. package/.docs/organized/code-examples/with-pi.md +2044 -0
  28. package/.docs/organized/code-examples/with-react-hook-form.md +11 -11
  29. package/.docs/organized/code-examples/with-react-ink-web.md +691 -0
  30. package/.docs/organized/code-examples/with-react-ink.md +309 -102
  31. package/.docs/organized/code-examples/with-react-router.md +14 -14
  32. package/.docs/organized/code-examples/with-resumable-stream.md +11 -11
  33. package/.docs/organized/code-examples/with-store.md +70 -66
  34. package/.docs/organized/code-examples/with-tanstack.md +25 -11
  35. package/.docs/organized/code-examples/with-tap-runtime.md +8 -8
  36. package/.docs/organized/code-examples/with-virtualized-thread.md +656 -0
  37. package/.docs/raw/docs/(docs)/architecture.mdx +65 -53
  38. package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +1 -1
  39. package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +3 -0
  40. package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +44 -1
  41. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +14 -3
  42. package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +1 -1
  43. package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +2 -23
  44. package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +22 -0
  45. package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +93 -9
  46. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +21 -86
  47. package/.docs/raw/docs/guides/chain-of-thought.mdx +1 -1
  48. package/.docs/raw/docs/guides/index.mdx +3 -0
  49. package/.docs/raw/docs/guides/input-history.mdx +55 -0
  50. package/.docs/raw/docs/guides/virtualization.mdx +63 -0
  51. package/.docs/raw/docs/ink/adapters.mdx +23 -1
  52. package/.docs/raw/docs/ink/hooks.mdx +22 -19
  53. package/.docs/raw/docs/migrations/toolkit-tools.mdx +14 -8
  54. package/.docs/raw/docs/react-native/hooks.mdx +26 -18
  55. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +39 -0
  56. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +3 -3
  57. package/.docs/raw/docs/runtimes/concepts/architecture.mdx +46 -4
  58. package/.docs/raw/docs/runtimes/concepts/stability.mdx +1 -1
  59. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +54 -12
  60. package/.docs/raw/docs/runtimes/custom/data-stream.mdx +1 -1
  61. package/.docs/raw/docs/runtimes/custom/external-store.mdx +62 -4
  62. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +120 -4
  63. package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +9 -9
  64. package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +13 -0
  65. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +5 -5
  66. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +3 -3
  67. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +6 -6
  68. package/.docs/raw/docs/tools/backend.mdx +19 -11
  69. package/.docs/raw/docs/tools/defining-tools.mdx +177 -52
  70. package/.docs/raw/docs/tools/index.mdx +7 -12
  71. package/.docs/raw/docs/tools/mcp.mdx +83 -15
  72. package/.docs/raw/docs/tools/multi-agent.mdx +5 -5
  73. package/.docs/raw/docs/tools/tool-ui.mdx +64 -28
  74. package/.docs/raw/docs/tools/user-managed-mcp.mdx +4 -4
  75. package/.docs/raw/docs/ui/dot-matrix.mdx +133 -0
  76. package/.docs/raw/docs/ui/mermaid.mdx +16 -9
  77. package/.docs/raw/docs/ui/model-selector.mdx +219 -52
  78. package/.docs/raw/docs/ui/number-roll.mdx +154 -0
  79. package/.docs/raw/docs/ui/part-grouping.mdx +2 -2
  80. package/.docs/raw/docs/ui/reasoning.mdx +3 -3
  81. package/.docs/raw/docs/ui/streamdown.mdx +2 -0
  82. package/.docs/raw/docs/ui/syntax-highlighting.mdx +1 -1
  83. package/.docs/raw/docs/ui/thread.mdx +52 -0
  84. package/.docs/raw/docs/ui/tool-fallback.mdx +16 -0
  85. package/.docs/raw/docs/utilities/react-o11y.mdx +2 -2
  86. package/dist/constants.js.map +1 -1
  87. package/dist/index.js.map +1 -1
  88. package/dist/prepare-docs/code-examples.js.map +1 -1
  89. package/dist/prepare-docs/copy-raw.js.map +1 -1
  90. package/dist/prepare-docs/prepare.js.map +1 -1
  91. package/dist/stdio.js.map +1 -1
  92. package/dist/tools/docs.js.map +1 -1
  93. package/dist/tools/examples.js.map +1 -1
  94. package/dist/tools/tests/test-setup.js.map +1 -1
  95. package/dist/utils/mdx.js.map +1 -1
  96. package/dist/utils/paths.js.map +1 -1
  97. package/package.json +4 -4
  98. /package/.docs/raw/docs/{(docs)/copilots → copilots}/assistant-frame.mdx +0 -0
  99. /package/.docs/raw/docs/{(docs)/copilots → copilots}/make-assistant-visible.mdx +0 -0
  100. /package/.docs/raw/docs/{(docs)/copilots → copilots}/model-context.mdx +0 -0
  101. /package/.docs/raw/docs/{(docs)/copilots → copilots}/motivation.mdx +0 -0
  102. /package/.docs/raw/docs/{(docs)/copilots → copilots}/use-assistant-instructions.mdx +0 -0
@@ -17,14 +17,44 @@ If you do not have an existing store, use [`LocalRuntime`](/docs/runtimes/custom
17
17
 
18
18
  ## Architecture
19
19
 
20
- ```mermaid
21
- graph TD
20
+ <Flow.Root
21
+ llm={`graph TD
22
22
  A[Your state] -->|messages| B[ExternalStoreAdapter]
23
23
  B --> C[ExternalStoreRuntime]
24
24
  C --> D[assistant-ui components]
25
25
  D -->|user actions| B
26
- B -->|state updates| A
27
- ```
26
+ B -->|state updates| A`}
27
+ >
28
+ <Flow.Canvas
29
+ className="pr-44"
30
+ edges={[
31
+ {
32
+ from: "components",
33
+ to: "adapter",
34
+ route: "loop-right",
35
+ label: "user actions",
36
+ laneOffset: 40,
37
+ },
38
+ {
39
+ from: "adapter",
40
+ to: "state",
41
+ route: "loop-right",
42
+ label: "state updates",
43
+ laneOffset: 88,
44
+ },
45
+ ]}
46
+ >
47
+ <Flow.Column>
48
+ <Flow.Node flowId="state">Your state</Flow.Node>
49
+ <Flow.Arrow direction="down" label="messages" length={36} />
50
+ <Flow.Node flowId="adapter">ExternalStoreAdapter</Flow.Node>
51
+ <Flow.Arrow direction="down" length={36} />
52
+ <Flow.Node>ExternalStoreRuntime</Flow.Node>
53
+ <Flow.Arrow direction="down" length={36} />
54
+ <Flow.Node flowId="components">assistant-ui components</Flow.Node>
55
+ </Flow.Column>
56
+ </Flow.Canvas>
57
+ </Flow.Root>
28
58
 
29
59
  Key idea: you own the state, the adapter translates between your format and assistant-ui's. UI features are capability-based; if you provide `setMessages`, branching turns on; if you provide `onEdit`, editing turns on; etc.
30
60
 
@@ -178,6 +208,7 @@ Each handler enables a specific UI feature.
178
208
  | `onReload` | Regenerate button |
179
209
  | `onCancel` | Cancel button while generating |
180
210
  | `onAddToolResult` | Client-side tool result handoff |
211
+ | `queue` | Queueing messages sent while a run is in progress |
181
212
 
182
213
  ## Streaming responses
183
214
 
@@ -312,6 +343,33 @@ const runtime = useExternalStoreRuntime({
312
343
  });
313
344
  ```
314
345
 
346
+ ## Queueing messages during a run
347
+
348
+ By default, sending while the thread is running is disabled. Provide a `queue` adapter to buffer a message sent during a run and process it once the run settles. The pending message is exposed on `composer.queue` and renders through [`ComposerPrimitive.Queue`](/docs/api-reference/primitives/composer).
349
+
350
+ The `createMessageQueue` helper owns the FIFO ordering and the in-flight guard. Supply a driver that runs a message, pass its `adapter` to the runtime, and tell the queue when a run starts (`notifyBusy()`, so concurrent sends buffer) and ends (`notifyIdle()`).
351
+
352
+ ```tsx
353
+ import { useEffect, useRef, useState } from "react";
354
+ import { createMessageQueue, useExternalStoreRuntime } from "@assistant-ui/react";
355
+
356
+ const [queue] = useState(() => createMessageQueue({ run: onNew }));
357
+
358
+ const runtime = useExternalStoreRuntime({
359
+ messages,
360
+ isRunning,
361
+ onNew,
362
+ queue: queue.adapter,
363
+ });
364
+
365
+ const wasRunning = useRef(isRunning);
366
+ useEffect(() => {
367
+ if (!wasRunning.current && isRunning) queue.notifyBusy();
368
+ if (wasRunning.current && !isRunning) queue.notifyIdle();
369
+ wasRunning.current = isRunning;
370
+ }, [isRunning, queue]);
371
+ ```
372
+
315
373
  ## Multi-thread
316
374
 
317
375
  `ExternalStoreRuntime` uses `ExternalStoreThreadListAdapter` (synchronous, inline). See [threads](/docs/runtimes/concepts/threads#externalstorethreadlistadapter) for the contract and best practices on keeping `currentThreadId` in sync with your store.
@@ -457,18 +457,122 @@ function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
457
457
 
458
458
  See the [tools guide](/docs/tools/defining-tools) for advanced patterns.
459
459
 
460
- ### Human-in-the-loop approval
460
+ ### Human-in-the-loop tools
461
461
 
462
- Require user confirmation before specific tools execute:
462
+ Tools listed in `unstable_humanToolNames` are not executed by code. The run pauses on the tool call and the user supplies the result through the tool UI:
463
463
 
464
464
  ```ts
465
465
  const runtime = useLocalRuntime(MyModelAdapter, {
466
- unstable_humanToolNames: ["delete_file", "send_email"],
466
+ unstable_humanToolNames: ["send_email"],
467
+ });
468
+ ```
469
+
470
+ The pause is driven by the message status your adapter returns; `LocalRuntime` never sets it for you. When the model requests a human tool call, end the run with `status: { type: "requires-action", reason: "tool-calls" }`. Without that status, the runtime marks the message complete and nothing waits. Only return it while a listed tool call is missing its result: unresolved tool calls that are not listed do not hold the run, so the runtime would invoke your adapter again immediately.
471
+
472
+ ```tsx
473
+ const MyModelAdapter: ChatModelAdapter = {
474
+ async run({ messages, abortSignal, unstable_getMessage }) {
475
+ const toolResults = unstable_getMessage().content.flatMap((part) =>
476
+ part.type === "tool-call" && part.result !== undefined
477
+ ? [{ toolCallId: part.toolCallId, result: part.result }]
478
+ : [],
479
+ );
480
+
481
+ const result = await fetch("<YOUR_API_ENDPOINT>", {
482
+ method: "POST",
483
+ headers: { "Content-Type": "application/json" },
484
+ body: JSON.stringify({ messages, toolResults }),
485
+ signal: abortSignal,
486
+ });
487
+ const data = await result.json();
488
+
489
+ if (data.toolCall) {
490
+ return {
491
+ content: [
492
+ {
493
+ type: "tool-call",
494
+ toolCallId: data.toolCall.id,
495
+ toolName: data.toolCall.name,
496
+ args: data.toolCall.args,
497
+ argsText: JSON.stringify(data.toolCall.args),
498
+ },
499
+ ],
500
+ status: { type: "requires-action", reason: "tool-calls" },
501
+ };
502
+ }
503
+
504
+ return { content: [{ type: "text", text: data.text }] };
505
+ },
506
+ };
507
+ ```
508
+
509
+ The full loop:
510
+
511
+ 1. **The run pauses.** While a listed tool call has no result, the runtime stops invoking your adapter. The unresolved tool call part reports `status.type === "requires-action"` to its renderer.
512
+ 2. **The user responds.** The tool UI completes the call with `addResult(...)`. The stock [`ToolFallback`](/docs/ui/tool-fallback) component handles this out of the box: in the requires-action state it shows Allow and Deny buttons that record the decision as the tool result.
513
+ 3. **The run resumes.** Once every listed tool call has a result, the runtime invokes your adapter again. The resumed call receives the same `messages` array as before (it ends at the user message; the in-progress assistant message is not part of it), so read the recorded results from `unstable_getMessage().content` as shown above. Content returned by the resumed call is appended to the same assistant message.
514
+
515
+ For a custom confirmation UI, register a human tool whose `render` completes the call with `addResult`. The shape of the result payload is yours to define; the adapter receives it verbatim and translates it for your backend:
516
+
517
+ ```tsx
518
+ const toolkit = defineToolkit({
519
+ send_email: {
520
+ type: "human",
521
+ description: "Send an email after the user confirms",
522
+ parameters: z.object({ to: z.string(), subject: z.string() }),
523
+ render: ({ args, result, addResult }) => {
524
+ if (result) {
525
+ return <p>{result.approved ? "Sent" : `Cancelled: ${result.reason}`}</p>;
526
+ }
527
+ return (
528
+ <div>
529
+ <p>
530
+ Send "{args.subject}" to {args.to}?
531
+ </p>
532
+ <button onClick={() => addResult({ approved: true })}>Allow</button>
533
+ <button
534
+ onClick={() =>
535
+ addResult({ approved: false, reason: "User declined" })
536
+ }
537
+ >
538
+ Deny
539
+ </button>
540
+ </div>
541
+ );
542
+ },
543
+ },
467
544
  });
468
545
  ```
469
546
 
470
547
  `unstable_humanToolNames` is unstable; see [stability](/docs/runtimes/concepts/stability).
471
548
 
549
+ ### Approval gates
550
+
551
+ The [server-side approval gate](/docs/tools/tool-ui#server-side-approval-gates) is also supported on `LocalRuntime`, for actions your backend executes after the user authorizes them. Where a human tool asks the user to supply the tool result, an approval gate asks the user to allow or block an action the adapter performs. Emit `approval: { id }` on the tool call part and end the run with the same `requires-action` status:
552
+
553
+ ```tsx
554
+ return {
555
+ content: [
556
+ {
557
+ type: "tool-call",
558
+ toolCallId: data.toolCall.id,
559
+ toolName: data.toolCall.name,
560
+ args: data.toolCall.args,
561
+ argsText: JSON.stringify(data.toolCall.args),
562
+ approval: { id: data.toolCall.id },
563
+ },
564
+ ],
565
+ status: { type: "requires-action", reason: "tool-calls" },
566
+ };
567
+ ```
568
+
569
+ A tool call with a pending approval pauses the run, whether or not the tool is listed in `unstable_humanToolNames`. Always emit gates in the pending state (`approval: { id }` with no `approved` field); a part that arrives already decided is treated as resolved and the runtime invokes the adapter again immediately. The stock [`ToolFallback`](/docs/ui/tool-fallback) Allow and Deny buttons, or a custom renderer calling `respondToApproval({ approved, reason? })`, record the decision:
570
+
571
+ - **Deny** sets `approval.approved: false` and synthesizes an error result (`{ error: reason || "Tool approval denied" }` with `isError: true`), so the model sees the denial.
572
+ - **Allow** sets `approval.approved: true` and leaves the result empty; performing the action is your adapter's job.
573
+
574
+ Once every pending approval on the message is decided and every listed human tool has a result, the runtime invokes your adapter again. Read the decisions from `unstable_getMessage().content`, perform the approved actions, and return the follow-up response. A tool call that carries an approval is owned by the gate: it does not additionally require a result, even when its name is listed in `unstable_humanToolNames`.
575
+
472
576
  ## Resuming a run
473
577
 
474
578
  `resumeRun` reconnects to an in-progress assistant run. Useful for page refresh, network reconnect, tab backgrounding, or thread switching when the backend is still generating.
@@ -521,6 +625,18 @@ function useStreamReconnect(threadId: string) {
521
625
  }
522
626
  ```
523
627
 
628
+ ## Queueing messages during a run
629
+
630
+ Set `unstable_enableMessageQueue` to keep the composer usable while a run is in progress. A message sent during a run is held in `composer.queue` and sent once the run settles; steering a queued message runs it next.
631
+
632
+ ```tsx
633
+ const runtime = useLocalRuntime(MyModelAdapter, {
634
+ unstable_enableMessageQueue: true,
635
+ });
636
+ ```
637
+
638
+ Render the pending messages with [`ComposerPrimitive.Queue`](/docs/api-reference/primitives/composer) and [`QueueItemPrimitive`](/docs/api-reference/primitives/queue-item).
639
+
524
640
  ## Adapters
525
641
 
526
642
  Attachments, speech, feedback, history, and suggestions are wired through the standard adapter contracts, see [adapters](/docs/runtimes/concepts/adapters):
@@ -731,7 +847,7 @@ const CustomAPIAdapter: ChatModelAdapter = {
731
847
  name: "unstable_humanToolNames",
732
848
  type: "string[]",
733
849
  description:
734
- "Tool names that require human approval before execution (unstable).",
850
+ "Tool names that pause the run until the user supplies a result via addResult (unstable).",
735
851
  },
736
852
  ]}
737
853
  />
@@ -389,8 +389,8 @@ Register the renderer with a backend toolkit entry inside `AssistantRuntimeProvi
389
389
  ```tsx
390
390
  import {
391
391
  AssistantRuntimeProvider,
392
+ defineToolkit,
392
393
  Tools,
393
- type Toolkit,
394
394
  useAui,
395
395
  } from "@assistant-ui/react";
396
396
  import {
@@ -399,12 +399,12 @@ import {
399
399
  } from "@assistant-ui/react-google-adk";
400
400
  import { Thread } from "@/components/assistant-ui/thread";
401
401
 
402
- const toolkit = {
402
+ const toolkit = defineToolkit({
403
403
  adk_request_input: {
404
404
  type: "backend",
405
405
  render: RequestInputToolUI,
406
406
  },
407
- } satisfies Toolkit;
407
+ });
408
408
 
409
409
  function App() {
410
410
  const runtime = useAdkRuntime({
@@ -426,8 +426,8 @@ function App() {
426
426
  ```tsx
427
427
  import {
428
428
  AssistantRuntimeProvider,
429
+ defineToolkit,
429
430
  Tools,
430
- type Toolkit,
431
431
  useAui,
432
432
  } from "@assistant-ui/react-native";
433
433
  import {
@@ -439,12 +439,12 @@ import { Thread } from "@/components/assistant-ui/thread";
439
439
 
440
440
  const API_URL = process.env.EXPO_PUBLIC_API_URL ?? "http://localhost:3000";
441
441
 
442
- const toolkit = {
442
+ const toolkit = defineToolkit({
443
443
  adk_request_input: {
444
444
  type: "backend",
445
445
  render: RequestInputToolUI,
446
446
  },
447
- } satisfies Toolkit;
447
+ });
448
448
 
449
449
  function App() {
450
450
  const runtime = useAdkRuntime({
@@ -468,8 +468,8 @@ function App() {
468
468
  ```tsx
469
469
  import {
470
470
  AssistantRuntimeProvider,
471
+ defineToolkit,
471
472
  Tools,
472
- type Toolkit,
473
473
  useAui,
474
474
  } from "@assistant-ui/react-ink";
475
475
  import {
@@ -479,12 +479,12 @@ import {
479
479
  import { Box } from "ink";
480
480
  import { Thread } from "./components/thread.js";
481
481
 
482
- const toolkit = {
482
+ const toolkit = defineToolkit({
483
483
  adk_request_input: {
484
484
  type: "backend",
485
485
  render: RequestInputToolUI,
486
486
  },
487
- } satisfies Toolkit;
487
+ });
488
488
 
489
489
  function App() {
490
490
  const runtime = useAdkRuntime({
@@ -106,6 +106,19 @@ LangGraph can emit structured UI components alongside assistant messages via `pu
106
106
 
107
107
  See [Generative UI](/docs/runtimes/langgraph/generative-ui) for full setup: enabling the `custom` stream channel, emitting UI messages, registering renderers, dynamic loading, and persisting UI state across thread switches.
108
108
 
109
+ ## Queueing messages during a run
110
+
111
+ Set `unstable_enableMessageQueue` to keep the composer usable while a run is streaming. A message sent during a run is held in `composer.queue` and sent once the run settles; steering a queued message runs it next.
112
+
113
+ ```tsx
114
+ const runtime = useLangGraphRuntime({
115
+ stream,
116
+ unstable_enableMessageQueue: true,
117
+ });
118
+ ```
119
+
120
+ Render the pending messages with [`ComposerPrimitive.Queue`](/docs/api-reference/primitives/composer) and [`QueueItemPrimitive`](/docs/api-reference/primitives/queue-item).
121
+
109
122
  ## Next
110
123
 
111
124
  <Cards>
@@ -94,14 +94,14 @@ This simply displays the tool name and arguments passed to it, but not the resul
94
94
 
95
95
  import { Thread } from "@/components/assistant-ui/thread";
96
96
  import { PriceSnapshotToolUI } from "@/components/tools/price-snapshot/PriceSnapshotTool";
97
- import { AuiProvider, Tools, type Toolkit, useAui } from "@assistant-ui/react";
97
+ import { AuiProvider, defineToolkit, Tools, useAui } from "@assistant-ui/react";
98
98
 
99
- const toolkit = {
99
+ const toolkit = defineToolkit({
100
100
  price_snapshot: {
101
101
  type: "backend",
102
102
  render: PriceSnapshotToolUI,
103
103
  },
104
- } satisfies Toolkit;
104
+ });
105
105
 
106
106
  export default function Home() {
107
107
  const aui = useAui({ tools: Tools({ toolkit }) });
@@ -326,12 +326,12 @@ export const ToolFallback: ToolCallMessagePartComponent = ({
326
326
  ### Bind fallback UI
327
327
 
328
328
  ```tsx title="@/app/page.tsx"
329
- const toolkit = {
329
+ const toolkit = defineToolkit({
330
330
  price_snapshot: {
331
331
  type: "backend",
332
332
  render: PriceSnapshotToolUI,
333
333
  },
334
- } satisfies Toolkit;
334
+ });
335
335
 
336
336
  export default function Home() {
337
337
  const aui = useAui({ tools: Tools({ toolkit }) });
@@ -212,9 +212,9 @@ export function TransactionConfirmationPending(props: TransactionConfirmation) {
212
212
  import { Thread } from "@/components/assistant-ui/thread";
213
213
  import { PriceSnapshotToolUI } from "@/components/tools/price-snapshot/PriceSnapshotTool";
214
214
  import { PurchaseStockToolUI } from "@/components/tools/purchase-stock/PurchaseStockTool";
215
- import { AuiProvider, Tools, type Toolkit, useAui } from "@assistant-ui/react";
215
+ import { AuiProvider, defineToolkit, Tools, useAui } from "@assistant-ui/react";
216
216
 
217
- const toolkit = {
217
+ const toolkit = defineToolkit({
218
218
  price_snapshot: {
219
219
  type: "backend",
220
220
  render: PriceSnapshotToolUI,
@@ -223,7 +223,7 @@ const toolkit = {
223
223
  type: "backend",
224
224
  render: PurchaseStockToolUI,
225
225
  },
226
- } satisfies Toolkit;
226
+ });
227
227
 
228
228
  export default function Home() {
229
229
  const aui = useAui({ tools: Tools({ toolkit }) });
@@ -28,13 +28,13 @@ assistant-ui ships React adapter packages for these. Pick the matching card and
28
28
  icon={<VercelIcon width={20} height={20} />}
29
29
  title="Vercel AI SDK"
30
30
  description="useChat hook, streaming, tools, attachments, multi-step. v6 current; v5 / v4 legacy."
31
- href="/docs/runtimes/ai-sdk"
31
+ href="/docs/runtimes/ai-sdk/overview"
32
32
  />
33
33
  <Card
34
34
  icon={<LangGraphIcon width={20} height={20} className="text-[#1C3C3C] dark:text-[#5b9595]" />}
35
35
  title="LangGraph"
36
36
  description="Direct integration with @langchain/langgraph-sdk. Subgraph events, UI messages, message metadata."
37
- href="/docs/runtimes/langgraph"
37
+ href="/docs/runtimes/langgraph/overview"
38
38
  />
39
39
  <Card
40
40
  icon={<LangChainIcon width={20} height={20} className="text-[#7FC8FF]" />}
@@ -46,7 +46,7 @@ assistant-ui ships React adapter packages for these. Pick the matching card and
46
46
  icon={<AdkIcon width={20} height={20} />}
47
47
  title="Google ADK"
48
48
  description="ADK JS or Python agents. Tool confirmations, auth flows, multi-agent, code execution."
49
- href="/docs/runtimes/google-adk"
49
+ href="/docs/runtimes/google-adk/overview"
50
50
  />
51
51
  <Card
52
52
  icon={<A2AIcon width={20} height={20} />}
@@ -58,14 +58,14 @@ assistant-ui ships React adapter packages for these. Pick the matching card and
58
58
  icon={<AguiIcon width={20} height={20} />}
59
59
  title="AG-UI Protocol"
60
60
  description="AG-UI agents (CopilotKit, custom servers). Streaming text, thinking, tool calls, state snapshots."
61
- href="/docs/runtimes/ag-ui"
61
+ href="/docs/runtimes/ag-ui/overview"
62
62
  />
63
63
  <PlatformOnly platforms={["react"]}>
64
64
  <Card
65
65
  icon={<OpenCodeIcon width={20} height={20} />}
66
66
  title="OpenCode"
67
67
  description="OpenCode coding-agent server. Permission flows, questions, fork / revert. Experimental."
68
- href="/docs/runtimes/opencode"
68
+ href="/docs/runtimes/opencode/overview"
69
69
  />
70
70
  </PlatformOnly>
71
71
  </Cards>
@@ -104,7 +104,7 @@ If you do not know your framework yet, or your backend is custom, pick by what y
104
104
  | Backend that already speaks the data stream protocol | [DataStream](/docs/runtimes/custom/data-stream) |
105
105
  | Stream full agent state snapshots (not just messages) | [AssistantTransport](/docs/runtimes/custom/assistant-transport) |
106
106
 
107
- If none of the framework adapters fits, start at [custom backend](/docs/runtimes/custom).
107
+ If none of the framework adapters fits, start at [custom backend](/docs/runtimes/custom/overview).
108
108
 
109
109
  ## Shared concepts
110
110
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Backend Tools
3
- description: Wire assistant-ui toolkits into your server with the AI SDK — generativeTools, frontendTools, mixing client and server tools, and multi-modal results.
3
+ description: Wire assistant-ui toolkits into your server with the AI SDK — AISDKToolkit, frontendTools, mixing client and server tools, and multi-modal results.
4
4
  platforms: ["react"]
5
5
  ---
6
6
 
@@ -24,16 +24,18 @@ const {
24
24
  } = await req.json();
25
25
  ```
26
26
 
27
- ## Generative toolkits: `generativeTools`
27
+ ## Generative toolkits: `AISDKToolkit`
28
28
 
29
- When you author tools in a [`"use generative"` file](/docs/tools/defining-tools#quick-start-use-generative), the same import resolves to the **server build** inside a route handler — schema plus any backend `execute`, with renderers stripped. Pass it to `generativeTools` together with the uploaded `tools`:
29
+ When you author tools in a [`"use generative"` file](/docs/tools/defining-tools#quick-start-use-generative), the same import resolves to the **server build** inside a route handler — schema plus any backend `execute`, with renderers stripped. Wrap it in an `AISDKToolkit` and call `.tools()` with the uploaded `tools`:
30
30
 
31
31
  ```ts title="app/api/chat/route.ts"
32
- import { generativeTools } from "@assistant-ui/react-ai-sdk";
32
+ import { AISDKToolkit } from "@assistant-ui/react-ai-sdk";
33
33
  import { streamText, convertToModelMessages, type UIMessage } from "ai";
34
34
  import { openai } from "@ai-sdk/openai";
35
35
  import toolkit from "../../toolkit";
36
36
 
37
+ const aiToolkit = new AISDKToolkit({ toolkit });
38
+
37
39
  export async function POST(req: Request) {
38
40
  const { messages, system, tools } = await req.json();
39
41
 
@@ -41,24 +43,29 @@ export async function POST(req: Request) {
41
43
  model: openai("gpt-5.4-nano"),
42
44
  system,
43
45
  messages: await convertToModelMessages(messages),
44
- tools: generativeTools({ toolkit, frontendTools: tools }),
46
+ tools: await aiToolkit.tools({ frontend: tools }),
45
47
  });
46
48
 
47
49
  return result.toUIMessageStreamResponse();
48
50
  }
49
51
  ```
50
52
 
51
- `generativeTools` registers every toolkit tool with the model using its schema, wires the backend `execute` where the server build carries one, and merges in the uploaded `frontendTools`. A server `execute` wins over an uploaded entry of the same name. Frontend and human tools (no server `execute`) are exposed schema-only and left for the client and the user to fulfill.
53
+ `AISDKToolkit.tools()` registers every toolkit tool with the model using its schema, wires the backend `execute` where the server build carries one, and merges in the uploaded frontend tools. A server `execute` wins over an uploaded entry of the same name. Frontend and human tools (no server `execute`) are exposed schema-only and left for the client and the user to fulfill.
52
54
 
53
55
  <Callout type="info">
54
- If your toolkit spreads in MCP server tools (`defineMcpToolkit`), use
55
- `new AISDKToolkit({ toolkit }).tools({ frontend })` instead of
56
- `generativeTools` — it also opens the MCP connections. See [MCP](/docs/tools/mcp).
56
+ If your toolkit spreads in MCP server tools (`defineMcpToolkit`), `.tools()`
57
+ also opens those connections. A module-scope `aiToolkit` pools them across
58
+ requests; see [MCP](/docs/tools/mcp) for the connection lifecycle and when to
59
+ call `aiToolkit.close()`. The older
60
+ `generativeTools({ toolkit, frontendTools })` is deprecated, MCP-less, and
61
+ superseded by `AISDKToolkit`.
57
62
  </Callout>
58
63
 
59
64
  ## Client-defined tools: `frontendTools`
60
65
 
61
- If you don't use the generative compiler — for example a plain [`satisfies Toolkit`](/docs/tools/defining-tools#render-only-tools-for-externally-executed-tools) with browser-executed tools — the AI SDK adapter still serializes those tools into the request `tools`. Convert them to the AI SDK shape with `frontendTools` and spread your own server tools alongside:
66
+ If a toolkit cannot go through the generative compiler, the AI SDK adapter still
67
+ serializes browser-executed tools into the request `tools`. Convert them to the
68
+ AI SDK shape with `frontendTools` and spread your own server tools alongside:
62
69
 
63
70
  ```ts title="app/api/chat/route.ts"
64
71
  import { frontendTools } from "@assistant-ui/react-ai-sdk";
@@ -86,7 +93,8 @@ export async function POST(req: Request) {
86
93
  }
87
94
  ```
88
95
 
89
- `generativeTools` calls `frontendTools` for you under the hood; reach for `frontendTools` directly when you're not on the generative build.
96
+ `AISDKToolkit.tools()` calls `frontendTools` for you under the hood; reach for
97
+ `frontendTools` directly when you're not on the generative build.
90
98
 
91
99
  <Callout type="tip">
92
100
  `toToolsJSONSchema` emits the uploaded tools in alphabetical order, so two