@assistant-ui/mcp-docs-server 0.1.36 → 0.1.39

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 (246) hide show
  1. package/.docs/organized/code-examples/waterfall.md +8 -9
  2. package/.docs/organized/code-examples/with-a2a.md +16 -11
  3. package/.docs/organized/code-examples/with-ag-ui.md +16 -11
  4. package/.docs/organized/code-examples/{with-ai-sdk-v6.md → with-ai-sdk-v7.md} +32 -21
  5. package/.docs/organized/code-examples/with-artifacts.md +17 -10
  6. package/.docs/organized/code-examples/with-assistant-transport.md +15 -8
  7. package/.docs/organized/code-examples/with-browser-extension.md +15 -8
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +17 -10
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +10 -9
  10. package/.docs/organized/code-examples/with-cloud.md +18 -13
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +17 -10
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +20 -13
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +20 -13
  14. package/.docs/organized/code-examples/with-eve.md +16 -9
  15. package/.docs/organized/code-examples/with-expo.md +26 -23
  16. package/.docs/organized/code-examples/with-external-store.md +16 -11
  17. package/.docs/organized/code-examples/with-ffmpeg.md +18 -13
  18. package/.docs/organized/code-examples/with-generative-ui.md +20 -15
  19. package/.docs/organized/code-examples/with-google-adk.md +16 -9
  20. package/.docs/organized/code-examples/with-heat-graph.md +8 -9
  21. package/.docs/organized/code-examples/with-image-generation.md +17 -10
  22. package/.docs/organized/code-examples/with-interactables.md +19 -15
  23. package/.docs/organized/code-examples/with-langchain.md +17 -10
  24. package/.docs/organized/code-examples/with-langgraph.md +17 -10
  25. package/.docs/organized/code-examples/with-livekit.md +21 -14
  26. package/.docs/organized/code-examples/with-mcp.md +40 -18
  27. package/.docs/organized/code-examples/with-opencode.md +22 -17
  28. package/.docs/organized/code-examples/with-pi.md +47 -12
  29. package/.docs/organized/code-examples/with-react-hook-form.md +19 -14
  30. package/.docs/organized/code-examples/with-react-ink-web.md +6 -6
  31. package/.docs/organized/code-examples/with-react-ink.md +2 -2
  32. package/.docs/organized/code-examples/with-react-router.md +20 -15
  33. package/.docs/organized/code-examples/with-resumable-stream.md +19 -12
  34. package/.docs/organized/code-examples/with-store.md +8 -9
  35. package/.docs/organized/code-examples/with-tanstack.md +17 -11
  36. package/.docs/organized/code-examples/with-tap-runtime.md +16 -11
  37. package/.docs/organized/code-examples/with-virtualized-thread.md +17 -12
  38. package/.docs/raw/docs/(docs)/base-ui.mdx +39 -0
  39. package/.docs/raw/docs/(docs)/cli.mdx +18 -2
  40. package/.docs/raw/docs/(docs)/installation.mdx +15 -1
  41. package/.docs/raw/docs/(docs)/rtl.mdx +2 -4
  42. package/.docs/raw/docs/(reference)/api-reference/generative-ui/actions.mdx +56 -0
  43. package/.docs/raw/docs/(reference)/api-reference/generative-ui/components.mdx +86 -0
  44. package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +19 -1
  45. package/.docs/raw/docs/(reference)/api-reference/generative-ui/json-generative-ui.mdx +42 -0
  46. package/.docs/raw/docs/(reference)/api-reference/generative-ui/rendering.mdx +53 -2
  47. package/.docs/raw/docs/(reference)/api-reference/generative-ui/slack.mdx +81 -0
  48. package/.docs/raw/docs/(reference)/api-reference/generative-ui/teams.mdx +86 -0
  49. package/.docs/raw/docs/(reference)/api-reference/generative-ui/tokens.mdx +62 -0
  50. package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +4 -1
  51. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +4 -2
  52. package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +37 -0
  53. package/.docs/raw/docs/(reference)/api-reference/overview.mdx +1 -0
  54. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +29 -29
  55. package/.docs/raw/docs/(reference)/api-reference/primitives/composition.mdx +1 -0
  56. package/.docs/raw/docs/(reference)/api-reference/primitives/index.mdx +3 -0
  57. package/.docs/raw/docs/(reference)/api-reference/primitives/selection-toolbar.mdx +2 -0
  58. package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +0 -2
  59. package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +12 -9
  60. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +4 -0
  61. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +7 -1
  62. package/.docs/raw/docs/cloud/langgraph.mdx +4 -2
  63. package/.docs/raw/docs/guides/chatgpt-subscription.mdx +108 -0
  64. package/.docs/raw/docs/guides/dictation.mdx +185 -257
  65. package/.docs/raw/docs/guides/index.mdx +10 -0
  66. package/.docs/raw/docs/guides/mentions.mdx +31 -3
  67. package/.docs/raw/docs/guides/resumable-streams.mdx +12 -1
  68. package/.docs/raw/docs/guides/speech.mdx +47 -29
  69. package/.docs/raw/docs/guides/suggestions.mdx +70 -1
  70. package/.docs/raw/docs/guides/voice.mdx +197 -267
  71. package/.docs/raw/docs/ink/primitives.mdx +35 -1
  72. package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +13 -7
  73. package/.docs/raw/docs/integrations/frameworks/cloudflare-agents/overview.mdx +3 -3
  74. package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +1 -1
  75. package/.docs/raw/docs/integrations/frameworks/mastra/overview.mdx +3 -3
  76. package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +1 -1
  77. package/.docs/raw/docs/integrations/gateways/index.mdx +2 -2
  78. package/.docs/raw/docs/integrations/index.mdx +1 -1
  79. package/.docs/raw/docs/integrations/observability/helicone.mdx +1 -1
  80. package/.docs/raw/docs/integrations/observability/langfuse.mdx +1 -1
  81. package/.docs/raw/docs/integrations/observability/langsmith.mdx +3 -3
  82. package/.docs/raw/docs/migrations/index.mdx +50 -0
  83. package/.docs/raw/docs/migrations/react-langgraph-v0-7.mdx +2 -2
  84. package/.docs/raw/docs/migrations/toolkit-tools.mdx +4 -2
  85. package/.docs/raw/docs/primitives/chain-of-thought.mdx +6 -1
  86. package/.docs/raw/docs/primitives/composer.mdx +16 -0
  87. package/.docs/raw/docs/primitives/selection-toolbar.mdx +25 -0
  88. package/.docs/raw/docs/primitives/thread-list.mdx +30 -6
  89. package/.docs/raw/docs/react-native/primitives.mdx +23 -0
  90. package/.docs/raw/docs/runtimes/a2a/quickstart.mdx +1 -1
  91. package/.docs/raw/docs/runtimes/ag-ui/agent-state.mdx +124 -0
  92. package/.docs/raw/docs/runtimes/ag-ui/quickstart.mdx +1 -1
  93. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +13 -2
  94. package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +13 -4
  95. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +11 -11
  96. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +5 -5
  97. package/.docs/raw/docs/runtimes/ai-sdk/{v6.mdx → v6-legacy.mdx} +8 -6
  98. package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +717 -0
  99. package/.docs/raw/docs/runtimes/concepts/adapters.mdx +1 -1
  100. package/.docs/raw/docs/runtimes/concepts/architecture.mdx +1 -1
  101. package/.docs/raw/docs/runtimes/custom/data-stream.mdx +10 -19
  102. package/.docs/raw/docs/runtimes/custom/external-store.mdx +1 -1
  103. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +2 -0
  104. package/.docs/raw/docs/runtimes/eve/overview.mdx +1 -1
  105. package/.docs/raw/docs/runtimes/google-adk/quickstart.mdx +1 -1
  106. package/.docs/raw/docs/runtimes/langchain.mdx +2 -2
  107. package/.docs/raw/docs/runtimes/langgraph/agent-state.mdx +181 -0
  108. package/.docs/raw/docs/runtimes/opencode/hooks.mdx +1 -1
  109. package/.docs/raw/docs/tools/backend.mdx +6 -3
  110. package/.docs/raw/docs/tools/defining-tools.mdx +12 -1
  111. package/.docs/raw/docs/tools/generative-ui.mdx +95 -0
  112. package/.docs/raw/docs/tools/mcp-apps.mdx +37 -10
  113. package/.docs/raw/docs/tools/mcp.mdx +105 -3
  114. package/.docs/raw/docs/tools/tool-ui.mdx +6 -4
  115. package/.docs/raw/docs/tools/user-managed-mcp.mdx +41 -4
  116. package/.docs/raw/docs/ui/accordion.mdx +16 -10
  117. package/.docs/raw/docs/ui/assistant-modal.mdx +8 -4
  118. package/.docs/raw/docs/ui/attachment.mdx +5 -1
  119. package/.docs/raw/docs/ui/badge.mdx +23 -12
  120. package/.docs/raw/docs/ui/follow-up-suggestions.mdx +80 -0
  121. package/.docs/raw/docs/ui/model-selector.mdx +69 -6
  122. package/.docs/raw/docs/ui/select.mdx +22 -14
  123. package/.docs/raw/docs/ui/sources.mdx +1 -1
  124. package/.docs/raw/docs/ui/tabs.mdx +25 -14
  125. package/.docs/raw/docs/utilities/heat-graph.mdx +2 -2
  126. package/README.md +1 -1
  127. package/dist/constants.d.ts +2 -1
  128. package/dist/constants.d.ts.map +1 -1
  129. package/dist/constants.js +2 -1
  130. package/dist/constants.js.map +1 -1
  131. package/dist/index.d.ts +0 -1
  132. package/dist/index.d.ts.map +1 -1
  133. package/dist/index.js +51 -0
  134. package/dist/index.js.map +1 -1
  135. package/dist/prepare-docs/code-examples.d.ts.map +1 -1
  136. package/dist/prepare-docs/copy-raw.d.ts.map +1 -1
  137. package/dist/prepare-docs/copy-raw.js +0 -4
  138. package/dist/prepare-docs/copy-raw.js.map +1 -1
  139. package/dist/prompts/xulux-playground.d.ts +12 -0
  140. package/dist/prompts/xulux-playground.d.ts.map +1 -0
  141. package/dist/prompts/xulux-playground.js +33 -0
  142. package/dist/prompts/xulux-playground.js.map +1 -0
  143. package/dist/tools/docs.d.ts +2 -4
  144. package/dist/tools/docs.d.ts.map +1 -1
  145. package/dist/tools/docs.js +38 -10
  146. package/dist/tools/docs.js.map +1 -1
  147. package/dist/tools/examples.d.ts +5 -5
  148. package/dist/tools/examples.d.ts.map +1 -1
  149. package/dist/tools/examples.js +10 -7
  150. package/dist/tools/examples.js.map +1 -1
  151. package/dist/tools/resources.d.ts +6 -0
  152. package/dist/tools/resources.d.ts.map +1 -0
  153. package/dist/tools/resources.js +74 -0
  154. package/dist/tools/resources.js.map +1 -0
  155. package/dist/tools/search.d.ts +30 -0
  156. package/dist/tools/search.d.ts.map +1 -0
  157. package/dist/tools/search.js +39 -0
  158. package/dist/tools/search.js.map +1 -0
  159. package/dist/tools/tests/test-setup.d.ts.map +1 -1
  160. package/dist/tools/tests/test-setup.js +7 -1
  161. package/dist/tools/tests/test-setup.js.map +1 -1
  162. package/dist/tools/xulux-templates.d.ts +72 -0
  163. package/dist/tools/xulux-templates.d.ts.map +1 -0
  164. package/dist/tools/xulux-templates.js +82 -0
  165. package/dist/tools/xulux-templates.js.map +1 -0
  166. package/dist/utils/cache.d.ts +5 -0
  167. package/dist/utils/cache.d.ts.map +1 -0
  168. package/dist/utils/cache.js +18 -0
  169. package/dist/utils/cache.js.map +1 -0
  170. package/dist/utils/logger.d.ts.map +1 -1
  171. package/dist/utils/mcp-format.d.ts +1 -0
  172. package/dist/utils/mcp-format.d.ts.map +1 -1
  173. package/dist/utils/mcp-format.js +7 -4
  174. package/dist/utils/mcp-format.js.map +1 -1
  175. package/dist/utils/mdx.d.ts +2 -1
  176. package/dist/utils/mdx.d.ts.map +1 -1
  177. package/dist/utils/mdx.js +19 -2
  178. package/dist/utils/mdx.js.map +1 -1
  179. package/dist/utils/paths.d.ts +2 -1
  180. package/dist/utils/paths.d.ts.map +1 -1
  181. package/dist/utils/paths.js +20 -1
  182. package/dist/utils/paths.js.map +1 -1
  183. package/dist/utils/search.d.ts +10 -0
  184. package/dist/utils/search.d.ts.map +1 -0
  185. package/dist/utils/search.js +97 -0
  186. package/dist/utils/search.js.map +1 -0
  187. package/dist/utils/security.d.ts.map +1 -1
  188. package/dist/xulux/catalog-client.d.ts +14 -0
  189. package/dist/xulux/catalog-client.d.ts.map +1 -0
  190. package/dist/xulux/catalog-client.js +67 -0
  191. package/dist/xulux/catalog-client.js.map +1 -0
  192. package/dist/xulux/fallback-catalog.d.ts +7 -0
  193. package/dist/xulux/fallback-catalog.d.ts.map +1 -0
  194. package/dist/xulux/fallback-catalog.js +47 -0
  195. package/dist/xulux/fallback-catalog.js.map +1 -0
  196. package/dist/xulux/fetch-sandbox.d.ts +5 -0
  197. package/dist/xulux/fetch-sandbox.d.ts.map +1 -0
  198. package/dist/xulux/fetch-sandbox.js +40 -0
  199. package/dist/xulux/fetch-sandbox.js.map +1 -0
  200. package/dist/xulux/template-service.d.ts +84 -0
  201. package/dist/xulux/template-service.d.ts.map +1 -0
  202. package/dist/xulux/template-service.js +223 -0
  203. package/dist/xulux/template-service.js.map +1 -0
  204. package/dist/xulux/types.d.ts +55 -0
  205. package/dist/xulux/types.d.ts.map +1 -0
  206. package/dist/xulux/types.js +6 -0
  207. package/dist/xulux/types.js.map +1 -0
  208. package/package.json +5 -5
  209. package/src/constants.ts +2 -0
  210. package/src/index.ts +67 -0
  211. package/src/prepare-docs/copy-raw.ts +0 -5
  212. package/src/prompts/xulux-playground.ts +36 -0
  213. package/src/tools/docs.ts +52 -4
  214. package/src/tools/examples.ts +18 -11
  215. package/src/tools/resources.ts +114 -0
  216. package/src/tools/search.ts +46 -0
  217. package/src/tools/tests/completions.test.ts +46 -0
  218. package/src/tools/tests/directory-size-cap.test.ts +50 -0
  219. package/src/tools/tests/docs.test.ts +20 -0
  220. package/src/tools/tests/examples.test.ts +5 -5
  221. package/src/tools/tests/listings-cache.test.ts +19 -0
  222. package/src/tools/tests/mcp-protocol.test.ts +92 -1
  223. package/src/tools/tests/resources.test.ts +102 -0
  224. package/src/tools/tests/search.test.ts +37 -0
  225. package/src/tools/tests/test-setup.ts +10 -0
  226. package/src/tools/tests/xulux-templates.test.ts +262 -0
  227. package/src/tools/xulux-templates.ts +141 -0
  228. package/src/utils/cache.ts +20 -0
  229. package/src/utils/mcp-format.ts +8 -6
  230. package/src/utils/mdx.ts +21 -1
  231. package/src/utils/paths.ts +25 -0
  232. package/src/utils/search.ts +131 -0
  233. package/src/utils/tests/cache.test.ts +51 -0
  234. package/src/utils/tests/mcp-format.test.ts +22 -0
  235. package/src/utils/tests/security.test.ts +1 -1
  236. package/src/xulux/catalog-client.ts +105 -0
  237. package/src/xulux/fallback-catalog.ts +63 -0
  238. package/src/xulux/fetch-sandbox.ts +56 -0
  239. package/src/xulux/template-service.ts +406 -0
  240. package/src/xulux/types.ts +60 -0
  241. package/.docs/raw/blog/2024-07-29-hello/index.mdx +0 -64
  242. package/.docs/raw/blog/2024-09-11/index.mdx +0 -10
  243. package/.docs/raw/blog/2024-12-15/index.mdx +0 -10
  244. package/.docs/raw/blog/2025-01-31-changelog/index.mdx +0 -127
  245. package/.docs/raw/blog/2026-03-launch-week/index.mdx +0 -258
  246. package/.docs/raw/docs/(reference)/api-reference/hooks/utilities.mdx +0 -464
