mountly-mcp 4.1.0 → 4.3.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 (41) hide show
  1. package/GLOSSARY.md +1 -0
  2. package/README.md +123 -12
  3. package/dist/bridge/iframe-entry.js +7 -7
  4. package/dist/build/emit-meta.d.ts +3 -0
  5. package/dist/build/index.d.ts +3 -0
  6. package/dist/build/index.js +1 -1
  7. package/dist/chunk-4SZHJ4MK.js +9 -0
  8. package/dist/chunk-4SZHJ4MK.js.map +1 -0
  9. package/dist/{chunk-KWVGW5Q7.js → chunk-GN2IQYV3.js} +1 -1
  10. package/dist/chunk-GN2IQYV3.js.map +1 -0
  11. package/dist/{chunk-SXF23BZD.js → chunk-HIAIQVK5.js} +4 -7
  12. package/dist/chunk-HIAIQVK5.js.map +1 -0
  13. package/dist/{chunk-MTJDRWGD.js → chunk-IDGTP2NI.js} +4 -2
  14. package/dist/chunk-IDGTP2NI.js.map +1 -0
  15. package/dist/cli.js +2 -2
  16. package/dist/compose/index.d.ts +48 -0
  17. package/dist/compose/index.js +141 -0
  18. package/dist/compose/index.js.map +1 -0
  19. package/dist/dev/host-entry.js +13 -6
  20. package/dist/dev/index.js +1 -1
  21. package/dist/{doctor-B54H6CRN.js → doctor-4MTC6Y4X.js} +38 -3
  22. package/dist/doctor-4MTC6Y4X.js.map +1 -0
  23. package/dist/index.js +3 -3
  24. package/dist/json-render/index.js +6 -5
  25. package/dist/json-render/index.js.map +1 -1
  26. package/dist/openai/server.d.ts +16 -0
  27. package/dist/openai/server.js +20 -0
  28. package/dist/openai/server.js.map +1 -0
  29. package/dist/react/index.js +4 -2
  30. package/dist/react/openai-extensions.d.ts +6 -0
  31. package/dist/react/openai-extensions.js +25 -0
  32. package/dist/react/openai-extensions.js.map +1 -0
  33. package/dist/server/index.js +11 -6
  34. package/dist/server/index.js.map +1 -1
  35. package/dist/types.d.ts +3 -0
  36. package/dist/vite/index.js +1 -1
  37. package/package.json +25 -3
  38. package/dist/chunk-KWVGW5Q7.js.map +0 -1
  39. package/dist/chunk-MTJDRWGD.js.map +0 -1
  40. package/dist/chunk-SXF23BZD.js.map +0 -1
  41. package/dist/doctor-B54H6CRN.js.map +0 -1
