@assistant-ui/mcp-docs-server 0.1.38 → 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.
Files changed (258) hide show
  1. package/.docs/organized/code-examples/waterfall.md +11 -12
  2. package/.docs/organized/code-examples/with-a2a.md +18 -13
  3. package/.docs/organized/code-examples/with-ag-ui.md +19 -14
  4. package/.docs/organized/code-examples/{with-ai-sdk-v6.md → with-ai-sdk-v7.md} +34 -23
  5. package/.docs/organized/code-examples/with-artifacts.md +473 -141
  6. package/.docs/organized/code-examples/with-assistant-transport.md +17 -10
  7. package/.docs/organized/code-examples/with-browser-extension.md +17 -10
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +21 -14
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +12 -11
  10. package/.docs/organized/code-examples/with-cloud.md +20 -15
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +19 -12
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +22 -15
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +22 -15
  14. package/.docs/organized/code-examples/with-eve.md +18 -11
  15. package/.docs/organized/code-examples/with-expo.md +35 -52
  16. package/.docs/organized/code-examples/with-external-store.md +18 -13
  17. package/.docs/organized/code-examples/with-ffmpeg.md +20 -15
  18. package/.docs/organized/code-examples/with-generative-ui.md +22 -17
  19. package/.docs/organized/code-examples/with-google-adk.md +18 -11
  20. package/.docs/organized/code-examples/with-heat-graph.md +10 -11
  21. package/.docs/organized/code-examples/with-image-generation.md +19 -12
  22. package/.docs/organized/code-examples/with-interactables.md +21 -17
  23. package/.docs/organized/code-examples/with-langchain.md +19 -12
  24. package/.docs/organized/code-examples/with-langgraph.md +19 -12
  25. package/.docs/organized/code-examples/with-livekit.md +23 -16
  26. package/.docs/organized/code-examples/with-mcp.md +46 -28
  27. package/.docs/organized/code-examples/with-opencode.md +25 -23
  28. package/.docs/organized/code-examples/with-pi.md +54 -19
  29. package/.docs/organized/code-examples/with-react-hook-form.md +21 -16
  30. package/.docs/organized/code-examples/with-react-ink-web.md +9 -9
  31. package/.docs/organized/code-examples/with-react-ink.md +4 -4
  32. package/.docs/organized/code-examples/with-react-router.md +22 -17
  33. package/.docs/organized/code-examples/with-resumable-stream.md +21 -14
  34. package/.docs/organized/code-examples/with-store.md +10 -11
  35. package/.docs/organized/code-examples/with-tanstack.md +19 -13
  36. package/.docs/organized/code-examples/with-tap-runtime.md +18 -13
  37. package/.docs/organized/code-examples/with-virtualized-thread.md +19 -14
  38. package/.docs/raw/docs/(docs)/base-ui.mdx +39 -0
  39. package/.docs/raw/docs/(docs)/cli.mdx +19 -1
  40. package/.docs/raw/docs/(docs)/devtools.mdx +7 -2
  41. package/.docs/raw/docs/(docs)/installation.mdx +15 -1
  42. package/.docs/raw/docs/(docs)/rtl.mdx +2 -4
  43. package/.docs/raw/docs/(reference)/api-reference/generative-ui/a2ui.mdx +40 -0
  44. package/.docs/raw/docs/(reference)/api-reference/generative-ui/actions.mdx +56 -0
  45. package/.docs/raw/docs/(reference)/api-reference/generative-ui/components.mdx +86 -0
  46. package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +22 -1
  47. package/.docs/raw/docs/(reference)/api-reference/generative-ui/json-generative-ui.mdx +42 -0
  48. package/.docs/raw/docs/(reference)/api-reference/generative-ui/rendering.mdx +53 -2
  49. package/.docs/raw/docs/(reference)/api-reference/generative-ui/slack.mdx +81 -0
  50. package/.docs/raw/docs/(reference)/api-reference/generative-ui/teams.mdx +86 -0
  51. package/.docs/raw/docs/(reference)/api-reference/generative-ui/tokens.mdx +62 -0
  52. package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +19 -420
  53. package/.docs/raw/docs/(reference)/api-reference/hooks/state.mdx +4 -1
  54. package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +4 -1
  55. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +25 -2
  56. package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +37 -0
  57. package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +1 -9
  58. package/.docs/raw/docs/(reference)/api-reference/overview.mdx +1 -0
  59. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +14 -31
  60. package/.docs/raw/docs/(reference)/api-reference/primitives/composition.mdx +1 -0
  61. package/.docs/raw/docs/(reference)/api-reference/primitives/index.mdx +3 -0
  62. package/.docs/raw/docs/(reference)/api-reference/primitives/selection-toolbar.mdx +2 -0
  63. package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +0 -2
  64. package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +12 -9
  65. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +4 -0
  66. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +7 -1
  67. package/.docs/raw/docs/cloud/langgraph.mdx +4 -2
  68. package/.docs/raw/docs/copilots/model-context.mdx +1 -1
  69. package/.docs/raw/docs/copilots/motivation.mdx +1 -1
  70. package/.docs/raw/docs/guides/attachments.mdx +3 -3
  71. package/.docs/raw/docs/guides/branching.mdx +2 -2
  72. package/.docs/raw/docs/guides/chatgpt-subscription.mdx +108 -0
  73. package/.docs/raw/docs/guides/context-api.mdx +89 -111
  74. package/.docs/raw/docs/guides/dictation.mdx +185 -257
  75. package/.docs/raw/docs/guides/editing.mdx +5 -5
  76. package/.docs/raw/docs/guides/electron.mdx +369 -0
  77. package/.docs/raw/docs/guides/index.mdx +20 -0
  78. package/.docs/raw/docs/guides/mentions.mdx +31 -3
  79. package/.docs/raw/docs/guides/quoting.mdx +3 -3
  80. package/.docs/raw/docs/guides/resumable-stream-deployment.mdx +2 -2
  81. package/.docs/raw/docs/guides/resumable-streams.mdx +12 -1
  82. package/.docs/raw/docs/guides/speech.mdx +47 -29
  83. package/.docs/raw/docs/guides/suggestions.mdx +70 -1
  84. package/.docs/raw/docs/guides/voice.mdx +197 -267
  85. package/.docs/raw/docs/ink/hooks.mdx +3 -3
  86. package/.docs/raw/docs/ink/primitives.mdx +49 -8
  87. package/.docs/raw/docs/integrations/auth/better-auth.mdx +2 -2
  88. package/.docs/raw/docs/integrations/auth/clerk.mdx +2 -2
  89. package/.docs/raw/docs/integrations/auth/next-auth.mdx +3 -3
  90. package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +10 -4
  91. package/.docs/raw/docs/integrations/frameworks/cloudflare-agents/overview.mdx +3 -3
  92. package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +1 -1
  93. package/.docs/raw/docs/integrations/frameworks/mastra/overview.mdx +3 -3
  94. package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +1 -1
  95. package/.docs/raw/docs/integrations/gateways/index.mdx +2 -2
  96. package/.docs/raw/docs/integrations/observability/helicone.mdx +1 -1
  97. package/.docs/raw/docs/integrations/observability/langfuse.mdx +1 -1
  98. package/.docs/raw/docs/integrations/observability/langsmith.mdx +2 -2
  99. package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +36 -9
  100. package/.docs/raw/docs/migrations/index.mdx +50 -0
  101. package/.docs/raw/docs/migrations/toolkit-tools.mdx +4 -2
  102. package/.docs/raw/docs/migrations/v0-15.mdx +156 -0
  103. package/.docs/raw/docs/primitives/chain-of-thought.mdx +6 -1
  104. package/.docs/raw/docs/primitives/composer.mdx +17 -1
  105. package/.docs/raw/docs/primitives/selection-toolbar.mdx +25 -0
  106. package/.docs/raw/docs/primitives/thread-list.mdx +2 -2
  107. package/.docs/raw/docs/react-native/hooks.mdx +3 -3
  108. package/.docs/raw/docs/react-native/index.mdx +2 -2
  109. package/.docs/raw/docs/react-native/primitives.mdx +62 -5
  110. package/.docs/raw/docs/runtimes/ag-ui/agent-state.mdx +124 -0
  111. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +10 -1
  112. package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +14 -5
  113. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +15 -15
  114. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +8 -8
  115. package/.docs/raw/docs/runtimes/ai-sdk/{v6.mdx → v6-legacy.mdx} +11 -9
  116. package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +717 -0
  117. package/.docs/raw/docs/runtimes/concepts/adapters.mdx +1 -1
  118. package/.docs/raw/docs/runtimes/concepts/architecture.mdx +1 -1
  119. package/.docs/raw/docs/runtimes/concepts/threads.mdx +10 -10
  120. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +2 -2
  121. package/.docs/raw/docs/runtimes/custom/data-stream.mdx +10 -19
  122. package/.docs/raw/docs/runtimes/custom/external-store.mdx +2 -2
  123. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +5 -3
  124. package/.docs/raw/docs/runtimes/eve/overview.mdx +1 -1
  125. package/.docs/raw/docs/runtimes/langchain.mdx +2 -2
  126. package/.docs/raw/docs/runtimes/langgraph/agent-state.mdx +181 -0
  127. package/.docs/raw/docs/runtimes/langgraph/overview.mdx +1 -1
  128. package/.docs/raw/docs/runtimes/opencode/hooks.mdx +3 -1
  129. package/.docs/raw/docs/tools/a2ui.mdx +107 -0
  130. package/.docs/raw/docs/tools/backend.mdx +6 -3
  131. package/.docs/raw/docs/tools/defining-tools.mdx +7 -1
  132. package/.docs/raw/docs/tools/generative-ui.mdx +60 -2
  133. package/.docs/raw/docs/tools/interactables-legacy.mdx +4 -4
  134. package/.docs/raw/docs/tools/interactables.mdx +3 -3
  135. package/.docs/raw/docs/tools/mcp-apps.mdx +90 -13
  136. package/.docs/raw/docs/tools/mcp.mdx +100 -3
  137. package/.docs/raw/docs/tools/tool-ui.mdx +6 -4
  138. package/.docs/raw/docs/tools/user-managed-mcp.mdx +77 -9
  139. package/.docs/raw/docs/ui/accordion.mdx +16 -10
  140. package/.docs/raw/docs/ui/assistant-modal.mdx +8 -4
  141. package/.docs/raw/docs/ui/attachment.mdx +5 -1
  142. package/.docs/raw/docs/ui/badge.mdx +23 -12
  143. package/.docs/raw/docs/ui/follow-up-suggestions.mdx +4 -2
  144. package/.docs/raw/docs/ui/model-selector.mdx +33 -3
  145. package/.docs/raw/docs/ui/part-grouping.mdx +0 -4
  146. package/.docs/raw/docs/ui/reasoning.mdx +1 -1
  147. package/.docs/raw/docs/ui/select.mdx +22 -14
  148. package/.docs/raw/docs/ui/sources.mdx +1 -1
  149. package/.docs/raw/docs/ui/tabs.mdx +25 -14
  150. package/.docs/raw/docs/utilities/heat-graph.mdx +2 -2
  151. package/dist/constants.d.ts.map +1 -1
  152. package/dist/index.d.ts +1 -2
  153. package/dist/index.d.ts.map +1 -1
  154. package/dist/index.js +41 -2
  155. package/dist/index.js.map +1 -1
  156. package/dist/prepare-docs/code-examples.d.ts.map +1 -1
  157. package/dist/prepare-docs/code-examples.js.map +1 -1
  158. package/dist/prepare-docs/copy-raw.d.ts.map +1 -1
  159. package/dist/prepare-docs/prepare.d.ts +1 -1
  160. package/dist/prompts/xulux-playground.d.ts +12 -0
  161. package/dist/prompts/xulux-playground.d.ts.map +1 -0
  162. package/dist/prompts/xulux-playground.js +33 -0
  163. package/dist/prompts/xulux-playground.js.map +1 -0
  164. package/dist/stdio.d.ts +1 -1
  165. package/dist/tools/docs.d.ts +8 -14
  166. package/dist/tools/docs.d.ts.map +1 -1
  167. package/dist/tools/docs.js +26 -10
  168. package/dist/tools/docs.js.map +1 -1
  169. package/dist/tools/examples.d.ts +6 -12
  170. package/dist/tools/examples.d.ts.map +1 -1
  171. package/dist/tools/examples.js +11 -8
  172. package/dist/tools/examples.js.map +1 -1
  173. package/dist/tools/resources.d.ts +1 -2
  174. package/dist/tools/resources.d.ts.map +1 -1
  175. package/dist/tools/resources.js +1 -1
  176. package/dist/tools/resources.js.map +1 -1
  177. package/dist/tools/search.d.ts +6 -15
  178. package/dist/tools/search.d.ts.map +1 -1
  179. package/dist/tools/search.js +2 -2
  180. package/dist/tools/search.js.map +1 -1
  181. package/dist/tools/tests/mcp-test-client.d.ts +15 -0
  182. package/dist/tools/tests/mcp-test-client.d.ts.map +1 -0
  183. package/dist/tools/tests/mcp-test-client.js +68 -0
  184. package/dist/tools/tests/mcp-test-client.js.map +1 -0
  185. package/dist/tools/tests/test-setup.d.ts.map +1 -1
  186. package/dist/tools/tests/test-setup.js +5 -1
  187. package/dist/tools/tests/test-setup.js.map +1 -1
  188. package/dist/tools/xulux-templates.d.ts +58 -0
  189. package/dist/tools/xulux-templates.d.ts.map +1 -0
  190. package/dist/tools/xulux-templates.js +82 -0
  191. package/dist/tools/xulux-templates.js.map +1 -0
  192. package/dist/utils/cache.d.ts +5 -0
  193. package/dist/utils/cache.d.ts.map +1 -0
  194. package/dist/utils/cache.js +18 -0
  195. package/dist/utils/cache.js.map +1 -0
  196. package/dist/utils/logger.d.ts.map +1 -1
  197. package/dist/utils/mcp-format.d.ts +1 -0
  198. package/dist/utils/mcp-format.d.ts.map +1 -1
  199. package/dist/utils/mcp-format.js +7 -4
  200. package/dist/utils/mcp-format.js.map +1 -1
  201. package/dist/utils/mdx.d.ts.map +1 -1
  202. package/dist/utils/paths.d.ts +1 -1
  203. package/dist/utils/paths.d.ts.map +1 -1
  204. package/dist/utils/paths.js +3 -1
  205. package/dist/utils/paths.js.map +1 -1
  206. package/dist/utils/search.d.ts.map +1 -1
  207. package/dist/utils/security.d.ts.map +1 -1
  208. package/dist/utils/security.js.map +1 -1
  209. package/dist/xulux/catalog-client.d.ts +14 -0
  210. package/dist/xulux/catalog-client.d.ts.map +1 -0
  211. package/dist/xulux/catalog-client.js +67 -0
  212. package/dist/xulux/catalog-client.js.map +1 -0
  213. package/dist/xulux/fallback-catalog.d.ts +7 -0
  214. package/dist/xulux/fallback-catalog.d.ts.map +1 -0
  215. package/dist/xulux/fallback-catalog.js +47 -0
  216. package/dist/xulux/fallback-catalog.js.map +1 -0
  217. package/dist/xulux/fetch-sandbox.d.ts +5 -0
  218. package/dist/xulux/fetch-sandbox.d.ts.map +1 -0
  219. package/dist/xulux/fetch-sandbox.js +40 -0
  220. package/dist/xulux/fetch-sandbox.js.map +1 -0
  221. package/dist/xulux/template-service.d.ts +84 -0
  222. package/dist/xulux/template-service.d.ts.map +1 -0
  223. package/dist/xulux/template-service.js +223 -0
  224. package/dist/xulux/template-service.js.map +1 -0
  225. package/dist/xulux/types.d.ts +55 -0
  226. package/dist/xulux/types.d.ts.map +1 -0
  227. package/dist/xulux/types.js +6 -0
  228. package/dist/xulux/types.js.map +1 -0
  229. package/package.json +7 -6
  230. package/src/index.ts +55 -2
  231. package/src/prompts/xulux-playground.ts +36 -0
  232. package/src/tools/docs.ts +27 -5
  233. package/src/tools/examples.ts +17 -12
  234. package/src/tools/resources.ts +1 -4
  235. package/src/tools/search.ts +2 -2
  236. package/src/tools/tests/completions.test.ts +40 -26
  237. package/src/tools/tests/docs.test.ts +20 -0
  238. package/src/tools/tests/examples.test.ts +5 -5
  239. package/src/tools/tests/integration.test.ts +3 -4
  240. package/src/tools/tests/listings-cache.test.ts +19 -0
  241. package/src/tools/tests/mcp-protocol.test.ts +173 -108
  242. package/src/tools/tests/mcp-test-client.ts +111 -0
  243. package/src/tools/tests/resources.test.ts +97 -66
  244. package/src/tools/tests/test-setup.ts +8 -0
  245. package/src/tools/tests/xulux-templates.test.ts +262 -0
  246. package/src/tools/xulux-templates.ts +141 -0
  247. package/src/utils/cache.ts +20 -0
  248. package/src/utils/mcp-format.ts +8 -6
  249. package/src/utils/paths.ts +4 -1
  250. package/src/utils/tests/cache.test.ts +51 -0
  251. package/src/utils/tests/mcp-format.test.ts +22 -0
  252. package/src/utils/tests/security.test.ts +1 -1
  253. package/src/xulux/catalog-client.ts +105 -0
  254. package/src/xulux/fallback-catalog.ts +63 -0
  255. package/src/xulux/fetch-sandbox.ts +56 -0
  256. package/src/xulux/template-service.ts +406 -0
  257. package/src/xulux/types.ts +60 -0
  258. package/.docs/raw/docs/(reference)/api-reference/hooks/utilities.mdx +0 -464
