@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
@@ -0,0 +1,63 @@
1
+ import {
2
+ XULUX_MCP_CATALOG_VERSION,
3
+ type XuluxCatalog,
4
+ type XuluxCatalogTemplate,
5
+ } from "./types.js";
6
+
7
+ // Minimal bundled fallback catalog for degraded mode. It supports discovery
8
+ // (ids + names) only. Full authoring details and preview/download URLs
9
+ // require the live catalog endpoint. Do NOT grow this into a copy of the
10
+ // full template knowledge base — the docs endpoint owns freshness.
11
+
12
+ export const FALLBACK_NOTE =
13
+ "The live assistant-ui template catalog could not be fetched, so this is minimal bundled fallback data. " +
14
+ "Template details, previews, and downloads require live catalog access. " +
15
+ "Check network access to the docs site or set XULUX_CATALOG_URL to a reachable catalog endpoint.";
16
+
17
+ const fallbackTemplate = (
18
+ overrides: Pick<XuluxCatalogTemplate, "id" | "kind" | "name" | "summary">,
19
+ ): XuluxCatalogTemplate => ({
20
+ templateId: overrides.id,
21
+ versionId: null,
22
+ assistantPlacement: "unavailable in fallback mode",
23
+ features: [],
24
+ customizable: [],
25
+ versions: [],
26
+ ...overrides,
27
+ });
28
+
29
+ export const FALLBACK_CATALOG: XuluxCatalog = {
30
+ version: XULUX_MCP_CATALOG_VERSION,
31
+ generatedAt: "fallback",
32
+ docsOrigin: "https://www.assistant-ui.com",
33
+ templates: [
34
+ fallbackTemplate({
35
+ id: "base-assistant-ui",
36
+ kind: "template",
37
+ name: "Configurable Base Assistant UI",
38
+ summary:
39
+ "Hosted configurable assistant-ui Base chat template with threads, composer, suggestions, and no-key demo flows.",
40
+ }),
41
+ fallbackTemplate({
42
+ id: "webpage-assistant",
43
+ kind: "template",
44
+ name: "Webpage with Assistant",
45
+ summary:
46
+ "Docs/website layout with a sidebar or modal assistant grounded in configurable pages.",
47
+ }),
48
+ fallbackTemplate({
49
+ id: "product-page-assistant",
50
+ kind: "template",
51
+ name: "Product Page with Floating Assistant",
52
+ summary:
53
+ "Mock product dashboard with a floating modal support assistant and analyze-then-handoff flow.",
54
+ }),
55
+ fallbackTemplate({
56
+ id: "expo-react-native",
57
+ kind: "example",
58
+ name: "Expo React Native Assistant",
59
+ summary:
60
+ "Mobile AI chat app built with Expo and assistant-ui React Native primitives.",
61
+ }),
62
+ ],
63
+ };
@@ -0,0 +1,56 @@
1
+ const SANDBOX_FETCH_HEADERS = {
2
+ Accept: "application/json, application/zip, application/octet-stream, */*",
3
+ // Blaxel preview hosts intermittently reset Node's default fetch path.
4
+ "User-Agent": "curl/8.7.1",
5
+ };
6
+
7
+ const MAX_ATTEMPTS = 3;
8
+ const RETRY_DELAY_MS = 300;
9
+
10
+ function isRetryableFetchError(error: unknown) {
11
+ if (!(error instanceof Error)) return false;
12
+ const cause = error.cause as { code?: string } | undefined;
13
+ const code = cause?.code ?? error.message;
14
+ return (
15
+ code === "ECONNRESET" ||
16
+ code === "ETIMEDOUT" ||
17
+ code === "ECONNREFUSED" ||
18
+ code === "fetch failed"
19
+ );
20
+ }
21
+
22
+ function sleep(ms: number) {
23
+ return new Promise((resolve) => setTimeout(resolve, ms));
24
+ }
25
+
26
+ function mergeHeaders(headers?: HeadersInit): Headers {
27
+ const merged = new Headers(SANDBOX_FETCH_HEADERS);
28
+ if (!headers) return merged;
29
+ new Headers(headers).forEach((value, key) => merged.set(key, value));
30
+ return merged;
31
+ }
32
+
33
+ export async function fetchSandboxResource(
34
+ url: string | URL,
35
+ init?: RequestInit,
36
+ ): Promise<Response> {
37
+ let lastError: unknown;
38
+
39
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt += 1) {
40
+ try {
41
+ return await fetch(url, {
42
+ ...init,
43
+ cache: "no-store",
44
+ headers: mergeHeaders(init?.headers),
45
+ });
46
+ } catch (error) {
47
+ lastError = error;
48
+ if (!isRetryableFetchError(error) || attempt === MAX_ATTEMPTS) {
49
+ throw error;
50
+ }
51
+ await sleep(RETRY_DELAY_MS * attempt);
52
+ }
53
+ }
54
+
55
+ throw lastError;
56
+ }
@@ -0,0 +1,406 @@
1
+ import { fetchSandboxResource } from "./fetch-sandbox.js";
2
+ import type {
3
+ XuluxCatalog,
4
+ XuluxCatalogTemplate,
5
+ XuluxCatalogVersion,
6
+ } from "./types.js";
7
+
8
+ // ---------------------------------------------------------------------------
9
+ // Local, catalog-backed template operations for the assistant-ui template MCP tools.
10
+ // These functions never mutate any UI. Preview operations only return URLs.
11
+ // ---------------------------------------------------------------------------
12
+
13
+ export interface ResolvedTemplate {
14
+ template: XuluxCatalogTemplate;
15
+ version: XuluxCatalogVersion | null;
16
+ }
17
+
18
+ /**
19
+ * Resolves a template by id, preserving the app-route semantics:
20
+ * - exact `templateId` match wins
21
+ * - version-specific entry ids (e.g. `webpage-assistant-product-docs`)
22
+ * remain valid inputs
23
+ * - explicit `versionId` wins when provided
24
+ * - otherwise the template's default version is used
25
+ */
26
+ export function resolveTemplate(
27
+ catalog: XuluxCatalog,
28
+ templateId: string,
29
+ versionId?: string | undefined,
30
+ ): ResolvedTemplate | null {
31
+ let template = catalog.templates.find(
32
+ (t) => t.templateId === templateId || t.id === templateId,
33
+ );
34
+ let impliedVersionId: string | undefined;
35
+
36
+ if (!template) {
37
+ // Version-specific entry id such as `webpage-assistant-product-docs`.
38
+ template = catalog.templates.find((t) =>
39
+ t.versions.some((v) => v.entryId === templateId),
40
+ );
41
+ if (template) {
42
+ impliedVersionId = template.versions.find(
43
+ (v) => v.entryId === templateId,
44
+ )?.id;
45
+ }
46
+ }
47
+
48
+ if (!template) return null;
49
+
50
+ const effectiveVersionId =
51
+ versionId ?? impliedVersionId ?? template.versionId;
52
+ const version = effectiveVersionId
53
+ ? (template.versions.find((v) => v.id === effectiveVersionId) ?? null)
54
+ : null;
55
+
56
+ return { template, version };
57
+ }
58
+
59
+ export interface TemplateListItem {
60
+ id: string;
61
+ name: string;
62
+ summary: string;
63
+ assistantPlacement: string;
64
+ features: string[];
65
+ customizable: string[];
66
+ versions: Array<{ id: string; name: string; description: string }>;
67
+ kind: "template" | "example";
68
+ }
69
+
70
+ export function listTemplates(catalog: XuluxCatalog): {
71
+ templates: TemplateListItem[];
72
+ } {
73
+ return {
74
+ templates: catalog.templates.map((t) => ({
75
+ id: t.templateId,
76
+ name: t.name,
77
+ summary: t.summary,
78
+ assistantPlacement: t.assistantPlacement,
79
+ features: t.features,
80
+ customizable: t.customizable,
81
+ versions: t.versions.map((v) => ({
82
+ id: v.id,
83
+ name: v.name,
84
+ description: v.description,
85
+ })),
86
+ kind: t.kind,
87
+ })),
88
+ };
89
+ }
90
+
91
+ async function fetchTemplateContract(
92
+ sandboxBaseUrl: string,
93
+ versionId: string | null,
94
+ ): Promise<Record<string, unknown> | null> {
95
+ try {
96
+ const url = new URL("/api/template/contract", sandboxBaseUrl);
97
+ if (versionId) url.searchParams.set("v", versionId);
98
+ const res = await fetchSandboxResource(url.toString());
99
+ if (!res.ok) return null;
100
+ return (await res.json()) as Record<string, unknown>;
101
+ } catch {
102
+ return null;
103
+ }
104
+ }
105
+
106
+ export interface TemplateDetails {
107
+ id: string;
108
+ name: string;
109
+ selectedVersionId: string | null;
110
+ summary: string;
111
+ assistantPlacement: string;
112
+ configRoots?: Record<string, unknown>;
113
+ rules: {
114
+ required: string[];
115
+ unsupported?: string[];
116
+ };
117
+ tools: {
118
+ builtIn: unknown[];
119
+ customToolSupported: boolean;
120
+ renderers: unknown[];
121
+ };
122
+ exampleConfig: Record<string, unknown> | null;
123
+ previewUrl?: string;
124
+ downloadUrl?: string;
125
+ warnings?: string[];
126
+ exampleConfigNote?: string;
127
+ }
128
+
129
+ export interface TemplateError {
130
+ error: string;
131
+ retryHint?: string;
132
+ }
133
+
134
+ export async function getTemplateDetails(
135
+ catalog: XuluxCatalog,
136
+ input: { templateId: string; versionId?: string | undefined },
137
+ ): Promise<TemplateDetails | TemplateError> {
138
+ const resolved = resolveTemplate(catalog, input.templateId, input.versionId);
139
+ if (!resolved) {
140
+ return {
141
+ error: `Template "${input.templateId}" not found.`,
142
+ retryHint:
143
+ "Call assistantUITemplates and use one of the returned template ids.",
144
+ };
145
+ }
146
+
147
+ const { template, version } = resolved;
148
+
149
+ if (template.kind === "example") {
150
+ return {
151
+ id: template.templateId,
152
+ name: template.name,
153
+ selectedVersionId: null,
154
+ summary: template.summary,
155
+ assistantPlacement: template.assistantPlacement,
156
+ rules: template.rules ?? {
157
+ required: [
158
+ "This entry is a fixed demo and is not schema-customizable.",
159
+ "Preview and download it as-is. Do not pass a config for this entry.",
160
+ ],
161
+ },
162
+ tools: { builtIn: [], customToolSupported: false, renderers: [] },
163
+ exampleConfig: null,
164
+ ...(template.previewUrl ? { previewUrl: template.previewUrl } : {}),
165
+ ...(template.downloadUrl ? { downloadUrl: template.downloadUrl } : {}),
166
+ };
167
+ }
168
+
169
+ const selectedVersionId = version?.id ?? template.versionId;
170
+
171
+ if (!template.configRoots || !template.tools) {
172
+ return {
173
+ error: `No authoring schema found for template "${template.templateId}".`,
174
+ retryHint:
175
+ "The catalog may be in degraded fallback mode. Retry when live catalog access is restored.",
176
+ };
177
+ }
178
+
179
+ let exampleConfig: Record<string, unknown> | null = null;
180
+ let exampleConfigNote: string;
181
+ if (template.sandboxBaseUrl) {
182
+ const contract = await fetchTemplateContract(
183
+ template.sandboxBaseUrl,
184
+ selectedVersionId,
185
+ );
186
+ exampleConfig =
187
+ (contract?.exampleCompleteConfig as Record<string, unknown> | null) ??
188
+ null;
189
+ exampleConfigNote = exampleConfig
190
+ ? `Resolved defaults for version "${selectedVersionId}". Use as a complete working starting point.`
191
+ : "Could not reach the template sandbox to resolve exampleConfig. Use configRoots schemas and defaults to author config manually.";
192
+ } else {
193
+ exampleConfigNote =
194
+ "This template has no sandbox URL in the catalog, so exampleConfig is unavailable. Use configRoots schemas and defaults.";
195
+ }
196
+
197
+ return {
198
+ id: template.templateId,
199
+ name: template.name,
200
+ selectedVersionId,
201
+ summary: template.summary,
202
+ assistantPlacement: template.assistantPlacement,
203
+ configRoots: template.configRoots,
204
+ rules: template.rules ?? { required: [] },
205
+ tools: template.tools,
206
+ exampleConfig,
207
+ exampleConfigNote,
208
+ ...(version?.previewUrl
209
+ ? { previewUrl: version.previewUrl }
210
+ : template.previewUrl
211
+ ? { previewUrl: template.previewUrl }
212
+ : {}),
213
+ ...(version?.downloadUrl
214
+ ? { downloadUrl: version.downloadUrl }
215
+ : template.downloadUrl
216
+ ? { downloadUrl: template.downloadUrl }
217
+ : {}),
218
+ };
219
+ }
220
+
221
+ function toAbsolute(baseUrl: string, url: string): string {
222
+ if (/^https?:\/\//.test(url)) return url;
223
+ return `${baseUrl}${url.startsWith("/") ? "" : "/"}${url}`;
224
+ }
225
+
226
+ function withVersion(url: string, versionId: string | undefined): string {
227
+ if (!versionId) return url;
228
+ const [path, query = ""] = url.split("?");
229
+ const params = new URLSearchParams(query);
230
+ if (!params.has("v")) params.set("v", versionId);
231
+ return `${path}?${params.toString()}`;
232
+ }
233
+
234
+ function hasConfig(config: Record<string, unknown> | undefined): boolean {
235
+ return !!config && Object.keys(config).length > 0;
236
+ }
237
+
238
+ export interface TemplatePreviewResult {
239
+ success: boolean;
240
+ templateId: string;
241
+ versionId: string | null;
242
+ previewUrl?: string;
243
+ downloadUrl?: string;
244
+ title?: string;
245
+ customized?: boolean;
246
+ validationWarnings?: unknown[];
247
+ error?: string;
248
+ retryHint?: string;
249
+ details?: string;
250
+ summary?: string;
251
+ }
252
+
253
+ export async function createTemplatePreview(
254
+ catalog: XuluxCatalog,
255
+ input: {
256
+ templateId: string;
257
+ versionId?: string | undefined;
258
+ config?: Record<string, unknown> | undefined;
259
+ },
260
+ ): Promise<TemplatePreviewResult> {
261
+ const resolved = resolveTemplate(catalog, input.templateId, input.versionId);
262
+ if (!resolved) {
263
+ return {
264
+ success: false,
265
+ templateId: input.templateId,
266
+ versionId: input.versionId ?? null,
267
+ error: `Template "${input.templateId}" not found.`,
268
+ retryHint:
269
+ "Call assistantUITemplates and use one of the returned template ids.",
270
+ };
271
+ }
272
+
273
+ const { template, version } = resolved;
274
+ const tid = template.templateId;
275
+
276
+ if (template.kind === "example") {
277
+ if (hasConfig(input.config)) {
278
+ return {
279
+ success: false,
280
+ templateId: tid,
281
+ versionId: null,
282
+ error: `Template "${tid}" is a fixed demo and does not support config.`,
283
+ retryHint:
284
+ "Call assistantUITemplateDetails for this template. If no configRoots are returned, call assistantUITemplatePreview again without config or choose a configurable hosted template.",
285
+ };
286
+ }
287
+
288
+ if (!template.previewUrl) {
289
+ return {
290
+ success: false,
291
+ templateId: tid,
292
+ versionId: null,
293
+ error: `Fixed demo "${tid}" has no preview URL in the catalog.`,
294
+ retryHint:
295
+ "The catalog may be in degraded fallback mode. Retry when live catalog access is restored.",
296
+ };
297
+ }
298
+
299
+ return {
300
+ success: true,
301
+ templateId: tid,
302
+ versionId: null,
303
+ previewUrl: template.previewUrl,
304
+ ...(template.downloadUrl ? { downloadUrl: template.downloadUrl } : {}),
305
+ title: template.name,
306
+ customized: false,
307
+ summary: `Resolved ${template.name} as a fixed demo. Preview and download URLs point at hosted resources; nothing was opened in any UI.`,
308
+ };
309
+ }
310
+
311
+ const baseUrl = template.sandboxBaseUrl;
312
+ if (!baseUrl) {
313
+ return {
314
+ success: false,
315
+ templateId: tid,
316
+ versionId: version?.id ?? template.versionId,
317
+ error: `Template "${tid}" has no sandbox URL in the catalog.`,
318
+ retryHint:
319
+ "The catalog may be in degraded fallback mode. Retry when live catalog access is restored.",
320
+ };
321
+ }
322
+
323
+ const effectiveVersionId = version?.id ?? template.versionId;
324
+
325
+ if (hasConfig(input.config)) {
326
+ try {
327
+ const sessionUrl = new URL("/api/preview/session", baseUrl);
328
+ if (effectiveVersionId)
329
+ sessionUrl.searchParams.set("v", effectiveVersionId);
330
+ const res = await fetchSandboxResource(sessionUrl.toString(), {
331
+ method: "POST",
332
+ headers: { "Content-Type": "application/json" },
333
+ body: JSON.stringify(input.config),
334
+ });
335
+ if (!res.ok) {
336
+ const details = await res.text();
337
+ return {
338
+ success: false,
339
+ templateId: tid,
340
+ versionId: effectiveVersionId,
341
+ error: `Preview session failed: HTTP ${res.status}`,
342
+ details,
343
+ retryHint:
344
+ "Check validationWarnings for the specific fields that failed. " +
345
+ "Call assistantUITemplateDetails for this template and use configRoots schemas to correct the config. " +
346
+ "Pass only hostUi, assistant, and brandTheme at the top level.",
347
+ };
348
+ }
349
+ const data = (await res.json()) as {
350
+ previewUrl?: string;
351
+ downloadUrl?: string;
352
+ validationWarnings?: unknown[];
353
+ };
354
+ if (!data.previewUrl) {
355
+ return {
356
+ success: false,
357
+ templateId: tid,
358
+ versionId: effectiveVersionId,
359
+ error: "Session endpoint did not return a previewUrl.",
360
+ };
361
+ }
362
+ return {
363
+ success: true,
364
+ templateId: tid,
365
+ versionId: effectiveVersionId,
366
+ previewUrl: toAbsolute(
367
+ baseUrl,
368
+ withVersion(data.previewUrl, effectiveVersionId ?? undefined),
369
+ ),
370
+ downloadUrl: toAbsolute(
371
+ baseUrl,
372
+ withVersion(
373
+ data.downloadUrl ?? "/api/download",
374
+ effectiveVersionId ?? undefined,
375
+ ),
376
+ ),
377
+ title: version?.name ?? template.name,
378
+ customized: true,
379
+ summary: `Created a configured preview session for ${template.name}. URLs point at the hosted sandbox; nothing was opened in any UI.`,
380
+ validationWarnings: data.validationWarnings ?? [],
381
+ };
382
+ } catch (err) {
383
+ return {
384
+ success: false,
385
+ templateId: tid,
386
+ versionId: effectiveVersionId,
387
+ error: err instanceof Error ? err.message : String(err),
388
+ };
389
+ }
390
+ }
391
+
392
+ const previewUrl = version?.previewUrl ?? template.previewUrl;
393
+ const downloadUrl =
394
+ version?.downloadUrl ?? template.downloadUrl ?? `${baseUrl}/api/download`;
395
+
396
+ return {
397
+ success: true,
398
+ templateId: tid,
399
+ versionId: effectiveVersionId,
400
+ previewUrl: previewUrl ?? baseUrl,
401
+ downloadUrl,
402
+ title: version?.name ?? template.name,
403
+ customized: false,
404
+ summary: `Resolved preview and download URLs for ${template.name}. Nothing was opened in any UI.`,
405
+ };
406
+ }
@@ -0,0 +1,60 @@
1
+ // MCP-safe assistant-ui template catalog types. These mirror the payload served by the docs
2
+ // app at GET /api/xulux/mcp-catalog. The MCP package never imports docs-app
3
+ // internals; the endpoint payload is the contract.
4
+
5
+ export const XULUX_MCP_CATALOG_VERSION = 1;
6
+
7
+ export interface XuluxCatalogVersion {
8
+ id: string;
9
+ entryId: string;
10
+ name: string;
11
+ description: string;
12
+ previewUrl: string;
13
+ downloadUrl: string;
14
+ }
15
+
16
+ export interface XuluxCatalogTemplate {
17
+ id: string;
18
+ templateId: string;
19
+ versionId: string | null;
20
+ kind: "template" | "example";
21
+ name: string;
22
+ summary: string;
23
+ assistantPlacement: string;
24
+ features: string[];
25
+ customizable: string[];
26
+ versions: XuluxCatalogVersion[];
27
+ previewUrl?: string;
28
+ downloadUrl?: string;
29
+ sandboxBaseUrl?: string;
30
+ configRoots?: Record<string, unknown>;
31
+ rules?: {
32
+ required: string[];
33
+ unsupported?: string[];
34
+ };
35
+ tools?: {
36
+ builtIn: unknown[];
37
+ customToolSupported: boolean;
38
+ renderers: unknown[];
39
+ };
40
+ }
41
+
42
+ export interface XuluxCatalog {
43
+ version: number;
44
+ generatedAt: string;
45
+ docsOrigin: string;
46
+ templates: XuluxCatalogTemplate[];
47
+ }
48
+
49
+ export interface XuluxCatalogResult {
50
+ catalog: XuluxCatalog;
51
+ /** True when the bundled fallback catalog was used instead of live data. */
52
+ degraded: boolean;
53
+ /** Present when the live fetch failed and the fallback was used. */
54
+ degradedReason?: string;
55
+ }
56
+
57
+ export interface XuluxCatalogError {
58
+ error: string;
59
+ hint?: string;
60
+ }