@@ -0,0 +1,141 @@
1
+ // src/compose/index.ts
2
+ import { z } from "zod";
3
+ import {
4
+ RESOURCE_MIME_TYPE,
5
+ registerAppResource,
6
+ registerAppTool
7
+ } from "@modelcontextprotocol/ext-apps/server";
8
+ var DEFAULT_RESOURCE_URI = "ui://mountly/compose.html";
9
+ var DEFAULT_TOOL_NAME = "show_page";
10
+ function composeViewHtml(options) {
11
+ const importMap = JSON.stringify({ imports: options.imports }).replace(/</g, "\\u003c");
12
+ const styles = (options.styles ?? "").replace(/<\/style/gi, "<\\/style");
13
+ const title = (options.title ?? "Page").replace(/[<&]/g, "");
14
+ return `<!doctype html>
15
+ <html lang="en">
16
+ <head>
17
+ <meta charset="utf-8" />
18
+ <title>${title}</title>
19
+ <script type="importmap">${importMap}</script>
20
+ <style>${styles}</style>
21
+ </head>
22
+ <body>
23
+ <main id="page"></main>
24
+ <script type="module">
25
+ import { App } from "@modelcontextprotocol/ext-apps";
26
+ import { connectView } from "mountly-compose";
27
+
28
+ const app = new App({ name: "mountly-compose", version: "1.0.0" });
29
+ connectView(app, document.querySelector("#page"), {
30
+ request: "Show the updated page with ${DEFAULT_TOOL_NAME}.",
31
+ });
32
+ await app.connect();
33
+ </script>
34
+ </body>
35
+ </html>
36
+ `;
37
+ }
38
+ function originsOf(options) {
39
+ const urls = [
40
+ ...Object.values(options.imports),
41
+ ...options.catalog.elements.flatMap((el) => el.embed ? [el.embed] : [])
42
+ ];
43
+ const origins = /* @__PURE__ */ new Set();
44
+ for (const url of urls) {
45
+ try {
46
+ origins.add(new URL(url).origin);
47
+ } catch {
48
+ }
49
+ }
50
+ return [...origins];
51
+ }
52
+ function tagsOf(node, into = /* @__PURE__ */ new Set()) {
53
+ into.add(node.tag);
54
+ for (const child of node.children ?? []) tagsOf(child, into);
55
+ return into;
56
+ }
57
+ function viewPayload(catalog, page) {
58
+ return { page, elements: catalog.elements, actions: catalog.actions };
59
+ }
60
+ function registerComposeApp(server, options) {
61
+ const { catalog } = options;
62
+ const name = options.name ?? DEFAULT_TOOL_NAME;
63
+ const resourceUri = options.resourceUri ?? DEFAULT_RESOURCE_URI;
64
+ if (!resourceUri.startsWith("ui://")) {
65
+ throw new Error(
66
+ `mountly-mcp/compose: resourceUri must use 'ui://' (received '${resourceUri}')`
67
+ );
68
+ }
69
+ const origins = originsOf(options);
70
+ const csp = { connectDomains: origins, resourceDomains: origins, frameDomains: [] };
71
+ const html = composeViewHtml(options);
72
+ registerAppResource(
73
+ server,
74
+ "compose_view",
75
+ resourceUri,
76
+ {
77
+ mimeType: RESOURCE_MIME_TYPE,
78
+ description: "A page composed from teams' custom elements.",
79
+ _meta: { ui: { prefersBorder: false, csp } }
80
+ },
81
+ async () => ({
82
+ contents: [
83
+ { uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html, _meta: { ui: { csp } } }
84
+ ]
85
+ })
86
+ );
87
+ const description = [
88
+ `Show the user a page built from the components below. Call it with { "page": <tree> }.`,
89
+ `To change the page, call it again with the whole updated page.`,
90
+ options.instructions ?? "",
91
+ "",
92
+ catalog.prompt()
93
+ ].filter((line, index) => line || index > 2).join("\n");
94
+ registerAppTool(
95
+ server,
96
+ name,
97
+ {
98
+ title: "Show page",
99
+ description,
100
+ inputSchema: { page: z.unknown().describe("The page as a tree: { tag, attrs, children }.") },
101
+ _meta: { ui: { resourceUri, visibility: ["model", "app"] } }
102
+ },
103
+ (async (args) => {
104
+ const errors = catalog.validate(args.page);
105
+ let page = args.page;
106
+ let note = "";
107
+ if (errors.length) {
108
+ const { tree, dropped } = catalog.repair(args.page);
109
+ if (!tree || catalog.validate(tree).length) {
110
+ return {
111
+ isError: true,
112
+ content: [
113
+ {
114
+ type: "text",
115
+ text: `The page cannot be shown:
116
+ ${errors.join("\n")}
117
+ Fix these and call ${name} again.`
118
+ }
119
+ ]
120
+ };
121
+ }
122
+ page = tree;
123
+ note = ` Removed what the catalog does not allow: ${dropped.join("; ")}.`;
124
+ }
125
+ const hostTags = new Set(
126
+ catalog.elements.filter((el) => el.team === "host").map((el) => el.tag)
127
+ );
128
+ const tags = [...tagsOf(page)].filter((tag) => !hostTags.has(tag));
129
+ return {
130
+ content: [{ type: "text", text: `Showing ${tags.join(", ") || "the page"}.${note}` }],
131
+ structuredContent: viewPayload(catalog, page)
132
+ };
133
+ })
134
+ );
135
+ return { tool: name, resourceUri };
136
+ }
137
+ export {
138
+ composeViewHtml,
139
+ registerComposeApp
140
+ };
141
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/compose/index.ts"],"sourcesContent":["/**\n * Show a page composed from many teams' custom elements inside an MCP host.\n *\n * The agent is the composer: `show_page` carries the catalog's prompt as its\n * description, the agent calls it with a tree, the server validates (or\n * repairs) it, and the `ui://` view renders it with `mountly-compose` — each\n * team's code loading on first use. When the user acts in a widget, the view\n * sends the agent its next turn, with the current page, so it edits rather than\n * starts over.\n */\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport {\n RESOURCE_MIME_TYPE,\n registerAppResource,\n registerAppTool,\n} from \"@modelcontextprotocol/ext-apps/server\";\nimport type { Catalog, Tree } from \"mountly-compose\";\n\nconst DEFAULT_RESOURCE_URI = \"ui://mountly/compose.html\";\nconst DEFAULT_TOOL_NAME = \"show_page\";\n\nexport interface ComposeViewOptions {\n /**\n * The view's import map. Must map `mountly-compose` and\n * `@modelcontextprotocol/ext-apps` (its `app-with-deps` build), plus\n * whatever the teams' peer embeds import: `react`, `react/jsx-runtime`,\n * `react-dom/client`, `mountly-react`, `mountly/embed`.\n */\n imports: Record<string, string>;\n /** The page's own CSS: host layout (`ui-stack`, `ui-grid`, `ui-note`) and design tokens. */\n styles?: string;\n /** Document title. */\n title?: string;\n}\n\nexport interface RegisterComposeAppOptions extends ComposeViewOptions {\n catalog: Catalog;\n /** MCP tool name. Default `show_page`. */\n name?: string;\n /** `ui://` resource for the view. */\n resourceUri?: string;\n /** Appended to the tool description: what the agent may fill ids from, house rules. */\n instructions?: string;\n}\n\n/** The view HTML: an import map, the host's CSS, and a module that renders tool results. */\nexport function composeViewHtml(options: ComposeViewOptions): string {\n const importMap = JSON.stringify({ imports: options.imports }).replace(/</g, \"\\\\u003c\");\n const styles = (options.styles ?? \"\").replace(/<\\/style/gi, \"<\\\\/style\");\n const title = (options.title ?? \"Page\").replace(/[<&]/g, \"\");\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <title>${title}</title>\n <script type=\"importmap\">${importMap}</script>\n <style>${styles}</style>\n </head>\n <body>\n <main id=\"page\"></main>\n <script type=\"module\">\n import { App } from \"@modelcontextprotocol/ext-apps\";\n import { connectView } from \"mountly-compose\";\n\n const app = new App({ name: \"mountly-compose\", version: \"1.0.0\" });\n connectView(app, document.querySelector(\"#page\"), {\n request: \"Show the updated page with ${DEFAULT_TOOL_NAME}.\",\n });\n await app.connect();\n </script>\n </body>\n</html>\n`;\n}\n\n/** Every origin the view loads code or data from, for its content security policy. */\nfunction originsOf(options: RegisterComposeAppOptions): string[] {\n const urls = [\n ...Object.values(options.imports),\n ...options.catalog.elements.flatMap((el) => (el.embed ? [el.embed] : [])),\n ];\n const origins = new Set<string>();\n for (const url of urls) {\n try {\n origins.add(new URL(url).origin);\n } catch {\n // A relative URL is same-origin with the view and needs no entry.\n }\n }\n return [...origins];\n}\n\nfunction tagsOf(node: Tree, into = new Set<string>()): Set<string> {\n into.add(node.tag);\n for (const child of node.children ?? []) tagsOf(child, into);\n return into;\n}\n\n/**\n * What the view renders with: the page, and the catalog's elements and actions.\n * All the elements, not just the page's: an action names the widgets the next\n * page must show, and the view hands that to the agent on the next turn.\n */\nfunction viewPayload(catalog: Catalog, page: Tree) {\n return { page, elements: catalog.elements, actions: catalog.actions };\n}\n\n/**\n * Register the `show_page` tool and its `ui://` view on an MCP server.\n *\n * The input is `{ page }`, typed loosely on purpose: a strict schema would make\n * the SDK reject a near-miss before the handler could repair it. The handler\n * validates against the catalog, strips small mistakes, and otherwise returns\n * the reasons as a tool error the agent can act on.\n */\nexport function registerComposeApp(\n server: McpServer,\n options: RegisterComposeAppOptions,\n): { tool: string; resourceUri: string } {\n const { catalog } = options;\n const name = options.name ?? DEFAULT_TOOL_NAME;\n const resourceUri = options.resourceUri ?? DEFAULT_RESOURCE_URI;\n if (!resourceUri.startsWith(\"ui://\")) {\n throw new Error(\n `mountly-mcp/compose: resourceUri must use 'ui://' (received '${resourceUri}')`,\n );\n }\n const origins = originsOf(options);\n const csp = { connectDomains: origins, resourceDomains: origins, frameDomains: [] };\n const html = composeViewHtml(options);\n\n registerAppResource(\n server,\n \"compose_view\",\n resourceUri,\n {\n mimeType: RESOURCE_MIME_TYPE,\n description: \"A page composed from teams' custom elements.\",\n _meta: { ui: { prefersBorder: false, csp } },\n },\n async () => ({\n contents: [\n { uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html, _meta: { ui: { csp } } },\n ],\n }),\n );\n\n const description = [\n `Show the user a page built from the components below. Call it with { \"page\": <tree> }.`,\n `To change the page, call it again with the whole updated page.`,\n options.instructions ?? \"\",\n \"\",\n catalog.prompt(),\n ]\n .filter((line, index) => line || index > 2)\n .join(\"\\n\");\n\n registerAppTool(\n server,\n name,\n {\n title: \"Show page\",\n description,\n inputSchema: { page: z.unknown().describe(\"The page as a tree: { tag, attrs, children }.\") },\n _meta: { ui: { resourceUri, visibility: [\"model\", \"app\"] } },\n } as never,\n (async (args: { page?: unknown }) => {\n const errors = catalog.validate(args.page);\n let page = args.page as Tree;\n let note = \"\";\n if (errors.length) {\n const { tree, dropped } = catalog.repair(args.page);\n if (!tree || catalog.validate(tree).length) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `The page cannot be shown:\\n${errors.join(\"\\n\")}\\nFix these and call ${name} again.`,\n },\n ],\n };\n }\n page = tree;\n note = ` Removed what the catalog does not allow: ${dropped.join(\"; \")}.`;\n }\n const hostTags = new Set(\n catalog.elements.filter((el) => el.team === \"host\").map((el) => el.tag),\n );\n const tags = [...tagsOf(page)].filter((tag) => !hostTags.has(tag));\n return {\n content: [{ type: \"text\", text: `Showing ${tags.join(\", \") || \"the page\"}.${note}` }],\n structuredContent: viewPayload(catalog, page),\n };\n }) as never,\n );\n\n return { tool: name, resourceUri };\n}\n"],"mappings":"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","names":[]}
@@ -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.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
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.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
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.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
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.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
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.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
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.0_zod@4.4.3__react-dom@19_cmaoxxnyjgqtqbxeim6duwrgeu/node_modules/@modelcontextprotocol/ext-apps/dist/src/app-bridge.js
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);
@@ -18213,6 +18213,8 @@ container holding the app. Specify either width or maxWidth, and either height o
18213
18213
  var capabilities = {
18214
18214
  openLinks: {},
18215
18215
  logging: {},
18216
+ // A view's `sendMessage` asks the host to post the user's next turn to the agent.
18217
+ message: { text: {} },
18216
18218
  ...config2.hasServer ? { serverTools: {} } : {}
18217
18219
  };