@@ -1,102 +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 {
3
+ CallToolRequestSchema,
4
+ GetPromptRequestSchema,
4
5
  InitializeRequestSchema,
6
+ ListPromptsRequestSchema,
5
7
  ListToolsRequestSchema,
6
- CallToolRequestSchema,
7
- type InitializeRequest,
8
- type ListToolsRequest,
9
- type CallToolRequest,
10
- } from "@modelcontextprotocol/sdk/types.js";
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
+ }
11
65
 
12
66
  describe("MCP Protocol Integration", () => {
13
- // These tests verify the MCP protocol layer handles requests correctly
14
- // and that parameter schemas are properly converted to JSON schemas
15
- it("should handle Initialize request", async () => {
16
- 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 = {
17
82
  method: "initialize",
18
83
  params: {
19
- protocolVersion: "2024-11-05",
84
+ protocolVersion: "2025-11-25",
20
85
  capabilities: {},
21
86
  clientInfo: {
22
- name: "test-client",
87
+ name: "mcp-docs-server-tests",
23
88
  version: "1.0.0",
24
89
  },
25
90
  },
26
91
  };
27
92
 
28
- // Parse and validate the request
29
- const parsed = InitializeRequestSchema.parse(request);
30
- expect(parsed).toBeDefined();
31
-
32
- // The server should have an initialize handler set up
33
- const handlers = (server as any).server._requestHandlers;
34
- expect(handlers).toBeDefined();
35
- 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
+ });
36
98
  });
