@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.
Files changed (185) hide show
  1. package/.docs/organized/code-examples/waterfall.md +17 -18
  2. package/.docs/organized/code-examples/with-a2a.md +20 -15
  3. package/.docs/organized/code-examples/with-ag-ui.md +19 -17
  4. package/.docs/organized/code-examples/with-ai-sdk-v7.md +17 -16
  5. package/.docs/organized/code-examples/with-artifacts.md +471 -145
  6. package/.docs/organized/code-examples/with-assistant-transport.md +23 -31
  7. package/.docs/organized/code-examples/with-browser-extension.md +21 -14
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +22 -20
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +13 -14
  10. package/.docs/organized/code-examples/with-cloud.md +22 -17
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +17 -16
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +17 -17
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +21 -19
  14. package/.docs/organized/code-examples/with-eve.md +70 -16
  15. package/.docs/organized/code-examples/with-expo.md +32 -51
  16. package/.docs/organized/code-examples/with-external-store.md +20 -15
  17. package/.docs/organized/code-examples/with-ffmpeg.md +24 -18
  18. package/.docs/organized/code-examples/with-generative-ui.md +20 -21
  19. package/.docs/organized/code-examples/with-google-adk.md +21 -16
  20. package/.docs/organized/code-examples/with-heat-graph.md +10 -11
  21. package/.docs/organized/code-examples/with-image-generation.md +13 -14
  22. package/.docs/organized/code-examples/with-interactables.md +17 -16
  23. package/.docs/organized/code-examples/with-langchain.md +13 -14
  24. package/.docs/organized/code-examples/with-langgraph.md +23 -17
  25. package/.docs/organized/code-examples/with-livekit.md +17 -17
  26. package/.docs/organized/code-examples/with-mcp.md +36 -32
  27. package/.docs/organized/code-examples/with-nuxt.md +2492 -0
  28. package/.docs/organized/code-examples/with-opencode.md +27 -21
  29. package/.docs/organized/code-examples/with-pi.md +69 -67
  30. package/.docs/organized/code-examples/with-react-hook-form.md +18 -17
  31. package/.docs/organized/code-examples/with-react-ink-web.md +10 -11
  32. package/.docs/organized/code-examples/with-react-ink.md +7 -7
  33. package/.docs/organized/code-examples/with-react-router.md +23 -17
  34. package/.docs/organized/code-examples/with-resumable-stream.md +15 -16
  35. package/.docs/organized/code-examples/with-store.md +31 -20
  36. package/.docs/organized/code-examples/with-tanstack.md +22 -16
  37. package/.docs/organized/code-examples/with-tap-runtime.md +19 -19
  38. package/.docs/organized/code-examples/with-virtualized-thread.md +12 -13
  39. package/.docs/organized/code-examples/with-vue.md +408 -0
  40. package/.docs/raw/docs/(docs)/cli.mdx +3 -1
  41. package/.docs/raw/docs/(docs)/devtools.mdx +7 -2
  42. package/.docs/raw/docs/(docs)/index.mdx +9 -76
  43. package/.docs/raw/docs/(docs)/installation.mdx +4 -18
  44. package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +24 -4
  45. package/.docs/raw/docs/(reference)/api-reference/generative-ui/a2ui.mdx +40 -0
  46. package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +3 -0
  47. package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +19 -420
  48. package/.docs/raw/docs/(reference)/api-reference/hooks/state.mdx +4 -1
  49. package/.docs/raw/docs/(reference)/api-reference/integrations/eve.mdx +8 -0
  50. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +21 -0
  51. package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +1 -9
  52. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +1 -18
  53. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +24 -1
  54. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +1 -1
  55. package/.docs/raw/docs/cloud/ai-sdk.mdx +2 -2
  56. package/.docs/raw/docs/cloud/langgraph.mdx +1 -1
  57. package/.docs/raw/docs/copilots/model-context.mdx +5 -4
  58. package/.docs/raw/docs/copilots/motivation.mdx +5 -5
  59. package/.docs/raw/docs/guides/attachments.mdx +3 -3
  60. package/.docs/raw/docs/guides/branching.mdx +2 -2
  61. package/.docs/raw/docs/guides/chain-of-thought.mdx +1 -1
  62. package/.docs/raw/docs/guides/context-api.mdx +89 -111
  63. package/.docs/raw/docs/guides/editing.mdx +5 -5
  64. package/.docs/raw/docs/guides/electron.mdx +369 -0
  65. package/.docs/raw/docs/guides/index.mdx +10 -0
  66. package/.docs/raw/docs/guides/quoting.mdx +3 -3
  67. package/.docs/raw/docs/guides/resumable-stream-deployment.mdx +2 -2
  68. package/.docs/raw/docs/guides/resumable-streams.mdx +74 -3
  69. package/.docs/raw/docs/guides/suggestions.mdx +9 -9
  70. package/.docs/raw/docs/ink/hooks.mdx +12 -7
  71. package/.docs/raw/docs/ink/primitives.mdx +18 -10
  72. package/.docs/raw/docs/integrations/attachments/custom-adapter.mdx +16 -0
  73. package/.docs/raw/docs/integrations/auth/better-auth.mdx +3 -3
  74. package/.docs/raw/docs/integrations/auth/clerk.mdx +3 -3
  75. package/.docs/raw/docs/integrations/auth/next-auth.mdx +4 -4
  76. package/.docs/raw/docs/integrations/frameworks/cloudflare-agents/overview.mdx +1 -1
  77. package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +2 -2
  78. package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +1 -1
  79. package/.docs/raw/docs/integrations/gateways/index.mdx +2 -2
  80. package/.docs/raw/docs/integrations/observability/helicone.mdx +2 -2
  81. package/.docs/raw/docs/integrations/observability/langfuse.mdx +1 -1
  82. package/.docs/raw/docs/integrations/observability/langsmith.mdx +3 -3
  83. package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +36 -9
  84. package/.docs/raw/docs/migrations/toolkit-tools.mdx +15 -13
  85. package/.docs/raw/docs/migrations/v0-15.mdx +236 -0
  86. package/.docs/raw/docs/primitives/attachment.mdx +2 -2
  87. package/.docs/raw/docs/primitives/composer.mdx +3 -3
  88. package/.docs/raw/docs/primitives/message.mdx +33 -1
  89. package/.docs/raw/docs/primitives/suggestion.mdx +1 -1
  90. package/.docs/raw/docs/primitives/thread-list.mdx +2 -2
  91. package/.docs/raw/docs/react-native/hooks.mdx +17 -7
  92. package/.docs/raw/docs/react-native/index.mdx +3 -3
  93. package/.docs/raw/docs/react-native/primitives.mdx +41 -7
  94. package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +3 -3
  95. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +5 -5
  96. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +5 -5
  97. package/.docs/raw/docs/runtimes/ai-sdk/v6-legacy.mdx +13 -14
  98. package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +11 -12
  99. package/.docs/raw/docs/runtimes/concepts/threads.mdx +50 -11
  100. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +20 -2
  101. package/.docs/raw/docs/runtimes/custom/external-store.mdx +31 -2
  102. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +39 -12
  103. package/.docs/raw/docs/runtimes/eve/overview.mdx +51 -0
  104. package/.docs/raw/docs/runtimes/eve/quickstart.mdx +51 -2
  105. package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +9 -12
  106. package/.docs/raw/docs/runtimes/langchain.mdx +2 -2
  107. package/.docs/raw/docs/runtimes/langgraph/overview.mdx +1 -1
  108. package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +1 -1
  109. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +7 -7
  110. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +4 -4
  111. package/.docs/raw/docs/runtimes/opencode/hooks.mdx +4 -1
  112. package/.docs/raw/docs/runtimes/opencode/overview.mdx +10 -0
  113. package/.docs/raw/docs/tools/a2ui.mdx +107 -0
  114. package/.docs/raw/docs/tools/backend.mdx +2 -2
  115. package/.docs/raw/docs/tools/defining-tools.mdx +9 -7
  116. package/.docs/raw/docs/tools/dynamic-tools.mdx +6 -4
  117. package/.docs/raw/docs/tools/interactables-legacy.mdx +28 -15
  118. package/.docs/raw/docs/tools/interactables.mdx +27 -16
  119. package/.docs/raw/docs/tools/mcp-apps.mdx +96 -8
  120. package/.docs/raw/docs/tools/mcp.mdx +9 -7
  121. package/.docs/raw/docs/tools/tool-ui.mdx +26 -22
  122. package/.docs/raw/docs/tools/user-managed-mcp.mdx +64 -14
  123. package/.docs/raw/docs/ui/file.mdx +6 -1
  124. package/.docs/raw/docs/ui/follow-up-suggestions.mdx +2 -0
  125. package/.docs/raw/docs/ui/mcp-config.mdx +8 -3
  126. package/.docs/raw/docs/ui/model-selector.mdx +9 -9
  127. package/.docs/raw/docs/ui/part-grouping.mdx +1 -5
  128. package/.docs/raw/docs/ui/reasoning.mdx +1 -1
  129. package/.docs/raw/docs/ui/thread.mdx +24 -5
  130. package/.docs/raw/docs/utilities/react-o11y.mdx +7 -9
  131. package/dist/constants.js +2 -2
  132. package/dist/constants.js.map +1 -1
  133. package/dist/index.d.ts +1 -1
  134. package/dist/index.js +2 -2
  135. package/dist/index.js.map +1 -1
  136. package/dist/prepare-docs/code-examples.js.map +1 -1
  137. package/dist/prepare-docs/prepare.d.ts +1 -1
  138. package/dist/prepare-docs/prepare.js.map +1 -1
  139. package/dist/stdio.d.ts +1 -1
  140. package/dist/tools/docs.d.ts +6 -10
  141. package/dist/tools/docs.d.ts.map +1 -1
  142. package/dist/tools/docs.js +6 -4
  143. package/dist/tools/docs.js.map +1 -1
  144. package/dist/tools/examples.d.ts +4 -8
  145. package/dist/tools/examples.d.ts.map +1 -1
  146. package/dist/tools/examples.js +4 -3
  147. package/dist/tools/examples.js.map +1 -1
  148. package/dist/tools/resources.d.ts +1 -1
  149. package/dist/tools/resources.d.ts.map +1 -1
  150. package/dist/tools/resources.js +3 -2
  151. package/dist/tools/resources.js.map +1 -1
  152. package/dist/tools/search.d.ts +4 -10
  153. package/dist/tools/search.d.ts.map +1 -1
  154. package/dist/tools/search.js +2 -2
  155. package/dist/tools/search.js.map +1 -1
  156. package/dist/tools/tests/mcp-test-client.d.ts +15 -0
  157. package/dist/tools/tests/mcp-test-client.d.ts.map +1 -0
  158. package/dist/tools/tests/mcp-test-client.js +68 -0
  159. package/dist/tools/tests/mcp-test-client.js.map +1 -0
  160. package/dist/tools/tests/test-setup.d.ts.map +1 -1
  161. package/dist/tools/tests/test-setup.js +2 -1
  162. package/dist/tools/tests/test-setup.js.map +1 -1
  163. package/dist/tools/xulux-templates.d.ts +9 -23
  164. package/dist/tools/xulux-templates.d.ts.map +1 -1
  165. package/dist/tools/xulux-templates.js +8 -6
  166. package/dist/tools/xulux-templates.js.map +1 -1
  167. package/dist/utils/logger.d.ts.map +1 -1
  168. package/dist/utils/mdx.js +2 -1
  169. package/dist/utils/mdx.js.map +1 -1
  170. package/dist/utils/security.js.map +1 -1
  171. package/dist/xulux/catalog-client.js +1 -1
  172. package/dist/xulux/catalog-client.js.map +1 -1
  173. package/package.json +6 -5
  174. package/src/index.ts +2 -2
  175. package/src/tools/docs.ts +2 -2
  176. package/src/tools/examples.ts +2 -2
  177. package/src/tools/resources.ts +1 -4
  178. package/src/tools/search.ts +2 -2
  179. package/src/tools/tests/completions.test.ts +40 -26
  180. package/src/tools/tests/docs.test.ts +2 -2
  181. package/src/tools/tests/integration.test.ts +3 -4
  182. package/src/tools/tests/mcp-protocol.test.ts +160 -175
  183. package/src/tools/tests/mcp-test-client.ts +111 -0
  184. package/src/tools/tests/resources.test.ts +97 -66
  185. package/src/tools/xulux-templates.ts +4 -4
