mountly-mcp 0.0.0 → 3.1.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 (100) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +258 -20
  3. package/dist/artifact/index.d.ts +49 -0
  4. package/dist/artifact/index.js +21 -0
  5. package/dist/artifact/index.js.map +1 -0
  6. package/dist/bridge/iframe-entry.d.ts +1 -0
  7. package/dist/bridge/iframe-entry.js +251 -209
  8. package/dist/bridge/index.d.ts +17 -12
  9. package/dist/bridge/index.js +2 -2
  10. package/dist/build/emit-html.d.ts +30 -0
  11. package/dist/build/emit-meta.d.ts +17 -0
  12. package/dist/build/index.d.ts +32 -47
  13. package/dist/build/index.js +9 -135
  14. package/dist/build/index.js.map +1 -1
  15. package/dist/chunk-33DMFKKD.js +111 -0
  16. package/dist/chunk-33DMFKKD.js.map +1 -0
  17. package/dist/chunk-4HC3OWAQ.js +185 -0
  18. package/dist/chunk-4HC3OWAQ.js.map +1 -0
  19. package/dist/chunk-4WAKOYMK.js +196 -0
  20. package/dist/chunk-4WAKOYMK.js.map +1 -0
  21. package/dist/chunk-5EHNJCMF.js +58 -0
  22. package/dist/chunk-5EHNJCMF.js.map +1 -0
  23. package/dist/chunk-6QHKKFO3.js +30 -0
  24. package/dist/chunk-6QHKKFO3.js.map +1 -0
  25. package/dist/chunk-DKK26TRS.js +199 -0
  26. package/dist/chunk-DKK26TRS.js.map +1 -0
  27. package/dist/{chunk-A64ZILAS.js → chunk-DQLQOMDM.js} +2 -1
  28. package/dist/{chunk-A64ZILAS.js.map → chunk-DQLQOMDM.js.map} +1 -1
  29. package/dist/chunk-KRAS266U.js +260 -0
  30. package/dist/chunk-KRAS266U.js.map +1 -0
  31. package/dist/chunk-NLQDVNZP.js +115 -0
  32. package/dist/chunk-NLQDVNZP.js.map +1 -0
  33. package/dist/chunk-SNWNF2XI.js +171 -0
  34. package/dist/chunk-SNWNF2XI.js.map +1 -0
  35. package/dist/chunk-UECZA34Q.js +52 -0
  36. package/dist/chunk-UECZA34Q.js.map +1 -0
  37. package/dist/chunk-YJA4H6IB.js +13 -0
  38. package/dist/chunk-YJA4H6IB.js.map +1 -0
  39. package/dist/cli.d.ts +2 -0
  40. package/dist/cli.js +286 -0
  41. package/dist/cli.js.map +1 -0
  42. package/dist/connect-server-7L4HGKKL.js +7 -0
  43. package/dist/connect-server-7L4HGKKL.js.map +1 -0
  44. package/dist/dev/connect-server.d.ts +13 -0
  45. package/dist/dev/host-entry.d.ts +13 -0
  46. package/dist/dev/host-entry.js +18330 -0
  47. package/dist/dev/index.d.ts +45 -0
  48. package/dist/dev/index.js +14 -0
  49. package/dist/dev/index.js.map +1 -0
  50. package/dist/dev/sandbox-entry.d.ts +7 -0
  51. package/dist/dev/sandbox-entry.js +80 -0
  52. package/dist/html.d.ts +4 -0
  53. package/dist/index.d.ts +6 -26
  54. package/dist/index.js +8 -3
  55. package/dist/json-render/app.d.ts +22 -0
  56. package/dist/json-render/app.js +10 -0
  57. package/dist/json-render/app.js.map +1 -0
  58. package/dist/json-render/index.d.ts +10 -0
  59. package/dist/json-render/index.js +119 -0
  60. package/dist/json-render/index.js.map +1 -0
  61. package/dist/json-render/mcp.d.ts +49 -0
  62. package/dist/json-render/mcp.js +11 -0
  63. package/dist/json-render/mcp.js.map +1 -0
  64. package/dist/json-render/server.d.ts +72 -0
  65. package/dist/json-render/server.js +98 -0
  66. package/dist/json-render/server.js.map +1 -0
  67. package/dist/json-render/spec-stream.d.ts +26 -0
  68. package/dist/json-render/use-spec-stream.d.ts +26 -0
  69. package/dist/json-render/widget.d.ts +59 -0
  70. package/dist/react/context.d.ts +14 -0
  71. package/dist/react/create-widget.d.ts +11 -0
  72. package/dist/react/hooks.d.ts +46 -0
  73. package/dist/react/index.d.ts +3 -0
  74. package/dist/react/index.js +35 -0
  75. package/dist/react/index.js.map +1 -0
  76. package/dist/sandbox/index.d.ts +16 -0
  77. package/dist/sandbox/index.js +8 -0
  78. package/dist/sandbox/index.js.map +1 -0
  79. package/dist/schema.d.ts +20 -0
  80. package/dist/server/index.d.ts +73 -0
  81. package/dist/server/index.js +235 -0
  82. package/dist/server/index.js.map +1 -0
  83. package/dist/svelte/index.d.ts +35 -0
  84. package/dist/svelte/index.js +9 -0
  85. package/dist/svelte/index.js.map +1 -0
  86. package/dist/testing/index.d.ts +27 -0
  87. package/dist/testing/index.js +312 -0
  88. package/dist/testing/index.js.map +1 -0
  89. package/dist/testing/render-artifact.d.ts +35 -0
  90. package/dist/types.d.ts +55 -0
  91. package/dist/vite/index.d.ts +23 -0
  92. package/dist/vite/index.js +211 -0
  93. package/dist/vite/index.js.map +1 -0
  94. package/dist/vue/index.d.ts +56 -0
  95. package/dist/vue/index.js +89 -0
  96. package/dist/vue/index.js.map +1 -0
  97. package/package.json +166 -20
  98. package/dist/chunk-BUGGLMNA.js +0 -132
  99. package/dist/chunk-BUGGLMNA.js.map +0 -1
  100. package/dist/types-B9JAWm4W.d.ts +0 -62
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/json-render/server.ts"],"sourcesContent":["import { type LanguageModel, streamText } from \"ai\";\nimport {\n type Catalog,\n type Spec,\n type SpecValidationIssues,\n autoFixSpec,\n buildUserPrompt,\n validateSpec,\n} from \"@json-render/core\";\nimport { compileTextStreamToSpecs } from \"./spec-stream.js\";\n\n// The pure stream driver lives in `./spec-stream` (no AI-SDK coupling); re-export\n// it here so it sits next to `streamSpec` for server-side consumers.\nexport { compileTextStreamToSpecs } from \"./spec-stream.js\";\n\nexport interface StreamSpecOptions {\n /** The catalog the spec must conform to (drives the system prompt). */\n catalog: Catalog;\n /** Any AI SDK model — `ollama(...)`, `google(...)`, `groq(...)`, etc. */\n model: LanguageModel;\n /** The natural-language UI request. */\n prompt: string;\n /** Optional runtime state to ground the generation. */\n state?: Record<string, unknown>;\n /** Sampling temperature. Default 0.2 — low for consistent JSONL output. */\n temperature?: number;\n}\n\nexport interface SpecResult {\n /** The compiled, repaired spec. */\n spec: Spec;\n /** Raw model output (JSONL patches). */\n raw: string;\n /** Repairs applied by `autoFixSpec`: props-vs-element fields, and dangling child references. */\n fixes: string[];\n /** Validation issues against the catalog. */\n issues: SpecValidationIssues;\n}\n\n/**\n * The handle {@link streamSpec} returns — one object, two ways to consume it\n * (mirrors the AI SDK's `streamText` result):\n *\n * - **iterate** `partialSpecStream` to watch the UI build itself, element by\n * element, as the model emits patches;\n * - **await** `spec` (or `result` for everything) for the final, repaired spec\n * — no need to touch the stream.\n *\n * The model request starts immediately; the promises resolve when it completes,\n * whether or not you iterate the stream.\n */\nexport interface SpecStream {\n /** Live: each value is the progressively-built spec (the UI assembling itself). */\n partialSpecStream: AsyncIterable<Spec>;\n /** The final repaired + validated result. */\n result: Promise<SpecResult>;\n /** Convenience: the final repaired spec only. */\n spec: Promise<Spec>;\n /** Convenience: the raw JSONL the model emitted. */\n raw: Promise<string>;\n}\n\n/**\n * Generate a catalog-constrained json-render spec from any AI SDK model — the\n * one entry point for turning a prompt into UI.\n *\n * Uses `streamText` + json-render's stream compiler (no JSON-mode or\n * tool-calling, so a small local model can drive it). The returned\n * {@link SpecStream} is both **awaitable** for the final spec and **iterable**\n * for the live build:\n *\n * ```ts\n * import { streamSpec } from \"mountly-mcp/json-render/server\";\n * import { ollama } from \"ai-sdk-ollama\";\n *\n * // Blocking (e.g. an MCP tool returning structuredContent):\n * const { spec } = await streamSpec({ catalog, model: ollama(\"granite4.1:3b\"), prompt }).result;\n *\n * // Live (watch it build):\n * const ui = streamSpec({ catalog, model, prompt });\n * for await (const partial of ui.partialSpecStream) render(partial);\n * const { spec, issues } = await ui.result;\n * ```\n *\n * The promise rejects if the model never produces a spec with a resolvable root.\n */\nexport function streamSpec(options: StreamSpecOptions): SpecStream {\n const { catalog, model, prompt, state, temperature = 0.2 } = options;\n\n // Async queue: the eager pump pushes partial specs; the iterator drains them.\n const queue: Spec[] = [];\n let done = false;\n let failure: unknown = null;\n let wake: (() => void) | null = null;\n const ping = () => {\n wake?.();\n wake = null;\n };\n\n let resolveResult!: (r: SpecResult) => void;\n let rejectResult!: (e: unknown) => void;\n const result = new Promise<SpecResult>((res, rej) => {\n resolveResult = res;\n rejectResult = rej;\n });\n // The error also surfaces through the stream / `result`; don't let the base\n // promise trip an unhandledRejection if a caller reads neither.\n void result.catch(() => {});\n\n // Start the model request immediately (AI SDK semantics), pumping partials\n // into the queue and resolving the promises when it settles.\n void (async () => {\n let raw = \"\";\n try {\n const stream = streamText({\n model,\n system: catalog.prompt({ mode: \"standalone\" }),\n prompt: buildUserPrompt({ prompt, state }),\n temperature,\n });\n const text = (async function* () {\n for await (const delta of stream.textStream) {\n raw += delta;\n yield delta;\n }\n })();\n\n const driver = compileTextStreamToSpecs(text);\n let step = await driver.next();\n while (!step.done) {\n queue.push(step.value);\n ping();\n step = await driver.next();\n }\n const compiled = (step.value ?? {}) as Spec;\n\n // Guard before autoFixSpec: a small model can emit patches that never\n // scaffold a resolvable root. autoFixSpec would throw a cryptic\n // `Object.entries(undefined)`; fail with a clear message instead.\n if (\n typeof compiled.root !== \"string\" ||\n !compiled.elements ||\n !compiled.elements[compiled.root]\n ) {\n throw new Error(\n \"mountly-mcp/json-render: model did not produce a valid spec (no resolvable root element)\",\n );\n }\n\n const { spec, fixes } = autoFixSpec(compiled as unknown as Spec);\n // A final snapshot with repairs applied, so a live renderer settles on the\n // same spec the awaiting caller receives.\n queue.push(spec);\n resolveResult({ spec, raw, fixes, issues: validateSpec(spec) });\n } catch (error) {\n failure = error;\n rejectResult(error);\n } finally {\n done = true;\n ping();\n }\n })();\n\n async function* partialSpecStream(): AsyncGenerator<Spec> {\n let i = 0;\n while (true) {\n // Bounded by queue.length above, so the index is always populated.\n while (i < queue.length) yield queue[i++] as Spec;\n if (done) {\n if (failure) throw failure;\n return;\n }\n await new Promise<void>((res) => {\n wake = res;\n });\n }\n }\n\n return {\n partialSpecStream: { [Symbol.asyncIterator]: partialSpecStream },\n result,\n // Getters so an unread convenience promise can't trip unhandledRejection.\n get spec() {\n return result.then((r) => r.spec);\n },\n get raw() {\n return result.then((r) => r.raw);\n },\n };\n}\n"],"mappings":";;;;;AAAA,SAA6B,kBAAkB;AAC/C;AAAA,EAIE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AA8EA,SAAS,WAAW,SAAwC;AACjE,QAAM,EAAE,SAAS,OAAO,QAAQ,OAAO,cAAc,IAAI,IAAI;AAG7D,QAAM,QAAgB,CAAC;AACvB,MAAI,OAAO;AACX,MAAI,UAAmB;AACvB,MAAI,OAA4B;AAChC,QAAM,OAAO,MAAM;AACjB,WAAO;AACP,WAAO;AAAA,EACT;AAEA,MAAI;AACJ,MAAI;AACJ,QAAM,SAAS,IAAI,QAAoB,CAAC,KAAK,QAAQ;AACnD,oBAAgB;AAChB,mBAAe;AAAA,EACjB,CAAC;AAGD,OAAK,OAAO,MAAM,MAAM;AAAA,EAAC,CAAC;AAI1B,QAAM,YAAY;AAChB,QAAI,MAAM;AACV,QAAI;AACF,YAAM,SAAS,WAAW;AAAA,QACxB;AAAA,QACA,QAAQ,QAAQ,OAAO,EAAE,MAAM,aAAa,CAAC;AAAA,QAC7C,QAAQ,gBAAgB,EAAE,QAAQ,MAAM,CAAC;AAAA,QACzC;AAAA,MACF,CAAC;AACD,YAAM,QAAQ,mBAAmB;AAC/B,yBAAiB,SAAS,OAAO,YAAY;AAC3C,iBAAO;AACP,gBAAM;AAAA,QACR;AAAA,MACF,GAAG;AAEH,YAAM,SAAS,yBAAyB,IAAI;AAC5C,UAAI,OAAO,MAAM,OAAO,KAAK;AAC7B,aAAO,CAAC,KAAK,MAAM;AACjB,cAAM,KAAK,KAAK,KAAK;AACrB,aAAK;AACL,eAAO,MAAM,OAAO,KAAK;AAAA,MAC3B;AACA,YAAM,WAAY,KAAK,SAAS,CAAC;AAKjC,UACE,OAAO,SAAS,SAAS,YACzB,CAAC,SAAS,YACV,CAAC,SAAS,SAAS,SAAS,IAAI,GAChC;AACA,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,YAAM,EAAE,MAAM,MAAM,IAAI,YAAY,QAA2B;AAG/D,YAAM,KAAK,IAAI;AACf,oBAAc,EAAE,MAAM,KAAK,OAAO,QAAQ,aAAa,IAAI,EAAE,CAAC;AAAA,IAChE,SAAS,OAAO;AACd,gBAAU;AACV,mBAAa,KAAK;AAAA,IACpB,UAAE;AACA,aAAO;AACP,WAAK;AAAA,IACP;AAAA,EACF,GAAG;AAEH,kBAAgB,oBAA0C;AACxD,QAAI,IAAI;AACR,WAAO,MAAM;AAEX,aAAO,IAAI,MAAM,OAAQ,OAAM,MAAM,GAAG;AACxC,UAAI,MAAM;AACR,YAAI,QAAS,OAAM;AACnB;AAAA,MACF;AACA,YAAM,IAAI,QAAc,CAAC,QAAQ;AAC/B,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AAAA,IACL,mBAAmB,EAAE,CAAC,OAAO,aAAa,GAAG,kBAAkB;AAAA,IAC/D;AAAA;AAAA,IAEA,IAAI,OAAO;AACT,aAAO,OAAO,KAAK,CAAC,MAAM,EAAE,IAAI;AAAA,IAClC;AAAA,IACA,IAAI,MAAM;AACR,aAAO,OAAO,KAAK,CAAC,MAAM,EAAE,GAAG;AAAA,IACjC;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,26 @@
1
+ import type { Spec } from "@json-render/core";
2
+ export { compileSpecStream, createJsonRenderTransform, createSpecStreamCompiler, parseSpecStreamLine, } from "@json-render/core";
3
+ /**
4
+ * Drive json-render's stream compiler from a raw text stream of JSONL patches,
5
+ * yielding the progressively-built spec after each complete line. This is the
6
+ * live "model token → patch → spec" path, decoupled from any AI SDK so it can
7
+ * drive a real model (see `streamSpec` in `mountly-mcp/json-render/server`), replay
8
+ * a saved transcript, bridge a custom transport, or be unit-tested with a
9
+ * hand-written stream. Buffers partial lines across chunk boundaries. The
10
+ * generator's *return* value is the final compiled spec (before `autoFixSpec`).
11
+ *
12
+ * ```ts
13
+ * for await (const partial of compileTextStreamToSpecs(result.textStream)) {
14
+ * render(partial); // each yield is one more element built
15
+ * }
16
+ * ```
17
+ */
18
+ export declare function compileTextStreamToSpecs(textStream: AsyncIterable<string>): AsyncGenerator<Spec, Spec, void>;
19
+ /**
20
+ * Turn a finished spec into json-render's streaming patch format — ordered
21
+ * RFC-6902 JSONL lines (`/elements` scaffold, `/state`, `/root`, then each
22
+ * element parent-before-child). Feed these to a `createSpecStreamCompiler` to
23
+ * replay the build progressively, so a known spec streams in the same way a
24
+ * live model would emit it. Pure spec work — no AI SDK dependency.
25
+ */
26
+ export declare function specToPatchLines(spec: Spec): string[];
@@ -0,0 +1,26 @@
1
+ import type { Spec } from "@json-render/core";
2
+ export interface UseSpecStreamOptions {
3
+ /** Delay between patches, ms. Default 90. Set 0 to render instantly. */
4
+ intervalMs?: number;
5
+ }
6
+ export interface SpecStreamState {
7
+ /** The progressively-built spec (partial while streaming, full when done). */
8
+ spec: Spec | null;
9
+ /** Convenience: `spec?.state`, to pass straight to the renderer. */
10
+ state: Record<string, unknown> | undefined;
11
+ /** True while patches are still being applied. */
12
+ loading: boolean;
13
+ }
14
+ /**
15
+ * Replay a finished spec as a live json-render stream — and own all the
16
+ * ceremony. Give it the target spec; it scaffolds a `createSpecStreamCompiler`,
17
+ * replays the patches with a delay, exposes the partial spec + `loading` after
18
+ * each step, and cancels cleanly when the target changes (so a self-driving UI
19
+ * that swaps specs mid-stream just works). Re-streams whenever `target` changes.
20
+ *
21
+ * ```tsx
22
+ * const { spec, state, loading } = useSpecStream(specs[view]);
23
+ * return <Renderer spec={spec} state={state} loading={loading} onAction={...} />;
24
+ * ```
25
+ */
26
+ export declare function useSpecStream(target: Spec | null | undefined, options?: UseSpecStreamOptions): SpecStreamState;
@@ -0,0 +1,59 @@
1
+ import { type ComponentMap } from "@json-render/react";
2
+ import type { Catalog, SchemaDefinition } from "@json-render/core";
3
+ import type { App } from "@modelcontextprotocol/ext-apps";
4
+ import type { AdapterOptions, WidgetModule } from "mountly/adapter";
5
+ /** The catalog-data constraint createRenderer infers against. */
6
+ type CatalogData = {
7
+ components: Record<string, {
8
+ props: unknown;
9
+ }>;
10
+ };
11
+ /**
12
+ * Route a generated UI's actions back to the MCP host. Called with the action
13
+ * name, its (resolved) params, and the view-side `App` handle.
14
+ */
15
+ export type ActionRouter = (name: string, params: Record<string, unknown> | undefined, mcp: App) => void;
16
+ /**
17
+ * Default router: an `ask` action with a string `prompt` param sends a
18
+ * follow-up user turn to the model via `App.sendMessage`. This is the agent
19
+ * loop json-render alone cannot do — it has no concept of an MCP host.
20
+ */
21
+ export declare const defaultActionRouter: ActionRouter;
22
+ /**
23
+ * Identity helper that types a components map against a catalog (so it can be
24
+ * defined once and reused by both `createGenerativeWidget` and `createRenderer`)
25
+ * without losing per-component prop inference.
26
+ *
27
+ * ```ts
28
+ * export const components = defineComponents(catalog, {
29
+ * Metric: ({ element }) => <span>{element.props.value}</span>,
30
+ * });
31
+ * ```
32
+ */
33
+ export declare function defineComponents<TDef extends SchemaDefinition, TCatalog extends CatalogData>(_catalog: Catalog<TDef, TCatalog>, components: ComponentMap<TCatalog["components"]>): ComponentMap<TCatalog["components"]>;
34
+ export interface GenerativeWidgetOptions<TDef extends SchemaDefinition, TCatalog extends CatalogData> extends AdapterOptions {
35
+ /** The json-render catalog — the vocabulary the agent may compose. */
36
+ catalog: Catalog<TDef, TCatalog>;
37
+ /** Native component implementations for each catalog type. */
38
+ components: ComponentMap<TCatalog["components"]>;
39
+ /**
40
+ * How a generated UI's actions reach the agent. Defaults to
41
+ * {@link defaultActionRouter} (`ask` → `App.sendMessage`).
42
+ */
43
+ onAction?: ActionRouter;
44
+ }
45
+ /**
46
+ * Turn a json-render catalog + components into a mountly MCP Apps widget.
47
+ *
48
+ * The returned widget reads the spec from the tool result, renders it natively
49
+ * (resolving `$state` bindings from `spec.state`), and routes the rendered UI's
50
+ * actions back to the MCP host. One call replaces the manual
51
+ * catalog→registry→renderer→state→action-bridge wiring.
52
+ *
53
+ * ```ts
54
+ * const widget = createGenerativeWidget({ catalog, components, styles, shadow: true });
55
+ * (globalThis as { __mountlyMcpWidget__?: unknown }).__mountlyMcpWidget__ = widget;
56
+ * ```
57
+ */
58
+ export declare function createGenerativeWidget<TDef extends SchemaDefinition, TCatalog extends CatalogData>(options: GenerativeWidgetOptions<TDef, TCatalog>): WidgetModule;
59
+ export {};
@@ -0,0 +1,14 @@
1
+ import type { App, McpUiHostContext, McpUiToolInputNotification, McpUiToolInputPartialNotification, McpUiToolResultNotification } from "@modelcontextprotocol/ext-apps";
2
+ /**
3
+ * Shared per-widget context. The bridge populates this on each mount/update,
4
+ * so consumers can synchronously read the initial values (no event subscription
5
+ * race) and the React tree re-renders when the bridge calls `update()`.
6
+ */
7
+ export interface McpContextValue {
8
+ app: App;
9
+ toolInput?: McpUiToolInputNotification["params"];
10
+ toolInputPartial?: McpUiToolInputPartialNotification["params"];
11
+ toolResult?: McpUiToolResultNotification["params"];
12
+ hostContext?: McpUiHostContext;
13
+ }
14
+ export declare const McpContext: import("react").Context<McpContextValue | null>;
@@ -0,0 +1,11 @@
1
+ import { type ComponentType } from "react";
2
+ import type { AdapterOptions, WidgetModule } from "mountly/adapter";
3
+ /**
4
+ * Wraps a React component as a mountly `WidgetModule` driven by the
5
+ * mountly-mcp bridge. The bridge passes spec-defined props
6
+ * (`mcp` / `toolInput` / `toolResult` / `hostContext`) on each `mount` /
7
+ * `update`. The wrapper consumes those bridge props and exposes them through
8
+ * `useMcpHost()` and the other hooks from `mountly-mcp/react`; the wrapped
9
+ * component's own props remain unchanged.
10
+ */
11
+ export declare function createMcpWidget<P extends object>(Component: ComponentType<P>, options?: AdapterOptions): WidgetModule;
@@ -0,0 +1,46 @@
1
+ import { useAutoResize as useAutoResizeBase } from "@modelcontextprotocol/ext-apps/react";
2
+ import type { App, McpUiDisplayMode, McpUiHostContext, McpUiUpdateModelContextRequest } from "@modelcontextprotocol/ext-apps";
3
+ /** The view-side ext-apps App. Use this to call server tools, send messages, open links, etc. */
4
+ export declare function useMcpHost(): App;
5
+ /** Complete notification params; read the tool arguments from `.arguments`. */
6
+ export declare function useToolInput<T = unknown>(): T | undefined;
7
+ /**
8
+ * Streaming tool arguments (`ui/notifications/tool-input-partial`), present only
9
+ * when the bridge runs with `streamToolInput`. Best-effort recovered JSON —
10
+ * fields may be missing or change between notifications, so render loading
11
+ * states from it, never critical operations. Undefined once the complete
12
+ * {@link useToolInput} arrives.
13
+ */
14
+ export declare function useToolInputPartial<T = unknown>(): Partial<T> | undefined;
15
+ /** Complete MCP tool result; structured data is available at `.structuredContent`. */
16
+ export declare function useToolResult<T = unknown>(): T | undefined;
17
+ /** Latest host context snapshot (theme, styles, displayMode, container dimensions, etc.). */
18
+ export declare function useHostContext(): McpUiHostContext | undefined;
19
+ /** Current display mode reported by the host. */
20
+ export declare function useDisplayMode(): McpUiDisplayMode | undefined;
21
+ /**
22
+ * Request a display mode change, resolving to the mode the host actually
23
+ * applied — which the spec allows to differ from the one asked for.
24
+ *
25
+ * Modes the host didn't list in `availableDisplayModes` are never requested
26
+ * (the spec requires views to check first); the current mode comes back
27
+ * unchanged instead.
28
+ */
29
+ export declare function useRequestDisplayMode(): (mode: McpUiDisplayMode) => Promise<McpUiDisplayMode | undefined>;
30
+ /** Apply host CSS variables (and react to host-context changes). */
31
+ export declare function useHostStyleVariables(): void;
32
+ /** Apply host-provided `@font-face`/`@import` rules. */
33
+ export declare function useHostFonts(): void;
34
+ /** One-shot helper: apply both host style variables and host fonts. */
35
+ export declare function useHostStyles(): void;
36
+ /** Track the document theme reactively. */
37
+ export declare function useDocumentTheme(): import("@modelcontextprotocol/ext-apps").McpUiTheme;
38
+ /**
39
+ * Return a function that calls `app.updateModelContext(params)`.
40
+ *
41
+ * Context updates are available to the model in future turns without
42
+ * triggering an immediate response. Only the last update is kept.
43
+ */
44
+ export declare function useUpdateModelContext(): (params: McpUiUpdateModelContextRequest["params"]) => Promise<void>;
45
+ /** Manually control auto-resize (rarely needed — the bridge calls setupSizeChangedNotifications by default). */
46
+ export declare function useAutoResize(...args: Parameters<typeof useAutoResizeBase>): void;
@@ -0,0 +1,3 @@
1
+ export * from "./context.js";
2
+ export * from "./hooks.js";
3
+ export * from "./create-widget.js";
@@ -0,0 +1,35 @@
1
+ import {
2
+ McpContext,
3
+ createMcpWidget,
4
+ useAutoResize,
5
+ useDisplayMode,
6
+ useDocumentTheme,
7
+ useHostContext,
8
+ useHostFonts,
9
+ useHostStyleVariables,
10
+ useHostStyles,
11
+ useMcpHost,
12
+ useRequestDisplayMode,
13
+ useToolInput,
14
+ useToolInputPartial,
15
+ useToolResult,
16
+ useUpdateModelContext
17
+ } from "../chunk-NLQDVNZP.js";
18
+ export {
19
+ McpContext,
20
+ createMcpWidget,
21
+ useAutoResize,
22
+ useDisplayMode,
23
+ useDocumentTheme,
24
+ useHostContext,
25
+ useHostFonts,
26
+ useHostStyleVariables,
27
+ useHostStyles,
28
+ useMcpHost,
29
+ useRequestDisplayMode,
30
+ useToolInput,
31
+ useToolInputPartial,
32
+ useToolResult,
33
+ useUpdateModelContext
34
+ };
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,16 @@
1
+ export interface SandboxProxyHtmlOptions {
2
+ /**
3
+ * The compiled sandbox entry IIFE to inline. When omitted the page emits an
4
+ * empty `<script>` — the caller must arrange for the runtime to load another
5
+ * way (e.g. a `<script src>` tag).
6
+ */
7
+ sandboxEntryScript?: string;
8
+ }
9
+ /**
10
+ * Return the complete sandbox proxy HTML document for the given host origin.
11
+ *
12
+ * This is the security boundary described in MCP Apps §8.4: it runs on an
13
+ * origin distinct from the host, receives the view HTML over postMessage,
14
+ * injects the CSP the sidecar declared, and relays messages.
15
+ */
16
+ export declare function sandboxProxyHtml(hostOrigin: string, options?: SandboxProxyHtmlOptions): string;
@@ -0,0 +1,8 @@
1
+ import {
2
+ sandboxProxyHtml
3
+ } from "../chunk-6QHKKFO3.js";
4
+ import "../chunk-YJA4H6IB.js";
5
+ export {
6
+ sandboxProxyHtml
7
+ };
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Spec constants — re-exported from the canonical SDK so they cannot drift.
3
+ * See `@modelcontextprotocol/ext-apps` for the source of truth.
4
+ */
5
+ export { LATEST_PROTOCOL_VERSION as MCP_APPS_PROTOCOL_VERSION, RESOURCE_MIME_TYPE as MCP_APPS_MIME, RESOURCE_URI_META_KEY, } from "@modelcontextprotocol/ext-apps";
6
+ export { EXTENSION_ID } from "@modelcontextprotocol/ext-apps/server";
7
+ export declare const MCP_APPS_URI_SCHEME: "ui://";
8
+ /**
9
+ * Implementation-specific error codes for mountly-mcp's host/bridge layer.
10
+ * The spec doesn't standardize these; they appear in `notifications/message`
11
+ * params so hosts can categorize widget failures.
12
+ */
13
+ export declare const MCP_ERROR_CODES: {
14
+ readonly INITIALIZE_FAILED: "mountly-mcp/initialize-failed";
15
+ readonly INITIALIZE_TIMEOUT: "mountly-mcp/initialize-timeout";
16
+ readonly TOOL_CALL_FAILED: "mountly-mcp/tool-call-failed";
17
+ readonly WIDGET_MOUNT_THREW: "mountly-mcp/widget-mount-threw";
18
+ readonly INVALID_ORIGIN: "mountly-mcp/invalid-origin";
19
+ };
20
+ export type McpErrorCode = (typeof MCP_ERROR_CODES)[keyof typeof MCP_ERROR_CODES];
@@ -0,0 +1,73 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
4
+ import { EXTENSION_ID, RESOURCE_MIME_TYPE, type McpUiAppToolConfig } from "@modelcontextprotocol/ext-apps/server";
5
+ import type { McpUiToolVisibility } from "@modelcontextprotocol/ext-apps";
6
+ import { type McpAppArtifact } from "../artifact/index.js";
7
+ export interface McpWidgetToolResult {
8
+ structuredContent?: unknown;
9
+ content?: ReadonlyArray<{
10
+ type: string;
11
+ [key: string]: unknown;
12
+ }>;
13
+ isError?: boolean;
14
+ }
15
+ /** Complete official tool configuration, plus JSON Schema support at Mountly's seam. */
16
+ export interface McpAppToolConfig extends Omit<McpUiAppToolConfig, "_meta" | "inputSchema" | "outputSchema"> {
17
+ inputSchema?: object;
18
+ outputSchema?: object;
19
+ /** Additional metadata is preserved; Mountly only merges `_meta.ui`. */
20
+ _meta?: Record<string, unknown>;
21
+ }
22
+ /** A View is independent of the tools that reference it. */
23
+ export interface McpAppViewRegistration {
24
+ /** Built artifact or the path to its self-contained HTML. */
25
+ artifact: McpAppArtifact | string;
26
+ /** Optional assertion used by migration adapters; artifacts already own identity. */
27
+ uri?: string;
28
+ }
29
+ /** A tool links to a separately declared View through the protocol resource URI. */
30
+ export interface McpAppToolRegistration<Args = unknown> {
31
+ name: string;
32
+ resourceUri: string;
33
+ config?: McpAppToolConfig;
34
+ /** Convenience override for `_meta.ui.visibility`. */
35
+ visibility?: McpUiToolVisibility | ReadonlyArray<McpUiToolVisibility>;
36
+ handler: (args: Args, extra?: unknown) => Promise<McpWidgetToolResult>;
37
+ }
38
+ export interface RegisterMcpAppsOptions {
39
+ views: ReadonlyArray<McpAppViewRegistration>;
40
+ tools?: ReadonlyArray<McpAppToolRegistration>;
41
+ }
42
+ /**
43
+ * `createMcpAppServer` declares Views and tools exactly as `registerMcpApps`
44
+ * does; it only owns the `McpServer` for you. One vocabulary, two entry points.
45
+ */
46
+ export interface CreateMcpAppServerOptions extends RegisterMcpAppsOptions {
47
+ name: string;
48
+ version: string;
49
+ }
50
+ export interface RegisteredMcpApps {
51
+ artifacts: ReadonlyArray<McpAppArtifact>;
52
+ /** Remove every resource and tool installed by this registration. */
53
+ remove(): void;
54
+ }
55
+ /**
56
+ * Install Mountly Views and their independently declared tools into an existing,
57
+ * unconnected MCP server. The caller retains ownership of identity, auth,
58
+ * transports, prompts, ordinary tools, and deployment.
59
+ */
60
+ export declare function registerMcpApps(server: McpServer, options: RegisterMcpAppsOptions): Promise<RegisteredMcpApps>;
61
+ export interface RunningMcpAppServer {
62
+ connectInProcess(options?: {
63
+ ui?: boolean;
64
+ }): Promise<{
65
+ client: Client;
66
+ }>;
67
+ listen(transport: Transport): Promise<void>;
68
+ close(): Promise<void>;
69
+ }
70
+ /** Convenience adapter that retains the original one-call server experience. */
71
+ export declare function createMcpAppServer(options: CreateMcpAppServerOptions): RunningMcpAppServer;
72
+ export declare function serveStdio(options: CreateMcpAppServerOptions): Promise<RunningMcpAppServer>;
73
+ export { EXTENSION_ID, RESOURCE_MIME_TYPE };
@@ -0,0 +1,235 @@
1
+ import {
2
+ readMcpAppArtifact
3
+ } from "../chunk-DKK26TRS.js";
4
+
5
+ // src/server/index.ts
6
+ import { readFile } from "fs/promises";
7
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
8
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
+ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
10
+ import {
11
+ EXTENSION_ID,
12
+ RESOURCE_MIME_TYPE,
13
+ getUiCapability,
14
+ registerAppResource,
15
+ registerAppTool
16
+ } from "@modelcontextprotocol/ext-apps/server";
17
+ import { z } from "zod";
18
+ function normalizeVisibility(visibility) {
19
+ if (visibility === void 0) return void 0;
20
+ return typeof visibility === "string" ? [visibility] : [...visibility];
21
+ }
22
+ function isSchemaLibraryValue(value) {
23
+ return typeof value === "object" && value !== null && ("~standard" in value || "_def" in value);
24
+ }
25
+ function normalizeToolSchema(schema) {
26
+ if (!schema || Object.keys(schema).length === 0) return schema;
27
+ const candidate = schema;
28
+ if (isSchemaLibraryValue(candidate) || Object.values(candidate).some(isSchemaLibraryValue)) {
29
+ return schema;
30
+ }
31
+ const jsonSchemaKeys = [
32
+ "$schema",
33
+ "$ref",
34
+ "$defs",
35
+ "type",
36
+ "properties",
37
+ "required",
38
+ "additionalProperties",
39
+ "allOf",
40
+ "anyOf",
41
+ "oneOf"
42
+ ];
43
+ return jsonSchemaKeys.some((key) => key in candidate) ? z.fromJSONSchema(schema) : schema;
44
+ }
45
+ function withTextFallback(result) {
46
+ if (result.content && result.content.length > 0) return result;
47
+ if (result.structuredContent === void 0) return result;
48
+ return {
49
+ ...result,
50
+ content: [{ type: "text", text: JSON.stringify(result.structuredContent) }]
51
+ };
52
+ }
53
+ function withoutUiMetadata(meta) {
54
+ const copy = { ...meta };
55
+ delete copy.ui;
56
+ delete copy["ui/resourceUri"];
57
+ return copy;
58
+ }
59
+ function mergedToolMetadata(tool) {
60
+ const base = { ...tool.config?._meta ?? {} };
61
+ const existingUi = typeof base.ui === "object" && base.ui !== null ? base.ui : {};
62
+ const visibility = normalizeVisibility(tool.visibility) ?? [
63
+ ...existingUi.visibility ?? ["model", "app"]
64
+ ];
65
+ const meta = {
66
+ ...base,
67
+ ui: { ...existingUi, resourceUri: tool.resourceUri, visibility }
68
+ };
69
+ return { meta, withoutUi: withoutUiMetadata(base), visibility };
70
+ }
71
+ async function loadViews(registrations) {
72
+ const loaded = [];
73
+ const names = /* @__PURE__ */ new Set();
74
+ const uris = /* @__PURE__ */ new Set();
75
+ for (const registration of registrations) {
76
+ if (registration.uri !== void 0 && !registration.uri.startsWith("ui://")) {
77
+ throw new Error(
78
+ `mountly-mcp/server: registration uri must use the 'ui://' scheme (received '${registration.uri}')`
79
+ );
80
+ }
81
+ const supplied = typeof registration.artifact === "string" ? void 0 : registration.artifact;
82
+ const artifact = await readMcpAppArtifact(
83
+ typeof registration.artifact === "string" ? registration.artifact : registration.artifact.htmlPath,
84
+ registration.uri ?? supplied?.uri
85
+ );
86
+ if (supplied && supplied.name !== artifact.name) {
87
+ throw new Error(
88
+ `mountly-mcp: supplied artifact name '${supplied.name}' does not match file artifact name '${artifact.name}'`
89
+ );
90
+ }
91
+ if (names.has(artifact.name))
92
+ throw new Error(`mountly-mcp: duplicate app name '${artifact.name}'`);
93
+ if (uris.has(artifact.uri)) throw new Error(`mountly-mcp: duplicate app uri '${artifact.uri}'`);
94
+ names.add(artifact.name);
95
+ uris.add(artifact.uri);
96
+ loaded.push({ artifact, html: await readFile(artifact.htmlPath, "utf8") });
97
+ }
98
+ return loaded;
99
+ }
100
+ async function registerMcpApps(server, options) {
101
+ if (server.isConnected()) {
102
+ throw new Error("mountly-mcp: registerMcpApps() must run before the server connects");
103
+ }
104
+ if (options.views.length === 0) {
105
+ throw new Error("mountly-mcp: registerMcpApps() requires at least one View");
106
+ }
107
+ const views = await loadViews(options.views);
108
+ const byUri = new Map(views.map((view) => [view.artifact.uri, view]));
109
+ for (const tool of options.tools ?? []) {
110
+ if (!byUri.has(tool.resourceUri)) {
111
+ throw new Error(
112
+ `mountly-mcp: tool '${tool.name}' references undeclared View '${tool.resourceUri}'`
113
+ );
114
+ }
115
+ }
116
+ server.server.registerCapabilities({
117
+ extensions: { [EXTENSION_ID]: { mimeTypes: [RESOURCE_MIME_TYPE] } }
118
+ });
119
+ const resources = views.map(
120
+ ({ artifact, html }) => registerAppResource(
121
+ server,
122
+ artifact.name,
123
+ artifact.uri,
124
+ {
125
+ mimeType: artifact.declaration.mimeType,
126
+ description: artifact.declaration.description,
127
+ _meta: { ui: artifact.declaration._meta.ui }
128
+ },
129
+ async () => ({
130
+ contents: [
131
+ {
132
+ uri: artifact.uri,
133
+ mimeType: artifact.declaration.mimeType,
134
+ text: html,
135
+ _meta: { ui: artifact.declaration._meta.ui }
136
+ }
137
+ ]
138
+ })
139
+ )
140
+ );
141
+ const tools = (options.tools ?? []).map((registration) => {
142
+ const { meta, withoutUi, visibility } = mergedToolMetadata(registration);
143
+ const config = registration.config ?? {};
144
+ const registered = registerAppTool(
145
+ server,
146
+ registration.name,
147
+ {
148
+ ...config,
149
+ inputSchema: normalizeToolSchema(config.inputSchema),
150
+ outputSchema: normalizeToolSchema(config.outputSchema),
151
+ _meta: meta
152
+ },
153
+ (async (args, extra) => withTextFallback(await registration.handler(args, extra)))
154
+ );
155
+ return { registration, registered, metadataWithoutUi: withoutUi, visibility };
156
+ });
157
+ const previousInitialized = server.server.oninitialized;
158
+ const handleInitialized = () => {
159
+ previousInitialized?.();
160
+ const ui = getUiCapability(server.server.getClientCapabilities());
161
+ if (ui?.mimeTypes?.includes(RESOURCE_MIME_TYPE)) return;
162
+ for (const resource of resources) resource.remove();
163
+ for (const tool of tools) {
164
+ if (tool.visibility.includes("model")) {
165
+ tool.registered.update({ _meta: tool.metadataWithoutUi });
166
+ } else {
167
+ tool.registered.remove();
168
+ }
169
+ }
170
+ };
171
+ server.server.oninitialized = handleInitialized;
172
+ return {
173
+ artifacts: views.map((view) => view.artifact),
174
+ remove() {
175
+ for (const resource of resources) resource.remove();
176
+ for (const tool of tools) tool.registered.remove();
177
+ if (server.server.oninitialized === handleInitialized) {
178
+ server.server.oninitialized = previousInitialized;
179
+ }
180
+ }
181
+ };
182
+ }
183
+ function createConfiguredServer(options) {
184
+ return new McpServer(
185
+ { name: options.name, version: options.version },
186
+ { capabilities: { resources: {}, tools: {} } }
187
+ );
188
+ }
189
+ function createMcpAppServer(options) {
190
+ let server;
191
+ async function build() {
192
+ const next = createConfiguredServer(options);
193
+ await registerMcpApps(next, { views: options.views, tools: options.tools });
194
+ server = next;
195
+ return next;
196
+ }
197
+ return {
198
+ async connectInProcess(connectOptions) {
199
+ const next = await build();
200
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
201
+ await next.connect(serverTransport);
202
+ const client = new Client(
203
+ { name: `${options.name}-inprocess-client`, version: options.version },
204
+ connectOptions?.ui ?? true ? {
205
+ capabilities: {
206
+ extensions: { [EXTENSION_ID]: { mimeTypes: [RESOURCE_MIME_TYPE] } }
207
+ }
208
+ } : void 0
209
+ );
210
+ await client.connect(clientTransport);
211
+ return { client };
212
+ },
213
+ async listen(transport) {
214
+ const next = await build();
215
+ await next.connect(transport);
216
+ },
217
+ async close() {
218
+ await server?.close();
219
+ }
220
+ };
221
+ }
222
+ async function serveStdio(options) {
223
+ const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
224
+ const server = createMcpAppServer(options);
225
+ await server.listen(new StdioServerTransport());
226
+ return server;
227
+ }
228
+ export {
229
+ EXTENSION_ID,
230
+ RESOURCE_MIME_TYPE,
231
+ createMcpAppServer,
232
+ registerMcpApps,
233
+ serveStdio
234
+ };
235
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server/index.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { InMemoryTransport } from \"@modelcontextprotocol/sdk/inMemory.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport {\n EXTENSION_ID,\n RESOURCE_MIME_TYPE,\n getUiCapability,\n registerAppResource,\n registerAppTool,\n type McpUiAppToolConfig,\n} from \"@modelcontextprotocol/ext-apps/server\";\nimport type { McpUiToolMeta, McpUiToolVisibility } from \"@modelcontextprotocol/ext-apps\";\nimport { z } from \"zod\";\nimport { readMcpAppArtifact, type McpAppArtifact } from \"../artifact/index.js\";\n\nexport interface McpWidgetToolResult {\n structuredContent?: unknown;\n content?: ReadonlyArray<{ type: string; [key: string]: unknown }>;\n isError?: boolean;\n}\n\n/** Complete official tool configuration, plus JSON Schema support at Mountly's seam. */\nexport interface McpAppToolConfig extends Omit<\n McpUiAppToolConfig,\n \"_meta\" | \"inputSchema\" | \"outputSchema\"\n> {\n inputSchema?: object;\n outputSchema?: object;\n /** Additional metadata is preserved; Mountly only merges `_meta.ui`. */\n _meta?: Record<string, unknown>;\n}\n\n/** A View is independent of the tools that reference it. */\nexport interface McpAppViewRegistration {\n /** Built artifact or the path to its self-contained HTML. */\n artifact: McpAppArtifact | string;\n /** Optional assertion used by migration adapters; artifacts already own identity. */\n uri?: string;\n}\n\n/** A tool links to a separately declared View through the protocol resource URI. */\nexport interface McpAppToolRegistration<Args = unknown> {\n name: string;\n resourceUri: string;\n config?: McpAppToolConfig;\n /** Convenience override for `_meta.ui.visibility`. */\n visibility?: McpUiToolVisibility | ReadonlyArray<McpUiToolVisibility>;\n handler: (args: Args, extra?: unknown) => Promise<McpWidgetToolResult>;\n}\n\nexport interface RegisterMcpAppsOptions {\n views: ReadonlyArray<McpAppViewRegistration>;\n tools?: ReadonlyArray<McpAppToolRegistration>;\n}\n\n/**\n * `createMcpAppServer` declares Views and tools exactly as `registerMcpApps`\n * does; it only owns the `McpServer` for you. One vocabulary, two entry points.\n */\nexport interface CreateMcpAppServerOptions extends RegisterMcpAppsOptions {\n name: string;\n version: string;\n}\n\ninterface LoadedView {\n artifact: McpAppArtifact;\n html: string;\n}\n\ninterface InstalledTool {\n registration: McpAppToolRegistration;\n registered: { remove(): void; update(u: { _meta?: object }): void };\n metadataWithoutUi: Record<string, unknown>;\n visibility: ReadonlyArray<McpUiToolVisibility>;\n}\n\nexport interface RegisteredMcpApps {\n artifacts: ReadonlyArray<McpAppArtifact>;\n /** Remove every resource and tool installed by this registration. */\n remove(): void;\n}\n\nfunction normalizeVisibility(\n visibility: McpAppToolRegistration[\"visibility\"],\n): McpUiToolVisibility[] | undefined {\n if (visibility === undefined) return undefined;\n return typeof visibility === \"string\" ? [visibility] : [...visibility];\n}\n\nfunction isSchemaLibraryValue(value: unknown): boolean {\n return typeof value === \"object\" && value !== null && (\"~standard\" in value || \"_def\" in value);\n}\n\n/** Convert documented JSON Schema while preserving Zod and Standard Schema values. */\nfunction normalizeToolSchema(schema: object | undefined): object | undefined {\n if (!schema || Object.keys(schema).length === 0) return schema;\n const candidate = schema as Record<string, unknown>;\n if (isSchemaLibraryValue(candidate) || Object.values(candidate).some(isSchemaLibraryValue)) {\n return schema;\n }\n const jsonSchemaKeys = [\n \"$schema\",\n \"$ref\",\n \"$defs\",\n \"type\",\n \"properties\",\n \"required\",\n \"additionalProperties\",\n \"allOf\",\n \"anyOf\",\n \"oneOf\",\n ];\n return jsonSchemaKeys.some((key) => key in candidate)\n ? z.fromJSONSchema(schema as never)\n : schema;\n}\n\nfunction withTextFallback(result: McpWidgetToolResult): McpWidgetToolResult {\n if (result.content && result.content.length > 0) return result;\n if (result.structuredContent === undefined) return result;\n return {\n ...result,\n content: [{ type: \"text\", text: JSON.stringify(result.structuredContent) }],\n };\n}\n\nfunction withoutUiMetadata(meta: Record<string, unknown>): Record<string, unknown> {\n const copy = { ...meta };\n delete copy.ui;\n delete copy[\"ui/resourceUri\"];\n return copy;\n}\n\nfunction mergedToolMetadata(tool: McpAppToolRegistration): {\n meta: Record<string, unknown> & { ui: McpUiToolMeta };\n withoutUi: Record<string, unknown>;\n visibility: McpUiToolVisibility[];\n} {\n const base = { ...(tool.config?._meta ?? {}) };\n const existingUi =\n typeof base.ui === \"object\" && base.ui !== null ? (base.ui as Partial<McpUiToolMeta>) : {};\n const visibility = normalizeVisibility(tool.visibility) ?? [\n ...(existingUi.visibility ?? [\"model\", \"app\"]),\n ];\n const meta = {\n ...base,\n ui: { ...existingUi, resourceUri: tool.resourceUri, visibility },\n };\n return { meta, withoutUi: withoutUiMetadata(base), visibility };\n}\n\nasync function loadViews(\n registrations: ReadonlyArray<McpAppViewRegistration>,\n): Promise<ReadonlyArray<LoadedView>> {\n const loaded: LoadedView[] = [];\n const names = new Set<string>();\n const uris = new Set<string>();\n for (const registration of registrations) {\n if (registration.uri !== undefined && !registration.uri.startsWith(\"ui://\")) {\n throw new Error(\n `mountly-mcp/server: registration uri must use the 'ui://' scheme (received '${registration.uri}')`,\n );\n }\n const supplied = typeof registration.artifact === \"string\" ? undefined : registration.artifact;\n const artifact = await readMcpAppArtifact(\n typeof registration.artifact === \"string\"\n ? registration.artifact\n : registration.artifact.htmlPath,\n registration.uri ?? supplied?.uri,\n );\n if (supplied && supplied.name !== artifact.name) {\n throw new Error(\n `mountly-mcp: supplied artifact name '${supplied.name}' does not match file artifact name '${artifact.name}'`,\n );\n }\n if (names.has(artifact.name))\n throw new Error(`mountly-mcp: duplicate app name '${artifact.name}'`);\n if (uris.has(artifact.uri)) throw new Error(`mountly-mcp: duplicate app uri '${artifact.uri}'`);\n names.add(artifact.name);\n uris.add(artifact.uri);\n loaded.push({ artifact, html: await readFile(artifact.htmlPath, \"utf8\") });\n }\n return loaded;\n}\n\n/**\n * Install Mountly Views and their independently declared tools into an existing,\n * unconnected MCP server. The caller retains ownership of identity, auth,\n * transports, prompts, ordinary tools, and deployment.\n */\nexport async function registerMcpApps(\n server: McpServer,\n options: RegisterMcpAppsOptions,\n): Promise<RegisteredMcpApps> {\n if (server.isConnected()) {\n throw new Error(\"mountly-mcp: registerMcpApps() must run before the server connects\");\n }\n if (options.views.length === 0) {\n throw new Error(\"mountly-mcp: registerMcpApps() requires at least one View\");\n }\n const views = await loadViews(options.views);\n const byUri = new Map(views.map((view) => [view.artifact.uri, view]));\n for (const tool of options.tools ?? []) {\n if (!byUri.has(tool.resourceUri)) {\n throw new Error(\n `mountly-mcp: tool '${tool.name}' references undeclared View '${tool.resourceUri}'`,\n );\n }\n }\n\n server.server.registerCapabilities({\n extensions: { [EXTENSION_ID]: { mimeTypes: [RESOURCE_MIME_TYPE] } },\n } as never);\n\n const resources = views.map(({ artifact, html }) =>\n registerAppResource(\n server,\n artifact.name,\n artifact.uri,\n {\n mimeType: artifact.declaration.mimeType,\n description: artifact.declaration.description,\n _meta: { ui: artifact.declaration._meta.ui },\n },\n async () => ({\n contents: [\n {\n uri: artifact.uri,\n mimeType: artifact.declaration.mimeType,\n text: html,\n _meta: { ui: artifact.declaration._meta.ui },\n },\n ],\n }),\n ),\n );\n\n const tools: InstalledTool[] = (options.tools ?? []).map((registration) => {\n const { meta, withoutUi, visibility } = mergedToolMetadata(registration);\n const config = registration.config ?? {};\n const registered = registerAppTool(\n server,\n registration.name,\n {\n ...config,\n inputSchema: normalizeToolSchema(config.inputSchema) as never,\n outputSchema: normalizeToolSchema(config.outputSchema) as never,\n _meta: meta,\n },\n (async (args: unknown, extra: unknown) =>\n withTextFallback(await registration.handler(args, extra))) as never,\n );\n return { registration, registered, metadataWithoutUi: withoutUi, visibility };\n });\n\n const previousInitialized = server.server.oninitialized;\n const handleInitialized = () => {\n previousInitialized?.();\n const ui = getUiCapability(server.server.getClientCapabilities());\n if (ui?.mimeTypes?.includes(RESOURCE_MIME_TYPE)) return;\n for (const resource of resources) resource.remove();\n for (const tool of tools) {\n if (tool.visibility.includes(\"model\")) {\n tool.registered.update({ _meta: tool.metadataWithoutUi });\n } else {\n tool.registered.remove();\n }\n }\n };\n server.server.oninitialized = handleInitialized;\n\n return {\n artifacts: views.map((view) => view.artifact),\n remove() {\n for (const resource of resources) resource.remove();\n for (const tool of tools) tool.registered.remove();\n if (server.server.oninitialized === handleInitialized) {\n server.server.oninitialized = previousInitialized;\n }\n },\n };\n}\n\nexport interface RunningMcpAppServer {\n connectInProcess(options?: { ui?: boolean }): Promise<{ client: Client }>;\n listen(transport: Transport): Promise<void>;\n close(): Promise<void>;\n}\n\nfunction createConfiguredServer(options: CreateMcpAppServerOptions): McpServer {\n return new McpServer(\n { name: options.name, version: options.version },\n { capabilities: { resources: {}, tools: {} } },\n );\n}\n\n/** Convenience adapter that retains the original one-call server experience. */\nexport function createMcpAppServer(options: CreateMcpAppServerOptions): RunningMcpAppServer {\n let server: McpServer | undefined;\n async function build(): Promise<McpServer> {\n const next = createConfiguredServer(options);\n await registerMcpApps(next, { views: options.views, tools: options.tools });\n server = next;\n return next;\n }\n return {\n async connectInProcess(connectOptions) {\n const next = await build();\n const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();\n await next.connect(serverTransport);\n const client = new Client(\n { name: `${options.name}-inprocess-client`, version: options.version },\n (connectOptions?.ui ?? true)\n ? ({\n capabilities: {\n extensions: { [EXTENSION_ID]: { mimeTypes: [RESOURCE_MIME_TYPE] } },\n },\n } as never)\n : undefined,\n );\n await client.connect(clientTransport);\n return { client };\n },\n async listen(transport) {\n const next = await build();\n await next.connect(transport);\n },\n async close() {\n await server?.close();\n },\n };\n}\n\nexport async function serveStdio(options: CreateMcpAppServerOptions): Promise<RunningMcpAppServer> {\n const { StdioServerTransport } = await import(\"@modelcontextprotocol/sdk/server/stdio.js\");\n const server = createMcpAppServer(options);\n await server.listen(new StdioServerTransport());\n return server;\n}\n\nexport { EXTENSION_ID, RESOURCE_MIME_TYPE };\n"],"mappings":"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","names":[]}