18218
18220
  var bridge = new DX(null, { name: "mountly-mcp-dev", version: "1.0.0" }, capabilities, {
@@ -18225,6 +18227,11 @@ container holding the app. Specify either width or maxWidth, and either height o
18225
18227
  }
18226
18228
  });
18227
18229
  bridge.onloggingmessage = (params) => log("in", `notifications/message ${params.level}`);
18230
+ bridge.onmessage = async ({ content }) => {
18231
+ const text = content.map((block) => "text" in block ? block.text : `[${block.type}]`).join(" ");
18232
+ log("in", `ui/message ${text}`);
18233
+ return {};
18234
+ };
18228
18235
  bridge.onopenlink = async ({ url: url2 }) => {
18229
18236
  window.open(url2, "_blank", "noopener,noreferrer");
18230
18237
  return {};
package/dist/dev/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  sandboxProxyHtml,
3
3
  startDevHost
4
- } from "../chunk-KWVGW5Q7.js";
4
+ } from "../chunk-GN2IQYV3.js";
5
5
  import {
6
6
  connectMcpServer
7
7
  } from "../chunk-OWAXBNZA.js";
@@ -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 { join, resolve } from "path";
10
10
  function nodeMajor() {
11
11
  return Number(process.versions.node.split(".")[0] ?? 0);
12
12
  }
@@ -18,6 +18,33 @@ function tryResolve(specifier, fromDir) {
18
18
  return null;
19
19
  }
20
20
  }
21
+ var OPENAI_USAGE = /openai\/ui|useOpenAIExtensions|mountly-mcp\/openai|mountly-mcp\/react\/openai|@openai\/mcp-extensions|openaiUi\b|openaiUiToolMeta|enableOpenAiExtensions/;
22
+ function projectSourcesMentionOpenAi(cwd, viteConfig) {
23
+ const candidates = [];
24
+ if (viteConfig) candidates.push(viteConfig);
25
+ for (const name of ["server.ts", "server.mjs", "server.js", "src/server.ts", "src/view.tsx"]) {
26
+ const path = resolve(cwd, name);
27
+ if (existsSync(path)) candidates.push(path);
28
+ }
29
+ const srcDir = resolve(cwd, "src");
30
+ if (existsSync(srcDir)) {
31
+ try {
32
+ for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
33
+ if (entry.isFile() && /\.(tsx?|jsx?|mjs|cjs)$/.test(entry.name)) {
34
+ candidates.push(join(srcDir, entry.name));
35
+ }
36
+ }
37
+ } catch {
38
+ }
39
+ }
40
+ for (const path of candidates) {
41
+ try {
42
+ if (OPENAI_USAGE.test(readFileSync(path, "utf8"))) return true;
43
+ } catch {
44
+ }
45
+ }
46
+ return false;
47
+ }
21
48
  function runDoctor(cwd = process.cwd()) {
22
49
  const checks = [];
23
50
  const major = nodeMajor();
@@ -88,6 +115,14 @@ function runDoctor(cwd = process.cwd()) {
88
115
  }
89
116
  }
90
117
  }
118
+ if (projectSourcesMentionOpenAi(cwd, viteConfig)) {
119
+ const openai = tryResolve("@openai/mcp-extensions/package.json", cwd);
120
+ checks.push({
121
+ id: "openai-extensions",
122
+ ok: Boolean(openai),
123
+ 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"
124
+ });
125
+ }
91
126
  const hard = checks.filter((c) => c.id !== "manifest" && c.id !== "playwright");
92
127
  const ok = hard.every((c) => c.ok);
93
128
  return { ok, checks };
@@ -119,4 +154,4 @@ export {
119
154
  formatDoctorReport,
120
155
  runDoctor
121
156
  };