@@ -1,46 +1,60 @@
1
- import { describe, it, expect } from "vitest";
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
- function handler(method: string) {
5
- const handlers = (server as any).server._requestHandlers;
6
- const h = handlers.get(method);
7
- if (!h) throw new Error(`No handler for ${method}`);
8
- return h;
9
- }
5
+ type CompletionResult = {
6
+ completion: {
7
+ values: string[];
8
+ };
9
+ };
10
10
 
11
- async function complete(uri: string, name: string, value: string) {
12
- return handler("completion/complete")(
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: { ref: { type: "ref/resource", uri }, argument: { name, value } },
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 res = await complete("aui-docs:///{+path}", "path", "api");
24
- expect(res.completion.values.length).toBeGreaterThan(0);
34
+ const result = await complete("aui-docs:///{+path}", "path", "api");
35
+
36
+ expect(result.completion.values.length).toBeGreaterThan(0);
25
37
  expect(
26
- res.completion.values.every((v: string) =>
27
- v.toLowerCase().includes("api"),
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 res = await complete("aui-example:///{+name}", "name", "with");
34
- expect(res.completion.values.length).toBeGreaterThan(0);
45
+ const result = await complete("aui-example:///{+name}", "name", "with");
46
+
47
+ expect(result.completion.values.length).toBeGreaterThan(0);
35
48
  expect(
36
- res.completion.values.every((v: string) =>
37
- v.toLowerCase().includes("with"),
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 res = await complete("aui-docs:///{+path}", "path", "zzzqqxnope");
44
- expect(res.completion.values).toHaveLength(0);
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("assistant-ui");
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("assistant-ui");
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/sdk/server/mcp.js";
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(Object.keys(docsTools.parameters)).toContain("paths");
28
+ expect(docsTools.parameters.shape.paths).toBeDefined();
30
29
 
31
30
  expect(examplesTools.parameters).toBeDefined();
32
- expect(Object.keys(examplesTools.parameters)).toContain("example");
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 { describe, it, expect } from "vitest";
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
- type InitializeRequest,
10
- type ListToolsRequest,
11
- type CallToolRequest,
12
- type ListPromptsRequest,
13
- type GetPromptRequest,
14
- } from "@modelcontextprotocol/sdk/types.js";
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
- // These tests verify the MCP protocol layer handles requests correctly
18
- // and that parameter schemas are properly converted to JSON schemas
19
- it("should handle Initialize request", async () => {
20
- const request: InitializeRequest = {
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: "2024-11-05",
84
+ protocolVersion: "2025-11-25",
24
85
  capabilities: {},
25
86
  clientInfo: {
26
- name: "test-client",
87
+ name: "mcp-docs-server-tests",
27
88
  version: "1.0.0",
28
89
  },
29
90
  },
30
91
  };
31
92
 
32
- // Parse and validate the request
33
- const parsed = InitializeRequestSchema.parse(request);
34
- expect(parsed).toBeDefined();
35
-
36
- // The server should have an initialize handler set up
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("should handle ListTools request", async () => {
43
- const request: ListToolsRequest = {
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
- // Check the tools have proper JSON schemas
65
- const docsTool = result.tools.find(
66
- (t: any) => t.name === "assistantUIDocs",
67
- );
68
- expect(docsTool).toBeDefined();
69
- expect(docsTool.inputSchema).toBeDefined();
70
- expect(docsTool.inputSchema.type).toBe("object");
71
- expect(docsTool.inputSchema.properties).toBeDefined();
72
-
73
- const examplesTool = result.tools.find(
74
- (t: any) => t.name === "assistantUIExamples",
75
- );
76
- expect(examplesTool).toBeDefined();
77
- expect(examplesTool.inputSchema).toBeDefined();
78
- expect(examplesTool.inputSchema.type).toBe("object");
79
- expect(examplesTool.inputSchema.properties).toBeDefined();
80
-
81
- const searchTool = result.tools.find(
82
- (t: any) => t.name === "assistantUISearch",
83
- );
84
- expect(searchTool).toBeDefined();
85
- expect(searchTool.inputSchema).toBeDefined();
86
- expect(searchTool.inputSchema.type).toBe("object");
87
- expect(searchTool.inputSchema.properties).toBeDefined();
88
-
89
- // registerTool metadata is surfaced on tools/list: `title` is a
90
- // top-level field, `annotations` carries only the hint flags.
91
- expect(docsTool.title).toBe("assistant-ui Documentation");
92
- expect(docsTool.annotations?.readOnlyHint).toBe(true);
93
- expect(docsTool.annotations?.openWorldHint).toBe(false);
94
- expect(examplesTool.title).toBe("assistant-ui Examples");
95
- expect(examplesTool.annotations?.readOnlyHint).toBe(true);
96
- expect(examplesTool.annotations?.openWorldHint).toBe(false);
97
- expect(searchTool.title).toBe("Search assistant-ui Documentation");
98
- expect(searchTool.annotations?.readOnlyHint).toBe(true);
99
- expect(searchTool.annotations?.openWorldHint).toBe(false);
100
-
101
- const xuluxListTool = result.tools.find(
102
- (t: any) => t.name === "assistantUITemplates",
103
- );
104
- expect(xuluxListTool).toBeDefined();
105
- expect(xuluxListTool.title).toBe("assistant-ui Templates");
106
- expect(xuluxListTool.annotations?.readOnlyHint).toBe(true);
107
- expect(xuluxListTool.annotations?.openWorldHint).toBe(true);
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("should handle CallTool request for assistantUIDocs", async () => {
127
- const request: CallToolRequest = {
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
- // Call the handler through the MCP protocol layer
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("should handle CallTool request for assistantUIExamples with no arguments", async () => {
155
- const request: CallToolRequest = {
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
- // The server should have a tools/call handler
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("should handle ListPrompts request", async () => {
181
- const request: ListPromptsRequest = {
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
- (p: any) => p.name === "assistant-ui-template-workflow",
191
+ (candidate) => candidate.name === "assistant-ui-template-workflow",
198
192
  );
199
- expect(prompt).toBeDefined();
200
- expect(prompt.title).toBe("assistant-ui Template Workflow");
193
+
194
+ expect(prompt).toMatchObject({
195
+ title: "assistant-ui Template Workflow",
196
+ });
201
197
  });
202
198
 
203
- it("should handle GetPrompt request for assistant-ui-template-workflow", async () => {
204
- const request: GetPromptRequest = {
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 parsed = GetPromptRequestSchema.parse(request);
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].content.type).toBe("text");
223
- expect(result.messages[0].content.text).toContain("assistantUITemplates");
224
- expect(result.messages[0].content.text).toContain(
225
- "assistantUITemplateDetails",
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
+ }