mountly-mcp 4.0.1 → 4.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 (59) hide show
  1. package/GLOSSARY.md +37 -0
  2. package/README.md +75 -197
  3. package/dist/add-DAFMA4F3.js +263 -0
  4. package/dist/add-DAFMA4F3.js.map +1 -0
  5. package/dist/add.d.ts +13 -0
  6. package/dist/bridge/iframe-entry.js +3 -1
  7. package/dist/bridge/index.js +1 -1
  8. package/dist/{chunk-JVQZGKBL.js → chunk-2HNXL5A4.js} +4 -2
  9. package/dist/chunk-2HNXL5A4.js.map +1 -0
  10. package/dist/{chunk-SADNMGP6.js → chunk-5L3QHYWK.js} +1 -1
  11. package/dist/{chunk-SADNMGP6.js.map → chunk-5L3QHYWK.js.map} +1 -1
  12. package/dist/chunk-EOWAWZH2.js +34 -0
  13. package/dist/chunk-EOWAWZH2.js.map +1 -0
  14. package/dist/{chunk-UECZA34Q.js → chunk-OWAXBNZA.js} +10 -5
  15. package/dist/chunk-OWAXBNZA.js.map +1 -0
  16. package/dist/{chunk-AEVQKFXX.js → chunk-SXF23BZD.js} +2 -2
  17. package/dist/cli.js +96 -24
  18. package/dist/cli.js.map +1 -1
  19. package/dist/connect-server-P2UZ62YC.js +7 -0
  20. package/dist/create.js +43 -15
  21. package/dist/create.js.map +1 -1
  22. package/dist/dev/index.js +1 -1
  23. package/dist/doctor-B54H6CRN.js +122 -0
  24. package/dist/doctor-B54H6CRN.js.map +1 -0
  25. package/dist/doctor.d.ts +16 -0
  26. package/dist/errors-4ERW7HZB.js +11 -0
  27. package/dist/errors-4ERW7HZB.js.map +1 -0
  28. package/dist/errors.d.ts +25 -0
  29. package/dist/index.js +2 -2
  30. package/dist/json-render/index.js +2 -2
  31. package/dist/react/index.js +2 -2
  32. package/dist/svelte/index.js +1 -1
  33. package/dist/vue/index.js +1 -1
  34. package/package.json +14 -11
  35. package/templates/react/README.md.tmpl +83 -6
  36. package/templates/react/package.json.tmpl +2 -0
  37. package/templates/react/serve-stdio.mjs.tmpl +9 -0
  38. package/templates/react/server.mjs.tmpl +6 -1
  39. package/templates/react/src/view.tsx.tmpl +1 -1
  40. package/templates/svelte/README.md.tmpl +27 -4
  41. package/templates/svelte/package.json.tmpl +2 -0
  42. package/templates/svelte/serve-stdio.mjs.tmpl +5 -0
  43. package/templates/svelte/server.mjs.tmpl +6 -1
  44. package/templates/svelte/src/Dashboard.svelte.tmpl +1 -1
  45. package/templates/vanilla/README.md.tmpl +27 -11
  46. package/templates/vanilla/package.json.tmpl +2 -0
  47. package/templates/vanilla/serve-stdio.mjs.tmpl +5 -0
  48. package/templates/vanilla/server.mjs.tmpl +6 -1
  49. package/templates/vanilla/src/view.ts.tmpl +1 -1
  50. package/templates/vue/README.md.tmpl +27 -4
  51. package/templates/vue/package.json.tmpl +2 -0
  52. package/templates/vue/serve-stdio.mjs.tmpl +5 -0
  53. package/templates/vue/server.mjs.tmpl +6 -1
  54. package/templates/vue/src/Dashboard.vue.tmpl +1 -1
  55. package/dist/chunk-JVQZGKBL.js.map +0 -1
  56. package/dist/chunk-UECZA34Q.js.map +0 -1
  57. package/dist/connect-server-7L4HGKKL.js +0 -7
  58. /package/dist/{chunk-AEVQKFXX.js.map → chunk-SXF23BZD.js.map} +0 -0
  59. /package/dist/{connect-server-7L4HGKKL.js.map → connect-server-P2UZ62YC.js.map} +0 -0