package/src/tools/docs.ts CHANGED
@@ -1,7 +1,12 @@
1
1
  import { z } from "zod/v3";
2
2
  import { stat, lstat } from "node:fs/promises";
3
3
  import { join, extname } from "node:path";
4
- import { DOCS_PATH, MDX_EXTENSION, MAX_FILE_SIZE } from "../constants.js";
4
+ import {
5
+ DOCS_PATH,
6
+ MDX_EXTENSION,
7
+ MAX_FILE_SIZE,
8
+ MAX_DIRECTORY_CONTENT_SIZE,
9
+ } from "../constants.js";
5
10
  import { logger } from "../utils/logger.js";
6
11
  import {
7
12
  listDirContents,
@@ -29,7 +34,11 @@ interface DocResult {
29
34
  content?: string;
30
35
  files?: string[];
31
36
  directories?: string[];
37
+ summaries?: Array<{ name: string; title?: string; excerpt?: string }>;
38
+ title?: string;
39
+ excerpt?: string;
32
40
  suggestions?: string[];
41
+ hint?: string;
33
42
  error?: string;
34
43
  }
35
44
 
@@ -79,16 +88,46 @@ async function readDocumentation(docPath: string): Promise<DocResult> {
79
88
  const { directories, files } = await listDirContents(fullPath);
80
89
 
81
90
  const contents: Record<string, string> = {};
91
+ const summaries: Array<{
92
+ name: string;
93
+ title?: string;
94
+ excerpt?: string;
95
+ }> = [];
96
+ let aggregateSize = 0;
97
+ let truncated = false;
82
98
  for (const file of files) {
99
+ let fileSize: number;
100
+ try {
101
+ fileSize = (await stat(join(fullPath, file))).size;
102
+ } catch (error) {
103
+ logger.warn(
104
+ `Failed to stat MDX file: ${join(fullPath, file)}`,
105
+ error,
106
+ );
107
+ continue;
108
+ }
109
+ aggregateSize += fileSize;
110
+ if (aggregateSize > MAX_DIRECTORY_CONTENT_SIZE) {
111
+ truncated = true;
112
+ break;
113
+ }
83
114
  const mdxContent = await readMDXFile(join(fullPath, file));
84
115
  if (mdxContent) {
85
- const fileName = file.replace(MDX_EXTENSION, "");
86
- contents[fileName] = formatMDXContent(mdxContent);
116
+ const name = file.replace(MDX_EXTENSION, "");
117
+ contents[name] = formatMDXContent(mdxContent);
118
+ const title = mdxContent.frontmatter["title"];
119
+ summaries.push({
120
+ name,
121
+ ...(typeof title === "string" && { title }),
122
+ ...(mdxContent.excerpt !== undefined && {
123
+ excerpt: mdxContent.excerpt,
124
+ }),
125
+ });
87
126
  }
88
127
  }
89
128
 
90
129
  const content =
91
- Object.keys(contents).length > 0
130
+ !truncated && Object.keys(contents).length > 0
92
131
  ? JSON.stringify(contents, null, 2)
93
132
  : undefined;
94
133
  return {
@@ -97,7 +136,11 @@ async function readDocumentation(docPath: string): Promise<DocResult> {
97
136
  type: "directory",
98
137
  directories,
99
138
  files: files.map((f) => f.replace(MDX_EXTENSION, "")),
139
+ ...(summaries.length > 0 && { summaries }),
100
140
  ...(content !== undefined && { content }),
141
+ ...(truncated && {
142
+ hint: `Directory content exceeds ${MAX_DIRECTORY_CONTENT_SIZE} bytes and was omitted; summaries cover only the files read before the limit. Request individual files by path to retrieve their content.`,
143
+ }),
101
144
  };
102
145
  }
103
146
  }
@@ -132,11 +175,16 @@ async function readDocumentation(docPath: string): Promise<DocResult> {
132
175
  const mdxContent = await readMDXFile(mdxPath);
133
176
 
134
177
  if (mdxContent) {
178
+ const title = mdxContent.frontmatter["title"];
135
179
  return {
136
180
  path: docPath,
137
181
  found: true,
138
182
  type: "file",
139
183
  content: formatMDXContent(mdxContent),
184
+ ...(typeof title === "string" && { title }),
185
+ ...(mdxContent.excerpt !== undefined && {
186
+ excerpt: mdxContent.excerpt,
187
+ }),
140
188
  };
141
189
  }
142
190
  }
@@ -2,6 +2,7 @@ import { z } from "zod/v3";
2
2
  import { readFile, readdir, lstat } from "node:fs/promises";
3
3
  import { join, extname } from "node:path";
4
4
  import { CODE_EXAMPLES_PATH, MAX_FILE_SIZE } from "../constants.js";
5
+ import { cacheListing } from "../utils/cache.js";
5
6
  import { logger } from "../utils/logger.js";
6
7
  import { formatMCPResponse } from "../utils/mcp-format.js";
7
8
  import { sanitizePath } from "../utils/security.js";
@@ -11,24 +12,30 @@ const examplesInputSchema = z.object({
11
12
  .string()
12
13
  .optional()
13
14
  .describe(
14
- 'Example name (e.g., "with-ai-sdk-v6"). Leave empty to list all examples.',
15
+ 'Example name (e.g., "with-ai-sdk-v7"). Leave empty to list all examples.',
15
16
  ),
16
17
  });
17
18
 
18
- async function listCodeExamples(): Promise<string[]> {
19
- try {
20
- const files = await readdir(CODE_EXAMPLES_PATH);
21
- return files
22
- .filter((file) => extname(file) === ".md")
23
- .map((file) => file.replace(".md", ""))
24
- .sort();
25
- } catch (error) {
19
+ const loadCodeExamples = cacheListing(scanCodeExamples);
20
+
21
+ export function listCodeExamples(): Promise<string[]> {
22
+ return loadCodeExamples().catch((error) => {
26
23
  logger.error("Failed to list code examples", error);
27
24
  return [];
28
- }
25
+ });
26
+ }
27
+
28
+ async function scanCodeExamples(): Promise<string[]> {
29
+ const files = await readdir(CODE_EXAMPLES_PATH);
30
+ return files
31
+ .filter((file) => extname(file) === ".md")
32
+ .map((file) => file.replace(".md", ""))
33
+ .sort();
29
34
  }
30
35
 
31
- async function readCodeExample(exampleName: string): Promise<string | null> {
36
+ export async function readCodeExample(
37
+ exampleName: string,
38
+ ): Promise<string | null> {
32
39
  try {
33
40
  const sanitized = sanitizePath(exampleName);
34
41
  const filePath = join(CODE_EXAMPLES_PATH, `${sanitized}.md`);
@@ -0,0 +1,114 @@
1
+ import {
2
+ McpServer,
3
+ ResourceTemplate,
4
+ } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { join } from "node:path";
6
+ import { DOCS_PATH, MDX_EXTENSION } from "../constants.js";
7
+ import { getAvailableDocFiles } from "../utils/paths.js";
8
+ import { readMDXFileSafe, formatMDXContent } from "../utils/mdx.js";
9
+ import { sanitizePath } from "../utils/security.js";
10
+ import { listCodeExamples, readCodeExample } from "./examples.js";
11
+ import { logger } from "../utils/logger.js";
12
+
13
+ const MARKDOWN_MIME_TYPE = "text/markdown";
14
+ const DOCS_SCHEME = "aui-docs";
15
+ const EXAMPLE_SCHEME = "aui-example";
16
+
17
+ function templateVarToPath(value: string | string[] | undefined): string {
18
+ if (Array.isArray(value)) return value.join("/");
19
+ return value ?? "";
20
+ }
21
+
22
+ interface MarkdownResourceConfig {
23
+ name: string;
24
+ scheme: string;
25
+ variable: string;
26
+ title: string;
27
+ description: string;
28
+ notFoundLabel: string;
29
+ list: () => Promise<string[]>;
30
+ read: (key: string) => Promise<string | null>;
31
+ }
32
+
33
+ function registerMarkdownResource(
34
+ server: McpServer,
35
+ config: MarkdownResourceConfig,
36
+ ): void {
37
+ const {
38
+ name,
39
+ scheme,
40
+ variable,
41
+ title,
42
+ description,
43
+ notFoundLabel,
44
+ list,
45
+ read,
46
+ } = config;
47
+ server.registerResource(
48
+ name,
49
+ new ResourceTemplate(`${scheme}:///{+${variable}}`, {
50
+ list: async () => ({
51
+ resources: (await list()).map((key) => ({
52
+ uri: `${scheme}:///${key}`,
53
+ name: key,
54
+ mimeType: MARKDOWN_MIME_TYPE,
55
+ })),
56
+ }),
57
+ complete: {
58
+ [variable]: async (value) => {
59
+ const needle = value.toLowerCase();
60
+ return (await list()).filter((key) =>
61
+ key.toLowerCase().includes(needle),
62
+ );
63
+ },
64
+ },
65
+ }),
66
+ { title, description, mimeType: MARKDOWN_MIME_TYPE },
67
+ async (uri, variables) => {
68
+ const key = templateVarToPath(variables[variable]);
69
+ const text = await read(key);
70
+ if (text === null) {
71
+ throw new Error(`${notFoundLabel} not found: ${key}`);
72
+ }
73
+ return {
74
+ contents: [{ uri: uri.href, mimeType: MARKDOWN_MIME_TYPE, text }],
75
+ };
76
+ },
77
+ );
78
+ }
79
+
80
+ async function readDocResource(path: string): Promise<string | null> {
81
+ const sanitized = sanitizePath(path);
82
+ const mdx = await readMDXFileSafe(
83
+ join(DOCS_PATH, `${sanitized}${MDX_EXTENSION}`),
84
+ );
85
+ return mdx ? formatMDXContent(mdx) : null;
86
+ }
87
+
88
+ export function registerResources(server: McpServer): void {
89
+ registerMarkdownResource(server, {
90
+ name: "assistant-ui-docs",
91
+ scheme: DOCS_SCHEME,
92
+ variable: "path",
93
+ title: "assistant-ui Documentation",
94
+ description:
95
+ "Individual assistant-ui documentation pages, readable as markdown by path.",
96
+ notFoundLabel: "Documentation",
97
+ list: getAvailableDocFiles,
98
+ read: readDocResource,
99
+ });
100
+
101
+ registerMarkdownResource(server, {
102
+ name: "assistant-ui-examples",
103
+ scheme: EXAMPLE_SCHEME,
104
+ variable: "name",
105
+ title: "assistant-ui Examples",
106
+ description:
107
+ "Complete assistant-ui example projects, readable as markdown.",
108
+ notFoundLabel: "Example",
109
+ list: listCodeExamples,
110
+ read: readCodeExample,
111
+ });
112
+
113
+ logger.debug("Registered docs and examples resources");
114
+ }
@@ -0,0 +1,46 @@
1
+ import { z } from "zod/v3";
2
+ import { logger } from "../utils/logger.js";
3
+ import { formatMCPResponse } from "../utils/mcp-format.js";
4
+ import { searchDocs } from "../utils/search.js";
5
+
6
+ const searchInputSchema = z.object({
7
+ query: z
8
+ .string()
9
+ .min(1)
10
+ .describe("Keyword(s) to search assistant-ui documentation for."),
11
+ limit: z
12
+ .number()
13
+ .int()
14
+ .min(1)
15
+ .max(25)
16
+ .default(10)
17
+ .describe("Maximum number of results to return (default 10)."),
18
+ });
19
+
20
+ export const searchTools = {
21
+ name: "assistantUISearch",
22
+ description:
23
+ "Search assistant-ui documentation by keyword. Returns ranked matches with their doc path, title, and a snippet. Fetch full content with assistantUIDocs using a returned path.",
24
+ parameters: searchInputSchema.shape,
25
+ execute: async ({ query, limit }: z.infer<typeof searchInputSchema>) => {
26
+ logger.info(`Searching documentation for: ${query}`);
27
+ try {
28
+ const results = await searchDocs(query, limit);
29
+ if (results.length === 0) {
30
+ return formatMCPResponse({
31
+ query,
32
+ results: [],
33
+ total: 0,
34
+ hint: 'No matches. Use assistantUIDocs with "/" to list all sections.',
35
+ });
36
+ }
37
+ return formatMCPResponse({ query, results, total: results.length });
38
+ } catch (error) {
39
+ logger.error("Failed to search documentation", error);
40
+ return formatMCPResponse({
41
+ error: "Failed to search documentation",
42
+ message: error instanceof Error ? error.message : String(error),
43
+ });
44
+ }
45
+ },
46
+ };
@@ -0,0 +1,46 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { server } from "../../index.js";
3
+
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
+ }
10
+
11
+ async function complete(uri: string, name: string, value: string) {
12
+ return handler("completion/complete")(
13
+ {
14
+ method: "completion/complete",
15
+ params: { ref: { type: "ref/resource", uri }, argument: { name, value } },
16
+ },
17
+ {},
18
+ );
19
+ }
20
+
21
+ describe("resource argument completions", () => {
22
+ 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);
25
+ expect(
26
+ res.completion.values.every((v: string) =>
27
+ v.toLowerCase().includes("api"),
28
+ ),
29
+ ).toBe(true);
30
+ });
31
+
32
+ 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);
35
+ expect(
36
+ res.completion.values.every((v: string) =>
37
+ v.toLowerCase().includes("with"),
38
+ ),
39
+ ).toBe(true);
40
+ });
41
+
42
+ 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);
45
+ });
46
+ });
@@ -0,0 +1,50 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+
3
+ vi.mock("../../utils/paths.js", async (importOriginal) => ({
4
+ ...(await importOriginal<typeof import("../../utils/paths.js")>()),
5
+ listDirContents: vi.fn(async () => ({
6
+ directories: [],
7
+ files: ["first.mdx", "second.mdx"],
8
+ })),
9
+ }));
10
+
11
+ vi.mock("node:fs/promises", async (importOriginal) => {
12
+ const actual = await importOriginal<typeof import("node:fs/promises")>();
13
+ return {
14
+ ...actual,
15
+ stat: vi.fn(async (path: string) => ({
16
+ isFile: () => path.endsWith(".mdx"),
17
+ isDirectory: () => !path.endsWith(".mdx"),
18
+ size: 600 * 1024,
19
+ })),
20
+ };
21
+ });
22
+
23
+ vi.mock("../../utils/mdx.js", async (importOriginal) => ({
24
+ ...(await importOriginal<typeof import("../../utils/mdx.js")>()),
25
+ readMDXFile: vi.fn(async () => ({
26
+ content: "x".repeat(600 * 1024),
27
+ frontmatter: {},
28
+ })),
29
+ formatMDXContent: (mdx: { content: string }) => mdx.content,
30
+ }));
31
+
32
+ import { docsTools } from "../docs.js";
33
+
34
+ function parse(result: { content: Array<{ text: string }> }) {
35
+ return JSON.parse(result.content[0]!.text);
36
+ }
37
+
38
+ describe("assistantUIDocs directory aggregate size cap", () => {
39
+ it("omits inlined content and returns a hint when a directory exceeds the cap", async () => {
40
+ const result = await docsTools.execute({
41
+ paths: ["(reference)/api-reference/primitives"],
42
+ });
43
+ const parsed = parse(result);
44
+
45
+ expect(parsed.type).toBe("directory");
46
+ expect(parsed.files).toEqual(["first", "second"]);
47
+ expect(parsed.content).toBeUndefined();
48
+ expect(parsed.hint).toContain("exceeds");
49
+ });
50
+ });
@@ -91,6 +91,26 @@ describe("assistantUIDocs", () => {
91
91
  expect(result.content).toContain("assistant-ui");
92
92
  });
