mountly-mcp 4.0.1 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GLOSSARY.md +37 -0
- package/README.md +75 -197
- package/dist/add-DAFMA4F3.js +263 -0
- package/dist/add-DAFMA4F3.js.map +1 -0
- package/dist/add.d.ts +13 -0
- package/dist/bridge/iframe-entry.js +3 -1
- package/dist/bridge/index.js +1 -1
- package/dist/{chunk-JVQZGKBL.js → chunk-2HNXL5A4.js} +4 -2
- package/dist/chunk-2HNXL5A4.js.map +1 -0
- package/dist/{chunk-SADNMGP6.js → chunk-5L3QHYWK.js} +1 -1
- package/dist/{chunk-SADNMGP6.js.map → chunk-5L3QHYWK.js.map} +1 -1
- package/dist/chunk-EOWAWZH2.js +34 -0
- package/dist/chunk-EOWAWZH2.js.map +1 -0
- package/dist/{chunk-UECZA34Q.js → chunk-OWAXBNZA.js} +10 -5
- package/dist/chunk-OWAXBNZA.js.map +1 -0
- package/dist/{chunk-AEVQKFXX.js → chunk-SXF23BZD.js} +2 -2
- package/dist/cli.js +96 -24
- package/dist/cli.js.map +1 -1
- package/dist/connect-server-P2UZ62YC.js +7 -0
- package/dist/create.js +43 -15
- package/dist/create.js.map +1 -1
- package/dist/dev/index.js +1 -1
- package/dist/doctor-B54H6CRN.js +122 -0
- package/dist/doctor-B54H6CRN.js.map +1 -0
- package/dist/doctor.d.ts +16 -0
- package/dist/errors-4ERW7HZB.js +11 -0
- package/dist/errors-4ERW7HZB.js.map +1 -0
- package/dist/errors.d.ts +25 -0
- package/dist/index.js +2 -2
- package/dist/json-render/index.js +2 -2
- package/dist/react/index.js +2 -2
- package/dist/svelte/index.js +1 -1
- package/dist/vue/index.js +1 -1
- package/package.json +14 -11
- package/templates/react/README.md.tmpl +83 -6
- package/templates/react/package.json.tmpl +2 -0
- package/templates/react/serve-stdio.mjs.tmpl +9 -0
- package/templates/react/server.mjs.tmpl +6 -1
- package/templates/react/src/view.tsx.tmpl +1 -1
- package/templates/svelte/README.md.tmpl +27 -4
- package/templates/svelte/package.json.tmpl +2 -0
- package/templates/svelte/serve-stdio.mjs.tmpl +5 -0
- package/templates/svelte/server.mjs.tmpl +6 -1
- package/templates/svelte/src/Dashboard.svelte.tmpl +1 -1
- package/templates/vanilla/README.md.tmpl +27 -11
- package/templates/vanilla/package.json.tmpl +2 -0
- package/templates/vanilla/serve-stdio.mjs.tmpl +5 -0
- package/templates/vanilla/server.mjs.tmpl +6 -1
- package/templates/vanilla/src/view.ts.tmpl +1 -1
- package/templates/vue/README.md.tmpl +27 -4
- package/templates/vue/package.json.tmpl +2 -0
- package/templates/vue/serve-stdio.mjs.tmpl +5 -0
- package/templates/vue/server.mjs.tmpl +6 -1
- package/templates/vue/src/Dashboard.vue.tmpl +1 -1
- package/dist/chunk-JVQZGKBL.js.map +0 -1
- package/dist/chunk-UECZA34Q.js.map +0 -1
- package/dist/connect-server-7L4HGKKL.js +0 -7
- /package/dist/{chunk-AEVQKFXX.js.map → chunk-SXF23BZD.js.map} +0 -0
- /package/dist/{connect-server-7L4HGKKL.js.map → connect-server-P2UZ62YC.js.map} +0 -0
|
@@ -1,14 +1,37 @@
|
|
|
1
1
|
# {{name}}
|
|
2
2
|
|
|
3
|
-
MCP App scaffolded with `mountly-mcp create
|
|
3
|
+
MCP App scaffolded with `mountly-mcp create` (Svelte).
|
|
4
|
+
|
|
5
|
+
## Runbook
|
|
4
6
|
|
|
5
7
|
```bash
|
|
6
8
|
pnpm install
|
|
7
9
|
pnpm dev
|
|
8
10
|
pnpm verify
|
|
11
|
+
pnpm verify:render
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| File | Role |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `src/view.ts` | `createMcpView` entry |
|
|
17
|
+
| `src/Dashboard.svelte` | View UI: `toolResult` prop |
|
|
18
|
+
| `server.mjs` | `registerMcpApps` factory |
|
|
19
|
+
| `mcp.fixtures.json` | Tool args for `dev --server` |
|
|
20
|
+
|
|
21
|
+
## Open in a real host
|
|
22
|
+
|
|
23
|
+
`serve-stdio.mjs` is included. Point Claude Desktop or VS Code at that entry (stdio). ChatGPT: use your connector against the same process.
|
|
24
|
+
|
|
25
|
+
## CI
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pnpm exec mountly-mcp build
|
|
29
|
+
pnpm exec playwright install chromium
|
|
30
|
+
pnpm exec mountly-mcp verify --strict --render
|
|
9
31
|
```
|
|
10
32
|
|
|
11
|
-
|
|
12
|
-
- Server: `server.mjs` (`registerMcpApps`)
|
|
33
|
+
## Add another View
|
|
13
34
|
|
|
14
|
-
|
|
35
|
+
```bash
|
|
36
|
+
pnpm exec mountly-mcp add settings --framework svelte
|
|
37
|
+
```
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
"dev": "mountly-mcp dev --server ./server.mjs",
|
|
9
9
|
"verify": "mountly-mcp verify",
|
|
10
10
|
"verify:strict": "mountly-mcp verify --strict",
|
|
11
|
+
"verify:render": "mountly-mcp verify --strict --render",
|
|
12
|
+
"doctor": "mountly-mcp doctor",
|
|
11
13
|
"typecheck": "svelte-check --tsconfig ./tsconfig.json"
|
|
12
14
|
},
|
|
13
15
|
"dependencies": {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
1
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
3
|
import { readMcpAppManifest } from "mountly-mcp/artifact";
|
|
3
4
|
import { registerMcpApps } from "mountly-mcp/server";
|
|
@@ -5,6 +6,10 @@ import { z } from "zod";
|
|
|
5
6
|
|
|
6
7
|
const URI = "{{uri}}";
|
|
7
8
|
|
|
9
|
+
// Resolved from this file, not the cwd: an MCP host spawns the process with a
|
|
10
|
+
// working directory of its own choosing.
|
|
11
|
+
const MANIFEST = fileURLToPath(new URL("./dist/mountly-mcp.manifest.json", import.meta.url));
|
|
12
|
+
|
|
8
13
|
function sampleCards() {
|
|
9
14
|
return [
|
|
10
15
|
{ label: "Requests", value: Math.floor(1200 + Math.random() * 200) },
|
|
@@ -20,7 +25,7 @@ export default async function createServer() {
|
|
|
20
25
|
{ capabilities: { tools: {}, resources: {} } },
|
|
21
26
|
);
|
|
22
27
|
|
|
23
|
-
const { artifacts } = await readMcpAppManifest(
|
|
28
|
+
const { artifacts } = await readMcpAppManifest(MANIFEST);
|
|
24
29
|
|
|
25
30
|
await registerMcpApps(server, {
|
|
26
31
|
views: artifacts.map((artifact) => ({ artifact })),
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
<p class="eyebrow">MCP App · Mountly</p>
|
|
62
62
|
<h1>{title}</h1>
|
|
63
63
|
<p class="subtitle">
|
|
64
|
-
Built with <code>mountly-mcp</code
|
|
64
|
+
Built with <code>mountly-mcp</code>. Try Refresh to call an app-only tool.
|
|
65
65
|
</p>
|
|
66
66
|
<div class="grid">
|
|
67
67
|
{#if cards.length === 0}
|
|
@@ -1,20 +1,36 @@
|
|
|
1
1
|
# {{name}}
|
|
2
2
|
|
|
3
|
-
MCP App scaffolded with `mountly-mcp create
|
|
3
|
+
MCP App scaffolded with `mountly-mcp create` (vanilla: no framework).
|
|
4
|
+
|
|
5
|
+
## Runbook
|
|
4
6
|
|
|
5
7
|
```bash
|
|
6
8
|
pnpm install
|
|
7
|
-
pnpm dev
|
|
8
|
-
pnpm verify
|
|
9
|
+
pnpm dev
|
|
10
|
+
pnpm verify
|
|
11
|
+
pnpm verify:render
|
|
9
12
|
```
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
| File | Role |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `src/view.ts` | `publishMcpView`: `mount` / `update` / `unmount` |
|
|
17
|
+
| `server.mjs` | `registerMcpApps` factory |
|
|
18
|
+
| `mcp.fixtures.json` | Tool args for `dev --server` |
|
|
19
|
+
|
|
20
|
+
## Open in a real host
|
|
21
|
+
|
|
22
|
+
`serve-stdio.mjs` is included. Register that command in Claude Desktop / VS Code / ChatGPT.
|
|
14
23
|
|
|
15
|
-
|
|
16
|
-
`publishMcpView` takes the object directly. The bridge drives it exactly as it
|
|
17
|
-
drives a React, Vue or Svelte View. Add a framework later by swapping
|
|
18
|
-
`publishMcpView(...)` for that framework's `createMcpView(Component)`.
|
|
24
|
+
## CI
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
```bash
|
|
27
|
+
pnpm exec mountly-mcp build
|
|
28
|
+
pnpm exec playwright install chromium
|
|
29
|
+
pnpm exec mountly-mcp verify --strict --render
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Add another View
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pnpm exec mountly-mcp add settings --framework vanilla
|
|
36
|
+
```
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
"dev": "mountly-mcp dev --server ./server.mjs",
|
|
9
9
|
"verify": "mountly-mcp verify",
|
|
10
10
|
"verify:strict": "mountly-mcp verify --strict",
|
|
11
|
+
"verify:render": "mountly-mcp verify --strict --render",
|
|
12
|
+
"doctor": "mountly-mcp doctor",
|
|
11
13
|
"typecheck": "tsc --noEmit"
|
|
12
14
|
},
|
|
13
15
|
"dependencies": {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
1
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
3
|
import { readMcpAppManifest } from "mountly-mcp/artifact";
|
|
3
4
|
import { registerMcpApps } from "mountly-mcp/server";
|
|
@@ -5,6 +6,10 @@ import { z } from "zod";
|
|
|
5
6
|
|
|
6
7
|
const URI = "{{uri}}";
|
|
7
8
|
|
|
9
|
+
// Resolved from this file, not the cwd: an MCP host spawns the process with a
|
|
10
|
+
// working directory of its own choosing.
|
|
11
|
+
const MANIFEST = fileURLToPath(new URL("./dist/mountly-mcp.manifest.json", import.meta.url));
|
|
12
|
+
|
|
8
13
|
function sampleCards() {
|
|
9
14
|
return [
|
|
10
15
|
{ label: "Requests", value: Math.floor(1200 + Math.random() * 200) },
|
|
@@ -20,7 +25,7 @@ export default async function createServer() {
|
|
|
20
25
|
{ capabilities: { tools: {}, resources: {} } },
|
|
21
26
|
);
|
|
22
27
|
|
|
23
|
-
const { artifacts } = await readMcpAppManifest(
|
|
28
|
+
const { artifacts } = await readMcpAppManifest(MANIFEST);
|
|
24
29
|
|
|
25
30
|
await registerMcpApps(server, {
|
|
26
31
|
views: artifacts.map((artifact) => ({ artifact })),
|
|
@@ -2,7 +2,7 @@ import { publishMcpView, type McpViewProps } from "mountly-mcp";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* No framework, no adapter. A View is just `mount` / `update` / `unmount`, so
|
|
5
|
-
* `publishMcpView` takes this object directly
|
|
5
|
+
* `publishMcpView` takes this object directly. The bridge drives it exactly
|
|
6
6
|
* as it drives a React, Vue or Svelte View.
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -1,14 +1,37 @@
|
|
|
1
1
|
# {{name}}
|
|
2
2
|
|
|
3
|
-
MCP App scaffolded with `mountly-mcp create
|
|
3
|
+
MCP App scaffolded with `mountly-mcp create` (Vue).
|
|
4
|
+
|
|
5
|
+
## Runbook
|
|
4
6
|
|
|
5
7
|
```bash
|
|
6
8
|
pnpm install
|
|
7
9
|
pnpm dev
|
|
8
10
|
pnpm verify
|
|
11
|
+
pnpm verify:render
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| File | Role |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `src/view.ts` | `createMcpView` entry |
|
|
17
|
+
| `src/Dashboard.vue` | View UI: `useToolResult` |
|
|
18
|
+
| `server.mjs` | `registerMcpApps` factory |
|
|
19
|
+
| `mcp.fixtures.json` | Tool args for `dev --server` |
|
|
20
|
+
|
|
21
|
+
## Open in a real host
|
|
22
|
+
|
|
23
|
+
`serve-stdio.mjs` is included. Point Claude Desktop / VS Code at `node` + absolute path to that file (see the React scaffold README for JSON shapes). ChatGPT: use your connector against the same process.
|
|
24
|
+
|
|
25
|
+
## CI
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pnpm exec mountly-mcp build
|
|
29
|
+
pnpm exec playwright install chromium
|
|
30
|
+
pnpm exec mountly-mcp verify --strict --render
|
|
9
31
|
```
|
|
10
32
|
|
|
11
|
-
|
|
12
|
-
- Server: `server.mjs` (`registerMcpApps`)
|
|
33
|
+
## Add another View
|
|
13
34
|
|
|
14
|
-
|
|
35
|
+
```bash
|
|
36
|
+
pnpm exec mountly-mcp add settings --framework vue
|
|
37
|
+
```
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
"dev": "mountly-mcp dev --server ./server.mjs",
|
|
9
9
|
"verify": "mountly-mcp verify",
|
|
10
10
|
"verify:strict": "mountly-mcp verify --strict",
|
|
11
|
+
"verify:render": "mountly-mcp verify --strict --render",
|
|
12
|
+
"doctor": "mountly-mcp doctor",
|
|
11
13
|
"typecheck": "vue-tsc --noEmit"
|
|
12
14
|
},
|
|
13
15
|
"dependencies": {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
1
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
3
|
import { readMcpAppManifest } from "mountly-mcp/artifact";
|
|
3
4
|
import { registerMcpApps } from "mountly-mcp/server";
|
|
@@ -5,6 +6,10 @@ import { z } from "zod";
|
|
|
5
6
|
|
|
6
7
|
const URI = "{{uri}}";
|
|
7
8
|
|
|
9
|
+
// Resolved from this file, not the cwd: an MCP host spawns the process with a
|
|
10
|
+
// working directory of its own choosing.
|
|
11
|
+
const MANIFEST = fileURLToPath(new URL("./dist/mountly-mcp.manifest.json", import.meta.url));
|
|
12
|
+
|
|
8
13
|
function sampleCards() {
|
|
9
14
|
return [
|
|
10
15
|
{ label: "Requests", value: Math.floor(1200 + Math.random() * 200) },
|
|
@@ -20,7 +25,7 @@ export default async function createServer() {
|
|
|
20
25
|
{ capabilities: { tools: {}, resources: {} } },
|
|
21
26
|
);
|
|
22
27
|
|
|
23
|
-
const { artifacts } = await readMcpAppManifest(
|
|
28
|
+
const { artifacts } = await readMcpAppManifest(MANIFEST);
|
|
24
29
|
|
|
25
30
|
await registerMcpApps(server, {
|
|
26
31
|
views: artifacts.map((artifact) => ({ artifact })),
|
|
@@ -68,7 +68,7 @@ async function refresh() {
|
|
|
68
68
|
<p class="eyebrow">MCP App · Mountly</p>
|
|
69
69
|
<h1>{{ title }}</h1>
|
|
70
70
|
<p class="subtitle">
|
|
71
|
-
Built with <code>mountly-mcp</code
|
|
71
|
+
Built with <code>mountly-mcp</code>. Try Refresh to call an app-only tool.
|
|
72
72
|
</p>
|
|
73
73
|
<div class="grid">
|
|
74
74
|
<div v-if="cards.length === 0" v-for="i in 3" :key="i" class="metric">
|
|
@@ -1 +0,0 @@
|
|
|
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":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dev/connect-server.ts"],"sourcesContent":["import type { McpUiToolMeta } from \"@modelcontextprotocol/ext-apps\";\n\nexport interface ConnectedMcpServer {\n callTool: (name: string, args: unknown) => Promise<unknown>;\n /** The model-visible tool bound to `uri` — the one a host would call. */\n toolFor(uri: string): Promise<string | undefined>;\n close(): Promise<void>;\n}\n\n/**\n * Connect a server module in-process for a development session.\n *\n * The module default-exports either the server or a function returning it: a\n * `RunningMcpAppServer` from `createMcpAppServer`, or a raw `McpServer`.\n */\nexport async function connectMcpServer(path: string): Promise<ConnectedMcpServer> {\n const { pathToFileURL } = await import(\"node:url\");\n const { resolve } = await import(\"node:path\");\n const mod = (await import(pathToFileURL(resolve(path)).href)) as { default?: unknown };\n if (mod.default === undefined) {\n throw new Error(\n `mountly-mcp: ${path} has no default export. Export your server (or a function returning it) as default.`,\n );\n }\n const value = typeof mod.default === \"function\" ? await mod.default() : mod.default;\n\n let client: import(\"@modelcontextprotocol/sdk/client/index.js\").Client;\n if (\n value !== null &&\n typeof value === \"object\" &&\n \"connectInProcess\" in value &&\n typeof value.connectInProcess === \"function\"\n ) {\n const running = value as { connectInProcess(): Promise<{ client: typeof client }> };\n ({ client } = await running.connectInProcess());\n } else if (\n value !== null &&\n typeof value === \"object\" &&\n \"connect\" in value &&\n typeof value.connect === \"function\"\n ) {\n const [{ Client }, { InMemoryTransport }, { EXTENSION_ID, RESOURCE_MIME_TYPE }] =\n await Promise.all([\n import(\"@modelcontextprotocol/sdk/client/index.js\"),\n import(\"@modelcontextprotocol/sdk/inMemory.js\"),\n import(\"@modelcontextprotocol/ext-apps/server\"),\n ]);\n const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();\n await (value as { connect(transport: unknown): Promise<void> }).connect(serverTransport);\n client = new Client({ name: \"mountly-mcp-dev\", version: \"0.0.1\" }, {\n capabilities: { extensions: { [EXTENSION_ID]: { mimeTypes: [RESOURCE_MIME_TYPE] } } },\n } as never);\n await client.connect(clientTransport);\n } else {\n throw new TypeError(\n `mountly-mcp: ${path} must default-export an MCP server, RunningMcpAppServer, or a function returning one`,\n );\n }\n\n return {\n callTool: (name, args) =>\n client.callTool({ name, arguments: (args ?? {}) as Record<string, unknown> }),\n async toolFor(uri) {\n const { tools } = await client.listTools();\n const uiMeta = (tool: { _meta?: unknown }): McpUiToolMeta | undefined =>\n (tool._meta as { ui?: McpUiToolMeta } | undefined)?.ui;\n const bound = tools.filter((tool) => uiMeta(tool)?.resourceUri === uri);\n const visible = bound.find((tool) => {\n const visibility = uiMeta(tool)?.visibility;\n return visibility === undefined || visibility.includes(\"model\");\n });\n return (visible ?? bound[0])?.name;\n },\n close: () => client.close(),\n };\n}\n"],"mappings":";AAeA,eAAsB,iBAAiB,MAA2C;AAChF,QAAM,EAAE,cAAc,IAAI,MAAM,OAAO,KAAU;AACjD,QAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,MAAW;AAC5C,QAAM,MAAO,MAAM,OAAO,cAAc,QAAQ,IAAI,CAAC,EAAE;AACvD,MAAI,IAAI,YAAY,QAAW;AAC7B,UAAM,IAAI;AAAA,MACR,gBAAgB,IAAI;AAAA,IACtB;AAAA,EACF;AACA,QAAM,QAAQ,OAAO,IAAI,YAAY,aAAa,MAAM,IAAI,QAAQ,IAAI,IAAI;AAE5E,MAAI;AACJ,MACE,UAAU,QACV,OAAO,UAAU,YACjB,sBAAsB,SACtB,OAAO,MAAM,qBAAqB,YAClC;AACA,UAAM,UAAU;AAChB,KAAC,EAAE,OAAO,IAAI,MAAM,QAAQ,iBAAiB;AAAA,EAC/C,WACE,UAAU,QACV,OAAO,UAAU,YACjB,aAAa,SACb,OAAO,MAAM,YAAY,YACzB;AACA,UAAM,CAAC,EAAE,OAAO,GAAG,EAAE,kBAAkB,GAAG,EAAE,cAAc,mBAAmB,CAAC,IAC5E,MAAM,QAAQ,IAAI;AAAA,MAChB,OAAO,2CAA2C;AAAA,MAClD,OAAO,uCAAuC;AAAA,MAC9C,OAAO,uCAAuC;AAAA,IAChD,CAAC;AACH,UAAM,CAAC,iBAAiB,eAAe,IAAI,kBAAkB,iBAAiB;AAC9E,UAAO,MAAyD,QAAQ,eAAe;AACvF,aAAS,IAAI,OAAO,EAAE,MAAM,mBAAmB,SAAS,QAAQ,GAAG;AAAA,MACjE,cAAc,EAAE,YAAY,EAAE,CAAC,YAAY,GAAG,EAAE,WAAW,CAAC,kBAAkB,EAAE,EAAE,EAAE;AAAA,IACtF,CAAU;AACV,UAAM,OAAO,QAAQ,eAAe;AAAA,EACtC,OAAO;AACL,UAAM,IAAI;AAAA,MACR,gBAAgB,IAAI;AAAA,IACtB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,UAAU,CAAC,MAAM,SACf,OAAO,SAAS,EAAE,MAAM,WAAY,QAAQ,CAAC,EAA8B,CAAC;AAAA,IAC9E,MAAM,QAAQ,KAAK;AACjB,YAAM,EAAE,MAAM,IAAI,MAAM,OAAO,UAAU;AACzC,YAAM,SAAS,CAAC,SACb,KAAK,OAA8C;AACtD,YAAM,QAAQ,MAAM,OAAO,CAAC,SAAS,OAAO,IAAI,GAAG,gBAAgB,GAAG;AACtE,YAAM,UAAU,MAAM,KAAK,CAAC,SAAS;AACnC,cAAM,aAAa,OAAO,IAAI,GAAG;AACjC,eAAO,eAAe,UAAa,WAAW,SAAS,OAAO;AAAA,MAChE,CAAC;AACD,cAAQ,WAAW,MAAM,CAAC,IAAI;AAAA,IAChC;AAAA,IACA,OAAO,MAAM,OAAO,MAAM;AAAA,EAC5B;AACF;","names":[]}
|
|
File without changes
|
|
File without changes
|