@assistant-ui/mcp-docs-server 0.1.39 → 0.2.0
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 +7 -7
- package/.docs/organized/code-examples/with-a2a.md +7 -7
- package/.docs/organized/code-examples/with-ag-ui.md +8 -8
- package/.docs/organized/code-examples/with-ai-sdk-v7.md +9 -9
- package/.docs/organized/code-examples/with-artifacts.md +463 -138
- package/.docs/organized/code-examples/with-assistant-transport.md +7 -7
- package/.docs/organized/code-examples/with-browser-extension.md +6 -6
- package/.docs/organized/code-examples/with-chain-of-thought.md +11 -11
- package/.docs/organized/code-examples/with-cloud-standalone.md +9 -9
- package/.docs/organized/code-examples/with-cloud.md +9 -9
- package/.docs/organized/code-examples/with-custom-thread-list.md +9 -9
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +11 -11
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +11 -11
- package/.docs/organized/code-examples/with-eve.md +8 -8
- package/.docs/organized/code-examples/with-expo.md +26 -46
- package/.docs/organized/code-examples/with-external-store.md +7 -7
- package/.docs/organized/code-examples/with-ffmpeg.md +9 -9
- package/.docs/organized/code-examples/with-generative-ui.md +10 -10
- package/.docs/organized/code-examples/with-google-adk.md +8 -8
- package/.docs/organized/code-examples/with-heat-graph.md +6 -6
- package/.docs/organized/code-examples/with-image-generation.md +9 -9
- package/.docs/organized/code-examples/with-interactables.md +9 -9
- package/.docs/organized/code-examples/with-langchain.md +9 -9
- package/.docs/organized/code-examples/with-langgraph.md +9 -9
- package/.docs/organized/code-examples/with-livekit.md +11 -11
- package/.docs/organized/code-examples/with-mcp.md +28 -23
- package/.docs/organized/code-examples/with-opencode.md +8 -11
- package/.docs/organized/code-examples/with-pi.md +14 -14
- package/.docs/organized/code-examples/with-react-hook-form.md +10 -10
- package/.docs/organized/code-examples/with-react-ink-web.md +5 -5
- package/.docs/organized/code-examples/with-react-ink.md +2 -2
- package/.docs/organized/code-examples/with-react-router.md +11 -11
- package/.docs/organized/code-examples/with-resumable-stream.md +10 -10
- package/.docs/organized/code-examples/with-store.md +6 -6
- package/.docs/organized/code-examples/with-tanstack.md +9 -9
- package/.docs/organized/code-examples/with-tap-runtime.md +7 -7
- package/.docs/organized/code-examples/with-virtualized-thread.md +8 -8
- package/.docs/raw/docs/(docs)/cli.mdx +2 -0
- package/.docs/raw/docs/(docs)/devtools.mdx +7 -2
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/a2ui.mdx +40 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +3 -0
- package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +19 -420
- package/.docs/raw/docs/(reference)/api-reference/hooks/state.mdx +4 -1
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +21 -0
- package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +1 -9
- package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +1 -18
- package/.docs/raw/docs/cloud/langgraph.mdx +1 -1
- package/.docs/raw/docs/copilots/model-context.mdx +1 -1
- package/.docs/raw/docs/copilots/motivation.mdx +1 -1
- package/.docs/raw/docs/guides/attachments.mdx +3 -3
- package/.docs/raw/docs/guides/branching.mdx +2 -2
- package/.docs/raw/docs/guides/context-api.mdx +89 -111
- package/.docs/raw/docs/guides/editing.mdx +5 -5
- package/.docs/raw/docs/guides/electron.mdx +369 -0
- package/.docs/raw/docs/guides/index.mdx +10 -0
- package/.docs/raw/docs/guides/quoting.mdx +3 -3
- package/.docs/raw/docs/guides/resumable-stream-deployment.mdx +2 -2
- package/.docs/raw/docs/ink/hooks.mdx +3 -3
- package/.docs/raw/docs/ink/primitives.mdx +14 -7
- package/.docs/raw/docs/integrations/auth/better-auth.mdx +2 -2
- package/.docs/raw/docs/integrations/auth/clerk.mdx +2 -2
- package/.docs/raw/docs/integrations/auth/next-auth.mdx +3 -3
- package/.docs/raw/docs/integrations/observability/langsmith.mdx +1 -1
- package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +36 -9
- package/.docs/raw/docs/migrations/v0-15.mdx +156 -0
- package/.docs/raw/docs/primitives/composer.mdx +1 -1
- package/.docs/raw/docs/primitives/thread-list.mdx +2 -2
- package/.docs/raw/docs/react-native/hooks.mdx +3 -3
- package/.docs/raw/docs/react-native/index.mdx +2 -2
- package/.docs/raw/docs/react-native/primitives.mdx +39 -5
- package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +3 -3
- package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +4 -4
- package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +4 -4
- package/.docs/raw/docs/runtimes/ai-sdk/v6-legacy.mdx +4 -4
- package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +2 -2
- package/.docs/raw/docs/runtimes/concepts/threads.mdx +10 -10
- package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +2 -2
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +3 -3
- package/.docs/raw/docs/runtimes/langchain.mdx +2 -2
- package/.docs/raw/docs/runtimes/langgraph/overview.mdx +1 -1
- package/.docs/raw/docs/runtimes/opencode/hooks.mdx +3 -1
- package/.docs/raw/docs/tools/a2ui.mdx +107 -0
- package/.docs/raw/docs/tools/interactables-legacy.mdx +4 -4
- package/.docs/raw/docs/tools/interactables.mdx +3 -3
- package/.docs/raw/docs/tools/mcp-apps.mdx +61 -2
- package/.docs/raw/docs/tools/user-managed-mcp.mdx +52 -7
- package/.docs/raw/docs/ui/follow-up-suggestions.mdx +2 -0
- package/.docs/raw/docs/ui/model-selector.mdx +1 -1
- package/.docs/raw/docs/ui/part-grouping.mdx +0 -4
- package/.docs/raw/docs/ui/reasoning.mdx +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/prepare-docs/code-examples.js.map +1 -1
- package/dist/prepare-docs/prepare.d.ts +1 -1
- package/dist/stdio.d.ts +1 -1
- package/dist/tools/docs.d.ts +6 -10
- package/dist/tools/docs.d.ts.map +1 -1
- package/dist/tools/docs.js +2 -2
- package/dist/tools/docs.js.map +1 -1
- package/dist/tools/examples.d.ts +4 -8
- package/dist/tools/examples.d.ts.map +1 -1
- package/dist/tools/examples.js +2 -2
- package/dist/tools/examples.js.map +1 -1
- package/dist/tools/resources.d.ts +1 -1
- package/dist/tools/resources.d.ts.map +1 -1
- package/dist/tools/resources.js +1 -1
- package/dist/tools/resources.js.map +1 -1
- package/dist/tools/search.d.ts +4 -10
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js +2 -2
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/tests/mcp-test-client.d.ts +15 -0
- package/dist/tools/tests/mcp-test-client.d.ts.map +1 -0
- package/dist/tools/tests/mcp-test-client.js +68 -0
- package/dist/tools/tests/mcp-test-client.js.map +1 -0
- package/dist/tools/tests/test-setup.d.ts.map +1 -1
- package/dist/tools/xulux-templates.d.ts +9 -23
- package/dist/tools/xulux-templates.d.ts.map +1 -1
- package/dist/tools/xulux-templates.js +4 -4
- package/dist/tools/xulux-templates.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/security.js.map +1 -1
- package/package.json +4 -3
- package/src/index.ts +2 -2
- package/src/tools/docs.ts +2 -2
- package/src/tools/examples.ts +2 -2
- package/src/tools/resources.ts +1 -4
- package/src/tools/search.ts +2 -2
- package/src/tools/tests/completions.test.ts +40 -26
- package/src/tools/tests/integration.test.ts +3 -4
- package/src/tools/tests/mcp-protocol.test.ts +160 -175
- package/src/tools/tests/mcp-test-client.ts +111 -0
- package/src/tools/tests/resources.test.ts +97 -66
- package/src/tools/xulux-templates.ts +4 -4
|
@@ -595,7 +595,7 @@ async function* createCustomStream(): AsyncGenerator<ChatModelRunResult> {
|
|
|
595
595
|
};
|
|
596
596
|
}
|
|
597
597
|
|
|
598
|
-
aui.thread
|
|
598
|
+
aui.thread.resumeRun({
|
|
599
599
|
parentId: "message-id",
|
|
600
600
|
stream: createCustomStream,
|
|
601
601
|
});
|
|
@@ -617,8 +617,8 @@ function useStreamReconnect(threadId: string) {
|
|
|
617
617
|
r.json(),
|
|
618
618
|
);
|
|
619
619
|
if (status.isRunning) {
|
|
620
|
-
const parentId = aui.thread
|
|
621
|
-
aui.thread
|
|
620
|
+
const parentId = aui.thread.getState().messages.at(-1)?.id ?? null;
|
|
621
|
+
aui.thread.resumeRun({ parentId });
|
|
622
622
|
}
|
|
623
623
|
})();
|
|
624
624
|
}, [aui, threadId]);
|
|
@@ -5,7 +5,7 @@ description: Use LangChain's useStream hook with a React chat UI through assista
|
|
|
5
5
|
|
|
6
6
|
import { LangGraphIcon } from "@/components/icons/langgraph";
|
|
7
7
|
|
|
8
|
-
`@assistant-ui/react-langchain` wraps [`useStream`](https://
|
|
8
|
+
`@assistant-ui/react-langchain` wraps [`useStream`](https://reference.langchain.com/javascript/langchain-react/use-stream) from `@langchain/react` and exposes it as an assistant-ui runtime. It targets the same backend as [`@assistant-ui/react-langgraph`](/docs/runtimes/langgraph/overview) (LangGraph Cloud) but at a higher level, delegating stream plumbing to the upstream hook.
|
|
9
9
|
|
|
10
10
|
## When to use it
|
|
11
11
|
|
|
@@ -32,7 +32,7 @@ Shared adapters (attachments, speech, feedback) work the same way described in [
|
|
|
32
32
|
|
|
33
33
|
## Requirements
|
|
34
34
|
|
|
35
|
-
- A LangGraph Cloud API server (locally via [LangGraph Studio](https://
|
|
35
|
+
- A LangGraph Cloud API server (locally via [LangGraph Studio](https://docs.langchain.com/langsmith/quick-start-studio) or hosted via [LangSmith](https://www.langchain.com/langsmith)).
|
|
36
36
|
- The graph state must include a `messages` key with LangChain-alike messages, or pass a custom `messagesKey`.
|
|
37
37
|
|
|
38
38
|
## Quickstart
|
|
@@ -13,7 +13,7 @@ description: Build a chat UI for LangGraph agents in React with assistant-ui —
|
|
|
13
13
|
|
|
14
14
|
Pick the LangGraph runtime when:
|
|
15
15
|
|
|
16
|
-
- You have (or want) a LangGraph Cloud server, locally via [LangGraph Studio](https://
|
|
16
|
+
- You have (or want) a LangGraph Cloud server, locally via [LangGraph Studio](https://docs.langchain.com/langsmith/quick-start-studio) or hosted via [LangSmith](https://www.langchain.com/langsmith).
|
|
17
17
|
- Your graph state has a `messages` key with LangChain-alike messages.
|
|
18
18
|
- You want generative UI (`ui_message`), per-message metadata, subgraph events, or checkpoint-based message editing.
|
|
19
19
|
|
|
@@ -9,7 +9,9 @@ OpenCode-specific React hooks for interacting with the running session. All hook
|
|
|
9
9
|
|
|
10
10
|
## Permissions
|
|
11
11
|
|
|
12
|
-
OpenCode pauses tool execution to ask the user for permission (e.g. running shell commands, writing files). `
|
|
12
|
+
OpenCode pauses tool execution to ask the user for permission (e.g. running shell commands, writing files). Permissions linked to a tool call are projected into assistant-ui's standard tool approval contract, so the default `ToolFallback` renders working **Allow**, **Always allow**, and **Deny** actions without a custom permission component. The always option only appears when OpenCode offers patterns to persist.
|
|
13
|
+
|
|
14
|
+
`useOpenCodePermissions` remains available for custom tool renderers and permission requests that are not linked to a tool call. It returns the pending permission requests and a reply function:
|
|
13
15
|
|
|
14
16
|
```tsx
|
|
15
17
|
import { useOpenCodePermissions } from "@assistant-ui/react-opencode";
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: A2UI over AG-UI
|
|
3
|
+
description: Render A2UI surfaces from AG-UI activity snapshots as generative UI, using the community a2ui-surface convention.
|
|
4
|
+
platforms: ["react"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
[A2UI](https://a2ui.org/) is a declarative generative UI protocol: the agent streams surface operations (create a surface, upsert components, update a data model) and the host renders them from a pre-approved component catalog, with no code over the wire. The AG-UI ecosystem carries A2UI as `ACTIVITY_SNAPSHOT` events with `activityType: "a2ui-surface"` and the operations under `content.a2ui_operations`; this is the convention emitted by [`@ag-ui/a2ui-middleware`](https://github.com/ag-ui-protocol/ag-ui/tree/main/middlewares/a2ui-middleware).
|
|
8
|
+
|
|
9
|
+
`useAgUiRuntime` consumes these snapshots natively. Each surface becomes one tool-call part with `toolCallId` `a2ui:<surfaceId>` and `toolName` `"present"`, whose args are the converted generative UI spec, so surfaces render through the same path as the [`present` frontend tool](/docs/tools/generative-ui). Snapshots with any other `activityType` are ignored, and the MCP Apps activity path is unaffected.
|
|
10
|
+
|
|
11
|
+
## Wire contract
|
|
12
|
+
|
|
13
|
+
A backend paints or updates a surface by emitting an activity snapshot on the AG-UI stream:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"type": "ACTIVITY_SNAPSHOT",
|
|
18
|
+
"messageId": "a2ui-surface-call_1",
|
|
19
|
+
"activityType": "a2ui-surface",
|
|
20
|
+
"replace": true,
|
|
21
|
+
"content": {
|
|
22
|
+
"a2ui_operations": [
|
|
23
|
+
{ "version": "v0.9", "createSurface": { "surfaceId": "s1" } },
|
|
24
|
+
{
|
|
25
|
+
"version": "v0.9",
|
|
26
|
+
"updateComponents": {
|
|
27
|
+
"surfaceId": "s1",
|
|
28
|
+
"components": [
|
|
29
|
+
{ "id": "root", "component": "Card", "title": "Order", "children": ["total"] },
|
|
30
|
+
{ "id": "total", "component": "Text", "text": { "path": "/total" } }
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"version": "v0.9",
|
|
36
|
+
"updateDataModel": { "surfaceId": "s1", "path": "/", "contents": { "total": "$42" } }
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- A snapshot with `replace: true` (the schema default) rebuilds that `messageId`'s surface state from the operations it carries. `replace: false` means the snapshot is ignored when that `messageId` has already been seen, matching the AG-UI event spec. The middleware always emits `replace: true`.
|
|
44
|
+
- Surfaces are keyed by the `surfaceId` inside the operations, not by `messageId`. Multiple snapshots that share a `messageId` update their surfaces in place, and the synthesized part keeps its `toolCallId`, so the rendered surface updates without duplicating parts.
|
|
45
|
+
- Component trees use the A2UI adjacency-list model: nodes reference children by id, the root node has id `root`, and props of shape `{ "path": "/x/y" }` are JSON Pointer bindings resolved against the surface data model. Both v0.9 and v1.0 operation payloads are accepted, including the v1.0 inline `components` and `dataModel` on `createSurface`.
|
|
46
|
+
- Lifecycle snapshots that carry a `status` (such as `"building"`) but no `a2ui_operations` are tolerated and produce no part until operations arrive. A `deleteSurface` operation removes the surface's part.
|
|
47
|
+
- The `a2ui:` tool-call id prefix is reserved for synthesized surface parts: they are client-side render artifacts and are excluded from the history sent back to the agent, so genuine agent tool calls must not use ids starting with `a2ui:`.
|
|
48
|
+
|
|
49
|
+
## Quick start
|
|
50
|
+
|
|
51
|
+
Register the `present` frontend tool from `@assistant-ui/react-generative-ui`; incoming surfaces then render with the default vocabulary:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import {
|
|
55
|
+
JSONGenerativeUI,
|
|
56
|
+
defaultGenerativeUILibrary,
|
|
57
|
+
} from "@assistant-ui/react-generative-ui";
|
|
58
|
+
|
|
59
|
+
const generative = new JSONGenerativeUI({
|
|
60
|
+
library: defaultGenerativeUILibrary,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const toolkit = {
|
|
64
|
+
present: generative.present({ display: "standalone" }),
|
|
65
|
+
};
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Pass the toolkit to your assistant as on the [Generative UI](/docs/tools/generative-ui) page; no other configuration is needed on `useAgUiRuntime`.
|
|
69
|
+
|
|
70
|
+
## Actions
|
|
71
|
+
|
|
72
|
+
`Button` nodes dispatch `$action` objects with type `"a2ui:action"` through the action registry. Wire the registry to `useAgUiSendA2uiAction` inside a component; the hook returns a stable function, so the toolkit can be memoized on it:
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
import { useMemo } from "react";
|
|
76
|
+
import { useAgUiSendA2uiAction } from "@assistant-ui/react-ag-ui";
|
|
77
|
+
import {
|
|
78
|
+
JSONGenerativeUI,
|
|
79
|
+
createActionRegistry,
|
|
80
|
+
defaultGenerativeUILibrary,
|
|
81
|
+
} from "@assistant-ui/react-generative-ui";
|
|
82
|
+
|
|
83
|
+
function useA2uiToolkit() {
|
|
84
|
+
const sendA2uiAction = useAgUiSendA2uiAction();
|
|
85
|
+
return useMemo(() => {
|
|
86
|
+
const generative = new JSONGenerativeUI({
|
|
87
|
+
library: defaultGenerativeUILibrary,
|
|
88
|
+
actions: createActionRegistry({
|
|
89
|
+
"a2ui:action": ({ payload }) => sendA2uiAction(payload),
|
|
90
|
+
}),
|
|
91
|
+
});
|
|
92
|
+
return { present: generative.present({ display: "standalone" }) };
|
|
93
|
+
}, [sendA2uiAction]);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Sending an action triggers a run with no new user message, and the agent receives it as `forwardedProps.a2uiAction.userAction`, the convention `@ag-ui/a2ui-middleware` consumes. The action rides exactly one run; the `type` field is stripped and a `timestamp` is added when absent.
|
|
98
|
+
|
|
99
|
+
The middleware turns the action into a synthetic `log_a2ui_event` tool-call pair that is never declared in `input.tools`; a backend that validates tool calls against the declared tool list will reject it.
|
|
100
|
+
|
|
101
|
+
## Component mapping
|
|
102
|
+
|
|
103
|
+
The converter maps the A2UI basic catalog onto the default generative UI vocabulary: `Text` becomes `Markdown` (`h1` to `h6` variants become `Header`, `caption` becomes `Caption`), `Column` becomes `Col`, `TextField` becomes `Input` (the control name is derived from the last segment of its binding path), `CheckBox` becomes `Checkbox`, and `Image`, `Row`, `Card`, `Divider`, `Button` map one to one. A node with template children expands into a `ListView` over the bound list. `Button` actions become `$action` objects with type `"a2ui:action"` carrying the action name, `surfaceId`, and `sourceComponentId`, which is how your action registry receives them. Unknown components and operations are skipped with a debug warning, and conversion is bounded (depth 32, 100 template items, 5000 nodes) so a malformed stream cannot hang the client.
|
|
104
|
+
|
|
105
|
+
## Limitations
|
|
106
|
+
|
|
107
|
+
- The upstream middleware currently emits v0.9 operation payloads; the renderer accepts v0.9 and v1.0 shapes.
|
|
@@ -298,7 +298,7 @@ function MyRuntimeProvider({ children }) {
|
|
|
298
298
|
|
|
299
299
|
useEffect(() => {
|
|
300
300
|
// Set up persistence adapter
|
|
301
|
-
aui.interactables
|
|
301
|
+
aui.interactables.setPersistenceAdapter({
|
|
302
302
|
save: async (state) => {
|
|
303
303
|
localStorage.setItem("interactables", JSON.stringify(state));
|
|
304
304
|
},
|
|
@@ -307,7 +307,7 @@ function MyRuntimeProvider({ children }) {
|
|
|
307
307
|
// Restore saved state on mount
|
|
308
308
|
const saved = localStorage.getItem("interactables");
|
|
309
309
|
if (saved) {
|
|
310
|
-
aui.interactables
|
|
310
|
+
aui.interactables.importState(JSON.parse(saved));
|
|
311
311
|
}
|
|
312
312
|
}, [aui]);
|
|
313
313
|
|
|
@@ -334,10 +334,10 @@ State changes are automatically debounced (500ms) before saving. When a componen
|
|
|
334
334
|
For custom persistence strategies, use `exportState` and `importState` directly:
|
|
335
335
|
|
|
336
336
|
```tsx
|
|
337
|
-
const snapshot = aui.interactables
|
|
337
|
+
const snapshot = aui.interactables.exportState();
|
|
338
338
|
// => { "note-1": { name: "note", state: { title: "Hello" } }, ... }
|
|
339
339
|
|
|
340
|
-
aui.interactables
|
|
340
|
+
aui.interactables.importState(snapshot);
|
|
341
341
|
// Imported state is picked up when components next register
|
|
342
342
|
```
|
|
343
343
|
|
|
@@ -618,7 +618,7 @@ function MyRuntimeProvider({ children }) {
|
|
|
618
618
|
|
|
619
619
|
`load` is called when the adapter is attached and may be async. Loaded state seeds interactables as they register; a local edit made while a slow `load` is still in flight wins over the loaded value. Thread-scoped interactables are not touched by the adapter; they persist via thread history.
|
|
620
620
|
|
|
621
|
-
For dynamic setups (an adapter that depends on auth), call `aui.interactables
|
|
621
|
+
For dynamic setups (an adapter that depends on auth), call `aui.interactables.setPersistenceAdapter(adapter)` imperatively instead.
|
|
622
622
|
|
|
623
623
|
### Sync Status
|
|
624
624
|
|
|
@@ -640,10 +640,10 @@ State changes are automatically debounced (500ms) before saving. When the owning
|
|
|
640
640
|
For custom persistence strategies, use `exportState` and `importState` directly:
|
|
641
641
|
|
|
642
642
|
```tsx
|
|
643
|
-
const snapshot = aui.interactables
|
|
643
|
+
const snapshot = aui.interactables.exportState();
|
|
644
644
|
// => { "note-1": { name: "note", state: { title: "Hello" } }, ... }
|
|
645
645
|
|
|
646
|
-
aui.interactables
|
|
646
|
+
aui.interactables.importState(snapshot);
|
|
647
647
|
// Imported state is picked up when components next register
|
|
648
648
|
```
|
|
649
649
|
|
|
@@ -164,7 +164,7 @@ const result = streamText({
|
|
|
164
164
|
|
|
165
165
|
[OpenAI Apps SDK](https://developers.openai.com/apps-sdk) servers carry the same `ui://` template under a different convention: the pointer is `_meta["openai/outputTemplate"]` on the tool definition (not `_meta.ui.resourceUri`), and the resource is served as `text/html+skybridge` rather than `text/html;profile=mcp-app`. `@ai-sdk/mcp` does not recognize `openai/outputTemplate`, so it never populates `callProviderMetadata.mcp.app` and the renderer stays idle.
|
|
166
166
|
|
|
167
|
-
The renderer needs no change; you only have to surface the pointer. assistant-ui
|
|
167
|
+
The renderer needs no change; you only have to surface the pointer. assistant-ui reads the canonical `result._meta.ui.resourceUri` off tool results (and still accepts the deprecated flat `result._meta["ui/resourceUri"]`), so the smallest bridge is to copy the template onto the result by tool name. Build the map once from the tool listing, then stamp it inside each tool's `execute`:
|
|
168
168
|
|
|
169
169
|
```ts
|
|
170
170
|
import type { Tool } from "ai";
|
|
@@ -184,7 +184,13 @@ const withTemplateUri = (tool: Tool, name: string): Tool => {
|
|
|
184
184
|
...tool,
|
|
185
185
|
execute: async (args, options) => {
|
|
186
186
|
const result = (await exec(args, options)) as { _meta?: Record<string, unknown> };
|
|
187
|
-
return {
|
|
187
|
+
return {
|
|
188
|
+
...result,
|
|
189
|
+
_meta: {
|
|
190
|
+
...result._meta,
|
|
191
|
+
ui: { ...(result._meta?.["ui"] as Record<string, unknown>), resourceUri: uri },
|
|
192
|
+
},
|
|
193
|
+
};
|
|
188
194
|
},
|
|
189
195
|
} satisfies Tool;
|
|
190
196
|
};
|
|
@@ -204,6 +210,59 @@ Your `mcp-apps/read-resource` handler reads the `ui://` resource as in the route
|
|
|
204
210
|
|
|
205
211
|
The cleaner long-term fix is upstream: if `@ai-sdk/mcp`'s `getMCPAppToolMeta` also read `openai/outputTemplate`, then `callProviderMetadata.mcp.app` would populate automatically and this bridge would be unnecessary.
|
|
206
212
|
|
|
213
|
+
## AG-UI integration
|
|
214
|
+
|
|
215
|
+
With `@assistant-ui/react-ag-ui`, the backend associates an MCP App with a tool call through an `ACTIVITY_SNAPSHOT`. Include the tool call ID, the app's `ui://` resource URI, and the MCP server identity when routing across multiple servers:
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"type": "ACTIVITY_SNAPSHOT",
|
|
220
|
+
"activityType": "mcp-apps",
|
|
221
|
+
"content": {
|
|
222
|
+
"toolCallId": "call-1",
|
|
223
|
+
"resourceUri": "ui://maps/result.html",
|
|
224
|
+
"serverId": "maps",
|
|
225
|
+
"result": {
|
|
226
|
+
"content": [{ "type": "text", "text": "Map ready" }],
|
|
227
|
+
"structuredContent": { "center": [37.77, -122.42] },
|
|
228
|
+
"_meta": { "initialView": "street" },
|
|
229
|
+
"isError": false
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
`serverId` is optional. If it is absent, the runtime accepts `serverHash` as a fallback. Older middleware may omit `toolCallId`, in which case the snapshot applies to the last resolved tool call; new integrations should include it so concurrent tool calls are correlated unambiguously. Emit the snapshot after the tool call's `TOOL_CALL_START`. A snapshot that names a tool call restored from prior history applies to that message directly, so re-emitting the activity after a `MESSAGES_SNAPSHOT` rehydrates its widget; a snapshot for a tool call ID the runtime has never seen is silently ignored.
|
|
236
|
+
|
|
237
|
+
Send the normal `TOOL_CALL_RESULT` with a concise, model-visible summary:
|
|
238
|
+
|
|
239
|
+
```json
|
|
240
|
+
{
|
|
241
|
+
"type": "TOOL_CALL_RESULT",
|
|
242
|
+
"toolCallId": "call-1",
|
|
243
|
+
"content": "Map ready"
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The snapshot's `result` becomes the widget-visible `part.result`, while the `TOOL_CALL_RESULT.content` string is retained for the model as `part.modelContent`, a text content-part array (`[{ "type": "text", "text": "Map ready" }]`).
|
|
248
|
+
|
|
249
|
+
Alternatively, AG-UI servers can place MCP host fields directly on `TOOL_CALL_RESULT`:
|
|
250
|
+
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"type": "TOOL_CALL_RESULT",
|
|
254
|
+
"toolCallId": "call-1",
|
|
255
|
+
"content": "Map ready",
|
|
256
|
+
"structuredContent": { "center": [37.77, -122.42] },
|
|
257
|
+
"_meta": { "initialView": "street" },
|
|
258
|
+
"isError": false
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
In this form, the runtime assembles a `CallToolResult`-shaped `part.result` from `content`, `structuredContent`, `_meta`, and `isError`. At least one of `structuredContent` or `_meta` must be present; when both are absent the enriched path does not activate and the result falls back to the plain `content` string. The `_meta` can also carry the app pointer itself (canonical `ui.resourceUri`, or the deprecated flat `"ui/resourceUri"`), in which case no `ACTIVITY_SNAPSHOT` is needed to activate the widget. A snapshot remains the only carrier for server identity (`serverId`), and when both provide a `resourceUri` the snapshot wins.
|
|
263
|
+
|
|
264
|
+
The visibility split follows the MCP Apps contract: `content` is model-visible; `structuredContent` and `_meta` are available only to the host and widget. When assistant-ui serializes history into a later AG-UI request, it sends the saved model-visible text and never includes `structuredContent` or `_meta`.
|
|
265
|
+
|
|
207
266
|
## Bridge protocol
|
|
208
267
|
|
|
209
268
|
The bridge implements the MCP UI JSON-RPC protocol over `window.postMessage`, filtered by both `event.source === frame.iframe.contentWindow` AND `event.origin === frame.origin` — the cross-origin domain `SafeContentFrame` issues per render. Messages from any other origin or window are dropped silently.
|
|
@@ -114,7 +114,7 @@ Pass children to override the trigger:
|
|
|
114
114
|
</McpConfigDialog>
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
-
**Compose your own from primitives
|
|
117
|
+
**Compose your own from primitives.** Four namespaces are available, all unstyled and `data-*`-driven. The iteration primitives take a **render function** so the body re-runs per server with the right scope:
|
|
118
118
|
|
|
119
119
|
```tsx title="app/mcp/page.tsx"
|
|
120
120
|
"use client";
|
|
@@ -179,7 +179,7 @@ The iteration primitives wrap each item in an `McpServerByIdProvider`, so the ne
|
|
|
179
179
|
</McpAddFormPrimitive.Root>
|
|
180
180
|
```
|
|
181
181
|
|
|
182
|
-
The form owns its own draft state and submits via `aui.mcp
|
|
182
|
+
The form owns its own draft state and submits via `aui.mcp.addCustomServer(...)`. Pass a render function to `AuthFields` to fully customize it.
|
|
183
183
|
|
|
184
184
|
</Step>
|
|
185
185
|
<Step>
|
|
@@ -236,12 +236,56 @@ If no chat runtime is mounted, `McpManagerResource` brings its own minimal `mode
|
|
|
236
236
|
```ts
|
|
237
237
|
// In an event handler, never in render.
|
|
238
238
|
const aui = useAui();
|
|
239
|
-
const out = await aui.mcp
|
|
239
|
+
const out = await aui.mcp.server({ id: "linear" }).callTool("search", { q });
|
|
240
240
|
```
|
|
241
241
|
|
|
242
242
|
</Step>
|
|
243
243
|
</Steps>
|
|
244
244
|
|
|
245
|
+
## Form elicitation
|
|
246
|
+
|
|
247
|
+
Connected servers can request structured user input through form-mode elicitation. Render `McpElicitationPrimitive.Items` inside a server-scoped subtree, then compose fields and response actions from the unstyled primitives:
|
|
248
|
+
|
|
249
|
+
Answering a request requires composing `McpElicitationPrimitive`, because the server waits for the form response otherwise. Set `elicitation: false` on a connector or custom server to opt it out of advertising the capability. Numeric fields can stay text inputs (parseable strings are coerced); boolean fields must compose a checkbox, because string drafts for booleans are flagged invalid rather than coerced. Clearing a field returns it to the unanswered state, so an empty text input is omitted from the response rather than submitted as `""` or flagged invalid, unless the schema admits `""` for that property through an `enum` member or a `""` default. Render `enum` properties as a select; when `""` is not a member, its blank option is the unanswered state.
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
import { McpElicitationPrimitive } from "@assistant-ui/react-mcp";
|
|
253
|
+
|
|
254
|
+
<McpElicitationPrimitive.Items>
|
|
255
|
+
{() => (
|
|
256
|
+
<McpElicitationPrimitive.Root>
|
|
257
|
+
<McpElicitationPrimitive.Message />
|
|
258
|
+
<McpElicitationPrimitive.Error />
|
|
259
|
+
<McpElicitationPrimitive.Fields>
|
|
260
|
+
{({ name, schema, value, setValue }) =>
|
|
261
|
+
(schema as { type?: string } | undefined)?.type === "boolean" ? (
|
|
262
|
+
<label>
|
|
263
|
+
{name}
|
|
264
|
+
<input
|
|
265
|
+
type="checkbox"
|
|
266
|
+
checked={value === true}
|
|
267
|
+
onChange={(event) => setValue(event.target.checked)}
|
|
268
|
+
/>
|
|
269
|
+
</label>
|
|
270
|
+
) : (
|
|
271
|
+
<input
|
|
272
|
+
name={name}
|
|
273
|
+
value={typeof value === "string" ? value : ""}
|
|
274
|
+
onChange={(event) => setValue(event.target.value)}
|
|
275
|
+
/>
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
</McpElicitationPrimitive.Fields>
|
|
279
|
+
<McpElicitationPrimitive.Accept>Submit</McpElicitationPrimitive.Accept>
|
|
280
|
+
<McpElicitationPrimitive.Decline>Decline</McpElicitationPrimitive.Decline>
|
|
281
|
+
<McpElicitationPrimitive.Cancel>Cancel</McpElicitationPrimitive.Cancel>
|
|
282
|
+
</McpElicitationPrimitive.Root>
|
|
283
|
+
)}
|
|
284
|
+
</McpElicitationPrimitive.Items>
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Client-side validation errors keep the elicitation pending so the user can correct the form, and `McpElicitationPrimitive.Error` renders the resulting message.
|
|
288
|
+
|
|
245
289
|
## Storage
|
|
246
290
|
|
|
247
291
|
All persisted state — custom server records, OAuth tokens, PKCE verifiers, DCR client info — goes through a single `MCPStorage` resource. Three built-ins:
|
|
@@ -312,9 +356,9 @@ Imperative methods: `useAui` + resolve in a callback (never during render):
|
|
|
312
356
|
const aui = useAui();
|
|
313
357
|
|
|
314
358
|
// inside an event handler:
|
|
315
|
-
await aui.mcp
|
|
316
|
-
await aui.mcp
|
|
317
|
-
await aui.mcp
|
|
359
|
+
await aui.mcp.addCustomServer({ name, url, auth: { type: "bearer", token } });
|
|
360
|
+
await aui.mcp.server({ id }).connect();
|
|
361
|
+
await aui.mcp.server({ id }).callTool("echo", { text: "hi" });
|
|
318
362
|
|
|
319
363
|
// Build a paginated resource browser/preview UI.
|
|
320
364
|
type ResourcePage = {
|
|
@@ -322,7 +366,7 @@ type ResourcePage = {
|
|
|
322
366
|
nextCursor?: string;
|
|
323
367
|
};
|
|
324
368
|
|
|
325
|
-
const server = aui.mcp
|
|
369
|
+
const server = aui.mcp.server({ id });
|
|
326
370
|
const resources: ResourcePage["resources"] = [];
|
|
327
371
|
let nextCursor: string | undefined;
|
|
328
372
|
|
|
@@ -346,6 +390,7 @@ What ships:
|
|
|
346
390
|
|
|
347
391
|
- Tool listing and invocation, auto-registered as frontend tools
|
|
348
392
|
- Resource listing and reads for app-built browsers or preview panes
|
|
393
|
+
- Form-mode elicitation with app-composed fields and response actions
|
|
349
394
|
- OAuth (PKCE + DCR), bearer, none
|
|
350
395
|
- StreamableHTTP transport
|
|
351
396
|
- Manual connect/disconnect
|
|
@@ -74,6 +74,8 @@ const ThreadViewportFooter = () => {
|
|
|
74
74
|
|
|
75
75
|
The component only renders when the thread is not empty, not currently running, and has at least one suggestion. Each suggestion uses `ThreadPrimitive.Suggestion`, replaces the composer text with the prompt, and sends it immediately.
|
|
76
76
|
|
|
77
|
+
Suggestions stay on a single line. When they overflow, the row scrolls horizontally without a scrollbar, and each edge with clipped content fades out — so no fade shows at the start edge until you scroll.
|
|
78
|
+
|
|
77
79
|
## Related Components
|
|
78
80
|
|
|
79
81
|
- [Thread](/docs/ui/thread) - Complete chat interface with message list and composer
|
|
@@ -308,7 +308,7 @@ The picker implements the WAI-ARIA combobox pattern over Popover + Command.
|
|
|
308
308
|
|
|
309
309
|
The default `ModelSelector` export registers the selection with assistant-ui's `ModelContext` system:
|
|
310
310
|
|
|
311
|
-
1. The component calls `aui.modelContext
|
|
311
|
+
1. The component calls `aui.modelContext.register()` with `config.modelName`, plus `config.reasoningEffort` when the selected model supports the chosen level
|
|
312
312
|
2. The `AssistantChatTransport` includes `config` in the request body of every chat request
|
|
313
313
|
3. Your API route reads `config.modelName` and `config.reasoningEffort`
|
|
314
314
|
|
|
@@ -248,10 +248,6 @@ The synthetic `"standalone-tool-call"` key on `groupPartByType` matches all of t
|
|
|
248
248
|
</MessagePrimitive.GroupedParts>;
|
|
249
249
|
```
|
|
250
250
|
|
|
251
|
-
<Callout type="info">
|
|
252
|
-
The `"mcp-app"` key is deprecated in favor of `"standalone-tool-call"`, which is a superset (it also matches MCP-app tool calls). Existing `"mcp-app"` usage keeps working.
|
|
253
|
-
</Callout>
|
|
254
|
-
|
|
255
251
|
### Render Tool Calls as Flat Rows
|
|
256
252
|
|
|
257
253
|
The previous section keeps `"tool-call"` folded into the chain-of-thought and only lifts standalone tool UIs out. To keep reasoning collapsed in a group while every tool call renders as its own row in the message flow, map both tool keys to `[]`:
|
|
@@ -28,7 +28,7 @@ Previously, reasoning parts were rendered via `components.Reasoning` and grouped
|
|
|
28
28
|
|
|
29
29
|
Render reasoning parts through `MessagePrimitive.GroupedParts`. Group consecutive reasoning parts with `"group-reasoning"`, then compose `ReasoningRoot`, `ReasoningTrigger`, `ReasoningContent`, and `ReasoningText` around the grouped children.
|
|
30
30
|
|
|
31
|
-
While reasoning is streaming, `part.status.type === "running"`. Pass that to `streaming` so the accordion auto-opens during streaming with a bottom-pinned live preview of the newest tokens,
|
|
31
|
+
While reasoning is streaming, `part.status.type === "running"`. Pass that to `streaming` so the accordion auto-opens during streaming with a bottom-pinned live preview of the newest tokens, and permanently defers to the first manual toggle. `streaming` only holds the disclosure open; when it ends the accordion returns to `defaultOpen`, which is `false` by default and therefore collapses. Pass `defaultOpen` to keep reasoning expanded once the run finishes.
|
|
32
32
|
|
|
33
33
|
```tsx title="/app/components/assistant-ui/thread.tsx"
|
|
34
34
|
import { MessagePrimitive, groupPartByType } from "@assistant-ui/react";
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -7,8 +7,8 @@ import { xuluxTemplateDetailsTool, xuluxTemplatePreviewTool, xuluxTemplatesListT
|
|
|
7
7
|
import { xuluxPlaygroundPrompt } from "./prompts/xulux-playground.js";
|
|
8
8
|
import { registerResources } from "./tools/resources.js";
|
|
9
9
|
import { join } from "node:path";
|
|
10
|
-
import { McpServer } from "@modelcontextprotocol/
|
|
11
|
-
import { StdioServerTransport } from "@modelcontextprotocol/
|
|
10
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
11
|
+
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
|
|
12
12
|
import { readFileSync } from "node:fs";
|
|
13
13
|
//#region src/index.ts
|
|
14
14
|
const packageJson = JSON.parse(readFileSync(join(PACKAGE_DIR, "package.json"), "utf-8"));
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/server\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/server/stdio\";\nimport { docsTools } from \"./tools/docs.js\";\nimport { examplesTools } from \"./tools/examples.js\";\nimport { searchTools } from \"./tools/search.js\";\nimport {\n xuluxTemplatesListTool,\n xuluxTemplateDetailsTool,\n xuluxTemplatePreviewTool,\n} from \"./tools/xulux-templates.js\";\nimport { xuluxPlaygroundPrompt } from \"./prompts/xulux-playground.js\";\nimport { registerResources } from \"./tools/resources.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.registerTool(\n docsTools.name,\n {\n title: \"assistant-ui Documentation\",\n description: docsTools.description,\n inputSchema: docsTools.parameters,\n annotations: { readOnlyHint: true, openWorldHint: false },\n },\n docsTools.execute,\n);\nserver.registerTool(\n examplesTools.name,\n {\n title: \"assistant-ui Examples\",\n description: examplesTools.description,\n inputSchema: examplesTools.parameters,\n annotations: { readOnlyHint: true, openWorldHint: false },\n },\n examplesTools.execute,\n);\nserver.registerTool(\n searchTools.name,\n {\n title: \"Search assistant-ui Documentation\",\n description: searchTools.description,\n inputSchema: searchTools.parameters,\n annotations: { readOnlyHint: true, openWorldHint: false },\n },\n searchTools.execute,\n);\n\nserver.registerTool(\n xuluxTemplatesListTool.name,\n {\n title: \"assistant-ui Templates\",\n description: xuluxTemplatesListTool.description,\n inputSchema: xuluxTemplatesListTool.parameters,\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n xuluxTemplatesListTool.execute,\n);\nserver.registerTool(\n xuluxTemplateDetailsTool.name,\n {\n title: \"assistant-ui Template Details\",\n description: xuluxTemplateDetailsTool.description,\n inputSchema: xuluxTemplateDetailsTool.parameters,\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n xuluxTemplateDetailsTool.execute,\n);\nserver.registerTool(\n xuluxTemplatePreviewTool.name,\n {\n title: \"assistant-ui Template Preview URLs\",\n description: xuluxTemplatePreviewTool.description,\n inputSchema: xuluxTemplatePreviewTool.parameters,\n annotations: { readOnlyHint: false, openWorldHint: true },\n },\n xuluxTemplatePreviewTool.execute,\n);\n\nserver.registerPrompt(\n xuluxPlaygroundPrompt.name,\n {\n title: \"assistant-ui Template Workflow\",\n description: xuluxPlaygroundPrompt.description,\n },\n () => ({\n messages: [\n {\n role: \"user\" as const,\n content: { type: \"text\" as const, text: xuluxPlaygroundPrompt.text },\n },\n ],\n }),\n);\n\nregisterResources(server);\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":";;;;;;;;;;;;;AAkBA,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,aACL,UAAU,MACV;CACE,OAAO;CACP,aAAa,UAAU;CACvB,aAAa,UAAU;CACvB,aAAa;EAAE,cAAc;EAAM,eAAe;CAAM;AAC1D,GACA,UAAU,OACZ;AACA,OAAO,aACL,cAAc,MACd;CACE,OAAO;CACP,aAAa,cAAc;CAC3B,aAAa,cAAc;CAC3B,aAAa;EAAE,cAAc;EAAM,eAAe;CAAM;AAC1D,GACA,cAAc,OAChB;AACA,OAAO,aACL,YAAY,MACZ;CACE,OAAO;CACP,aAAa,YAAY;CACzB,aAAa,YAAY;CACzB,aAAa;EAAE,cAAc;EAAM,eAAe;CAAM;AAC1D,GACA,YAAY,OACd;AAEA,OAAO,aACL,uBAAuB,MACvB;CACE,OAAO;CACP,aAAa,uBAAuB;CACpC,aAAa,uBAAuB;CACpC,aAAa;EAAE,cAAc;EAAM,eAAe;CAAK;AACzD,GACA,uBAAuB,OACzB;AACA,OAAO,aACL,yBAAyB,MACzB;CACE,OAAO;CACP,aAAa,yBAAyB;CACtC,aAAa,yBAAyB;CACtC,aAAa;EAAE,cAAc;EAAM,eAAe;CAAK;AACzD,GACA,yBAAyB,OAC3B;AACA,OAAO,aACL,yBAAyB,MACzB;CACE,OAAO;CACP,aAAa,yBAAyB;CACtC,aAAa,yBAAyB;CACtC,aAAa;EAAE,cAAc;EAAO,eAAe;CAAK;AAC1D,GACA,yBAAyB,OAC3B;AAEA,OAAO,eACL,sBAAsB,MACtB;CACE,OAAO;CACP,aAAa,sBAAsB;AACrC,UACO,EACL,UAAU,CACR;CACE,MAAM;CACN,SAAS;EAAE,MAAM;EAAiB,MAAM,sBAAsB;CAAK;AACrE,CACF,EACF,EACF;AAEA,kBAAkB,MAAM;AAExB,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;
|
|
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,GAShB;QAAA,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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {}
|
package/dist/stdio.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {}
|
package/dist/tools/docs.d.ts
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
import { z } from "zod
|
|
1
|
+
import { z } from "zod";
|
|
2
2
|
//#region src/tools/docs.d.ts
|
|
3
3
|
declare const docsInputSchema: z.ZodObject<{
|
|
4
|
-
paths: z.ZodArray<z.ZodString
|
|
5
|
-
},
|
|
6
|
-
paths: string[];
|
|
7
|
-
}, {
|
|
8
|
-
paths: string[];
|
|
9
|
-
}>;
|
|
4
|
+
paths: z.ZodArray<z.ZodString>;
|
|
5
|
+
}, z.core.$strip>;
|
|
10
6
|
declare const docsTools: {
|
|
11
7
|
name: string;
|
|
12
8
|
description: string;
|
|
13
|
-
parameters: {
|
|
14
|
-
paths: z.ZodArray<z.ZodString
|
|
15
|
-
}
|
|
9
|
+
parameters: z.ZodObject<{
|
|
10
|
+
paths: z.ZodArray<z.ZodString>;
|
|
11
|
+
}, z.core.$strip>;
|
|
16
12
|
execute: ({ paths }: z.infer<typeof docsInputSchema>) => Promise<{
|
|
17
13
|
content: Array<{
|
|
18
14
|
type: "text";
|
package/dist/tools/docs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docs.d.ts","names":[],"sources":["../../src/tools/docs.ts"],"mappings":";;cAoBM,iBAAe,EAAA
|
|
1
|
+
{"version":3,"file":"docs.d.ts","names":[],"sources":["../../src/tools/docs.ts"],"mappings":";;cAoBM,iBAAe,EAAA;;GAOnB,EAAA,KAAA;cAwLW;;;;;;EAKgB,YAAA,SAAA,EAAE,aAAa,qBAAgB"}
|
package/dist/tools/docs.js
CHANGED
|
@@ -5,7 +5,7 @@ import { formatMDXContent, readMDXFile } from "../utils/mdx.js";
|
|
|
5
5
|
import { formatMCPResponse } from "../utils/mcp-format.js";
|
|
6
6
|
import { sanitizePath } from "../utils/security.js";
|
|
7
7
|
import { extname, join } from "node:path";
|
|
8
|
-
import { z } from "zod
|
|
8
|
+
import { z } from "zod";
|
|
9
9
|
import { lstat, stat } from "node:fs/promises";
|
|
10
10
|
//#region src/tools/docs.ts
|
|
11
11
|
const docsInputSchema = z.object({ paths: z.array(z.string()).min(1).describe("Documentation paths to retrieve (e.g., [\"getting-started\", \"api-reference/primitives/thread\"])") });
|
|
@@ -138,7 +138,7 @@ async function readDocumentation(docPath) {
|
|
|
138
138
|
const docsTools = {
|
|
139
139
|
name: "assistantUIDocs",
|
|
140
140
|
description: "Retrieve assistant-ui documentation by path. Use \"/\" to list all sections. Supports multiple paths in a single request.",
|
|
141
|
-
parameters: docsInputSchema
|
|
141
|
+
parameters: docsInputSchema,
|
|
142
142
|
execute: async ({ paths }) => {
|
|
143
143
|
logger.info(`Retrieving documentation for paths: ${paths.join(", ")}`);
|
|
144
144
|
try {
|