mountly-mcp 4.2.0 → 4.4.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/GLOSSARY.md +1 -0
- package/README.md +165 -0
- package/dist/bridge/iframe-entry.js +7 -7
- package/dist/build/emit-meta.d.ts +3 -0
- package/dist/build/index.d.ts +3 -0
- package/dist/build/index.js +1 -1
- package/dist/chunk-4SZHJ4MK.js +9 -0
- package/dist/chunk-4SZHJ4MK.js.map +1 -0
- package/dist/{chunk-SXF23BZD.js → chunk-HIAIQVK5.js} +4 -7
- package/dist/chunk-HIAIQVK5.js.map +1 -0
- package/dist/{chunk-MTJDRWGD.js → chunk-IDGTP2NI.js} +4 -2
- package/dist/chunk-IDGTP2NI.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/dev/host-entry.js +6 -6
- package/dist/{doctor-B54H6CRN.js → doctor-NB3DE4CG.js} +45 -3
- package/dist/doctor-NB3DE4CG.js.map +1 -0
- package/dist/events/index.d.ts +97 -0
- package/dist/events/index.js +215 -0
- package/dist/events/index.js.map +1 -0
- package/dist/json-render/index.js +2 -1
- package/dist/json-render/index.js.map +1 -1
- package/dist/openai/server.d.ts +16 -0
- package/dist/openai/server.js +20 -0
- package/dist/openai/server.js.map +1 -0
- package/dist/react/index.js +4 -2
- package/dist/react/openai-extensions.d.ts +6 -0
- package/dist/react/openai-extensions.js +25 -0
- package/dist/react/openai-extensions.js.map +1 -0
- package/dist/server/index.js +11 -6
- package/dist/server/index.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/vite/index.js +1 -1
- package/package.json +20 -3
- package/templates/react/README.md.tmpl +3 -0
- package/templates/react/package.json.tmpl +6 -3
- package/templates/svelte/README.md.tmpl +2 -0
- package/templates/svelte/package.json.tmpl +5 -3
- package/templates/svelte/src/env.d.ts.tmpl +10 -0
- package/templates/vanilla/README.md.tmpl +2 -0
- package/templates/vanilla/package.json.tmpl +5 -2
- package/templates/vue/README.md.tmpl +2 -0
- package/templates/vue/package.json.tmpl +6 -4
- package/templates/vue/src/env.d.ts.tmpl +10 -0
- package/dist/chunk-MTJDRWGD.js.map +0 -1
- package/dist/chunk-SXF23BZD.js.map +0 -1
- package/dist/doctor-B54H6CRN.js.map +0 -1
package/GLOSSARY.md
CHANGED
|
@@ -13,6 +13,7 @@ docs must use these names.
|
|
|
13
13
|
| **`registerMcpApps`** | Install Views and UI-linked tools on an unconnected `McpServer`. |
|
|
14
14
|
| **`readMcpAppManifest`** | Load `dist/mountly-mcp.manifest.json` from `mountly-mcp/artifact`. |
|
|
15
15
|
| **`mcp.fixtures.json`** | Named samples for `mountly-mcp dev` (tool args with `--server`, else `structuredContent`). |
|
|
16
|
+
| **OpenAI extensions** | ChatGPT-only extras via `@openai/mcp-extensions`. Helpers: `mountly-mcp/openai/server`, `mountly-mcp/react/openai`. |
|
|
16
17
|
| **`create`** | `mountly-mcp create`: scaffold a greenfield App. |
|
|
17
18
|
| **`add`** | `mountly-mcp add`: add a second View to an existing Mountly Vite project. |
|
|
18
19
|
| **`build`** | `mountly-mcp build`: build Views via Vite. |
|
package/README.md
CHANGED
|
@@ -137,6 +137,168 @@ await registerMcpApps(server, {
|
|
|
137
137
|
|
|
138
138
|
Call `registerMcpApps` before `connect`. You keep auth, transport, and deploy.
|
|
139
139
|
|
|
140
|
+
## ChatGPT / OpenAI extensions
|
|
141
|
+
|
|
142
|
+
Install the optional peer [`@openai/mcp-extensions`](https://www.npmjs.com/package/@openai/mcp-extensions)
|
|
143
|
+
for ChatGPT entrypoints, mentions, forms, and settings. Mountly helpers import that SDK.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pnpm add @openai/mcp-extensions
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Resource metadata** — pass `openaiUi` on the Vite View (or `buildMcpResource`).
|
|
150
|
+
`registerMcpApps` writes it to `_meta["openai/ui"]` on the `ui://` resource:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
mountlyMcpViews({
|
|
154
|
+
apps: [
|
|
155
|
+
{
|
|
156
|
+
entry: "src/view.tsx",
|
|
157
|
+
uri: "ui://parts/library",
|
|
158
|
+
name: "parts_library",
|
|
159
|
+
openaiUi: {
|
|
160
|
+
preferredDisplayMode: "fullscreen",
|
|
161
|
+
availableDisplayModes: ["inline", "fullscreen"],
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Tool entrypoints** — merge typed `openai/ui` into the tool `_meta`:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import {
|
|
172
|
+
enableOpenAiExtensions,
|
|
173
|
+
openaiUiToolMeta,
|
|
174
|
+
} from "mountly-mcp/openai/server";
|
|
175
|
+
|
|
176
|
+
const openai = enableOpenAiExtensions(server); // mentions, settings, elicitInput
|
|
177
|
+
|
|
178
|
+
await registerMcpApps(server, {
|
|
179
|
+
views: artifacts.map((artifact) => ({ artifact })),
|
|
180
|
+
tools: [
|
|
181
|
+
{
|
|
182
|
+
name: "parts.library",
|
|
183
|
+
resourceUri: "ui://parts/library",
|
|
184
|
+
config: {
|
|
185
|
+
title: "Parts Library",
|
|
186
|
+
inputSchema: {},
|
|
187
|
+
_meta: {
|
|
188
|
+
...openaiUiToolMeta({
|
|
189
|
+
entrypoints: [{ type: "global" }],
|
|
190
|
+
}),
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
handler: async () => ({ structuredContent: {} }),
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**View-side** — import from the OpenAI React entry:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import { useOpenAIExtensions } from "mountly-mcp/react/openai";
|
|
203
|
+
|
|
204
|
+
function Library() {
|
|
205
|
+
const openai = useOpenAIExtensions();
|
|
206
|
+
// openai.message, openai.deepLink, openai.files, …
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Entrypoint types** place one View on several ChatGPT surfaces. Other hosts
|
|
211
|
+
ignore `openai/ui` and render the View inline:
|
|
212
|
+
|
|
213
|
+
| `type` | ChatGPT surface | Extra fields |
|
|
214
|
+
| ---------- | ----------------------------------------------- | -------------------------------- |
|
|
215
|
+
| `global` | Persistent sidebar panel | `quickAction` (sidebar shortcut) |
|
|
216
|
+
| `thread` | Conversation panel beside the chat | none |
|
|
217
|
+
| `file` | File viewer/editor for matching extensions | `extensions` |
|
|
218
|
+
| `settings` | Plugin settings page | `searchTerms` |
|
|
219
|
+
|
|
220
|
+
A file viewer receives the opened file as tool input. Parse it with the SDK schema:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
import { OpenAIFileEntrypointInputSchema } from "@openai/mcp-extensions/server";
|
|
224
|
+
|
|
225
|
+
{
|
|
226
|
+
name: "parts.open_cad",
|
|
227
|
+
resourceUri: "ui://parts/cad-viewer",
|
|
228
|
+
config: {
|
|
229
|
+
inputSchema: OpenAIFileEntrypointInputSchema.shape,
|
|
230
|
+
_meta: openaiUiToolMeta({
|
|
231
|
+
entrypoints: [{ type: "file", extensions: [".step", ".stl"] }],
|
|
232
|
+
}),
|
|
233
|
+
},
|
|
234
|
+
handler: async ({ file }) => ({ structuredContent: { name: file.name, uri: file.resourceUri } }),
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Smoke in ChatGPT: serve Streamable HTTP at `/mcp`, enable Developer mode, add the
|
|
239
|
+
connector, then open a `global` entrypoint from the Desktop sidebar. See the
|
|
240
|
+
[host matrix](https://mountly.dev/mcp-apps/host-matrix/).
|
|
241
|
+
|
|
242
|
+
## MCP Events (experimental)
|
|
243
|
+
|
|
244
|
+
`mountly-mcp/events` lets your server wake a ChatGPT conversation when something
|
|
245
|
+
changes, such as a finished deploy or a new comment. It implements webhook
|
|
246
|
+
delivery from the draft MCP Events spec: Standard Webhooks signing, callback
|
|
247
|
+
challenge verification, retries with backoff, and `410` cleanup. The API follows
|
|
248
|
+
the draft spec.
|
|
249
|
+
|
|
250
|
+
You need an **MCP 2.0** server (`@modelcontextprotocol/server` v2, protocol
|
|
251
|
+
`2026-07-28`). Register the helpers as custom methods:
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
import { McpServer, createMcpHandler } from "@modelcontextprotocol/server";
|
|
255
|
+
import { defineEvents } from "mountly-mcp/events";
|
|
256
|
+
import { z } from "zod";
|
|
257
|
+
|
|
258
|
+
const events = defineEvents({
|
|
259
|
+
events: {
|
|
260
|
+
"deploy.finished": {
|
|
261
|
+
description: "A deploy for the app finished.",
|
|
262
|
+
input: z.object({ app: z.string() }),
|
|
263
|
+
payload: z.object({ app: z.string(), id: z.string(), status: z.enum(["ok", "failed"]) }),
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
store: subscriptionsTable, // get/set/delete/values; a Map is fine for dev
|
|
267
|
+
authorize: ({ arguments: args, authInfo }) => canSeeApp(authInfo, args.app),
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const handler = createMcpHandler(() => {
|
|
271
|
+
const server = new McpServer({ name: "deploys", version: "1.0.0" });
|
|
272
|
+
server.server.registerCapabilities({ events: {} });
|
|
273
|
+
const params = z.looseObject({});
|
|
274
|
+
server.server.setRequestHandler("events/list", { params }, () => events.list());
|
|
275
|
+
server.server.setRequestHandler("events/subscribe", { params }, (p, ctx) =>
|
|
276
|
+
events.subscribe(p, { authInfo: ctx.http?.authInfo }));
|
|
277
|
+
server.server.setRequestHandler("events/unsubscribe", { params }, (p, ctx) =>
|
|
278
|
+
events.unsubscribe(p, { authInfo: ctx.http?.authInfo }));
|
|
279
|
+
return server;
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Later, when it happens:
|
|
283
|
+
await events.emit(
|
|
284
|
+
"deploy.finished",
|
|
285
|
+
{ app: "web", id: "d_42", status: "ok" },
|
|
286
|
+
(args) => args.app === "web",
|
|
287
|
+
);
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Give the events server at least one tool. ChatGPT creates a connector once
|
|
291
|
+
`tools/list` succeeds.
|
|
292
|
+
|
|
293
|
+
`authorize` runs on subscribe and again before each delivery, using the principal
|
|
294
|
+
stored at subscribe time, so revoking access stops delivery. Callback URLs use
|
|
295
|
+
`https:`; pass `allowCallbackUrl` to restrict them further. Keep `authInfo`
|
|
296
|
+
serializable when your store persists subscriptions.
|
|
297
|
+
|
|
298
|
+
ChatGPT subscribes from Work chats and processes events asynchronously. See the
|
|
299
|
+
[`mcp-chatgpt-plugin` example](https://github.com/jagreehal/mountly/tree/main/docs/examples/mcp-chatgpt-plugin) for a
|
|
300
|
+
full walkthrough.
|
|
301
|
+
|
|
140
302
|
## Verify
|
|
141
303
|
|
|
142
304
|
```bash
|
|
@@ -200,11 +362,14 @@ the view sends the agent its next turn with the current page. The
|
|
|
200
362
|
| ------------------------- | --------------------------------------- |
|
|
201
363
|
| `mountly-mcp` | `runBridge`, `publishMcpView`, types |
|
|
202
364
|
| `mountly-mcp/react` | `createMcpView` + hooks |
|
|
365
|
+
| `mountly-mcp/react/openai` | `useOpenAIExtensions` (ChatGPT peer) |
|
|
203
366
|
| `mountly-mcp/vue` | `createMcpView` + composables |
|
|
204
367
|
| `mountly-mcp/svelte` | `createMcpView` (props) |
|
|
205
368
|
| `mountly-mcp/vite` | `mountlyMcpViews()` |
|
|
206
369
|
| `mountly-mcp/artifact` | Manifest APIs |
|
|
207
370
|
| `mountly-mcp/server` | `registerMcpApps` |
|
|
371
|
+
| `mountly-mcp/openai/server` | ChatGPT helpers (`enableOpenAiExtensions`, `openaiUiToolMeta`) |
|
|
372
|
+
| `mountly-mcp/events` | Experimental MCP Events (`defineEvents`, `signWebhook`) |
|
|
208
373
|
| `mountly-mcp/dev` | Local host helpers |
|
|
209
374
|
| `mountly-mcp/testing` | `verifyMcpApps` |
|
|
210
375
|
| `mountly-mcp/json-render` | Generative path |
|
|
@@ -15437,7 +15437,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
15437
15437
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/mini/parse.js
|
|
15438
15438
|
init_core2();
|
|
15439
15439
|
|
|
15440
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.
|
|
15440
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_@cfworker+json-schema@4.1.1_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
|
|
15441
15441
|
function isZ4Schema(s) {
|
|
15442
15442
|
const schema = s;
|
|
15443
15443
|
return !!schema._zod;
|
|
@@ -15500,7 +15500,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
15500
15500
|
return void 0;
|
|
15501
15501
|
}
|
|
15502
15502
|
|
|
15503
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.
|
|
15503
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_@cfworker+json-schema@4.1.1_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
|
|
15504
15504
|
init_v4();
|
|
15505
15505
|
var RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task";
|
|
15506
15506
|
var JSONRPC_VERSION = "2.0";
|
|
@@ -17018,7 +17018,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
17018
17018
|
}
|
|
17019
17019
|
};
|
|
17020
17020
|
|
|
17021
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.
|
|
17021
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_@cfworker+json-schema@4.1.1_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
|
|
17022
17022
|
function isTerminal(status) {
|
|
17023
17023
|
return status === "completed" || status === "failed" || status === "cancelled";
|
|
17024
17024
|
}
|
|
@@ -17026,7 +17026,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
17026
17026
|
// ../../node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@4.4.3/node_modules/zod-to-json-schema/dist/esm/parsers/string.js
|
|
17027
17027
|
var ALPHA_NUMERIC = new Set("ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789");
|
|
17028
17028
|
|
|
17029
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.
|
|
17029
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_@cfworker+json-schema@4.1.1_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
|
|
17030
17030
|
function getMethodLiteral(schema) {
|
|
17031
17031
|
const shape = getObjectShape(schema);
|
|
17032
17032
|
const methodSchema = shape?.method;
|
|
@@ -17047,7 +17047,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
17047
17047
|
return result.data;
|
|
17048
17048
|
}
|
|
17049
17049
|
|
|
17050
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.
|
|
17050
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_@cfworker+json-schema@4.1.1_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
|
|
17051
17051
|
var DEFAULT_REQUEST_TIMEOUT_MSEC = 6e4;
|
|
17052
17052
|
var Protocol = class {
|
|
17053
17053
|
constructor(_options) {
|
|
@@ -18001,7 +18001,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
18001
18001
|
return result;
|
|
18002
18002
|
}
|
|
18003
18003
|
|
|
18004
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+ext-apps@1.7.5_@modelcontextprotocol+sdk@1.30.
|
|
18004
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+ext-apps@1.7.5_@modelcontextprotocol+sdk@1.30.0_@cfworker+json-schema@4_zach7odob275zkkz2vkisyu32i/node_modules/@modelcontextprotocol/ext-apps/dist/src/app.js
|
|
18005
18005
|
init_v4();
|
|
18006
18006
|
init_v4();
|
|
18007
18007
|
var p = ((X) => typeof __require < "u" ? __require : typeof Proxy < "u" ? new Proxy(X, { get: (Y2, Z) => (typeof __require < "u" ? __require : Y2)[Z] }) : X)(function(X) {
|
|
@@ -18467,7 +18467,7 @@ container holding the app. Specify either width or maxWidth, and either height o
|
|
|
18467
18467
|
__publicField(_i, "ONE_SHOT_EVENTS", /* @__PURE__ */ new Set(["toolinput", "toolinputpartial", "toolresult", "toolcancelled"]));
|
|
18468
18468
|
var i = _i;
|
|
18469
18469
|
|
|
18470
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+ext-apps@1.7.5_@modelcontextprotocol+sdk@1.30.
|
|
18470
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+ext-apps@1.7.5_@modelcontextprotocol+sdk@1.30.0_@cfworker+json-schema@4_zach7odob275zkkz2vkisyu32i/node_modules/@modelcontextprotocol/ext-apps/dist/src/server/index.js
|
|
18471
18471
|
init_v4();
|
|
18472
18472
|
var r = ((Z) => typeof __require < "u" ? __require : typeof Proxy < "u" ? new Proxy(Z, { get: ($, J) => (typeof __require < "u" ? __require : $)[J] }) : Z)(function(Z) {
|
|
18473
18473
|
if (typeof __require < "u") return __require.apply(this, arguments);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { McpUiDisplayMode, McpUiResourceCsp, McpUiResourcePermissions } from "@modelcontextprotocol/ext-apps";
|
|
2
|
+
import type { OpenAIUiResourceMetadata } from "@openai/mcp-extensions/server";
|
|
2
3
|
import type { McpResourceDeclaration } from "../types.js";
|
|
3
4
|
export interface EmitMetaInput {
|
|
4
5
|
uri: string;
|
|
@@ -13,5 +14,7 @@ export interface EmitMetaInput {
|
|
|
13
14
|
domain?: string;
|
|
14
15
|
/** Whether the view requests the host to show a visible border + background. */
|
|
15
16
|
prefersBorder?: boolean;
|
|
17
|
+
/** ChatGPT resource display metadata written to `_meta["openai/ui"]`. */
|
|
18
|
+
openaiUi?: OpenAIUiResourceMetadata;
|
|
16
19
|
}
|
|
17
20
|
export declare function emitMeta(input: EmitMetaInput): McpResourceDeclaration;
|
package/dist/build/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { McpUiDisplayMode, McpUiResourceCsp, McpUiResourcePermissions } from "@modelcontextprotocol/ext-apps";
|
|
2
|
+
import type { OpenAIUiResourceMetadata } from "@openai/mcp-extensions/server";
|
|
2
3
|
import type { McpResourceDeclaration } from "../types.js";
|
|
3
4
|
import type { McpAppArtifact } from "../artifact/index.js";
|
|
4
5
|
/**
|
|
@@ -33,6 +34,8 @@ export interface BuildSelfContainedOptions {
|
|
|
33
34
|
domain?: string;
|
|
34
35
|
/** Whether the view requests the host to show a visible border + background. */
|
|
35
36
|
prefersBorder?: boolean;
|
|
37
|
+
/** ChatGPT resource display metadata (`_meta["openai/ui"]`). */
|
|
38
|
+
openaiUi?: OpenAIUiResourceMetadata;
|
|
36
39
|
cssEntry?: string;
|
|
37
40
|
bridgeRuntimePath?: string;
|
|
38
41
|
}
|
package/dist/build/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/context.ts"],"sourcesContent":["import { createContext } from \"react\";\nimport type {\n App,\n McpUiHostContext,\n McpUiToolInputNotification,\n McpUiToolInputPartialNotification,\n McpUiToolResultNotification,\n} from \"@modelcontextprotocol/ext-apps\";\n\n/**\n * Shared per-View context. The bridge populates this on each mount/update,\n * so consumers can synchronously read the initial values (no event subscription\n * race) and the React tree re-renders when the bridge calls `update()`.\n */\nexport interface McpContextValue {\n app: App;\n toolInput?: McpUiToolInputNotification[\"params\"];\n toolInputPartial?: McpUiToolInputPartialNotification[\"params\"];\n toolResult?: McpUiToolResultNotification[\"params\"];\n hostContext?: McpUiHostContext;\n}\n\nexport const McpContext = createContext<McpContextValue | null>(null);\nMcpContext.displayName = \"McpContext\";\n"],"mappings":";AAAA,SAAS,qBAAqB;AAsBvB,IAAM,aAAa,cAAsC,IAAI;AACpE,WAAW,cAAc;","names":[]}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
McpContext
|
|
3
|
+
} from "./chunk-4SZHJ4MK.js";
|
|
1
4
|
import {
|
|
2
5
|
publishMcpView
|
|
3
6
|
} from "./chunk-5L3QHYWK.js";
|
|
4
7
|
|
|
5
|
-
// src/react/context.ts
|
|
6
|
-
import { createContext } from "react";
|
|
7
|
-
var McpContext = createContext(null);
|
|
8
|
-
McpContext.displayName = "McpContext";
|
|
9
|
-
|
|
10
8
|
// src/react/hooks.ts
|
|
11
9
|
import { useContext } from "react";
|
|
12
10
|
import {
|
|
@@ -100,7 +98,6 @@ function createMcpView(Component, options) {
|
|
|
100
98
|
}
|
|
101
99
|
|
|
102
100
|
export {
|
|
103
|
-
McpContext,
|
|
104
101
|
useMcpApp,
|
|
105
102
|
useToolInput,
|
|
106
103
|
useToolInputPartial,
|
|
@@ -116,4 +113,4 @@ export {
|
|
|
116
113
|
useAutoResize,
|
|
117
114
|
createMcpView
|
|
118
115
|
};
|
|
119
|
-
//# sourceMappingURL=chunk-
|
|
116
|
+
//# sourceMappingURL=chunk-HIAIQVK5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/hooks.ts","../src/react/create-view.ts"],"sourcesContent":["import { useContext } from \"react\";\nimport {\n useAutoResize as useAutoResizeBase,\n useDocumentTheme as useDocumentThemeBase,\n useHostFonts as useHostFontsBase,\n useHostStyleVariables as useHostStyleVariablesBase,\n useHostStyles as useHostStylesBase,\n} from \"@modelcontextprotocol/ext-apps/react\";\nimport type {\n App,\n McpUiDisplayMode,\n McpUiHostContext,\n McpUiUpdateModelContextRequest,\n} from \"@modelcontextprotocol/ext-apps\";\nimport { McpContext } from \"./context.js\";\n\nfunction useEnsuredContext() {\n const ctx = useContext(McpContext);\n if (!ctx) {\n throw new Error(\n \"mountly-mcp/react: useMcpApp/useToolInput/etc must be used inside a View wrapped with createMcpView().\",\n );\n }\n return ctx;\n}\n\n/** The view-side ext-apps App. Use this to call server tools, send messages, open links, etc. */\nexport function useMcpApp(): App {\n return useEnsuredContext().app;\n}\n\n/** Complete notification params; read the tool arguments from `.arguments`. */\nexport function useToolInput<T = unknown>(): T | undefined {\n return useEnsuredContext().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 * fields may be missing or change between notifications, so render loading\n * states from it, never critical operations. Undefined once the complete\n * {@link useToolInput} arrives.\n */\nexport function useToolInputPartial<T = unknown>(): Partial<T> | undefined {\n return useEnsuredContext().toolInputPartial as Partial<T> | undefined;\n}\n\n/** Complete MCP tool result; structured data is available at `.structuredContent`. */\nexport function useToolResult<T = unknown>(): T | undefined {\n return useEnsuredContext().toolResult as T | undefined;\n}\n\n/** Latest host context snapshot (theme, styles, displayMode, container dimensions, etc.). */\nexport function useHostContext(): McpUiHostContext | undefined {\n return useEnsuredContext().hostContext;\n}\n\n/** Current display mode reported by the host. */\nexport function useDisplayMode(): McpUiDisplayMode | undefined {\n return useHostContext()?.displayMode;\n}\n\n/**\n * Request a display mode change, resolving to the mode the host actually\n * applied — which the spec allows to differ from the one asked for.\n *\n * Modes the host didn't list in `availableDisplayModes` are never requested\n * (the spec requires views to check first); the current mode comes back\n * unchanged instead.\n */\nexport function useRequestDisplayMode(): (\n mode: McpUiDisplayMode,\n) => Promise<McpUiDisplayMode | undefined> {\n const { app, hostContext } = useEnsuredContext();\n return async (mode) => {\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\n/** Apply host CSS variables (and react to host-context changes). */\nexport function useHostStyleVariables(): void {\n const { app, hostContext } = useEnsuredContext();\n useHostStyleVariablesBase(app, hostContext ?? null);\n}\n\n/** Apply host-provided `@font-face`/`@import` rules. */\nexport function useHostFonts(): void {\n const { app, hostContext } = useEnsuredContext();\n useHostFontsBase(app, hostContext ?? null);\n}\n\n/** One-shot helper: apply both host style variables and host fonts. */\nexport function useHostStyles(): void {\n const { app, hostContext } = useEnsuredContext();\n useHostStylesBase(app, hostContext ?? null);\n}\n\n/** Track the document theme reactively. */\nexport function useDocumentTheme() {\n return useDocumentThemeBase();\n}\n\n/**\n * Return a function that calls `app.updateModelContext(params)`.\n *\n * Context updates are available to the model in future turns without\n * triggering an immediate response. Only the last update is kept.\n */\nexport function useUpdateModelContext(): (\n params: McpUiUpdateModelContextRequest[\"params\"],\n) => Promise<void> {\n const { app } = useEnsuredContext();\n return async (params) => {\n await app.updateModelContext(params);\n };\n}\n\n/** Manually control auto-resize (rarely needed — the bridge calls setupSizeChangedNotifications by default). */\nexport function useAutoResize(...args: Parameters<typeof useAutoResizeBase>) {\n return useAutoResizeBase(...args);\n}\n","/**\n * Wraps a React component as an MCP Apps View and publishes it for the bridge.\n * One call is enough — no manual global assign.\n */\nimport { createElement, type ComponentType } from \"react\";\nimport { createWidget } from \"mountly-react\";\nimport type { AdapterOptions } from \"mountly/adapter\";\nimport type { McpView, McpViewProps } from \"../types.js\";\nimport { publishMcpView } from \"../publish.js\";\nimport { McpContext, type McpContextValue } from \"./context.js\";\n\nexport function createMcpView<P extends object>(\n Component: ComponentType<P>,\n options?: AdapterOptions,\n): McpView {\n const Wrapped = (props: P & McpViewProps) => {\n const { mcp, toolInput, toolInputPartial, toolResult, hostContext, ...rest } =\n props as McpViewProps & P;\n const value: McpContextValue = {\n app: mcp,\n toolInput,\n toolInputPartial,\n toolResult,\n hostContext,\n };\n return createElement(\n McpContext.Provider,\n { value },\n createElement(Component as ComponentType, rest as P),\n );\n };\n return publishMcpView(createWidget(Wrapped as ComponentType<P & McpViewProps>, options));\n}\n"],"mappings":";;;;;;;;AAAA,SAAS,kBAAkB;AAC3B;AAAA,EACE,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,gBAAgB;AAAA,EAChB,yBAAyB;AAAA,EACzB,iBAAiB;AAAA,OACZ;AASP,SAAS,oBAAoB;AAC3B,QAAM,MAAM,WAAW,UAAU;AACjC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,YAAiB;AAC/B,SAAO,kBAAkB,EAAE;AAC7B;AAGO,SAAS,eAA2C;AACzD,SAAO,kBAAkB,EAAE;AAC7B;AASO,SAAS,sBAA2D;AACzE,SAAO,kBAAkB,EAAE;AAC7B;AAGO,SAAS,gBAA4C;AAC1D,SAAO,kBAAkB,EAAE;AAC7B;AAGO,SAAS,iBAA+C;AAC7D,SAAO,kBAAkB,EAAE;AAC7B;AAGO,SAAS,iBAA+C;AAC7D,SAAO,eAAe,GAAG;AAC3B;AAUO,SAAS,wBAE2B;AACzC,QAAM,EAAE,KAAK,YAAY,IAAI,kBAAkB;AAC/C,SAAO,OAAO,SAAS;AACrB,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;AAGO,SAAS,wBAA8B;AAC5C,QAAM,EAAE,KAAK,YAAY,IAAI,kBAAkB;AAC/C,4BAA0B,KAAK,eAAe,IAAI;AACpD;AAGO,SAAS,eAAqB;AACnC,QAAM,EAAE,KAAK,YAAY,IAAI,kBAAkB;AAC/C,mBAAiB,KAAK,eAAe,IAAI;AAC3C;AAGO,SAAS,gBAAsB;AACpC,QAAM,EAAE,KAAK,YAAY,IAAI,kBAAkB;AAC/C,oBAAkB,KAAK,eAAe,IAAI;AAC5C;AAGO,SAAS,mBAAmB;AACjC,SAAO,qBAAqB;AAC9B;AAQO,SAAS,wBAEG;AACjB,QAAM,EAAE,IAAI,IAAI,kBAAkB;AAClC,SAAO,OAAO,WAAW;AACvB,UAAM,IAAI,mBAAmB,MAAM;AAAA,EACrC;AACF;AAGO,SAAS,iBAAiB,MAA4C;AAC3E,SAAO,kBAAkB,GAAG,IAAI;AAClC;;;ACvHA,SAAS,qBAAyC;AAClD,SAAS,oBAAoB;AAMtB,SAAS,cACd,WACA,SACS;AACT,QAAM,UAAU,CAAC,UAA4B;AAC3C,UAAM,EAAE,KAAK,WAAW,kBAAkB,YAAY,aAAa,GAAG,KAAK,IACzE;AACF,UAAM,QAAyB;AAAA,MAC7B,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,WAAO;AAAA,MACL,WAAW;AAAA,MACX,EAAE,MAAM;AAAA,MACR,cAAc,WAA4B,IAAS;AAAA,IACrD;AAAA,EACF;AACA,SAAO,eAAe,aAAa,SAA4C,OAAO,CAAC;AACzF;","names":[]}
|
|
@@ -99,6 +99,7 @@ function emitMeta(input) {
|
|
|
99
99
|
displayModes: input.displayModes ?? ["inline"],
|
|
100
100
|
_meta: { ui }
|
|
101
101
|
};
|
|
102
|
+
if (input.openaiUi !== void 0) decl._meta["openai/ui"] = input.openaiUi;
|
|
102
103
|
if (input.description !== void 0) decl.description = input.description;
|
|
103
104
|
return decl;
|
|
104
105
|
}
|
|
@@ -136,7 +137,8 @@ async function writeResource(options) {
|
|
|
136
137
|
csp: options.mode === "cdn" ? mergeCdnIntoCsp(options.csp, options.cdn) : options.csp,
|
|
137
138
|
permissions: options.permissions,
|
|
138
139
|
domain: options.domain,
|
|
139
|
-
prefersBorder: options.prefersBorder
|
|
140
|
+
prefersBorder: options.prefersBorder,
|
|
141
|
+
openaiUi: options.openaiUi
|
|
140
142
|
});
|
|
141
143
|
const config = {
|
|
142
144
|
awaitToolResult: declaration.awaitToolResult,
|
|
@@ -193,4 +195,4 @@ export {
|
|
|
193
195
|
buildMcpResourceFromSource,
|
|
194
196
|
buildMcpResource
|
|
195
197
|
};
|
|
196
|
-
//# sourceMappingURL=chunk-
|
|
198
|
+
//# sourceMappingURL=chunk-IDGTP2NI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/build/index.ts","../src/build/emit-html.ts","../src/build/emit-meta.ts"],"sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport { resolve } from \"node:path\";\nimport type {\n McpUiDisplayMode,\n McpUiResourceCsp,\n McpUiResourcePermissions,\n} from \"@modelcontextprotocol/ext-apps\";\nimport type { OpenAIUiResourceMetadata } from \"@openai/mcp-extensions/server\";\nimport type { McpResourceDeclaration } from \"../types.js\";\nimport type { McpAppArtifact } from \"../artifact/index.js\";\nimport { emitHtml } from \"./emit-html.js\";\nimport { emitMeta } from \"./emit-meta.js\";\n\n/**\n * Absolute path to the iframe bridge runtime shipped with this package.\n *\n * `buildMcpResource` inlines this file's contents into the emitted HTML so\n * the View can speak the MCP Apps postMessage protocol. Use this when you\n * need to point another build tool at the runtime, or when you want a\n * stable, version-pinned alternative to passing `bridgeRuntimePath` yourself.\n *\n * Resolves relative to the installed package, so it works identically in\n * monorepos and in projects that installed mountly-mcp from npm.\n */\nexport function getBridgeRuntimePath(): string {\n // Ask the module resolver rather than walking up from __dirname: the subpath\n // is a declared export, so this holds wherever a bundler puts this file.\n return fileURLToPath(import.meta.resolve(\"mountly-mcp/bridge/iframe-entry.js\"));\n}\n\nfunction defaultBridgeRuntimePath(): string {\n return getBridgeRuntimePath();\n}\n\nexport interface BuildSelfContainedOptions {\n entry: string;\n uri: string;\n name: string;\n output: string;\n description?: string;\n awaitToolResult?: boolean;\n displayModes?: ReadonlyArray<McpUiDisplayMode>;\n /**\n * Render progressive states from `ui/notifications/tool-input-partial`.\n * Off by default — see `RunBridgeOptions.streamToolInput`.\n */\n streamToolInput?: boolean;\n csp?: McpUiResourceCsp;\n /** Sandbox permissions requested by the view (camera/microphone/geolocation/clipboardWrite). */\n permissions?: McpUiResourcePermissions;\n /** Dedicated sandbox origin (host-dependent format). */\n domain?: string;\n /** Whether the view requests the host to show a visible border + background. */\n prefersBorder?: boolean;\n /** ChatGPT resource display metadata (`_meta[\"openai/ui\"]`). */\n openaiUi?: OpenAIUiResourceMetadata;\n cssEntry?: string;\n bridgeRuntimePath?: string;\n}\n\nexport interface BuildCdnOptions extends BuildSelfContainedOptions {\n cdn: { jsUrl: string; cssUrl: string };\n}\n\nexport type BuildOptions = BuildSelfContainedOptions | BuildCdnOptions;\n\nexport interface BuildResult {\n htmlPath: string;\n metaPath: string;\n declaration: McpResourceDeclaration;\n artifact: McpAppArtifact;\n}\n\nexport interface BuildFromSourceOptions extends Omit<\n BuildSelfContainedOptions,\n \"entry\" | \"cssEntry\"\n> {\n /** The bundled View JS. */\n js: string;\n /** The View's CSS, if it has any. */\n css?: string;\n}\n\n/**\n * Same as {@link buildMcpResource}, but taking the bundle in memory instead of\n * from disk — for bundler plugins that already hold the emitted sources and\n * shouldn't have to guess when they land on the filesystem.\n */\nexport async function buildMcpResourceFromSource(\n options: BuildFromSourceOptions,\n): Promise<BuildResult> {\n const { js, css, ...rest } = options;\n return writeResource({ ...rest, mode: \"self-contained\", js, css: css ?? \"\" });\n}\n\nexport async function buildMcpResource(options: BuildOptions): Promise<BuildResult> {\n if (!(\"cdn\" in options)) {\n const js = await readFile(options.entry, \"utf8\");\n const css = options.cssEntry ? await readFile(options.cssEntry, \"utf8\") : \"\";\n return writeResource({ ...options, mode: \"self-contained\", js, css });\n }\n return writeResource({ ...options, mode: \"cdn\" });\n}\n\ntype ResourceMeta = Omit<BuildSelfContainedOptions, \"entry\" | \"cssEntry\">;\n\ntype WriteResourceOptions =\n | (ResourceMeta & { mode: \"self-contained\"; js: string; css: string })\n | (ResourceMeta & { mode: \"cdn\"; cdn: BuildCdnOptions[\"cdn\"] });\n\nasync function writeResource(options: WriteResourceOptions): Promise<BuildResult> {\n const bridgeRuntimeJs = await readFile(\n options.bridgeRuntimePath ?? defaultBridgeRuntimePath(),\n \"utf8\",\n );\n\n const declaration = emitMeta({\n uri: options.uri,\n name: options.name,\n description: options.description,\n awaitToolResult: options.awaitToolResult,\n displayModes: options.displayModes,\n csp: options.mode === \"cdn\" ? mergeCdnIntoCsp(options.csp, options.cdn) : options.csp,\n permissions: options.permissions,\n domain: options.domain,\n prefersBorder: options.prefersBorder,\n openaiUi: options.openaiUi,\n });\n\n // Same values the sidecar declares, so the view's `ui/initialize` matches it.\n const config = {\n awaitToolResult: declaration.awaitToolResult,\n displayModes: declaration.displayModes,\n streamToolInput: options.streamToolInput ?? false,\n };\n\n const html =\n options.mode === \"cdn\"\n ? emitHtml({\n mode: \"cdn\",\n uri: options.uri,\n jsUrl: options.cdn.jsUrl,\n cssUrl: options.cdn.cssUrl,\n bridgeRuntimeJs,\n config,\n })\n : emitHtml({\n mode: \"self-contained\",\n uri: options.uri,\n js: options.js,\n css: options.css,\n bridgeRuntimeJs,\n config,\n });\n\n const metaPath = `${options.output}.meta.json`;\n await writeFile(options.output, html, \"utf8\");\n await writeFile(metaPath, JSON.stringify(declaration, null, 2), \"utf8\");\n\n const htmlPath = resolve(options.output);\n return {\n htmlPath,\n metaPath: resolve(metaPath),\n declaration,\n artifact: {\n name: declaration.name,\n uri: declaration.uri,\n htmlPath,\n metaPath: resolve(metaPath),\n declaration,\n },\n };\n}\n\nfunction mergeCdnIntoCsp(\n csp: McpUiResourceCsp | undefined,\n cdn: { jsUrl: string; cssUrl: string },\n): McpUiResourceCsp {\n const jsOrigin = new URL(cdn.jsUrl).origin;\n const cssOrigin = new URL(cdn.cssUrl).origin;\n const merged: McpUiResourceCsp = { ...(csp ?? {}) };\n const existing = new Set(merged.resourceDomains ?? []);\n existing.add(jsOrigin);\n existing.add(cssOrigin);\n merged.resourceDomains = Array.from(existing);\n return merged;\n}\n\nexport { emitHtml } from \"./emit-html.js\";\nexport { emitMeta } from \"./emit-meta.js\";\n","import { MCP_APPS_PROTOCOL_VERSION } from \"../schema.js\";\nimport type { McpUiDisplayMode } from \"@modelcontextprotocol/ext-apps\";\nimport { escapeInlineScript, serializeInlineScriptValue } from \"../html.js\";\n\n/**\n * Runtime config the emitted HTML hands to the inlined bridge\n * (`bridge/iframe-entry.ts` reads these off `globalThis`). Without it the view\n * falls back to `awaitToolResult: true` / `availableDisplayModes: [\"inline\"]`,\n * regardless of what the build declared in the sidecar.\n */\nexport interface EmitHtmlConfig {\n awaitToolResult?: boolean;\n displayModes?: ReadonlyArray<McpUiDisplayMode>;\n streamToolInput?: boolean;\n}\n\nexport interface EmitHtmlSelfContainedInput {\n mode: \"self-contained\";\n uri: string;\n js: string;\n css: string;\n bridgeRuntimeJs: string;\n config?: EmitHtmlConfig;\n}\n\nexport interface EmitHtmlCdnInput {\n mode: \"cdn\";\n uri: string;\n jsUrl: string;\n cssUrl: string;\n bridgeRuntimeJs: string;\n config?: EmitHtmlConfig;\n}\n\nexport type EmitHtmlInput = EmitHtmlSelfContainedInput | EmitHtmlCdnInput;\n\nfunction escapeAttr(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\nfunction escapeStyle(value: string): string {\n return value.replace(/<\\/style/gi, \"<\\\\/style\");\n}\n\n/**\n * Classic (non-module) script so it runs at parse time — before the deferred\n * module scripts that boot the bridge.\n */\nfunction configScript(config: EmitHtmlConfig | undefined): string[] {\n if (!config) return [];\n const assignments: string[] = [];\n if (config.awaitToolResult !== undefined) {\n assignments.push(\n `globalThis.__mountlyMcpAwaitToolResult__=${serializeInlineScriptValue(config.awaitToolResult)};`,\n );\n }\n if (config.displayModes !== undefined) {\n assignments.push(\n `globalThis.__mountlyMcpAvailableDisplayModes__=${serializeInlineScriptValue(config.displayModes)};`,\n );\n }\n if (config.streamToolInput !== undefined) {\n assignments.push(\n `globalThis.__mountlyMcpStreamToolInput__=${serializeInlineScriptValue(config.streamToolInput)};`,\n );\n }\n if (assignments.length === 0) return [];\n return [`<script>${assignments.join(\"\")}</script>`];\n}\n\nexport function emitHtml(input: EmitHtmlInput): string {\n const head = [\n `<meta charset=\"utf-8\">`,\n `<meta name=\"mountly-mcp-protocol\" content=\"${MCP_APPS_PROTOCOL_VERSION}\">`,\n `<meta name=\"mountly-mcp-uri\" content=\"${escapeAttr(input.uri)}\">`,\n ];\n\n if (input.mode === \"self-contained\") {\n head.push(`<style>${escapeStyle(input.css)}</style>`);\n const body = [\n `<div id=\"mountly-mcp-root\"></div>`,\n ...configScript(input.config),\n `<script type=\"module\">${escapeInlineScript(input.js)}\\n${escapeInlineScript(input.bridgeRuntimeJs)}</script>`,\n ];\n return template(head, body);\n }\n\n head.push(`<link rel=\"stylesheet\" href=\"${escapeAttr(input.cssUrl)}\">`);\n // The View bundle MUST load before the bridge: module scripts execute in\n // document order, and the bridge reads the published View synchronously.\n const body = [\n `<div id=\"mountly-mcp-root\"></div>`,\n ...configScript(input.config),\n `<script type=\"module\" src=\"${escapeAttr(input.jsUrl)}\"></script>`,\n `<script type=\"module\">${escapeInlineScript(input.bridgeRuntimeJs)}</script>`,\n ];\n return template(head, body);\n}\n\nfunction template(head: string[], body: string[]): string {\n return `<!doctype html>\n<html data-mountly-mcp>\n<head>\n${head.join(\"\\n\")}\n</head>\n<body>\n${body.join(\"\\n\")}\n</body>\n</html>`;\n}\n","import { MCP_APPS_MIME, MCP_APPS_PROTOCOL_VERSION } from \"../schema.js\";\nimport type {\n McpUiDisplayMode,\n McpUiResourceCsp,\n McpUiResourcePermissions,\n} from \"@modelcontextprotocol/ext-apps\";\nimport type { OpenAIUiResourceMetadata } from \"@openai/mcp-extensions/server\";\nimport type { McpResourceDeclaration } from \"../types.js\";\n\nexport interface EmitMetaInput {\n uri: string;\n name: string;\n description?: string;\n awaitToolResult?: boolean;\n displayModes?: ReadonlyArray<McpUiDisplayMode>;\n csp?: McpUiResourceCsp;\n /** Sandbox permissions requested by the view (camera/microphone/geolocation/clipboardWrite). */\n permissions?: McpUiResourcePermissions;\n /** Dedicated sandbox origin requested by the view (host-dependent format). */\n domain?: string;\n /** Whether the view requests the host to show a visible border + background. */\n prefersBorder?: boolean;\n /** ChatGPT resource display metadata written to `_meta[\"openai/ui\"]`. */\n openaiUi?: OpenAIUiResourceMetadata;\n}\n\nfunction assertUiUri(uri: string): void {\n if (!uri.startsWith(\"ui://\")) {\n throw new Error(`mountly-mcp: UI resource uri must use the 'ui://' scheme (received '${uri}')`);\n }\n}\n\nexport function emitMeta(input: EmitMetaInput): McpResourceDeclaration {\n assertUiUri(input.uri);\n\n const ui: McpResourceDeclaration[\"_meta\"][\"ui\"] = {};\n if (input.csp) ui.csp = input.csp;\n if (input.permissions) ui.permissions = input.permissions;\n if (input.domain !== undefined) ui.domain = input.domain;\n if (input.prefersBorder !== undefined) ui.prefersBorder = input.prefersBorder;\n\n const decl: McpResourceDeclaration = {\n protocolVersion: MCP_APPS_PROTOCOL_VERSION,\n uri: input.uri,\n name: input.name,\n mimeType: MCP_APPS_MIME,\n awaitToolResult: input.awaitToolResult ?? true,\n displayModes: input.displayModes ?? [\"inline\"],\n _meta: { ui },\n };\n if (input.openaiUi !== undefined) decl._meta[\"openai/ui\"] = input.openaiUi;\n if (input.description !== undefined) decl.description = input.description;\n return decl;\n}\n"],"mappings":"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","names":["body"]}
|
package/dist/cli.js
CHANGED
|
@@ -349,7 +349,7 @@ async function main() {
|
|
|
349
349
|
return addView(parseAddArgs(rest));
|
|
350
350
|
}
|
|
351
351
|
if (command === "doctor") {
|
|
352
|
-
const { doctorCommand } = await import("./doctor-
|
|
352
|
+
const { doctorCommand } = await import("./doctor-NB3DE4CG.js");
|
|
353
353
|
return doctorCommand();
|
|
354
354
|
}
|
|
355
355
|
if (command === "dev") return dev(parseArgs(rest));
|
package/dist/dev/host-entry.js
CHANGED
|
@@ -15186,7 +15186,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
15186
15186
|
}
|
|
15187
15187
|
});
|
|
15188
15188
|
|
|
15189
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.
|
|
15189
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_@cfworker+json-schema@4.1.1_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
|
|
15190
15190
|
init_v4();
|
|
15191
15191
|
var RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task";
|
|
15192
15192
|
var JSONRPC_VERSION = "2.0";
|
|
@@ -16707,7 +16707,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
16707
16707
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/mini/parse.js
|
|
16708
16708
|
init_core2();
|
|
16709
16709
|
|
|
16710
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.
|
|
16710
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_@cfworker+json-schema@4.1.1_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
|
|
16711
16711
|
function isZ4Schema(s2) {
|
|
16712
16712
|
const schema = s2;
|
|
16713
16713
|
return !!schema._zod;
|
|
@@ -16770,7 +16770,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
16770
16770
|
return void 0;
|
|
16771
16771
|
}
|
|
16772
16772
|
|
|
16773
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.
|
|
16773
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_@cfworker+json-schema@4.1.1_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
|
|
16774
16774
|
function isTerminal(status) {
|
|
16775
16775
|
return status === "completed" || status === "failed" || status === "cancelled";
|
|
16776
16776
|
}
|
|
@@ -16778,7 +16778,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
16778
16778
|
// ../../node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@4.4.3/node_modules/zod-to-json-schema/dist/esm/parsers/string.js
|
|
16779
16779
|
var ALPHA_NUMERIC = new Set("ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789");
|
|
16780
16780
|
|
|
16781
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.
|
|
16781
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_@cfworker+json-schema@4.1.1_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
|
|
16782
16782
|
function getMethodLiteral(schema) {
|
|
16783
16783
|
const shape = getObjectShape(schema);
|
|
16784
16784
|
const methodSchema = shape?.method;
|
|
@@ -16799,7 +16799,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
16799
16799
|
return result.data;
|
|
16800
16800
|
}
|
|
16801
16801
|
|
|
16802
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.
|
|
16802
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_@cfworker+json-schema@4.1.1_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
|
|
16803
16803
|
var DEFAULT_REQUEST_TIMEOUT_MSEC = 6e4;
|
|
16804
16804
|
var Protocol = class {
|
|
16805
16805
|
constructor(_options) {
|
|
@@ -17734,7 +17734,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
17734
17734
|
}
|
|
17735
17735
|
};
|
|
17736
17736
|
|
|
17737
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+ext-apps@1.7.5_@modelcontextprotocol+sdk@1.30.
|
|
17737
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+ext-apps@1.7.5_@modelcontextprotocol+sdk@1.30.0_@cfworker+json-schema@4_zach7odob275zkkz2vkisyu32i/node_modules/@modelcontextprotocol/ext-apps/dist/src/app-bridge.js
|
|
17738
17738
|
init_v4();
|
|
17739
17739
|
var ZQ = ((X) => typeof __require < "u" ? __require : typeof Proxy < "u" ? new Proxy(X, { get: (Y, Z) => (typeof __require < "u" ? __require : Y)[Z] }) : X)(function(X) {
|
|
17740
17740
|
if (typeof __require < "u") return __require.apply(this, arguments);
|
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
} from "./chunk-EOWAWZH2.js";
|
|
5
5
|
|
|
6
6
|
// src/doctor.ts
|
|
7
|
-
import { existsSync, readFileSync } from "fs";
|
|
7
|
+
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
8
8
|
import { createRequire } from "module";
|
|
9
|
-
import { resolve } from "path";
|
|
9
|
+
import { dirname, join, resolve } from "path";
|
|
10
10
|
function nodeMajor() {
|
|
11
11
|
return Number(process.versions.node.split(".")[0] ?? 0);
|
|
12
12
|
}
|
|
@@ -18,6 +18,40 @@ function tryResolve(specifier, fromDir) {
|
|
|
18
18
|
return null;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
function findInstalled(pkg, fromDir) {
|
|
22
|
+
for (let dir = resolve(fromDir); ; dir = dirname(dir)) {
|
|
23
|
+
const path = join(dir, "node_modules", pkg, "package.json");
|
|
24
|
+
if (existsSync(path)) return path;
|
|
25
|
+
if (dirname(dir) === dir) return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
var OPENAI_USAGE = /openai\/ui|useOpenAIExtensions|mountly-mcp\/openai|mountly-mcp\/react\/openai|@openai\/mcp-extensions|openaiUi\b|openaiUiToolMeta|enableOpenAiExtensions/;
|
|
29
|
+
function projectSourcesMentionOpenAi(cwd, viteConfig) {
|
|
30
|
+
const candidates = [];
|
|
31
|
+
if (viteConfig) candidates.push(viteConfig);
|
|
32
|
+
for (const name of ["server.ts", "server.mjs", "server.js", "src/server.ts", "src/view.tsx"]) {
|
|
33
|
+
const path = resolve(cwd, name);
|
|
34
|
+
if (existsSync(path)) candidates.push(path);
|
|
35
|
+
}
|
|
36
|
+
const srcDir = resolve(cwd, "src");
|
|
37
|
+
if (existsSync(srcDir)) {
|
|
38
|
+
try {
|
|
39
|
+
for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
|
|
40
|
+
if (entry.isFile() && /\.(tsx?|jsx?|mjs|cjs)$/.test(entry.name)) {
|
|
41
|
+
candidates.push(join(srcDir, entry.name));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
for (const path of candidates) {
|
|
48
|
+
try {
|
|
49
|
+
if (OPENAI_USAGE.test(readFileSync(path, "utf8"))) return true;
|
|
50
|
+
} catch {
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
21
55
|
function runDoctor(cwd = process.cwd()) {
|
|
22
56
|
const checks = [];
|
|
23
57
|
const major = nodeMajor();
|
|
@@ -88,6 +122,14 @@ function runDoctor(cwd = process.cwd()) {
|
|
|
88
122
|
}
|
|
89
123
|
}
|
|
90
124
|
}
|
|
125
|
+
if (projectSourcesMentionOpenAi(cwd, viteConfig)) {
|
|
126
|
+
const openai = findInstalled("@openai/mcp-extensions", cwd);
|
|
127
|
+
checks.push({
|
|
128
|
+
id: "openai-extensions",
|
|
129
|
+
ok: Boolean(openai),
|
|
130
|
+
detail: openai ? `@openai/mcp-extensions resolvable (${openai})` : "Project references OpenAI MCP extensions but @openai/mcp-extensions is not installed. Run: pnpm add @openai/mcp-extensions"
|
|
131
|
+
});
|
|
132
|
+
}
|
|
91
133
|
const hard = checks.filter((c) => c.id !== "manifest" && c.id !== "playwright");
|
|
92
134
|
const ok = hard.every((c) => c.ok);
|
|
93
135
|
return { ok, checks };
|
|
@@ -119,4 +161,4 @@ export {
|
|
|
119
161
|
formatDoctorReport,
|
|
120
162
|
runDoctor
|
|
121
163
|
};
|
|
122
|
-
//# sourceMappingURL=doctor-
|
|
164
|
+
//# sourceMappingURL=doctor-NB3DE4CG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/doctor.ts"],"sourcesContent":["import { existsSync, readFileSync, readdirSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { CLI_ERROR_CODES, cliError } from \"./errors.js\";\n\nexport interface DoctorCheck {\n id: string;\n ok: boolean;\n detail: string;\n}\n\nexport interface DoctorReport {\n ok: boolean;\n checks: DoctorCheck[];\n}\n\nfunction nodeMajor(): number {\n return Number(process.versions.node.split(\".\")[0] ?? 0);\n}\n\nfunction tryResolve(specifier: string, fromDir: string): string | null {\n try {\n const require = createRequire(resolve(fromDir, \"package.json\"));\n return require.resolve(specifier);\n } catch {\n return null;\n }\n}\n\n/**\n * Path to `pkg` in the project's node_modules, walking up from `fromDir`. Reads the\n * filesystem, so packages whose `exports` omit `./package.json` resolve too.\n */\nfunction findInstalled(pkg: string, fromDir: string): string | null {\n for (let dir = resolve(fromDir); ; dir = dirname(dir)) {\n const path = join(dir, \"node_modules\", pkg, \"package.json\");\n if (existsSync(path)) return path;\n if (dirname(dir) === dir) return null;\n }\n}\n\nconst OPENAI_USAGE =\n /openai\\/ui|useOpenAIExtensions|mountly-mcp\\/openai|mountly-mcp\\/react\\/openai|@openai\\/mcp-extensions|openaiUi\\b|openaiUiToolMeta|enableOpenAiExtensions/;\n\nfunction projectSourcesMentionOpenAi(cwd: string, viteConfig?: string): boolean {\n const candidates: string[] = [];\n if (viteConfig) candidates.push(viteConfig);\n for (const name of [\"server.ts\", \"server.mjs\", \"server.js\", \"src/server.ts\", \"src/view.tsx\"]) {\n const path = resolve(cwd, name);\n if (existsSync(path)) candidates.push(path);\n }\n const srcDir = resolve(cwd, \"src\");\n if (existsSync(srcDir)) {\n try {\n for (const entry of readdirSync(srcDir, { withFileTypes: true })) {\n if (entry.isFile() && /\\.(tsx?|jsx?|mjs|cjs)$/.test(entry.name)) {\n candidates.push(join(srcDir, entry.name));\n }\n }\n } catch {\n // ignore unreadable src\n }\n }\n for (const path of candidates) {\n try {\n if (OPENAI_USAGE.test(readFileSync(path, \"utf8\"))) return true;\n } catch {\n // skip\n }\n }\n return false;\n}\n\n/**\n * Check the local project for Mountly MCP readiness. Exits nonzero via the\n * CLI when `ok` is false.\n */\nexport function runDoctor(cwd = process.cwd()): DoctorReport {\n const checks: DoctorCheck[] = [];\n\n const major = nodeMajor();\n checks.push({\n id: \"node\",\n ok: major >= 20,\n detail:\n major >= 20\n ? `Node ${process.versions.node} (ok; 20+ required, 22+ recommended)`\n : `Node ${process.versions.node} is below 20. Upgrade Node, then re-run mountly-mcp doctor.`,\n });\n\n const viteConfigCandidates = [\n \"vite.config.ts\",\n \"vite.config.mts\",\n \"vite.config.js\",\n \"vite.config.mjs\",\n ].map((name) => resolve(cwd, name));\n const viteConfig = viteConfigCandidates.find((path) => existsSync(path));\n checks.push({\n id: \"vite-config\",\n ok: Boolean(viteConfig),\n detail: viteConfig\n ? `Found ${viteConfig}`\n : \"No vite.config.* in this directory. Run from the View project, or: npx mountly-mcp create my-app --framework react\",\n });\n\n if (viteConfig) {\n const source = readFileSync(viteConfig, \"utf8\");\n const hasPlugin = /mountlyMcpViews\\s*\\(/.test(source);\n checks.push({\n id: \"vite-plugin\",\n ok: hasPlugin,\n detail: hasPlugin\n ? \"vite config imports mountlyMcpViews()\"\n : `Add mountlyMcpViews() from \"mountly-mcp/vite\" to ${viteConfig}`,\n });\n }\n\n const manifest = resolve(cwd, \"dist/mountly-mcp.manifest.json\");\n checks.push({\n id: \"manifest\",\n ok: existsSync(manifest),\n detail: existsSync(manifest)\n ? `Found ${manifest}`\n : \"No dist/mountly-mcp.manifest.json yet. Run: npx mountly-mcp build\",\n });\n\n const playwright = tryResolve(\"playwright\", cwd) ?? tryResolve(\"@playwright/test\", cwd);\n checks.push({\n id: \"playwright\",\n ok: Boolean(playwright),\n detail: playwright\n ? `Playwright resolvable for verify --render (${playwright})`\n : \"Playwright not installed. For CI render checks: pnpm add -D playwright && npx playwright install chromium\",\n });\n\n const pkgPath = resolve(cwd, \"package.json\");\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as {\n dependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (deps.react) {\n const reactPath = tryResolve(\"react/package.json\", cwd);\n if (reactPath) {\n const reactPkg = JSON.parse(readFileSync(reactPath, \"utf8\")) as { version?: string };\n const reactMajor = Number((reactPkg.version ?? \"0\").split(\".\")[0]);\n checks.push({\n id: \"react\",\n ok: reactMajor >= 19,\n detail:\n reactMajor >= 19\n ? `React ${reactPkg.version} (ok)`\n : `React ${reactPkg.version} is below 19. mountly-mcp/react and mountly-react require React 19.`,\n });\n }\n }\n if (deps.vite) {\n const vitePath = tryResolve(\"vite/package.json\", cwd);\n if (vitePath) {\n const vitePkg = JSON.parse(readFileSync(vitePath, \"utf8\")) as { version?: string };\n const viteMajor = Number((vitePkg.version ?? \"0\").split(\".\")[0]);\n checks.push({\n id: \"vite\",\n ok: viteMajor >= 8,\n detail:\n viteMajor >= 8\n ? `Vite ${vitePkg.version} (ok)`\n : `Vite ${vitePkg.version} is below 8. mountly-mcp/vite is built and tested against Vite 8.`,\n });\n }\n }\n }\n\n if (projectSourcesMentionOpenAi(cwd, viteConfig)) {\n const openai = findInstalled(\"@openai/mcp-extensions\", cwd);\n checks.push({\n id: \"openai-extensions\",\n ok: Boolean(openai),\n detail: openai\n ? `@openai/mcp-extensions resolvable (${openai})`\n : 'Project references OpenAI MCP extensions but @openai/mcp-extensions is not installed. Run: pnpm add @openai/mcp-extensions',\n });\n }\n\n // Manifest missing is advisory for a brand-new scaffold before first build.\n const hard = checks.filter((c) => c.id !== \"manifest\" && c.id !== \"playwright\");\n const ok = hard.every((c) => c.ok);\n\n return { ok, checks };\n}\n\nexport function formatDoctorReport(report: DoctorReport): string {\n const lines = [\"mountly-mcp doctor\", \"\"];\n for (const check of report.checks) {\n lines.push(`${check.ok ? \"ok\" : \"FAIL\"} ${check.id.padEnd(14)} ${check.detail}`);\n }\n lines.push(\"\");\n lines.push(report.ok ? \"Ready for mountly-mcp build / dev / verify.\" : \"Fix FAIL items above.\");\n lines.push(\"\");\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport function doctorCommand(cwd = process.cwd()): void {\n const report = runDoctor(cwd);\n process.stdout.write(formatDoctorReport(report));\n if (!report.ok) {\n throw cliError(\n CLI_ERROR_CODES.DOCTOR_FAILED,\n \"one or more doctor checks failed\",\n \"Fix the FAIL lines above, then re-run: mountly-mcp doctor\",\n );\n }\n}\n"],"mappings":"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","names":["require"]}
|