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

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 (81) hide show
  1. package/.docs/organized/code-examples/waterfall.md +5 -5
  2. package/.docs/organized/code-examples/with-a2a.md +5 -5
  3. package/.docs/organized/code-examples/with-ag-ui.md +9 -9
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +7 -7
  5. package/.docs/organized/code-examples/with-artifacts.md +37 -31
  6. package/.docs/organized/code-examples/with-assistant-transport.md +8 -8
  7. package/.docs/organized/code-examples/with-browser-extension.md +5 -5
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +68 -47
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +7 -7
  10. package/.docs/organized/code-examples/with-cloud.md +7 -7
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +7 -7
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +8 -8
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +8 -8
  14. package/.docs/organized/code-examples/with-expo.md +33 -24
  15. package/.docs/organized/code-examples/with-external-store.md +5 -5
  16. package/.docs/organized/code-examples/with-ffmpeg.md +10 -10
  17. package/.docs/organized/code-examples/with-generative-ui.md +70 -64
  18. package/.docs/organized/code-examples/with-google-adk.md +6 -6
  19. package/.docs/organized/code-examples/with-heat-graph.md +5 -5
  20. package/.docs/organized/code-examples/with-image-generation.md +7 -7
  21. package/.docs/organized/code-examples/with-interactables.md +7 -7
  22. package/.docs/organized/code-examples/with-langchain.md +7 -7
  23. package/.docs/organized/code-examples/with-langgraph.md +30 -26
  24. package/.docs/organized/code-examples/with-livekit.md +8 -8
  25. package/.docs/organized/code-examples/with-mcp.md +8 -8
  26. package/.docs/organized/code-examples/with-opencode.md +6 -6
  27. package/.docs/organized/code-examples/with-react-hook-form.md +7 -7
  28. package/.docs/organized/code-examples/with-react-ink.md +295 -100
  29. package/.docs/organized/code-examples/with-react-router.md +11 -11
  30. package/.docs/organized/code-examples/with-resumable-stream.md +8 -8
  31. package/.docs/organized/code-examples/with-store.md +64 -64
  32. package/.docs/organized/code-examples/with-tanstack.md +8 -8
  33. package/.docs/organized/code-examples/with-tap-runtime.md +5 -5
  34. package/.docs/raw/docs/(docs)/architecture.mdx +52 -41
  35. package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +1 -1
  36. package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +3 -0
  37. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +14 -3
  38. package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +94 -3
  39. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +5 -69
  40. package/.docs/raw/docs/ink/adapters.mdx +23 -1
  41. package/.docs/raw/docs/ink/hooks.mdx +20 -17
  42. package/.docs/raw/docs/migrations/toolkit-tools.mdx +14 -8
  43. package/.docs/raw/docs/react-native/hooks.mdx +25 -17
  44. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +41 -0
  45. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +3 -3
  46. package/.docs/raw/docs/runtimes/concepts/architecture.mdx +46 -4
  47. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +54 -12
  48. package/.docs/raw/docs/runtimes/custom/external-store.mdx +62 -4
  49. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +12 -0
  50. package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +9 -9
  51. package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +13 -0
  52. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +5 -5
  53. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +3 -3
  54. package/.docs/raw/docs/tools/backend.mdx +19 -11
  55. package/.docs/raw/docs/tools/defining-tools.mdx +177 -52
  56. package/.docs/raw/docs/tools/index.mdx +7 -12
  57. package/.docs/raw/docs/tools/mcp.mdx +83 -15
  58. package/.docs/raw/docs/tools/multi-agent.mdx +5 -5
  59. package/.docs/raw/docs/tools/tool-ui.mdx +27 -27
  60. package/.docs/raw/docs/tools/user-managed-mcp.mdx +4 -4
  61. package/.docs/raw/docs/ui/mermaid.mdx +16 -9
  62. package/.docs/raw/docs/ui/part-grouping.mdx +2 -2
  63. package/.docs/raw/docs/ui/tool-fallback.mdx +16 -0
  64. package/.docs/raw/docs/utilities/react-o11y.mdx +2 -2
  65. package/dist/constants.js.map +1 -1
  66. package/dist/index.js.map +1 -1
  67. package/dist/prepare-docs/code-examples.js.map +1 -1
  68. package/dist/prepare-docs/copy-raw.js.map +1 -1
  69. package/dist/prepare-docs/prepare.js.map +1 -1
  70. package/dist/stdio.js.map +1 -1
  71. package/dist/tools/docs.js.map +1 -1
  72. package/dist/tools/examples.js.map +1 -1
  73. package/dist/tools/tests/test-setup.js.map +1 -1
  74. package/dist/utils/mdx.js.map +1 -1
  75. package/dist/utils/paths.js.map +1 -1
  76. package/package.json +4 -4
  77. /package/.docs/raw/docs/{(docs)/copilots → copilots}/assistant-frame.mdx +0 -0
  78. /package/.docs/raw/docs/{(docs)/copilots → copilots}/make-assistant-visible.mdx +0 -0
  79. /package/.docs/raw/docs/{(docs)/copilots → copilots}/model-context.mdx +0 -0
  80. /package/.docs/raw/docs/{(docs)/copilots → copilots}/motivation.mdx +0 -0
  81. /package/.docs/raw/docs/{(docs)/copilots → copilots}/use-assistant-instructions.mdx +0 -0
