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,199 @@
1
+ // src/artifact/index.ts
2
+ import { readFile, writeFile } from "fs/promises";
3
+ import { dirname, isAbsolute, relative, resolve } from "path";
4
+ import {
5
+ McpUiDisplayModeSchema,
6
+ McpUiResourceMetaSchema,
7
+ RESOURCE_MIME_TYPE,
8
+ LATEST_PROTOCOL_VERSION
9
+ } from "@modelcontextprotocol/ext-apps";
10
+ var MCP_APP_MANIFEST_VERSION = 1;
11
+ var MCP_APP_MANIFEST_FILE = "mountly-mcp.manifest.json";
12
+ var McpAppConformanceError = class extends Error {
13
+ constructor(diagnostics) {
14
+ super(diagnostics.map((d) => `${d.code}: ${d.message}`).join("\n"));
15
+ this.name = "McpAppConformanceError";
16
+ this.diagnostics = diagnostics;
17
+ }
18
+ };
19
+ function object(value) {
20
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
21
+ }
22
+ function inspectMcpResourceDeclaration(value, source) {
23
+ const diagnostics = [];
24
+ const add = (code, message) => {
25
+ diagnostics.push({ severity: "error", code, message, source });
26
+ };
27
+ const candidate = object(value);
28
+ if (!candidate) {
29
+ add("artifact/not-object", "resource declaration must be an object");
30
+ return diagnostics;
31
+ }
32
+ if (candidate.protocolVersion !== LATEST_PROTOCOL_VERSION) {
33
+ add(
34
+ "artifact/protocol-version",
35
+ `sidecar protocolVersion must be '${LATEST_PROTOCOL_VERSION}' (received '${String(candidate.protocolVersion)}')`
36
+ );
37
+ }
38
+ if (typeof candidate.uri !== "string" || !candidate.uri.startsWith("ui://")) {
39
+ add("artifact/uri", `uri must use the 'ui://' scheme (received '${String(candidate.uri)}')`);
40
+ }
41
+ if (typeof candidate.name !== "string" || candidate.name.trim() === "") {
42
+ add("artifact/name", "name must be a non-empty string");
43
+ }
44
+ if (candidate.mimeType !== RESOURCE_MIME_TYPE) {
45
+ add(
46
+ "artifact/mime-type",
47
+ `sidecar mimeType must be '${RESOURCE_MIME_TYPE}' (received '${String(candidate.mimeType)}')`
48
+ );
49
+ }
50
+ if (typeof candidate.awaitToolResult !== "boolean") {
51
+ add("artifact/await-tool-result", "awaitToolResult must be a boolean");
52
+ }
53
+ if (!Array.isArray(candidate.displayModes) || candidate.displayModes.length === 0 || candidate.displayModes.some((mode) => !McpUiDisplayModeSchema.safeParse(mode).success)) {
54
+ add("artifact/display-modes", "displayModes must contain supported MCP Apps display modes");
55
+ }
56
+ const meta = object(candidate._meta);
57
+ const ui = meta && object(meta.ui);
58
+ if (!ui || !McpUiResourceMetaSchema.safeParse(ui).success) {
59
+ add("artifact/resource-meta", "_meta.ui must be valid MCP Apps resource metadata");
60
+ }
61
+ return diagnostics;
62
+ }
63
+ function parseMcpResourceDeclaration(value, source) {
64
+ const diagnostics = inspectMcpResourceDeclaration(value, source);
65
+ if (diagnostics.length > 0) throw new McpAppConformanceError(diagnostics);
66
+ return value;
67
+ }
68
+ async function readMcpAppArtifact(htmlPath, expectedUri) {
69
+ const absoluteHtmlPath = resolve(htmlPath);
70
+ const metaPath = `${absoluteHtmlPath}.meta.json`;
71
+ const [html, raw] = await Promise.all([
72
+ readFile(absoluteHtmlPath, "utf8"),
73
+ readFile(metaPath, "utf8")
74
+ ]);
75
+ if (!/^\s*<!doctype html>/i.test(html)) {
76
+ throw new McpAppConformanceError([
77
+ {
78
+ severity: "error",
79
+ code: "artifact/html5",
80
+ message: "View HTML must start with an HTML5 doctype",
81
+ source: absoluteHtmlPath
82
+ }
83
+ ]);
84
+ }
85
+ let decoded;
86
+ try {
87
+ decoded = JSON.parse(raw);
88
+ } catch (error) {
89
+ throw new McpAppConformanceError([
90
+ {
91
+ severity: "error",
92
+ code: "artifact/meta-json",
93
+ message: `metadata is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
94
+ source: metaPath
95
+ }
96
+ ]);
97
+ }
98
+ const declaration = parseMcpResourceDeclaration(decoded, metaPath);
99
+ if (expectedUri !== void 0 && declaration.uri !== expectedUri) {
100
+ throw new McpAppConformanceError([
101
+ {
102
+ severity: "error",
103
+ code: "artifact/uri-mismatch",
104
+ message: `registration uri '${expectedUri}' does not match sidecar uri '${declaration.uri}'`,
105
+ source: metaPath
106
+ }
107
+ ]);
108
+ }
109
+ return {
110
+ name: declaration.name,
111
+ uri: declaration.uri,
112
+ htmlPath: absoluteHtmlPath,
113
+ metaPath,
114
+ declaration
115
+ };
116
+ }
117
+ function manifestPath(path, base) {
118
+ return isAbsolute(path) ? path : resolve(base, path);
119
+ }
120
+ async function writeMcpAppManifest(path, artifacts) {
121
+ const absolutePath = resolve(path);
122
+ const base = dirname(absolutePath);
123
+ const names = /* @__PURE__ */ new Set();
124
+ const uris = /* @__PURE__ */ new Set();
125
+ for (const artifact of artifacts) {
126
+ if (names.has(artifact.name))
127
+ throw new Error(`mountly-mcp: duplicate app name '${artifact.name}'`);
128
+ if (uris.has(artifact.uri)) throw new Error(`mountly-mcp: duplicate app uri '${artifact.uri}'`);
129
+ names.add(artifact.name);
130
+ uris.add(artifact.uri);
131
+ }
132
+ const manifest = {
133
+ formatVersion: MCP_APP_MANIFEST_VERSION,
134
+ protocolVersion: LATEST_PROTOCOL_VERSION,
135
+ apps: artifacts.map((artifact) => ({
136
+ name: artifact.name,
137
+ uri: artifact.uri,
138
+ html: relative(base, artifact.htmlPath),
139
+ meta: relative(base, artifact.metaPath),
140
+ declaration: artifact.declaration
141
+ }))
142
+ };
143
+ await writeFile(absolutePath, JSON.stringify(manifest, null, 2), "utf8");
144
+ return manifest;
145
+ }
146
+ async function readMcpAppManifest(path) {
147
+ const absolutePath = resolve(path);
148
+ const base = dirname(absolutePath);
149
+ let decoded;
150
+ try {
151
+ decoded = JSON.parse(await readFile(absolutePath, "utf8"));
152
+ } catch (error) {
153
+ throw new Error(
154
+ `mountly-mcp: could not read manifest '${absolutePath}': ${error instanceof Error ? error.message : String(error)}`
155
+ );
156
+ }
157
+ const candidate = object(decoded);
158
+ if (candidate?.formatVersion !== MCP_APP_MANIFEST_VERSION || candidate.protocolVersion !== LATEST_PROTOCOL_VERSION || !Array.isArray(candidate.apps)) {
159
+ throw new Error(
160
+ `mountly-mcp: '${absolutePath}' is not a supported mountly-mcp manifest (expected formatVersion ${MCP_APP_MANIFEST_VERSION})`
161
+ );
162
+ }
163
+ const manifest = decoded;
164
+ const names = /* @__PURE__ */ new Set();
165
+ const uris = /* @__PURE__ */ new Set();
166
+ const artifacts = [];
167
+ for (const entry of manifest.apps) {
168
+ if (names.has(entry.name)) throw new Error(`mountly-mcp: duplicate app name '${entry.name}'`);
169
+ if (uris.has(entry.uri)) throw new Error(`mountly-mcp: duplicate app uri '${entry.uri}'`);
170
+ names.add(entry.name);
171
+ uris.add(entry.uri);
172
+ const declaration = parseMcpResourceDeclaration(entry.declaration, absolutePath);
173
+ if (declaration.name !== entry.name || declaration.uri !== entry.uri) {
174
+ throw new Error(
175
+ `mountly-mcp: manifest identity does not match declaration for '${entry.name}'`
176
+ );
177
+ }
178
+ artifacts.push({
179
+ name: entry.name,
180
+ uri: entry.uri,
181
+ htmlPath: manifestPath(entry.html, base),
182
+ metaPath: manifestPath(entry.meta, base),
183
+ declaration
184
+ });
185
+ }
186
+ return { path: absolutePath, manifest, artifacts };
187
+ }
188
+
189
+ export {
190
+ MCP_APP_MANIFEST_VERSION,
191
+ MCP_APP_MANIFEST_FILE,
192
+ McpAppConformanceError,
193
+ inspectMcpResourceDeclaration,
194
+ parseMcpResourceDeclaration,
195
+ readMcpAppArtifact,
196
+ writeMcpAppManifest,
197
+ readMcpAppManifest
198
+ };
199
+ //# sourceMappingURL=chunk-DKK26TRS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/artifact/index.ts"],"sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, isAbsolute, relative, resolve } from \"node:path\";\nimport {\n McpUiDisplayModeSchema,\n McpUiResourceMetaSchema,\n RESOURCE_MIME_TYPE,\n LATEST_PROTOCOL_VERSION,\n} from \"@modelcontextprotocol/ext-apps\";\nimport type { McpResourceDeclaration } from \"../types.js\";\n\nexport const MCP_APP_MANIFEST_VERSION = 1 as const;\nexport const MCP_APP_MANIFEST_FILE = \"mountly-mcp.manifest.json\";\n\nexport interface McpAppArtifact {\n /** Developer-facing unique key. */\n name: string;\n /** Protocol identity. */\n uri: string;\n /** Absolute path to the self-contained View HTML. */\n htmlPath: string;\n /** Absolute path to the transitional per-View sidecar. */\n metaPath: string;\n declaration: McpResourceDeclaration;\n}\n\nexport interface McpAppManifestEntry {\n name: string;\n uri: string;\n /** Path relative to the manifest, unless explicitly absolute. */\n html: string;\n /** Path relative to the manifest, unless explicitly absolute. */\n meta: string;\n declaration: McpResourceDeclaration;\n}\n\nexport interface McpAppManifest {\n formatVersion: typeof MCP_APP_MANIFEST_VERSION;\n protocolVersion: typeof LATEST_PROTOCOL_VERSION;\n apps: ReadonlyArray<McpAppManifestEntry>;\n}\n\nexport type ConformanceSeverity = \"error\" | \"warning\";\n\nexport interface ConformanceDiagnostic {\n severity: ConformanceSeverity;\n code: string;\n message: string;\n source?: string;\n}\n\nexport class McpAppConformanceError extends Error {\n readonly diagnostics: ReadonlyArray<ConformanceDiagnostic>;\n\n constructor(diagnostics: ReadonlyArray<ConformanceDiagnostic>) {\n super(diagnostics.map((d) => `${d.code}: ${d.message}`).join(\"\\n\"));\n this.name = \"McpAppConformanceError\";\n this.diagnostics = diagnostics;\n }\n}\n\nfunction object(value: unknown): Record<string, unknown> | undefined {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nexport function inspectMcpResourceDeclaration(\n value: unknown,\n source?: string,\n): ReadonlyArray<ConformanceDiagnostic> {\n const diagnostics: ConformanceDiagnostic[] = [];\n const add = (code: string, message: string): void => {\n diagnostics.push({ severity: \"error\", code, message, source });\n };\n const candidate = object(value);\n if (!candidate) {\n add(\"artifact/not-object\", \"resource declaration must be an object\");\n return diagnostics;\n }\n if (candidate.protocolVersion !== LATEST_PROTOCOL_VERSION) {\n add(\n \"artifact/protocol-version\",\n `sidecar protocolVersion must be '${LATEST_PROTOCOL_VERSION}' (received '${String(candidate.protocolVersion)}')`,\n );\n }\n if (typeof candidate.uri !== \"string\" || !candidate.uri.startsWith(\"ui://\")) {\n add(\"artifact/uri\", `uri must use the 'ui://' scheme (received '${String(candidate.uri)}')`);\n }\n if (typeof candidate.name !== \"string\" || candidate.name.trim() === \"\") {\n add(\"artifact/name\", \"name must be a non-empty string\");\n }\n if (candidate.mimeType !== RESOURCE_MIME_TYPE) {\n add(\n \"artifact/mime-type\",\n `sidecar mimeType must be '${RESOURCE_MIME_TYPE}' (received '${String(candidate.mimeType)}')`,\n );\n }\n if (typeof candidate.awaitToolResult !== \"boolean\") {\n add(\"artifact/await-tool-result\", \"awaitToolResult must be a boolean\");\n }\n if (\n !Array.isArray(candidate.displayModes) ||\n candidate.displayModes.length === 0 ||\n candidate.displayModes.some((mode) => !McpUiDisplayModeSchema.safeParse(mode).success)\n ) {\n add(\"artifact/display-modes\", \"displayModes must contain supported MCP Apps display modes\");\n }\n const meta = object(candidate._meta);\n const ui = meta && object(meta.ui);\n if (!ui || !McpUiResourceMetaSchema.safeParse(ui).success) {\n add(\"artifact/resource-meta\", \"_meta.ui must be valid MCP Apps resource metadata\");\n }\n return diagnostics;\n}\n\nexport function parseMcpResourceDeclaration(\n value: unknown,\n source?: string,\n): McpResourceDeclaration {\n const diagnostics = inspectMcpResourceDeclaration(value, source);\n if (diagnostics.length > 0) throw new McpAppConformanceError(diagnostics);\n return value as McpResourceDeclaration;\n}\n\nexport async function readMcpAppArtifact(\n htmlPath: string,\n expectedUri?: string,\n): Promise<McpAppArtifact> {\n const absoluteHtmlPath = resolve(htmlPath);\n const metaPath = `${absoluteHtmlPath}.meta.json`;\n const [html, raw] = await Promise.all([\n readFile(absoluteHtmlPath, \"utf8\"),\n readFile(metaPath, \"utf8\"),\n ]);\n if (!/^\\s*<!doctype html>/i.test(html)) {\n throw new McpAppConformanceError([\n {\n severity: \"error\",\n code: \"artifact/html5\",\n message: \"View HTML must start with an HTML5 doctype\",\n source: absoluteHtmlPath,\n },\n ]);\n }\n let decoded: unknown;\n try {\n decoded = JSON.parse(raw);\n } catch (error) {\n throw new McpAppConformanceError([\n {\n severity: \"error\",\n code: \"artifact/meta-json\",\n message: `metadata is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,\n source: metaPath,\n },\n ]);\n }\n const declaration = parseMcpResourceDeclaration(decoded, metaPath);\n if (expectedUri !== undefined && declaration.uri !== expectedUri) {\n throw new McpAppConformanceError([\n {\n severity: \"error\",\n code: \"artifact/uri-mismatch\",\n message: `registration uri '${expectedUri}' does not match sidecar uri '${declaration.uri}'`,\n source: metaPath,\n },\n ]);\n }\n return {\n name: declaration.name,\n uri: declaration.uri,\n htmlPath: absoluteHtmlPath,\n metaPath,\n declaration,\n };\n}\n\nfunction manifestPath(path: string, base: string): string {\n return isAbsolute(path) ? path : resolve(base, path);\n}\n\nexport async function writeMcpAppManifest(\n path: string,\n artifacts: ReadonlyArray<McpAppArtifact>,\n): Promise<McpAppManifest> {\n const absolutePath = resolve(path);\n const base = dirname(absolutePath);\n const names = new Set<string>();\n const uris = new Set<string>();\n for (const artifact of artifacts) {\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 }\n const manifest: McpAppManifest = {\n formatVersion: MCP_APP_MANIFEST_VERSION,\n protocolVersion: LATEST_PROTOCOL_VERSION,\n apps: artifacts.map((artifact) => ({\n name: artifact.name,\n uri: artifact.uri,\n html: relative(base, artifact.htmlPath),\n meta: relative(base, artifact.metaPath),\n declaration: artifact.declaration,\n })),\n };\n await writeFile(absolutePath, JSON.stringify(manifest, null, 2), \"utf8\");\n return manifest;\n}\n\nexport async function readMcpAppManifest(path: string): Promise<{\n path: string;\n manifest: McpAppManifest;\n artifacts: ReadonlyArray<McpAppArtifact>;\n}> {\n const absolutePath = resolve(path);\n const base = dirname(absolutePath);\n let decoded: unknown;\n try {\n decoded = JSON.parse(await readFile(absolutePath, \"utf8\"));\n } catch (error) {\n throw new Error(\n `mountly-mcp: could not read manifest '${absolutePath}': ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n const candidate = object(decoded);\n if (\n candidate?.formatVersion !== MCP_APP_MANIFEST_VERSION ||\n candidate.protocolVersion !== LATEST_PROTOCOL_VERSION ||\n !Array.isArray(candidate.apps)\n ) {\n throw new Error(\n `mountly-mcp: '${absolutePath}' is not a supported mountly-mcp manifest (expected formatVersion ${MCP_APP_MANIFEST_VERSION})`,\n );\n }\n const manifest = decoded as McpAppManifest;\n const names = new Set<string>();\n const uris = new Set<string>();\n const artifacts: McpAppArtifact[] = [];\n for (const entry of manifest.apps) {\n if (names.has(entry.name)) throw new Error(`mountly-mcp: duplicate app name '${entry.name}'`);\n if (uris.has(entry.uri)) throw new Error(`mountly-mcp: duplicate app uri '${entry.uri}'`);\n names.add(entry.name);\n uris.add(entry.uri);\n const declaration = parseMcpResourceDeclaration(entry.declaration, absolutePath);\n if (declaration.name !== entry.name || declaration.uri !== entry.uri) {\n throw new Error(\n `mountly-mcp: manifest identity does not match declaration for '${entry.name}'`,\n );\n }\n artifacts.push({\n name: entry.name,\n uri: entry.uri,\n htmlPath: manifestPath(entry.html, base),\n metaPath: manifestPath(entry.meta, base),\n declaration,\n });\n }\n return { path: absolutePath, manifest, artifacts };\n}\n"],"mappings":"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","names":[]}
@@ -7,6 +7,7 @@ import {
7
7
  import { EXTENSION_ID } from "@modelcontextprotocol/ext-apps/server";
8
8
  var MCP_APPS_URI_SCHEME = "ui://";
9
9
  var MCP_ERROR_CODES = {
10
+ INITIALIZE_FAILED: "mountly-mcp/initialize-failed",
10
11
  INITIALIZE_TIMEOUT: "mountly-mcp/initialize-timeout",
11
12
  TOOL_CALL_FAILED: "mountly-mcp/tool-call-failed",
12
13
  WIDGET_MOUNT_THREW: "mountly-mcp/widget-mount-threw",
@@ -21,4 +22,4 @@ export {
21
22
  RESOURCE_URI_META_KEY,
22
23
  EXTENSION_ID
23
24
  };
24
- //# sourceMappingURL=chunk-A64ZILAS.js.map
25
+ //# sourceMappingURL=chunk-DQLQOMDM.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/schema.ts"],"sourcesContent":["/**\n * Spec constants — re-exported from the canonical SDK so they cannot drift.\n * See `@modelcontextprotocol/ext-apps` for the source of truth.\n */\nexport {\n LATEST_PROTOCOL_VERSION as MCP_APPS_PROTOCOL_VERSION,\n RESOURCE_MIME_TYPE as MCP_APPS_MIME,\n RESOURCE_URI_META_KEY,\n} from \"@modelcontextprotocol/ext-apps\";\n\nexport { EXTENSION_ID } from \"@modelcontextprotocol/ext-apps/server\";\n\nexport const MCP_APPS_URI_SCHEME = \"ui://\" as const;\n\n/**\n * Implementation-specific error codes for mountly-mcp's host/bridge layer.\n * The spec doesn't standardize these; they appear in `notifications/message`\n * params so hosts can categorize widget failures.\n */\nexport const MCP_ERROR_CODES = {\n INITIALIZE_TIMEOUT: \"mountly-mcp/initialize-timeout\",\n TOOL_CALL_FAILED: \"mountly-mcp/tool-call-failed\",\n WIDGET_MOUNT_THREW: \"mountly-mcp/widget-mount-threw\",\n INVALID_ORIGIN: \"mountly-mcp/invalid-origin\",\n} as const;\n\nexport type McpErrorCode = (typeof MCP_ERROR_CODES)[keyof typeof MCP_ERROR_CODES];\n"],"mappings":";AAIA;AAAA,EAC6B;AAAA,EACL;AAAA,EACtB;AAAA,OACK;AAEP,SAAS,oBAAoB;AAEtB,IAAM,sBAAsB;AAO5B,IAAM,kBAAkB;AAAA,EAC7B,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,gBAAgB;AAClB;","names":[]}
1
+ {"version":3,"sources":["../src/schema.ts"],"sourcesContent":["/**\n * Spec constants — re-exported from the canonical SDK so they cannot drift.\n * See `@modelcontextprotocol/ext-apps` for the source of truth.\n */\nexport {\n LATEST_PROTOCOL_VERSION as MCP_APPS_PROTOCOL_VERSION,\n RESOURCE_MIME_TYPE as MCP_APPS_MIME,\n RESOURCE_URI_META_KEY,\n} from \"@modelcontextprotocol/ext-apps\";\n\nexport { EXTENSION_ID } from \"@modelcontextprotocol/ext-apps/server\";\n\nexport const MCP_APPS_URI_SCHEME = \"ui://\" as const;\n\n/**\n * Implementation-specific error codes for mountly-mcp's host/bridge layer.\n * The spec doesn't standardize these; they appear in `notifications/message`\n * params so hosts can categorize widget failures.\n */\nexport const MCP_ERROR_CODES = {\n INITIALIZE_FAILED: \"mountly-mcp/initialize-failed\",\n INITIALIZE_TIMEOUT: \"mountly-mcp/initialize-timeout\",\n TOOL_CALL_FAILED: \"mountly-mcp/tool-call-failed\",\n WIDGET_MOUNT_THREW: \"mountly-mcp/widget-mount-threw\",\n INVALID_ORIGIN: \"mountly-mcp/invalid-origin\",\n} as const;\n\nexport type McpErrorCode = (typeof MCP_ERROR_CODES)[keyof typeof MCP_ERROR_CODES];\n"],"mappings":";AAIA;AAAA,EAC6B;AAAA,EACL;AAAA,EACtB;AAAA,OACK;AAEP,SAAS,oBAAoB;AAEtB,IAAM,sBAAsB;AAO5B,IAAM,kBAAkB;AAAA,EAC7B,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,gBAAgB;AAClB;","names":[]}
@@ -0,0 +1,260 @@
1
+ import {
2
+ escapeInlineScript,
3
+ serializeInlineScriptValue
4
+ } from "./chunk-YJA4H6IB.js";
5
+
6
+ // src/dev/index.ts
7
+ import { existsSync } from "fs";
8
+ import { readFile } from "fs/promises";
9
+ import { createServer } from "http";
10
+ import { fileURLToPath } from "url";
11
+ function escapeHtml(value) {
12
+ return value.replace(
13
+ /[&<>"']/g,
14
+ (character) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[character]
15
+ );
16
+ }
17
+ var runtimeCache = /* @__PURE__ */ new Map();
18
+ function loadRuntime(name) {
19
+ const cached = runtimeCache.get(name);
20
+ if (cached) return cached;
21
+ const loading = (async () => {
22
+ try {
23
+ return await readFile(
24
+ fileURLToPath(import.meta.resolve(`mountly-mcp/dev/${name}.js`)),
25
+ "utf8"
26
+ );
27
+ } catch {
28
+ }
29
+ const entry = fileURLToPath(new URL(`./${name}.ts`, import.meta.url));
30
+ if (!existsSync(entry)) {
31
+ throw new Error(
32
+ `mountly-mcp dev: ${name} runtime missing. Expected the packaged 'mountly-mcp/dev/${name}.js' export, or ${entry} when running from source.`
33
+ );
34
+ }
35
+ const { build } = await import("vite");
36
+ const result = await build({
37
+ configFile: false,
38
+ logLevel: "silent",
39
+ build: {
40
+ write: false,
41
+ target: "es2022",
42
+ minify: false,
43
+ lib: { entry, formats: ["iife"], name: "__mountlyMcpDevRuntime" }
44
+ }
45
+ });
46
+ const outputs = Array.isArray(result) ? result : [result];
47
+ const chunk = outputs.flatMap((output) => "output" in output ? output.output : []).find((item) => item.type === "chunk");
48
+ if (!chunk || chunk.type !== "chunk")
49
+ throw new Error(`mountly-mcp dev: failed to bundle ${name}`);
50
+ return chunk.code;
51
+ })();
52
+ runtimeCache.set(name, loading);
53
+ return loading;
54
+ }
55
+ function listenOnFreePort(server, port) {
56
+ return new Promise((resolvePort, reject) => {
57
+ const attempt = (candidate) => {
58
+ const onError = (error) => {
59
+ server.removeListener("listening", onListening);
60
+ if (error.code === "EADDRINUSE" && candidate < port + 50) attempt(candidate + 1);
61
+ else reject(error);
62
+ };
63
+ const onListening = () => {
64
+ server.removeListener("error", onError);
65
+ const address = server.address();
66
+ resolvePort(typeof address === "object" && address !== null ? address.port : candidate);
67
+ };
68
+ server.once("error", onError);
69
+ server.once("listening", onListening);
70
+ server.listen(candidate);
71
+ };
72
+ attempt(port);
73
+ });
74
+ }
75
+ async function startDevHost(options) {
76
+ const { htmlPath, fixtures = {}, toolInput = {}, callTool } = options;
77
+ const metaPath = `${htmlPath}.meta.json`;
78
+ const initialMeta = JSON.parse(await readFile(metaPath, "utf8"));
79
+ const toolName = options.toolName ?? initialMeta.name;
80
+ const [hostRuntime, sandboxRuntime] = await Promise.all([
81
+ loadRuntime("host-entry"),
82
+ loadRuntime("sandbox-entry")
83
+ ]);
84
+ let version = 1;
85
+ let hostOrigin = "";
86
+ const sandboxServer = createServer((_req, res) => {
87
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store" });
88
+ res.end(sandboxProxyShell(hostOrigin, sandboxRuntime));
89
+ });
90
+ const sandboxPort = await listenOnFreePort(
91
+ sandboxServer,
92
+ options.sandboxPort ?? (options.hostPort ?? 5179) + 1
93
+ );
94
+ const sandboxOrigin = `http://localhost:${sandboxPort}`;
95
+ const hostServer = createServer(async (req, res) => {
96
+ const path = new URL(req.url ?? "/", "http://localhost").pathname;
97
+ const send = (status, type, body) => {
98
+ res.writeHead(status, { "Content-Type": type, "Cache-Control": "no-store" });
99
+ res.end(body);
100
+ };
101
+ try {
102
+ if (path === "/call" && req.method === "POST") {
103
+ if (!callTool) return send(400, "text/plain", "no server connected");
104
+ const body = await new Promise((done, fail) => {
105
+ let raw = "";
106
+ req.on("data", (chunk) => raw += chunk);
107
+ req.on("end", () => done(raw));
108
+ req.on("error", fail);
109
+ });
110
+ const { name, arguments: args } = JSON.parse(body);
111
+ const result = await callTool(name, args).catch((error) => ({
112
+ isError: true,
113
+ content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }]
114
+ }));
115
+ return send(200, "application/json", JSON.stringify(result));
116
+ }
117
+ if (path === "/") return send(200, "text/html; charset=utf-8", hostPage());
118
+ if (path === "/host.js") return send(200, "text/javascript; charset=utf-8", hostRuntime);
119
+ if (path === "/version") return send(200, "application/json", String(version));
120
+ if (path === "/widget.html")
121
+ return send(200, "text/plain; charset=utf-8", await readFile(htmlPath, "utf8"));
122
+ if (path === "/widget.meta.json")
123
+ return send(200, "application/json", await readFile(metaPath, "utf8"));
124
+ send(404, "text/plain", "not found");
125
+ } catch (error) {
126
+ send(500, "text/plain", error instanceof Error ? error.message : String(error));
127
+ }
128
+ });
129
+ let hostPort;
130
+ try {
131
+ hostPort = await listenOnFreePort(hostServer, options.hostPort ?? 5179);
132
+ } catch (caught) {
133
+ await new Promise((done) => sandboxServer.close(() => done()));
134
+ throw caught;
135
+ }
136
+ hostOrigin = `http://localhost:${hostPort}`;
137
+ function hostPage() {
138
+ return devHostHtml({
139
+ toolName,
140
+ sandboxOrigin,
141
+ fixtures,
142
+ toolInput,
143
+ uri: initialMeta.uri,
144
+ hasServer: callTool !== void 0
145
+ });
146
+ }
147
+ return {
148
+ hostUrl: hostOrigin,
149
+ sandboxUrl: sandboxOrigin,
150
+ reload() {
151
+ version += 1;
152
+ },
153
+ async close() {
154
+ await Promise.all([
155
+ new Promise((done) => hostServer.close(() => done())),
156
+ new Promise((done) => sandboxServer.close(() => done()))
157
+ ]);
158
+ }
159
+ };
160
+ }
161
+ function devHostHtml(options) {
162
+ const { toolName, sandboxOrigin, fixtures, toolInput, uri, hasServer } = options;
163
+ const buttons = Object.keys(fixtures).map(
164
+ (name, index) => `<button class="btn" data-fixture="${escapeHtml(name)}"${index === 0 ? ' data-active="true"' : ""}>${escapeHtml(name)}</button>`
165
+ ).join("\n");
166
+ return `<!doctype html>
167
+ <html lang="en">
168
+ <head>
169
+ <meta charset="utf-8">
170
+ <meta name="viewport" content="width=device-width,initial-scale=1">
171
+ <title>mountly-mcp dev \xB7 ${escapeHtml(toolName)}</title>
172
+ <style>
173
+ :root { color-scheme:light; --bg:#f6f7f9; --fg:#0b0c0f; --muted:#5c6675; --accent:#1d4ed8; --border:#dcdfe5; }
174
+ :root[data-theme="dark"] { color-scheme:dark; --bg:#0b0c0f; --fg:#e6e9ef; --muted:#9aa3b2; --accent:#7aa2ff; --border:#252a33; }
175
+ * { box-sizing:border-box; }
176
+ body { margin:0; min-height:100vh; background:var(--bg); color:var(--fg); font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif; }
177
+ header { padding:18px 24px; border-bottom:1px solid var(--border); display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
178
+ header h1 { margin:0; font-size:16px; font-weight:600; }
179
+ header code { color:var(--muted); font-size:12px; }
180
+ #fixtures { display:flex; gap:8px; flex-wrap:wrap; }
181
+ .spacer { flex:1; }
182
+ .btn { appearance:none; border:1px solid var(--border); background:transparent; color:var(--fg); padding:6px 12px; border-radius:6px; font:inherit; cursor:pointer; }
183
+ .btn:hover { border-color:var(--accent); color:var(--accent); }
184
+ .btn[data-active="true"] { background:var(--accent); color:white; border-color:var(--accent); }
185
+ main { display:grid; grid-template-columns:minmax(420px,1fr) 360px; gap:20px; padding:24px; align-items:start; }
186
+ iframe { width:100%; height:460px; border:1px solid var(--border); border-radius:12px; background:white; }
187
+ .panel { border:1px solid var(--border); border-radius:12px; padding:16px; }
188
+ .panel + .panel { margin-top:12px; }
189
+ .panel h2 { margin:0 0 8px; font-size:13px; text-transform:uppercase; letter-spacing:.08em; color:var(--muted); }
190
+ pre { margin:0; padding:10px; border-radius:8px; font:12px/1.45 ui-monospace,Menlo,Consolas,monospace; overflow:auto; max-height:200px; }
191
+ .log { font:11px/1.5 ui-monospace,Menlo,Consolas,monospace; color:var(--muted); white-space:pre-wrap; max-height:280px; overflow:auto; }
192
+ @media (max-width:880px) { main { grid-template-columns:1fr; padding:16px; } }
193
+ </style>
194
+ </head>
195
+ <body>
196
+ <header>
197
+ <h1>${escapeHtml(toolName)}</h1>
198
+ <code>${escapeHtml(uri)}</code>
199
+ <span class="spacer"></span>
200
+ <div id="fixtures">${buttons}</div>
201
+ <button class="btn" id="theme" type="button">Theme</button>
202
+ <button class="btn" id="teardown" type="button">Teardown</button>
203
+ </header>
204
+ <main>
205
+ <iframe id="sandbox" sandbox="allow-scripts allow-same-origin" title="mountly-mcp sandbox proxy"></iframe>
206
+ <div>
207
+ <section class="panel">
208
+ <h2>Last structuredContent</h2>
209
+ <pre id="payload">(none yet)</pre>
210
+ </section>
211
+ <section class="panel">
212
+ <h2>Channel log</h2>
213
+ <div class="log" id="log"></div>
214
+ </section>
215
+ </div>
216
+ </main>
217
+ <script>
218
+ globalThis.__mountlyMcpDevHost__ = ${serializeInlineScriptValue({
219
+ toolName,
220
+ sandboxOrigin,
221
+ fixtures,
222
+ toolInput,
223
+ uri,
224
+ hasServer,
225
+ sandboxPath: "/sandbox-proxy.html"
226
+ })};
227
+ const HAS_SERVER = ${hasServer ? "true" : "false"};
228
+ </script>
229
+ <script src="/host.js"></script>
230
+ </body>
231
+ </html>
232
+ `;
233
+ }
234
+ function sandboxProxyShell(hostOrigin, runtime) {
235
+ return `<!doctype html>
236
+ <html lang="en">
237
+ <head>
238
+ <meta charset="utf-8">
239
+ <title>mountly-mcp sandbox proxy</title>
240
+ <style>
241
+ html, body { margin: 0; padding: 0; width: 100%; height: 100%; background: transparent; }
242
+ #inner { width: 100%; height: 100%; border: 0; background: white; }
243
+ </style>
244
+ </head>
245
+ <body>
246
+ <script>globalThis.__mountlyMcpSandbox__ = ${serializeInlineScriptValue({ hostOrigin })};</script>
247
+ <script>${escapeInlineScript(runtime)}</script>
248
+ </body>
249
+ </html>
250
+ `;
251
+ }
252
+ async function sandboxProxyHtml(hostOrigin) {
253
+ return sandboxProxyShell(hostOrigin, await loadRuntime("sandbox-entry"));
254
+ }
255
+
256
+ export {
257
+ startDevHost,
258
+ sandboxProxyHtml
259
+ };
260
+ //# sourceMappingURL=chunk-KRAS266U.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dev/index.ts"],"sourcesContent":["/**\n * A local MCP Apps host, so a widget can be developed without installing a\n * real host and restarting it on every change.\n *\n * It is a genuine host, not a preview shim: two origins as the spec requires\n * (§8.4), a sandbox proxy that injects the sidecar's CSP and permissions, the\n * full `ui/initialize` handshake, and tool-input/tool-result delivery. A widget\n * that works here works in Claude, and one that reaches for `eval` fails here\n * for the same reason it would fail there.\n *\n * ```ts\n * const host = await startDevHost({ htmlPath: \"dist/widget.html\", fixtures });\n * console.log(host.hostUrl);\n * ```\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { createServer, type Server } from \"node:http\";\nimport { fileURLToPath } from \"node:url\";\nimport type { McpResourceDeclaration } from \"../types.js\";\nimport { escapeInlineScript, serializeInlineScriptValue } from \"../html.js\";\n\nexport interface DevHostOptions {\n /** Built widget HTML. Its `.meta.json` sidecar is read alongside. */\n htmlPath: string;\n /**\n * Named samples, each becoming a button in the host chrome.\n *\n * Without `callTool` the value is delivered straight through as\n * `structuredContent`. With `callTool` it is the tool's *arguments*, and\n * what the view receives is whatever the real tool returns.\n */\n fixtures?: Record<string, unknown>;\n /**\n * Run a real tool. Supplied when the developer points `dev` at their server,\n * and used both for the fixture buttons and for `tools/call` requests the\n * view makes itself — so app-only tools behave as they will in production.\n */\n callTool?: (name: string, args: unknown) => Promise<unknown>;\n /** Arguments delivered as `ui/notifications/tool-input`. */\n toolInput?: Record<string, unknown>;\n /** Shown in the host chrome. Defaults to the sidecar's resource name. */\n toolName?: string;\n /** Defaults to 5179, incrementing until a free port is found. */\n hostPort?: number;\n /** Defaults to `hostPort + 1`, incrementing until a free port is found. */\n sandboxPort?: number;\n}\n\nexport interface DevHost {\n hostUrl: string;\n sandboxUrl: string;\n /** Tell connected browsers to reload — call after a rebuild. */\n reload(): void;\n close(): Promise<void>;\n}\n\nfunction escapeHtml(value: string): string {\n return value.replace(\n /[&<>\"']/g,\n (character) =>\n ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\", \"'\": \"&#39;\" })[character]!,\n );\n}\n\nconst runtimeCache = new Map<string, Promise<string>>();\n\n/**\n * Load one of the browser-side runtimes (`host-entry`, `sandbox-entry`) as a\n * string to inline into a page.\n *\n * Installed, the built IIFE is a declared package export, so ask the module\n * resolver for it. Walking from `import.meta.url` breaks the moment a bundler\n * hoists this module into a shared chunk at a different depth — which is what\n * tsup does, leaving it looking in dist/ while the file sits in dist/dev/.\n */\nfunction loadRuntime(name: \"host-entry\" | \"sandbox-entry\"): Promise<string> {\n const cached = runtimeCache.get(name);\n if (cached) return cached;\n const loading = (async () => {\n try {\n return await readFile(\n fileURLToPath(import.meta.resolve(`mountly-mcp/dev/${name}.js`)),\n \"utf8\",\n );\n } catch {\n // Running from source in this repo, where nothing is built yet.\n }\n const entry = fileURLToPath(new URL(`./${name}.ts`, import.meta.url));\n if (!existsSync(entry)) {\n throw new Error(\n `mountly-mcp dev: ${name} runtime missing. Expected the packaged 'mountly-mcp/dev/${name}.js' export, or ${entry} when running from source.`,\n );\n }\n const { build } = await import(\"vite\");\n const result = await build({\n configFile: false,\n logLevel: \"silent\",\n build: {\n write: false,\n target: \"es2022\",\n minify: false,\n lib: { entry, formats: [\"iife\"], name: \"__mountlyMcpDevRuntime\" },\n },\n });\n const outputs = Array.isArray(result) ? result : [result];\n const chunk = outputs\n .flatMap((output) => (\"output\" in output ? output.output : []))\n .find((item) => item.type === \"chunk\");\n if (!chunk || chunk.type !== \"chunk\")\n throw new Error(`mountly-mcp dev: failed to bundle ${name}`);\n return chunk.code;\n })();\n runtimeCache.set(name, loading);\n return loading;\n}\n\nfunction listenOnFreePort(server: Server, port: number): Promise<number> {\n return new Promise((resolvePort, reject) => {\n const attempt = (candidate: number): void => {\n // Listeners are paired and torn down together: `listen()`'s callback form\n // leaves a stale one attached across retries, which then resolves with\n // the port that was already taken.\n const onError = (error: NodeJS.ErrnoException): void => {\n server.removeListener(\"listening\", onListening);\n // ponytail: linear probe, fine for a dev tool on a developer's laptop.\n if (error.code === \"EADDRINUSE\" && candidate < port + 50) attempt(candidate + 1);\n else reject(error);\n };\n const onListening = (): void => {\n server.removeListener(\"error\", onError);\n const address = server.address();\n resolvePort(typeof address === \"object\" && address !== null ? address.port : candidate);\n };\n server.once(\"error\", onError);\n server.once(\"listening\", onListening);\n server.listen(candidate);\n };\n attempt(port);\n });\n}\n\nexport async function startDevHost(options: DevHostOptions): Promise<DevHost> {\n const { htmlPath, fixtures = {}, toolInput = {}, callTool } = options;\n\n const metaPath = `${htmlPath}.meta.json`;\n const initialMeta = JSON.parse(await readFile(metaPath, \"utf8\")) as McpResourceDeclaration;\n const toolName = options.toolName ?? initialMeta.name;\n const [hostRuntime, sandboxRuntime] = await Promise.all([\n loadRuntime(\"host-entry\"),\n loadRuntime(\"sandbox-entry\"),\n ]);\n\n // Bumped on reload(); the browser polls it and reloads when it changes. The\n // widget HTML is read per request rather than baked into the page, so a\n // rebuild is picked up with no server restart.\n let version = 1;\n\n // The two servers reference each other's origin, and each only needs the\n // other's once a request arrives — so the sandbox reads this after the host\n // has bound its port.\n let hostOrigin = \"\";\n\n const sandboxServer = createServer((_req, res) => {\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\", \"Cache-Control\": \"no-store\" });\n res.end(sandboxProxyShell(hostOrigin, sandboxRuntime));\n });\n const sandboxPort = await listenOnFreePort(\n sandboxServer,\n options.sandboxPort ?? (options.hostPort ?? 5179) + 1,\n );\n const sandboxOrigin = `http://localhost:${sandboxPort}`;\n\n const hostServer = createServer(async (req, res) => {\n const path = new URL(req.url ?? \"/\", \"http://localhost\").pathname;\n const send = (status: number, type: string, body: string | Buffer): void => {\n res.writeHead(status, { \"Content-Type\": type, \"Cache-Control\": \"no-store\" });\n res.end(body);\n };\n try {\n if (path === \"/call\" && req.method === \"POST\") {\n if (!callTool) return send(400, \"text/plain\", \"no server connected\");\n const body = await new Promise<string>((done, fail) => {\n let raw = \"\";\n req.on(\"data\", (chunk) => (raw += chunk));\n req.on(\"end\", () => done(raw));\n req.on(\"error\", fail);\n });\n const { name, arguments: args } = JSON.parse(body) as { name: string; arguments?: unknown };\n // Tool failures are the view's problem to render, not the dev server's\n // to crash on, so they come back as a normal result.\n const result = await callTool(name, args).catch((error: unknown) => ({\n isError: true,\n content: [{ type: \"text\", text: error instanceof Error ? error.message : String(error) }],\n }));\n return send(200, \"application/json\", JSON.stringify(result));\n }\n if (path === \"/\") return send(200, \"text/html; charset=utf-8\", hostPage());\n if (path === \"/host.js\") return send(200, \"text/javascript; charset=utf-8\", hostRuntime);\n if (path === \"/version\") return send(200, \"application/json\", String(version));\n if (path === \"/widget.html\")\n return send(200, \"text/plain; charset=utf-8\", await readFile(htmlPath, \"utf8\"));\n if (path === \"/widget.meta.json\")\n return send(200, \"application/json\", await readFile(metaPath, \"utf8\"));\n send(404, \"text/plain\", \"not found\");\n } catch (error) {\n // A rebuild in flight can briefly leave the file missing; say so rather\n // than crashing the dev server.\n send(500, \"text/plain\", error instanceof Error ? error.message : String(error));\n }\n });\n let hostPort: number;\n try {\n hostPort = await listenOnFreePort(hostServer, options.hostPort ?? 5179);\n } catch (caught) {\n await new Promise<void>((done) => sandboxServer.close(() => done()));\n throw caught;\n }\n hostOrigin = `http://localhost:${hostPort}`;\n\n function hostPage(): string {\n return devHostHtml({\n toolName,\n sandboxOrigin,\n fixtures,\n toolInput,\n uri: initialMeta.uri,\n hasServer: callTool !== undefined,\n });\n }\n\n return {\n hostUrl: hostOrigin,\n sandboxUrl: sandboxOrigin,\n reload() {\n version += 1;\n },\n async close() {\n await Promise.all([\n new Promise<void>((done) => hostServer.close(() => done())),\n new Promise<void>((done) => sandboxServer.close(() => done())),\n ]);\n },\n };\n}\n\ninterface HostPageOptions {\n toolName: string;\n sandboxOrigin: string;\n fixtures: Record<string, unknown>;\n toolInput: Record<string, unknown>;\n uri: string;\n hasServer: boolean;\n}\n\nfunction devHostHtml(options: HostPageOptions): string {\n const { toolName, sandboxOrigin, fixtures, toolInput, uri, hasServer } = options;\n const buttons = Object.keys(fixtures)\n .map(\n (name, index) =>\n `<button class=\"btn\" data-fixture=\"${escapeHtml(name)}\"${index === 0 ? ' data-active=\"true\"' : \"\"}>${escapeHtml(name)}</button>`,\n )\n .join(\"\\n\");\n\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>mountly-mcp dev · ${escapeHtml(toolName)}</title>\n<style>\n :root { color-scheme:light; --bg:#f6f7f9; --fg:#0b0c0f; --muted:#5c6675; --accent:#1d4ed8; --border:#dcdfe5; }\n :root[data-theme=\"dark\"] { color-scheme:dark; --bg:#0b0c0f; --fg:#e6e9ef; --muted:#9aa3b2; --accent:#7aa2ff; --border:#252a33; }\n * { box-sizing:border-box; }\n body { margin:0; min-height:100vh; background:var(--bg); color:var(--fg); font:14px/1.5 -apple-system,BlinkMacSystemFont,\"Segoe UI\",system-ui,sans-serif; }\n header { padding:18px 24px; border-bottom:1px solid var(--border); display:flex; align-items:center; gap:12px; flex-wrap:wrap; }\n header h1 { margin:0; font-size:16px; font-weight:600; }\n header code { color:var(--muted); font-size:12px; }\n #fixtures { display:flex; gap:8px; flex-wrap:wrap; }\n .spacer { flex:1; }\n .btn { appearance:none; border:1px solid var(--border); background:transparent; color:var(--fg); padding:6px 12px; border-radius:6px; font:inherit; cursor:pointer; }\n .btn:hover { border-color:var(--accent); color:var(--accent); }\n .btn[data-active=\"true\"] { background:var(--accent); color:white; border-color:var(--accent); }\n main { display:grid; grid-template-columns:minmax(420px,1fr) 360px; gap:20px; padding:24px; align-items:start; }\n iframe { width:100%; height:460px; border:1px solid var(--border); border-radius:12px; background:white; }\n .panel { border:1px solid var(--border); border-radius:12px; padding:16px; }\n .panel + .panel { margin-top:12px; }\n .panel h2 { margin:0 0 8px; font-size:13px; text-transform:uppercase; letter-spacing:.08em; color:var(--muted); }\n pre { margin:0; padding:10px; border-radius:8px; font:12px/1.45 ui-monospace,Menlo,Consolas,monospace; overflow:auto; max-height:200px; }\n .log { font:11px/1.5 ui-monospace,Menlo,Consolas,monospace; color:var(--muted); white-space:pre-wrap; max-height:280px; overflow:auto; }\n @media (max-width:880px) { main { grid-template-columns:1fr; padding:16px; } }\n</style>\n</head>\n<body>\n<header>\n <h1>${escapeHtml(toolName)}</h1>\n <code>${escapeHtml(uri)}</code>\n <span class=\"spacer\"></span>\n <div id=\"fixtures\">${buttons}</div>\n <button class=\"btn\" id=\"theme\" type=\"button\">Theme</button>\n <button class=\"btn\" id=\"teardown\" type=\"button\">Teardown</button>\n</header>\n<main>\n <iframe id=\"sandbox\" sandbox=\"allow-scripts allow-same-origin\" title=\"mountly-mcp sandbox proxy\"></iframe>\n <div>\n <section class=\"panel\">\n <h2>Last structuredContent</h2>\n <pre id=\"payload\">(none yet)</pre>\n </section>\n <section class=\"panel\">\n <h2>Channel log</h2>\n <div class=\"log\" id=\"log\"></div>\n </section>\n </div>\n</main>\n<script>\nglobalThis.__mountlyMcpDevHost__ = ${serializeInlineScriptValue({\n toolName,\n sandboxOrigin,\n fixtures,\n toolInput,\n uri,\n hasServer,\n sandboxPath: \"/sandbox-proxy.html\",\n })};\nconst HAS_SERVER = ${hasServer ? \"true\" : \"false\"};\n</script>\n<script src=\"/host.js\"></script>\n</body>\n</html>\n`;\n}\n/**\n * The proxy document: a shell around the compiled sandbox runtime.\n *\n * Kept separate from {@link sandboxProxyHtml} so the dev server can load the\n * runtime once at startup and still stamp in the host origin per request,\n * which it only learns after binding a port.\n */\nfunction sandboxProxyShell(hostOrigin: string, runtime: string): string {\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>mountly-mcp sandbox proxy</title>\n<style>\n html, body { margin: 0; padding: 0; width: 100%; height: 100%; background: transparent; }\n #inner { width: 100%; height: 100%; border: 0; background: white; }\n</style>\n</head>\n<body>\n<script>globalThis.__mountlyMcpSandbox__ = ${serializeInlineScriptValue({ hostOrigin })};</script>\n<script>${escapeInlineScript(runtime)}</script>\n</body>\n</html>\n`;\n}\n\n/**\n * The MCP Apps sandbox proxy document (spec §8.4), ready to serve from an\n * origin distinct from the host page.\n *\n * Exported because this is the security boundary — it builds the CSP from the\n * sidecar and decides what the view may execute and reach. Anything standing\n * up a host, including the example harnesses in this repo, should serve this\n * rather than keep a copy that drifts from it.\n */\nexport async function sandboxProxyHtml(hostOrigin: string): Promise<string> {\n return sandboxProxyShell(hostOrigin, await loadRuntime(\"sandbox-entry\"));\n}\n\nexport { connectMcpServer, type ConnectedMcpServer } from \"./connect-server.js\";\n"],"mappings":";;;;;;AAeA,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB;AACzB,SAAS,oBAAiC;AAC1C,SAAS,qBAAqB;AAuC9B,SAAS,WAAW,OAAuB;AACzC,SAAO,MAAM;AAAA,IACX;AAAA,IACA,CAAC,eACE,EAAE,KAAK,SAAS,KAAK,QAAQ,KAAK,QAAQ,KAAK,UAAU,KAAK,QAAQ,GAAG,SAAS;AAAA,EACvF;AACF;AAEA,IAAM,eAAe,oBAAI,IAA6B;AAWtD,SAAS,YAAY,MAAuD;AAC1E,QAAM,SAAS,aAAa,IAAI,IAAI;AACpC,MAAI,OAAQ,QAAO;AACnB,QAAM,WAAW,YAAY;AAC3B,QAAI;AACF,aAAO,MAAM;AAAA,QACX,cAAc,YAAY,QAAQ,mBAAmB,IAAI,KAAK,CAAC;AAAA,QAC/D;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AACA,UAAM,QAAQ,cAAc,IAAI,IAAI,KAAK,IAAI,OAAO,YAAY,GAAG,CAAC;AACpE,QAAI,CAAC,WAAW,KAAK,GAAG;AACtB,YAAM,IAAI;AAAA,QACR,oBAAoB,IAAI,4DAA4D,IAAI,mBAAmB,KAAK;AAAA,MAClH;AAAA,IACF;AACA,UAAM,EAAE,MAAM,IAAI,MAAM,OAAO,MAAM;AACrC,UAAM,SAAS,MAAM,MAAM;AAAA,MACzB,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,OAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,KAAK,EAAE,OAAO,SAAS,CAAC,MAAM,GAAG,MAAM,yBAAyB;AAAA,MAClE;AAAA,IACF,CAAC;AACD,UAAM,UAAU,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AACxD,UAAM,QAAQ,QACX,QAAQ,CAAC,WAAY,YAAY,SAAS,OAAO,SAAS,CAAC,CAAE,EAC7D,KAAK,CAAC,SAAS,KAAK,SAAS,OAAO;AACvC,QAAI,CAAC,SAAS,MAAM,SAAS;AAC3B,YAAM,IAAI,MAAM,qCAAqC,IAAI,EAAE;AAC7D,WAAO,MAAM;AAAA,EACf,GAAG;AACH,eAAa,IAAI,MAAM,OAAO;AAC9B,SAAO;AACT;AAEA,SAAS,iBAAiB,QAAgB,MAA+B;AACvE,SAAO,IAAI,QAAQ,CAAC,aAAa,WAAW;AAC1C,UAAM,UAAU,CAAC,cAA4B;AAI3C,YAAM,UAAU,CAAC,UAAuC;AACtD,eAAO,eAAe,aAAa,WAAW;AAE9C,YAAI,MAAM,SAAS,gBAAgB,YAAY,OAAO,GAAI,SAAQ,YAAY,CAAC;AAAA,YAC1E,QAAO,KAAK;AAAA,MACnB;AACA,YAAM,cAAc,MAAY;AAC9B,eAAO,eAAe,SAAS,OAAO;AACtC,cAAM,UAAU,OAAO,QAAQ;AAC/B,oBAAY,OAAO,YAAY,YAAY,YAAY,OAAO,QAAQ,OAAO,SAAS;AAAA,MACxF;AACA,aAAO,KAAK,SAAS,OAAO;AAC5B,aAAO,KAAK,aAAa,WAAW;AACpC,aAAO,OAAO,SAAS;AAAA,IACzB;AACA,YAAQ,IAAI;AAAA,EACd,CAAC;AACH;AAEA,eAAsB,aAAa,SAA2C;AAC5E,QAAM,EAAE,UAAU,WAAW,CAAC,GAAG,YAAY,CAAC,GAAG,SAAS,IAAI;AAE9D,QAAM,WAAW,GAAG,QAAQ;AAC5B,QAAM,cAAc,KAAK,MAAM,MAAM,SAAS,UAAU,MAAM,CAAC;AAC/D,QAAM,WAAW,QAAQ,YAAY,YAAY;AACjD,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,QAAQ,IAAI;AAAA,IACtD,YAAY,YAAY;AAAA,IACxB,YAAY,eAAe;AAAA,EAC7B,CAAC;AAKD,MAAI,UAAU;AAKd,MAAI,aAAa;AAEjB,QAAM,gBAAgB,aAAa,CAAC,MAAM,QAAQ;AAChD,QAAI,UAAU,KAAK,EAAE,gBAAgB,4BAA4B,iBAAiB,WAAW,CAAC;AAC9F,QAAI,IAAI,kBAAkB,YAAY,cAAc,CAAC;AAAA,EACvD,CAAC;AACD,QAAM,cAAc,MAAM;AAAA,IACxB;AAAA,IACA,QAAQ,gBAAgB,QAAQ,YAAY,QAAQ;AAAA,EACtD;AACA,QAAM,gBAAgB,oBAAoB,WAAW;AAErD,QAAM,aAAa,aAAa,OAAO,KAAK,QAAQ;AAClD,UAAM,OAAO,IAAI,IAAI,IAAI,OAAO,KAAK,kBAAkB,EAAE;AACzD,UAAM,OAAO,CAAC,QAAgB,MAAc,SAAgC;AAC1E,UAAI,UAAU,QAAQ,EAAE,gBAAgB,MAAM,iBAAiB,WAAW,CAAC;AAC3E,UAAI,IAAI,IAAI;AAAA,IACd;AACA,QAAI;AACF,UAAI,SAAS,WAAW,IAAI,WAAW,QAAQ;AAC7C,YAAI,CAAC,SAAU,QAAO,KAAK,KAAK,cAAc,qBAAqB;AACnE,cAAM,OAAO,MAAM,IAAI,QAAgB,CAAC,MAAM,SAAS;AACrD,cAAI,MAAM;AACV,cAAI,GAAG,QAAQ,CAAC,UAAW,OAAO,KAAM;AACxC,cAAI,GAAG,OAAO,MAAM,KAAK,GAAG,CAAC;AAC7B,cAAI,GAAG,SAAS,IAAI;AAAA,QACtB,CAAC;AACD,cAAM,EAAE,MAAM,WAAW,KAAK,IAAI,KAAK,MAAM,IAAI;AAGjD,cAAM,SAAS,MAAM,SAAS,MAAM,IAAI,EAAE,MAAM,CAAC,WAAoB;AAAA,UACnE,SAAS;AAAA,UACT,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,CAAC;AAAA,QAC1F,EAAE;AACF,eAAO,KAAK,KAAK,oBAAoB,KAAK,UAAU,MAAM,CAAC;AAAA,MAC7D;AACA,UAAI,SAAS,IAAK,QAAO,KAAK,KAAK,4BAA4B,SAAS,CAAC;AACzE,UAAI,SAAS,WAAY,QAAO,KAAK,KAAK,kCAAkC,WAAW;AACvF,UAAI,SAAS,WAAY,QAAO,KAAK,KAAK,oBAAoB,OAAO,OAAO,CAAC;AAC7E,UAAI,SAAS;AACX,eAAO,KAAK,KAAK,6BAA6B,MAAM,SAAS,UAAU,MAAM,CAAC;AAChF,UAAI,SAAS;AACX,eAAO,KAAK,KAAK,oBAAoB,MAAM,SAAS,UAAU,MAAM,CAAC;AACvE,WAAK,KAAK,cAAc,WAAW;AAAA,IACrC,SAAS,OAAO;AAGd,WAAK,KAAK,cAAc,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,IAChF;AAAA,EACF,CAAC;AACD,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,iBAAiB,YAAY,QAAQ,YAAY,IAAI;AAAA,EACxE,SAAS,QAAQ;AACf,UAAM,IAAI,QAAc,CAAC,SAAS,cAAc,MAAM,MAAM,KAAK,CAAC,CAAC;AACnE,UAAM;AAAA,EACR;AACA,eAAa,oBAAoB,QAAQ;AAEzC,WAAS,WAAmB;AAC1B,WAAO,YAAY;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,KAAK,YAAY;AAAA,MACjB,WAAW,aAAa;AAAA,IAC1B,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AACP,iBAAW;AAAA,IACb;AAAA,IACA,MAAM,QAAQ;AACZ,YAAM,QAAQ,IAAI;AAAA,QAChB,IAAI,QAAc,CAAC,SAAS,WAAW,MAAM,MAAM,KAAK,CAAC,CAAC;AAAA,QAC1D,IAAI,QAAc,CAAC,SAAS,cAAc,MAAM,MAAM,KAAK,CAAC,CAAC;AAAA,MAC/D,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAWA,SAAS,YAAY,SAAkC;AACrD,QAAM,EAAE,UAAU,eAAe,UAAU,WAAW,KAAK,UAAU,IAAI;AACzE,QAAM,UAAU,OAAO,KAAK,QAAQ,EACjC;AAAA,IACC,CAAC,MAAM,UACL,qCAAqC,WAAW,IAAI,CAAC,IAAI,UAAU,IAAI,wBAAwB,EAAE,IAAI,WAAW,IAAI,CAAC;AAAA,EACzH,EACC,KAAK,IAAI;AAEZ,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,8BAKkB,WAAW,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QA0BvC,WAAW,QAAQ,CAAC;AAAA,UAClB,WAAW,GAAG,CAAC;AAAA;AAAA,uBAEF,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qCAkBO,2BAA2B;AAAA,IAC5D;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,EACf,CAAC,CAAC;AAAA,qBACiB,YAAY,SAAS,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAMjD;AAQA,SAAS,kBAAkB,YAAoB,SAAyB;AACtE,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6CAWoC,2BAA2B,EAAE,WAAW,CAAC,CAAC;AAAA,UAC7E,mBAAmB,OAAO,CAAC;AAAA;AAAA;AAAA;AAIrC;AAWA,eAAsB,iBAAiB,YAAqC;AAC1E,SAAO,kBAAkB,YAAY,MAAM,YAAY,eAAe,CAAC;AACzE;","names":[]}