mountly-mcp 3.1.0 → 4.0.1
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/README.md +59 -29
- package/dist/bridge/iframe-entry.js +24 -16
- package/dist/bridge/index.d.ts +5 -5
- package/dist/bridge/index.js +2 -2
- package/dist/build/index.d.ts +3 -3
- package/dist/build/index.js +2 -2
- package/dist/{chunk-NLQDVNZP.js → chunk-AEVQKFXX.js} +12 -8
- package/dist/chunk-AEVQKFXX.js.map +1 -0
- package/dist/{chunk-SNWNF2XI.js → chunk-JVQZGKBL.js} +13 -13
- package/dist/chunk-JVQZGKBL.js.map +1 -0
- package/dist/chunk-KWVGW5Q7.js +582 -0
- package/dist/chunk-KWVGW5Q7.js.map +1 -0
- package/dist/{chunk-DQLQOMDM.js → chunk-M4WFJR3C.js} +2 -2
- package/dist/{chunk-DQLQOMDM.js.map → chunk-M4WFJR3C.js.map} +1 -1
- package/dist/{chunk-4WAKOYMK.js → chunk-MTJDRWGD.js} +2 -2
- package/dist/chunk-MTJDRWGD.js.map +1 -0
- package/dist/chunk-SADNMGP6.js +16 -0
- package/dist/chunk-SADNMGP6.js.map +1 -0
- package/dist/cli.js +21 -10
- package/dist/cli.js.map +1 -1
- package/dist/create.d.ts +10 -0
- package/dist/create.js +113 -0
- package/dist/create.js.map +1 -0
- package/dist/dev/host-entry.js +2 -2
- package/dist/dev/index.d.ts +1 -1
- package/dist/dev/index.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +10 -2
- package/dist/json-render/{widget.d.ts → generative-view.d.ts} +9 -11
- package/dist/json-render/index.d.ts +1 -1
- package/dist/json-render/index.js +9 -8
- package/dist/json-render/index.js.map +1 -1
- package/dist/publish.d.ts +10 -0
- package/dist/react/context.d.ts +1 -1
- package/dist/react/create-view.d.ts +8 -0
- package/dist/react/hooks.d.ts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +6 -5
- package/dist/schema.d.ts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js.map +1 -1
- package/dist/svelte/index.d.ts +10 -16
- package/dist/svelte/index.js +7 -3
- package/dist/svelte/index.js.map +1 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +1 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/types.d.ts +14 -4
- package/dist/vite/index.d.ts +6 -6
- package/dist/vite/index.js +7 -7
- package/dist/vite/index.js.map +1 -1
- package/dist/vue/index.d.ts +12 -17
- package/dist/vue/index.js +10 -6
- package/dist/vue/index.js.map +1 -1
- package/package.json +11 -15
- package/templates/react/.gitignore.tmpl +4 -0
- package/templates/react/README.md.tmpl +15 -0
- package/templates/react/mcp.fixtures.json.tmpl +4 -0
- package/templates/react/package.json.tmpl +27 -0
- package/templates/react/server.mjs.tmpl +70 -0
- package/templates/react/src/view.tsx.tmpl +160 -0
- package/templates/react/tsconfig.json.tmpl +15 -0
- package/templates/react/vite.config.ts.tmpl +22 -0
- package/templates/svelte/.gitignore.tmpl +4 -0
- package/templates/svelte/README.md.tmpl +14 -0
- package/templates/svelte/mcp.fixtures.json.tmpl +4 -0
- package/templates/svelte/package.json.tmpl +26 -0
- package/templates/svelte/server.mjs.tmpl +70 -0
- package/templates/svelte/src/Dashboard.svelte.tmpl +158 -0
- package/templates/svelte/src/view.ts.tmpl +14 -0
- package/templates/svelte/tsconfig.json.tmpl +14 -0
- package/templates/svelte/vite.config.ts.tmpl +22 -0
- package/templates/vanilla/.gitignore.tmpl +4 -0
- package/templates/vanilla/README.md.tmpl +20 -0
- package/templates/vanilla/mcp.fixtures.json.tmpl +4 -0
- package/templates/vanilla/package.json.tmpl +22 -0
- package/templates/vanilla/server.mjs.tmpl +70 -0
- package/templates/vanilla/src/view.ts.tmpl +177 -0
- package/templates/vanilla/tsconfig.json.tmpl +14 -0
- package/templates/vanilla/vite.config.ts.tmpl +20 -0
- package/templates/vue/.gitignore.tmpl +4 -0
- package/templates/vue/README.md.tmpl +14 -0
- package/templates/vue/mcp.fixtures.json.tmpl +4 -0
- package/templates/vue/package.json.tmpl +26 -0
- package/templates/vue/server.mjs.tmpl +70 -0
- package/templates/vue/src/Dashboard.vue.tmpl +159 -0
- package/templates/vue/src/view.ts.tmpl +14 -0
- package/templates/vue/tsconfig.json.tmpl +15 -0
- package/templates/vue/vite.config.ts.tmpl +22 -0
- package/dist/chunk-4WAKOYMK.js.map +0 -1
- package/dist/chunk-KRAS266U.js +0 -260
- package/dist/chunk-KRAS266U.js.map +0 -1
- package/dist/chunk-NLQDVNZP.js.map +0 -1
- package/dist/chunk-SNWNF2XI.js.map +0 -1
- package/dist/react/create-widget.d.ts +0 -11
package/README.md
CHANGED
|
@@ -1,29 +1,51 @@
|
|
|
1
1
|
# mountly-mcp
|
|
2
2
|
|
|
3
|
-
**Build MCP Apps from
|
|
3
|
+
**Build MCP Apps from React, Vue, or Svelte components — new or existing.**
|
|
4
4
|
|
|
5
5
|
[MCP Apps](https://github.com/modelcontextprotocol/ext-apps) (SEP-1865) lets an
|
|
6
6
|
MCP server render interactive UI inside Claude, ChatGPT and other hosts. The
|
|
7
|
-
official SDK gives you the protocol
|
|
8
|
-
|
|
7
|
+
official SDK gives you the protocol. `mountly-mcp` is the view layer on top:
|
|
8
|
+
write a component, get a `ui://` resource, a local host, and server registration
|
|
9
|
+
without hand-writing the handshake HTML.
|
|
9
10
|
|
|
10
11
|
```ts
|
|
11
|
-
import {
|
|
12
|
-
import Dashboard from "./Dashboard
|
|
12
|
+
import { createMcpView } from "mountly-mcp/react";
|
|
13
|
+
import Dashboard from "./Dashboard";
|
|
13
14
|
|
|
14
|
-
(
|
|
15
|
-
createMcpWidget(Dashboard);
|
|
15
|
+
createMcpView(Dashboard);
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
application already owns. Your component is unchanged, and the same one can
|
|
21
|
-
keep running on your website.
|
|
18
|
+
That path is the default whether the dashboard is three metric cards for one
|
|
19
|
+
host or a multi-View production server. Prefer the scaffolder for greenfield:
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
```bash
|
|
22
|
+
npx mountly-mcp create my-app --framework react
|
|
23
|
+
cd my-app && pnpm install && pnpm dev
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`--framework` takes `react`, `vue`, `svelte`, or `vanilla`. Vanilla pulls in no
|
|
27
|
+
framework and no adapter: a View is just `mount` / `update` / `unmount`, so
|
|
28
|
+
`publishMcpView` takes the object directly and the bridge drives it the same
|
|
29
|
+
way it drives the others.
|
|
30
|
+
|
|
31
|
+
## Build with Agent Skills
|
|
32
|
+
|
|
33
|
+
| Skill | Try it |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| [`create-mcp-app`](../../plugins/mountly-mcp/skills/create-mcp-app/SKILL.md) | _"Create an MCP App"_ |
|
|
36
|
+
| [`add-app-to-server`](../../plugins/mountly-mcp/skills/add-app-to-server/SKILL.md) | _"Add UI to my MCP server"_ |
|
|
37
|
+
| [`convert-web-app`](../../plugins/mountly-mcp/skills/convert-web-app/SKILL.md) | _"Turn my component into an MCP App"_ |
|
|
38
|
+
| [`migrate-ext-apps`](../../plugins/mountly-mcp/skills/migrate-ext-apps/SKILL.md) | _"Migrate from ext-apps"_ |
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
/plugin marketplace add jagreehal/mountly
|
|
42
|
+
/plugin install mountly-mcp@mountly
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Docs: [Agent Skills](https://mountly.dev/mcp-apps/agent-skills/).
|
|
46
|
+
`mountly-mcp build` emits a versioned App manifest; `registerMcpApps` installs
|
|
47
|
+
the Views into the MCP server you already own. Existing website components stay
|
|
48
|
+
reusable as-is.
|
|
27
49
|
|
|
28
50
|
The wire protocol is delegated to the official `@modelcontextprotocol/sdk` and
|
|
29
51
|
`@modelcontextprotocol/ext-apps` packages in full, so server transports,
|
|
@@ -33,8 +55,11 @@ reimplementation of it. Both are runtime dependencies of `mountly-mcp`.
|
|
|
33
55
|
## Install
|
|
34
56
|
|
|
35
57
|
```bash
|
|
36
|
-
|
|
37
|
-
npm install mountly-
|
|
58
|
+
# React (default) — mountly + mountly-react included
|
|
59
|
+
npm install mountly-mcp react react-dom
|
|
60
|
+
|
|
61
|
+
# Vue / Svelte
|
|
62
|
+
npm install mountly-mcp mountly-vue vue # or mountly-svelte + svelte
|
|
38
63
|
```
|
|
39
64
|
|
|
40
65
|
Install `@modelcontextprotocol/sdk` directly only when your application imports
|
|
@@ -46,12 +71,12 @@ server entry point does not require a separate SDK install.
|
|
|
46
71
|
```ts
|
|
47
72
|
// vite.config.ts
|
|
48
73
|
import vue from "@vitejs/plugin-vue";
|
|
49
|
-
import {
|
|
74
|
+
import { mountlyMcpViews } from "mountly-mcp/vite";
|
|
50
75
|
|
|
51
76
|
export default defineConfig({
|
|
52
77
|
plugins: [
|
|
53
78
|
vue(),
|
|
54
|
-
|
|
79
|
+
mountlyMcpViews({
|
|
55
80
|
apps: [
|
|
56
81
|
{
|
|
57
82
|
entry: "src/dashboard.ts",
|
|
@@ -89,7 +114,7 @@ npx mountly-mcp dev
|
|
|
89
114
|
npx mountly-mcp dev --app weather_dashboard
|
|
90
115
|
```
|
|
91
116
|
|
|
92
|
-
Builds the
|
|
117
|
+
Builds the View, serves it in a local MCP Apps host, and rebuilds on save.
|
|
93
118
|
Config comes from the plugin already in your vite config.
|
|
94
119
|
|
|
95
120
|
It's a real host, not a preview: the published official `AppBridge` drives the
|
|
@@ -249,10 +274,10 @@ renders what it can. Drop the loose branch once core relaxes those fields.
|
|
|
249
274
|
| Entry | What's in it |
|
|
250
275
|
| ---------------------- | -------------------------------------------------------- |
|
|
251
276
|
| `mountly-mcp` | `runBridge`, spec types re-exported from ext-apps |
|
|
252
|
-
| `mountly-mcp/react` | `
|
|
253
|
-
| `mountly-mcp/vue` | `
|
|
254
|
-
| `mountly-mcp/svelte` | `
|
|
255
|
-
| `mountly-mcp/vite` | single- and multi-View `
|
|
277
|
+
| `mountly-mcp/react` | `createMcpView` + hooks |
|
|
278
|
+
| `mountly-mcp/vue` | `createMcpView` + composables |
|
|
279
|
+
| `mountly-mcp/svelte` | `createMcpView` (props-based) |
|
|
280
|
+
| `mountly-mcp/vite` | single- and multi-View `mountlyMcpViews()` build plugin |
|
|
256
281
|
| `mountly-mcp/artifact` | versioned manifest and App artifact APIs |
|
|
257
282
|
| `mountly-mcp/dev` | `startDevHost()` and in-process server connectivity |
|
|
258
283
|
| `mountly-mcp/build` | `buildMcpResource` for other bundlers |
|
|
@@ -262,13 +287,18 @@ renders what it can. Drop the loose branch once core relaxes those fields.
|
|
|
262
287
|
Types use the spec's own names (`McpUiHostContext`, `McpUiDisplayMode`, …) —
|
|
263
288
|
one vocabulary, no translation layer.
|
|
264
289
|
|
|
265
|
-
## Agent
|
|
290
|
+
## Agent Skills
|
|
291
|
+
|
|
292
|
+
Install the plugin skills and ask your coding agent to create an MCP App:
|
|
293
|
+
[Agent Skills docs](https://mountly.dev/mcp-apps/agent-skills/) ·
|
|
294
|
+
[`create-mcp-app`](../../plugins/mountly-mcp/skills/create-mcp-app/SKILL.md).
|
|
266
295
|
|
|
267
|
-
|
|
268
|
-
[`plugins/mountly-mcp/skills/create-mcp-widget`](../../../plugins/mountly-mcp/skills/create-mcp-widget/SKILL.md).
|
|
296
|
+
Greenfield without an agent: `npx mountly-mcp create my-app --framework react`.
|
|
269
297
|
|
|
270
298
|
## See also
|
|
271
299
|
|
|
272
|
-
- [`docs/examples/mcp-
|
|
273
|
-
|
|
300
|
+
- [`docs/examples/mcp-release-readiness`](../../docs/examples/mcp-release-readiness) —
|
|
301
|
+
production-shaped Vite View (closest to `create`)
|
|
302
|
+
- [`docs/examples/mcp-app-demo`](../../docs/examples/mcp-app-demo) — advanced
|
|
303
|
+
protocol harness (not the greenfield starter)
|
|
274
304
|
- `docs/how-to-test.md`
|
|
@@ -18556,7 +18556,7 @@ container holding the app. Specify either width or maxWidth, and either height o
|
|
|
18556
18556
|
INITIALIZE_FAILED: "mountly-mcp/initialize-failed",
|
|
18557
18557
|
INITIALIZE_TIMEOUT: "mountly-mcp/initialize-timeout",
|
|
18558
18558
|
TOOL_CALL_FAILED: "mountly-mcp/tool-call-failed",
|
|
18559
|
-
|
|
18559
|
+
VIEW_MOUNT_THREW: "mountly-mcp/view-mount-threw",
|
|
18560
18560
|
INVALID_ORIGIN: "mountly-mcp/invalid-origin"
|
|
18561
18561
|
};
|
|
18562
18562
|
|
|
@@ -18573,7 +18573,7 @@ container holding the app. Specify either width or maxWidth, and either height o
|
|
|
18573
18573
|
}
|
|
18574
18574
|
function runBridge(options) {
|
|
18575
18575
|
const {
|
|
18576
|
-
|
|
18576
|
+
view: view2,
|
|
18577
18577
|
container: container2,
|
|
18578
18578
|
awaitToolResult: awaitToolResult2 = true,
|
|
18579
18579
|
streamToolInput: streamToolInput2 = false,
|
|
@@ -18612,31 +18612,31 @@ container holding the app. Specify either width or maxWidth, and either height o
|
|
|
18612
18612
|
if (stopped) return;
|
|
18613
18613
|
try {
|
|
18614
18614
|
if (!mounted) {
|
|
18615
|
-
const r2 =
|
|
18615
|
+
const r2 = view2.mount(container2, props);
|
|
18616
18616
|
if (r2 instanceof Promise) await r2;
|
|
18617
18617
|
mounted = true;
|
|
18618
18618
|
container2.setAttribute("data-mountly-mcp-state", "mounted");
|
|
18619
|
-
} else if (
|
|
18620
|
-
const r2 =
|
|
18619
|
+
} else if (view2.update) {
|
|
18620
|
+
const r2 = view2.update(container2, props);
|
|
18621
18621
|
if (r2 instanceof Promise) await r2;
|
|
18622
18622
|
} else {
|
|
18623
|
-
await
|
|
18623
|
+
await view2.unmount(container2);
|
|
18624
18624
|
mounted = false;
|
|
18625
|
-
const r2 =
|
|
18625
|
+
const r2 = view2.mount(container2, props);
|
|
18626
18626
|
if (r2 instanceof Promise) await r2;
|
|
18627
18627
|
mounted = true;
|
|
18628
18628
|
}
|
|
18629
18629
|
} catch (error51) {
|
|
18630
|
-
notifyError(MCP_ERROR_CODES.
|
|
18630
|
+
notifyError(MCP_ERROR_CODES.VIEW_MOUNT_THREW, error51);
|
|
18631
18631
|
}
|
|
18632
18632
|
});
|
|
18633
18633
|
return pending;
|
|
18634
18634
|
}
|
|
18635
|
-
async function
|
|
18635
|
+
async function unmountView() {
|
|
18636
18636
|
await pending;
|
|
18637
18637
|
if (!mounted) return;
|
|
18638
18638
|
mounted = false;
|
|
18639
|
-
await
|
|
18639
|
+
await view2.unmount(container2);
|
|
18640
18640
|
}
|
|
18641
18641
|
const handleToolInput = (params) => {
|
|
18642
18642
|
toolInput = params;
|
|
@@ -18669,7 +18669,7 @@ container holding the app. Specify either width or maxWidth, and either height o
|
|
|
18669
18669
|
stopped = true;
|
|
18670
18670
|
detachTeardown();
|
|
18671
18671
|
detachAppHandlers();
|
|
18672
|
-
await
|
|
18672
|
+
await unmountView();
|
|
18673
18673
|
return await previousTeardown?.(params, extra) ?? {};
|
|
18674
18674
|
};
|
|
18675
18675
|
app.onteardown = bridgeTeardown;
|
|
@@ -18702,7 +18702,7 @@ container holding the app. Specify either width or maxWidth, and either height o
|
|
|
18702
18702
|
stopped = true;
|
|
18703
18703
|
detachTeardown();
|
|
18704
18704
|
detachAppHandlers();
|
|
18705
|
-
void
|
|
18705
|
+
void unmountView();
|
|
18706
18706
|
if (!options.app) void app.close().catch(() => void 0);
|
|
18707
18707
|
}
|
|
18708
18708
|
detachTeardown = options.onTeardown?.(stopBridge) ?? (typeof window !== "undefined" ? (() => {
|
|
@@ -18717,21 +18717,29 @@ container holding the app. Specify either width or maxWidth, and either height o
|
|
|
18717
18717
|
};
|
|
18718
18718
|
}
|
|
18719
18719
|
|
|
18720
|
+
// src/publish.ts
|
|
18721
|
+
var MCP_VIEW_GLOBAL_KEY = "__mountlyMcpView__";
|
|
18722
|
+
function getPublishedMcpView() {
|
|
18723
|
+
return globalThis[MCP_VIEW_GLOBAL_KEY];
|
|
18724
|
+
}
|
|
18725
|
+
|
|
18720
18726
|
// src/bridge/iframe-entry.ts
|
|
18721
|
-
var
|
|
18727
|
+
var view = getPublishedMcpView();
|
|
18722
18728
|
var awaitToolResult = globalThis.__mountlyMcpAwaitToolResult__ ?? true;
|
|
18723
18729
|
var streamToolInput = globalThis.__mountlyMcpStreamToolInput__ ?? false;
|
|
18724
18730
|
var availableDisplayModes = globalThis.__mountlyMcpAvailableDisplayModes__ ?? ["inline"];
|
|
18725
18731
|
var appInfo = globalThis.__mountlyMcpAppInfo__;
|
|
18726
|
-
if (!
|
|
18727
|
-
throw new Error(
|
|
18732
|
+
if (!view) {
|
|
18733
|
+
throw new Error(
|
|
18734
|
+
`[mountly-mcp] no View published \u2014 call createMcpView(...) (or publishMcpView) in your entry so ${MCP_VIEW_GLOBAL_KEY} is set`
|
|
18735
|
+
);
|
|
18728
18736
|
}
|
|
18729
18737
|
var container = document.getElementById("mountly-mcp-root");
|
|
18730
18738
|
if (!container) {
|
|
18731
18739
|
throw new Error("[mountly-mcp] #mountly-mcp-root not found in iframe DOM");
|
|
18732
18740
|
}
|
|
18733
18741
|
var bridge = runBridge({
|
|
18734
|
-
|
|
18742
|
+
view,
|
|
18735
18743
|
container,
|
|
18736
18744
|
awaitToolResult,
|
|
18737
18745
|
streamToolInput,
|
package/dist/bridge/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* View-side bridge that turns an MCP Apps `App` into a mountly
|
|
2
|
+
* View-side bridge that turns an MCP Apps `App` into a mountly view module
|
|
3
3
|
* lifecycle. The full spec handshake — `ui/initialize`,
|
|
4
4
|
* `ui/notifications/initialized`, `ui/notifications/tool-input(-partial)`,
|
|
5
5
|
* `ui/notifications/tool-result`, `ui/notifications/tool-cancelled`,
|
|
@@ -7,15 +7,15 @@
|
|
|
7
7
|
* `ui/notifications/size-changed` — is delegated to ext-apps's `App`.
|
|
8
8
|
*/
|
|
9
9
|
import { App, PostMessageTransport, type McpUiDisplayMode } from "@modelcontextprotocol/ext-apps";
|
|
10
|
-
import type {
|
|
10
|
+
import type { McpView } from "../types.js";
|
|
11
11
|
export interface RunBridgeOptions {
|
|
12
12
|
/** Pre-constructed App (used by jsdom tests); otherwise one is created here. */
|
|
13
13
|
app?: App;
|
|
14
|
-
|
|
14
|
+
view: McpView;
|
|
15
15
|
container: Element;
|
|
16
16
|
/**
|
|
17
17
|
* If true (default), mount is deferred until the first tool-result arrives;
|
|
18
|
-
* the
|
|
18
|
+
* the View renders against `{ toolInput, toolResult, hostContext, mcp }`
|
|
19
19
|
* props on each notification.
|
|
20
20
|
*
|
|
21
21
|
* If false, mount fires immediately after `ui/initialize` with `{ mcp }`
|
|
@@ -27,7 +27,7 @@ export interface RunBridgeOptions {
|
|
|
27
27
|
*
|
|
28
28
|
* Off by default — the spec lets views ignore partials, and they carry
|
|
29
29
|
* best-effort recovered JSON that MUST NOT drive critical operations. When
|
|
30
|
-
* on, partials arrive as `toolInputPartial` (mounting the
|
|
30
|
+
* on, partials arrive as `toolInputPartial` (mounting the View early if
|
|
31
31
|
* needed) and are cleared once the complete `tool-input` lands.
|
|
32
32
|
*/
|
|
33
33
|
streamToolInput?: boolean;
|
package/dist/bridge/index.js
CHANGED
package/dist/build/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { McpAppArtifact } from "../artifact/index.js";
|
|
|
5
5
|
* Absolute path to the iframe bridge runtime shipped with this package.
|
|
6
6
|
*
|
|
7
7
|
* `buildMcpResource` inlines this file's contents into the emitted HTML so
|
|
8
|
-
* the
|
|
8
|
+
* the View can speak the MCP Apps postMessage protocol. Use this when you
|
|
9
9
|
* need to point another build tool at the runtime, or when you want a
|
|
10
10
|
* stable, version-pinned alternative to passing `bridgeRuntimePath` yourself.
|
|
11
11
|
*
|
|
@@ -50,9 +50,9 @@ export interface BuildResult {
|
|
|
50
50
|
artifact: McpAppArtifact;
|
|
51
51
|
}
|
|
52
52
|
export interface BuildFromSourceOptions extends Omit<BuildSelfContainedOptions, "entry" | "cssEntry"> {
|
|
53
|
-
/** The bundled
|
|
53
|
+
/** The bundled View JS. */
|
|
54
54
|
js: string;
|
|
55
|
-
/** The
|
|
55
|
+
/** The View's CSS, if it has any. */
|
|
56
56
|
css?: string;
|
|
57
57
|
}
|
|
58
58
|
/**
|
package/dist/build/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
publishMcpView
|
|
3
|
+
} from "./chunk-SADNMGP6.js";
|
|
4
|
+
|
|
1
5
|
// src/react/context.ts
|
|
2
6
|
import { createContext } from "react";
|
|
3
7
|
var McpContext = createContext(null);
|
|
@@ -16,12 +20,12 @@ function useEnsuredContext() {
|
|
|
16
20
|
const ctx = useContext(McpContext);
|
|
17
21
|
if (!ctx) {
|
|
18
22
|
throw new Error(
|
|
19
|
-
"mountly-mcp/react:
|
|
23
|
+
"mountly-mcp/react: useMcpApp/useToolInput/etc must be used inside a View wrapped with createMcpView()."
|
|
20
24
|
);
|
|
21
25
|
}
|
|
22
26
|
return ctx;
|
|
23
27
|
}
|
|
24
|
-
function
|
|
28
|
+
function useMcpApp() {
|
|
25
29
|
return useEnsuredContext().app;
|
|
26
30
|
}
|
|
27
31
|
function useToolInput() {
|
|
@@ -73,10 +77,10 @@ function useAutoResize(...args) {
|
|
|
73
77
|
return useAutoResizeBase(...args);
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
// src/react/create-
|
|
80
|
+
// src/react/create-view.ts
|
|
77
81
|
import { createElement } from "react";
|
|
78
82
|
import { createWidget } from "mountly-react";
|
|
79
|
-
function
|
|
83
|
+
function createMcpView(Component, options) {
|
|
80
84
|
const Wrapped = (props) => {
|
|
81
85
|
const { mcp, toolInput, toolInputPartial, toolResult, hostContext, ...rest } = props;
|
|
82
86
|
const value = {
|
|
@@ -92,12 +96,12 @@ function createMcpWidget(Component, options) {
|
|
|
92
96
|
createElement(Component, rest)
|
|
93
97
|
);
|
|
94
98
|
};
|
|
95
|
-
return createWidget(Wrapped, options);
|
|
99
|
+
return publishMcpView(createWidget(Wrapped, options));
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
export {
|
|
99
103
|
McpContext,
|
|
100
|
-
|
|
104
|
+
useMcpApp,
|
|
101
105
|
useToolInput,
|
|
102
106
|
useToolInputPartial,
|
|
103
107
|
useToolResult,
|
|
@@ -110,6 +114,6 @@ export {
|
|
|
110
114
|
useDocumentTheme,
|
|
111
115
|
useUpdateModelContext,
|
|
112
116
|
useAutoResize,
|
|
113
|
-
|
|
117
|
+
createMcpView
|
|
114
118
|
};
|
|
115
|
-
//# sourceMappingURL=chunk-
|
|
119
|
+
//# sourceMappingURL=chunk-AEVQKFXX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/context.ts","../src/react/hooks.ts","../src/react/create-view.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","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,qBAAqB;AAsBvB,IAAM,aAAa,cAAsC,IAAI;AACpE,WAAW,cAAc;;;ACvBzB,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":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MCP_ERROR_CODES
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-M4WFJR3C.js";
|
|
4
4
|
|
|
5
5
|
// src/bridge/index.ts
|
|
6
6
|
import {
|
|
@@ -19,7 +19,7 @@ function renderErrorBoundary(container, code, message) {
|
|
|
19
19
|
}
|
|
20
20
|
function runBridge(options) {
|
|
21
21
|
const {
|
|
22
|
-
|
|
22
|
+
view,
|
|
23
23
|
container,
|
|
24
24
|
awaitToolResult = true,
|
|
25
25
|
streamToolInput = false,
|
|
@@ -58,31 +58,31 @@ function runBridge(options) {
|
|
|
58
58
|
if (stopped) return;
|
|
59
59
|
try {
|
|
60
60
|
if (!mounted) {
|
|
61
|
-
const r =
|
|
61
|
+
const r = view.mount(container, props);
|
|
62
62
|
if (r instanceof Promise) await r;
|
|
63
63
|
mounted = true;
|
|
64
64
|
container.setAttribute("data-mountly-mcp-state", "mounted");
|
|
65
|
-
} else if (
|
|
66
|
-
const r =
|
|
65
|
+
} else if (view.update) {
|
|
66
|
+
const r = view.update(container, props);
|
|
67
67
|
if (r instanceof Promise) await r;
|
|
68
68
|
} else {
|
|
69
|
-
await
|
|
69
|
+
await view.unmount(container);
|
|
70
70
|
mounted = false;
|
|
71
|
-
const r =
|
|
71
|
+
const r = view.mount(container, props);
|
|
72
72
|
if (r instanceof Promise) await r;
|
|
73
73
|
mounted = true;
|
|
74
74
|
}
|
|
75
75
|
} catch (error) {
|
|
76
|
-
notifyError(MCP_ERROR_CODES.
|
|
76
|
+
notifyError(MCP_ERROR_CODES.VIEW_MOUNT_THREW, error);
|
|
77
77
|
}
|
|
78
78
|
});
|
|
79
79
|
return pending;
|
|
80
80
|
}
|
|
81
|
-
async function
|
|
81
|
+
async function unmountView() {
|
|
82
82
|
await pending;
|
|
83
83
|
if (!mounted) return;
|
|
84
84
|
mounted = false;
|
|
85
|
-
await
|
|
85
|
+
await view.unmount(container);
|
|
86
86
|
}
|
|
87
87
|
const handleToolInput = (params) => {
|
|
88
88
|
toolInput = params;
|
|
@@ -115,7 +115,7 @@ function runBridge(options) {
|
|
|
115
115
|
stopped = true;
|
|
116
116
|
detachTeardown();
|
|
117
117
|
detachAppHandlers();
|
|
118
|
-
await
|
|
118
|
+
await unmountView();
|
|
119
119
|
return await previousTeardown?.(params, extra) ?? {};
|
|
120
120
|
};
|
|
121
121
|
app.onteardown = bridgeTeardown;
|
|
@@ -148,7 +148,7 @@ function runBridge(options) {
|
|
|
148
148
|
stopped = true;
|
|
149
149
|
detachTeardown();
|
|
150
150
|
detachAppHandlers();
|
|
151
|
-
void
|
|
151
|
+
void unmountView();
|
|
152
152
|
if (!options.app) void app.close().catch(() => void 0);
|
|
153
153
|
}
|
|
154
154
|
detachTeardown = options.onTeardown?.(stopBridge) ?? (typeof window !== "undefined" ? (() => {
|
|
@@ -168,4 +168,4 @@ export {
|
|
|
168
168
|
PostMessageTransport,
|
|
169
169
|
runBridge
|
|
170
170
|
};
|
|
171
|
-
//# sourceMappingURL=chunk-
|
|
171
|
+
//# sourceMappingURL=chunk-JVQZGKBL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bridge/index.ts"],"sourcesContent":["/**\n * View-side bridge that turns an MCP Apps `App` into a mountly view module\n * lifecycle. The full spec handshake — `ui/initialize`,\n * `ui/notifications/initialized`, `ui/notifications/tool-input(-partial)`,\n * `ui/notifications/tool-result`, `ui/notifications/tool-cancelled`,\n * `ui/notifications/host-context-changed`, `ui/resource-teardown`,\n * `ui/notifications/size-changed` — is delegated to ext-apps's `App`.\n */\nimport {\n App,\n PostMessageTransport,\n type McpUiAppCapabilities,\n type McpUiDisplayMode,\n type McpUiToolInputNotification,\n type McpUiToolInputPartialNotification,\n type McpUiToolResultNotification,\n} from \"@modelcontextprotocol/ext-apps\";\n\nimport { MCP_ERROR_CODES } from \"../schema.js\";\nimport type { McpView, McpViewProps } from \"../types.js\";\n\nexport interface RunBridgeOptions {\n /** Pre-constructed App (used by jsdom tests); otherwise one is created here. */\n app?: App;\n view: McpView;\n container: Element;\n /**\n * If true (default), mount is deferred until the first tool-result arrives;\n * the View renders against `{ toolInput, toolResult, hostContext, mcp }`\n * props on each notification.\n *\n * If false, mount fires immediately after `ui/initialize` with `{ mcp }`\n * only — for \"stateless\" views that drive themselves via `mcp.callServerTool`.\n */\n awaitToolResult?: boolean;\n /**\n * Render progressive states from `ui/notifications/tool-input-partial`.\n *\n * Off by default — the spec lets views ignore partials, and they carry\n * best-effort recovered JSON that MUST NOT drive critical operations. When\n * on, partials arrive as `toolInputPartial` (mounting the View early if\n * needed) and are cleared once the complete `tool-input` lands.\n */\n streamToolInput?: boolean;\n /** Display modes this view supports. Declared to the host during `ui/initialize`. */\n availableDisplayModes?: ReadonlyArray<McpUiDisplayMode>;\n /** App identity sent during `ui/initialize`. */\n appInfo?: { name: string; version: string };\n /** Override for tests; defaults to `window.addEventListener('beforeunload', ...)`. */\n onTeardown?: (handler: () => void) => () => void;\n}\n\nexport interface RunningBridge {\n /** Resolves once `ui/initialize` handshake completes and the first mount (or deferred mount) is wired. */\n ready: Promise<void>;\n /** The underlying App; exposed for hosts that want to drive the view directly. */\n app: App;\n /** Synchronously triggers teardown. */\n stop(): void;\n}\n\n/**\n * `data-mountly-mcp-state` is the View's machine-readable outcome, read by\n * `verify --render`. It only ever moves forward — `mounted` once the View\n * has mounted, `error` if the boundary replaced it — so a state written here\n * can never be clobbered by a later lifecycle step.\n */\nfunction renderErrorBoundary(container: Element, code: string, message?: string): void {\n container.setAttribute(\"data-mountly-mcp-state\", \"error\");\n const doc = container.ownerDocument;\n if (!doc) return;\n const errorDiv = doc.createElement(\"div\");\n errorDiv.setAttribute(\"data-mountly-mcp-error\", code);\n if (message !== undefined) errorDiv.textContent = message;\n container.innerHTML = \"\";\n container.appendChild(errorDiv);\n}\n\nexport function runBridge(options: RunBridgeOptions): RunningBridge {\n const {\n view,\n container,\n awaitToolResult = true,\n streamToolInput = false,\n availableDisplayModes = [\"inline\"],\n appInfo = { name: \"mountly-mcp\", version: \"0.0.0\" },\n } = options;\n\n const appCapabilities: McpUiAppCapabilities = {\n availableDisplayModes: [...availableDisplayModes],\n };\n\n const app = options.app ?? new App(appInfo, appCapabilities);\n\n let toolInput: McpUiToolInputNotification[\"params\"] | undefined;\n let toolInputPartial: McpUiToolInputPartialNotification[\"params\"] | undefined;\n let toolResult: McpUiToolResultNotification[\"params\"] | undefined;\n let mounted = false;\n let stopped = false;\n let pending: Promise<void> = Promise.resolve();\n\n function currentProps(): McpViewProps {\n return {\n mcp: app,\n toolInput,\n toolInputPartial,\n toolResult,\n hostContext: app.getHostContext(),\n };\n }\n\n function notifyError(code: string, error: unknown): void {\n const message = error instanceof Error ? error.message : String(error);\n try {\n void app.sendLog({ level: \"error\", data: { code, message } }).catch(() => undefined);\n } catch {\n // ignore — log channel may not be ready\n }\n renderErrorBoundary(container, code, message);\n }\n\n function renderWith(props: McpViewProps): Promise<void> {\n pending = pending.then(async () => {\n if (stopped) return;\n try {\n if (!mounted) {\n const r = view.mount(container, props);\n if (r instanceof Promise) await r;\n mounted = true;\n container.setAttribute(\"data-mountly-mcp-state\", \"mounted\");\n } else if (view.update) {\n const r = view.update(container, props);\n if (r instanceof Promise) await r;\n } else {\n await view.unmount(container);\n mounted = false;\n const r = view.mount(container, props);\n if (r instanceof Promise) await r;\n mounted = true;\n }\n } catch (error) {\n notifyError(MCP_ERROR_CODES.VIEW_MOUNT_THREW, error);\n }\n });\n return pending;\n }\n\n async function unmountView(): Promise<void> {\n await pending;\n if (!mounted) return;\n mounted = false;\n await view.unmount(container);\n }\n\n // Event handlers MUST be registered before connect() — ext-apps will refuse\n // to attach handlers retroactively after the initialize handshake completes.\n const handleToolInput = (params: McpUiToolInputNotification[\"params\"]) => {\n toolInput = params;\n toolInputPartial = undefined;\n if (!awaitToolResult) return;\n if (mounted || toolResult !== undefined) void renderWith(currentProps());\n };\n\n const handleToolInputPartial = (params: McpUiToolInputPartialNotification[\"params\"]) => {\n // Views MAY ignore partial input per spec; opt in with `streamToolInput`.\n // Consumers wanting finer control can subscribe via app.addEventListener.\n if (!streamToolInput) return;\n toolInputPartial = params;\n void renderWith(currentProps());\n };\n\n const handleToolResult = (params: McpUiToolResultNotification[\"params\"]) => {\n toolResult = params;\n void renderWith(currentProps());\n };\n\n const handleToolCancelled = () => {\n void renderWith(currentProps());\n };\n\n const handleHostContextChanged = () => {\n if (mounted) void renderWith(currentProps());\n };\n\n app.addEventListener(\"toolinput\", handleToolInput);\n app.addEventListener(\"toolinputpartial\", handleToolInputPartial);\n app.addEventListener(\"toolresult\", handleToolResult);\n app.addEventListener(\"toolcancelled\", handleToolCancelled);\n app.addEventListener(\"hostcontextchanged\", handleHostContextChanged);\n\n const previousTeardown = app.onteardown;\n const bridgeTeardown: NonNullable<typeof app.onteardown> = async (params, extra) => {\n stopped = true;\n detachTeardown();\n detachAppHandlers();\n await unmountView();\n return (await previousTeardown?.(params, extra)) ?? {};\n };\n app.onteardown = bridgeTeardown;\n\n function detachAppHandlers(): void {\n app.removeEventListener(\"toolinput\", handleToolInput);\n app.removeEventListener(\"toolinputpartial\", handleToolInputPartial);\n app.removeEventListener(\"toolresult\", handleToolResult);\n app.removeEventListener(\"toolcancelled\", handleToolCancelled);\n app.removeEventListener(\"hostcontextchanged\", handleHostContextChanged);\n if (app.onteardown === bridgeTeardown) app.onteardown = previousTeardown;\n }\n\n const ready = (async () => {\n try {\n if (!options.app) {\n // App.connect() defaults to PostMessageTransport(window.parent, window.parent)\n // and owns automatic resize setup.\n await app.connect();\n }\n\n if (!awaitToolResult) {\n await renderWith(currentProps());\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const code = /timeout/i.test(message)\n ? MCP_ERROR_CODES.INITIALIZE_TIMEOUT\n : MCP_ERROR_CODES.INITIALIZE_FAILED;\n notifyError(code, error);\n throw error;\n }\n })();\n\n let detachTeardown: () => void = () => undefined;\n\n function stopBridge(): void {\n if (stopped) return;\n stopped = true;\n detachTeardown();\n detachAppHandlers();\n void unmountView();\n if (!options.app) void app.close().catch(() => undefined);\n }\n\n detachTeardown =\n options.onTeardown?.(stopBridge) ??\n (typeof window !== \"undefined\"\n ? (() => {\n const h = () => stopBridge();\n window.addEventListener(\"beforeunload\", h);\n return () => window.removeEventListener(\"beforeunload\", h);\n })()\n : () => undefined);\n\n return {\n ready,\n app,\n stop: stopBridge,\n };\n}\n\nexport { App, PostMessageTransport };\nexport type {\n McpUiAppCapabilities,\n McpUiHostCapabilities,\n McpUiHostContext,\n McpUiToolInputNotification,\n McpUiToolInputPartialNotification,\n McpUiToolResultNotification,\n McpUiToolCancelledNotification,\n McpUiHostContextChangedNotification,\n} from \"@modelcontextprotocol/ext-apps\";\n"],"mappings":"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","names":[]}
|