@assistant-ui/mcp-docs-server 0.1.8 → 0.1.10

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 (28) hide show
  1. package/.docs/organized/code-examples/with-ai-sdk-v5.md +24 -17
  2. package/.docs/organized/code-examples/with-assistant-transport.md +1599 -0
  3. package/.docs/organized/code-examples/with-cloud.md +11 -11
  4. package/.docs/organized/code-examples/with-external-store.md +9 -9
  5. package/.docs/organized/code-examples/with-ffmpeg.md +17 -16
  6. package/.docs/organized/code-examples/with-langgraph.md +33 -110
  7. package/.docs/organized/code-examples/with-parent-id-grouping.md +9 -9
  8. package/.docs/organized/code-examples/with-react-hook-form.md +17 -16
  9. package/.docs/raw/docs/api-reference/primitives/Thread.mdx +40 -8
  10. package/.docs/raw/docs/cloud/persistence/langgraph.mdx +42 -66
  11. package/.docs/raw/docs/copilots/assistant-frame.mdx +18 -16
  12. package/.docs/raw/docs/devtools.mdx +51 -0
  13. package/.docs/raw/docs/getting-started.mdx +2 -4
  14. package/.docs/raw/docs/guides/ToolUI.mdx +112 -37
  15. package/.docs/raw/docs/guides/Tools.mdx +170 -6
  16. package/.docs/raw/docs/migrations/react-langgraph-v0-7.mdx +324 -0
  17. package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +2 -2
  18. package/.docs/raw/docs/runtimes/custom/local.mdx +1 -1
  19. package/.docs/raw/docs/runtimes/langgraph/index.mdx +55 -20
  20. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +6 -5
  21. package/.docs/raw/docs/runtimes/mastra/overview.mdx +3 -3
  22. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +13 -13
  23. package/.docs/raw/docs/ui/Thread.mdx +368 -5
  24. package/package.json +8 -8
  25. package/.docs/raw/docs/migrations/v0-7.mdx +0 -188
  26. package/.docs/raw/docs/migrations/v0-8.mdx +0 -160
  27. package/.docs/raw/docs/migrations/v0-9.mdx +0 -75
  28. package/.docs/raw/docs/ui/primitives/Thread.mdx +0 -197
@@ -2,9 +2,9 @@
2
2
  title: Chat History for LangGraph Cloud
3
3
  ---
4
4
 
5
- import { Steps, Step } from 'fumadocs-ui/components/steps';
6
- import { Callout } from 'fumadocs-ui/components/callout';
7
- import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
5
+ import { Steps, Step } from "fumadocs-ui/components/steps";
6
+ import { Callout } from "fumadocs-ui/components/callout";
7
+ import { Tab, Tabs } from "fumadocs-ui/components/tabs";
8
8
 
9
9
  ## Overview
10
10
 
@@ -13,7 +13,8 @@ assistant-cloud provides thread management and persistent chat history for appli
13
13
  ## Prerequisites
14
14
 
15
15
  <Callout type="info">