37
99
 
38
- it("should handle ListTools request", async () => {
39
- const request: ListToolsRequest = {
100
+ it("lists tool metadata and JSON schemas", async () => {
101
+ const request = ListToolsRequestSchema.parse({
40
102
  method: "tools/list",
41
103
  params: {},
42
- };
43
-
44
- // Parse and validate the request
45
- const parsed = ListToolsRequestSchema.parse(request);
46
- expect(parsed).toBeDefined();
47
-
48
- // The server should have a tools/list handler
49
- const handlers = (server as any).server._requestHandlers;
50
- expect(handlers.get("tools/list")).toBeDefined();
51
-
52
- // Call the handler
53
- const handler = handlers.get("tools/list");
54
- const result = await handler(parsed, {});
104
+ });
105
+ const result = await client.request<ToolListResult>(request);
55
106
 
56
- expect(result).toBeDefined();
57
- expect(result.tools).toBeInstanceOf(Array);
58
- expect(result.tools).toHaveLength(3);
107
+ expect(result.tools).toHaveLength(6);
59
108
 
60
- // Check the tools have proper JSON schemas
61
- const docsTool = result.tools.find(
62
- (t: any) => t.name === "assistantUIDocs",
63
- );
64
- expect(docsTool).toBeDefined();
65
- expect(docsTool.inputSchema).toBeDefined();
66
- expect(docsTool.inputSchema.type).toBe("object");
67
- expect(docsTool.inputSchema.properties).toBeDefined();
68
-
69
- const examplesTool = result.tools.find(
70
- (t: any) => t.name === "assistantUIExamples",
71
- );
72
- expect(examplesTool).toBeDefined();
73
- expect(examplesTool.inputSchema).toBeDefined();
74
- expect(examplesTool.inputSchema.type).toBe("object");
75
- expect(examplesTool.inputSchema.properties).toBeDefined();
76
-
77
- const searchTool = result.tools.find(
78
- (t: any) => t.name === "assistantUISearch",
79
- );
80
- expect(searchTool).toBeDefined();
81
- expect(searchTool.inputSchema).toBeDefined();
82
- expect(searchTool.inputSchema.type).toBe("object");
83
- expect(searchTool.inputSchema.properties).toBeDefined();
84
-
85
- // registerTool metadata is surfaced on tools/list: `title` is a
86
- // top-level field, `annotations` carries only the hint flags.
87
- expect(docsTool.title).toBe("assistant-ui Documentation");
88
- expect(docsTool.annotations?.readOnlyHint).toBe(true);
89
- expect(docsTool.annotations?.openWorldHint).toBe(false);
90
- expect(examplesTool.title).toBe("assistant-ui Examples");
91
- expect(examplesTool.annotations?.readOnlyHint).toBe(true);
92
- expect(examplesTool.annotations?.openWorldHint).toBe(false);
93
- expect(searchTool.title).toBe("Search assistant-ui Documentation");
94
- expect(searchTool.annotations?.readOnlyHint).toBe(true);
95
- expect(searchTool.annotations?.openWorldHint).toBe(false);
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();
96
154
  });
97
155
 
98
- it("should handle CallTool request for assistantUIDocs", async () => {
99
- const request: CallToolRequest = {
156
+ it("calls assistantUIDocs", async () => {
157
+ const request = CallToolRequestSchema.parse({
100
158
  method: "tools/call",
101
159
  params: {
102
160
  name: "assistantUIDocs",
@@ -104,48 +162,55 @@ describe("MCP Protocol Integration", () => {
104
162
  paths: ["/"],
105
163
  },
106
164
  },
107
- };
108
-
109
- // Parse and validate the request
110
- const parsed = CallToolRequestSchema.parse(request);
111
- expect(parsed).toBeDefined();
165
+ });
166
+ const result = await client.request<ToolCallResult>(request);
112
167
 
113
- // The server should have a tools/call handler
114
- const handlers = (server as any).server._requestHandlers;
115
- expect(handlers.get("tools/call")).toBeDefined();
116
-
117
- // Call the handler through the MCP protocol layer
118
- const handler = handlers.get("tools/call");
119
- const result = await handler(parsed, {});
120
-
121
- expect(result).toBeDefined();
122
- expect(result.content).toBeDefined();
123
- expect(result.content[0].type).toBe("text");
168
+ expect(result.content[0]?.type).toBe("text");
124
169
  });
125
170
 
126
- it("should handle CallTool request for assistantUIExamples with no arguments", async () => {
127
- const request: CallToolRequest = {
171
+ it("calls assistantUIExamples without arguments", async () => {
172
+ const request = CallToolRequestSchema.parse({
128
173
  method: "tools/call",
129
174
  params: {
130
175
  name: "assistantUIExamples",
131
176
  arguments: {},
132
177
  },
133
- };
178
+ });
179
+ const result = await client.request<ToolCallResult>(request);
134
180
 
135
- // Parse and validate the request
136
- const parsed = CallToolRequestSchema.parse(request);
137
- expect(parsed).toBeDefined();
181
+ expect(result.content[0]?.type).toBe("text");
182
+ });
138
183
 
139
- // The server should have a tools/call handler
140
- const handlers = (server as any).server._requestHandlers;
141
- expect(handlers.get("tools/call")).toBeDefined();
184
+ it("lists prompts", async () => {
185
+ const request = ListPromptsRequestSchema.parse({
186
+ method: "prompts/list",
187
+ params: {},
188
+ });
189
+ const result = await client.request<PromptListResult>(request);
190
+ const prompt = result.prompts.find(
191
+ (candidate) => candidate.name === "assistant-ui-template-workflow",
192
+ );
142
193
 
143
- // Call the handler
144
- const handler = handlers.get("tools/call");
145
- const result = await handler(parsed, {});
194
+ expect(prompt).toMatchObject({
195
+ title: "assistant-ui Template Workflow",
196
+ });
197
+ });
146
198
 
147
- expect(result).toBeDefined();
148
- expect(result.content).toBeDefined();
149
- expect(result.content[0].type).toBe("text");
199
+ it("gets the assistant-ui-template-workflow prompt", async () => {
200
+ const request = GetPromptRequestSchema.parse({
201
+ method: "prompts/get",
202
+ params: {
203
+ name: "assistant-ui-template-workflow",
204
+ arguments: {},
205
+ },
206
+ });
207
+ const result = await client.request<PromptGetResult>(request);
208
+ const text = result.messages[0]?.content.text;
209
+
210
+ expect(result.messages).toHaveLength(1);
211
+ expect(result.messages[0]?.content.type).toBe("text");
212
+ expect(text).toContain("assistantUITemplates");
213
+ expect(text).toContain("assistantUITemplateDetails");
214
+ expect(text).toContain("assistantUITemplatePreview");
150
215
  });
151
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
+ }
@@ -1,102 +1,133 @@
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;
5
+ type Resource = {
6
+ uri: string;
7
+ };
8
+
9
+ type ResourceListResult = {
10
+ resources: Resource[];
11
+ };
12
+
13
+ type ResourceReadResult = {
14
+ contents: Array<{
15
+ mimeType?: string;
16
+ text?: string;
17
+ }>;
18
+ };
19
+
20
+ function requireResource(resources: Resource[], prefix: string): Resource {
21
+ const resource = resources.find((candidate) =>
22
+ candidate.uri.startsWith(prefix),
23
+ );
24
+ expect(resource).toBeDefined();
25
+ if (!resource) throw new Error(`Missing resource with URI prefix: ${prefix}`);
26
+ return resource;
9
27
  }
10
28
 
11
29
  describe("MCP resources", () => {
30
+ let client: McpTestClient;
31
+
32
+ beforeEach(async () => {
33
+ client = await createMcpTestClient(server);
34
+ await client.initialize();
35
+ });
36
+
37
+ afterEach(async () => {
38
+ await client?.close();
39
+ });
40
+
12
41
  it("lists docs and example resources", async () => {
13
- const result = await handler("resources/list")(
14
- { method: "resources/list", params: {} },
15
- {},
16
- );
42
+ const result = await client.request<ResourceListResult>({
43
+ method: "resources/list",
44
+ params: {},
45
+ });
46
+
17
47
  expect(result.resources.length).toBeGreaterThan(0);
18
48
  expect(
19
- result.resources.some((r: any) => r.uri.startsWith("aui-docs:///")),
49
+ result.resources.some((resource) =>
50
+ resource.uri.startsWith("aui-docs:///"),
51
+ ),
20
52
  ).toBe(true);
21
53
  expect(
22
- result.resources.some((r: any) => r.uri.startsWith("aui-example:///")),
54
+ result.resources.some((resource) =>
55
+ resource.uri.startsWith("aui-example:///"),
56
+ ),
23
57
  ).toBe(true);
24
58
  });
25
59
 
26
60
  it("reads a documentation resource as markdown", async () => {
27
- const list = await handler("resources/list")(
28
- { method: "resources/list", params: {} },
29
- {},
30
- );
31
- const doc = list.resources.find((r: any) =>
32
- r.uri.startsWith("aui-docs:///"),
33
- );
34
- const read = await handler("resources/read")(
35
- { method: "resources/read", params: { uri: doc.uri } },
36
- {},
37
- );
38
- expect(read.contents[0].mimeType).toBe("text/markdown");
39
- expect(typeof read.contents[0].text).toBe("string");
40
- expect(read.contents[0].text.length).toBeGreaterThan(0);
61
+ const list = await client.request<ResourceListResult>({
62
+ method: "resources/list",
63
+ params: {},
64
+ });
65
+ const doc = requireResource(list.resources, "aui-docs:///");
66
+ const read = await client.request<ResourceReadResult>({
67
+ method: "resources/read",
68
+ params: { uri: doc.uri },
69
+ });
70
+
71
+ expect(read.contents[0]?.mimeType).toBe("text/markdown");
72
+ expect(typeof read.contents[0]?.text).toBe("string");
73
+ expect(read.contents[0]?.text.length).toBeGreaterThan(0);
41
74
  });
42
75
 
43
76
  it("reads a slash-containing documentation resource", async () => {
44
- const list = await handler("resources/list")(
45
- { method: "resources/list", params: {} },
46
- {},
47
- );
77
+ const list = await client.request<ResourceListResult>({
78
+ method: "resources/list",
79
+ params: {},
80
+ });
48
81
  const prefix = "aui-docs:///";
49
82
  const nested = list.resources.find(
50
- (r: any) =>
51
- r.uri.startsWith(prefix) && r.uri.slice(prefix.length).includes("/"),
83
+ (resource) =>
84
+ resource.uri.startsWith(prefix) &&
85
+ resource.uri.slice(prefix.length).includes("/"),
52
86
  );
87
+
53
88
  expect(nested).toBeDefined();
54
- const read = await handler("resources/read")(
55
- { method: "resources/read", params: { uri: nested.uri } },
56
- {},
57
- );
58
- expect(read.contents[0].mimeType).toBe("text/markdown");
59
- expect(read.contents[0].text.length).toBeGreaterThan(0);
89
+ if (!nested) throw new Error("Missing nested documentation resource");
90
+
91
+ const read = await client.request<ResourceReadResult>({
92
+ method: "resources/read",
93
+ params: { uri: nested.uri },
94
+ });
95
+
96
+ expect(read.contents[0]?.mimeType).toBe("text/markdown");
97
+ expect(read.contents[0]?.text.length).toBeGreaterThan(0);
60
98
  });
61
99
 
62
100
  it("errors on an unknown documentation resource", async () => {
63
101
  await expect(
64
- handler("resources/read")(
65
- {
66
- method: "resources/read",
67
- params: { uri: "aui-docs:///definitely-not-a-real-doc" },
68
- },
69
- {},
70
- ),
102
+ client.request({
103
+ method: "resources/read",
104
+ params: { uri: "aui-docs:///definitely-not-a-real-doc" },
105
+ }),
71
106
  ).rejects.toThrow();
72
107
  });
73
108
 
74
109
  it("reads an example resource as markdown", async () => {
75
- const list = await handler("resources/list")(
76
- { method: "resources/list", params: {} },
77
- {},
78
- );
79
- const example = list.resources.find((r: any) =>
80
- r.uri.startsWith("aui-example:///"),
81
- );
82
- const read = await handler("resources/read")(
83
- { method: "resources/read", params: { uri: example.uri } },
84
- {},
85
- );
86
- expect(read.contents[0].mimeType).toBe("text/markdown");
87
- expect(typeof read.contents[0].text).toBe("string");
88
- expect(read.contents[0].text.length).toBeGreaterThan(0);
110
+ const list = await client.request<ResourceListResult>({
111
+ method: "resources/list",
112
+ params: {},
113
+ });
114
+ const example = requireResource(list.resources, "aui-example:///");
115
+ const read = await client.request<ResourceReadResult>({
116
+ method: "resources/read",
117
+ params: { uri: example.uri },
118
+ });
119
+
120
+ expect(read.contents[0]?.mimeType).toBe("text/markdown");
121
+ expect(typeof read.contents[0]?.text).toBe("string");
122
+ expect(read.contents[0]?.text.length).toBeGreaterThan(0);
89
123
  });
90
124
 
91
125
  it("errors on an unknown example resource", async () => {
92
126
  await expect(
93
- handler("resources/read")(
94
- {
95
- method: "resources/read",
96
- params: { uri: "aui-example:///definitely-not-a-real-example" },
97
- },
98
- {},
99
- ),
127
+ client.request({
128
+ method: "resources/read",
129
+ params: { uri: "aui-example:///definitely-not-a-real-example" },
130
+ }),
100
131
  ).rejects.toThrow();
101
132
  });
102
133
  });
@@ -5,11 +5,19 @@ import { PACKAGE_DIR } from "../../constants.js";
5
5
  import { docsTools } from "../docs.js";
6
6
  import { examplesTools } from "../examples.js";
7
7
  import { searchTools } from "../search.js";
8
+ import {
9
+ xuluxTemplatesListTool,
10
+ xuluxTemplateDetailsTool,
11
+ xuluxTemplatePreviewTool,
12
+ } from "../xulux-templates.js";
8
13
 
9
14
  const tools = {
10
15
  assistantUIDocs: docsTools,
11
16
  assistantUIExamples: examplesTools,
12
17
  assistantUISearch: searchTools,
18
+ assistantUITemplates: xuluxTemplatesListTool,
19
+ assistantUITemplateDetails: xuluxTemplateDetailsTool,
20
+ assistantUITemplatePreview: xuluxTemplatePreviewTool,
13
21
  };
14
22
 
15
23
  export const testContext = {