package/GLOSSARY.md ADDED
@@ -0,0 +1,37 @@
1
+ # Mountly MCP glossary
2
+
3
+ Public vocabulary for `mountly-mcp`. Skills, templates, CLI help, README, and
4
+ docs must use these names.
5
+
6
+ | Term | Meaning |
7
+ | ------------------------ | -------------------------------------------------------------------------------------------- |
8
+ | **App** | MCP server plus its Views (SEP-1865 product). |
9
+ | **View** | Iframe UI for a tool result (`ui://` resource). |
10
+ | **`ui://`** | Required scheme for View resource URIs. |
11
+ | **`createMcpView`** | Wrap a React / Vue / Svelte component (`mountly-mcp/react` etc.). Vanilla: `publishMcpView`. |
12
+ | **`mountlyMcpViews`** | Vite plugin. Emits View HTML and `dist/mountly-mcp.manifest.json`. |
13
+ | **`registerMcpApps`** | Install Views and UI-linked tools on an unconnected `McpServer`. |
14
+ | **`readMcpAppManifest`** | Load `dist/mountly-mcp.manifest.json` from `mountly-mcp/artifact`. |
15
+ | **`mcp.fixtures.json`** | Named samples for `mountly-mcp dev` (tool args with `--server`, else `structuredContent`). |
16
+ | **`create`** | `mountly-mcp create`: scaffold a greenfield App. |
17
+ | **`add`** | `mountly-mcp add`: add a second View to an existing Mountly Vite project. |
18
+ | **`build`** | `mountly-mcp build`: build Views via Vite. |
19
+ | **`dev`** | `mountly-mcp dev`: local sandboxed AppBridge host with live reload. |
20
+ | **`verify`** | `mountly-mcp verify`: conformance (`--strict`, `--render`). |
21
+ | **`doctor`** | `mountly-mcp doctor`: check Node, Vite plugin, Playwright, peer floors. |
22
+
23
+ ## Do not put in MCP docs / skills / templates
24
+
25
+ - Islands vocabulary, HTML island attributes, microfrontends
26
+ - Cloning `jagreehal/mountly` or `modelcontextprotocol/ext-apps` for greenfield Views
27
+ - Hand-writing View HTML or postMessage for Views
28
+ - Calling Mountly the "default view layer" until hosts recommend it
29
+
30
+ ## Happy path
31
+
32
+ ```bash
33
+ npx mountly-mcp create my-app --framework react # react | vue | svelte | vanilla
34
+ cd my-app && pnpm install && pnpm dev
35
+ pnpm verify
36
+ pnpm verify --strict --render # CI
37
+ ```
package/README.md CHANGED
@@ -1,12 +1,16 @@
1
1
  # mountly-mcp
2
2
 
3
- **Build MCP Apps from React, Vue, or Svelte components — new or existing.**
3
+ Build MCP Apps Views from React, Vue, or Svelte. New apps or components you
4
+ already ship.
4
5
 
