@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.
- package/.docs/organized/code-examples/waterfall.md +5 -5
- package/.docs/organized/code-examples/with-a2a.md +5 -5
- package/.docs/organized/code-examples/with-ag-ui.md +9 -9
- package/.docs/organized/code-examples/with-ai-sdk-v6.md +7 -7
- package/.docs/organized/code-examples/with-artifacts.md +37 -31
- package/.docs/organized/code-examples/with-assistant-transport.md +8 -8
- package/.docs/organized/code-examples/with-browser-extension.md +5 -5
- package/.docs/organized/code-examples/with-chain-of-thought.md +68 -47
- package/.docs/organized/code-examples/with-cloud-standalone.md +7 -7
- package/.docs/organized/code-examples/with-cloud.md +7 -7
- package/.docs/organized/code-examples/with-custom-thread-list.md +7 -7
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +8 -8
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +8 -8
- package/.docs/organized/code-examples/with-expo.md +33 -24
- package/.docs/organized/code-examples/with-external-store.md +5 -5
- package/.docs/organized/code-examples/with-ffmpeg.md +10 -10
- package/.docs/organized/code-examples/with-generative-ui.md +70 -64
- package/.docs/organized/code-examples/with-google-adk.md +6 -6
- package/.docs/organized/code-examples/with-heat-graph.md +5 -5
- package/.docs/organized/code-examples/with-image-generation.md +7 -7
- package/.docs/organized/code-examples/with-interactables.md +7 -7
- package/.docs/organized/code-examples/with-langchain.md +7 -7
- package/.docs/organized/code-examples/with-langgraph.md +30 -26
- package/.docs/organized/code-examples/with-livekit.md +8 -8
- package/.docs/organized/code-examples/with-mcp.md +8 -8
- package/.docs/organized/code-examples/with-opencode.md +6 -6
- package/.docs/organized/code-examples/with-react-hook-form.md +7 -7
- package/.docs/organized/code-examples/with-react-ink.md +295 -100
- package/.docs/organized/code-examples/with-react-router.md +11 -11
- package/.docs/organized/code-examples/with-resumable-stream.md +8 -8
- package/.docs/organized/code-examples/with-store.md +64 -64
- package/.docs/organized/code-examples/with-tanstack.md +8 -8
- package/.docs/organized/code-examples/with-tap-runtime.md +5 -5
- package/.docs/raw/docs/(docs)/architecture.mdx +52 -41
- package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +3 -0
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +14 -3
- package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +94 -3
- package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +5 -69
- package/.docs/raw/docs/ink/adapters.mdx +23 -1
- package/.docs/raw/docs/ink/hooks.mdx +20 -17
- package/.docs/raw/docs/migrations/toolkit-tools.mdx +14 -8
- package/.docs/raw/docs/react-native/hooks.mdx +25 -17
- package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +41 -0
- package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +3 -3
- package/.docs/raw/docs/runtimes/concepts/architecture.mdx +46 -4
- package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +54 -12
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +62 -4
- package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +12 -0
- package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +9 -9
- package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +13 -0
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +5 -5
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +3 -3
- package/.docs/raw/docs/tools/backend.mdx +19 -11
- package/.docs/raw/docs/tools/defining-tools.mdx +177 -52
- package/.docs/raw/docs/tools/index.mdx +7 -12
- package/.docs/raw/docs/tools/mcp.mdx +83 -15
- package/.docs/raw/docs/tools/multi-agent.mdx +5 -5
- package/.docs/raw/docs/tools/tool-ui.mdx +27 -27
- package/.docs/raw/docs/tools/user-managed-mcp.mdx +4 -4
- package/.docs/raw/docs/ui/mermaid.mdx +16 -9
- package/.docs/raw/docs/ui/part-grouping.mdx +2 -2
- package/.docs/raw/docs/ui/tool-fallback.mdx +16 -0
- package/.docs/raw/docs/utilities/react-o11y.mdx +2 -2
- package/dist/constants.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/prepare-docs/code-examples.js.map +1 -1
- package/dist/prepare-docs/copy-raw.js.map +1 -1
- package/dist/prepare-docs/prepare.js.map +1 -1
- package/dist/stdio.js.map +1 -1
- package/dist/tools/docs.js.map +1 -1
- package/dist/tools/examples.js.map +1 -1
- package/dist/tools/tests/test-setup.js.map +1 -1
- package/dist/utils/mdx.js.map +1 -1
- package/dist/utils/paths.js.map +1 -1
- package/package.json +4 -4
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/assistant-frame.mdx +0 -0
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/make-assistant-visible.mdx +0 -0
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/model-context.mdx +0 -0
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/motivation.mdx +0 -0
- /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
|
|
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
|
|
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/
|
|
150
|
-
"use
|
|
213
|
+
```tsx title="app/toolkit.tsx"
|
|
214
|
+
"use generative";
|
|
151
215
|
|
|
152
|
-
import
|
|
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
|
|
221
|
+
export default defineToolkit({
|
|
222
|
+
...defineMcpToolkit({
|
|
223
|
+
github: { type: "http", url: "https://mcp.example.com/mcp" },
|
|
224
|
+
}),
|
|
158
225
|
github_get_issue: {
|
|
159
|
-
|
|
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
|
-
}
|
|
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
|
|
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
|
-
}
|
|
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
|
|
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
|
-
}
|
|
298
|
+
});
|
|
232
299
|
```
|
|
233
300
|
|
|
234
301
|
</Tab>
|
|
235
302
|
</PlatformTabs>
|
|
236
303
|
|
|
237
|
-
Register the toolkit once with `Tools({ toolkit })`.
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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 {
|
|
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
|
-
})
|
|
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 {
|
|
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
|
-
})
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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 {
|
|
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
|
-
})
|
|
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
|
-
├─
|
|
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
|
|
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
|
|
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
|
|
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
|
|
53
|
+
Configure rendering options in `mermaid-diagram.tsx`:
|
|
54
54
|
|
|
55
55
|
```tsx title="/components/assistant-ui/mermaid-diagram.tsx"
|
|
56
|
-
|
|
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
|
-
- **
|
|
64
|
-
- **
|
|
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
|
-
|
|
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
|
|
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
|
-
}
|
|
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
|
-
- **
|
|
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
|
-
|
|
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">;
|
package/dist/constants.js.map
CHANGED
|
@@ -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,
|
|
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,
|
|
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":";;;;;AAKA,MAAM,aAAa,KACjB,UACA,wDACF;AACA,MAAM,YAAY;AAOlB,eAAe,cACb,KACA,SACwB;CACxB,MAAM,QAAuB,CAAC;CAE9B,IAAI;EACF,MAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;EAE1D,KAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,WAAW,KAAK,KAAK,MAAM,IAAI;GAErC,IAAI,MAAM,YAAY;QAShB,CAAC;KAPH;KACA;KACA;KACA;KACA;KACA;IAEU,
|
|
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":";;;;;AAKA,MAAM,aAAa,KACjB,UACA,wDACF;AACA,MAAM,YAAY;AAOlB,eAAe,cACb,KACA,SACwB;CACxB,MAAM,QAAuB,CAAC;CAE9B,IAAI;EACF,MAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;EAE1D,KAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,WAAW,KAAK,KAAK,MAAM,IAAI;GAErC,IAAI,MAAM,YAAY;QAShB,CAAC;KAPH;KACA;KACA;KACA;KACA;KACA;IAEU,CAAC,CAAC,SAAS,MAAM,IAAI,GAAG;KAClC,MAAM,WAAW,MAAM,cAAc,UAAU,OAAO;KACtD,MAAM,KAAK,GAAG,QAAQ;IACxB;UACK,IAAI,MAAM,OAAO,GAAG;IACzB,MAAM,cAAc;KAClB;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF;IACA,MAAM,MAAM,QAAQ,MAAM,IAAI,CAAC,CAAC,YAAY;IAE5C,IACE,YAAY,SAAS,GAAG,KACxB,MAAM,SAAS,kBACf,MAAM,SAAS,iBAEf,IAAI;KACF,MAAM,UAAU,MAAM,SAAS,UAAU,OAAO;KAChD,MAAM,eAAe,SAAS,SAAS,QAAQ;KAC/C,MAAM,KAAK;MAAE,MAAM;MAAc;KAAQ,CAAC;IAC5C,SAAS,OAAO;KACd,OAAO,KAAK,wBAAwB,YAAY,KAAK;IACvD;GAEJ;EACF;CACF,SAAS,OAAO;EACd,OAAO,MAAM,6BAA6B,OAAO,KAAK;CACxD;CAEA,OAAO;AACT;AAEA,SAAS,YAAY,UAA0B;CAY7C,OAAO;EATL,OAAO;EACP,QAAQ;EACR,OAAO;EACP,QAAQ;EACR,SAAS;EACT,QAAQ;EACR,OAAO;EACP,QAAQ;CAEE,EAXA,QAAQ,QAAQ,CAAC,CAAC,YAWd,MAAM;AACxB;AAEA,eAAsB,sBAAqC;CACzD,OAAO,KAAK,4BAA4B;CAExC,IAAI;EACF,MAAM,GAAG,YAAY;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;EACrD,MAAM,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;EAE3C,MAAM,cAAc,MAAM,QAAQ,eAAe,EAAE,eAAe,KAAK,CAAC;EAExE,KAAK,MAAM,OAAO,aAChB,IAAI,IAAI,YAAY,KAAK,CAAC,IAAI,KAAK,WAAW,GAAG,GAAG;GAClD,MAAM,cAAc,KAAK,eAAe,IAAI,IAAI;GAChD,OAAO,KAAK,uBAAuB,IAAI,MAAM;GAE7C,IAAI,cAAc;GAClB,IAAI;IACF,MAAM,kBAAkB,KAAK,aAAa,cAAc;IAIxD,cAHoB,KAAK,MACvB,MAAM,SAAS,iBAAiB,OAAO,CAEjB,CAAC,CAAC,eAAe;GAC3C,SAAS,OAAY;IACnB,IAAI,OAAO,SAAS,UAClB,OAAO,KAAK,mCAAmC,IAAI,KAAK,IAAI,KAAK;SAEjE,OAAO,MAAM,sCAAsC,IAAI,MAAM;GAEjE;GAEA,MAAM,QAAQ,MAAM,cAAc,aAAa,WAAW;GAE1D,MAAM,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;GAEjD,IAAI,WAAW,cAAc,IAAI,KAAK;GACtC,IAAI,aACF,YAAY,GAAG,YAAY;GAG7B,IAAI,aAAa;GACjB,KAAK,MAAM,QAAQ,OAAO;IACxB,MAAM,QAAQ,KAAK,QAAQ,MAAM,IAAI,CAAC,CAAC;IACvC,IAAI,aAAa,QAAQ,WAAW;KAClC,YAAY;KACZ;IACF;IAGA,YAAY,MAAM,KAAK,KAAK,QAAQ,OAAO,GAAG,EAAE;IAChD,YAAY,SAAS,YAAY,KAAK,IAAI,EAAE;IAC5C,YAAY,KAAK;IACjB,YAAY;IAEZ,cAAc;GAChB;GAEA,MAAM,aAAa,KAAK,YAAY,GAAG,IAAI,KAAK,IAAI;GACpD,MAAM,UAAU,YAAY,UAAU,OAAO;GAC7C,OAAO,MAAM,oBAAoB,YAAY;EAC/C;EAGF,OAAO,KAAK,oCAAoC;CAClD,SAAS,OAAO;EACd,OAAO,MAAM,mCAAmC,KAAK;EACrD,MAAM;CACR;AACF"}
|