16
- You need an assistant-cloud account to follow this guide. [Sign up here](https://cloud.assistant-ui.com/) to get started.
16
+ You need an assistant-cloud account to follow this guide. [Sign up
17
+ here](https://cloud.assistant-ui.com/) to get started.
17
18
  </Callout>
18
19
 
19
20
  ## Setup Guide
@@ -75,40 +76,12 @@ Create a runtime provider that integrates LangGraph with assistant-cloud. Choose
75
76
  import {
76
77
  AssistantCloud,
77
78
  AssistantRuntimeProvider,
78
- useCloudThreadListRuntime,
79
- useThreadListItemRuntime,
80
79
  } from "@assistant-ui/react";
81
80
  import { useLangGraphRuntime } from "@assistant-ui/react-langgraph";
82
81
  import { createThread, getThreadState, sendMessage } from "@/lib/chatApi";
83
82
  import { LangChainMessage } from "@assistant-ui/react-langgraph";
84
83
  import { useMemo } from "react";
85
84
 
86
- const useMyLangGraphRuntime = () => {
87
- const threadListItemRuntime = useThreadListItemRuntime();
88
-
89
- const runtime = useLangGraphRuntime({
90
- stream: async function* (messages) {
91
- const { externalId } = await threadListItemRuntime.initialize();
92
- if (!externalId) throw new Error("Thread not found");
93
-
94
- return sendMessage({
95
- threadId: externalId,
96
- messages,
97
- });
98
- },
99
-
100
- onSwitchToThread: async (externalId) => {
101
- const state = await getThreadState(externalId);
102
- return {
103
- messages:
104
- (state.values as { messages?: LangChainMessage[] }).messages ?? [],
105
- };
106
- },
107
- });
108
-
109
- return runtime;
110
- };
111
-
112
85
  export function MyRuntimeProvider({
113
86
  children,
114
87
  }: Readonly<{
@@ -123,13 +96,28 @@ export function MyRuntimeProvider({
123
96
  [],
124
97
  );
125
98
 
126
- const runtime = useCloudThreadListRuntime({
99
+ const runtime = useLangGraphRuntime({
127
100
  cloud,
128
- runtimeHook: useMyLangGraphRuntime,
101
+ stream: async function* (messages, { initialize }) {
102
+ const { externalId } = await initialize();
103
+ if (!externalId) throw new Error("Thread not found");
104
+
105
+ return sendMessage({
106
+ threadId: externalId,
107
+ messages,
108
+ });
109
+ },
129
110
  create: async () => {
130
111
  const { thread_id } = await createThread();
131
112
  return { externalId: thread_id };
132
113
  },
114
+ load: async (externalId) => {
115
+ const state = await getThreadState(externalId);
116
+ return {
117
+ messages:
118
+ (state.values as { messages?: LangChainMessage[] }).messages ?? [],
119
+ };
120
+ },
133
121
  });
134
122
 
135
123
  return (
@@ -150,8 +138,6 @@ export function MyRuntimeProvider({
150
138
  import {
151
139
  AssistantCloud,
152
140
  AssistantRuntimeProvider,
153
- useCloudThreadListRuntime,
154
- useThreadListItemRuntime,
155
141
  } from "@assistant-ui/react";
156
142
  import { useLangGraphRuntime } from "@assistant-ui/react-langgraph";
157
143
  import { createThread, getThreadState, sendMessage } from "@/lib/chatApi";
@@ -159,32 +145,6 @@ import { LangChainMessage } from "@assistant-ui/react-langgraph";
159
145
  import { useAuth } from "@clerk/nextjs";
160
146
  import { useMemo } from "react";
161
147
 
162
- const useMyLangGraphRuntime = () => {
163
- const threadListItemRuntime = useThreadListItemRuntime();
164
-
165
- const runtime = useLangGraphRuntime({
166
- stream: async function* (messages) {
167
- const { externalId } = await threadListItemRuntime.initialize();
168
- if (!externalId) throw new Error("Thread not found");
169
-
170
- return sendMessage({
171
- threadId: externalId,
172
- messages,
173
- });
174
- },
175
-
176
- onSwitchToThread: async (externalId) => {
177
- const state = await getThreadState(externalId);
178
- return {
179
- messages:
180
- (state.values as { messages?: LangChainMessage[] }).messages ?? []
181
- };
182
- },
183
- });
184
-
185
- return runtime;
186
- };
187
-
188
148
  export function MyRuntimeProvider({
189
149
  children,
190
150
  }: Readonly<{
@@ -201,13 +161,28 @@ export function MyRuntimeProvider({
201
161
  [getToken],
202
162
  );
203
163
 
204
- const runtime = useCloudThreadListRuntime({
164
+ const runtime = useLangGraphRuntime({
205
165
  cloud,
206
- runtimeHook: useMyLangGraphRuntime,
166
+ stream: async function* (messages, { initialize }) {
167
+ const { externalId } = await initialize();
168
+ if (!externalId) throw new Error("Thread not found");
169
+
170
+ return sendMessage({
171
+ threadId: externalId,
172
+ messages,
173
+ });
174
+ },
207
175
  create: async () => {
208
176
  const { thread_id } = await createThread();
209
177
  return { externalId: thread_id };
210
178
  },
179
+ load: async (externalId) => {
180
+ const state = await getThreadState(externalId);
181
+ return {
182
+ messages:
183
+ (state.values as { messages?: LangChainMessage[] }).messages ?? [],
184
+ };
185
+ },
211
186
  });
212
187
 
213
188
  return (
@@ -219,7 +194,8 @@ export function MyRuntimeProvider({
219
194
  ```
220
195
 
221
196
  <Callout type="info">
222
- For Clerk authentication, configure the `"assistant-ui"` token template in your Clerk dashboard.
197
+ For Clerk authentication, configure the `"assistant-ui"` token template in
198
+ your Clerk dashboard.
223
199
  </Callout>
224
200
 
225
201
  </Tab>
@@ -227,7 +203,7 @@ export function MyRuntimeProvider({
227
203
  </Tabs>
228
204
 
229
205
  <Callout type="info">
230
- The `useMyLangGraphRuntime` hook is extracted into a separate function because it will be mounted multiple times, once per active thread.
206
+ The `useLangGraphRuntime` hook now directly accepts `cloud`, `create`, and `load` parameters for simplified thread management. The runtime handles thread lifecycle internally.
231
207
  </Callout>
232
208
 
233
209
  </Step>
@@ -74,7 +74,7 @@ function ParentComponent() {
74
74
 
75
75
  return (
76
76
  <div>
77
- <Thread /> {/* Your assistant UI */}
77
+ <Thread /> {/* Your assistant-ui */}
78
78
  <iframe
79
79
  ref={iframeRef}
80
80
  src="https://trusted-iframe-domain.com/embed"
@@ -98,10 +98,10 @@ const registry = new ModelContextRegistry();
98
98
  const toolHandle = registry.addTool({
99
99
  toolName: "convertCurrency",
100
100
  description: "Convert between currencies",
101
- parameters: z.object({
101
+ parameters: z.object({
102
102
  amount: z.number(),
103
103
  from: z.string(),
104
- to: z.string()
104
+ to: z.string(),
105
105
  }),
106
106
  execute: async ({ amount, from, to }) => {
107
107
  const rate = await fetchExchangeRate(from, to);
@@ -113,19 +113,19 @@ const toolHandle = registry.addTool({
113
113
  toolHandle.update({
114
114
  toolName: "convertCurrency",
115
115
  description: "Convert between currencies with live rates", // Updated description
116
- parameters: z.object({
116
+ parameters: z.object({
117
117
  amount: z.number(),
118
118
  from: z.string(),
119
119
  to: z.string(),
120
- includesFees: z.boolean().optional()
120
+ includesFees: z.boolean().optional(),
121
121
  }),
122
122
  execute: async ({ amount, from, to, includesFees }) => {
123
123
  const rate = await fetchExchangeRate(from, to);
124
124
  const fee = includesFees ? 0.02 : 0; // 2% fee
125
- return {
126
- result: amount * rate * (1 - fee),
125
+ return {
126
+ result: amount * rate * (1 - fee),
127
127
  currency: to,
128
- fee: includesFees ? amount * rate * fee : 0
128
+ fee: includesFees ? amount * rate * fee : 0,
129
129
  };
130
130
  },
131
131
  });
@@ -377,20 +377,22 @@ Provide data visualization tools in an iframe:
377
377
  registry.addTool({
378
378
  toolName: "createChart",
379
379
  description: "Generate a chart from data",
380
- parameters: z.object({
381
- data: z.array(z.object({
382
- label: z.string(),
383
- value: z.number()
384
- })),
380
+ parameters: z.object({
381
+ data: z.array(
382
+ z.object({
383
+ label: z.string(),
384
+ value: z.number(),
385
+ }),
386
+ ),
385
387
  chartType: z.enum(["bar", "line", "pie"]),
386
- title: z.string().optional()
388
+ title: z.string().optional(),
387
389
  }),
388
390
  execute: async ({ data, chartType, title }) => {
389
391
  // Generate chart using a library like Chart.js or D3
390
392
  const chartUrl = await generateChart(data, chartType, title);
391
- return {
393
+ return {
392
394
  chartUrl,
393
- summary: `Created ${chartType} chart with ${data.length} data points`
395
+ summary: `Created ${chartType} chart with ${data.length} data points`,
394
396
  };
395
397
  },
396
398
  });
@@ -0,0 +1,51 @@
1
+ ---
2
+ title: DevTools
3
+ ---
4
+
5
+ import { Step, Steps } from "fumadocs-ui/components/steps";
6
+
7
+ Hey, the assistant-ui DevTools allows you to debug the assistant-ui state and context, and events without resorting to `console.log`. It's an easy way to see how data flows to the assistant-ui's runtime layer.
8
+
9
+ ![Screenshot of the DevTools UI showing logs and state panels](../../../../.github/assets/devtoolsui.png)
10
+
11
+ ## Setup
12
+
13
+ <Steps>
14
+ <Step>
15
+
16
+ ### Install the DevTools package
17
+
18
+ ```bash
19
+ npm install @assistant-ui/react-devtools
20
+ ```
21
+
22
+ </Step>
23
+ <Step>
24
+
25
+ ### Mount the DevTools modal
26
+
27
+ ```tsx
28
+ import { AssistantRuntimeProvider } from "@assistant-ui/react";
29
+ import { DevToolsModal } from "@assistant-ui/react-devtools";
30
+
31
+ export function AssistantApp() {
32
+ return (
33
+ <AssistantRuntimeProvider>
34
+ <DevToolsModal />
35
+ {/* ...your assistant-ui... */}
36
+ </AssistantRuntimeProvider>
37
+ );
38
+ }
39
+ ```
40
+
41
+ </Step>
42
+ <Step>
43
+
44
+ ### Verify the DevTools overlay
45
+
46
+ That's it! In development builds you should now see the DevTools in the lower-right corner of your site.
47
+
48
+ ![DevTools floating modal anchored in the lower-right corner of a page](../../../../.github/assets/devtoolsmodal.png)
49
+
50
+ </Step>
51
+ </Steps>
@@ -283,8 +283,7 @@ const ThreadWelcomeSuggestions: FC = () => {
283
283
  <ThreadPrimitive.Suggestion
284
284
  className="aui-thread-welcome-suggestion"
285
285
  prompt="What is the weather in Tokyo?"
286
- method="replace"
287
- autoSend
286
+ send
288
287
  >
289
288
  <span className="aui-thread-welcome-suggestion-text">
290
289
  What is the weather in Tokyo?
@@ -293,8 +292,7 @@ const ThreadWelcomeSuggestions: FC = () => {
293
292
  <ThreadPrimitive.Suggestion
294
293
  className="aui-thread-welcome-suggestion"
295
294
  prompt="What is assistant-ui?"
296
- method="replace"
297
- autoSend
295
+ send
298
296
  >
299
297
  <span className="aui-thread-welcome-suggestion-text">
300
298
  What is assistant-ui?
@@ -378,17 +378,42 @@ const DatePickerToolUI = makeAssistantToolUI<
378
378
 
379
379
  ### Multi-Step Interactions
380
380
 
381
- Build complex workflows with multiple user interactions:
381
+ Build complex workflows with human-in-the-loop patterns for multi-step user interactions:
382
382
 
383
383
  ```tsx
384
- const ApprovalToolUI = makeAssistantToolUI<
385
- { action: string; details: any },
386
- { approved: boolean; reason?: string }
387
- >({
384
+ const DeleteProjectTool = makeAssistantTool({
385
+ toolName: "deleteProject",
386
+ execute: async ({ projectId }, { human }) => {
387
+ const response = await human({ action, details });
388
+ if (!response.approved) throw new Error("Project deletion cancelled");
389
+
390
+ await deleteProject(projectId);
391
+ return { success: true };
392
+ },
393
+ });
394
+
395
+ const ApprovalTool = makeAssistantTool({
396
+ ...tool({
397
+ description: "Request user approval for an action",
398
+ parameters: z.object({
399
+ action: z.string(),
400
+ details: z.any(),
401
+ }),
402
+ execute: async ({ action, details }, { human }) => {
403
+ // Request approval from user
404
+ const response = await human({ action, details });
405
+
406
+ return {
407
+ approved: response.approved,
408
+ reason: response.reason,
409
+ };
410
+ },
411
+ }),
388
412
  toolName: "requestApproval",
389
- render: ({ args, result, addResult }) => {
413
+ render: ({ args, result, interrupt, resume }) => {
390
414
  const [reason, setReason] = useState("");
391
415
 
416
+ // Show result after approval/rejection
392
417
  if (result) {
393
418
  return (
394
419
  <div className={result.approved ? "text-green-600" : "text-red-600"}>
@@ -397,41 +422,53 @@ const ApprovalToolUI = makeAssistantToolUI<
397
422
  );
398
423
  }
399
424
 
400
- return (
401
- <div className="rounded border-2 border-yellow-400 p-4">
402
- <h4 className="font-bold">Approval Required</h4>
403
- <p className="my-2">{args.action}</p>
404
- <pre className="rounded bg-gray-100 p-2 text-sm">
405
- {JSON.stringify(args.details, null, 2)}
406
- </pre>
407
-
408
- <div className="mt-4 flex gap-2">
409
- <button
410
- onClick={() => addResult({ approved: true })}
411
- className="rounded bg-green-500 px-4 py-2 text-white"
412
- >
413
- Approve
414
- </button>
415
- <button
416
- onClick={() => addResult({ approved: false, reason })}
417
- className="rounded bg-red-500 px-4 py-2 text-white"
418
- >
419
- Reject
420
- </button>
421
- <input
422
- type="text"
423
- placeholder="Rejection reason..."
424
- value={reason}
425
- onChange={(e) => setReason(e.target.value)}
426
- className="flex-1 rounded border px-2"
427
- />
425
+ // Show approval UI when waiting for user input
426
+ if (interrupt) {
427
+ return (
428
+ <div className="rounded border-2 border-yellow-400 p-4">
429
+ <h4 className="font-bold">Approval Required</h4>
430
+ <p className="my-2">{interrupt.payload.action}</p>
431
+ <pre className="rounded bg-gray-100 p-2 text-sm">
432
+ {JSON.stringify(interrupt.payload.details, null, 2)}
433
+ </pre>
434
+
435
+ <div className="mt-4 flex gap-2">
436
+ <button
437
+ onClick={() => resume({ approved: true })}
438
+ className="rounded bg-green-500 px-4 py-2 text-white"
439
+ >
440
+ Approve
441
+ </button>
442
+ <button
443
+ onClick={() => resume({ approved: false, reason })}
444
+ className="rounded bg-red-500 px-4 py-2 text-white"
445
+ >
446
+ Reject
447
+ </button>
448
+ <input
449
+ type="text"
450
+ placeholder="Rejection reason..."
451
+ value={reason}
452
+ onChange={(e) => setReason(e.target.value)}
453
+ className="flex-1 rounded border px-2"
454
+ />
455
+ </div>
428
456
  </div>
429
- </div>
430
- );
457
+ );
458
+ }
459
+
460
+ return <div>Processing...</div>;
431
461
  },
432
462
  });
433
463
  ```
434
464
 
465
+ <Callout type="tip">
466
+ Use tool human input (`human()` / `resume()`) for workflows that need to
467
+ pause tool execution and wait for user input. Use `addResult()` for "human
468
+ tools" where the AI requests a tool call but the entire execution happens
469
+ through user interaction.
470
+ </Callout>
471
+
435
472
  ## Advanced Features
436
473
 
437
474
  ### Tool Status Handling
@@ -589,14 +626,52 @@ type ToolUIRenderProps<TArgs, TResult> = {
589
626
  toolName: string;
590
627
  toolCallId: string;
591
628
 
592
- // Interactive callback
629
+ // Interactive callbacks
593
630
  addResult: (result: TResult) => void;
631
+ resume: (payload: unknown) => void;
632
+
633
+ // Interrupt state
634
+ interrupt?: { type: "human"; payload: unknown }; // Payload from context.human()
594
635
 
595
636
  // Optional artifact data
596
637
  artifact?: unknown;
597
638
  };
598
639
  ```
599
640
 
641
+ ### Human Input Handling
642
+
643
+ When a tool calls `human()` during execution, the payload becomes available in the render function as `interrupt.payload`:
644
+
645
+ ```tsx
646
+ const ConfirmationToolUI = makeAssistantToolUI<
647
+ { action: string },
648
+ { confirmed: boolean }
649
+ >({
650
+ toolName: "confirmAction",
651
+ render: ({ args, result, interrupt, resume }) => {
652
+ // Tool is waiting for user input
653
+ if (interrupt) {
654
+ return (
655
+ <div className="confirmation-dialog">
656
+ <p>Confirm: {interrupt.payload.message}</p>
657
+ <button onClick={() => resume(true)}>Yes</button>
658
+ <button onClick={() => resume(false)}>No</button>
659
+ </div>
660
+ );
661
+ }
662
+
663
+ // Tool completed
664
+ if (result) {
665
+ return <div>Action {result.confirmed ? "confirmed" : "cancelled"}</div>;
666
+ }
667
+
668
+ return <div>Processing...</div>;
669
+ },
670
+ });
671
+ ```
672
+
673
+ Learn more about tool human input in the [Tools Guide](/docs/guides/Tools#tool-human-input).
674
+
600
675
  ## Best Practices
601
676
 
602
677
  ### 1. Handle All Status States