122
- //# sourceMappingURL=doctor-B54H6CRN.js.map
157
+ //# sourceMappingURL=doctor-4MTC6Y4X.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 { 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\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 = tryResolve(\"@openai/mcp-extensions/package.json\", 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":";;;;;;AAAA,SAAS,YAAY,cAAc,mBAAmB;AACtD,SAAS,qBAAqB;AAC9B,SAAS,MAAM,eAAe;AAc9B,SAAS,YAAoB;AAC3B,SAAO,OAAO,QAAQ,SAAS,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC;AACxD;AAEA,SAAS,WAAW,WAAmB,SAAgC;AACrE,MAAI;AACF,UAAMA,WAAU,cAAc,QAAQ,SAAS,cAAc,CAAC;AAC9D,WAAOA,SAAQ,QAAQ,SAAS;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAM,eACJ;AAEF,SAAS,4BAA4B,KAAa,YAA8B;AAC9E,QAAM,aAAuB,CAAC;AAC9B,MAAI,WAAY,YAAW,KAAK,UAAU;AAC1C,aAAW,QAAQ,CAAC,aAAa,cAAc,aAAa,iBAAiB,cAAc,GAAG;AAC5F,UAAM,OAAO,QAAQ,KAAK,IAAI;AAC9B,QAAI,WAAW,IAAI,EAAG,YAAW,KAAK,IAAI;AAAA,EAC5C;AACA,QAAM,SAAS,QAAQ,KAAK,KAAK;AACjC,MAAI,WAAW,MAAM,GAAG;AACtB,QAAI;AACF,iBAAW,SAAS,YAAY,QAAQ,EAAE,eAAe,KAAK,CAAC,GAAG;AAChE,YAAI,MAAM,OAAO,KAAK,yBAAyB,KAAK,MAAM,IAAI,GAAG;AAC/D,qBAAW,KAAK,KAAK,QAAQ,MAAM,IAAI,CAAC;AAAA,QAC1C;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACA,aAAW,QAAQ,YAAY;AAC7B,QAAI;AACF,UAAI,aAAa,KAAK,aAAa,MAAM,MAAM,CAAC,EAAG,QAAO;AAAA,IAC5D,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,UAAU,MAAM,QAAQ,IAAI,GAAiB;AAC3D,QAAM,SAAwB,CAAC;AAE/B,QAAM,QAAQ,UAAU;AACxB,SAAO,KAAK;AAAA,IACV,IAAI;AAAA,IACJ,IAAI,SAAS;AAAA,IACb,QACE,SAAS,KACL,QAAQ,QAAQ,SAAS,IAAI,yCAC7B,QAAQ,QAAQ,SAAS,IAAI;AAAA,EACrC,CAAC;AAED,QAAM,uBAAuB;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,IAAI,CAAC,SAAS,QAAQ,KAAK,IAAI,CAAC;AAClC,QAAM,aAAa,qBAAqB,KAAK,CAAC,SAAS,WAAW,IAAI,CAAC;AACvE,SAAO,KAAK;AAAA,IACV,IAAI;AAAA,IACJ,IAAI,QAAQ,UAAU;AAAA,IACtB,QAAQ,aACJ,SAAS,UAAU,KACnB;AAAA,EACN,CAAC;AAED,MAAI,YAAY;AACd,UAAM,SAAS,aAAa,YAAY,MAAM;AAC9C,UAAM,YAAY,uBAAuB,KAAK,MAAM;AACpD,WAAO,KAAK;AAAA,MACV,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,QAAQ,YACJ,0CACA,oDAAoD,UAAU;AAAA,IACpE,CAAC;AAAA,EACH;AAEA,QAAM,WAAW,QAAQ,KAAK,gCAAgC;AAC9D,SAAO,KAAK;AAAA,IACV,IAAI;AAAA,IACJ,IAAI,WAAW,QAAQ;AAAA,IACvB,QAAQ,WAAW,QAAQ,IACvB,SAAS,QAAQ,KACjB;AAAA,EACN,CAAC;AAED,QAAM,aAAa,WAAW,cAAc,GAAG,KAAK,WAAW,oBAAoB,GAAG;AACtF,SAAO,KAAK;AAAA,IACV,IAAI;AAAA,IACJ,IAAI,QAAQ,UAAU;AAAA,IACtB,QAAQ,aACJ,8CAA8C,UAAU,MACxD;AAAA,EACN,CAAC;AAED,QAAM,UAAU,QAAQ,KAAK,cAAc;AAC3C,MAAI,WAAW,OAAO,GAAG;AACvB,UAAM,MAAM,KAAK,MAAM,aAAa,SAAS,MAAM,CAAC;AAKpD,UAAM,OAAO,EAAE,GAAG,IAAI,cAAc,GAAG,IAAI,gBAAgB;AAC3D,QAAI,KAAK,OAAO;AACd,YAAM,YAAY,WAAW,sBAAsB,GAAG;AACtD,UAAI,WAAW;AACb,cAAM,WAAW,KAAK,MAAM,aAAa,WAAW,MAAM,CAAC;AAC3D,cAAM,aAAa,QAAQ,SAAS,WAAW,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC;AACjE,eAAO,KAAK;AAAA,UACV,IAAI;AAAA,UACJ,IAAI,cAAc;AAAA,UAClB,QACE,cAAc,KACV,SAAS,SAAS,OAAO,UACzB,SAAS,SAAS,OAAO;AAAA,QACjC,CAAC;AAAA,MACH;AAAA,IACF;AACA,QAAI,KAAK,MAAM;AACb,YAAM,WAAW,WAAW,qBAAqB,GAAG;AACpD,UAAI,UAAU;AACZ,cAAM,UAAU,KAAK,MAAM,aAAa,UAAU,MAAM,CAAC;AACzD,cAAM,YAAY,QAAQ,QAAQ,WAAW,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC;AAC/D,eAAO,KAAK;AAAA,UACV,IAAI;AAAA,UACJ,IAAI,aAAa;AAAA,UACjB,QACE,aAAa,IACT,QAAQ,QAAQ,OAAO,UACvB,QAAQ,QAAQ,OAAO;AAAA,QAC/B,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,4BAA4B,KAAK,UAAU,GAAG;AAChD,UAAM,SAAS,WAAW,uCAAuC,GAAG;AACpE,WAAO,KAAK;AAAA,MACV,IAAI;AAAA,MACJ,IAAI,QAAQ,MAAM;AAAA,MAClB,QAAQ,SACJ,sCAAsC,MAAM,MAC5C;AAAA,IACN,CAAC;AAAA,EACH;AAGA,QAAM,OAAO,OAAO,OAAO,CAAC,MAAM,EAAE,OAAO,cAAc,EAAE,OAAO,YAAY;AAC9E,QAAM,KAAK,KAAK,MAAM,CAAC,MAAM,EAAE,EAAE;AAEjC,SAAO,EAAE,IAAI,OAAO;AACtB;AAEO,SAAS,mBAAmB,QAA8B;AAC/D,QAAM,QAAQ,CAAC,sBAAsB,EAAE;AACvC,aAAW,SAAS,OAAO,QAAQ;AACjC,UAAM,KAAK,GAAG,MAAM,KAAK,OAAO,MAAM,KAAK,MAAM,GAAG,OAAO,EAAE,CAAC,IAAI,MAAM,MAAM,EAAE;AAAA,EAClF;AACA,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,OAAO,KAAK,gDAAgD,uBAAuB;AAC9F,QAAM,KAAK,EAAE;AACb,SAAO,GAAG,MAAM,KAAK,IAAI,CAAC;AAAA;AAC5B;AAEO,SAAS,cAAc,MAAM,QAAQ,IAAI,GAAS;AACvD,QAAM,SAAS,UAAU,GAAG;AAC5B,UAAQ,OAAO,MAAM,mBAAmB,MAAM,CAAC;AAC/C,MAAI,CAAC,OAAO,IAAI;AACd,UAAM;AAAA,MACJ,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;","names":["require"]}
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
- import {
2
- sandboxProxyHtml
3
- } from "./chunk-6QHKKFO3.js";
4
1
  import {
5
2
  MCP_VIEW_GLOBAL_KEY,
6
3
  getPublishedMcpView,
7
4
  publishMcpView
8
5
  } from "./chunk-5L3QHYWK.js";