93
93
 
94
+ it("includes title and excerpt on a file response", async () => {
95
+ const result = await testContext.callTool("assistantUIDocs", {
96
+ paths: ["(docs)/index"],
97
+ });
98
+ expect(result.type).toBe("file");
99
+ expect(result.title ?? result.excerpt).toBeDefined();
100
+ });
101
+
102
+ it("includes per-file summaries on a directory listing", async () => {
103
+ const result = await testContext.callTool("assistantUIDocs", {
104
+ paths: ["(reference)/api-reference/primitives"],
105
+ });
106
+ expect(result.type).toBe("directory");
107
+ expect(Array.isArray(result.summaries)).toBe(true);
108
+ expect(result.summaries.length).toBeGreaterThan(0);
109
+ const summary = result.summaries[0];
110
+ expect(summary.name).toBeDefined();
111
+ expect(summary.title ?? summary.excerpt).toBeDefined();
112
+ });
113
+
94
114
  it("should skip symlinks and large files", async () => {
95
115
  const mockedLstat = vi.mocked(fs.lstat);
96
116
 
@@ -19,20 +19,20 @@ describe("assistantUIExamples", () => {
19
19
  expect(result.examples).toBeDefined();
20
20
  expect(Array.isArray(result.examples)).toBe(true);
21
21
  expect(result.examples.length).toBeGreaterThan(0);
22
- expect(result.examples).toContain("with-ai-sdk-v6");
22
+ expect(result.examples).toContain("with-ai-sdk-v7");
23
23
  expect(result.examples).toContain("with-langgraph");
24
24
  expect(result.total).toBe(result.examples.length);
25
25
  });
26
26
 
27
27
  it("should retrieve specific example code", async () => {
28
28
  const result = await testContext.callTool("assistantUIExamples", {
29
- example: "with-ai-sdk-v6",
29
+ example: "with-ai-sdk-v7",
30
30
  });
31
31
 
32
32
  expect(result.type).toBe("example");
33
- expect(result.name).toBe("with-ai-sdk-v6");
33
+ expect(result.name).toBe("with-ai-sdk-v7");
34
34
  expect(result.content).toBeDefined();
35
- expect(result.content).toContain("# Example: with-ai-sdk-v6");
35
+ expect(result.content).toContain("# Example: with-ai-sdk-v7");
36
36
  expect(result.content).toContain("app/api/chat/route.ts");
37
37
  expect(result.content).toContain("streamText");
38
38
  });
@@ -50,7 +50,7 @@ describe("assistantUIExamples", () => {
50
50
 
51
51
  it("should include all files in example", async () => {
52
52
  const result = await testContext.callTool("assistantUIExamples", {
53
- example: "with-ai-sdk-v6",
53
+ example: "with-ai-sdk-v7",
54
54
  });
55
55
 
56
56
  expect(result.content).toContain("package.json");
@@ -0,0 +1,19 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { getAvailableDocFiles } from "../../utils/paths.js";
3
+ import { listCodeExamples } from "../examples.js";
4
+
5
+ describe("listing caches", () => {
6
+ it("caches the doc file listing (same instance on repeat calls)", async () => {
7
+ const first = await getAvailableDocFiles();
8
+ const second = await getAvailableDocFiles();
9
+ expect(second).toBe(first);
10
+ expect(first.length).toBeGreaterThan(0);
11
+ });
12
+
13
+ it("caches the example listing (same instance on repeat calls)", async () => {
14
+ const first = await listCodeExamples();
15
+ const second = await listCodeExamples();
16
+ expect(second).toBe(first);
17
+ expect(first.length).toBeGreaterThan(0);
18
+ });
19
+ });
@@ -4,9 +4,13 @@ import {
4
4
  InitializeRequestSchema,
5
5
  ListToolsRequestSchema,
6
6
  CallToolRequestSchema,
7
+ ListPromptsRequestSchema,
8
+ GetPromptRequestSchema,
7
9
  type InitializeRequest,
8
10
  type ListToolsRequest,
9
11
  type CallToolRequest,
12
+ type ListPromptsRequest,
13
+ type GetPromptRequest,
10
14
  } from "@modelcontextprotocol/sdk/types.js";
11
15
 
12
16
  describe("MCP Protocol Integration", () => {
@@ -55,7 +59,7 @@ describe("MCP Protocol Integration", () => {
55
59
 
56
60
  expect(result).toBeDefined();
57
61
  expect(result.tools).toBeInstanceOf(Array);
58
- expect(result.tools).toHaveLength(2);
62
+ expect(result.tools).toHaveLength(6);
59
63
 
60
64
  // Check the tools have proper JSON schemas
61
65
  const docsTool = result.tools.find(
@@ -74,6 +78,14 @@ describe("MCP Protocol Integration", () => {
74
78
  expect(examplesTool.inputSchema.type).toBe("object");
75
79
  expect(examplesTool.inputSchema.properties).toBeDefined();
76
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
+
77
89
  // registerTool metadata is surfaced on tools/list: `title` is a
78
90
  // top-level field, `annotations` carries only the hint flags.
79
91
  expect(docsTool.title).toBe("assistant-ui Documentation");
@@ -82,6 +94,33 @@ describe("MCP Protocol Integration", () => {
82
94
  expect(examplesTool.title).toBe("assistant-ui Examples");
83
95
  expect(examplesTool.annotations?.readOnlyHint).toBe(true);
84
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);
85
124
  });
86
125
 
87
126
  it("should handle CallTool request for assistantUIDocs", async () => {
@@ -137,4 +176,56 @@ describe("MCP Protocol Integration", () => {
137
176
  expect(result.content).toBeDefined();
138
177
  expect(result.content[0].type).toBe("text");
139
178
  });
179
+
180
+ it("should handle ListPrompts request", async () => {
181
+ const request: ListPromptsRequest = {
182
+ method: "prompts/list",
183
+ 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);
196
+ const prompt = result.prompts.find(
197
+ (p: any) => p.name === "assistant-ui-template-workflow",
198
+ );
199
+ expect(prompt).toBeDefined();
200
+ expect(prompt.title).toBe("assistant-ui Template Workflow");
201
+ });
202
+
203
+ it("should handle GetPrompt request for assistant-ui-template-workflow", async () => {
204
+ const request: GetPromptRequest = {
205
+ method: "prompts/get",
206
+ params: {
207
+ name: "assistant-ui-template-workflow",
208
+ arguments: {},
209
+ },
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, {});
220
+
221
+ 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
+ );
230
+ });
140
231
  });