@@ -15,6 +15,12 @@ client ──► /api/chat ──► MCP client ──► MCP server (HTTP
15
15
 
16
16
  The MCP client lives on the server inside your AI SDK route handler. It connects to one or more MCP servers, calls `tools()` to get a tool map, and hands that map to `streamText`. assistant-ui's existing tool-call UI (`ToolFallback`, or toolkit entries with `render`) renders the results.
17
17
 
18
+ <Callout type="info">
19
+ If you use a `"use generative"` toolkit, spread `defineMcpToolkit({ ... })`
20
+ in the toolkit and use `AISDKToolkit` in your route. It opens the MCP clients,
21
+ merges their tools with your toolkit, and closes them for you.
22
+ </Callout>
23
+
18
24
  ## Setup
19
25
 
20
26
  <Steps>
@@ -71,9 +77,64 @@ const mcpClient = await createMCPClient({
71
77
  </Step>
72
78
  <Step>
73
79
 
80
+ ### Define MCP servers in your toolkit
81
+
82
+ In a generative toolkit, spread `defineMcpToolkit({ ... })` with one entry per
83
+ MCP server. The entry key names the server connection; the MCP server publishes
84
+ the actual tool names.
85
+
86
+ ```tsx title="app/toolkit.tsx"
87
+ "use generative";
88
+
89
+ import { defineMcpToolkit, defineToolkit } from "@assistant-ui/react";
90
+
91
+ export default defineToolkit({
92
+ ...defineMcpToolkit({
93
+ github: {
94
+ type: "http",
95
+ url: "https://mcp.example.com/mcp",
96
+ },
97
+ }),
98
+ });
99
+ ```
100
+
101
+ Use `AISDKToolkit` in the route. It opens the MCP clients, merges their tools
102
+ with the rest of your toolkit, and closes them when you call `close()`:
103
+
104
+ ```ts title="app/api/chat/route.ts"
105
+ import { AISDKToolkit } from "@assistant-ui/react-ai-sdk";
106
+ import { openai } from "@ai-sdk/openai";
107
+ import { streamText, convertToModelMessages } from "ai";
108
+ import type { UIMessage } from "ai";
109
+ import toolkit from "../../toolkit";
110
+
111
+ export async function POST(req: Request) {
112
+ const { messages, tools }: { messages: UIMessage[]; tools?: Record<string, any> } =
113
+ await req.json();
114
+
115
+ const aiToolkit = new AISDKToolkit({ toolkit });
116
+
117
+ const result = streamText({
118
+ model: openai("gpt-5.4-mini"),
119
+ messages: await convertToModelMessages(messages),
120
+ tools: await aiToolkit.tools({ frontend: tools }),
121
+ onFinish: async () => {
122
+ await aiToolkit.close();
123
+ },
124
+ });
125
+
126
+ return result.toUIMessageStreamResponse();
127
+ }
128
+ ```
129
+
130
+ </Step>
131
+ <Step>
132
+
74
133
  ### Wire the tools into the route
75
134
 
76
- `mcpClient.tools()` returns an object shaped exactly like the `tools` argument of `streamText`. Spread it in alongside any of your own tools, and close the client when the response finishes:
135
+ For manual MCP client control, `mcpClient.tools()` returns an object shaped
136
+ exactly like the `tools` argument of `streamText`. Spread it in alongside any of
137
+ your own tools, and close the client when the response finishes:
77
138
 
78
139
  ```ts title="app/api/chat/route.ts"
79
140
  import { createMCPClient } from "@ai-sdk/mcp";
@@ -141,22 +202,28 @@ If two servers expose tools with the same name, the later spread wins. Rename or
141
202
 
142
203
  ### Render results in the UI
143
204
 
144
- Tool calls flow through the existing assistant-ui tool-call rendering. With no setup, the bundled `<ToolFallback>` component renders the call name, arguments, and result. To customize the appearance for a specific tool, add a backend entry to your toolkit:
205
+ Tool calls flow through the existing assistant-ui tool-call rendering. With no
206
+ setup, the bundled `<ToolFallback>` component renders the call name, arguments,
207
+ and result. To customize the appearance for a specific tool in a generative
208
+ toolkit, add an `externalTool()` renderer whose key matches the MCP tool name:
145
209
 
146
210
  <PlatformTabs>
147
211
  <Tab value="React">
148
212
 
149
- ```tsx title="app/components/GitHubIssueToolUI.tsx"
150
- "use client";
213
+ ```tsx title="app/toolkit.tsx"
214
+ "use generative";
151
215
 
152
- import type { Toolkit } from "@assistant-ui/react";
216
+ import { defineMcpToolkit, defineToolkit, externalTool } from "@assistant-ui/react";
153
217
 
154
218
  type Args = { repo: string; number: number };
155
219
  type Result = { title: string; state: string; url: string };
156
220
 
157
- export const toolkit = {
221
+ export default defineToolkit({
222
+ ...defineMcpToolkit({
223
+ github: { type: "http", url: "https://mcp.example.com/mcp" },
224
+ }),
158
225
  github_get_issue: {
159
- type: "backend",
226
+ execute: externalTool(),
160
227
  render: ({ args, result }: { args: Args; result?: Result }) => (
161
228
  <div className="rounded border p-3">
162
229
  <div className="font-mono text-sm">{args.repo}#{args.number}</div>
@@ -168,20 +235,20 @@ export const toolkit = {
168
235
  </div>
169
236
  ),
170
237
  },
171
- } satisfies Toolkit;
238
+ });
172
239
  ```
173
240
 
174
241
  </Tab>
175
242
  <Tab value="React Native">
176
243
 
177
244
  ```tsx title="components/GitHubIssueToolUI.tsx"
178
- import type { Toolkit } from "@assistant-ui/react-native";
245
+ import { defineToolkit } from "@assistant-ui/react-native";
179
246
  import { Linking, Pressable, Text, View } from "react-native";
180
247
 
181
248
  type Args = { repo: string; number: number };
182
249
  type Result = { title: string; state: string; url: string };
183
250
 
184
- export const toolkit = {
251
+ export const toolkit = defineToolkit({
185
252
  github_get_issue: {
186
253
  type: "backend",
187
254
  render: ({ args, result }: { args: Args; result?: Result }) => (
@@ -199,20 +266,20 @@ export const toolkit = {
199
266
  </View>
200
267
  ),
201
268
  },
202
- } satisfies Toolkit;
269
+ });
203
270
  ```
204
271
 
205
272
  </Tab>
206
273
  <Tab value="React Ink">
207
274
 
208
275
  ```tsx title="components/GitHubIssueToolUI.tsx"
209
- import type { Toolkit } from "@assistant-ui/react-ink";
276
+ import { defineToolkit } from "@assistant-ui/react-ink";
210
277
  import { Box, Text } from "ink";
211
278
 
212
279
  type Args = { repo: string; number: number };
213
280
  type Result = { title: string; state: string; url: string };
214
281
 
215
- export const toolkit = {
282
+ export const toolkit = defineToolkit({
216
283
  github_get_issue: {
217
284
  type: "backend",
218
285
  render: ({ args, result }: { args: Args; result?: Result }) => (
@@ -228,13 +295,14 @@ export const toolkit = {
228
295
  </Box>
229
296
  ),
230
297
  },
231
- } satisfies Toolkit;
298
+ });
232
299
  ```
233
300
 
234
301
  </Tab>
235
302
  </PlatformTabs>
236
303
 
237
- Register the toolkit once with `Tools({ toolkit })`. The toolkit key must match the name your MCP server publishes.
304
+ Register the toolkit once with `Tools({ toolkit })`. Renderer keys such as
305
+ `github_get_issue` must match the tool names your MCP server publishes.
238
306
 
239
307
  ```tsx title="app/components/RuntimeProvider.tsx"
240
308
  "use client";
@@ -25,12 +25,12 @@ Key behaviors:
25
25
 
26
26
  ```tsx
27
27
  import {
28
+ defineToolkit,
28
29
  Tools,
29
- type Toolkit,
30
30
  MessagePartPrimitive,
31
31
  } from "@assistant-ui/react";
32
32
 
33
- const toolkit = {
33
+ const toolkit = defineToolkit({
34
34
  invoke_researcher: {
35
35
  type: "backend",
36
36
  render: ({ args, status }) => (
@@ -47,7 +47,7 @@ const toolkit = {
47
47
  </div>
48
48
  ),
49
49
  },
50
- } satisfies Toolkit;
50
+ });
51
51
  ```
52
52
 
53
53
  </Step>
@@ -154,7 +154,7 @@ When using LangGraph, subgraph events (`onSubgraphValues` / `onSubgraphUpdates`
154
154
  If a sub-agent's tool calls also have nested messages, the same pattern applies recursively:
155
155
 
156
156
  ```tsx
157
- const toolkit = {
157
+ const toolkit = defineToolkit({
158
158
  invoke_planner: {
159
159
  type: "backend",
160
160
  render: () => (
@@ -189,7 +189,7 @@ const toolkit = {
189
189
  </div>
190
190
  ),
191
191
  },
192
- } satisfies Toolkit;
192
+ });
193
193
  ```
194
194
 
195
195
  ## ReadonlyThreadProvider
@@ -96,14 +96,14 @@ Learn more about creating tools in the [Tools Guide](/docs/tools/defining-tools)
96
96
  If your tool is defined elsewhere (e.g., in your backend API, MCP server, or LangGraph), register a backend toolkit entry with just `render`:
97
97
 
98
98
  ```tsx
99
- const toolkit = {
99
+ const toolkit = defineToolkit({
100
100
  getWeather: {
101
101
  type: "backend",
102
102
  render: ({ args, result, status }) => {
103
103
  // UI rendering logic only
104
104
  },
105
105
  },
106
- } satisfies Toolkit;
106
+ });
107
107
  ```
108
108
 
109
109
  ## Quick Start Example
@@ -180,12 +180,12 @@ const WeatherToolUI: ToolCallMessagePartComponent<
180
180
  Put the renderer on the matching backend toolkit entry:
181
181
 
182
182
  ```tsx
183
- const toolkit = {
183
+ const toolkit = defineToolkit({
184
184
  getWeather: {
185
185
  type: "backend",
186
186
  render: WeatherToolUI,
187
187
  },
188
- } satisfies Toolkit;
188
+ });
189
189
 
190
190
  function App({ runtime }: { runtime: AssistantRuntime }) {
191
191
  const aui = useAui({ tools: Tools({ toolkit }) });
@@ -286,12 +286,12 @@ export const WebSearchToolUI: ToolCallMessagePartComponent<
286
286
  Register it on the toolkit:
287
287
 
288
288
  ```tsx
289
- const toolkit = {
289
+ const toolkit = defineToolkit({
290
290
  webSearch: {
291
291
  type: "backend",
292
292
  render: WebSearchToolUI,
293
293
  },
294
- } satisfies Toolkit;
294
+ });
295
295
  ```
296
296
 
297
297
  ### Dynamic Toolkit Pattern
@@ -301,7 +301,7 @@ Use a toolkit hook in its own file when the renderer needs component state:
301
301
  ```tsx title="analyze-data-toolkit.tsx"
302
302
  "use client";
303
303
 
304
- import { type Toolkit, useInlineRender } from "@assistant-ui/react";
304
+ import { defineToolkit, useInlineRender } from "@assistant-ui/react";
305
305
  import { useMemo } from "react";
306
306
 
307
307
  export function useAnalyzeDataToolkit(theme: "light" | "dark") {
@@ -317,12 +317,12 @@ export function useAnalyzeDataToolkit(theme: "light" | "dark") {
317
317
 
318
318
  return useMemo(
319
319
  () =>
320
- ({
320
+ defineToolkit({
321
321
  analyzeData: {
322
322
  type: "backend",
323
323
  render: renderAnalyzeData,
324
324
  },
325
- }) satisfies Toolkit,
325
+ }),
326
326
  [renderAnalyzeData],
327
327
  );
328
328
  }
@@ -358,7 +358,7 @@ For tools that need access to parent component props:
358
358
  ```tsx title="inventory-toolkit.tsx"
359
359
  "use client";
360
360
 
361
- import { type Toolkit, useInlineRender } from "@assistant-ui/react";
361
+ import { defineToolkit, useInlineRender } from "@assistant-ui/react";
362
362
  import { useMemo } from "react";
363
363
 
364
364
  export function useInventoryToolkit(productId: string, productName: string) {
@@ -376,12 +376,12 @@ export function useInventoryToolkit(productId: string, productName: string) {
376
376
 
377
377
  return useMemo(
378
378
  () =>
379
- ({
379
+ defineToolkit({
380
380
  checkInventory: {
381
381
  type: "backend",
382
382
  render: renderInventory,
383
383
  },
384
- }) satisfies Toolkit,
384
+ }),
385
385
  [renderInventory],
386
386
  );
387
387
  }
@@ -419,7 +419,7 @@ Create tools that collect user input during execution:
419
419
  </Callout>
420
420
 
421
421
  ```tsx
422
- const toolkit = {
422
+ const toolkit = defineToolkit({
423
423
  selectDate: {
424
424
  type: "human",
425
425
  description: "Ask the user to select a date.",
@@ -445,7 +445,7 @@ const toolkit = {
445
445
  );
446
446
  },
447
447
  },
448
- } satisfies Toolkit;
448
+ });
449
449
  ```
450
450
 
451
451
  ### Multi-Step Interactions
@@ -540,7 +540,7 @@ export default defineToolkit({
540
540
  Some runtimes (notably AI SDK v6's `needsApproval` tools) pause on the server and emit an approval request that the client must acknowledge before the tool runs. assistant-ui surfaces this on the tool part as `approval` and exposes `respondToApproval({ approved, reason? })` on the renderer:
541
541
 
542
542
  ```tsx
543
- const toolkit = {
543
+ const toolkit = defineToolkit({
544
544
  deploy: {
545
545
  type: "backend",
546
546
  render: ({ args, approval, respondToApproval, result }) => {
@@ -570,7 +570,7 @@ const toolkit = {
570
570
  return <p>Deployed</p>;
571
571
  },
572
572
  },
573
- } satisfies Toolkit;
573
+ });
574
574
  ```
575
575
 
576
576
  `approval.approved` is a three-state signal:
@@ -626,7 +626,7 @@ Sometimes you want to capture a tool call's streaming arguments but only render
626
626
  Return `null` from the tool UI's `render` until `status.type === "complete"`. The streaming args still arrive in `args` as the model emits them, you just ignore them until the call is done:
627
627
 
628
628
  ```tsx
629
- const toolkit = {
629
+ const toolkit = defineToolkit({
630
630
  renderChart: {
631
631
  type: "backend",
632
632
  render: ({ args, status }) => {
@@ -634,7 +634,7 @@ const toolkit = {
634
634
  return <Chart title={args.title} data={args.series} />;
635
635
  },
636
636
  },
637
- } satisfies Toolkit;
637
+ });
638
638
  ```
639
639
 
640
640
  The chart mounts once, with the final args, after streaming finishes. No re-renders during the stream.
@@ -682,7 +682,7 @@ Use `useToolArgsStatus` to react to per-field streaming state. The hook returns
682
682
  ```tsx
683
683
  import { useToolArgsStatus } from "@assistant-ui/react";
684
684
 
685
- const toolkit = {
685
+ const toolkit = defineToolkit({
686
686
  submitForm: {
687
687
  type: "backend",
688
688
  render: ({ args }) => {
@@ -714,7 +714,7 @@ const toolkit = {
714
714
  );
715
715
  },
716
716
  },
717
- } satisfies Toolkit;
717
+ });
718
718
  ```
719
719
 
720
720
  ### Partial Results & Streaming
@@ -722,7 +722,7 @@ const toolkit = {
722
722
  Display results as they stream in:
723
723
 
724
724
  ```tsx
725
- const toolkit = {
725
+ const toolkit = defineToolkit({
726
726
  analyzeData: {
727
727
  type: "backend",
728
728
  render: ({ result, status }) => {
@@ -757,7 +757,7 @@ const toolkit = {
757
757
  );
758
758
  },
759
759
  },
760
- } satisfies Toolkit;
760
+ });
761
761
  ```
762
762
 
763
763
  ### Custom Tool Fallback
@@ -802,7 +802,7 @@ type ToolCallMessagePartProps<TArgs, TResult> = {
802
802
  When a tool calls `human()` during execution, the payload becomes available in the render function as `interrupt.payload`:
803
803
 
804
804
  ```tsx
805
- const toolkit = {
805
+ const toolkit = defineToolkit({
806
806
  confirmAction: {
807
807
  type: "backend",
808
808
  render: ({ args, result, interrupt, resume }) => {
@@ -825,7 +825,7 @@ const toolkit = {
825
825
  return <div>Processing...</div>;
826
826
  },
827
827
  },
828
- } satisfies Toolkit;
828
+ });
829
829
  ```
830
830
 
831
831
  Learn more about tool human input in the [Tools Guide](/docs/tools/defining-tools#human-tools).
@@ -882,7 +882,7 @@ Use `useInlineRender` to prevent unnecessary re-renders:
882
882
  ```tsx title="heavy-computation-toolkit.tsx"
883
883
  "use client";
884
884
 
885
- import { type Toolkit, useInlineRender } from "@assistant-ui/react";
885
+ import { defineToolkit, useInlineRender } from "@assistant-ui/react";
886
886
  import { useMemo } from "react";
887
887
 
888
888
  export function useHeavyComputationToolkit() {
@@ -892,12 +892,12 @@ export function useHeavyComputationToolkit() {
892
892
 
893
893
  return useMemo(
894
894
  () =>
895
- ({
895
+ defineToolkit({
896
896
  heavyComputation: {
897
897
  type: "backend",
898
898
  render: renderHeavyComputation,
899
899
  },
900
- }) satisfies Toolkit,
900
+ }),
901
901
  [renderHeavyComputation],
902
902
  );
903
903
  }
@@ -17,12 +17,12 @@ Both flow through one connection lifecycle, one persisted state surface, and one
17
17
  ```
18
18
  useAui({ mcp: McpManagerResource({ connectors }) })
19
19
  │
20
- ├─ Tap resource — connection lifecycle, server lookup, OAuth/bearer auth
20
+ ├─ Resource — connection lifecycle, server lookup, OAuth/bearer auth
21
21
  ├─ Auto-mounts the modelContext scope when no chat runtime provides one
22
22
  └─ Registers connected tools as frontend tools — your chat sees them automatically
23
23
  ```
24
24
 
25
- The manager is a single tap resource. Mount it with `useAui` like any other scope. OAuth (PKCE + RFC 7591 dynamic client registration), bearer, and "no auth" are first-class. Token refresh runs inside the MCP SDK on 401; this package mediates persistence and the redirect step.
25
+ The manager is a single resource. Mount it with `useAui` like any other scope. OAuth (PKCE + RFC 7591 dynamic client registration), bearer, and "no auth" are first-class. Token refresh runs inside the MCP SDK on 401; this package mediates persistence and the redirect step.
26
26
 
27
27
  ## Setup
28
28
 
@@ -221,7 +221,7 @@ Tool names are prefixed `serverId__toolName` to avoid collisions across connecte
221
221
  If no chat runtime is mounted, `McpManagerResource` brings its own minimal `modelContext` along. Tools are still callable directly:
222
222
 
223
223
  ```ts
224
- // In an event handler — never in render. See the tap conventions.
224
+ // In an event handler, never in render.
225
225
  const aui = useAui();
226
226
  const out = await aui.mcp().server({ id: "linear" }).callTool("search", { q });
227
227
  ```
@@ -293,7 +293,7 @@ const connectionState = useAuiState((s) => s.mcpServer.connectionState);
293
293
  // ^ requires McpServerByIdProvider
294
294
  ```
295
295
 
296
- Imperative methods — `useAui` + resolve in a callback (never during render — see the [tap skill](https://github.com/assistant-ui/assistant-ui/blob/main/.claude/skills/tap/SKILL.md) for why):
296
+ Imperative methods: `useAui` + resolve in a callback (never during render):
297
297
 
298
298
  ```ts
299
299
  const aui = useAui();
@@ -50,34 +50,41 @@ export const MarkdownText = memo(MarkdownTextImpl);
50
50
 
51
51
  ## Configuration
52
52
 
53
- Configure mermaid options in `mermaid-diagram.tsx`:
53
+ Configure rendering options in `mermaid-diagram.tsx`:
54
54
 
55
55
  ```tsx title="/components/assistant-ui/mermaid-diagram.tsx"
56
- mermaid.initialize({ theme: "default" });
56
+ renderMermaidSVG(code, {
57
+ bg: "var(--background)",
58
+ fg: "var(--foreground)",
59
+ muted: "var(--muted-foreground)",
60
+ border: "var(--border)",
61
+ accent: "var(--foreground)",
62
+ transparent: true,
63
+ });
57
64
  ```
58
65
 
66
+ The palette follows your theme's background, foreground, and shadcn color tokens, so diagrams match light and dark mode automatically.
67
+
59
68
  ## Streaming Performance
60
69
 
61
70
  The `MermaidDiagram` component is optimized for streaming scenarios:
62
71
 
63
- - **Smart completion detection**: Only renders when the specific code block is complete
64
- - **Zero failed renders**: Avoids parsing incomplete diagram code during streaming
72
+ - **Skeleton while streaming**: Shows a placeholder skeleton until the response finishes streaming, then renders the diagram synchronously
73
+ - **Raw source fallback**: Invalid or unsupported diagrams fall back to displaying the raw source
65
74
 
66
75
 
67
76
  ## Supported Diagram Types
68
77
 
69
- Mermaid supports various diagram types including:
78
+ The component renders these diagram types:
70
79
 
71
80
  - Flowcharts and decision trees
72
81
  - Sequence diagrams
73
- - Gantt charts
74
82
  - Class diagrams
75
83
  - State diagrams
76
- - Git graphs
77
- - User journey maps
78
84
  - Entity relationship diagrams
85
+ - XY charts (bar, line, combined)
79
86
 
80
- See the [Mermaid documentation](https://mermaid.js.org/) for complete syntax reference.
87
+ Other mermaid diagram types fall back to displaying the raw source. See the [Mermaid documentation](https://mermaid.js.org/) for syntax reference.
81
88
 
82
89
  ## Related Components
83
90
 
@@ -213,7 +213,7 @@ Tool UIs fall into three buckets: prompting the user (human-in-the-loop), inform
213
213
  Mark a tool with `display: "standalone"` to keep its UI out of the grouped trace. `human` tools and MCP apps are standalone automatically; every other tool defaults to `"inline"` and opts in explicitly:
214
214
 
215
215
  ```tsx
216
- const toolkit = {
216
+ const toolkit = defineToolkit({
217
217
  ask_user: { type: "human", render: AskUI }, // standalone (forced)
218
218
  search_web: { type: "frontend", render: SearchUI }, // inline trace (default)
219
219
  checkout: {
@@ -221,7 +221,7 @@ const toolkit = {
221
221
  render: CheckoutUI,
222
222
  display: "standalone", // opt in
223
223
  },
224
- } satisfies Toolkit;
224
+ });
225
225
  ```
226
226
 
227
227
  The synthetic `"standalone-tool-call"` key on `groupPartByType` matches all of these. `MessagePrimitive.GroupedParts` passes the live tool-UI registry to `groupBy` as a second `context` argument, and the helper reads it to resolve the registry-driven cases — MCP-app calls are detected from the part alone, so nothing is threaded in:
@@ -9,6 +9,7 @@ import {
9
9
  ToolFallbackRunningSample,
10
10
  ToolFallbackCancelledSample,
11
11
  ToolFallbackStreamingSample,
12
+ ToolFallbackRequiresActionSample,
12
13
  } from "@/components/docs/samples/tool-fallback";
13
14
 
14
15
  <ToolFallbackSample />
@@ -71,6 +72,12 @@ Shows a muted appearance when a tool call was cancelled.
71
72
 
72
73
  <ToolFallbackCancelledSample />
73
74
 
75
+ ### Approval State
76
+
77
+ Shows the default Allow / Deny buttons rendered when a tool call enters `requires-action`. The block auto-expands so the decision is visible without a click. Edit your project's copy of `tool-fallback.tsx` to add trust escalation, edit-args, custom labels, or analytics hooks — the shadcn philosophy is you own the file.
78
+
79
+ <ToolFallbackRequiresActionSample />
80
+
74
81
  ## Composable API
75
82
 
76
83
  All sub-components are exported for custom layouts:
@@ -83,6 +90,7 @@ All sub-components are exported for custom layouts:
83
90
  | `ToolFallback.Args` | Displays tool arguments |
84
91
  | `ToolFallback.Result` | Displays tool execution result |
85
92
  | `ToolFallback.Error` | Displays error or cancellation messages |
93
+ | `ToolFallback.Approval` | Renders Allow / Deny buttons for `requires-action` tools; wires `resume` / `addResult` / `respondToApproval` |
86
94
 
87
95
  ```tsx
88
96
  import {
@@ -93,6 +101,7 @@ import {
93
101
  ToolFallbackArgs,
94
102
  ToolFallbackResult,
95
103
  ToolFallbackError,
104
+ ToolFallbackApproval,
96
105
  } from "@/components/assistant-ui/tool-fallback";
97
106
 
98
107
  // Compound component syntax
@@ -101,6 +110,13 @@ import {
101
110
  <ToolFallback.Content>
102
111
  <ToolFallback.Error status={status} />
103
112
  <ToolFallback.Args argsText={argsText} />
113
+ <ToolFallback.Approval
114
+ addResult={addResult}
115
+ resume={resume}
116
+ interrupt={interrupt}
117
+ approval={approval}
118
+ respondToApproval={respondToApproval}
119
+ />
104
120
  <ToolFallback.Result result={result} />
105
121
  </ToolFallback.Content>
106
122
  </ToolFallback.Root>
@@ -13,7 +13,7 @@ platforms: ["react"]
13
13
  - **Headless** — Zero styling opinions, bring your own CSS / Tailwind.
14
14
  - **Composable** — Radix-style primitive parts you fully control.
15
15
  - **Tree-aware** — Automatic depth, parent / child collapse, time range computation.
16
- - **Tap-powered** — Built on the same `@assistant-ui/tap` reactive primitives as the runtimes.
16
+ - **Reactive** — Built on the same reactive core as the runtimes, so spans stream into your UI as they change.
17
17
 
18
18
  ## Installation
19
19
 
@@ -98,7 +98,7 @@ function SpanRow() {
98
98
 
99
99
  ### SpanResource
100
100
 
101
- Tap resource that ingests raw span data and exposes a tree-aware reactive state to primitives.
101
+ Resource that ingests raw span data and exposes a tree-aware reactive state to primitives.
102
102
 
103
103
  ```ts
104
104
  SpanResource({ spans }: { spans: SpanData[] }): ClientOutput<"span">;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","names":[],"sources":["../src/constants.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport { dirname, join } from \"node:path\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport const ROOT_DIR = join(__dirname, \"../../../\");\nexport const PACKAGE_DIR = join(__dirname, \"../\");\n\nexport const EXAMPLES_PATH = join(ROOT_DIR, \"examples\");\n\nconst DOCS_BASE = join(PACKAGE_DIR, \".docs\");\nexport const DOCS_PATH = join(DOCS_BASE, \"raw/docs\");\nexport const CODE_EXAMPLES_PATH = join(DOCS_BASE, \"organized/code-examples\");\n\nexport const MDX_EXTENSION = \".mdx\";\nexport const MD_EXTENSION = \".md\";\n\nexport const MAX_FILE_SIZE = 10 * 1024 * 1024;\n\nexport const IS_PREPARE_MODE = process.argv[1]?.includes(\n \"prepare-docs/prepare\",\n);\n"],"mappings":";;;AAGA,MAAM,YAAY,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC;AAExD,MAAa,WAAW,KAAK,WAAW,WAAW;AACnD,MAAa,cAAc,KAAK,WAAW,KAAK;AAEhD,MAAa,gBAAgB,KAAK,UAAU,UAAU;AAEtD,MAAM,YAAY,KAAK,aAAa,OAAO;AAC3C,MAAa,YAAY,KAAK,WAAW,UAAU;AACnD,MAAa,qBAAqB,KAAK,WAAW,yBAAyB;AAE3E,MAAa,gBAAgB;AAC7B,MAAa,eAAe;AAE5B,MAAa,gBAAgB,KAAK,OAAO;AAEzC,MAAa,kBAAkB,QAAQ,KAAK,IAAI,SAC9C,sBACF"}
1
+ {"version":3,"file":"constants.js","names":[],"sources":["../src/constants.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport { dirname, join } from \"node:path\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport const ROOT_DIR = join(__dirname, \"../../../\");\nexport const PACKAGE_DIR = join(__dirname, \"../\");\n\nexport const EXAMPLES_PATH = join(ROOT_DIR, \"examples\");\n\nconst DOCS_BASE = join(PACKAGE_DIR, \".docs\");\nexport const DOCS_PATH = join(DOCS_BASE, \"raw/docs\");\nexport const CODE_EXAMPLES_PATH = join(DOCS_BASE, \"organized/code-examples\");\n\nexport const MDX_EXTENSION = \".mdx\";\nexport const MD_EXTENSION = \".md\";\n\nexport const MAX_FILE_SIZE = 10 * 1024 * 1024;\n\nexport const IS_PREPARE_MODE = process.argv[1]?.includes(\n \"prepare-docs/prepare\",\n);\n"],"mappings":";;;AAGA,MAAM,YAAY,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC;AAExD,MAAa,WAAW,KAAK,WAAW,WAAW;AACnD,MAAa,cAAc,KAAK,WAAW,KAAK;AAEhD,MAAa,gBAAgB,KAAK,UAAU,UAAU;AAEtD,MAAM,YAAY,KAAK,aAAa,OAAO;AAC3C,MAAa,YAAY,KAAK,WAAW,UAAU;AACnD,MAAa,qBAAqB,KAAK,WAAW,yBAAyB;AAE3E,MAAa,gBAAgB;AAC7B,MAAa,eAAe;AAE5B,MAAa,gBAAgB,KAAK,OAAO;AAEzC,MAAa,kBAAkB,QAAQ,KAAK,EAAE,EAAE,SAC9C,sBACF"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { docsTools } from \"./tools/docs.js\";\nimport { examplesTools } from \"./tools/examples.js\";\nimport { logger } from \"./utils/logger.js\";\nimport { PACKAGE_DIR } from \"./constants.js\";\n\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst packageJson = JSON.parse(\n readFileSync(join(PACKAGE_DIR, \"package.json\"), \"utf-8\"),\n);\n\nexport const server = new McpServer({\n name: \"assistant-ui-docs\",\n version: packageJson.version,\n});\n\nserver.tool(\n docsTools.name,\n docsTools.description,\n docsTools.parameters,\n docsTools.execute,\n);\nserver.tool(\n examplesTools.name,\n examplesTools.description,\n examplesTools.parameters,\n examplesTools.execute,\n);\n\nexport async function runServer() {\n try {\n logger.info(\n `Starting assistant-ui MCP docs server v${packageJson.version}`,\n );\n const transport = new StdioServerTransport();\n await server.connect(transport);\n } catch (error) {\n logger.error(\"Failed to start MCP server\", error);\n process.exit(1);\n }\n}\n\nif (import.meta.url === `file://${process.argv[1]}`) {\n void runServer().catch((error) => {\n console.error(\"Failed to start server:\", error);\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;;;AAUA,MAAM,cAAc,KAAK,MACvB,aAAa,KAAK,aAAa,cAAc,GAAG,OAAO,CACzD;AAEA,MAAa,SAAS,IAAI,UAAU;CAClC,MAAM;CACN,SAAS,YAAY;AACvB,CAAC;AAED,OAAO,KACL,UAAU,MACV,UAAU,aACV,UAAU,YACV,UAAU,OACZ;AACA,OAAO,KACL,cAAc,MACd,cAAc,aACd,cAAc,YACd,cAAc,OAChB;AAEA,eAAsB,YAAY;CAChC,IAAI;EACF,OAAO,KACL,0CAA0C,YAAY,SACxD;EACA,MAAM,YAAY,IAAI,qBAAqB;EAC3C,MAAM,OAAO,QAAQ,SAAS;CAChC,SAAS,OAAO;EACd,OAAO,MAAM,8BAA8B,KAAK;EAChD,QAAQ,KAAK,CAAC;CAChB;AACF;AAEA,IAAI,OAAO,KAAK,QAAQ,UAAU,QAAQ,KAAK,MAC7C,UAAe,EAAE,OAAO,UAAU;CAChC,QAAQ,MAAM,2BAA2B,KAAK;CAC9C,QAAQ,KAAK,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { docsTools } from \"./tools/docs.js\";\nimport { examplesTools } from \"./tools/examples.js\";\nimport { logger } from \"./utils/logger.js\";\nimport { PACKAGE_DIR } from \"./constants.js\";\n\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst packageJson = JSON.parse(\n readFileSync(join(PACKAGE_DIR, \"package.json\"), \"utf-8\"),\n);\n\nexport const server = new McpServer({\n name: \"assistant-ui-docs\",\n version: packageJson.version,\n});\n\nserver.tool(\n docsTools.name,\n docsTools.description,\n docsTools.parameters,\n docsTools.execute,\n);\nserver.tool(\n examplesTools.name,\n examplesTools.description,\n examplesTools.parameters,\n examplesTools.execute,\n);\n\nexport async function runServer() {\n try {\n logger.info(\n `Starting assistant-ui MCP docs server v${packageJson.version}`,\n );\n const transport = new StdioServerTransport();\n await server.connect(transport);\n } catch (error) {\n logger.error(\"Failed to start MCP server\", error);\n process.exit(1);\n }\n}\n\nif (import.meta.url === `file://${process.argv[1]}`) {\n void runServer().catch((error) => {\n console.error(\"Failed to start server:\", error);\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;;;AAUA,MAAM,cAAc,KAAK,MACvB,aAAa,KAAK,aAAa,cAAc,GAAG,OAAO,CACzD;AAEA,MAAa,SAAS,IAAI,UAAU;CAClC,MAAM;CACN,SAAS,YAAY;AACvB,CAAC;AAED,OAAO,KACL,UAAU,MACV,UAAU,aACV,UAAU,YACV,UAAU,OACZ;AACA,OAAO,KACL,cAAc,MACd,cAAc,aACd,cAAc,YACd,cAAc,OAChB;AAEA,eAAsB,YAAY;CAChC,IAAI;EACF,OAAO,KACL,0CAA0C,YAAY,SACxD;EACA,MAAM,YAAY,IAAI,qBAAqB;EAC3C,MAAM,OAAO,QAAQ,SAAS;CAChC,SAAS,OAAO;EACd,OAAO,MAAM,8BAA8B,KAAK;EAChD,QAAQ,KAAK,CAAC;CAChB;AACF;AAEA,IAAI,OAAO,KAAK,QAAQ,UAAU,QAAQ,KAAK,MAC7C,UAAe,CAAC,CAAC,OAAO,UAAU;CAChC,QAAQ,MAAM,2BAA2B,KAAK;CAC9C,QAAQ,KAAK,CAAC;AAChB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"code-examples.js","names":[],"sources":["../../src/prepare-docs/code-examples.ts"],"sourcesContent":["import { rm, mkdir, readdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join, relative, extname } from \"node:path\";\nimport { logger } from \"../utils/logger.js\";\nimport { ROOT_DIR, EXAMPLES_PATH } from \"../constants.js\";\n\nconst OUTPUT_DIR = join(\n ROOT_DIR,\n \"packages/mcp-docs-server/.docs/organized/code-examples\",\n);\nconst MAX_LINES = 10000;\n\ninterface FileContent {\n path: string;\n content: string;\n}\n\nasync function scanDirectory(\n dir: string,\n baseDir: string,\n): Promise<FileContent[]> {\n const files: FileContent[] = [];\n\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n const skipDirs = [\n \"node_modules\",\n \"dist\",\n \"build\",\n \".next\",\n \".git\",\n \".turbo\",\n ];\n if (!skipDirs.includes(entry.name)) {\n const subFiles = await scanDirectory(fullPath, baseDir);\n files.push(...subFiles);\n }\n } else if (entry.isFile()) {\n const includeExts = [\n \".ts\",\n \".tsx\",\n \".js\",\n \".jsx\",\n \".json\",\n \".css\",\n \".md\",\n \".mdx\",\n ];\n const ext = extname(entry.name).toLowerCase();\n\n if (\n includeExts.includes(ext) ||\n entry.name === \"package.json\" ||\n entry.name === \"tsconfig.json\"\n ) {\n try {\n const content = await readFile(fullPath, \"utf-8\");\n const relativePath = relative(baseDir, fullPath);\n files.push({ path: relativePath, content });\n } catch (error) {\n logger.warn(`Failed to read file: ${fullPath}`, error);\n }\n }\n }\n }\n } catch (error) {\n logger.error(`Failed to scan directory: ${dir}`, error);\n }\n\n return files;\n}\n\nfunction getFileType(filename: string): string {\n const ext = extname(filename).toLowerCase();\n const extMap: Record<string, string> = {\n \".ts\": \"typescript\",\n \".tsx\": \"tsx\",\n \".js\": \"javascript\",\n \".jsx\": \"jsx\",\n \".json\": \"json\",\n \".css\": \"css\",\n \".md\": \"markdown\",\n \".mdx\": \"mdx\",\n };\n return extMap[ext] || \"text\";\n}\n\nexport async function prepareCodeExamples(): Promise<void> {\n logger.info(\"Preparing code examples...\");\n\n try {\n await rm(OUTPUT_DIR, { recursive: true, force: true });\n await mkdir(OUTPUT_DIR, { recursive: true });\n\n const exampleDirs = await readdir(EXAMPLES_PATH, { withFileTypes: true });\n\n for (const dir of exampleDirs) {\n if (dir.isDirectory() && !dir.name.startsWith(\".\")) {\n const examplePath = join(EXAMPLES_PATH, dir.name);\n logger.info(`Processing example: ${dir.name}`);\n\n let description = \"\";\n try {\n const packageJsonPath = join(examplePath, \"package.json\");\n const packageJson = JSON.parse(\n await readFile(packageJsonPath, \"utf-8\"),\n );\n description = packageJson.description || \"\";\n } catch (error: any) {\n if (error?.code !== \"ENOENT\") {\n logger.warn(`Failed to read package.json for ${dir.name}:`, error);\n } else {\n logger.debug(`No package.json found for example: ${dir.name}`);\n }\n }\n\n const files = await scanDirectory(examplePath, examplePath);\n\n files.sort((a, b) => a.path.localeCompare(b.path));\n\n let markdown = `# Example: ${dir.name}\\n\\n`;\n if (description) {\n markdown += `${description}\\n\\n`;\n }\n\n let totalLines = 0;\n for (const file of files) {\n const lines = file.content.split(\"\\n\").length;\n if (totalLines + lines > MAX_LINES) {\n markdown += `\\n_Note: Additional files truncated due to size limits_\\n`;\n break;\n }\n\n // Normalize Windows backslashes to forward slashes for consistent markdown output\n markdown += `## ${file.path.replace(/\\\\/g, \"/\")}\\n\\n`;\n markdown += `\\`\\`\\`${getFileType(file.path)}\\n`;\n markdown += file.content;\n markdown += `\\n\\`\\`\\`\\n\\n`;\n\n totalLines += lines;\n }\n\n const outputPath = join(OUTPUT_DIR, `${dir.name}.md`);\n await writeFile(outputPath, markdown, \"utf-8\");\n logger.debug(`Created example: ${outputPath}`);\n }\n }\n\n logger.info(\"Code examples preparation complete\");\n } catch (error) {\n logger.error(\"Failed to prepare code examples\", error);\n throw error;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"code-examples.js","names":[],"sources":["../../src/prepare-docs/code-examples.ts"],"sourcesContent":["import { rm, mkdir, readdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join, relative, extname } from \"node:path\";\nimport { logger } from \"../utils/logger.js\";\nimport { ROOT_DIR, EXAMPLES_PATH } from \"../constants.js\";\n\nconst OUTPUT_DIR = join(\n ROOT_DIR,\n \"packages/mcp-docs-server/.docs/organized/code-examples\",\n);\nconst MAX_LINES = 10000;\n\ninterface FileContent {\n path: string;\n content: string;\n}\n\nasync function scanDirectory(\n dir: string,\n baseDir: string,\n): Promise<FileContent[]> {\n const files: FileContent[] = [];\n\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n const skipDirs = [\n \"node_modules\",\n \"dist\",\n \"build\",\n \".next\",\n \".git\",\n \".turbo\",\n ];\n if (!skipDirs.includes(entry.name)) {\n const subFiles = await scanDirectory(fullPath, baseDir);\n files.push(...subFiles);\n }\n } else if (entry.isFile()) {\n const includeExts = [\n \".ts\",\n \".tsx\",\n \".js\",\n \".jsx\",\n \".json\",\n \".css\",\n \".md\",\n \".mdx\",\n ];\n const ext = extname(entry.name).toLowerCase();\n\n if (\n includeExts.includes(ext) ||\n entry.name === \"package.json\" ||\n entry.name === \"tsconfig.json\"\n ) {\n try {\n const content = await readFile(fullPath, \"utf-8\");\n const relativePath = relative(baseDir, fullPath);\n files.push({ path: relativePath, content });\n } catch (error) {\n logger.warn(`Failed to read file: ${fullPath}`, error);\n }\n }\n }\n }\n } catch (error) {\n logger.error(`Failed to scan directory: ${dir}`, error);\n }\n\n return files;\n}\n\nfunction getFileType(filename: string): string {\n const ext = extname(filename).toLowerCase();\n const extMap: Record<string, string> = {\n \".ts\": \"typescript\",\n \".tsx\": \"tsx\",\n \".js\": \"javascript\",\n \".jsx\": \"jsx\",\n \".json\": \"json\",\n \".css\": \"css\",\n \".md\": \"markdown\",\n \".mdx\": \"mdx\",\n };\n return extMap[ext] || \"text\";\n}\n\nexport async function prepareCodeExamples(): Promise<void> {\n logger.info(\"Preparing code examples...\");\n\n try {\n await rm(OUTPUT_DIR, { recursive: true, force: true });\n await mkdir(OUTPUT_DIR, { recursive: true });\n\n const exampleDirs = await readdir(EXAMPLES_PATH, { withFileTypes: true });\n\n for (const dir of exampleDirs) {\n if (dir.isDirectory() && !dir.name.startsWith(\".\")) {\n const examplePath = join(EXAMPLES_PATH, dir.name);\n logger.info(`Processing example: ${dir.name}`);\n\n let description = \"\";\n try {\n const packageJsonPath = join(examplePath, \"package.json\");\n const packageJson = JSON.parse(\n await readFile(packageJsonPath, \"utf-8\"),\n );\n description = packageJson.description || \"\";\n } catch (error: any) {\n if (error?.code !== \"ENOENT\") {\n logger.warn(`Failed to read package.json for ${dir.name}:`, error);\n } else {\n logger.debug(`No package.json found for example: ${dir.name}`);\n }\n }\n\n const files = await scanDirectory(examplePath, examplePath);\n\n files.sort((a, b) => a.path.localeCompare(b.path));\n\n let markdown = `# Example: ${dir.name}\\n\\n`;\n if (description) {\n markdown += `${description}\\n\\n`;\n }\n\n let totalLines = 0;\n for (const file of files) {\n const lines = file.content.split(\"\\n\").length;\n if (totalLines + lines > MAX_LINES) {\n markdown += `\\n_Note: Additional files truncated due to size limits_\\n`;\n break;\n }\n\n // Normalize Windows backslashes to forward slashes for consistent markdown output\n markdown += `## ${file.path.replace(/\\\\/g, \"/\")}\\n\\n`;\n markdown += `\\`\\`\\`${getFileType(file.path)}\\n`;\n markdown += file.content;\n markdown += `\\n\\`\\`\\`\\n\\n`;\n\n totalLines += lines;\n }\n\n const outputPath = join(OUTPUT_DIR, `${dir.name}.md`);\n await writeFile(outputPath, markdown, \"utf-8\");\n logger.debug(`Created example: ${outputPath}`);\n }\n }\n\n logger.info(\"Code examples preparation complete\");\n } catch (error) {\n logger.error(\"Failed to prepare code examples\", error);\n throw error;\n }\n}\n"],"mappings":"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"}