6
+ import {
7
+ sandboxProxyHtml
8
+ } from "./chunk-6QHKKFO3.js";
9
9
  import {
10
10
  App,
11
11
  PostMessageTransport,
@@ -1,3 +1,7 @@
1
+ import {
2
+ buildAppHtml,
3
+ useJsonRenderApp
4
+ } from "../chunk-33DMFKKD.js";
1
5
  import {
2
6
  createJsonRenderMcpApp,
3
7
  registerJsonRenderResource,
@@ -15,12 +19,9 @@ import {
15
19
  createMcpView,
16
20
  useMcpApp,
17
21
  useToolResult
18
- } from "../chunk-SXF23BZD.js";
22
+ } from "../chunk-HIAIQVK5.js";
23
+ import "../chunk-4SZHJ4MK.js";
19
24
  import "../chunk-5L3QHYWK.js";
20
- import {
21
- buildAppHtml,
22
- useJsonRenderApp
23
- } from "../chunk-33DMFKKD.js";
24
25
  import "../chunk-YJA4H6IB.js";
25
26
 
26
27
  // src/json-render/generative-view.tsx
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/json-render/generative-view.tsx","../../src/json-render/index.ts","../../src/json-render/use-spec-stream.ts"],"sourcesContent":["import { createElement } from \"react\";\nimport { type ComponentMap, createRenderer } from \"@json-render/react\";\nimport type { Catalog, SchemaDefinition, Spec } from \"@json-render/core\";\nimport { createMcpView, useMcpApp, useToolResult } from \"../react/index.js\";\nimport type { App } from \"@modelcontextprotocol/ext-apps\";\nimport type { AdapterOptions } from \"mountly/adapter\";\nimport type { McpView } from \"../types.js\";\n\n/** The catalog-data constraint createRenderer infers against. */\ntype CatalogData = { components: Record<string, { props: unknown }> };\n\n/**\n * Route a generated UI's actions back to the MCP host. Called with the action\n * name, its (resolved) params, and the view-side `App` handle.\n */\nexport type ActionRouter = (\n name: string,\n params: Record<string, unknown> | undefined,\n mcp: App,\n) => void;\n\n/**\n * Default router: an `ask` action with a string `prompt` param sends a\n * follow-up user turn to the model via `App.sendMessage`. This is the agent\n * loop json-render alone cannot do — it has no concept of an MCP host.\n */\nexport const defaultActionRouter: ActionRouter = (name, params, mcp) => {\n if (name !== \"ask\") return;\n const prompt = params?.prompt;\n const text = typeof prompt === \"string\" ? prompt : \"\";\n if (text) {\n void mcp.sendMessage({ role: \"user\", content: [{ type: \"text\", text }] });\n }\n};\n\n/**\n * Identity helper that types a components map against a catalog (so it can be\n * defined once and reused by both `createGenerativeView` and `createRenderer`)\n * without losing per-component prop inference.\n *\n * ```ts\n * export const components = defineComponents(catalog, {\n * Metric: ({ element }) => <span>{element.props.value}</span>,\n * });\n * ```\n */\nexport function defineComponents<TDef extends SchemaDefinition, TCatalog extends CatalogData>(\n _catalog: Catalog<TDef, TCatalog>,\n components: ComponentMap<TCatalog[\"components\"]>,\n): ComponentMap<TCatalog[\"components\"]> {\n return components;\n}\n\nexport interface GenerativeViewOptions<\n TDef extends SchemaDefinition,\n TCatalog extends CatalogData,\n> extends AdapterOptions {\n /** The json-render catalog — the vocabulary the agent may compose. */\n catalog: Catalog<TDef, TCatalog>;\n /** Native component implementations for each catalog type. */\n components: ComponentMap<TCatalog[\"components\"]>;\n /**\n * How a generated UI's actions reach the agent. Defaults to\n * {@link defaultActionRouter} (`ask` → `App.sendMessage`).\n */\n onAction?: ActionRouter;\n}\n\ninterface ToolResultShape {\n structuredContent?: { spec?: Spec };\n}\n\n/**\n * Turn a json-render catalog + components into a mountly MCP Apps View.\n *\n * Reads the spec from the tool result, renders it natively (resolving `$state`\n * from `spec.state`), and routes actions back to the MCP host.\n *\n * ```ts\n * createGenerativeView({ catalog, components, styles, shadow: true });\n * ```\n */\nexport function createGenerativeView<TDef extends SchemaDefinition, TCatalog extends CatalogData>(\n options: GenerativeViewOptions<TDef, TCatalog>,\n): McpView {\n const { catalog, components, onAction = defaultActionRouter, ...adapter } = options;\n const Rendered = createRenderer(catalog, components);\n\n function GenerativeView() {\n const mcp = useMcpApp();\n const result = useToolResult<ToolResultShape>();\n const spec = result?.structuredContent?.spec ?? null;\n if (!spec) return null;\n return createElement(Rendered, {\n spec,\n state: spec.state,\n onAction: (name: string, params?: Record<string, unknown>) => onAction(name, params, mcp),\n });\n }\n\n return createMcpView(GenerativeView, adapter);\n}\n","export {\n type ActionRouter,\n type GenerativeViewOptions,\n createGenerativeView,\n defaultActionRouter,\n defineComponents,\n} from \"./generative-view.js\";\nexport {\n type JsonRenderToolOptions,\n type RegisterJsonRenderResourceOptions,\n type RegisterJsonRenderToolOptions,\n type CreateJsonRenderMcpAppOptions,\n createJsonRenderMcpApp,\n registerJsonRenderTool,\n registerJsonRenderResource,\n} from \"./mcp.js\";\nexport {\n type BuildAppHtmlOptions,\n type UseJsonRenderAppOptions,\n type UseJsonRenderAppReturn,\n buildAppHtml,\n useJsonRenderApp,\n} from \"./app.js\";\n\n// Convenience re-exports for rendering the same catalog natively (non-MCP) —\n// previews, tests, or a plain mountly feature. Pair with `mountly-react`'s\n// `createWidget` to mount a spec outside an MCP host.\nexport { createRenderer } from \"@json-render/react\";\nexport type { ComponentMap, ComponentRenderProps } from \"@json-render/react\";\n\n// Streaming primitives (json-render's own): replay a known spec as a patch\n// stream, compile patches back into a progressively-built spec, and the AI-SDK\n// transform that classifies a model's UI-message stream into spec patches.\nexport {\n compileSpecStream,\n compileTextStreamToSpecs,\n createJsonRenderTransform,\n createSpecStreamCompiler,\n parseSpecStreamLine,\n specToPatchLines,\n} from \"./spec-stream.js\";\nexport type { Spec } from \"@json-render/core\";\n\n// --- Two ways to stream ---\n//\n// Replay (known spec): `useSpecStream` takes a finished spec — e.g. the one an\n// MCP tool delivered as `structuredContent` — and replays it as a live build,\n// with the compiler, loading flag, and cancellation included.\nexport {\n type SpecStreamState,\n type UseSpecStreamOptions,\n useSpecStream,\n} from \"./use-spec-stream.js\";\n\n// Live (from a model): `useUIStream` / `useChatUI` are json-render's own client\n// hooks — POST a prompt to an endpoint that streams JSONL patches (see\n// `streamSpec` in `mountly-mcp/json-render/server`) and render the spec as it\n// builds. Re-exported so the live path is one import away.\nexport { useChatUI, useUIStream } from \"@json-render/react\";\nexport type { UseUIStreamOptions, UseUIStreamReturn } from \"@json-render/react\";\n","import { useEffect, useState } from \"react\";\nimport type { Spec } from \"@json-render/core\";\nimport { createSpecStreamCompiler } from \"@json-render/core\";\nimport { specToPatchLines } from \"./spec-stream.js\";\n\nexport interface UseSpecStreamOptions {\n /** Delay between patches, ms. Default 90. Set 0 to render instantly. */\n intervalMs?: number;\n}\n\nexport interface SpecStreamState {\n /** The progressively-built spec (partial while streaming, full when done). */\n spec: Spec | null;\n /** Convenience: `spec?.state`, to pass straight to the renderer. */\n state: Record<string, unknown> | undefined;\n /** True while patches are still being applied. */\n loading: boolean;\n}\n\n/**\n * Replay a finished spec as a live json-render stream — and own all the\n * ceremony. Give it the target spec; it scaffolds a `createSpecStreamCompiler`,\n * replays the patches with a delay, exposes the partial spec + `loading` after\n * each step, and cancels cleanly when the target changes (so a self-driving UI\n * that swaps specs mid-stream just works). Re-streams whenever `target` changes.\n *\n * ```tsx\n * const { spec, state, loading } = useSpecStream(specs[view]);\n * return <Renderer spec={spec} state={state} loading={loading} onAction={...} />;\n * ```\n */\nexport function useSpecStream(\n target: Spec | null | undefined,\n options: UseSpecStreamOptions = {},\n): SpecStreamState {\n const { intervalMs = 90 } = options;\n const [spec, setSpec] = useState<Spec | null>(null);\n const [loading, setLoading] = useState(false);\n\n useEffect(() => {\n if (!target) {\n setSpec(null);\n setLoading(false);\n return;\n }\n let cancelled = false;\n const compiler = createSpecStreamCompiler<Spec>();\n const lines = specToPatchLines(target);\n setLoading(true);\n\n void (async () => {\n for (const line of lines) {\n if (cancelled) return;\n const { result } = compiler.push(`${line}\\n`);\n setSpec({ ...result });\n if (intervalMs > 0) {\n await new Promise((r) => setTimeout(r, intervalMs));\n }\n }\n if (cancelled) return;\n setSpec(compiler.getResult());\n setLoading(false);\n })();\n\n return () => {\n cancelled = true;\n };\n }, [target, intervalMs]);\n\n return { spec, state: spec?.state, loading };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,qBAAqB;AAC9B,SAA4B,sBAAsB;AAyB3C,IAAM,sBAAoC,CAAC,MAAM,QAAQ,QAAQ;AACtE,MAAI,SAAS,MAAO;AACpB,QAAM,SAAS,QAAQ;AACvB,QAAM,OAAO,OAAO,WAAW,WAAW,SAAS;AACnD,MAAI,MAAM;AACR,SAAK,IAAI,YAAY,EAAE,MAAM,QAAQ,SAAS,CAAC,EAAE,MAAM,QAAQ,KAAK,CAAC,EAAE,CAAC;AAAA,EAC1E;AACF;AAaO,SAAS,iBACd,UACA,YACsC;AACtC,SAAO;AACT;AA+BO,SAAS,qBACd,SACS;AACT,QAAM,EAAE,SAAS,YAAY,WAAW,qBAAqB,GAAG,QAAQ,IAAI;AAC5E,QAAM,WAAW,eAAe,SAAS,UAAU;AAEnD,WAAS,iBAAiB;AACxB,UAAM,MAAM,UAAU;AACtB,UAAM,SAAS,cAA+B;AAC9C,UAAM,OAAO,QAAQ,mBAAmB,QAAQ;AAChD,QAAI,CAAC,KAAM,QAAO;AAClB,WAAO,cAAc,UAAU;AAAA,MAC7B;AAAA,MACA,OAAO,KAAK;AAAA,MACZ,UAAU,CAAC,MAAc,WAAqC,SAAS,MAAM,QAAQ,GAAG;AAAA,IAC1F,CAAC;AAAA,EACH;AAEA,SAAO,cAAc,gBAAgB,OAAO;AAC9C;;;AC1EA,SAAS,kBAAAA,uBAAsB;;;AC3B/B,SAAS,WAAW,gBAAgB;AAEpC,SAAS,4BAAAC,iCAAgC;AA6BlC,SAAS,cACd,QACA,UAAgC,CAAC,GAChB;AACjB,QAAM,EAAE,aAAa,GAAG,IAAI;AAC5B,QAAM,CAAC,MAAM,OAAO,IAAI,SAAsB,IAAI;AAClD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAE5C,YAAU,MAAM;AACd,QAAI,CAAC,QAAQ;AACX,cAAQ,IAAI;AACZ,iBAAW,KAAK;AAChB;AAAA,IACF;AACA,QAAI,YAAY;AAChB,UAAM,WAAWC,0BAA+B;AAChD,UAAM,QAAQ,iBAAiB,MAAM;AACrC,eAAW,IAAI;AAEf,UAAM,YAAY;AAChB,iBAAW,QAAQ,OAAO;AACxB,YAAI,UAAW;AACf,cAAM,EAAE,OAAO,IAAI,SAAS,KAAK,GAAG,IAAI;AAAA,CAAI;AAC5C,gBAAQ,EAAE,GAAG,OAAO,CAAC;AACrB,YAAI,aAAa,GAAG;AAClB,gBAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,UAAU,CAAC;AAAA,QACpD;AAAA,MACF;AACA,UAAI,UAAW;AACf,cAAQ,SAAS,UAAU,CAAC;AAC5B,iBAAW,KAAK;AAAA,IAClB,GAAG;AAEH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,QAAQ,UAAU,CAAC;AAEvB,SAAO,EAAE,MAAM,OAAO,MAAM,OAAO,QAAQ;AAC7C;;;ADZA,SAAS,WAAW,mBAAmB;","names":["createRenderer","createSpecStreamCompiler","createSpecStreamCompiler"]}
1
+ {"version":3,"sources":["../../src/json-render/generative-view.tsx","../../src/json-render/index.ts","../../src/json-render/use-spec-stream.ts"],"sourcesContent":["import { createElement } from \"react\";\nimport { type ComponentMap, createRenderer } from \"@json-render/react\";\nimport type { Catalog, SchemaDefinition, Spec } from \"@json-render/core\";\nimport { createMcpView, useMcpApp, useToolResult } from \"../react/index.js\";\nimport type { App } from \"@modelcontextprotocol/ext-apps\";\nimport type { AdapterOptions } from \"mountly/adapter\";\nimport type { McpView } from \"../types.js\";\n\n/** The catalog-data constraint createRenderer infers against. */\ntype CatalogData = { components: Record<string, { props: unknown }> };\n\n/**\n * Route a generated UI's actions back to the MCP host. Called with the action\n * name, its (resolved) params, and the view-side `App` handle.\n */\nexport type ActionRouter = (\n name: string,\n params: Record<string, unknown> | undefined,\n mcp: App,\n) => void;\n\n/**\n * Default router: an `ask` action with a string `prompt` param sends a\n * follow-up user turn to the model via `App.sendMessage`. This is the agent\n * loop json-render alone cannot do — it has no concept of an MCP host.\n */\nexport const defaultActionRouter: ActionRouter = (name, params, mcp) => {\n if (name !== \"ask\") return;\n const prompt = params?.prompt;\n const text = typeof prompt === \"string\" ? prompt : \"\";\n if (text) {\n void mcp.sendMessage({ role: \"user\", content: [{ type: \"text\", text }] });\n }\n};\n\n/**\n * Identity helper that types a components map against a catalog (so it can be\n * defined once and reused by both `createGenerativeView` and `createRenderer`)\n * without losing per-component prop inference.\n *\n * ```ts\n * export const components = defineComponents(catalog, {\n * Metric: ({ element }) => <span>{element.props.value}</span>,\n * });\n * ```\n */\nexport function defineComponents<TDef extends SchemaDefinition, TCatalog extends CatalogData>(\n _catalog: Catalog<TDef, TCatalog>,\n components: ComponentMap<TCatalog[\"components\"]>,\n): ComponentMap<TCatalog[\"components\"]> {\n return components;\n}\n\nexport interface GenerativeViewOptions<\n TDef extends SchemaDefinition,\n TCatalog extends CatalogData,\n> extends AdapterOptions {\n /** The json-render catalog — the vocabulary the agent may compose. */\n catalog: Catalog<TDef, TCatalog>;\n /** Native component implementations for each catalog type. */\n components: ComponentMap<TCatalog[\"components\"]>;\n /**\n * How a generated UI's actions reach the agent. Defaults to\n * {@link defaultActionRouter} (`ask` → `App.sendMessage`).\n */\n onAction?: ActionRouter;\n}\n\ninterface ToolResultShape {\n structuredContent?: { spec?: Spec };\n}\n\n/**\n * Turn a json-render catalog + components into a mountly MCP Apps View.\n *\n * Reads the spec from the tool result, renders it natively (resolving `$state`\n * from `spec.state`), and routes actions back to the MCP host.\n *\n * ```ts\n * createGenerativeView({ catalog, components, styles, shadow: true });\n * ```\n */\nexport function createGenerativeView<TDef extends SchemaDefinition, TCatalog extends CatalogData>(\n options: GenerativeViewOptions<TDef, TCatalog>,\n): McpView {\n const { catalog, components, onAction = defaultActionRouter, ...adapter } = options;\n const Rendered = createRenderer(catalog, components);\n\n function GenerativeView() {\n const mcp = useMcpApp();\n const result = useToolResult<ToolResultShape>();\n const spec = result?.structuredContent?.spec ?? null;\n if (!spec) return null;\n return createElement(Rendered, {\n spec,\n state: spec.state,\n onAction: (name: string, params?: Record<string, unknown>) => onAction(name, params, mcp),\n });\n }\n\n return createMcpView(GenerativeView, adapter);\n}\n","export {\n type ActionRouter,\n type GenerativeViewOptions,\n createGenerativeView,\n defaultActionRouter,\n defineComponents,\n} from \"./generative-view.js\";\nexport {\n type JsonRenderToolOptions,\n type RegisterJsonRenderResourceOptions,\n type RegisterJsonRenderToolOptions,\n type CreateJsonRenderMcpAppOptions,\n createJsonRenderMcpApp,\n registerJsonRenderTool,\n registerJsonRenderResource,\n} from \"./mcp.js\";\nexport {\n type BuildAppHtmlOptions,\n type UseJsonRenderAppOptions,\n type UseJsonRenderAppReturn,\n buildAppHtml,\n useJsonRenderApp,\n} from \"./app.js\";\n\n// Convenience re-exports for rendering the same catalog natively (non-MCP) —\n// previews, tests, or a plain mountly feature. Pair with `mountly-react`'s\n// `createWidget` to mount a spec outside an MCP host.\nexport { createRenderer } from \"@json-render/react\";\nexport type { ComponentMap, ComponentRenderProps } from \"@json-render/react\";\n\n// Streaming primitives (json-render's own): replay a known spec as a patch\n// stream, compile patches back into a progressively-built spec, and the AI-SDK\n// transform that classifies a model's UI-message stream into spec patches.\nexport {\n compileSpecStream,\n compileTextStreamToSpecs,\n createJsonRenderTransform,\n createSpecStreamCompiler,\n parseSpecStreamLine,\n specToPatchLines,\n} from \"./spec-stream.js\";\nexport type { Spec } from \"@json-render/core\";\n\n// --- Two ways to stream ---\n//\n// Replay (known spec): `useSpecStream` takes a finished spec — e.g. the one an\n// MCP tool delivered as `structuredContent` — and replays it as a live build,\n// with the compiler, loading flag, and cancellation included.\nexport {\n type SpecStreamState,\n type UseSpecStreamOptions,\n useSpecStream,\n} from \"./use-spec-stream.js\";\n\n// Live (from a model): `useUIStream` / `useChatUI` are json-render's own client\n// hooks — POST a prompt to an endpoint that streams JSONL patches (see\n// `streamSpec` in `mountly-mcp/json-render/server`) and render the spec as it\n// builds. Re-exported so the live path is one import away.\nexport { useChatUI, useUIStream } from \"@json-render/react\";\nexport type { UseUIStreamOptions, UseUIStreamReturn } from \"@json-render/react\";\n","import { useEffect, useState } from \"react\";\nimport type { Spec } from \"@json-render/core\";\nimport { createSpecStreamCompiler } from \"@json-render/core\";\nimport { specToPatchLines } from \"./spec-stream.js\";\n\nexport interface UseSpecStreamOptions {\n /** Delay between patches, ms. Default 90. Set 0 to render instantly. */\n intervalMs?: number;\n}\n\nexport interface SpecStreamState {\n /** The progressively-built spec (partial while streaming, full when done). */\n spec: Spec | null;\n /** Convenience: `spec?.state`, to pass straight to the renderer. */\n state: Record<string, unknown> | undefined;\n /** True while patches are still being applied. */\n loading: boolean;\n}\n\n/**\n * Replay a finished spec as a live json-render stream — and own all the\n * ceremony. Give it the target spec; it scaffolds a `createSpecStreamCompiler`,\n * replays the patches with a delay, exposes the partial spec + `loading` after\n * each step, and cancels cleanly when the target changes (so a self-driving UI\n * that swaps specs mid-stream just works). Re-streams whenever `target` changes.\n *\n * ```tsx\n * const { spec, state, loading } = useSpecStream(specs[view]);\n * return <Renderer spec={spec} state={state} loading={loading} onAction={...} />;\n * ```\n */\nexport function useSpecStream(\n target: Spec | null | undefined,\n options: UseSpecStreamOptions = {},\n): SpecStreamState {\n const { intervalMs = 90 } = options;\n const [spec, setSpec] = useState<Spec | null>(null);\n const [loading, setLoading] = useState(false);\n\n useEffect(() => {\n if (!target) {\n setSpec(null);\n setLoading(false);\n return;\n }\n let cancelled = false;\n const compiler = createSpecStreamCompiler<Spec>();\n const lines = specToPatchLines(target);\n setLoading(true);\n\n void (async () => {\n for (const line of lines) {\n if (cancelled) return;\n const { result } = compiler.push(`${line}\\n`);\n setSpec({ ...result });\n if (intervalMs > 0) {\n await new Promise((r) => setTimeout(r, intervalMs));\n }\n }\n if (cancelled) return;\n setSpec(compiler.getResult());\n setLoading(false);\n })();\n\n return () => {\n cancelled = true;\n };\n }, [target, intervalMs]);\n\n return { spec, state: spec?.state, loading };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,qBAAqB;AAC9B,SAA4B,sBAAsB;AAyB3C,IAAM,sBAAoC,CAAC,MAAM,QAAQ,QAAQ;AACtE,MAAI,SAAS,MAAO;AACpB,QAAM,SAAS,QAAQ;AACvB,QAAM,OAAO,OAAO,WAAW,WAAW,SAAS;AACnD,MAAI,MAAM;AACR,SAAK,IAAI,YAAY,EAAE,MAAM,QAAQ,SAAS,CAAC,EAAE,MAAM,QAAQ,KAAK,CAAC,EAAE,CAAC;AAAA,EAC1E;AACF;AAaO,SAAS,iBACd,UACA,YACsC;AACtC,SAAO;AACT;AA+BO,SAAS,qBACd,SACS;AACT,QAAM,EAAE,SAAS,YAAY,WAAW,qBAAqB,GAAG,QAAQ,IAAI;AAC5E,QAAM,WAAW,eAAe,SAAS,UAAU;AAEnD,WAAS,iBAAiB;AACxB,UAAM,MAAM,UAAU;AACtB,UAAM,SAAS,cAA+B;AAC9C,UAAM,OAAO,QAAQ,mBAAmB,QAAQ;AAChD,QAAI,CAAC,KAAM,QAAO;AAClB,WAAO,cAAc,UAAU;AAAA,MAC7B;AAAA,MACA,OAAO,KAAK;AAAA,MACZ,UAAU,CAAC,MAAc,WAAqC,SAAS,MAAM,QAAQ,GAAG;AAAA,IAC1F,CAAC;AAAA,EACH;AAEA,SAAO,cAAc,gBAAgB,OAAO;AAC9C;;;AC1EA,SAAS,kBAAAA,uBAAsB;;;AC3B/B,SAAS,WAAW,gBAAgB;AAEpC,SAAS,4BAAAC,iCAAgC;AA6BlC,SAAS,cACd,QACA,UAAgC,CAAC,GAChB;AACjB,QAAM,EAAE,aAAa,GAAG,IAAI;AAC5B,QAAM,CAAC,MAAM,OAAO,IAAI,SAAsB,IAAI;AAClD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAE5C,YAAU,MAAM;AACd,QAAI,CAAC,QAAQ;AACX,cAAQ,IAAI;AACZ,iBAAW,KAAK;AAChB;AAAA,IACF;AACA,QAAI,YAAY;AAChB,UAAM,WAAWC,0BAA+B;AAChD,UAAM,QAAQ,iBAAiB,MAAM;AACrC,eAAW,IAAI;AAEf,UAAM,YAAY;AAChB,iBAAW,QAAQ,OAAO;AACxB,YAAI,UAAW;AACf,cAAM,EAAE,OAAO,IAAI,SAAS,KAAK,GAAG,IAAI;AAAA,CAAI;AAC5C,gBAAQ,EAAE,GAAG,OAAO,CAAC;AACrB,YAAI,aAAa,GAAG;AAClB,gBAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,UAAU,CAAC;AAAA,QACpD;AAAA,MACF;AACA,UAAI,UAAW;AACf,cAAQ,SAAS,UAAU,CAAC;AAC5B,iBAAW,KAAK;AAAA,IAClB,GAAG;AAEH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,QAAQ,UAAU,CAAC;AAEvB,SAAO,EAAE,MAAM,OAAO,MAAM,OAAO,QAAQ;AAC7C;;;ADZA,SAAS,WAAW,mBAAmB;","names":["createRenderer","createSpecStreamCompiler","createSpecStreamCompiler"]}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * ChatGPT helpers that wrap `@openai/mcp-extensions/server`.
3
+ */
4
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { OpenAIExtensions, type OpenAIUiEntrypoint, type OpenAIUiResourceMetadata, type OpenAIUiToolMetadata } from "@openai/mcp-extensions/server";
6
+ /** Return the official OpenAI extensions surface for this MCP server. */
7
+ export declare function enableOpenAiExtensions(server: McpServer): OpenAIExtensions;
8
+ /** Build a `_meta` fragment with typed `"openai/ui"` tool metadata. */
9
+ export declare function openaiUiToolMeta(meta: OpenAIUiToolMetadata): {
10
+ "openai/ui": OpenAIUiToolMetadata;
11
+ };
12
+ /** Build a `_meta` fragment with typed `"openai/ui"` resource metadata. */
13
+ export declare function openaiUiResourceMeta(meta: OpenAIUiResourceMetadata): {
14
+ "openai/ui": OpenAIUiResourceMetadata;
15
+ };
16
+ export { OpenAIExtensions, type OpenAIUiEntrypoint, type OpenAIUiResourceMetadata, type OpenAIUiToolMetadata, };
@@ -0,0 +1,20 @@
1
+ // src/openai/server.ts
2
+ import {
3
+ OpenAIExtensions
4
+ } from "@openai/mcp-extensions/server";
5
+ function enableOpenAiExtensions(server) {
6
+ return new OpenAIExtensions(server);
7
+ }
8
+ function openaiUiToolMeta(meta) {
9
+ return { "openai/ui": meta };
10
+ }
11
+ function openaiUiResourceMeta(meta) {
12
+ return { "openai/ui": meta };
13
+ }
14
+ export {
15
+ OpenAIExtensions,
16
+ enableOpenAiExtensions,
17
+ openaiUiResourceMeta,
18
+ openaiUiToolMeta
19
+ };
20
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/openai/server.ts"],"sourcesContent":["/**\n * ChatGPT helpers that wrap `@openai/mcp-extensions/server`.\n */\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport {\n OpenAIExtensions,\n type OpenAIUiEntrypoint,\n type OpenAIUiResourceMetadata,\n type OpenAIUiToolMetadata,\n} from \"@openai/mcp-extensions/server\";\n\n/** Return the official OpenAI extensions surface for this MCP server. */\nexport function enableOpenAiExtensions(server: McpServer): OpenAIExtensions {\n return new OpenAIExtensions(server);\n}\n\n/** Build a `_meta` fragment with typed `\"openai/ui\"` tool metadata. */\nexport function openaiUiToolMeta(\n meta: OpenAIUiToolMetadata,\n): { \"openai/ui\": OpenAIUiToolMetadata } {\n return { \"openai/ui\": meta };\n}\n\n/** Build a `_meta` fragment with typed `\"openai/ui\"` resource metadata. */\nexport function openaiUiResourceMeta(\n meta: OpenAIUiResourceMetadata,\n): { \"openai/ui\": OpenAIUiResourceMetadata } {\n return { \"openai/ui\": meta };\n}\n\nexport {\n OpenAIExtensions,\n type OpenAIUiEntrypoint,\n type OpenAIUiResourceMetadata,\n type OpenAIUiToolMetadata,\n};\n"],"mappings":";AAIA;AAAA,EACE;AAAA,OAIK;AAGA,SAAS,uBAAuB,QAAqC;AAC1E,SAAO,IAAI,iBAAiB,MAAM;AACpC;AAGO,SAAS,iBACd,MACuC;AACvC,SAAO,EAAE,aAAa,KAAK;AAC7B;AAGO,SAAS,qBACd,MAC2C;AAC3C,SAAO,EAAE,aAAa,KAAK;AAC7B;","names":[]}
@@ -1,5 +1,4 @@
1
1
  import {
2
- McpContext,
3
2
  createMcpView,
4
3
  useAutoResize,
5
4
  useDisplayMode,
@@ -14,7 +13,10 @@ import {
14
13
  useToolInputPartial,
15
14
  useToolResult,
16
15
  useUpdateModelContext
17
- } from "../chunk-SXF23BZD.js";
16
+ } from "../chunk-HIAIQVK5.js";
17
+ import {
18
+ McpContext
19
+ } from "../chunk-4SZHJ4MK.js";
18
20
  import "../chunk-5L3QHYWK.js";
