@assistant-ui/mcp-docs-server 0.1.39 → 0.2.1
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 +17 -18
- package/.docs/organized/code-examples/with-a2a.md +20 -15
- package/.docs/organized/code-examples/with-ag-ui.md +19 -17
- package/.docs/organized/code-examples/with-ai-sdk-v7.md +17 -16
- package/.docs/organized/code-examples/with-artifacts.md +471 -145
- package/.docs/organized/code-examples/with-assistant-transport.md +23 -31
- package/.docs/organized/code-examples/with-browser-extension.md +21 -14
- package/.docs/organized/code-examples/with-chain-of-thought.md +22 -20
- package/.docs/organized/code-examples/with-cloud-standalone.md +13 -14
- package/.docs/organized/code-examples/with-cloud.md +22 -17
- package/.docs/organized/code-examples/with-custom-thread-list.md +17 -16
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +17 -17
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +21 -19
- package/.docs/organized/code-examples/with-eve.md +70 -16
- package/.docs/organized/code-examples/with-expo.md +32 -51
- package/.docs/organized/code-examples/with-external-store.md +20 -15
- package/.docs/organized/code-examples/with-ffmpeg.md +24 -18
- package/.docs/organized/code-examples/with-generative-ui.md +20 -21
- package/.docs/organized/code-examples/with-google-adk.md +21 -16
- package/.docs/organized/code-examples/with-heat-graph.md +10 -11
- package/.docs/organized/code-examples/with-image-generation.md +13 -14
- package/.docs/organized/code-examples/with-interactables.md +17 -16
- package/.docs/organized/code-examples/with-langchain.md +13 -14
- package/.docs/organized/code-examples/with-langgraph.md +23 -17
- package/.docs/organized/code-examples/with-livekit.md +17 -17
- package/.docs/organized/code-examples/with-mcp.md +36 -32
- package/.docs/organized/code-examples/with-nuxt.md +2492 -0
- package/.docs/organized/code-examples/with-opencode.md +27 -21
- package/.docs/organized/code-examples/with-pi.md +69 -67
- package/.docs/organized/code-examples/with-react-hook-form.md +18 -17
- package/.docs/organized/code-examples/with-react-ink-web.md +10 -11
- package/.docs/organized/code-examples/with-react-ink.md +7 -7
- package/.docs/organized/code-examples/with-react-router.md +23 -17
- package/.docs/organized/code-examples/with-resumable-stream.md +15 -16
- package/.docs/organized/code-examples/with-store.md +31 -20
- package/.docs/organized/code-examples/with-tanstack.md +22 -16
- package/.docs/organized/code-examples/with-tap-runtime.md +19 -19
- package/.docs/organized/code-examples/with-virtualized-thread.md +12 -13
- package/.docs/organized/code-examples/with-vue.md +408 -0
- package/.docs/raw/docs/(docs)/cli.mdx +3 -1
- package/.docs/raw/docs/(docs)/devtools.mdx +7 -2
- package/.docs/raw/docs/(docs)/index.mdx +9 -76
- package/.docs/raw/docs/(docs)/installation.mdx +4 -18
- package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +24 -4
- 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/eve.mdx +8 -0
- 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/(reference)/api-reference/utilities/miscellaneous.mdx +24 -1
- package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +1 -1
- package/.docs/raw/docs/cloud/ai-sdk.mdx +2 -2
- package/.docs/raw/docs/cloud/langgraph.mdx +1 -1
- package/.docs/raw/docs/copilots/model-context.mdx +5 -4
- package/.docs/raw/docs/copilots/motivation.mdx +5 -5
- package/.docs/raw/docs/guides/attachments.mdx +3 -3
- package/.docs/raw/docs/guides/branching.mdx +2 -2
- package/.docs/raw/docs/guides/chain-of-thought.mdx +1 -1
- 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/guides/resumable-streams.mdx +74 -3
- package/.docs/raw/docs/guides/suggestions.mdx +9 -9
- package/.docs/raw/docs/ink/hooks.mdx +12 -7
- package/.docs/raw/docs/ink/primitives.mdx +18 -10
- package/.docs/raw/docs/integrations/attachments/custom-adapter.mdx +16 -0
- package/.docs/raw/docs/integrations/auth/better-auth.mdx +3 -3
- package/.docs/raw/docs/integrations/auth/clerk.mdx +3 -3
- package/.docs/raw/docs/integrations/auth/next-auth.mdx +4 -4
- package/.docs/raw/docs/integrations/frameworks/cloudflare-agents/overview.mdx +1 -1
- package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +2 -2
- package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +1 -1
- package/.docs/raw/docs/integrations/gateways/index.mdx +2 -2
- package/.docs/raw/docs/integrations/observability/helicone.mdx +2 -2
- package/.docs/raw/docs/integrations/observability/langfuse.mdx +1 -1
- package/.docs/raw/docs/integrations/observability/langsmith.mdx +3 -3
- package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +36 -9
- package/.docs/raw/docs/migrations/toolkit-tools.mdx +15 -13
- package/.docs/raw/docs/migrations/v0-15.mdx +236 -0
- package/.docs/raw/docs/primitives/attachment.mdx +2 -2
- package/.docs/raw/docs/primitives/composer.mdx +3 -3
- package/.docs/raw/docs/primitives/message.mdx +33 -1
- package/.docs/raw/docs/primitives/suggestion.mdx +1 -1
- package/.docs/raw/docs/primitives/thread-list.mdx +2 -2
- package/.docs/raw/docs/react-native/hooks.mdx +17 -7
- package/.docs/raw/docs/react-native/index.mdx +3 -3
- package/.docs/raw/docs/react-native/primitives.mdx +41 -7
- package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +3 -3
- package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +5 -5
- package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +5 -5
- package/.docs/raw/docs/runtimes/ai-sdk/v6-legacy.mdx +13 -14
- package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +11 -12
- package/.docs/raw/docs/runtimes/concepts/threads.mdx +50 -11
- package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +20 -2
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +31 -2
- package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +39 -12
- package/.docs/raw/docs/runtimes/eve/overview.mdx +51 -0
- package/.docs/raw/docs/runtimes/eve/quickstart.mdx +51 -2
- package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +9 -12
- package/.docs/raw/docs/runtimes/langchain.mdx +2 -2
- package/.docs/raw/docs/runtimes/langgraph/overview.mdx +1 -1
- package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +1 -1
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +7 -7
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +4 -4
- package/.docs/raw/docs/runtimes/opencode/hooks.mdx +4 -1
- package/.docs/raw/docs/runtimes/opencode/overview.mdx +10 -0
- package/.docs/raw/docs/tools/a2ui.mdx +107 -0
- package/.docs/raw/docs/tools/backend.mdx +2 -2
- package/.docs/raw/docs/tools/defining-tools.mdx +9 -7
- package/.docs/raw/docs/tools/dynamic-tools.mdx +6 -4
- package/.docs/raw/docs/tools/interactables-legacy.mdx +28 -15
- package/.docs/raw/docs/tools/interactables.mdx +27 -16
- package/.docs/raw/docs/tools/mcp-apps.mdx +96 -8
- package/.docs/raw/docs/tools/mcp.mdx +9 -7
- package/.docs/raw/docs/tools/tool-ui.mdx +26 -22
- package/.docs/raw/docs/tools/user-managed-mcp.mdx +64 -14
- package/.docs/raw/docs/ui/file.mdx +6 -1
- package/.docs/raw/docs/ui/follow-up-suggestions.mdx +2 -0
- package/.docs/raw/docs/ui/mcp-config.mdx +8 -3
- package/.docs/raw/docs/ui/model-selector.mdx +9 -9
- package/.docs/raw/docs/ui/part-grouping.mdx +1 -5
- package/.docs/raw/docs/ui/reasoning.mdx +1 -1
- package/.docs/raw/docs/ui/thread.mdx +24 -5
- package/.docs/raw/docs/utilities/react-o11y.mdx +7 -9
- package/dist/constants.js +2 -2
- package/dist/constants.js.map +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/prepare-docs/prepare.js.map +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 +6 -4
- 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 +4 -3
- 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 +3 -2
- 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/tests/test-setup.js +2 -1
- package/dist/tools/tests/test-setup.js.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 +8 -6
- package/dist/tools/xulux-templates.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/mdx.js +2 -1
- package/dist/utils/mdx.js.map +1 -1
- package/dist/utils/security.js.map +1 -1
- package/dist/xulux/catalog-client.js +1 -1
- package/dist/xulux/catalog-client.js.map +1 -1
- package/package.json +6 -5
- 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/docs.test.ts +2 -2
- 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
|
@@ -1,46 +1,60 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
2
2
|
import { server } from "../../index.js";
|
|
3
|
+
import { createMcpTestClient, type McpTestClient } from "./mcp-test-client.js";
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
5
|
+
type CompletionResult = {
|
|
6
|
+
completion: {
|
|
7
|
+
values: string[];
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
describe("resource argument completions", () => {
|
|
12
|
+
let client: McpTestClient;
|
|
13
|
+
|
|
14
|
+
beforeEach(async () => {
|
|
15
|
+
client = await createMcpTestClient(server);
|
|
16
|
+
await client.initialize();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(async () => {
|
|
20
|
+
await client?.close();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
async function complete(uri: string, name: string, value: string) {
|
|
24
|
+
return client.request<CompletionResult>({
|
|
14
25
|
method: "completion/complete",
|
|
15
|
-
params: {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
26
|
+
params: {
|
|
27
|
+
ref: { type: "ref/resource", uri },
|
|
28
|
+
argument: { name, value },
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
20
32
|
|
|
21
|
-
describe("resource argument completions", () => {
|
|
22
33
|
it("suggests doc paths matching the partial value", async () => {
|
|
23
|
-
const
|
|
24
|
-
|
|
34
|
+
const result = await complete("aui-docs:///{+path}", "path", "api");
|
|
35
|
+
|
|
36
|
+
expect(result.completion.values.length).toBeGreaterThan(0);
|
|
25
37
|
expect(
|
|
26
|
-
|
|
27
|
-
|
|
38
|
+
result.completion.values.every((value) =>
|
|
39
|
+
value.toLowerCase().includes("api"),
|
|
28
40
|
),
|
|
29
41
|
).toBe(true);
|
|
30
42
|
});
|
|
31
43
|
|
|
32
44
|
it("suggests example names matching the partial value", async () => {
|
|
33
|
-
const
|
|
34
|
-
|
|
45
|
+
const result = await complete("aui-example:///{+name}", "name", "with");
|
|
46
|
+
|
|
47
|
+
expect(result.completion.values.length).toBeGreaterThan(0);
|
|
35
48
|
expect(
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
result.completion.values.every((value) =>
|
|
50
|
+
value.toLowerCase().includes("with"),
|
|
38
51
|
),
|
|
39
52
|
).toBe(true);
|
|
40
53
|
});
|
|
41
54
|
|
|
42
55
|
it("returns no values for a nonsense partial", async () => {
|
|
43
|
-
const
|
|
44
|
-
|
|
56
|
+
const result = await complete("aui-docs:///{+path}", "path", "zzzqqxnope");
|
|
57
|
+
|
|
58
|
+
expect(result.completion.values).toHaveLength(0);
|
|
45
59
|
});
|
|
46
60
|
});
|
|
@@ -42,7 +42,7 @@ describe("assistantUIDocs", () => {
|
|
|
42
42
|
expect(result.found).toBe(true);
|
|
43
43
|
expect(result.type).toBe("file");
|
|
44
44
|
expect(result.content).toBeDefined();
|
|
45
|
-
expect(result.content).toContain("
|
|
45
|
+
expect(result.content).toContain("title: Documentation");
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
it("should handle non-existent paths", async () => {
|
|
@@ -88,7 +88,7 @@ describe("assistantUIDocs", () => {
|
|
|
88
88
|
|
|
89
89
|
expect(result.content).toBeDefined();
|
|
90
90
|
expect(result.content).toContain("title:");
|
|
91
|
-
expect(result.content).toContain("
|
|
91
|
+
expect(result.content).toContain("description:");
|
|
92
92
|
});
|
|
93
93
|
|
|
94
94
|
it("includes title and excerpt on a file response", async () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
2
|
import { server } from "../../index.js";
|
|
3
|
-
import { McpServer } from "@modelcontextprotocol/
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
4
4
|
import { docsTools } from "../docs.js";
|
|
5
5
|
import { examplesTools } from "../examples.js";
|
|
6
6
|
|
|
@@ -24,12 +24,11 @@ describe("MCP Server Integration", () => {
|
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
it("should have valid input schemas", () => {
|
|
27
|
-
// docsTools.parameters is the Zod schema shape
|
|
28
27
|
expect(docsTools.parameters).toBeDefined();
|
|
29
|
-
expect(
|
|
28
|
+
expect(docsTools.parameters.shape.paths).toBeDefined();
|
|
30
29
|
|
|
31
30
|
expect(examplesTools.parameters).toBeDefined();
|
|
32
|
-
expect(
|
|
31
|
+
expect(examplesTools.parameters.shape.example).toBeDefined();
|
|
33
32
|
});
|
|
34
33
|
|
|
35
34
|
it("should execute tools successfully", async () => {
|
|
@@ -1,130 +1,160 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { server } from "../../index.js";
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
3
2
|
import {
|
|
4
|
-
InitializeRequestSchema,
|
|
5
|
-
ListToolsRequestSchema,
|
|
6
3
|
CallToolRequestSchema,
|
|
7
|
-
ListPromptsRequestSchema,
|
|
8
4
|
GetPromptRequestSchema,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from "
|
|
5
|
+
InitializeRequestSchema,
|
|
6
|
+
ListPromptsRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
} from "@modelcontextprotocol/core";
|
|
9
|
+
import { server } from "../../index.js";
|
|
10
|
+
import { createMcpTestClient, type McpTestClient } from "./mcp-test-client.js";
|
|
11
|
+
|
|
12
|
+
type Tool = {
|
|
13
|
+
name: string;
|
|
14
|
+
title?: string;
|
|
15
|
+
annotations?: {
|
|
16
|
+
readOnlyHint?: boolean;
|
|
17
|
+
openWorldHint?: boolean;
|
|
18
|
+
};
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type?: string;
|
|
21
|
+
properties?: Record<string, { description?: string }>;
|
|
22
|
+
required?: string[];
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type ToolListResult = {
|
|
27
|
+
tools: Tool[];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type ToolCallResult = {
|
|
31
|
+
content: Array<{
|
|
32
|
+
type: string;
|
|
33
|
+
}>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type PromptListResult = {
|
|
37
|
+
prompts: Array<{
|
|
38
|
+
name: string;
|
|
39
|
+
title?: string;
|
|
40
|
+
}>;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type PromptGetResult = {
|
|
44
|
+
messages: Array<{
|
|
45
|
+
content: {
|
|
46
|
+
type: string;
|
|
47
|
+
text?: string;
|
|
48
|
+
};
|
|
49
|
+
}>;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
type InitializeResult = {
|
|
53
|
+
protocolVersion: string;
|
|
54
|
+
serverInfo: {
|
|
55
|
+
name: string;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function requireTool(tools: Tool[], name: string): Tool {
|
|
60
|
+
const tool = tools.find((candidate) => candidate.name === name);
|
|
61
|
+
expect(tool).toBeDefined();
|
|
62
|
+
if (!tool) throw new Error(`Missing tool: ${name}`);
|
|
63
|
+
return tool;
|
|
64
|
+
}
|
|
15
65
|
|
|
16
66
|
describe("MCP Protocol Integration", () => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
67
|
+
let client: McpTestClient;
|
|
68
|
+
let initializeResult: Promise<InitializeResult>;
|
|
69
|
+
|
|
70
|
+
beforeEach(async () => {
|
|
71
|
+
client = await createMcpTestClient(server);
|
|
72
|
+
initializeResult = client.initialize() as Promise<InitializeResult>;
|
|
73
|
+
await initializeResult;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
afterEach(async () => {
|
|
77
|
+
await client?.close();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("handles Initialize requests", async () => {
|
|
81
|
+
const request = {
|
|
21
82
|
method: "initialize",
|
|
22
83
|
params: {
|
|
23
|
-
protocolVersion: "
|
|
84
|
+
protocolVersion: "2025-11-25",
|
|
24
85
|
capabilities: {},
|
|
25
86
|
clientInfo: {
|
|
26
|
-
name: "
|
|
87
|
+
name: "mcp-docs-server-tests",
|
|
27
88
|
version: "1.0.0",
|
|
28
89
|
},
|
|
29
90
|
},
|
|
30
91
|
};
|
|
31
92
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const handlers = (server as any).server._requestHandlers;
|
|
38
|
-
expect(handlers).toBeDefined();
|
|
39
|
-
expect(handlers.get("initialize")).toBeDefined();
|
|
93
|
+
expect(InitializeRequestSchema.parse(request)).toBeDefined();
|
|
94
|
+
await expect(initializeResult).resolves.toMatchObject({
|
|
95
|
+
protocolVersion: "2025-11-25",
|
|
96
|
+
serverInfo: { name: "assistant-ui-docs" },
|
|
97
|
+
});
|
|
40
98
|
});
|
|
41
99
|
|
|
42
|
-
it("
|
|
43
|
-
const request
|
|
100
|
+
it("lists tool metadata and JSON schemas", async () => {
|
|
101
|
+
const request = ListToolsRequestSchema.parse({
|
|
44
102
|
method: "tools/list",
|
|
45
103
|
params: {},
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
// Parse and validate the request
|
|
49
|
-
const parsed = ListToolsRequestSchema.parse(request);
|
|
50
|
-
expect(parsed).toBeDefined();
|
|
51
|
-
|
|
52
|
-
// The server should have a tools/list handler
|
|
53
|
-
const handlers = (server as any).server._requestHandlers;
|
|
54
|
-
expect(handlers.get("tools/list")).toBeDefined();
|
|
55
|
-
|
|
56
|
-
// Call the handler
|
|
57
|
-
const handler = handlers.get("tools/list");
|
|
58
|
-
const result = await handler(parsed, {});
|
|
104
|
+
});
|
|
105
|
+
const result = await client.request<ToolListResult>(request);
|
|
59
106
|
|
|
60
|
-
expect(result).toBeDefined();
|
|
61
|
-
expect(result.tools).toBeInstanceOf(Array);
|
|
62
107
|
expect(result.tools).toHaveLength(6);
|
|
63
108
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
expect(
|
|
108
|
-
|
|
109
|
-
const xuluxDetailsTool = result.tools.find(
|
|
110
|
-
(t: any) => t.name === "assistantUITemplateDetails",
|
|
111
|
-
);
|
|
112
|
-
expect(xuluxDetailsTool).toBeDefined();
|
|
113
|
-
expect(xuluxDetailsTool.title).toBe("assistant-ui Template Details");
|
|
114
|
-
expect(xuluxDetailsTool.annotations?.readOnlyHint).toBe(true);
|
|
115
|
-
expect(xuluxDetailsTool.annotations?.openWorldHint).toBe(true);
|
|
116
|
-
|
|
117
|
-
const xuluxPreviewTool = result.tools.find(
|
|
118
|
-
(t: any) => t.name === "assistantUITemplatePreview",
|
|
119
|
-
);
|
|
120
|
-
expect(xuluxPreviewTool).toBeDefined();
|
|
121
|
-
expect(xuluxPreviewTool.title).toBe("assistant-ui Template Preview URLs");
|
|
122
|
-
expect(xuluxPreviewTool.annotations?.readOnlyHint).toBe(false);
|
|
123
|
-
expect(xuluxPreviewTool.annotations?.openWorldHint).toBe(true);
|
|
109
|
+
for (const expected of [
|
|
110
|
+
{
|
|
111
|
+
name: "assistantUIDocs",
|
|
112
|
+
title: "assistant-ui Documentation",
|
|
113
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: "assistantUIExamples",
|
|
117
|
+
title: "assistant-ui Examples",
|
|
118
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: "assistantUISearch",
|
|
122
|
+
title: "Search assistant-ui Documentation",
|
|
123
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "assistantUITemplates",
|
|
127
|
+
title: "assistant-ui Templates",
|
|
128
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: "assistantUITemplateDetails",
|
|
132
|
+
title: "assistant-ui Template Details",
|
|
133
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "assistantUITemplatePreview",
|
|
137
|
+
title: "assistant-ui Template Preview URLs",
|
|
138
|
+
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
139
|
+
},
|
|
140
|
+
]) {
|
|
141
|
+
const tool = requireTool(result.tools, expected.name);
|
|
142
|
+
|
|
143
|
+
expect(tool).toMatchObject({
|
|
144
|
+
title: expected.title,
|
|
145
|
+
annotations: expected.annotations,
|
|
146
|
+
});
|
|
147
|
+
expect(tool.inputSchema.type).toBe("object");
|
|
148
|
+
expect(tool.inputSchema.properties).toBeDefined();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const search = requireTool(result.tools, "assistantUISearch");
|
|
152
|
+
expect(search.inputSchema.required ?? []).not.toContain("limit");
|
|
153
|
+
expect(search.inputSchema.properties?.["query"]?.description).toBeDefined();
|
|
124
154
|
});
|
|
125
155
|
|
|
126
|
-
it("
|
|
127
|
-
const request
|
|
156
|
+
it("calls assistantUIDocs", async () => {
|
|
157
|
+
const request = CallToolRequestSchema.parse({
|
|
128
158
|
method: "tools/call",
|
|
129
159
|
params: {
|
|
130
160
|
name: "assistantUIDocs",
|
|
@@ -132,100 +162,55 @@ describe("MCP Protocol Integration", () => {
|
|
|
132
162
|
paths: ["/"],
|
|
133
163
|
},
|
|
134
164
|
},
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
// Parse and validate the request
|
|
138
|
-
const parsed = CallToolRequestSchema.parse(request);
|
|
139
|
-
expect(parsed).toBeDefined();
|
|
140
|
-
|
|
141
|
-
// The server should have a tools/call handler
|
|
142
|
-
const handlers = (server as any).server._requestHandlers;
|
|
143
|
-
expect(handlers.get("tools/call")).toBeDefined();
|
|
165
|
+
});
|
|
166
|
+
const result = await client.request<ToolCallResult>(request);
|
|
144
167
|
|
|
145
|
-
|
|
146
|
-
const handler = handlers.get("tools/call");
|
|
147
|
-
const result = await handler(parsed, {});
|
|
148
|
-
|
|
149
|
-
expect(result).toBeDefined();
|
|
150
|
-
expect(result.content).toBeDefined();
|
|
151
|
-
expect(result.content[0].type).toBe("text");
|
|
168
|
+
expect(result.content[0]?.type).toBe("text");
|
|
152
169
|
});
|
|
153
170
|
|
|
154
|
-
it("
|
|
155
|
-
const request
|
|
171
|
+
it("calls assistantUIExamples without arguments", async () => {
|
|
172
|
+
const request = CallToolRequestSchema.parse({
|
|
156
173
|
method: "tools/call",
|
|
157
174
|
params: {
|
|
158
175
|
name: "assistantUIExamples",
|
|
159
176
|
arguments: {},
|
|
160
177
|
},
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
// Parse and validate the request
|
|
164
|
-
const parsed = CallToolRequestSchema.parse(request);
|
|
165
|
-
expect(parsed).toBeDefined();
|
|
178
|
+
});
|
|
179
|
+
const result = await client.request<ToolCallResult>(request);
|
|
166
180
|
|
|
167
|
-
|
|
168
|
-
const handlers = (server as any).server._requestHandlers;
|
|
169
|
-
expect(handlers.get("tools/call")).toBeDefined();
|
|
170
|
-
|
|
171
|
-
// Call the handler
|
|
172
|
-
const handler = handlers.get("tools/call");
|
|
173
|
-
const result = await handler(parsed, {});
|
|
174
|
-
|
|
175
|
-
expect(result).toBeDefined();
|
|
176
|
-
expect(result.content).toBeDefined();
|
|
177
|
-
expect(result.content[0].type).toBe("text");
|
|
181
|
+
expect(result.content[0]?.type).toBe("text");
|
|
178
182
|
});
|
|
179
183
|
|
|
180
|
-
it("
|
|
181
|
-
const request
|
|
184
|
+
it("lists prompts", async () => {
|
|
185
|
+
const request = ListPromptsRequestSchema.parse({
|
|
182
186
|
method: "prompts/list",
|
|
183
187
|
params: {},
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const parsed = ListPromptsRequestSchema.parse(request);
|
|
187
|
-
expect(parsed).toBeDefined();
|
|
188
|
-
|
|
189
|
-
const handlers = (server as any).server._requestHandlers;
|
|
190
|
-
expect(handlers.get("prompts/list")).toBeDefined();
|
|
191
|
-
|
|
192
|
-
const handler = handlers.get("prompts/list");
|
|
193
|
-
const result = await handler(parsed, {});
|
|
194
|
-
|
|
195
|
-
expect(result.prompts).toBeInstanceOf(Array);
|
|
188
|
+
});
|
|
189
|
+
const result = await client.request<PromptListResult>(request);
|
|
196
190
|
const prompt = result.prompts.find(
|
|
197
|
-
(
|
|
191
|
+
(candidate) => candidate.name === "assistant-ui-template-workflow",
|
|
198
192
|
);
|
|
199
|
-
|
|
200
|
-
expect(prompt
|
|
193
|
+
|
|
194
|
+
expect(prompt).toMatchObject({
|
|
195
|
+
title: "assistant-ui Template Workflow",
|
|
196
|
+
});
|
|
201
197
|
});
|
|
202
198
|
|
|
203
|
-
it("
|
|
204
|
-
const request
|
|
199
|
+
it("gets the assistant-ui-template-workflow prompt", async () => {
|
|
200
|
+
const request = GetPromptRequestSchema.parse({
|
|
205
201
|
method: "prompts/get",
|
|
206
202
|
params: {
|
|
207
203
|
name: "assistant-ui-template-workflow",
|
|
208
204
|
arguments: {},
|
|
209
205
|
},
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
const
|
|
213
|
-
expect(parsed).toBeDefined();
|
|
214
|
-
|
|
215
|
-
const handlers = (server as any).server._requestHandlers;
|
|
216
|
-
expect(handlers.get("prompts/get")).toBeDefined();
|
|
217
|
-
|
|
218
|
-
const handler = handlers.get("prompts/get");
|
|
219
|
-
const result = await handler(parsed, {});
|
|
206
|
+
});
|
|
207
|
+
const result = await client.request<PromptGetResult>(request);
|
|
208
|
+
const text = result.messages[0]?.content.text;
|
|
220
209
|
|
|
221
210
|
expect(result.messages).toHaveLength(1);
|
|
222
|
-
expect(result.messages[0]
|
|
223
|
-
expect(
|
|
224
|
-
expect(
|
|
225
|
-
|
|
226
|
-
);
|
|
227
|
-
expect(result.messages[0].content.text).toContain(
|
|
228
|
-
"assistantUITemplatePreview",
|
|
229
|
-
);
|
|
211
|
+
expect(result.messages[0]?.content.type).toBe("text");
|
|
212
|
+
expect(text).toContain("assistantUITemplates");
|
|
213
|
+
expect(text).toContain("assistantUITemplateDetails");
|
|
214
|
+
expect(text).toContain("assistantUITemplatePreview");
|
|
230
215
|
});
|
|
231
216
|
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InMemoryTransport,
|
|
3
|
+
type McpServer,
|
|
4
|
+
} from "@modelcontextprotocol/server";
|
|
5
|
+
|
|
6
|
+
type RawMcpRequest = {
|
|
7
|
+
method: string;
|
|
8
|
+
params?: unknown;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type JsonRpcResponse = {
|
|
12
|
+
id: number;
|
|
13
|
+
result?: unknown;
|
|
14
|
+
error?: {
|
|
15
|
+
code: number;
|
|
16
|
+
message: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type PendingRequest = {
|
|
21
|
+
resolve: (result: unknown) => void;
|
|
22
|
+
reject: (error: Error) => void;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type McpTestClient = {
|
|
26
|
+
initialize: () => Promise<unknown>;
|
|
27
|
+
request: <Result>(request: RawMcpRequest) => Promise<Result>;
|
|
28
|
+
close: () => Promise<void>;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function isJsonRpcResponse(message: unknown): message is JsonRpcResponse {
|
|
32
|
+
return (
|
|
33
|
+
typeof message === "object" &&
|
|
34
|
+
message !== null &&
|
|
35
|
+
"id" in message &&
|
|
36
|
+
typeof message.id === "number" &&
|
|
37
|
+
("result" in message || "error" in message)
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function createMcpTestClient(
|
|
42
|
+
server: McpServer,
|
|
43
|
+
): Promise<McpTestClient> {
|
|
44
|
+
const [clientTransport, serverTransport] =
|
|
45
|
+
InMemoryTransport.createLinkedPair();
|
|
46
|
+
const pendingRequests = new Map<number, PendingRequest>();
|
|
47
|
+
let nextRequestId = 1;
|
|
48
|
+
|
|
49
|
+
clientTransport.onmessage = (message) => {
|
|
50
|
+
if (!isJsonRpcResponse(message)) return;
|
|
51
|
+
|
|
52
|
+
const pendingRequest = pendingRequests.get(message.id);
|
|
53
|
+
if (!pendingRequest) return;
|
|
54
|
+
|
|
55
|
+
pendingRequests.delete(message.id);
|
|
56
|
+
if (message.error) {
|
|
57
|
+
pendingRequest.reject(
|
|
58
|
+
new Error(
|
|
59
|
+
`MCP request failed (${message.error.code}): ${message.error.message}`,
|
|
60
|
+
),
|
|
61
|
+
);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
pendingRequest.resolve(message.result);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
await clientTransport.start();
|
|
69
|
+
await server.connect(serverTransport);
|
|
70
|
+
|
|
71
|
+
const request = <Result>(rawRequest: RawMcpRequest): Promise<Result> => {
|
|
72
|
+
const id = nextRequestId++;
|
|
73
|
+
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
pendingRequests.set(id, { resolve, reject });
|
|
76
|
+
void clientTransport
|
|
77
|
+
.send({ jsonrpc: "2.0", id, ...rawRequest })
|
|
78
|
+
.catch((error: unknown) => {
|
|
79
|
+
pendingRequests.delete(id);
|
|
80
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
81
|
+
});
|
|
82
|
+
}) as Promise<Result>;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
initialize: async () => {
|
|
87
|
+
const result = await request({
|
|
88
|
+
method: "initialize",
|
|
89
|
+
params: {
|
|
90
|
+
protocolVersion: "2025-11-25",
|
|
91
|
+
capabilities: {},
|
|
92
|
+
clientInfo: {
|
|
93
|
+
name: "mcp-docs-server-tests",
|
|
94
|
+
version: "1.0.0",
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
await clientTransport.send({
|
|
100
|
+
jsonrpc: "2.0",
|
|
101
|
+
method: "notifications/initialized",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return result;
|
|
105
|
+
},
|
|
106
|
+
request,
|
|
107
|
+
close: async () => {
|
|
108
|
+
await clientTransport.close();
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|