mountly-mcp 0.0.0 → 4.0.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.
- package/LICENSE +201 -0
- package/README.md +283 -20
- package/dist/artifact/index.d.ts +49 -0
- package/dist/artifact/index.js +21 -0
- package/dist/artifact/index.js.map +1 -0
- package/dist/bridge/iframe-entry.d.ts +1 -0
- package/dist/bridge/iframe-entry.js +270 -220
- package/dist/bridge/index.d.ts +20 -15
- package/dist/bridge/index.js +2 -2
- package/dist/build/emit-html.d.ts +30 -0
- package/dist/build/emit-meta.d.ts +17 -0
- package/dist/build/index.d.ts +33 -48
- package/dist/build/index.js +9 -135
- package/dist/build/index.js.map +1 -1
- package/dist/chunk-33DMFKKD.js +111 -0
- package/dist/chunk-33DMFKKD.js.map +1 -0
- package/dist/chunk-4HC3OWAQ.js +185 -0
- package/dist/chunk-4HC3OWAQ.js.map +1 -0
- package/dist/chunk-5EHNJCMF.js +58 -0
- package/dist/chunk-5EHNJCMF.js.map +1 -0
- package/dist/chunk-6QHKKFO3.js +30 -0
- package/dist/chunk-6QHKKFO3.js.map +1 -0
- package/dist/chunk-AEVQKFXX.js +119 -0
- package/dist/chunk-AEVQKFXX.js.map +1 -0
- package/dist/chunk-DKK26TRS.js +199 -0
- package/dist/chunk-DKK26TRS.js.map +1 -0
- package/dist/chunk-JVQZGKBL.js +171 -0
- 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-A64ZILAS.js → chunk-M4WFJR3C.js} +3 -2
- package/dist/{chunk-A64ZILAS.js.map → chunk-M4WFJR3C.js.map} +1 -1
- package/dist/chunk-MTJDRWGD.js +196 -0
- 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/chunk-UECZA34Q.js +52 -0
- package/dist/chunk-UECZA34Q.js.map +1 -0
- package/dist/chunk-YJA4H6IB.js +13 -0
- package/dist/chunk-YJA4H6IB.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +297 -0
- package/dist/cli.js.map +1 -0
- package/dist/connect-server-7L4HGKKL.js +7 -0
- package/dist/connect-server-7L4HGKKL.js.map +1 -0
- package/dist/create.d.ts +10 -0
- package/dist/create.js +107 -0
- package/dist/create.js.map +1 -0
- package/dist/dev/connect-server.d.ts +13 -0
- package/dist/dev/host-entry.d.ts +13 -0
- package/dist/dev/host-entry.js +18330 -0
- package/dist/dev/index.d.ts +45 -0
- package/dist/dev/index.js +14 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/dev/sandbox-entry.d.ts +7 -0
- package/dist/dev/sandbox-entry.js +80 -0
- package/dist/html.d.ts +4 -0
- package/dist/index.d.ts +7 -26
- package/dist/index.js +16 -3
- package/dist/json-render/app.d.ts +22 -0
- package/dist/json-render/app.js +10 -0
- package/dist/json-render/app.js.map +1 -0
- package/dist/json-render/generative-view.d.ts +57 -0
- package/dist/json-render/index.d.ts +10 -0
- package/dist/json-render/index.js +120 -0
- package/dist/json-render/index.js.map +1 -0
- package/dist/json-render/mcp.d.ts +49 -0
- package/dist/json-render/mcp.js +11 -0
- package/dist/json-render/mcp.js.map +1 -0
- package/dist/json-render/server.d.ts +72 -0
- package/dist/json-render/server.js +98 -0
- package/dist/json-render/server.js.map +1 -0
- package/dist/json-render/spec-stream.d.ts +26 -0
- package/dist/json-render/use-spec-stream.d.ts +26 -0
- package/dist/publish.d.ts +10 -0
- package/dist/react/context.d.ts +14 -0
- package/dist/react/create-view.d.ts +8 -0
- package/dist/react/hooks.d.ts +46 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.js +36 -0
- package/dist/react/index.js.map +1 -0
- package/dist/sandbox/index.d.ts +16 -0
- package/dist/sandbox/index.js +8 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/schema.d.ts +20 -0
- package/dist/server/index.d.ts +73 -0
- package/dist/server/index.js +235 -0
- package/dist/server/index.js.map +1 -0
- package/dist/svelte/index.d.ts +29 -0
- package/dist/svelte/index.js +13 -0
- package/dist/svelte/index.js.map +1 -0
- package/dist/testing/index.d.ts +27 -0
- package/dist/testing/index.js +312 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/render-artifact.d.ts +35 -0
- package/dist/types.d.ts +65 -0
- package/dist/vite/index.d.ts +23 -0
- package/dist/vite/index.js +211 -0
- package/dist/vite/index.js.map +1 -0
- package/dist/vue/index.d.ts +51 -0
- package/dist/vue/index.js +93 -0
- package/dist/vue/index.js.map +1 -0
- package/package.json +162 -20
- 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/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-BUGGLMNA.js +0 -132
- package/dist/chunk-BUGGLMNA.js.map +0 -1
- package/dist/types-B9JAWm4W.d.ts +0 -62
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MCP_APP_MANIFEST_FILE,
|
|
3
|
+
writeMcpAppManifest
|
|
4
|
+
} from "../chunk-DKK26TRS.js";
|
|
5
|
+
import {
|
|
6
|
+
buildMcpResourceFromSource
|
|
7
|
+
} from "../chunk-MTJDRWGD.js";
|
|
8
|
+
import "../chunk-M4WFJR3C.js";
|
|
9
|
+
import "../chunk-YJA4H6IB.js";
|
|
10
|
+
|
|
11
|
+
// src/vite/index.ts
|
|
12
|
+
import { rm } from "fs/promises";
|
|
13
|
+
import { basename, isAbsolute, join, resolve } from "path";
|
|
14
|
+
function isCollection(options) {
|
|
15
|
+
return "apps" in options;
|
|
16
|
+
}
|
|
17
|
+
function safeBuildName(name, index) {
|
|
18
|
+
const safe = name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
19
|
+
return `mountly_mcp_${index}_${safe || "app"}`;
|
|
20
|
+
}
|
|
21
|
+
function outputPath(root, outDir, app) {
|
|
22
|
+
if (!app.output) return join(outDir, `${basename(app.name)}.html`);
|
|
23
|
+
return isAbsolute(app.output) ? app.output : resolve(root, app.output);
|
|
24
|
+
}
|
|
25
|
+
function mountlyMcpViews(options) {
|
|
26
|
+
const configuredApps = isCollection(options) ? [...options.apps] : [options];
|
|
27
|
+
const selectedForDev = process.env.MOUNTLY_MCP_SELECTED_APP;
|
|
28
|
+
const apps = selectedForDev ? configuredApps.filter((app) => app.name === selectedForDev) : configuredApps;
|
|
29
|
+
const manifestOption = isCollection(options) ? options.manifest : void 0;
|
|
30
|
+
if (apps.length === 0) throw new Error("mountly-mcp: apps must contain at least one View");
|
|
31
|
+
const names = /* @__PURE__ */ new Set();
|
|
32
|
+
const uris = /* @__PURE__ */ new Set();
|
|
33
|
+
for (const app of apps) {
|
|
34
|
+
if (!app.uri.startsWith("ui://")) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`mountly-mcp: UI resource uri must use the 'ui://' scheme (received '${app.uri}')`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
if (names.has(app.name)) throw new Error(`mountly-mcp: duplicate app name '${app.name}'`);
|
|
40
|
+
if (uris.has(app.uri)) throw new Error(`mountly-mcp: duplicate app uri '${app.uri}'`);
|
|
41
|
+
names.add(app.name);
|
|
42
|
+
uris.add(app.uri);
|
|
43
|
+
}
|
|
44
|
+
const environments = apps.map((app, index) => ({
|
|
45
|
+
app,
|
|
46
|
+
name: safeBuildName(app.name, index),
|
|
47
|
+
jsFile: `view-${index}-${basename(app.name)}.js`,
|
|
48
|
+
cssFile: `view-${index}-${basename(app.name)}.css`
|
|
49
|
+
}));
|
|
50
|
+
const byEnvironment = new Map(environments.map((environment) => [environment.name, environment]));
|
|
51
|
+
const emitted = /* @__PURE__ */ new Map();
|
|
52
|
+
const artifacts = /* @__PURE__ */ new Map();
|
|
53
|
+
let root = process.cwd();
|
|
54
|
+
let outDir = resolve(root, "dist");
|
|
55
|
+
let manifestWritten = false;
|
|
56
|
+
function currentEnvironment(context) {
|
|
57
|
+
const name = context.environment?.name;
|
|
58
|
+
return (name ? byEnvironment.get(name) : void 0) ?? (environments.length === 1 ? environments[0] : void 0);
|
|
59
|
+
}
|
|
60
|
+
const plugin = {
|
|
61
|
+
name: "mountly-mcp-views",
|
|
62
|
+
api: {
|
|
63
|
+
// The CLI consumes this documented plugin interface instead of scraping
|
|
64
|
+
// Vite's resolved build settings.
|
|
65
|
+
mountlyMcp: { apps: configuredApps, manifest: manifestOption }
|
|
66
|
+
},
|
|
67
|
+
// After Vite core plugins: css-post writes the stylesheet in its own
|
|
68
|
+
// closeBundle, and this plugin has to see the file to inline it.
|
|
69
|
+
enforce: "post",
|
|
70
|
+
// Only meaningful during `vite build`; a dev server has no bundle to emit.
|
|
71
|
+
apply: "build",
|
|
72
|
+
config(_config, env) {
|
|
73
|
+
const first = environments[0];
|
|
74
|
+
if (!first) throw new Error("mountly-mcp: configure at least one app");
|
|
75
|
+
return {
|
|
76
|
+
// Vite's lib mode leaves `process.env.NODE_ENV` for a consuming bundler
|
|
77
|
+
// to replace. There isn't one — the bundle is inlined into HTML and run
|
|
78
|
+
// in a sandboxed iframe, where `process` is undefined and React and Vue
|
|
79
|
+
// throw on their first environment check.
|
|
80
|
+
define: { "process.env.NODE_ENV": JSON.stringify(env.mode) },
|
|
81
|
+
// Legacy `vite build` builds one environment. Preserve that zero-config
|
|
82
|
+
// path for single-View projects; multi-View projects use
|
|
83
|
+
// `mountly-mcp build`, which drives the environments below.
|
|
84
|
+
build: {
|
|
85
|
+
lib: {
|
|
86
|
+
entry: first.app.entry,
|
|
87
|
+
// IIFE so the bundle inlines into a single <script> with no import
|
|
88
|
+
// graph left to resolve inside the sandboxed iframe.
|
|
89
|
+
formats: ["iife"],
|
|
90
|
+
name: "__mountlyMcpEntry0",
|
|
91
|
+
fileName: () => first.jsFile,
|
|
92
|
+
// Named explicitly: otherwise a lib build derives the stylesheet
|
|
93
|
+
// name from package.json and fails outright when there isn't one.
|
|
94
|
+
cssFileName: first.cssFile.replace(/\.css$/, "")
|
|
95
|
+
},
|
|
96
|
+
// One stylesheet to inline; per-chunk CSS would strand imports.
|
|
97
|
+
cssCodeSplit: false
|
|
98
|
+
},
|
|
99
|
+
builder: {
|
|
100
|
+
sharedPlugins: true,
|
|
101
|
+
async buildApp(builder) {
|
|
102
|
+
for (const environment of environments) {
|
|
103
|
+
const target = builder.environments[environment.name];
|
|
104
|
+
if (!target)
|
|
105
|
+
throw new Error(`mountly-mcp: missing Vite environment '${environment.name}'`);
|
|
106
|
+
await builder.build(target);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
environments: Object.fromEntries(
|
|
111
|
+
environments.map((environment, index) => [
|
|
112
|
+
environment.name,
|
|
113
|
+
{
|
|
114
|
+
// A custom environment defaults to a server consumer, which
|
|
115
|
+
// externalizes node_modules — React and Vue would become bare
|
|
116
|
+
// globals and the view would die on `vue is not defined`. A
|
|
117
|
+
// `ui://` resource has no import graph at runtime, so everything
|
|
118
|
+
// must be bundled in.
|
|
119
|
+
consumer: "client",
|
|
120
|
+
resolve: { noExternal: true },
|
|
121
|
+
build: {
|
|
122
|
+
emptyOutDir: index === 0,
|
|
123
|
+
lib: {
|
|
124
|
+
entry: environment.app.entry,
|
|
125
|
+
formats: ["iife"],
|
|
126
|
+
name: `__mountlyMcpEntry${index}`,
|
|
127
|
+
fileName: () => environment.jsFile,
|
|
128
|
+
cssFileName: environment.cssFile.replace(/\.css$/, "")
|
|
129
|
+
},
|
|
130
|
+
cssCodeSplit: false
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
])
|
|
134
|
+
)
|
|
135
|
+
};
|
|
136
|
+
},
|
|
137
|
+
configResolved(config) {
|
|
138
|
+
root = config.root;
|
|
139
|
+
outDir = resolve(config.root, config.build.outDir);
|
|
140
|
+
},
|
|
141
|
+
buildStart() {
|
|
142
|
+
const name = this.environment?.name;
|
|
143
|
+
if (apps.length > 1 && !byEnvironment.has(String(name))) {
|
|
144
|
+
this.error(
|
|
145
|
+
"mountly-mcp: multi-View builds require 'mountly-mcp build' (or 'vite build --app') so Vite builds every environment"
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
generateBundle: {
|
|
150
|
+
order: "post",
|
|
151
|
+
handler(_outputOptions, bundle) {
|
|
152
|
+
const environment = currentEnvironment(this);
|
|
153
|
+
if (!environment) return;
|
|
154
|
+
const chunk = bundle[environment.jsFile] ?? Object.values(bundle).find((item) => item.type === "chunk" && item.isEntry);
|
|
155
|
+
const cssAsset = bundle[environment.cssFile] ?? Object.values(bundle).find(
|
|
156
|
+
(item) => item.type === "asset" && item.fileName.endsWith(".css")
|
|
157
|
+
);
|
|
158
|
+
emitted.set(environment.name, {
|
|
159
|
+
js: chunk && "code" in chunk ? chunk.code : void 0,
|
|
160
|
+
css: cssAsset && "source" in cssAsset ? cssAsset.source.toString() : void 0,
|
|
161
|
+
jsFile: chunk?.fileName,
|
|
162
|
+
cssFile: cssAsset?.fileName,
|
|
163
|
+
names: Object.keys(bundle)
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
async closeBundle() {
|
|
168
|
+
const environment = currentEnvironment(this);
|
|
169
|
+
if (!environment || artifacts.has(environment.name)) return;
|
|
170
|
+
const source = emitted.get(environment.name);
|
|
171
|
+
if (source?.js === void 0) {
|
|
172
|
+
this.error(
|
|
173
|
+
`mountly-mcp: the build emitted no ${environment.jsFile} for '${environment.app.entry}'. Emitted: ${source?.names.join(", ") || "nothing"}`
|
|
174
|
+
);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const {
|
|
178
|
+
entry: _entry,
|
|
179
|
+
output: _output,
|
|
180
|
+
cleanIntermediate = true,
|
|
181
|
+
...resourceOptions
|
|
182
|
+
} = environment.app;
|
|
183
|
+
const result = await buildMcpResourceFromSource({
|
|
184
|
+
...resourceOptions,
|
|
185
|
+
js: source.js,
|
|
186
|
+
css: source.css,
|
|
187
|
+
output: outputPath(root, outDir, environment.app)
|
|
188
|
+
});
|
|
189
|
+
artifacts.set(environment.name, result.artifact);
|
|
190
|
+
if (cleanIntermediate) {
|
|
191
|
+
if (source.jsFile) await rm(join(outDir, source.jsFile), { force: true });
|
|
192
|
+
if (source.cssFile) await rm(join(outDir, source.cssFile), { force: true });
|
|
193
|
+
}
|
|
194
|
+
this.info?.(`mountly-mcp: ${environment.app.uri} \u2192 ${result.htmlPath}`);
|
|
195
|
+
if (!manifestWritten && artifacts.size === environments.length && manifestOption !== false) {
|
|
196
|
+
manifestWritten = true;
|
|
197
|
+
const path = manifestOption ? isAbsolute(manifestOption) ? manifestOption : resolve(root, manifestOption) : join(outDir, MCP_APP_MANIFEST_FILE);
|
|
198
|
+
await writeMcpAppManifest(
|
|
199
|
+
path,
|
|
200
|
+
environments.map((item) => artifacts.get(item.name))
|
|
201
|
+
);
|
|
202
|
+
this.info?.(`mountly-mcp: manifest \u2192 ${path}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
return plugin;
|
|
207
|
+
}
|
|
208
|
+
export {
|
|
209
|
+
mountlyMcpViews
|
|
210
|
+
};
|
|
211
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/vite/index.ts"],"sourcesContent":["import { rm } from \"node:fs/promises\";\nimport { basename, isAbsolute, join, resolve } from \"node:path\";\nimport type { BuildEnvironment, Plugin, UserConfig } from \"vite\";\nimport { MCP_APP_MANIFEST_FILE, writeMcpAppManifest } from \"../artifact/index.js\";\nimport type { McpAppArtifact } from \"../artifact/index.js\";\nimport { buildMcpResourceFromSource } from \"../build/index.js\";\nimport type { BuildSelfContainedOptions } from \"../build/index.js\";\n\nexport interface MountlyMcpViewOptions extends Omit<\n BuildSelfContainedOptions,\n \"entry\" | \"output\" | \"cssEntry\"\n> {\n entry: string;\n output?: string;\n cleanIntermediate?: boolean;\n}\n\nexport interface MountlyMcpViewsOptions {\n apps: ReadonlyArray<MountlyMcpViewOptions>;\n /** Defaults to `<outDir>/mountly-mcp.manifest.json`; false disables it. */\n manifest?: string | false;\n}\n\nexport type MountlyMcpViteOptions = MountlyMcpViewOptions | MountlyMcpViewsOptions;\n\nexport interface MountlyMcpViteApi {\n apps: ReadonlyArray<MountlyMcpViewOptions>;\n manifest?: string | false;\n}\n\nfunction isCollection(options: MountlyMcpViteOptions): options is MountlyMcpViewsOptions {\n return \"apps\" in options;\n}\n\nfunction safeBuildName(name: string, index: number): string {\n const safe = name.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n return `mountly_mcp_${index}_${safe || \"app\"}`;\n}\n\nfunction outputPath(root: string, outDir: string, app: MountlyMcpViewOptions): string {\n if (!app.output) return join(outDir, `${basename(app.name)}.html`);\n return isAbsolute(app.output) ? app.output : resolve(root, app.output);\n}\n\n/**\n * Build one or many independent MCP Apps Views. Each View is a Vite build\n * environment, so frameworks and CSS remain isolated while callers configure\n * the collection once.\n */\nexport function mountlyMcpViews(options: MountlyMcpViteOptions): Plugin {\n const configuredApps = isCollection(options) ? [...options.apps] : [options];\n const selectedForDev = process.env.MOUNTLY_MCP_SELECTED_APP;\n const apps = selectedForDev\n ? configuredApps.filter((app) => app.name === selectedForDev)\n : configuredApps;\n const manifestOption = isCollection(options) ? options.manifest : undefined;\n if (apps.length === 0) throw new Error(\"mountly-mcp: apps must contain at least one View\");\n\n const names = new Set<string>();\n const uris = new Set<string>();\n for (const app of apps) {\n if (!app.uri.startsWith(\"ui://\")) {\n throw new Error(\n `mountly-mcp: UI resource uri must use the 'ui://' scheme (received '${app.uri}')`,\n );\n }\n if (names.has(app.name)) throw new Error(`mountly-mcp: duplicate app name '${app.name}'`);\n if (uris.has(app.uri)) throw new Error(`mountly-mcp: duplicate app uri '${app.uri}'`);\n names.add(app.name);\n uris.add(app.uri);\n }\n\n const environments = apps.map((app, index) => ({\n app,\n name: safeBuildName(app.name, index),\n jsFile: `view-${index}-${basename(app.name)}.js`,\n cssFile: `view-${index}-${basename(app.name)}.css`,\n }));\n const byEnvironment = new Map(environments.map((environment) => [environment.name, environment]));\n const emitted = new Map<\n string,\n { js?: string; css?: string; jsFile?: string; cssFile?: string; names: string[] }\n >();\n const artifacts = new Map<string, McpAppArtifact>();\n let root = process.cwd();\n let outDir = resolve(root, \"dist\");\n let manifestWritten = false;\n\n function currentEnvironment(context: unknown) {\n const name = (context as { environment?: { name?: string } }).environment?.name;\n return (\n (name ? byEnvironment.get(name) : undefined) ??\n (environments.length === 1 ? environments[0] : undefined)\n );\n }\n\n const plugin: Plugin = {\n name: \"mountly-mcp-views\",\n api: {\n // The CLI consumes this documented plugin interface instead of scraping\n // Vite's resolved build settings.\n mountlyMcp: { apps: configuredApps, manifest: manifestOption } satisfies MountlyMcpViteApi,\n },\n // After Vite core plugins: css-post writes the stylesheet in its own\n // closeBundle, and this plugin has to see the file to inline it.\n enforce: \"post\",\n // Only meaningful during `vite build`; a dev server has no bundle to emit.\n apply: \"build\",\n\n config(_config, env): UserConfig {\n const first = environments[0];\n if (!first) throw new Error(\"mountly-mcp: configure at least one app\");\n return {\n // Vite's lib mode leaves `process.env.NODE_ENV` for a consuming bundler\n // to replace. There isn't one — the bundle is inlined into HTML and run\n // in a sandboxed iframe, where `process` is undefined and React and Vue\n // throw on their first environment check.\n define: { \"process.env.NODE_ENV\": JSON.stringify(env.mode) },\n // Legacy `vite build` builds one environment. Preserve that zero-config\n // path for single-View projects; multi-View projects use\n // `mountly-mcp build`, which drives the environments below.\n build: {\n lib: {\n entry: first.app.entry,\n // IIFE so the bundle inlines into a single <script> with no import\n // graph left to resolve inside the sandboxed iframe.\n formats: [\"iife\"] as const,\n name: \"__mountlyMcpEntry0\",\n fileName: () => first.jsFile,\n // Named explicitly: otherwise a lib build derives the stylesheet\n // name from package.json and fails outright when there isn't one.\n cssFileName: first.cssFile.replace(/\\.css$/, \"\"),\n },\n // One stylesheet to inline; per-chunk CSS would strand imports.\n cssCodeSplit: false,\n },\n builder: {\n sharedPlugins: true,\n async buildApp(builder) {\n for (const environment of environments) {\n const target = builder.environments[environment.name] as BuildEnvironment | undefined;\n if (!target)\n throw new Error(`mountly-mcp: missing Vite environment '${environment.name}'`);\n await builder.build(target);\n }\n },\n },\n environments: Object.fromEntries(\n environments.map((environment, index) => [\n environment.name,\n {\n // A custom environment defaults to a server consumer, which\n // externalizes node_modules — React and Vue would become bare\n // globals and the view would die on `vue is not defined`. A\n // `ui://` resource has no import graph at runtime, so everything\n // must be bundled in.\n consumer: \"client\" as const,\n resolve: { noExternal: true as const },\n build: {\n emptyOutDir: index === 0,\n lib: {\n entry: environment.app.entry,\n formats: [\"iife\"] as const,\n name: `__mountlyMcpEntry${index}`,\n fileName: () => environment.jsFile,\n cssFileName: environment.cssFile.replace(/\\.css$/, \"\"),\n },\n cssCodeSplit: false,\n },\n },\n ]),\n ),\n };\n },\n\n configResolved(config) {\n root = config.root;\n outDir = resolve(config.root, config.build.outDir);\n },\n\n buildStart() {\n const name = (this as { environment?: { name?: string } }).environment?.name;\n if (apps.length > 1 && !byEnvironment.has(String(name))) {\n this.error(\n \"mountly-mcp: multi-View builds require 'mountly-mcp build' (or 'vite build --app') so Vite builds every environment\",\n );\n }\n },\n\n generateBundle: {\n order: \"post\",\n handler(_outputOptions, bundle) {\n const environment = currentEnvironment(this);\n if (!environment) return;\n const chunk =\n bundle[environment.jsFile] ??\n Object.values(bundle).find((item) => item.type === \"chunk\" && item.isEntry);\n const cssAsset =\n bundle[environment.cssFile] ??\n Object.values(bundle).find(\n (item) => item.type === \"asset\" && item.fileName.endsWith(\".css\"),\n );\n emitted.set(environment.name, {\n js: chunk && \"code\" in chunk ? chunk.code : undefined,\n css: cssAsset && \"source\" in cssAsset ? cssAsset.source.toString() : undefined,\n jsFile: chunk?.fileName,\n cssFile: cssAsset?.fileName,\n names: Object.keys(bundle),\n });\n },\n },\n\n async closeBundle() {\n const environment = currentEnvironment(this);\n if (!environment || artifacts.has(environment.name)) return;\n const source = emitted.get(environment.name);\n if (source?.js === undefined) {\n this.error(\n `mountly-mcp: the build emitted no ${environment.jsFile} for '${environment.app.entry}'. Emitted: ${source?.names.join(\", \") || \"nothing\"}`,\n );\n return;\n }\n const {\n entry: _entry,\n output: _output,\n cleanIntermediate = true,\n ...resourceOptions\n } = environment.app;\n const result = await buildMcpResourceFromSource({\n ...resourceOptions,\n js: source.js,\n css: source.css,\n output: outputPath(root, outDir, environment.app),\n });\n artifacts.set(environment.name, result.artifact);\n if (cleanIntermediate) {\n if (source.jsFile) await rm(join(outDir, source.jsFile), { force: true });\n if (source.cssFile) await rm(join(outDir, source.cssFile), { force: true });\n }\n this.info?.(`mountly-mcp: ${environment.app.uri} → ${result.htmlPath}`);\n\n if (!manifestWritten && artifacts.size === environments.length && manifestOption !== false) {\n manifestWritten = true;\n const path = manifestOption\n ? isAbsolute(manifestOption)\n ? manifestOption\n : resolve(root, manifestOption)\n : join(outDir, MCP_APP_MANIFEST_FILE);\n await writeMcpAppManifest(\n path,\n environments.map((item) => artifacts.get(item.name) as McpAppArtifact),\n );\n this.info?.(`mountly-mcp: manifest → ${path}`);\n }\n },\n };\n\n return plugin;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vue entry point: turn a Vue component into an MCP App View.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* import { createMcpView } from "mountly-mcp/vue";
|
|
6
|
+
* import Dashboard from "./Dashboard.vue";
|
|
7
|
+
*
|
|
8
|
+
* createMcpView(Dashboard);
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* The component receives the bridge's props (`mcp`, `toolInput`, `toolResult`,
|
|
12
|
+
* `hostContext`) and can also reach them from any depth with the composables
|
|
13
|
+
* below, which mirror `mountly-mcp/react`'s hooks.
|
|
14
|
+
*/
|
|
15
|
+
import { type Component, type ComputedRef, type InjectionKey } from "vue";
|
|
16
|
+
import type { AdapterOptions } from "mountly/adapter";
|
|
17
|
+
import type { App, McpUiDisplayMode, McpUiHostContext } from "@modelcontextprotocol/ext-apps";
|
|
18
|
+
import type { McpView, McpViewProps } from "../types.js";
|
|
19
|
+
/** What {@link createMcpView} provides to descendants. */
|
|
20
|
+
export interface McpVueContext {
|
|
21
|
+
app: App;
|
|
22
|
+
toolInput?: McpViewProps["toolInput"];
|
|
23
|
+
toolInputPartial?: McpViewProps["toolInputPartial"];
|
|
24
|
+
toolResult?: McpViewProps["toolResult"];
|
|
25
|
+
hostContext?: McpUiHostContext;
|
|
26
|
+
}
|
|
27
|
+
export declare const McpInjectionKey: InjectionKey<ComputedRef<McpVueContext>>;
|
|
28
|
+
/** Wraps a Vue component as an MCP Apps View and publishes it for the bridge. */
|
|
29
|
+
export declare function createMcpView<P extends object>(Component: Component<P>, options?: AdapterOptions): McpView;
|
|
30
|
+
/** The view-side ext-apps App: call server tools, send messages, open links. */
|
|
31
|
+
export declare function useMcpApp(): App;
|
|
32
|
+
/** Complete notification params; read the tool arguments from `.arguments`. */
|
|
33
|
+
export declare function useToolInput<T = unknown>(): ComputedRef<T | undefined>;
|
|
34
|
+
/**
|
|
35
|
+
* Streaming tool arguments (`ui/notifications/tool-input-partial`), present only
|
|
36
|
+
* when the bridge runs with `streamToolInput`. Best-effort recovered JSON —
|
|
37
|
+
* render loading states from it, never critical operations.
|
|
38
|
+
*/
|
|
39
|
+
export declare function useToolInputPartial<T = unknown>(): ComputedRef<Partial<T> | undefined>;
|
|
40
|
+
/** Complete MCP tool result; structured data is available at `.structuredContent`. */
|
|
41
|
+
export declare function useToolResult<T = unknown>(): ComputedRef<T | undefined>;
|
|
42
|
+
/** Latest host context (theme, styles, displayMode, container dimensions). */
|
|
43
|
+
export declare function useHostContext(): ComputedRef<McpUiHostContext | undefined>;
|
|
44
|
+
/** Current display mode reported by the host. */
|
|
45
|
+
export declare function useDisplayMode(): ComputedRef<McpUiDisplayMode | undefined>;
|
|
46
|
+
/**
|
|
47
|
+
* Request a display mode change, resolving to the mode the host actually
|
|
48
|
+
* applied. Modes the host didn't advertise are never requested — the spec
|
|
49
|
+
* requires views to check `availableDisplayModes` first.
|
|
50
|
+
*/
|
|
51
|
+
export declare function useRequestDisplayMode(): (mode: McpUiDisplayMode) => Promise<McpUiDisplayMode | undefined>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {
|
|
2
|
+
publishMcpView
|
|
3
|
+
} from "../chunk-SADNMGP6.js";
|
|
4
|
+
|
|
5
|
+
// src/vue/index.ts
|
|
6
|
+
import {
|
|
7
|
+
computed,
|
|
8
|
+
defineComponent,
|
|
9
|
+
h,
|
|
10
|
+
inject,
|
|
11
|
+
provide
|
|
12
|
+
} from "vue";
|
|
13
|
+
import { createWidget } from "mountly-vue";
|
|
14
|
+
var McpInjectionKey = /* @__PURE__ */ Symbol("mountly-mcp");
|
|
15
|
+
function createMcpView(Component, options) {
|
|
16
|
+
const Wrapped = defineComponent({
|
|
17
|
+
name: "MountlyMcpProvider",
|
|
18
|
+
inheritAttrs: false,
|
|
19
|
+
props: {
|
|
20
|
+
mcp: { type: Object, required: true },
|
|
21
|
+
toolInput: { type: Object, default: void 0 },
|
|
22
|
+
toolInputPartial: { type: Object, default: void 0 },
|
|
23
|
+
toolResult: { type: Object, default: void 0 },
|
|
24
|
+
hostContext: { type: Object, default: void 0 }
|
|
25
|
+
},
|
|
26
|
+
setup(props, { attrs }) {
|
|
27
|
+
const context = computed(() => ({
|
|
28
|
+
app: props.mcp,
|
|
29
|
+
toolInput: props.toolInput,
|
|
30
|
+
toolInputPartial: props.toolInputPartial,
|
|
31
|
+
toolResult: props.toolResult,
|
|
32
|
+
hostContext: props.hostContext
|
|
33
|
+
}));
|
|
34
|
+
provide(McpInjectionKey, context);
|
|
35
|
+
return () => h(Component, attrs);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return publishMcpView(createWidget(Wrapped, options));
|
|
39
|
+
}
|
|
40
|
+
function useEnsuredContext() {
|
|
41
|
+
const context = inject(McpInjectionKey, null);
|
|
42
|
+
if (!context) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
"mountly-mcp/vue: useMcpApp/useToolResult/etc must be used inside a component wrapped with createMcpView()."
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return context;
|
|
48
|
+
}
|
|
49
|
+
function useMcpApp() {
|
|
50
|
+
return useEnsuredContext().value.app;
|
|
51
|
+
}
|
|
52
|
+
function useToolInput() {
|
|
53
|
+
const context = useEnsuredContext();
|
|
54
|
+
return computed(() => context.value.toolInput);
|
|
55
|
+
}
|
|
56
|
+
function useToolInputPartial() {
|
|
57
|
+
const context = useEnsuredContext();
|
|
58
|
+
return computed(() => context.value.toolInputPartial);
|
|
59
|
+
}
|
|
60
|
+
function useToolResult() {
|
|
61
|
+
const context = useEnsuredContext();
|
|
62
|
+
return computed(() => context.value.toolResult);
|
|
63
|
+
}
|
|
64
|
+
function useHostContext() {
|
|
65
|
+
const context = useEnsuredContext();
|
|
66
|
+
return computed(() => context.value.hostContext);
|
|
67
|
+
}
|
|
68
|
+
function useDisplayMode() {
|
|
69
|
+
const context = useEnsuredContext();
|
|
70
|
+
return computed(() => context.value.hostContext?.displayMode);
|
|
71
|
+
}
|
|
72
|
+
function useRequestDisplayMode() {
|
|
73
|
+
const context = useEnsuredContext();
|
|
74
|
+
return async (mode) => {
|
|
75
|
+
const { app, hostContext } = context.value;
|
|
76
|
+
const available = hostContext?.availableDisplayModes;
|
|
77
|
+
if (available && !available.includes(mode)) return hostContext?.displayMode;
|
|
78
|
+
const result = await app.requestDisplayMode({ mode });
|
|
79
|
+
return result.mode;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export {
|
|
83
|
+
McpInjectionKey,
|
|
84
|
+
createMcpView,
|
|
85
|
+
useDisplayMode,
|
|
86
|
+
useHostContext,
|
|
87
|
+
useMcpApp,
|
|
88
|
+
useRequestDisplayMode,
|
|
89
|
+
useToolInput,
|
|
90
|
+
useToolInputPartial,
|
|
91
|
+
useToolResult
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/vue/index.ts"],"sourcesContent":["/**\n * Vue entry point: turn a Vue component into an MCP App View.\n *\n * ```ts\n * import { createMcpView } from \"mountly-mcp/vue\";\n * import Dashboard from \"./Dashboard.vue\";\n *\n * createMcpView(Dashboard);\n * ```\n *\n * The component receives the bridge's props (`mcp`, `toolInput`, `toolResult`,\n * `hostContext`) and can also reach them from any depth with the composables\n * below, which mirror `mountly-mcp/react`'s hooks.\n */\nimport {\n computed,\n defineComponent,\n h,\n inject,\n provide,\n type Component,\n type ComputedRef,\n type InjectionKey,\n} from \"vue\";\nimport { createWidget } from \"mountly-vue\";\nimport type { AdapterOptions } from \"mountly/adapter\";\nimport type { App, McpUiDisplayMode, McpUiHostContext } from \"@modelcontextprotocol/ext-apps\";\nimport type { McpView, McpViewProps } from \"../types.js\";\nimport { publishMcpView } from \"../publish.js\";\n\n/** What {@link createMcpView} provides to descendants. */\nexport interface McpVueContext {\n app: App;\n toolInput?: McpViewProps[\"toolInput\"];\n toolInputPartial?: McpViewProps[\"toolInputPartial\"];\n toolResult?: McpViewProps[\"toolResult\"];\n hostContext?: McpUiHostContext;\n}\n\nexport const McpInjectionKey: InjectionKey<ComputedRef<McpVueContext>> = Symbol(\"mountly-mcp\");\n\n/** Wraps a Vue component as an MCP Apps View and publishes it for the bridge. */\nexport function createMcpView<P extends object>(\n Component: Component<P>,\n options?: AdapterOptions,\n): McpView {\n const Wrapped = defineComponent({\n name: \"MountlyMcpProvider\",\n inheritAttrs: false,\n props: {\n mcp: { type: Object, required: true },\n toolInput: { type: Object, default: undefined },\n toolInputPartial: { type: Object, default: undefined },\n toolResult: { type: Object, default: undefined },\n hostContext: { type: Object, default: undefined },\n },\n setup(props, { attrs }) {\n const context = computed<McpVueContext>(() => ({\n app: props.mcp as App,\n toolInput: props.toolInput as McpVueContext[\"toolInput\"],\n toolInputPartial: props.toolInputPartial as McpVueContext[\"toolInputPartial\"],\n toolResult: props.toolResult as McpVueContext[\"toolResult\"],\n hostContext: props.hostContext as McpUiHostContext | undefined,\n }));\n provide(McpInjectionKey, context);\n return () => h(Component as Component, attrs);\n },\n });\n return publishMcpView(createWidget(Wrapped, options));\n}\n\nfunction useEnsuredContext(): ComputedRef<McpVueContext> {\n const context = inject(McpInjectionKey, null);\n if (!context) {\n throw new Error(\n \"mountly-mcp/vue: useMcpApp/useToolResult/etc must be used inside a component wrapped with createMcpView().\",\n );\n }\n return context;\n}\n\n/** The view-side ext-apps App: call server tools, send messages, open links. */\nexport function useMcpApp(): App {\n return useEnsuredContext().value.app;\n}\n\n/** Complete notification params; read the tool arguments from `.arguments`. */\nexport function useToolInput<T = unknown>(): ComputedRef<T | undefined> {\n const context = useEnsuredContext();\n return computed(() => context.value.toolInput as T | undefined);\n}\n\n/**\n * Streaming tool arguments (`ui/notifications/tool-input-partial`), present only\n * when the bridge runs with `streamToolInput`. Best-effort recovered JSON —\n * render loading states from it, never critical operations.\n */\nexport function useToolInputPartial<T = unknown>(): ComputedRef<Partial<T> | undefined> {\n const context = useEnsuredContext();\n return computed(() => context.value.toolInputPartial as Partial<T> | undefined);\n}\n\n/** Complete MCP tool result; structured data is available at `.structuredContent`. */\nexport function useToolResult<T = unknown>(): ComputedRef<T | undefined> {\n const context = useEnsuredContext();\n return computed(() => context.value.toolResult as T | undefined);\n}\n\n/** Latest host context (theme, styles, displayMode, container dimensions). */\nexport function useHostContext(): ComputedRef<McpUiHostContext | undefined> {\n const context = useEnsuredContext();\n return computed(() => context.value.hostContext);\n}\n\n/** Current display mode reported by the host. */\nexport function useDisplayMode(): ComputedRef<McpUiDisplayMode | undefined> {\n const context = useEnsuredContext();\n return computed(() => context.value.hostContext?.displayMode);\n}\n\n/**\n * Request a display mode change, resolving to the mode the host actually\n * applied. Modes the host didn't advertise are never requested — the spec\n * requires views to check `availableDisplayModes` first.\n */\nexport function useRequestDisplayMode(): (\n mode: McpUiDisplayMode,\n) => Promise<McpUiDisplayMode | undefined> {\n const context = useEnsuredContext();\n return async (mode) => {\n const { app, hostContext } = context.value;\n const available = hostContext?.availableDisplayModes;\n if (available && !available.includes(mode)) return hostContext?.displayMode;\n const result = await app.requestDisplayMode({ mode });\n return result.mode;\n };\n}\n"],"mappings":";;;;;AAcA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP,SAAS,oBAAoB;AAetB,IAAM,kBAA4D,uBAAO,aAAa;AAGtF,SAAS,cACd,WACA,SACS;AACT,QAAM,UAAU,gBAAgB;AAAA,IAC9B,MAAM;AAAA,IACN,cAAc;AAAA,IACd,OAAO;AAAA,MACL,KAAK,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,MACpC,WAAW,EAAE,MAAM,QAAQ,SAAS,OAAU;AAAA,MAC9C,kBAAkB,EAAE,MAAM,QAAQ,SAAS,OAAU;AAAA,MACrD,YAAY,EAAE,MAAM,QAAQ,SAAS,OAAU;AAAA,MAC/C,aAAa,EAAE,MAAM,QAAQ,SAAS,OAAU;AAAA,IAClD;AAAA,IACA,MAAM,OAAO,EAAE,MAAM,GAAG;AACtB,YAAM,UAAU,SAAwB,OAAO;AAAA,QAC7C,KAAK,MAAM;AAAA,QACX,WAAW,MAAM;AAAA,QACjB,kBAAkB,MAAM;AAAA,QACxB,YAAY,MAAM;AAAA,QAClB,aAAa,MAAM;AAAA,MACrB,EAAE;AACF,cAAQ,iBAAiB,OAAO;AAChC,aAAO,MAAM,EAAE,WAAwB,KAAK;AAAA,IAC9C;AAAA,EACF,CAAC;AACD,SAAO,eAAe,aAAa,SAAS,OAAO,CAAC;AACtD;AAEA,SAAS,oBAAgD;AACvD,QAAM,UAAU,OAAO,iBAAiB,IAAI;AAC5C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,YAAiB;AAC/B,SAAO,kBAAkB,EAAE,MAAM;AACnC;AAGO,SAAS,eAAwD;AACtE,QAAM,UAAU,kBAAkB;AAClC,SAAO,SAAS,MAAM,QAAQ,MAAM,SAA0B;AAChE;AAOO,SAAS,sBAAwE;AACtF,QAAM,UAAU,kBAAkB;AAClC,SAAO,SAAS,MAAM,QAAQ,MAAM,gBAA0C;AAChF;AAGO,SAAS,gBAAyD;AACvE,QAAM,UAAU,kBAAkB;AAClC,SAAO,SAAS,MAAM,QAAQ,MAAM,UAA2B;AACjE;AAGO,SAAS,iBAA4D;AAC1E,QAAM,UAAU,kBAAkB;AAClC,SAAO,SAAS,MAAM,QAAQ,MAAM,WAAW;AACjD;AAGO,SAAS,iBAA4D;AAC1E,QAAM,UAAU,kBAAkB;AAClC,SAAO,SAAS,MAAM,QAAQ,MAAM,aAAa,WAAW;AAC9D;AAOO,SAAS,wBAE2B;AACzC,QAAM,UAAU,kBAAkB;AAClC,SAAO,OAAO,SAAS;AACrB,UAAM,EAAE,KAAK,YAAY,IAAI,QAAQ;AACrC,UAAM,YAAY,aAAa;AAC/B,QAAI,aAAa,CAAC,UAAU,SAAS,IAAI,EAAG,QAAO,aAAa;AAChE,UAAM,SAAS,MAAM,IAAI,mBAAmB,EAAE,KAAK,CAAC;AACpD,WAAO,OAAO;AAAA,EAChB;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,38 +1,180 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mountly-mcp",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
|
+
"description": "Build MCP Apps (SEP-1865) views from React, Vue or Svelte components — new or existing. One build emits the ui:// resource, its sidecar, and the MCP server wiring.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"chatgpt",
|
|
7
|
+
"claude",
|
|
8
|
+
"mcp",
|
|
9
|
+
"mcp-apps",
|
|
10
|
+
"mcp-ui",
|
|
11
|
+
"model-context-protocol",
|
|
12
|
+
"react",
|
|
13
|
+
"sep-1865",
|
|
14
|
+
"svelte",
|
|
15
|
+
"vite-plugin",
|
|
16
|
+
"vue",
|
|
17
|
+
"mcp-view"
|
|
18
|
+
],
|
|
5
19
|
"license": "Apache-2.0",
|
|
6
20
|
"repository": {
|
|
7
21
|
"type": "git",
|
|
8
22
|
"url": "https://github.com/jagreehal/mountly",
|
|
9
|
-
"directory": "packages/
|
|
23
|
+
"directory": "packages/mcp-apps"
|
|
10
24
|
},
|
|
25
|
+
"bin": {
|
|
26
|
+
"mountly-mcp": "./dist/cli.js"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"templates"
|
|
31
|
+
],
|
|
11
32
|
"type": "module",
|
|
12
33
|
"main": "./dist/index.js",
|
|
13
34
|
"types": "./dist/index.d.ts",
|
|
14
35
|
"exports": {
|
|
15
|
-
".": {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"import": "./dist/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./build": {
|
|
41
|
+
"types": "./dist/build/index.d.ts",
|
|
42
|
+
"import": "./dist/build/index.js"
|
|
43
|
+
},
|
|
44
|
+
"./artifact": {
|
|
45
|
+
"types": "./dist/artifact/index.d.ts",
|
|
46
|
+
"import": "./dist/artifact/index.js"
|
|
47
|
+
},
|
|
48
|
+
"./bridge": {
|
|
49
|
+
"types": "./dist/bridge/index.d.ts",
|
|
50
|
+
"import": "./dist/bridge/index.js"
|
|
51
|
+
},
|
|
52
|
+
"./bridge/iframe-entry.js": "./dist/bridge/iframe-entry.js",
|
|
53
|
+
"./react": {
|
|
54
|
+
"types": "./dist/react/index.d.ts",
|
|
55
|
+
"import": "./dist/react/index.js"
|
|
56
|
+
},
|
|
57
|
+
"./server": {
|
|
58
|
+
"types": "./dist/server/index.d.ts",
|
|
59
|
+
"import": "./dist/server/index.js"
|
|
60
|
+
},
|
|
61
|
+
"./json-render": {
|
|
62
|
+
"types": "./dist/json-render/index.d.ts",
|
|
63
|
+
"import": "./dist/json-render/index.js"
|
|
64
|
+
},
|
|
65
|
+
"./json-render/server": {
|
|
66
|
+
"types": "./dist/json-render/server.d.ts",
|
|
67
|
+
"import": "./dist/json-render/server.js"
|
|
68
|
+
},
|
|
69
|
+
"./json-render/app": {
|
|
70
|
+
"types": "./dist/json-render/app.d.ts",
|
|
71
|
+
"import": "./dist/json-render/app.js"
|
|
72
|
+
},
|
|
73
|
+
"./json-render/mcp": {
|
|
74
|
+
"types": "./dist/json-render/mcp.d.ts",
|
|
75
|
+
"import": "./dist/json-render/mcp.js"
|
|
76
|
+
},
|
|
77
|
+
"./vue": {
|
|
78
|
+
"types": "./dist/vue/index.d.ts",
|
|
79
|
+
"import": "./dist/vue/index.js"
|
|
80
|
+
},
|
|
81
|
+
"./svelte": {
|
|
82
|
+
"types": "./dist/svelte/index.d.ts",
|
|
83
|
+
"import": "./dist/svelte/index.js"
|
|
84
|
+
},
|
|
85
|
+
"./vite": {
|
|
86
|
+
"types": "./dist/vite/index.d.ts",
|
|
87
|
+
"import": "./dist/vite/index.js"
|
|
88
|
+
},
|
|
89
|
+
"./dev": {
|
|
90
|
+
"types": "./dist/dev/index.d.ts",
|
|
91
|
+
"import": "./dist/dev/index.js"
|
|
92
|
+
},
|
|
93
|
+
"./dev/host-entry.js": "./dist/dev/host-entry.js",
|
|
94
|
+
"./dev/sandbox-entry.js": "./dist/dev/sandbox-entry.js",
|
|
95
|
+
"./sandbox": {
|
|
96
|
+
"types": "./dist/sandbox/index.d.ts",
|
|
97
|
+
"import": "./dist/sandbox/index.js"
|
|
98
|
+
},
|
|
99
|
+
"./testing": {
|
|
100
|
+
"types": "./dist/testing/index.d.ts",
|
|
101
|
+
"import": "./dist/testing/index.js"
|
|
102
|
+
}
|
|
19
103
|
},
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
"scripts": {
|
|
23
|
-
"build": "tsup",
|
|
24
|
-
"typecheck": "tsc --noEmit",
|
|
25
|
-
"dev": "tsup --watch"
|
|
26
|
-
},
|
|
27
|
-
"peerDependencies": {
|
|
28
|
-
"mountly": "^0.2.2"
|
|
104
|
+
"publishConfig": {
|
|
105
|
+
"access": "public"
|
|
29
106
|
},
|
|
30
107
|
"dependencies": {
|
|
31
|
-
"@modelcontextprotocol/ext-apps": "^1.7.
|
|
108
|
+
"@modelcontextprotocol/ext-apps": "^1.7.5",
|
|
109
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
110
|
+
"mountly": "^0.4.0",
|
|
111
|
+
"mountly-react": "^2.0.2",
|
|
112
|
+
"zod": "^4.4.3"
|
|
32
113
|
},
|
|
33
114
|
"devDependencies": {
|
|
34
|
-
"
|
|
115
|
+
"@json-render/core": "0.20.0",
|
|
116
|
+
"@json-render/react": "0.20.0",
|
|
117
|
+
"@testing-library/react": "^16.3.2",
|
|
118
|
+
"@types/react": "^19.2.18",
|
|
119
|
+
"@types/react-dom": "^19.2.4",
|
|
120
|
+
"ai": "6.0.208",
|
|
121
|
+
"axe-core": "4.13.0",
|
|
122
|
+
"jsdom": "^30.0.1",
|
|
123
|
+
"react": "^19.2.8",
|
|
124
|
+
"react-dom": "^19.2.8",
|
|
35
125
|
"tsup": "^8.5.1",
|
|
36
|
-
"typescript": "^
|
|
126
|
+
"typescript": "^7.0.2",
|
|
127
|
+
"vite": "^8.1.5",
|
|
128
|
+
"mountly-svelte": "^2.0.3",
|
|
129
|
+
"mountly-vue": "^2.0.3"
|
|
130
|
+
},
|
|
131
|
+
"peerDependencies": {
|
|
132
|
+
"@json-render/core": "^0.20.0",
|
|
133
|
+
"@json-render/react": "^0.20.0",
|
|
134
|
+
"ai": "^7.0.65",
|
|
135
|
+
"axe-core": ">=4.10.0",
|
|
136
|
+
"mountly-svelte": "^2.0.3",
|
|
137
|
+
"mountly-vue": "^2.0.3",
|
|
138
|
+
"playwright": ">=1.62.1",
|
|
139
|
+
"react": "^19",
|
|
140
|
+
"react-dom": "^19",
|
|
141
|
+
"vite": "^8.2.1"
|
|
142
|
+
},
|
|
143
|
+
"peerDependenciesMeta": {
|
|
144
|
+
"@json-render/core": {
|
|
145
|
+
"optional": true
|
|
146
|
+
},
|
|
147
|
+
"@json-render/react": {
|
|
148
|
+
"optional": true
|
|
149
|
+
},
|
|
150
|
+
"ai": {
|
|
151
|
+
"optional": true
|
|
152
|
+
},
|
|
153
|
+
"react": {
|
|
154
|
+
"optional": true
|
|
155
|
+
},
|
|
156
|
+
"react-dom": {
|
|
157
|
+
"optional": true
|
|
158
|
+
},
|
|
159
|
+
"mountly-vue": {
|
|
160
|
+
"optional": true
|
|
161
|
+
},
|
|
162
|
+
"mountly-svelte": {
|
|
163
|
+
"optional": true
|
|
164
|
+
},
|
|
165
|
+
"playwright": {
|
|
166
|
+
"optional": true
|
|
167
|
+
},
|
|
168
|
+
"vite": {
|
|
169
|
+
"optional": true
|
|
170
|
+
},
|
|
171
|
+
"axe-core": {
|
|
172
|
+
"optional": true
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
"scripts": {
|
|
176
|
+
"build": "tsup && tsc -p tsconfig.json --noEmit false --declaration --emitDeclarationOnly --declarationMap false --outDir dist",
|
|
177
|
+
"typecheck": "tsc --noEmit",
|
|
178
|
+
"dev": "tsup --watch"
|
|
37
179
|
}
|
|
38
|
-
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# {{name}}
|
|
2
|
+
|
|
3
|
+
MCP App scaffolded with `mountly-mcp create`.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pnpm install
|
|
7
|
+
pnpm dev # sandboxed host on :5179
|
|
8
|
+
pnpm verify # conformance
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- View: `src/view.tsx` (`createMcpView`)
|
|
12
|
+
- Server: `server.mjs` (`registerMcpApps`)
|
|
13
|
+
- Fixtures: `mcp.fixtures.json` (tool args when using `--server`)
|
|
14
|
+
|
|
15
|
+
Do not clone the Mountly monorepo for greenfield apps — this project is the canonical path.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "vite build",
|
|
8
|
+
"dev": "mountly-mcp dev --server ./server.mjs",
|
|
9
|
+
"verify": "mountly-mcp verify",
|
|
10
|
+
"verify:strict": "mountly-mcp verify --strict",
|
|
11
|
+
"typecheck": "tsc --noEmit"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
15
|
+
"mountly-mcp": "^3.1.0",
|
|
16
|
+
"react": "^19.2.8",
|
|
17
|
+
"react-dom": "^19.2.8",
|
|
18
|
+
"zod": "^4.4.3"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/react": "^19.2.18",
|
|
22
|
+
"@types/react-dom": "^19.2.4",
|
|
23
|
+
"@vitejs/plugin-react": "^5.0.0",
|
|
24
|
+
"typescript": "^5.9.0",
|
|
25
|
+
"vite": "^8.2.1"
|
|
26
|
+
}
|
|
27
|
+
}
|