19
21
  export {
20
22
  McpContext,
@@ -0,0 +1,6 @@
1
+ import { OpenAIExtensions } from "@openai/mcp-extensions/app";
2
+ /**
3
+ * Official ChatGPT extension surface for this View.
4
+ * One `OpenAIExtensions` per App handle. Call `message`, `files`, `deepLink`, and the rest on the returned instance.
5
+ */
6
+ export declare function useOpenAIExtensions(): OpenAIExtensions;
@@ -0,0 +1,25 @@
1
+ import {
2
+ McpContext
3
+ } from "../chunk-4SZHJ4MK.js";
4
+
5
+ // src/react/openai-extensions.ts
6
+ import { useContext, useRef } from "react";
7
+ import { OpenAIExtensions } from "@openai/mcp-extensions/app";
8
+ function useOpenAIExtensions() {
9
+ const ctx = useContext(McpContext);
10
+ if (!ctx) {
11
+ throw new Error(
12
+ "mountly-mcp/react: useOpenAIExtensions must be used inside a View wrapped with createMcpView()."
13
+ );
14
+ }
15
+ const app = ctx.app;
16
+ const ref = useRef(null);
17
+ if (ref.current?.app !== app) {
18
+ ref.current = { app, extensions: new OpenAIExtensions(app) };
19
+ }
20
+ return ref.current.extensions;
21
+ }
22
+ export {
23
+ useOpenAIExtensions
24
+ };
25
+ //# sourceMappingURL=openai-extensions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/openai-extensions.ts"],"sourcesContent":["/**\n * ChatGPT extension hook backed by `@openai/mcp-extensions/app`.\n * Separate entry (`mountly-mcp/react/openai`) keeps the peer out of ordinary Views.\n */\nimport { useContext, useRef } from \"react\";\nimport type { App } from \"@modelcontextprotocol/ext-apps\";\nimport { OpenAIExtensions } from \"@openai/mcp-extensions/app\";\nimport { McpContext } from \"./context.js\";\n\n/**\n * Official ChatGPT extension surface for this View.\n * One `OpenAIExtensions` per App handle. Call `message`, `files`, `deepLink`, and the rest on the returned instance.\n */\nexport function useOpenAIExtensions(): OpenAIExtensions {\n const ctx = useContext(McpContext);\n if (!ctx) {\n throw new Error(\n \"mountly-mcp/react: useOpenAIExtensions must be used inside a View wrapped with createMcpView().\",\n );\n }\n const app = ctx.app;\n const ref = useRef<{ app: App; extensions: OpenAIExtensions } | null>(null);\n if (ref.current?.app !== app) {\n ref.current = { app, extensions: new OpenAIExtensions(app) };\n }\n return ref.current.extensions;\n}\n"],"mappings":";;;;;AAIA,SAAS,YAAY,cAAc;AAEnC,SAAS,wBAAwB;AAO1B,SAAS,sBAAwC;AACtD,QAAM,MAAM,WAAW,UAAU;AACjC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,MAAM,IAAI;AAChB,QAAM,MAAM,OAA0D,IAAI;AAC1E,MAAI,IAAI,SAAS,QAAQ,KAAK;AAC5B,QAAI,UAAU,EAAE,KAAK,YAAY,IAAI,iBAAiB,GAAG,EAAE;AAAA,EAC7D;AACA,SAAO,IAAI,QAAQ;AACrB;","names":[]}
@@ -116,15 +116,20 @@ async function registerMcpApps(server, options) {
116
116
  server.server.registerCapabilities({
117
117
  extensions: { [EXTENSION_ID]: { mimeTypes: [RESOURCE_MIME_TYPE] } }
118
118
  });
119
- const resources = views.map(
120
- ({ artifact, html }) => registerAppResource(
119
+ const resources = views.map(({ artifact, html }) => {
120
+ const resourceMeta = {
121
+ ui: artifact.declaration._meta.ui
122
+ };
123
+ const openaiUi = artifact.declaration._meta["openai/ui"];
124
+ if (openaiUi !== void 0) resourceMeta["openai/ui"] = openaiUi;
125
+ return registerAppResource(
121
126
  server,
122
127
  artifact.name,
123
128
  artifact.uri,
124
129
  {
125
130
  mimeType: artifact.declaration.mimeType,
126
131
  description: artifact.declaration.description,
127
- _meta: { ui: artifact.declaration._meta.ui }
132
+ _meta: resourceMeta
128
133
  },
129
134
  async () => ({
130
135
  contents: [
@@ -132,12 +137,12 @@ async function registerMcpApps(server, options) {
132
137
  uri: artifact.uri,
133
138
  mimeType: artifact.declaration.mimeType,
134
139
  text: html,
135
- _meta: { ui: artifact.declaration._meta.ui }
140
+ _meta: resourceMeta
136
141
  }
137
142
  ]
138
143
  })
139
- )
140
- );
144
+ );
145
+ });
141
146
  const tools = (options.tools ?? []).map((registration) => {
142
147
  const { meta, withoutUi, visibility } = mergedToolMetadata(registration);
143
148
  const config = registration.config ?? {};