5
- [MCP Apps](https://github.com/modelcontextprotocol/ext-apps) (SEP-1865) lets an
6
- MCP server render interactive UI inside Claude, ChatGPT and other hosts. The
7
- official SDK gives you the protocol. `mountly-mcp` is the view layer on top:
8
- write a component, get a `ui://` resource, a local host, and server registration
9
- without hand-writing the handshake HTML.
6
+ [MCP Apps](https://github.com/modelcontextprotocol/ext-apps) (SEP-1865) is the
7
+ protocol. Official packages speak the wire. `mountly-mcp` is the View kit:
8
+ component → `ui://` resource → local host → `registerMcpApps` on your server.
9
+
10
+ ```bash
11
+ npx mountly-mcp create my-app --framework react
12
+ cd my-app && pnpm install && pnpm dev
13
+ ```
10
14
 
11
15
  ```ts
12
16
  import { createMcpView } from "mountly-mcp/react";
@@ -15,81 +19,69 @@ import Dashboard from "./Dashboard";
15
19
  createMcpView(Dashboard);
16
20
  ```
17
21
 
18
- That path is the default whether the dashboard is three metric cards for one
19
- host or a multi-View production server. Prefer the scaffolder for greenfield:
22
+ `--framework`: `react` | `vue` | `svelte` | `vanilla`. Vanilla calls
23
+ `publishMcpView` with `mount` / `update` / `unmount`. No framework runtime.
20
24
 
21
- ```bash
22
- npx mountly-mcp create my-app --framework react
23
- cd my-app && pnpm install && pnpm dev
24
- ```
25
+ ## vs ext-apps
26
+
27
+ | Need | Package |
28
+ | ------------------------------------------------------- | -------------------------------- |
29
+ | Host / bridge protocol | `@modelcontextprotocol/ext-apps` |
30
+ | Component → View → `dev` → `verify` → `registerMcpApps` | `mountly-mcp` |
25
31
 
26
- `--framework` takes `react`, `vue`, `svelte`, or `vanilla`. Vanilla pulls in no
27
- framework and no adapter: a View is just `mount` / `update` / `unmount`, so
28
- `publishMcpView` takes the object directly and the bridge drives it the same
29
- way it drives the others.
32
+ Details: [Mountly vs ext-apps](https://mountly.dev/mcp-apps/vs-ext-apps/).
30
33
 
31
- ## Build with Agent Skills
34
+ ## Agent Skills
32
35
 
33
- | Skill | Try it |
34
- | --- | --- |
35
- | [`create-mcp-app`](../../plugins/mountly-mcp/skills/create-mcp-app/SKILL.md) | _"Create an MCP App"_ |
36
- | [`add-app-to-server`](../../plugins/mountly-mcp/skills/add-app-to-server/SKILL.md) | _"Add UI to my MCP server"_ |
37
- | [`convert-web-app`](../../plugins/mountly-mcp/skills/convert-web-app/SKILL.md) | _"Turn my component into an MCP App"_ |
38
- | [`migrate-ext-apps`](../../plugins/mountly-mcp/skills/migrate-ext-apps/SKILL.md) | _"Migrate from ext-apps"_ |
36
+ | Skill | Ask for |
37
+ | ---------------------------------------------------------------------------------- | ----------------------------------- |
38
+ | [`create-mcp-app`](../../plugins/mountly-mcp/skills/create-mcp-app/SKILL.md) | "Create an MCP App" |
39
+ | [`add-app-to-server`](../../plugins/mountly-mcp/skills/add-app-to-server/SKILL.md) | "Add UI to my MCP server" |
40
+ | [`convert-web-app`](../../plugins/mountly-mcp/skills/convert-web-app/SKILL.md) | "Turn my component into an MCP App" |
41
+ | [`migrate-ext-apps`](../../plugins/mountly-mcp/skills/migrate-ext-apps/SKILL.md) | "Migrate from ext-apps" |
39
42
 
40
43
  ```
41
44
  /plugin marketplace add jagreehal/mountly
42
45
  /plugin install mountly-mcp@mountly
43
46
  ```
44
47
 
45
- Docs: [Agent Skills](https://mountly.dev/mcp-apps/agent-skills/).
46
- `mountly-mcp build` emits a versioned App manifest; `registerMcpApps` installs
47
- the Views into the MCP server you already own. Existing website components stay
48
- reusable as-is.
49
-
50
- The wire protocol is delegated to the official `@modelcontextprotocol/sdk` and
51
- `@modelcontextprotocol/ext-apps` packages in full, so server transports,
52
- handshakes, capabilities and message types track the spec rather than a
53
- reimplementation of it. Both are runtime dependencies of `mountly-mcp`.
48
+ Or `npx skills add jagreehal/mountly`. Docs: [Agent Skills](https://mountly.dev/mcp-apps/agent-skills/).
54
49
 
55
50
  ## Install
56
51
 
57
52
  ```bash
58
- # React (default) — mountly + mountly-react included
53
+ # React (default). mountly + mountly-react are included.
59
54
  npm install mountly-mcp react react-dom
60
55
 
61
56
  # Vue / Svelte
62
57
  npm install mountly-mcp mountly-vue vue # or mountly-svelte + svelte
63
58
  ```
64
59
 
65
- Install `@modelcontextprotocol/sdk` directly only when your application imports
66
- its APIs itself, for example to configure a custom HTTP transport. Mountly's
67
- server entry point does not require a separate SDK install.
60
+ Add `@modelcontextprotocol/sdk` when you import `McpServer`.
61
+
62
+ Peers: Node 20+ (22 preferred), React 18 or 19. Scaffolds pin Vite 8.
63
+ `npx mountly-mcp doctor` checks a project.
68
64
 
69
- ## One build, one command
65
+ ## Build
70
66
 
71
67
  ```ts
72
68
  // vite.config.ts
73
- import vue from "@vitejs/plugin-vue";
69
+ import { defineConfig } from "vite";
70
+ import react from "@vitejs/plugin-react";
74
71
  import { mountlyMcpViews } from "mountly-mcp/vite";
75
72
 
76
73
  export default defineConfig({
77
74
  plugins: [
78
- vue(),
75
+ react(),
79
76
  mountlyMcpViews({
80
77
  apps: [
81
78
  {
82
- entry: "src/dashboard.ts",
79
+ entry: "src/view.tsx",
83
80
  uri: "ui://weather-server/dashboard",
84
81
  name: "weather_dashboard",
85
82
  displayModes: ["inline", "fullscreen"],
86
83
  csp: { connectDomains: ["https://api.weather.com"] },
87
84
  },
88
- {
89
- entry: "src/settings.ts",
90
- uri: "ui://weather-server/settings",
91
- name: "weather_settings",
92
- },
93
85
  ],
94
86
  }),
95
87
  ],
@@ -100,38 +92,19 @@ export default defineConfig({
100
92
  npx mountly-mcp build
101
93
  ```
102
94
 
103
- Each View is built in an isolated Vite environment and emitted as self-contained
104
- HTML with a transitional `.meta.json` file. The canonical
105
- `dist/mountly-mcp.manifest.json` gives build, dev, registration, and CI one
106
- versioned artifact contract. The original single-View plugin shape and
107
- `vite build` remain supported.
95
+ Writes HTML and `dist/mountly-mcp.manifest.json`.
108
96
 
109
- ## One command to develop it
97
+ ## Develop
110
98
 
111
99
  ```bash
112
- npx mountly-mcp dev
113
- # multi-View project
114
- npx mountly-mcp dev --app weather_dashboard
100
+ npx mountly-mcp dev --server ./server.mjs
101
+ npx mountly-mcp dev --app weather_dashboard --server ./server.mjs
115
102
  ```
116
103
 
117
- Builds the View, serves it in a local MCP Apps host, and rebuilds on save.
118
- Config comes from the plugin already in your vite config.
104
+ Real AppBridge host plus sandbox CSP. Tool arguments come from
105
+ `mcp.fixtures.json`.
119
106
 
120
- It's a real host, not a preview: the published official `AppBridge` drives the
121
- handshake and host behavior, while Mountly supplies two origins and a sandbox
122
- proxy enforcing the CSP and permissions the App artifact declares. Sample tool
123
- results come from `mcp.fixtures.json`,
124
- one button each, so clicking between them exercises the update path:
125
-
126
- ```json
127
- { "annual": { "total": 99 }, "monthly": { "total": 12 } }
128
- ```
129
-
130
- Add `--server ./server.js` (a module default-exporting your server) and the
131
- fixtures become tool _arguments_ instead: the view renders what your handler
132
- really returns, and the tool calls the view makes itself are routed there too.
133
-
134
- ## Install into your production server
107
+ ## Production server
135
108
 
136
109
  ```ts
137
110
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -153,152 +126,57 @@ await registerMcpApps(server, {
153
126
  config: {
154
127
  inputSchema: { type: "object", properties: { location: { type: "string" } } },
155
128
  },
156
- handler: async ({ location }) => ({ structuredContent: { location, temperature: 72 } }),
129
+ handler: async ({ location }) => ({
130
+ structuredContent: { location, temperature: 72 },
131
+ }),
157
132
  },
158
133
  ],
159
134
  });
135
+ // connect with YOUR transport after this
160
136
  ```
161
137
 
162
- Call `registerMcpApps` before connecting the server. Your application retains
163
- ownership of authentication, transport, prompts, non-UI tools, lifecycle, and
164
- deployment. Views and tools are independent, so several tools can link to one
165
- View and app-only tools use `visibility: "app"`. Mountly negotiates the UI
166
- extension and degrades to text-only model tools for hosts without MCP Apps.
167
-
168
- `createMcpAppServer` and `serveStdio` remain as convenience adapters for small
169
- servers and existing projects.
138
+ Call `registerMcpApps` before `connect`. You keep auth, transport, and deploy.
170
139
 
171
- ## Verify in CI
140
+ ## Verify
172
141
 
173
142
  ```bash
143
+ npx mountly-mcp doctor
174
144
  npx mountly-mcp verify
175
- npx mountly-mcp verify --strict
176
- npx mountly-mcp verify --render --strict
145
+ npx mountly-mcp verify --strict --render
177
146
  ```
178
147
 
179
- Static verification is deterministic and offline. `--render` additionally
180
- launches Chromium, waits for the explicit bridge mount state, and rejects Views
181
- that time out, throw, or mount without content. Errors always fail; advisory
182
- warnings fail only under `--strict`. The same engine is exported as
183
- `verifyMcpApps()` from `mountly-mcp/testing` for custom test runners. Install
184
- Playwright and Chromium for the browser tier.
185
-
186
- ## Reading tool data in a view
187
-
188
- The bridge exposes `mcp`, `toolInput`, `toolResult`, and `hostContext`. React
189
- and Vue consume those bridge props in their wrapper and expose them through
190
- hooks/composables, leaving the existing component's props unchanged. Svelte
191
- receives them directly as component props:
192
-
193
- ```tsx
194
- // React
195
- const result = useToolResult<{ structuredContent?: Quote }>();
196
- const quote = result?.structuredContent;
197
- useHostStyles(); // adopt the host's theme variables and fonts
198
- ```
199
-
200
- ```ts
201
- // Vue
202
- const result = useToolResult<{ structuredContent?: Quote }>(); // a ref
203
- const quote = computed(() => result.value?.structuredContent);
204
- ```
205
-
206
- Svelte reads the props directly via `$props()` — see `mountly-mcp/svelte`.
207
-
208
- ## json-render MCP Apps integration
209
-
210
- Mountly now exposes a first-class json-render MCP facade so catalog-driven UIs
211
- can be shipped as MCP Apps with minimal protocol code:
212
-
213
- ```ts
214
- import { createJsonRenderMcpApp } from "mountly-mcp/json-render/mcp";
215
- import { catalog } from "./catalog.js";
216
- import { html } from "./dist/app-html.js";
217
-
218
- const app = createJsonRenderMcpApp({
219
- name: "json-render-dashboard",
220
- version: "1.0.0",
221
- catalog,
222
- html,
223
- tool: {
224
- name: "render_ui",
225
- title: "Render UI",
226
- description: "Render an interactive dashboard from a json-render spec.",
227
- resourceUri: "ui://json-render-dashboard/view.html",
228
- },
229
- });
230
- ```
231
-
232
- For iframe-side React hosts, use `mountly-mcp/json-render/app`:
233
-
234
- ```tsx
235
- import { useJsonRenderApp } from "mountly-mcp/json-render/app";
148
+ ## Second View
236
149
 
237
- function View() {
238
- const { spec, loading, connected, error } = useJsonRenderApp({
239
- name: "json-render-dashboard",
240
- version: "1.0.0",
241
- });
242
-
243
- if (error) return <div>{error.message}</div>;
244
- if (!connected || loading || !spec) return <div>Waiting…</div>;
245
- return <pre>{JSON.stringify(spec, null, 2)}</pre>;
246
- }
150
+ ```bash
151
+ npx mountly-mcp add settings --framework react
247
152
  ```
248
153
 
249
- `buildAppHtml({ title, js, css })` is also exported from
250
- `mountly-mcp/json-render/app` to generate a self-contained HTML resource.
154
+ ## Glossary
251
155
 
252
- ### Interoperability with `@json-render/mcp`
156
+ [GLOSSARY.md](./GLOSSARY.md). Protocol: [`@modelcontextprotocol/ext-apps`](https://www.npmjs.com/package/@modelcontextprotocol/ext-apps).
253
157
 
254
- Mountly's facade is wire-compatible with [`@json-render/mcp`](https://www.npmjs.com/package/@json-render/mcp) in both
255
- directions, so you can mix the two without a bridge:
158
+ ## Advanced: json-render
256
159
 
257
- - **Tool input** is `{ spec }`, typed from `catalog.zodSchema()`, and the tool
258
- description carries `catalog.prompt()` — the model sees the full component
259
- vocabulary.
260
- - **Tool output** carries the spec twice: as JSON in `content[0].text` (the
261
- shape `@json-render/mcp`'s iframe hook reads) and as `structuredContent.spec`
262
- (what Mountly's own hosts read).
263
- - **`useJsonRenderApp`** reads either shape, so an iframe built with Mountly
264
- renders a `@json-render/mcp` server unchanged, and vice versa.
265
-
266
- One deliberate difference: `@json-render/core` 0.19 generates a _strict_ catalog
267
- schema that requires `visible` and `children` on every element, which rejects
268
- most first-attempt model output at the SDK's input gate. Mountly accepts the
269
- catalog schema **or** a looser spec shape, then validates in the handler and
270
- renders what it can. Drop the loose branch once core relaxes those fields.
160
+ `mountly-mcp/json-render` is optional catalog-driven UI. Start with `create` /
161
+ `createMcpView`. Docs: [Generative UI](https://mountly.dev/concepts/generative-ui/).
271
162
 
272
163
  ## Exports
273
164
 
274
- | Entry | What's in it |
275
- | ---------------------- | -------------------------------------------------------- |
276
- | `mountly-mcp` | `runBridge`, spec types re-exported from ext-apps |
277
- | `mountly-mcp/react` | `createMcpView` + hooks |
278
- | `mountly-mcp/vue` | `createMcpView` + composables |
279
- | `mountly-mcp/svelte` | `createMcpView` (props-based) |
280
- | `mountly-mcp/vite` | single- and multi-View `mountlyMcpViews()` build plugin |
281
- | `mountly-mcp/artifact` | versioned manifest and App artifact APIs |
282
- | `mountly-mcp/dev` | `startDevHost()` and in-process server connectivity |
283
- | `mountly-mcp/build` | `buildMcpResource` for other bundlers |
284
- | `mountly-mcp/server` | `registerMcpApps` plus convenience server adapters |
285
- | `mountly-mcp/testing` | static/browser conformance and report formatting |
286
-
287
- Types use the spec's own names (`McpUiHostContext`, `McpUiDisplayMode`, …) —
288
- one vocabulary, no translation layer.
289
-
290
- ## Agent Skills
291
-
292
- Install the plugin skills and ask your coding agent to create an MCP App:
293
- [Agent Skills docs](https://mountly.dev/mcp-apps/agent-skills/) ·
294
- [`create-mcp-app`](../../plugins/mountly-mcp/skills/create-mcp-app/SKILL.md).
295
-
296
- Greenfield without an agent: `npx mountly-mcp create my-app --framework react`.
165
+ | Entry | Contents |
166
+ | ------------------------- | ------------------------------------ |
167
+ | `mountly-mcp` | `runBridge`, `publishMcpView`, types |
168
+ | `mountly-mcp/react` | `createMcpView` + hooks |
169
+ | `mountly-mcp/vue` | `createMcpView` + composables |
170
+ | `mountly-mcp/svelte` | `createMcpView` (props) |
171
+ | `mountly-mcp/vite` | `mountlyMcpViews()` |
172
+ | `mountly-mcp/artifact` | Manifest APIs |
173
+ | `mountly-mcp/server` | `registerMcpApps` |
174
+ | `mountly-mcp/dev` | Local host helpers |
175
+ | `mountly-mcp/testing` | `verifyMcpApps` |
176
+ | `mountly-mcp/json-render` | Generative path |
297
177
 
298
178
  ## See also
299
179
 
300
- - [`docs/examples/mcp-release-readiness`](../../docs/examples/mcp-release-readiness) —
301
- production-shaped Vite View (closest to `create`)
302
- - [`docs/examples/mcp-app-demo`](../../docs/examples/mcp-app-demo) — advanced
303
- protocol harness (not the greenfield starter)
304
- - `docs/how-to-test.md`
180
+ - [`mcp-release-readiness`](../../docs/examples/mcp-release-readiness) — production-shaped Vite View
181
+ - [`mcp-app-demo`](../../docs/examples/mcp-app-demo) — protocol harness
182
+ - [Host matrix](https://mountly.dev/mcp-apps/host-matrix/)
@@ -0,0 +1,263 @@
1
+ import {
2
+ CLI_ERROR_CODES,
3
+ cliError
4
+ } from "./chunk-EOWAWZH2.js";
5
+
6
+ // src/add.ts
7
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
8
+ import { dirname, join, resolve } from "path";
9
+ function parseAddArgs(argv) {
10
+ const name = argv[0];
11
+ if (!name || name.startsWith("-")) {
12
+ throw cliError(
13
+ CLI_ERROR_CODES.UNKNOWN_OPTION,
14
+ "mountly-mcp add requires a View name",
15
+ "Example: mountly-mcp add settings --framework react"
16
+ );
17
+ }
18
+ const args = { name };
19
+ for (let i = 1; i < argv.length; i += 1) {
20
+ const arg = argv[i];
21
+ const next = () => {
22
+ const value = argv[i + 1];
23
+ if (value === void 0) {
24
+ throw cliError(
25
+ CLI_ERROR_CODES.UNKNOWN_OPTION,
26
+ `${arg} needs a value`,
27
+ "See: mountly-mcp --help"
28
+ );
29
+ }
30
+ i += 1;
31
+ return value;
32
+ };
33
+ if (arg === "--entry") args.entry = next();
34
+ else if (arg === "--uri") args.uri = next();
35
+ else if (arg === "--framework" || arg === "-f") {
36
+ const value = next();
37
+ if (!["react", "vue", "svelte", "vanilla"].includes(value)) {
38
+ throw cliError(
39
+ CLI_ERROR_CODES.UNKNOWN_OPTION,
40
+ `framework "${value}" not supported`,
41
+ "Use: react | vue | svelte | vanilla"
42
+ );
43
+ }
44
+ args.framework = value;
45
+ } else if (arg === "--config" || arg === "-c") args.config = next();
46
+ else {
47
+ throw cliError(
48
+ CLI_ERROR_CODES.UNKNOWN_OPTION,
49
+ `unknown add option '${arg}'`,
50
+ "Options: --entry --uri --framework --config"
51
+ );
52
+ }
53
+ }
54
+ return args;
55
+ }
56
+ function detectFramework(cwd) {
57
+ const pkgPath = join(cwd, "package.json");
58
+ if (!existsSync(pkgPath)) return "react";
59
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
60
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
61
+ if (deps.vue || deps["mountly-vue"]) return "vue";
62
+ if (deps.svelte || deps["mountly-svelte"]) return "svelte";
63
+ if (deps.react) return "react";
64
+ return "vanilla";
65
+ }
66
+ function findViteConfig(cwd, explicit) {
67
+ if (explicit) {
68
+ const path = resolve(cwd, explicit);
69
+ if (!existsSync(path)) {
70
+ throw cliError(
71
+ CLI_ERROR_CODES.NO_VITE_CONFIG,
72
+ `vite config not found: ${path}`,
73
+ "Pass --config path/to/vite.config.ts"
74
+ );
75
+ }
76
+ return path;
77
+ }
78
+ for (const name of ["vite.config.ts", "vite.config.mts", "vite.config.js", "vite.config.mjs"]) {
79
+ const path = join(cwd, name);
80
+ if (existsSync(path)) return path;
81
+ }
82
+ throw cliError(
83
+ CLI_ERROR_CODES.NO_VITE_CONFIG,
84
+ "no vite.config.* in this directory",
85
+ "Run from a Mountly MCP project, or: npx mountly-mcp create my-app --framework react"
86
+ );
87
+ }
88
+ function slugify(name) {
89
+ return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
90
+ }
91
+ function viewSource(framework, componentName) {
92
+ if (framework === "react") {
93
+ return `import { createMcpView, useToolResult } from "mountly-mcp/react";
94
+
95
+ interface Result {
96
+ structuredContent?: { title?: string };
97
+ }
98
+
99
+ function ${componentName}() {
100
+ const result = useToolResult<Result>();
101
+ const title = result?.structuredContent?.title ?? "${componentName}";
102
+ return (
103
+ <main style={{ fontFamily: "system-ui", padding: 20 }}>
104
+ <h1>{title}</h1>
105
+ <p>Edit <code>src/${slugify(componentName)}.tsx</code> \u2014 data comes from useToolResult().</p>
106
+ </main>
107
+ );
108
+ }
109
+
110
+ createMcpView(${componentName});
111
+ `;
112
+ }
113
+ if (framework === "vue") {
114
+ return `import { createMcpView } from "mountly-mcp/vue";
115
+ import ${componentName} from "./${componentName}.vue";
116
+
117
+ createMcpView(${componentName});
118
+ `;
119
+ }
120
+ if (framework === "svelte") {
121
+ return `import { createMcpView } from "mountly-mcp/svelte";
122
+ import ${componentName} from "./${componentName}.svelte";
123
+
124
+ createMcpView(${componentName});
125
+ `;
126
+ }
127
+ return `import { publishMcpView, type McpViewProps } from "mountly-mcp";
128
+
129
+ publishMcpView({
130
+ mount(el, props: McpViewProps) {
131
+ el.innerHTML = "<main style=\\"font-family:system-ui;padding:20px\\"><h1>${componentName}</h1><pre></pre></main>";
132
+ const pre = el.querySelector("pre");
133
+ if (pre) pre.textContent = JSON.stringify(props.toolResult?.structuredContent ?? {}, null, 2);
134
+ },
135
+ update(el, props: McpViewProps) {
136
+ const pre = el.querySelector("pre");
137
+ if (pre) pre.textContent = JSON.stringify(props.toolResult?.structuredContent ?? {}, null, 2);
138
+ },
139
+ unmount(el) {
140
+ el.innerHTML = "";
141
+ },
142
+ });
143
+ `;
144
+ }
145
+ function vueComponent(componentName) {
146
+ return `<script setup lang="ts">
147
+ import { computed } from "vue";
148
+ import { useToolResult } from "mountly-mcp/vue";
149
+
150
+ const result = useToolResult<{ structuredContent?: { title?: string } }>();
151
+ const title = computed(() => result.value?.structuredContent?.title ?? "${componentName}");
152
+ </script>
153
+
154
+ <template>
155
+ <main style="font-family: system-ui; padding: 20px">
156
+ <h1>{{ title }}</h1>
157
+ <p>Edit this View \u2014 data comes from useToolResult().</p>
158
+ </main>
159
+ </template>
160
+ `;
161
+ }
162
+ function svelteComponent(componentName) {
163
+ return `<script lang="ts">
164
+ let { toolResult } = $props();
165
+ const title = $derived(toolResult?.structuredContent?.title ?? "${componentName}");
166
+ </script>
167
+
168
+ <main style="font-family: system-ui; padding: 20px">
169
+ <h1>{title}</h1>
170
+ <p>Edit this View \u2014 toolResult arrives as a prop.</p>
171
+ </main>
172
+ `;
173
+ }
174
+ function addView(args, cwd = process.cwd()) {
175
+ const framework = args.framework ?? detectFramework(cwd);
176
+ const configPath = findViteConfig(cwd, args.config);
177
+ const configSource = readFileSync(configPath, "utf8");
178
+ if (!/mountlyMcpViews\s*\(/.test(configSource)) {
179
+ throw cliError(
180
+ CLI_ERROR_CODES.MISSING_VITE_PLUGIN,
181
+ `no mountlyMcpViews() in ${configPath}`,
182
+ `Add mountlyMcpViews({ apps: [...] }) from "mountly-mcp/vite" to ${configPath}`
183
+ );
184
+ }
185
+ const slug = slugify(args.name) || "view";
186
+ const pascal = slug.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
187
+ const uri = args.uri ?? `ui://app/${slug}`;
188
+ if (!uri.startsWith("ui://")) {
189
+ throw cliError(
190
+ CLI_ERROR_CODES.INVALID_UI_URI,
191
+ `uri must start with ui:// (received '${uri}')`,
192
+ "Example: --uri ui://my-server/settings"
193
+ );
194
+ }
195
+ const entry = args.entry ?? `src/${slug}.${framework === "react" ? "tsx" : "ts"}`;
196
+ const entryPath = resolve(cwd, entry);
197
+ if (existsSync(entryPath)) {
198
+ throw cliError(
199
+ CLI_ERROR_CODES.TARGET_EXISTS,
200
+ `entry already exists: ${entryPath}`,
201
+ "Pass a different --entry or remove the file first"
202
+ );
203
+ }
204
+ if (configSource.includes(JSON.stringify(uri)) || configSource.includes(JSON.stringify(slug))) {
205
+ throw cliError(
206
+ CLI_ERROR_CODES.TARGET_EXISTS,
207
+ `${configPath} already declares a View named '${slug}' or uri '${uri}'`,
208
+ "Pick another name, or pass --uri to use a different resource URI"
209
+ );
210
+ }
211
+ const appBlock = `{
212
+ entry: ${JSON.stringify(entry)},
213
+ uri: ${JSON.stringify(uri)},
214
+ name: ${JSON.stringify(slug)},
215
+ displayModes: ["inline", "fullscreen"],
216
+ prefersBorder: true,
217
+ awaitToolResult: true,
218
+ }`;
219
+ let next;
220
+ if (/apps:\s*\[\s*\]/.test(configSource)) {
221
+ next = configSource.replace(/apps:\s*\[\s*\]/, `apps: [
222
+ ${appBlock},
223
+ ]`);
224
+ } else if (/apps:\s*\[/.test(configSource)) {
225
+ next = configSource.replace(/apps:\s*\[/, `apps: [
226
+ ${appBlock},`);
227
+ } else {
228
+ throw cliError(
229
+ CLI_ERROR_CODES.MISSING_VITE_PLUGIN,
230
+ `could not find apps: [] in ${configPath}`,
231
+ "Add an apps array inside mountlyMcpViews({ ... })"
232
+ );
233
+ }
234
+ mkdirSync(dirname(entryPath), { recursive: true });
235
+ writeFileSync(entryPath, viewSource(framework, pascal));
236
+ if (framework === "vue") {
237
+ writeFileSync(join(dirname(entryPath), `${pascal}.vue`), vueComponent(pascal));
238
+ }
239
+ if (framework === "svelte") {
240
+ writeFileSync(join(dirname(entryPath), `${pascal}.svelte`), svelteComponent(pascal));
241
+ }
242
+ writeFileSync(configPath, next);
243
+ process.stdout.write(
244
+ [
245
+ "",
246
+ `\u2713 Added View "${slug}"`,
247
+ ` entry ${entry}`,
248
+ ` uri ${uri}`,
249
+ ` config ${configPath}`,
250
+ "",
251
+ "Next:",
252
+ " pnpm exec mountly-mcp build",
253
+ ` pnpm exec mountly-mcp dev --app ${slug}`,
254
+ " Register a tool with resourceUri matching the uri above via registerMcpApps.",
255
+ ""
256
+ ].join("\n")
257
+ );
258
+ }
259
+ export {
260
+ addView,
261
+ parseAddArgs
262
+ };
263
+ //# sourceMappingURL=add-DAFMA4F3.js.map