mountly-mcp 3.1.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -29
- package/dist/bridge/iframe-entry.js +24 -16
- package/dist/bridge/index.d.ts +5 -5
- package/dist/bridge/index.js +2 -2
- package/dist/build/index.d.ts +3 -3
- package/dist/build/index.js +2 -2
- package/dist/{chunk-NLQDVNZP.js → chunk-AEVQKFXX.js} +12 -8
- package/dist/chunk-AEVQKFXX.js.map +1 -0
- package/dist/{chunk-SNWNF2XI.js → chunk-JVQZGKBL.js} +13 -13
- package/dist/chunk-JVQZGKBL.js.map +1 -0
- package/dist/chunk-KWVGW5Q7.js +582 -0
- package/dist/chunk-KWVGW5Q7.js.map +1 -0
- package/dist/{chunk-DQLQOMDM.js → chunk-M4WFJR3C.js} +2 -2
- package/dist/{chunk-DQLQOMDM.js.map → chunk-M4WFJR3C.js.map} +1 -1
- package/dist/{chunk-4WAKOYMK.js → chunk-MTJDRWGD.js} +2 -2
- package/dist/chunk-MTJDRWGD.js.map +1 -0
- package/dist/chunk-SADNMGP6.js +16 -0
- package/dist/chunk-SADNMGP6.js.map +1 -0
- package/dist/cli.js +21 -10
- package/dist/cli.js.map +1 -1
- package/dist/create.d.ts +10 -0
- package/dist/create.js +113 -0
- package/dist/create.js.map +1 -0
- package/dist/dev/host-entry.js +2 -2
- package/dist/dev/index.d.ts +1 -1
- package/dist/dev/index.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +10 -2
- package/dist/json-render/{widget.d.ts → generative-view.d.ts} +9 -11
- package/dist/json-render/index.d.ts +1 -1
- package/dist/json-render/index.js +9 -8
- package/dist/json-render/index.js.map +1 -1
- package/dist/publish.d.ts +10 -0
- package/dist/react/context.d.ts +1 -1
- package/dist/react/create-view.d.ts +8 -0
- package/dist/react/hooks.d.ts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +6 -5
- package/dist/schema.d.ts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js.map +1 -1
- package/dist/svelte/index.d.ts +10 -16
- package/dist/svelte/index.js +7 -3
- package/dist/svelte/index.js.map +1 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +1 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/types.d.ts +14 -4
- package/dist/vite/index.d.ts +6 -6
- package/dist/vite/index.js +7 -7
- package/dist/vite/index.js.map +1 -1
- package/dist/vue/index.d.ts +12 -17
- package/dist/vue/index.js +10 -6
- package/dist/vue/index.js.map +1 -1
- package/package.json +11 -15
- package/templates/react/.gitignore.tmpl +4 -0
- package/templates/react/README.md.tmpl +15 -0
- package/templates/react/mcp.fixtures.json.tmpl +4 -0
- package/templates/react/package.json.tmpl +27 -0
- package/templates/react/server.mjs.tmpl +70 -0
- package/templates/react/src/view.tsx.tmpl +160 -0
- package/templates/react/tsconfig.json.tmpl +15 -0
- package/templates/react/vite.config.ts.tmpl +22 -0
- package/templates/svelte/.gitignore.tmpl +4 -0
- package/templates/svelte/README.md.tmpl +14 -0
- package/templates/svelte/mcp.fixtures.json.tmpl +4 -0
- package/templates/svelte/package.json.tmpl +26 -0
- package/templates/svelte/server.mjs.tmpl +70 -0
- package/templates/svelte/src/Dashboard.svelte.tmpl +158 -0
- package/templates/svelte/src/view.ts.tmpl +14 -0
- package/templates/svelte/tsconfig.json.tmpl +14 -0
- package/templates/svelte/vite.config.ts.tmpl +22 -0
- package/templates/vanilla/.gitignore.tmpl +4 -0
- package/templates/vanilla/README.md.tmpl +20 -0
- package/templates/vanilla/mcp.fixtures.json.tmpl +4 -0
- package/templates/vanilla/package.json.tmpl +22 -0
- package/templates/vanilla/server.mjs.tmpl +70 -0
- package/templates/vanilla/src/view.ts.tmpl +177 -0
- package/templates/vanilla/tsconfig.json.tmpl +14 -0
- package/templates/vanilla/vite.config.ts.tmpl +20 -0
- package/templates/vue/.gitignore.tmpl +4 -0
- package/templates/vue/README.md.tmpl +14 -0
- package/templates/vue/mcp.fixtures.json.tmpl +4 -0
- package/templates/vue/package.json.tmpl +26 -0
- package/templates/vue/server.mjs.tmpl +70 -0
- package/templates/vue/src/Dashboard.vue.tmpl +159 -0
- package/templates/vue/src/view.ts.tmpl +14 -0
- package/templates/vue/tsconfig.json.tmpl +15 -0
- package/templates/vue/vite.config.ts.tmpl +22 -0
- package/dist/chunk-4WAKOYMK.js.map +0 -1
- package/dist/chunk-KRAS266U.js +0 -260
- package/dist/chunk-KRAS266U.js.map +0 -1
- package/dist/chunk-NLQDVNZP.js.map +0 -1
- package/dist/chunk-SNWNF2XI.js.map +0 -1
- package/dist/react/create-widget.d.ts +0 -11
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
LATEST_PROTOCOL_VERSION,
|
|
3
3
|
RESOURCE_MIME_TYPE
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-M4WFJR3C.js";
|
|
5
5
|
import {
|
|
6
6
|
escapeInlineScript,
|
|
7
7
|
serializeInlineScriptValue
|
|
@@ -193,4 +193,4 @@ export {
|
|
|
193
193
|
buildMcpResourceFromSource,
|
|
194
194
|
buildMcpResource
|
|
195
195
|
};
|
|
196
|
-
//# sourceMappingURL=chunk-
|
|
196
|
+
//# sourceMappingURL=chunk-MTJDRWGD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/build/index.ts","../src/build/emit-html.ts","../src/build/emit-meta.ts"],"sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport { resolve } from \"node:path\";\nimport type {\n McpUiDisplayMode,\n McpUiResourceCsp,\n McpUiResourcePermissions,\n} from \"@modelcontextprotocol/ext-apps\";\nimport type { McpResourceDeclaration } from \"../types.js\";\nimport type { McpAppArtifact } from \"../artifact/index.js\";\nimport { emitHtml } from \"./emit-html.js\";\nimport { emitMeta } from \"./emit-meta.js\";\n\n/**\n * Absolute path to the iframe bridge runtime shipped with this package.\n *\n * `buildMcpResource` inlines this file's contents into the emitted HTML so\n * the View can speak the MCP Apps postMessage protocol. Use this when you\n * need to point another build tool at the runtime, or when you want a\n * stable, version-pinned alternative to passing `bridgeRuntimePath` yourself.\n *\n * Resolves relative to the installed package, so it works identically in\n * monorepos and in projects that installed mountly-mcp from npm.\n */\nexport function getBridgeRuntimePath(): string {\n // Ask the module resolver rather than walking up from __dirname: the subpath\n // is a declared export, so this holds wherever a bundler puts this file.\n return fileURLToPath(import.meta.resolve(\"mountly-mcp/bridge/iframe-entry.js\"));\n}\n\nfunction defaultBridgeRuntimePath(): string {\n return getBridgeRuntimePath();\n}\n\nexport interface BuildSelfContainedOptions {\n entry: string;\n uri: string;\n name: string;\n output: string;\n description?: string;\n awaitToolResult?: boolean;\n displayModes?: ReadonlyArray<McpUiDisplayMode>;\n /**\n * Render progressive states from `ui/notifications/tool-input-partial`.\n * Off by default — see `RunBridgeOptions.streamToolInput`.\n */\n streamToolInput?: boolean;\n csp?: McpUiResourceCsp;\n /** Sandbox permissions requested by the view (camera/microphone/geolocation/clipboardWrite). */\n permissions?: McpUiResourcePermissions;\n /** Dedicated sandbox origin (host-dependent format). */\n domain?: string;\n /** Whether the view requests the host to show a visible border + background. */\n prefersBorder?: boolean;\n cssEntry?: string;\n bridgeRuntimePath?: string;\n}\n\nexport interface BuildCdnOptions extends BuildSelfContainedOptions {\n cdn: { jsUrl: string; cssUrl: string };\n}\n\nexport type BuildOptions = BuildSelfContainedOptions | BuildCdnOptions;\n\nexport interface BuildResult {\n htmlPath: string;\n metaPath: string;\n declaration: McpResourceDeclaration;\n artifact: McpAppArtifact;\n}\n\nexport interface BuildFromSourceOptions extends Omit<\n BuildSelfContainedOptions,\n \"entry\" | \"cssEntry\"\n> {\n /** The bundled View JS. */\n js: string;\n /** The View's CSS, if it has any. */\n css?: string;\n}\n\n/**\n * Same as {@link buildMcpResource}, but taking the bundle in memory instead of\n * from disk — for bundler plugins that already hold the emitted sources and\n * shouldn't have to guess when they land on the filesystem.\n */\nexport async function buildMcpResourceFromSource(\n options: BuildFromSourceOptions,\n): Promise<BuildResult> {\n const { js, css, ...rest } = options;\n return writeResource({ ...rest, mode: \"self-contained\", js, css: css ?? \"\" });\n}\n\nexport async function buildMcpResource(options: BuildOptions): Promise<BuildResult> {\n if (!(\"cdn\" in options)) {\n const js = await readFile(options.entry, \"utf8\");\n const css = options.cssEntry ? await readFile(options.cssEntry, \"utf8\") : \"\";\n return writeResource({ ...options, mode: \"self-contained\", js, css });\n }\n return writeResource({ ...options, mode: \"cdn\" });\n}\n\ntype ResourceMeta = Omit<BuildSelfContainedOptions, \"entry\" | \"cssEntry\">;\n\ntype WriteResourceOptions =\n | (ResourceMeta & { mode: \"self-contained\"; js: string; css: string })\n | (ResourceMeta & { mode: \"cdn\"; cdn: BuildCdnOptions[\"cdn\"] });\n\nasync function writeResource(options: WriteResourceOptions): Promise<BuildResult> {\n const bridgeRuntimeJs = await readFile(\n options.bridgeRuntimePath ?? defaultBridgeRuntimePath(),\n \"utf8\",\n );\n\n const declaration = emitMeta({\n uri: options.uri,\n name: options.name,\n description: options.description,\n awaitToolResult: options.awaitToolResult,\n displayModes: options.displayModes,\n csp: options.mode === \"cdn\" ? mergeCdnIntoCsp(options.csp, options.cdn) : options.csp,\n permissions: options.permissions,\n domain: options.domain,\n prefersBorder: options.prefersBorder,\n });\n\n // Same values the sidecar declares, so the view's `ui/initialize` matches it.\n const config = {\n awaitToolResult: declaration.awaitToolResult,\n displayModes: declaration.displayModes,\n streamToolInput: options.streamToolInput ?? false,\n };\n\n const html =\n options.mode === \"cdn\"\n ? emitHtml({\n mode: \"cdn\",\n uri: options.uri,\n jsUrl: options.cdn.jsUrl,\n cssUrl: options.cdn.cssUrl,\n bridgeRuntimeJs,\n config,\n })\n : emitHtml({\n mode: \"self-contained\",\n uri: options.uri,\n js: options.js,\n css: options.css,\n bridgeRuntimeJs,\n config,\n });\n\n const metaPath = `${options.output}.meta.json`;\n await writeFile(options.output, html, \"utf8\");\n await writeFile(metaPath, JSON.stringify(declaration, null, 2), \"utf8\");\n\n const htmlPath = resolve(options.output);\n return {\n htmlPath,\n metaPath: resolve(metaPath),\n declaration,\n artifact: {\n name: declaration.name,\n uri: declaration.uri,\n htmlPath,\n metaPath: resolve(metaPath),\n declaration,\n },\n };\n}\n\nfunction mergeCdnIntoCsp(\n csp: McpUiResourceCsp | undefined,\n cdn: { jsUrl: string; cssUrl: string },\n): McpUiResourceCsp {\n const jsOrigin = new URL(cdn.jsUrl).origin;\n const cssOrigin = new URL(cdn.cssUrl).origin;\n const merged: McpUiResourceCsp = { ...(csp ?? {}) };\n const existing = new Set(merged.resourceDomains ?? []);\n existing.add(jsOrigin);\n existing.add(cssOrigin);\n merged.resourceDomains = Array.from(existing);\n return merged;\n}\n\nexport { emitHtml } from \"./emit-html.js\";\nexport { emitMeta } from \"./emit-meta.js\";\n","import { MCP_APPS_PROTOCOL_VERSION } from \"../schema.js\";\nimport type { McpUiDisplayMode } from \"@modelcontextprotocol/ext-apps\";\nimport { escapeInlineScript, serializeInlineScriptValue } from \"../html.js\";\n\n/**\n * Runtime config the emitted HTML hands to the inlined bridge\n * (`bridge/iframe-entry.ts` reads these off `globalThis`). Without it the view\n * falls back to `awaitToolResult: true` / `availableDisplayModes: [\"inline\"]`,\n * regardless of what the build declared in the sidecar.\n */\nexport interface EmitHtmlConfig {\n awaitToolResult?: boolean;\n displayModes?: ReadonlyArray<McpUiDisplayMode>;\n streamToolInput?: boolean;\n}\n\nexport interface EmitHtmlSelfContainedInput {\n mode: \"self-contained\";\n uri: string;\n js: string;\n css: string;\n bridgeRuntimeJs: string;\n config?: EmitHtmlConfig;\n}\n\nexport interface EmitHtmlCdnInput {\n mode: \"cdn\";\n uri: string;\n jsUrl: string;\n cssUrl: string;\n bridgeRuntimeJs: string;\n config?: EmitHtmlConfig;\n}\n\nexport type EmitHtmlInput = EmitHtmlSelfContainedInput | EmitHtmlCdnInput;\n\nfunction escapeAttr(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\nfunction escapeStyle(value: string): string {\n return value.replace(/<\\/style/gi, \"<\\\\/style\");\n}\n\n/**\n * Classic (non-module) script so it runs at parse time — before the deferred\n * module scripts that boot the bridge.\n */\nfunction configScript(config: EmitHtmlConfig | undefined): string[] {\n if (!config) return [];\n const assignments: string[] = [];\n if (config.awaitToolResult !== undefined) {\n assignments.push(\n `globalThis.__mountlyMcpAwaitToolResult__=${serializeInlineScriptValue(config.awaitToolResult)};`,\n );\n }\n if (config.displayModes !== undefined) {\n assignments.push(\n `globalThis.__mountlyMcpAvailableDisplayModes__=${serializeInlineScriptValue(config.displayModes)};`,\n );\n }\n if (config.streamToolInput !== undefined) {\n assignments.push(\n `globalThis.__mountlyMcpStreamToolInput__=${serializeInlineScriptValue(config.streamToolInput)};`,\n );\n }\n if (assignments.length === 0) return [];\n return [`<script>${assignments.join(\"\")}</script>`];\n}\n\nexport function emitHtml(input: EmitHtmlInput): string {\n const head = [\n `<meta charset=\"utf-8\">`,\n `<meta name=\"mountly-mcp-protocol\" content=\"${MCP_APPS_PROTOCOL_VERSION}\">`,\n `<meta name=\"mountly-mcp-uri\" content=\"${escapeAttr(input.uri)}\">`,\n ];\n\n if (input.mode === \"self-contained\") {\n head.push(`<style>${escapeStyle(input.css)}</style>`);\n const body = [\n `<div id=\"mountly-mcp-root\"></div>`,\n ...configScript(input.config),\n `<script type=\"module\">${escapeInlineScript(input.js)}\\n${escapeInlineScript(input.bridgeRuntimeJs)}</script>`,\n ];\n return template(head, body);\n }\n\n head.push(`<link rel=\"stylesheet\" href=\"${escapeAttr(input.cssUrl)}\">`);\n // The View bundle MUST load before the bridge: module scripts execute in\n // document order, and the bridge reads the published View synchronously.\n const body = [\n `<div id=\"mountly-mcp-root\"></div>`,\n ...configScript(input.config),\n `<script type=\"module\" src=\"${escapeAttr(input.jsUrl)}\"></script>`,\n `<script type=\"module\">${escapeInlineScript(input.bridgeRuntimeJs)}</script>`,\n ];\n return template(head, body);\n}\n\nfunction template(head: string[], body: string[]): string {\n return `<!doctype html>\n<html data-mountly-mcp>\n<head>\n${head.join(\"\\n\")}\n</head>\n<body>\n${body.join(\"\\n\")}\n</body>\n</html>`;\n}\n","import { MCP_APPS_MIME, MCP_APPS_PROTOCOL_VERSION } from \"../schema.js\";\nimport type {\n McpUiDisplayMode,\n McpUiResourceCsp,\n McpUiResourcePermissions,\n} from \"@modelcontextprotocol/ext-apps\";\nimport type { McpResourceDeclaration } from \"../types.js\";\n\nexport interface EmitMetaInput {\n uri: string;\n name: string;\n description?: string;\n awaitToolResult?: boolean;\n displayModes?: ReadonlyArray<McpUiDisplayMode>;\n csp?: McpUiResourceCsp;\n /** Sandbox permissions requested by the view (camera/microphone/geolocation/clipboardWrite). */\n permissions?: McpUiResourcePermissions;\n /** Dedicated sandbox origin requested by the view (host-dependent format). */\n domain?: string;\n /** Whether the view requests the host to show a visible border + background. */\n prefersBorder?: boolean;\n}\n\nfunction assertUiUri(uri: string): void {\n if (!uri.startsWith(\"ui://\")) {\n throw new Error(`mountly-mcp: UI resource uri must use the 'ui://' scheme (received '${uri}')`);\n }\n}\n\nexport function emitMeta(input: EmitMetaInput): McpResourceDeclaration {\n assertUiUri(input.uri);\n\n const ui: McpResourceDeclaration[\"_meta\"][\"ui\"] = {};\n if (input.csp) ui.csp = input.csp;\n if (input.permissions) ui.permissions = input.permissions;\n if (input.domain !== undefined) ui.domain = input.domain;\n if (input.prefersBorder !== undefined) ui.prefersBorder = input.prefersBorder;\n\n const decl: McpResourceDeclaration = {\n protocolVersion: MCP_APPS_PROTOCOL_VERSION,\n uri: input.uri,\n name: input.name,\n mimeType: MCP_APPS_MIME,\n awaitToolResult: input.awaitToolResult ?? true,\n displayModes: input.displayModes ?? [\"inline\"],\n _meta: { ui },\n };\n if (input.description !== undefined) decl.description = input.description;\n return decl;\n}\n"],"mappings":"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","names":["body"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/publish.ts
|
|
2
|
+
var MCP_VIEW_GLOBAL_KEY = "__mountlyMcpView__";
|
|
3
|
+
function publishMcpView(view) {
|
|
4
|
+
globalThis[MCP_VIEW_GLOBAL_KEY] = view;
|
|
5
|
+
return view;
|
|
6
|
+
}
|
|
7
|
+
function getPublishedMcpView() {
|
|
8
|
+
return globalThis[MCP_VIEW_GLOBAL_KEY];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
MCP_VIEW_GLOBAL_KEY,
|
|
13
|
+
publishMcpView,
|
|
14
|
+
getPublishedMcpView
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=chunk-SADNMGP6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/publish.ts"],"sourcesContent":["import type { McpView } from \"./types.js\";\n\n/** Internal bridge key — prefer {@link publishMcpView} / `createMcpView`. */\nexport const MCP_VIEW_GLOBAL_KEY = \"__mountlyMcpView__\" as const;\n\n/**\n * Publish a View module for the iframe bridge. Call once from your View entry\n * (or rely on `createMcpView`, which does this for you).\n */\nexport function publishMcpView(view: McpView): McpView {\n (globalThis as Record<string, unknown>)[MCP_VIEW_GLOBAL_KEY] = view;\n return view;\n}\n\n/** Read the published View module, if any. */\nexport function getPublishedMcpView(): McpView | undefined {\n return (globalThis as Record<string, unknown>)[MCP_VIEW_GLOBAL_KEY] as\n | McpView\n | undefined;\n}\n"],"mappings":";AAGO,IAAM,sBAAsB;AAM5B,SAAS,eAAe,MAAwB;AACrD,EAAC,WAAuC,mBAAmB,IAAI;AAC/D,SAAO;AACT;AAGO,SAAS,sBAA2C;AACzD,SAAQ,WAAuC,mBAAmB;AAGpE;","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
startDevHost
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-KWVGW5Q7.js";
|
|
5
5
|
import "./chunk-UECZA34Q.js";
|
|
6
6
|
import "./chunk-YJA4H6IB.js";
|
|
7
7
|
|
|
@@ -67,15 +67,20 @@ function parseBuildArgs(argv) {
|
|
|
67
67
|
return args;
|
|
68
68
|
}
|
|
69
69
|
function printUsage() {
|
|
70
|
-
process.stdout.write(`mountly-mcp \u2014 build, develop, and verify MCP Apps
|
|
70
|
+
process.stdout.write(`mountly-mcp \u2014 create, build, develop, and verify MCP Apps
|
|
71
71
|
|
|
72
72
|
Usage:
|
|
73
|
+
mountly-mcp create <name> [options]
|
|
73
74
|
mountly-mcp build [options]
|
|
74
75
|
mountly-mcp dev [options]
|
|
75
76
|
mountly-mcp verify [options]
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
-
|
|
78
|
+
Create options:
|
|
79
|
+
-f, --framework <name> react | vue | svelte (default: react)
|
|
80
|
+
--dir <path> output directory (default: ./<name>)
|
|
81
|
+
|
|
82
|
+
Dev / build options:
|
|
83
|
+
-c, --config <path> vite config to read the View from (default: auto)
|
|
79
84
|
-f, --fixtures <path> JSON of named samples (default: mcp.fixtures.json)
|
|
80
85
|
-s, --server <path> module default-exporting your MCP server, to call real tools
|
|
81
86
|
-a, --app <name> View to develop (required when the manifest has several)
|
|
@@ -90,7 +95,9 @@ Verify options:
|
|
|
90
95
|
--render require each View to mount with content in Chromium (needs playwright)
|
|
91
96
|
--json output the conformance report as JSON
|
|
92
97
|
|
|
93
|
-
|
|
98
|
+
Greenfield: prefer \`mountly-mcp create\` \u2014 do not clone the Mountly monorepo.
|
|
99
|
+
|
|
100
|
+
The View's entry, uri and name come from the mountlyMcpViews() plugin in
|
|
94
101
|
your vite config, so there is nothing extra to configure.
|
|
95
102
|
|
|
96
103
|
Without --server each fixture value is delivered as structuredContent. With
|
|
@@ -115,12 +122,12 @@ async function verify(args) {
|
|
|
115
122
|
);
|
|
116
123
|
if (!report.ok || args.strict && report.diagnostics.length > 0) process.exitCode = 1;
|
|
117
124
|
}
|
|
118
|
-
async function
|
|
125
|
+
async function loadViewOptions(configFile, appName) {
|
|
119
126
|
const { loadConfigFromFile } = await import("vite");
|
|
120
127
|
const loaded = await loadConfigFromFile({ command: "build", mode: "development" }, configFile);
|
|
121
128
|
if (!loaded) {
|
|
122
129
|
throw new Error(
|
|
123
|
-
"mountly-mcp: no vite config found. Pass --config, or add
|
|
130
|
+
"mountly-mcp: no vite config found. Pass --config, or add mountlyMcpViews() to vite.config.ts."
|
|
124
131
|
);
|
|
125
132
|
}
|
|
126
133
|
const plugins = (loaded.config.plugins ?? []).flat(
|
|
@@ -129,7 +136,7 @@ async function loadWidgetOptions(configFile, appName) {
|
|
|
129
136
|
const plugin = plugins.find((p) => p?.api?.mountlyMcp);
|
|
130
137
|
if (!plugin?.api?.mountlyMcp) {
|
|
131
138
|
throw new Error(
|
|
132
|
-
`mountly-mcp: no
|
|
139
|
+
`mountly-mcp: no mountlyMcpViews() plugin in ${loaded.path}. Add it to the plugins array.`
|
|
133
140
|
);
|
|
134
141
|
}
|
|
135
142
|
const apps = plugin.api.mountlyMcp.apps;
|
|
@@ -172,7 +179,7 @@ async function loadFixtures(path) {
|
|
|
172
179
|
}
|
|
173
180
|
}
|
|
174
181
|
async function dev(args) {
|
|
175
|
-
const { options, outDir, root } = await
|
|
182
|
+
const { options, outDir, root } = await loadViewOptions(args.config, args.app);
|
|
176
183
|
const fixtures = await loadFixtures(args.fixtures);
|
|
177
184
|
let server;
|
|
178
185
|
let toolName;
|
|
@@ -235,7 +242,7 @@ async function dev(args) {
|
|
|
235
242
|
mountly-mcp dev
|
|
236
243
|
host ${host.hostUrl}
|
|
237
244
|
sandbox ${host.sandboxUrl}
|
|
238
|
-
|
|
245
|
+
view ${htmlPath}
|
|
239
246
|
fixtures ${Object.keys(fixtures).length || "none \u2014 pass --fixtures"}
|
|
240
247
|
server ${server ? `${args.server} \u2192 ${toolName ?? "no tool bound to " + options.uri}` : "none \u2014 fixtures deliver directly"}
|
|
241
248
|
|
|
@@ -271,6 +278,10 @@ async function main() {
|
|
|
271
278
|
printUsage();
|
|
272
279
|
process.exit(command === void 0 ? 1 : 0);
|
|
273
280
|
}
|
|
281
|
+
if (command === "create") {
|
|
282
|
+
const { createProject, parseCreateArgs } = await import("./create.js");
|
|
283
|
+
return createProject(parseCreateArgs(rest));
|
|
284
|
+
}
|
|
274
285
|
if (command === "dev") return dev(parseArgs(rest));
|
|
275
286
|
if (command === "build") return buildApps(parseBuildArgs(rest));
|
|
276
287
|
if (command === "verify") return verify(parseVerifyArgs(rest));
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `mountly-mcp dev` — build the widget, serve it in a real sandboxed MCP Apps\n * host, and rebuild on change.\n *\n * Config comes from the `mountlyMcpWidget()` plugin already in vite.config.ts,\n * so there is nothing new to configure.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { startDevHost } from \"./dev/index.js\";\nimport type { ConnectedMcpServer } from \"./dev/connect-server.js\";\nimport type { MountlyMcpViteApi, MountlyMcpWidgetOptions } from \"./vite/index.js\";\n\ninterface Args {\n config?: string;\n fixtures?: string;\n server?: string;\n app?: string;\n port?: number;\n open: boolean;\n}\n\ninterface VerifyArgs {\n manifest?: string;\n html: string[];\n strict: boolean;\n render: boolean;\n json: boolean;\n}\n\ninterface BuildArgs {\n config?: string;\n}\n\nfunction parseArgs(argv: ReadonlyArray<string>): Args {\n const args: Args = { open: true };\n for (let i = 0; i < argv.length; i += 1) {\n const arg = argv[i];\n const next = (): string => {\n const value = argv[i + 1];\n if (value === undefined) throw new Error(`mountly-mcp: ${arg} needs a value`);\n i += 1;\n return value;\n };\n if (arg === \"--config\" || arg === \"-c\") args.config = next();\n else if (arg === \"--fixtures\" || arg === \"-f\") args.fixtures = next();\n else if (arg === \"--server\" || arg === \"-s\") args.server = next();\n else if (arg === \"--app\" || arg === \"-a\") args.app = next();\n else if (arg === \"--port\" || arg === \"-p\") args.port = Number(next());\n else if (arg === \"--no-open\") args.open = false;\n else if (arg === \"--help\" || arg === \"-h\") {\n printUsage();\n process.exit(0);\n } else throw new Error(`mountly-mcp: unknown option '${arg}'`);\n }\n return args;\n}\n\nfunction parseVerifyArgs(argv: ReadonlyArray<string>): VerifyArgs {\n const args: VerifyArgs = { html: [], strict: false, render: false, json: false };\n for (let i = 0; i < argv.length; i += 1) {\n const arg = argv[i];\n const next = (): string => {\n const value = argv[i + 1];\n if (value === undefined) throw new Error(`mountly-mcp: ${arg} needs a value`);\n i += 1;\n return value;\n };\n if (arg === \"--manifest\" || arg === \"-m\") args.manifest = next();\n else if (arg === \"--html\") args.html.push(next());\n else if (arg === \"--strict\") args.strict = true;\n else if (arg === \"--render\") args.render = true;\n else if (arg === \"--json\") args.json = true;\n else throw new Error(`mountly-mcp: unknown verify option '${arg}'`);\n }\n if (args.manifest && args.html.length > 0) {\n throw new Error(\"mountly-mcp: verify accepts either --manifest or --html, not both\");\n }\n return args;\n}\n\nfunction parseBuildArgs(argv: ReadonlyArray<string>): BuildArgs {\n const args: BuildArgs = {};\n for (let i = 0; i < argv.length; i += 1) {\n const arg = argv[i];\n if (arg === \"--config\" || arg === \"-c\") {\n const value = argv[i + 1];\n if (value === undefined) throw new Error(`mountly-mcp: ${arg} needs a value`);\n args.config = value;\n i += 1;\n } else throw new Error(`mountly-mcp: unknown build option '${arg}'`);\n }\n return args;\n}\n\nfunction printUsage(): void {\n process.stdout.write(`mountly-mcp — build, develop, and verify MCP Apps\n\nUsage:\n mountly-mcp build [options]\n mountly-mcp dev [options]\n mountly-mcp verify [options]\n\nOptions:\n -c, --config <path> vite config to read the widget from (default: auto)\n -f, --fixtures <path> JSON of named samples (default: mcp.fixtures.json)\n -s, --server <path> module default-exporting your MCP server, to call real tools\n -a, --app <name> View to develop (required when the manifest has several)\n -p, --port <number> host port (default: 5179; the sandbox takes the next one)\n --no-open don't open a browser\n -h, --help show this\n\nVerify options:\n -m, --manifest <path> manifest to verify (default: dist/mountly-mcp.manifest.json)\n --html <path> transitional single-View HTML; repeat for several\n --strict fail when warnings are present\n --render require each View to mount with content in Chromium (needs playwright)\n --json output the conformance report as JSON\n\nThe widget's entry, uri and name come from the mountlyMcpWidget() plugin in\nyour vite config, so there is nothing extra to configure.\n\nWithout --server each fixture value is delivered as structuredContent. With\n--server it is the tool's arguments instead, the view receives what the real\ntool returns, and tool calls the view makes itself are routed to your server.\n`);\n}\n\nasync function buildApps(args: BuildArgs): Promise<void> {\n const { createBuilder } = await import(\"vite\");\n const builder = await createBuilder({ configFile: args.config }, false);\n await builder.buildApp();\n}\n\nasync function verify(args: VerifyArgs): Promise<void> {\n const { formatConformanceReport, verifyMcpApps } = await import(\"./testing/index.js\");\n const report = await verifyMcpApps({\n manifestPath: args.manifest,\n htmlPaths: args.html.length > 0 ? args.html : undefined,\n render: args.render,\n });\n process.stdout.write(\n args.json ? JSON.stringify(report, null, 2) : formatConformanceReport(report),\n );\n if (!report.ok || (args.strict && report.diagnostics.length > 0)) process.exitCode = 1;\n}\n\n/**\n * Read the widget's options back out of the vite config. The plugin publishes\n * them on `api.mountlyMcp`, so this stays correct if its option shape changes.\n */\nasync function loadWidgetOptions(\n configFile: string | undefined,\n appName: string | undefined,\n): Promise<{ options: MountlyMcpWidgetOptions; outDir: string; root: string }> {\n const { loadConfigFromFile } = await import(\"vite\");\n const loaded = await loadConfigFromFile({ command: \"build\", mode: \"development\" }, configFile);\n if (!loaded) {\n throw new Error(\n \"mountly-mcp: no vite config found. Pass --config, or add mountlyMcpWidget() to vite.config.ts.\",\n );\n }\n // Flattened as unknown[]: vite's PluginOption is recursively nested, and\n // asking TS to flatten it by its own type sends the checker infinite.\n const plugins = ((loaded.config.plugins ?? []) as unknown[]).flat(\n Number.POSITIVE_INFINITY,\n ) as Array<{ name?: string; api?: { mountlyMcp?: MountlyMcpViteApi } } | null>;\n const plugin = plugins.find((p) => p?.api?.mountlyMcp);\n if (!plugin?.api?.mountlyMcp) {\n throw new Error(\n `mountly-mcp: no mountlyMcpWidget() plugin in ${loaded.path}. Add it to the plugins array.`,\n );\n }\n const apps = plugin.api.mountlyMcp.apps;\n if (apps.length > 1 && appName === undefined) {\n throw new Error(\n `mountly-mcp: this build contains several Views; pass --app <name>. Available: ${apps.map((app) => app.name).join(\", \")}`,\n );\n }\n const selected = appName === undefined ? apps[0] : apps.find((app) => app.name === appName);\n if (!selected) {\n throw new Error(\n `mountly-mcp: no View named '${String(appName)}'. Available: ${apps.map((app) => app.name).join(\", \")}`,\n );\n }\n return {\n options: selected,\n outDir: (loaded.config.build?.outDir as string | undefined) ?? \"dist\",\n root: (loaded.config.root as string | undefined) ?? process.cwd(),\n };\n}\n\n/**\n * Vite empties the out dir at the start of every build and the plugin writes\n * the resource in `closeBundle`, so the watcher's END event can arrive while\n * the files are still missing. Wait for the artifact rather than trusting the\n * event ordering — this also covers a build that failed and wrote nothing.\n */\nasync function waitForFile(path: string, timeoutMs = 15_000): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n try {\n await readFile(path);\n return;\n } catch {\n if (Date.now() > deadline) throw new Error(`mountly-mcp: timed out waiting for ${path}`);\n await new Promise((done) => setTimeout(done, 50));\n }\n }\n}\n\nasync function loadFixtures(path: string | undefined): Promise<Record<string, unknown>> {\n const file = path ?? \"mcp.fixtures.json\";\n try {\n return JSON.parse(await readFile(resolve(file), \"utf8\")) as Record<string, unknown>;\n } catch (error) {\n // An explicit --fixtures that doesn't load is a mistake worth reporting;\n // the default one simply being absent is not.\n if (path) throw new Error(`mountly-mcp: could not read fixtures '${file}': ${String(error)}`);\n return {};\n }\n}\n\nasync function dev(args: Args): Promise<void> {\n const { options, outDir, root } = await loadWidgetOptions(args.config, args.app);\n const fixtures = await loadFixtures(args.fixtures);\n\n // Connected after the first build, not here: a server module typically reads\n // the View it serves, which does not exist yet on a cold checkout.\n let server: ConnectedMcpServer | undefined;\n let toolName: string | undefined;\n\n const { build } = await import(\"vite\");\n const htmlPath = options.output\n ? resolve(root, options.output)\n : resolve(root, outDir, `${options.name}.html`);\n\n // A watching build: vite rebuilds on change, and each rebuild bumps the\n // version the browser is polling, which reloads it.\n let host: Awaited<ReturnType<typeof startDevHost>> | undefined;\n let started = false;\n\n const previousSelectedApp = process.env.MOUNTLY_MCP_SELECTED_APP;\n process.env.MOUNTLY_MCP_SELECTED_APP = options.name;\n const watcher = await build({\n configFile: args.config ?? undefined,\n build: { watch: {} },\n logLevel: \"warn\",\n }).finally(() => {\n if (previousSelectedApp === undefined) delete process.env.MOUNTLY_MCP_SELECTED_APP;\n else process.env.MOUNTLY_MCP_SELECTED_APP = previousSelectedApp;\n });\n\n // `build({ watch })` returns a rollup watcher; the first BUNDLE_END means the\n // widget exists on disk and the host has something to serve.\n const rollupWatcher = watcher as unknown as {\n on(event: \"event\", cb: (e: { code: string; error?: Error }) => void): void;\n close(): Promise<void>;\n };\n\n rollupWatcher.on(\"event\", (event) => {\n if (event.code === \"ERROR\") {\n process.stderr.write(`mountly-mcp: build failed — ${event.error?.message ?? \"unknown\"}\\n`);\n return;\n }\n if (event.code !== \"END\") return;\n if (started) {\n void waitForFile(`${htmlPath}.meta.json`).then(\n () => {\n host?.reload();\n process.stdout.write(\"mountly-mcp: rebuilt\\n\");\n },\n (error: unknown) => process.stderr.write(`${String(error)}\\n`),\n );\n return;\n }\n started = true;\n void (async () => {\n await waitForFile(`${htmlPath}.meta.json`);\n if (args.server) {\n const { connectMcpServer } = await import(\"./dev/connect-server.js\");\n server = await connectMcpServer(args.server);\n toolName = await server.toolFor(options.uri);\n if (!toolName) {\n throw new Error(\n `mountly-mcp: server '${args.server}' has no tool linked to View '${options.uri}'`,\n );\n }\n }\n host = await startDevHost({\n htmlPath,\n fixtures,\n toolInput: {},\n toolName,\n callTool: server?.callTool,\n hostPort: args.port,\n });\n process.stdout.write(\n `\\n mountly-mcp dev\\n` +\n ` host ${host.hostUrl}\\n` +\n ` sandbox ${host.sandboxUrl}\\n` +\n ` widget ${htmlPath}\\n` +\n ` fixtures ${Object.keys(fixtures).length || \"none — pass --fixtures\"}\\n` +\n ` server ${server ? `${args.server} → ${toolName ?? \"no tool bound to \" + options.uri}` : \"none — fixtures deliver directly\"}\\n\\n` +\n ` watching for changes; ctrl+c to stop\\n\\n`,\n );\n if (args.open) {\n const { spawn } = await import(\"node:child_process\");\n const opener =\n process.platform === \"darwin\"\n ? \"open\"\n : process.platform === \"win32\"\n ? \"start\"\n : \"xdg-open\";\n spawn(opener, [host.hostUrl], { stdio: \"ignore\", detached: true, shell: true }).unref();\n }\n })().catch((error: unknown) => {\n // Startup runs inside a watcher callback, where a rejection would\n // otherwise be silent and leave the process alive with no host.\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n void Promise.all([rollupWatcher.close(), server?.close()]).finally(() => process.exit(1));\n });\n });\n\n const shutdown = (): void => {\n void (async () => {\n await rollupWatcher.close();\n await host?.close();\n await server?.close();\n process.exit(0);\n })();\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n\nasync function main(): Promise<void> {\n const [command, ...rest] = process.argv.slice(2);\n if (command === undefined || command === \"--help\" || command === \"-h\") {\n printUsage();\n process.exit(command === undefined ? 1 : 0);\n }\n if (command === \"dev\") return dev(parseArgs(rest));\n if (command === \"build\") return buildApps(parseBuildArgs(rest));\n if (command === \"verify\") return verify(parseVerifyArgs(rest));\n process.stderr.write(`mountly-mcp: unknown command '${command}'. Try 'mountly-mcp --help'.\\n`);\n process.exit(1);\n}\n\nmain().catch((error: unknown) => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `mountly-mcp dev` — build the View, serve it in a real sandboxed MCP Apps\n * host, and rebuild on change.\n *\n * Config comes from the `mountlyMcpViews()` plugin already in vite.config.ts,\n * so there is nothing new to configure.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { startDevHost } from \"./dev/index.js\";\nimport type { ConnectedMcpServer } from \"./dev/connect-server.js\";\nimport type { MountlyMcpViteApi, MountlyMcpViewOptions } from \"./vite/index.js\";\n\ninterface Args {\n config?: string;\n fixtures?: string;\n server?: string;\n app?: string;\n port?: number;\n open: boolean;\n}\n\ninterface VerifyArgs {\n manifest?: string;\n html: string[];\n strict: boolean;\n render: boolean;\n json: boolean;\n}\n\ninterface BuildArgs {\n config?: string;\n}\n\nfunction parseArgs(argv: ReadonlyArray<string>): Args {\n const args: Args = { open: true };\n for (let i = 0; i < argv.length; i += 1) {\n const arg = argv[i];\n const next = (): string => {\n const value = argv[i + 1];\n if (value === undefined) throw new Error(`mountly-mcp: ${arg} needs a value`);\n i += 1;\n return value;\n };\n if (arg === \"--config\" || arg === \"-c\") args.config = next();\n else if (arg === \"--fixtures\" || arg === \"-f\") args.fixtures = next();\n else if (arg === \"--server\" || arg === \"-s\") args.server = next();\n else if (arg === \"--app\" || arg === \"-a\") args.app = next();\n else if (arg === \"--port\" || arg === \"-p\") args.port = Number(next());\n else if (arg === \"--no-open\") args.open = false;\n else if (arg === \"--help\" || arg === \"-h\") {\n printUsage();\n process.exit(0);\n } else throw new Error(`mountly-mcp: unknown option '${arg}'`);\n }\n return args;\n}\n\nfunction parseVerifyArgs(argv: ReadonlyArray<string>): VerifyArgs {\n const args: VerifyArgs = { html: [], strict: false, render: false, json: false };\n for (let i = 0; i < argv.length; i += 1) {\n const arg = argv[i];\n const next = (): string => {\n const value = argv[i + 1];\n if (value === undefined) throw new Error(`mountly-mcp: ${arg} needs a value`);\n i += 1;\n return value;\n };\n if (arg === \"--manifest\" || arg === \"-m\") args.manifest = next();\n else if (arg === \"--html\") args.html.push(next());\n else if (arg === \"--strict\") args.strict = true;\n else if (arg === \"--render\") args.render = true;\n else if (arg === \"--json\") args.json = true;\n else throw new Error(`mountly-mcp: unknown verify option '${arg}'`);\n }\n if (args.manifest && args.html.length > 0) {\n throw new Error(\"mountly-mcp: verify accepts either --manifest or --html, not both\");\n }\n return args;\n}\n\nfunction parseBuildArgs(argv: ReadonlyArray<string>): BuildArgs {\n const args: BuildArgs = {};\n for (let i = 0; i < argv.length; i += 1) {\n const arg = argv[i];\n if (arg === \"--config\" || arg === \"-c\") {\n const value = argv[i + 1];\n if (value === undefined) throw new Error(`mountly-mcp: ${arg} needs a value`);\n args.config = value;\n i += 1;\n } else throw new Error(`mountly-mcp: unknown build option '${arg}'`);\n }\n return args;\n}\n\nfunction printUsage(): void {\n process.stdout.write(`mountly-mcp — create, build, develop, and verify MCP Apps\n\nUsage:\n mountly-mcp create <name> [options]\n mountly-mcp build [options]\n mountly-mcp dev [options]\n mountly-mcp verify [options]\n\nCreate options:\n -f, --framework <name> react | vue | svelte (default: react)\n --dir <path> output directory (default: ./<name>)\n\nDev / build options:\n -c, --config <path> vite config to read the View from (default: auto)\n -f, --fixtures <path> JSON of named samples (default: mcp.fixtures.json)\n -s, --server <path> module default-exporting your MCP server, to call real tools\n -a, --app <name> View to develop (required when the manifest has several)\n -p, --port <number> host port (default: 5179; the sandbox takes the next one)\n --no-open don't open a browser\n -h, --help show this\n\nVerify options:\n -m, --manifest <path> manifest to verify (default: dist/mountly-mcp.manifest.json)\n --html <path> transitional single-View HTML; repeat for several\n --strict fail when warnings are present\n --render require each View to mount with content in Chromium (needs playwright)\n --json output the conformance report as JSON\n\nGreenfield: prefer \\`mountly-mcp create\\` — do not clone the Mountly monorepo.\n\nThe View's entry, uri and name come from the mountlyMcpViews() plugin in\nyour vite config, so there is nothing extra to configure.\n\nWithout --server each fixture value is delivered as structuredContent. With\n--server it is the tool's arguments instead, the view receives what the real\ntool returns, and tool calls the view makes itself are routed to your server.\n`);\n}\n\nasync function buildApps(args: BuildArgs): Promise<void> {\n const { createBuilder } = await import(\"vite\");\n const builder = await createBuilder({ configFile: args.config }, false);\n await builder.buildApp();\n}\n\nasync function verify(args: VerifyArgs): Promise<void> {\n const { formatConformanceReport, verifyMcpApps } = await import(\"./testing/index.js\");\n const report = await verifyMcpApps({\n manifestPath: args.manifest,\n htmlPaths: args.html.length > 0 ? args.html : undefined,\n render: args.render,\n });\n process.stdout.write(\n args.json ? JSON.stringify(report, null, 2) : formatConformanceReport(report),\n );\n if (!report.ok || (args.strict && report.diagnostics.length > 0)) process.exitCode = 1;\n}\n\n/**\n * Read the View's options back out of the vite config. The plugin publishes\n * them on `api.mountlyMcp`, so this stays correct if its option shape changes.\n */\nasync function loadViewOptions(\n configFile: string | undefined,\n appName: string | undefined,\n): Promise<{ options: MountlyMcpViewOptions; outDir: string; root: string }> {\n const { loadConfigFromFile } = await import(\"vite\");\n const loaded = await loadConfigFromFile({ command: \"build\", mode: \"development\" }, configFile);\n if (!loaded) {\n throw new Error(\n \"mountly-mcp: no vite config found. Pass --config, or add mountlyMcpViews() to vite.config.ts.\",\n );\n }\n // Flattened as unknown[]: vite's PluginOption is recursively nested, and\n // asking TS to flatten it by its own type sends the checker infinite.\n const plugins = ((loaded.config.plugins ?? []) as unknown[]).flat(\n Number.POSITIVE_INFINITY,\n ) as Array<{ name?: string; api?: { mountlyMcp?: MountlyMcpViteApi } } | null>;\n const plugin = plugins.find((p) => p?.api?.mountlyMcp);\n if (!plugin?.api?.mountlyMcp) {\n throw new Error(\n `mountly-mcp: no mountlyMcpViews() plugin in ${loaded.path}. Add it to the plugins array.`,\n );\n }\n const apps = plugin.api.mountlyMcp.apps;\n if (apps.length > 1 && appName === undefined) {\n throw new Error(\n `mountly-mcp: this build contains several Views; pass --app <name>. Available: ${apps.map((app) => app.name).join(\", \")}`,\n );\n }\n const selected = appName === undefined ? apps[0] : apps.find((app) => app.name === appName);\n if (!selected) {\n throw new Error(\n `mountly-mcp: no View named '${String(appName)}'. Available: ${apps.map((app) => app.name).join(\", \")}`,\n );\n }\n return {\n options: selected,\n outDir: (loaded.config.build?.outDir as string | undefined) ?? \"dist\",\n root: (loaded.config.root as string | undefined) ?? process.cwd(),\n };\n}\n\n/**\n * Vite empties the out dir at the start of every build and the plugin writes\n * the resource in `closeBundle`, so the watcher's END event can arrive while\n * the files are still missing. Wait for the artifact rather than trusting the\n * event ordering — this also covers a build that failed and wrote nothing.\n */\nasync function waitForFile(path: string, timeoutMs = 15_000): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n try {\n await readFile(path);\n return;\n } catch {\n if (Date.now() > deadline) throw new Error(`mountly-mcp: timed out waiting for ${path}`);\n await new Promise((done) => setTimeout(done, 50));\n }\n }\n}\n\nasync function loadFixtures(path: string | undefined): Promise<Record<string, unknown>> {\n const file = path ?? \"mcp.fixtures.json\";\n try {\n return JSON.parse(await readFile(resolve(file), \"utf8\")) as Record<string, unknown>;\n } catch (error) {\n // An explicit --fixtures that doesn't load is a mistake worth reporting;\n // the default one simply being absent is not.\n if (path) throw new Error(`mountly-mcp: could not read fixtures '${file}': ${String(error)}`);\n return {};\n }\n}\n\nasync function dev(args: Args): Promise<void> {\n const { options, outDir, root } = await loadViewOptions(args.config, args.app);\n const fixtures = await loadFixtures(args.fixtures);\n\n // Connected after the first build, not here: a server module typically reads\n // the View it serves, which does not exist yet on a cold checkout.\n let server: ConnectedMcpServer | undefined;\n let toolName: string | undefined;\n\n const { build } = await import(\"vite\");\n const htmlPath = options.output\n ? resolve(root, options.output)\n : resolve(root, outDir, `${options.name}.html`);\n\n // A watching build: vite rebuilds on change, and each rebuild bumps the\n // version the browser is polling, which reloads it.\n let host: Awaited<ReturnType<typeof startDevHost>> | undefined;\n let started = false;\n\n const previousSelectedApp = process.env.MOUNTLY_MCP_SELECTED_APP;\n process.env.MOUNTLY_MCP_SELECTED_APP = options.name;\n const watcher = await build({\n configFile: args.config ?? undefined,\n build: { watch: {} },\n logLevel: \"warn\",\n }).finally(() => {\n if (previousSelectedApp === undefined) delete process.env.MOUNTLY_MCP_SELECTED_APP;\n else process.env.MOUNTLY_MCP_SELECTED_APP = previousSelectedApp;\n });\n\n // `build({ watch })` returns a rollup watcher; the first BUNDLE_END means the\n // View exists on disk and the host has something to serve.\n const rollupWatcher = watcher as unknown as {\n on(event: \"event\", cb: (e: { code: string; error?: Error }) => void): void;\n close(): Promise<void>;\n };\n\n rollupWatcher.on(\"event\", (event) => {\n if (event.code === \"ERROR\") {\n process.stderr.write(`mountly-mcp: build failed — ${event.error?.message ?? \"unknown\"}\\n`);\n return;\n }\n if (event.code !== \"END\") return;\n if (started) {\n void waitForFile(`${htmlPath}.meta.json`).then(\n () => {\n host?.reload();\n process.stdout.write(\"mountly-mcp: rebuilt\\n\");\n },\n (error: unknown) => process.stderr.write(`${String(error)}\\n`),\n );\n return;\n }\n started = true;\n void (async () => {\n await waitForFile(`${htmlPath}.meta.json`);\n if (args.server) {\n const { connectMcpServer } = await import(\"./dev/connect-server.js\");\n server = await connectMcpServer(args.server);\n toolName = await server.toolFor(options.uri);\n if (!toolName) {\n throw new Error(\n `mountly-mcp: server '${args.server}' has no tool linked to View '${options.uri}'`,\n );\n }\n }\n host = await startDevHost({\n htmlPath,\n fixtures,\n toolInput: {},\n toolName,\n callTool: server?.callTool,\n hostPort: args.port,\n });\n process.stdout.write(\n `\\n mountly-mcp dev\\n` +\n ` host ${host.hostUrl}\\n` +\n ` sandbox ${host.sandboxUrl}\\n` +\n ` view ${htmlPath}\\n` +\n ` fixtures ${Object.keys(fixtures).length || \"none — pass --fixtures\"}\\n` +\n ` server ${server ? `${args.server} → ${toolName ?? \"no tool bound to \" + options.uri}` : \"none — fixtures deliver directly\"}\\n\\n` +\n ` watching for changes; ctrl+c to stop\\n\\n`,\n );\n if (args.open) {\n const { spawn } = await import(\"node:child_process\");\n const opener =\n process.platform === \"darwin\"\n ? \"open\"\n : process.platform === \"win32\"\n ? \"start\"\n : \"xdg-open\";\n spawn(opener, [host.hostUrl], { stdio: \"ignore\", detached: true, shell: true }).unref();\n }\n })().catch((error: unknown) => {\n // Startup runs inside a watcher callback, where a rejection would\n // otherwise be silent and leave the process alive with no host.\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n void Promise.all([rollupWatcher.close(), server?.close()]).finally(() => process.exit(1));\n });\n });\n\n const shutdown = (): void => {\n void (async () => {\n await rollupWatcher.close();\n await host?.close();\n await server?.close();\n process.exit(0);\n })();\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n\nasync function main(): Promise<void> {\n const [command, ...rest] = process.argv.slice(2);\n if (command === undefined || command === \"--help\" || command === \"-h\") {\n printUsage();\n process.exit(command === undefined ? 1 : 0);\n }\n if (command === \"create\") {\n const { createProject, parseCreateArgs } = await import(\"./create.js\");\n return createProject(parseCreateArgs(rest));\n }\n if (command === \"dev\") return dev(parseArgs(rest));\n if (command === \"build\") return buildApps(parseBuildArgs(rest));\n if (command === \"verify\") return verify(parseVerifyArgs(rest));\n process.stderr.write(`mountly-mcp: unknown command '${command}'. Try 'mountly-mcp --help'.\\n`);\n process.exit(1);\n}\n\nmain().catch((error: unknown) => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
|
package/dist/create.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare const SUPPORTED: readonly ["react", "vue", "svelte", "vanilla"];
|
|
2
|
+
type Framework = (typeof SUPPORTED)[number];
|
|
3
|
+
export interface CreateArgs {
|
|
4
|
+
name: string;
|
|
5
|
+
framework: Framework;
|
|
6
|
+
dir: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function parseCreateArgs(argv: ReadonlyArray<string>): CreateArgs;
|
|
9
|
+
export declare function createProject(args: CreateArgs): Promise<void>;
|
|
10
|
+
export {};
|
package/dist/create.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// src/create.ts
|
|
2
|
+
import {
|
|
3
|
+
cpSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
readdirSync,
|
|
8
|
+
statSync,
|
|
9
|
+
unlinkSync,
|
|
10
|
+
writeFileSync
|
|
11
|
+
} from "fs";
|
|
12
|
+
import { dirname, join, resolve } from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
var PACKAGE_ROOT = resolve(__dirname, "..");
|
|
16
|
+
var TEMPLATES = resolve(PACKAGE_ROOT, "templates");
|
|
17
|
+
function ownVersion() {
|
|
18
|
+
const pkg = JSON.parse(readFileSync(join(PACKAGE_ROOT, "package.json"), "utf8"));
|
|
19
|
+
if (!pkg.version) throw new Error("mountly-mcp: cannot read own version from package.json");
|
|
20
|
+
return pkg.version;
|
|
21
|
+
}
|
|
22
|
+
var SUPPORTED = ["react", "vue", "svelte", "vanilla"];
|
|
23
|
+
function parseCreateArgs(argv) {
|
|
24
|
+
const name = argv[0];
|
|
25
|
+
if (!name || name.startsWith("-")) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
"mountly-mcp create requires a project name. e.g. mountly-mcp create my-app --framework react"
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
let framework = "react";
|
|
31
|
+
let dir = `./${name}`;
|
|
32
|
+
for (let i = 1; i < argv.length; i += 1) {
|
|
33
|
+
const arg = argv[i];
|
|
34
|
+
if (arg === "--framework" || arg === "-f") {
|
|
35
|
+
const value = argv[++i];
|
|
36
|
+
if (value === void 0) throw new Error(`mountly-mcp: ${arg} needs a value`);
|
|
37
|
+
if (!SUPPORTED.includes(value)) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`mountly-mcp: framework "${value}" not supported. Available: ${SUPPORTED.join(", ")}`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
framework = value;
|
|
43
|
+
} else if (arg === "--dir") {
|
|
44
|
+
const value = argv[++i];
|
|
45
|
+
if (value === void 0) throw new Error("mountly-mcp: --dir needs a value");
|
|
46
|
+
dir = value;
|
|
47
|
+
} else {
|
|
48
|
+
throw new Error(`mountly-mcp: unknown create option '${arg}'`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { name, framework, dir };
|
|
52
|
+
}
|
|
53
|
+
function slugify(name) {
|
|
54
|
+
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
55
|
+
}
|
|
56
|
+
function renderTemplates(dir, ctx) {
|
|
57
|
+
for (const entry of readdirSync(dir)) {
|
|
58
|
+
const full = join(dir, entry);
|
|
59
|
+
if (statSync(full).isDirectory()) {
|
|
60
|
+
renderTemplates(full, ctx);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (!entry.endsWith(".tmpl")) continue;
|
|
64
|
+
let content = readFileSync(full, "utf8");
|
|
65
|
+
for (const [key, value] of Object.entries(ctx)) {
|
|
66
|
+
content = content.split(`{{${key}}}`).join(value);
|
|
67
|
+
}
|
|
68
|
+
writeFileSync(full.replace(/\.tmpl$/, ""), content);
|
|
69
|
+
unlinkSync(full);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function createProject(args) {
|
|
73
|
+
const target = resolve(args.dir);
|
|
74
|
+
if (existsSync(target)) {
|
|
75
|
+
throw new Error(`mountly-mcp: target directory already exists: ${target}`);
|
|
76
|
+
}
|
|
77
|
+
const tplDir = join(TEMPLATES, args.framework);
|
|
78
|
+
if (!existsSync(tplDir)) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`mountly-mcp: template for framework "${args.framework}" is missing at ${tplDir}`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
mkdirSync(target, { recursive: true });
|
|
84
|
+
cpSync(tplDir, target, { recursive: true });
|
|
85
|
+
const slug = slugify(args.name) || "app";
|
|
86
|
+
renderTemplates(target, {
|
|
87
|
+
name: args.name,
|
|
88
|
+
slug,
|
|
89
|
+
uri: `ui://${slug}/dashboard`,
|
|
90
|
+
framework: args.framework,
|
|
91
|
+
mcpVersion: ownVersion()
|
|
92
|
+
});
|
|
93
|
+
process.stdout.write(
|
|
94
|
+
[
|
|
95
|
+
"",
|
|
96
|
+
`\u2713 Created MCP App ${args.name} (${args.framework}) at ${target}`,
|
|
97
|
+
"",
|
|
98
|
+
"Next steps:",
|
|
99
|
+
` cd ${args.dir}`,
|
|
100
|
+
" pnpm install",
|
|
101
|
+
" pnpm dev # sandboxed host + live reload",
|
|
102
|
+
" pnpm verify # conformance check",
|
|
103
|
+
"",
|
|
104
|
+
"Do not clone the Mountly monorepo for greenfield apps \u2014 this scaffold is the canonical path.",
|
|
105
|
+
""
|
|
106
|
+
].join("\n")
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
createProject,
|
|
111
|
+
parseCreateArgs
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/create.ts"],"sourcesContent":["import {\n cpSync,\n existsSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n/** Package root: dist/cli.js → .. ; src/cli.ts during tests → .. when run via dist. */\nconst PACKAGE_ROOT = resolve(__dirname, \"..\");\nconst TEMPLATES = resolve(PACKAGE_ROOT, \"templates\");\n\n/**\n * The version of mountly-mcp doing the scaffolding, so the generated\n * package.json pins the API this CLI actually ships.\n *\n * Hardcoding the pin in the templates meant a major release left them behind:\n * `^3.1.0` cannot resolve to 4.0.0, so every scaffolded app installed a\n * package without the exports its own vite.config imported.\n */\nfunction ownVersion(): string {\n const pkg = JSON.parse(readFileSync(join(PACKAGE_ROOT, \"package.json\"), \"utf8\")) as {\n version?: string;\n };\n if (!pkg.version) throw new Error(\"mountly-mcp: cannot read own version from package.json\");\n return pkg.version;\n}\n\nconst SUPPORTED = [\"react\", \"vue\", \"svelte\", \"vanilla\"] as const;\ntype Framework = (typeof SUPPORTED)[number];\n\nexport interface CreateArgs {\n name: string;\n framework: Framework;\n dir: string;\n}\n\nexport function parseCreateArgs(argv: ReadonlyArray<string>): CreateArgs {\n const name = argv[0];\n if (!name || name.startsWith(\"-\")) {\n throw new Error(\n \"mountly-mcp create requires a project name. e.g. mountly-mcp create my-app --framework react\",\n );\n }\n\n let framework: Framework = \"react\";\n let dir = `./${name}`;\n\n for (let i = 1; i < argv.length; i += 1) {\n const arg = argv[i];\n if (arg === \"--framework\" || arg === \"-f\") {\n const value = argv[++i];\n if (value === undefined) throw new Error(`mountly-mcp: ${arg} needs a value`);\n if (!SUPPORTED.includes(value as Framework)) {\n throw new Error(\n `mountly-mcp: framework \"${value}\" not supported. Available: ${SUPPORTED.join(\", \")}`,\n );\n }\n framework = value as Framework;\n } else if (arg === \"--dir\") {\n const value = argv[++i];\n if (value === undefined) throw new Error(\"mountly-mcp: --dir needs a value\");\n dir = value;\n } else {\n throw new Error(`mountly-mcp: unknown create option '${arg}'`);\n }\n }\n\n return { name, framework, dir };\n}\n\nfunction slugify(name: string): string {\n return name\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nfunction renderTemplates(dir: string, ctx: Record<string, string>): void {\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry);\n if (statSync(full).isDirectory()) {\n renderTemplates(full, ctx);\n continue;\n }\n if (!entry.endsWith(\".tmpl\")) continue;\n let content = readFileSync(full, \"utf8\");\n for (const [key, value] of Object.entries(ctx)) {\n content = content.split(`{{${key}}}`).join(value);\n }\n writeFileSync(full.replace(/\\.tmpl$/, \"\"), content);\n unlinkSync(full);\n }\n}\n\nexport async function createProject(args: CreateArgs): Promise<void> {\n const target = resolve(args.dir);\n if (existsSync(target)) {\n throw new Error(`mountly-mcp: target directory already exists: ${target}`);\n }\n\n const tplDir = join(TEMPLATES, args.framework);\n if (!existsSync(tplDir)) {\n throw new Error(\n `mountly-mcp: template for framework \"${args.framework}\" is missing at ${tplDir}`,\n );\n }\n\n mkdirSync(target, { recursive: true });\n cpSync(tplDir, target, { recursive: true });\n\n const slug = slugify(args.name) || \"app\";\n renderTemplates(target, {\n name: args.name,\n slug,\n uri: `ui://${slug}/dashboard`,\n framework: args.framework,\n mcpVersion: ownVersion(),\n });\n\n process.stdout.write(\n [\n \"\",\n `✓ Created MCP App ${args.name} (${args.framework}) at ${target}`,\n \"\",\n \"Next steps:\",\n ` cd ${args.dir}`,\n \" pnpm install\",\n \" pnpm dev # sandboxed host + live reload\",\n \" pnpm verify # conformance check\",\n \"\",\n \"Do not clone the Mountly monorepo for greenfield apps — this scaffold is the canonical path.\",\n \"\",\n ].join(\"\\n\"),\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/dev/host-entry.js
CHANGED
|
@@ -18261,8 +18261,8 @@ container holding the app. Specify either width or maxWidth, and either height o
|
|
|
18261
18261
|
bridge.onsandboxready = async () => {
|
|
18262
18262
|
log("in", "ui/notifications/sandbox-proxy-ready");
|
|
18263
18263
|
const [html, declaration] = await Promise.all([
|
|
18264
|
-
fetch("/
|
|
18265
|
-
fetch("/
|
|
18264
|
+
fetch("/view.html").then((response) => response.text()),
|
|
18265
|
+
fetch("/view.meta.json").then((response) => response.json())
|
|
18266
18266
|
]);
|
|
18267
18267
|
await bridge.sendSandboxResourceReady({
|
|
18268
18268
|
html,
|
package/dist/dev/index.d.ts
CHANGED
package/dist/dev/index.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./types.js";
|
|
2
2
|
export * from "./schema.js";
|
|
3
|
+
export * from "./publish.js";
|
|
3
4
|
export { runBridge, App, PostMessageTransport } from "./bridge/index.js";
|
|
4
5
|
export type { RunBridgeOptions, RunningBridge } from "./bridge/index.js";
|
|
5
6
|
export { sandboxProxyHtml } from "./sandbox/index.js";
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
sandboxProxyHtml
|
|
3
3
|
} from "./chunk-6QHKKFO3.js";
|
|
4
|
+
import {
|
|
5
|
+
MCP_VIEW_GLOBAL_KEY,
|
|
6
|
+
getPublishedMcpView,
|
|
7
|
+
publishMcpView
|
|
8
|
+
} from "./chunk-SADNMGP6.js";
|
|
4
9
|
import {
|
|
5
10
|
App,
|
|
6
11
|
PostMessageTransport,
|
|
7
12
|
runBridge
|
|
8
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-JVQZGKBL.js";
|
|
9
14
|
import {
|
|
10
15
|
EXTENSION_ID,
|
|
11
16
|
LATEST_PROTOCOL_VERSION,
|
|
@@ -13,7 +18,7 @@ import {
|
|
|
13
18
|
MCP_ERROR_CODES,
|
|
14
19
|
RESOURCE_MIME_TYPE,
|
|
15
20
|
RESOURCE_URI_META_KEY
|
|
16
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-M4WFJR3C.js";
|
|
17
22
|
import "./chunk-YJA4H6IB.js";
|
|
18
23
|
export {
|
|
19
24
|
App,
|
|
@@ -22,8 +27,11 @@ export {
|
|
|
22
27
|
LATEST_PROTOCOL_VERSION as MCP_APPS_PROTOCOL_VERSION,
|
|
23
28
|
MCP_APPS_URI_SCHEME,
|
|
24
29
|
MCP_ERROR_CODES,
|
|
30
|
+
MCP_VIEW_GLOBAL_KEY,
|
|
25
31
|
PostMessageTransport,
|
|
26
32
|
RESOURCE_URI_META_KEY,
|
|
33
|
+
getPublishedMcpView,
|
|
34
|
+
publishMcpView,
|
|
27
35
|
runBridge,
|
|
28
36
|
sandboxProxyHtml
|
|
29
37
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type ComponentMap } from "@json-render/react";
|
|
2
2
|
import type { Catalog, SchemaDefinition } from "@json-render/core";
|
|
3
3
|
import type { App } from "@modelcontextprotocol/ext-apps";
|
|
4
|
-
import type { AdapterOptions
|
|
4
|
+
import type { AdapterOptions } from "mountly/adapter";
|
|
5
|
+
import type { McpView } from "../types.js";
|
|
5
6
|
/** The catalog-data constraint createRenderer infers against. */
|
|
6
7
|
type CatalogData = {
|
|
7
8
|
components: Record<string, {
|
|
@@ -21,7 +22,7 @@ export type ActionRouter = (name: string, params: Record<string, unknown> | unde
|
|
|
21
22
|
export declare const defaultActionRouter: ActionRouter;
|
|
22
23
|
/**
|
|
23
24
|
* Identity helper that types a components map against a catalog (so it can be
|
|
24
|
-
* defined once and reused by both `
|
|
25
|
+
* defined once and reused by both `createGenerativeView` and `createRenderer`)
|
|
25
26
|
* without losing per-component prop inference.
|
|
26
27
|
*
|
|
27
28
|
* ```ts
|
|
@@ -31,7 +32,7 @@ export declare const defaultActionRouter: ActionRouter;
|
|
|
31
32
|
* ```
|
|
32
33
|
*/
|
|
33
34
|
export declare function defineComponents<TDef extends SchemaDefinition, TCatalog extends CatalogData>(_catalog: Catalog<TDef, TCatalog>, components: ComponentMap<TCatalog["components"]>): ComponentMap<TCatalog["components"]>;
|
|
34
|
-
export interface
|
|
35
|
+
export interface GenerativeViewOptions<TDef extends SchemaDefinition, TCatalog extends CatalogData> extends AdapterOptions {
|
|
35
36
|
/** The json-render catalog — the vocabulary the agent may compose. */
|
|
36
37
|
catalog: Catalog<TDef, TCatalog>;
|
|
37
38
|
/** Native component implementations for each catalog type. */
|
|
@@ -43,17 +44,14 @@ export interface GenerativeWidgetOptions<TDef extends SchemaDefinition, TCatalog
|
|
|
43
44
|
onAction?: ActionRouter;
|
|
44
45
|
}
|
|
45
46
|
/**
|
|
46
|
-
* Turn a json-render catalog + components into a mountly MCP Apps
|
|
47
|
+
* Turn a json-render catalog + components into a mountly MCP Apps View.
|
|
47
48
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* actions back to the MCP host. One call replaces the manual
|
|
51
|
-
* catalog→registry→renderer→state→action-bridge wiring.
|
|
49
|
+
* Reads the spec from the tool result, renders it natively (resolving `$state`
|
|
50
|
+
* from `spec.state`), and routes actions back to the MCP host.
|
|
52
51
|
*
|
|
53
52
|
* ```ts
|
|
54
|
-
*
|
|
55
|
-
* (globalThis as { __mountlyMcpWidget__?: unknown }).__mountlyMcpWidget__ = widget;
|
|
53
|
+
* createGenerativeView({ catalog, components, styles, shadow: true });
|
|
56
54
|
* ```
|
|
57
55
|
*/
|
|
58
|
-
export declare function
|
|
56
|
+
export declare function createGenerativeView<TDef extends SchemaDefinition, TCatalog extends CatalogData>(options: GenerativeViewOptions<TDef, TCatalog>): McpView;
|
|
59
57
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { type ActionRouter, type
|
|
1
|
+
export { type ActionRouter, type GenerativeViewOptions, createGenerativeView, defaultActionRouter, defineComponents, } from "./generative-view.js";
|
|
2
2
|
export { type JsonRenderToolOptions, type RegisterJsonRenderResourceOptions, type RegisterJsonRenderToolOptions, type CreateJsonRenderMcpAppOptions, createJsonRenderMcpApp, registerJsonRenderTool, registerJsonRenderResource, } from "./mcp.js";
|
|
3
3
|
export { type BuildAppHtmlOptions, type UseJsonRenderAppOptions, type UseJsonRenderAppReturn, buildAppHtml, useJsonRenderApp, } from "./app.js";
|
|
4
4
|
export { createRenderer } from "@json-render/react";
|
|
@@ -12,17 +12,18 @@ import {
|
|
|
12
12
|
specToPatchLines
|
|
13
13
|
} from "../chunk-5EHNJCMF.js";
|
|
14
14
|
import {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
createMcpView,
|
|
16
|
+
useMcpApp,
|
|
17
17
|
useToolResult
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-AEVQKFXX.js";
|
|
19
|
+
import "../chunk-SADNMGP6.js";
|
|
19
20
|
import {
|
|
20
21
|
buildAppHtml,
|
|
21
22
|
useJsonRenderApp
|
|
22
23
|
} from "../chunk-33DMFKKD.js";
|
|
23
24
|
import "../chunk-YJA4H6IB.js";
|
|
24
25
|
|
|
25
|
-
// src/json-render/
|
|
26
|
+
// src/json-render/generative-view.tsx
|
|
26
27
|
import { createElement } from "react";
|
|
27
28
|
import { createRenderer } from "@json-render/react";
|
|
28
29
|
var defaultActionRouter = (name, params, mcp) => {
|
|
@@ -36,11 +37,11 @@ var defaultActionRouter = (name, params, mcp) => {
|
|
|
36
37
|
function defineComponents(_catalog, components) {
|
|
37
38
|
return components;
|
|
38
39
|
}
|
|
39
|
-
function
|
|
40
|
+
function createGenerativeView(options) {
|
|
40
41
|
const { catalog, components, onAction = defaultActionRouter, ...adapter } = options;
|
|
41
42
|
const Rendered = createRenderer(catalog, components);
|
|
42
43
|
function GenerativeView() {
|
|
43
|
-
const mcp =
|
|
44
|
+
const mcp = useMcpApp();
|
|
44
45
|
const result = useToolResult();
|
|
45
46
|
const spec = result?.structuredContent?.spec ?? null;
|
|
46
47
|
if (!spec) return null;
|
|
@@ -50,7 +51,7 @@ function createGenerativeWidget(options) {
|
|
|
50
51
|
onAction: (name, params) => onAction(name, params, mcp)
|
|
51
52
|
});
|
|
52
53
|
}
|
|
53
|
-
return
|
|
54
|
+
return createMcpView(GenerativeView, adapter);
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
// src/json-render/index.ts
|
|
@@ -100,7 +101,7 @@ export {
|
|
|
100
101
|
buildAppHtml,
|
|
101
102
|
compileSpecStream,
|
|
102
103
|
compileTextStreamToSpecs,
|
|
103
|
-
|
|
104
|
+
createGenerativeView,
|
|
104
105
|
createJsonRenderMcpApp,
|
|
105
106
|
createJsonRenderTransform,
|
|
106
107
|
createRenderer2 as createRenderer,
|