mountly-mcp 0.0.0 → 4.0.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 (132) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +283 -20
  3. package/dist/artifact/index.d.ts +49 -0
  4. package/dist/artifact/index.js +21 -0
  5. package/dist/artifact/index.js.map +1 -0
  6. package/dist/bridge/iframe-entry.d.ts +1 -0
  7. package/dist/bridge/iframe-entry.js +270 -220
  8. package/dist/bridge/index.d.ts +20 -15
  9. package/dist/bridge/index.js +2 -2
  10. package/dist/build/emit-html.d.ts +30 -0
  11. package/dist/build/emit-meta.d.ts +17 -0
  12. package/dist/build/index.d.ts +33 -48
  13. package/dist/build/index.js +9 -135
  14. package/dist/build/index.js.map +1 -1
  15. package/dist/chunk-33DMFKKD.js +111 -0
  16. package/dist/chunk-33DMFKKD.js.map +1 -0
  17. package/dist/chunk-4HC3OWAQ.js +185 -0
  18. package/dist/chunk-4HC3OWAQ.js.map +1 -0
  19. package/dist/chunk-5EHNJCMF.js +58 -0
  20. package/dist/chunk-5EHNJCMF.js.map +1 -0
  21. package/dist/chunk-6QHKKFO3.js +30 -0
  22. package/dist/chunk-6QHKKFO3.js.map +1 -0
  23. package/dist/chunk-AEVQKFXX.js +119 -0
  24. package/dist/chunk-AEVQKFXX.js.map +1 -0
  25. package/dist/chunk-DKK26TRS.js +199 -0
  26. package/dist/chunk-DKK26TRS.js.map +1 -0
  27. package/dist/chunk-JVQZGKBL.js +171 -0
  28. package/dist/chunk-JVQZGKBL.js.map +1 -0
  29. package/dist/chunk-KWVGW5Q7.js +582 -0
  30. package/dist/chunk-KWVGW5Q7.js.map +1 -0
  31. package/dist/{chunk-A64ZILAS.js → chunk-M4WFJR3C.js} +3 -2
  32. package/dist/{chunk-A64ZILAS.js.map → chunk-M4WFJR3C.js.map} +1 -1
  33. package/dist/chunk-MTJDRWGD.js +196 -0
  34. package/dist/chunk-MTJDRWGD.js.map +1 -0
  35. package/dist/chunk-SADNMGP6.js +16 -0
  36. package/dist/chunk-SADNMGP6.js.map +1 -0
  37. package/dist/chunk-UECZA34Q.js +52 -0
  38. package/dist/chunk-UECZA34Q.js.map +1 -0
  39. package/dist/chunk-YJA4H6IB.js +13 -0
  40. package/dist/chunk-YJA4H6IB.js.map +1 -0
  41. package/dist/cli.d.ts +2 -0
  42. package/dist/cli.js +297 -0
  43. package/dist/cli.js.map +1 -0
  44. package/dist/connect-server-7L4HGKKL.js +7 -0
  45. package/dist/connect-server-7L4HGKKL.js.map +1 -0
  46. package/dist/create.d.ts +10 -0
  47. package/dist/create.js +107 -0
  48. package/dist/create.js.map +1 -0
  49. package/dist/dev/connect-server.d.ts +13 -0
  50. package/dist/dev/host-entry.d.ts +13 -0
  51. package/dist/dev/host-entry.js +18330 -0
  52. package/dist/dev/index.d.ts +45 -0
  53. package/dist/dev/index.js +14 -0
  54. package/dist/dev/index.js.map +1 -0
  55. package/dist/dev/sandbox-entry.d.ts +7 -0
  56. package/dist/dev/sandbox-entry.js +80 -0
  57. package/dist/html.d.ts +4 -0
  58. package/dist/index.d.ts +7 -26
  59. package/dist/index.js +16 -3
  60. package/dist/json-render/app.d.ts +22 -0
  61. package/dist/json-render/app.js +10 -0
  62. package/dist/json-render/app.js.map +1 -0
  63. package/dist/json-render/generative-view.d.ts +57 -0
  64. package/dist/json-render/index.d.ts +10 -0
  65. package/dist/json-render/index.js +120 -0
  66. package/dist/json-render/index.js.map +1 -0
  67. package/dist/json-render/mcp.d.ts +49 -0
  68. package/dist/json-render/mcp.js +11 -0
  69. package/dist/json-render/mcp.js.map +1 -0
  70. package/dist/json-render/server.d.ts +72 -0
  71. package/dist/json-render/server.js +98 -0
  72. package/dist/json-render/server.js.map +1 -0
  73. package/dist/json-render/spec-stream.d.ts +26 -0
  74. package/dist/json-render/use-spec-stream.d.ts +26 -0
  75. package/dist/publish.d.ts +10 -0
  76. package/dist/react/context.d.ts +14 -0
  77. package/dist/react/create-view.d.ts +8 -0
  78. package/dist/react/hooks.d.ts +46 -0
  79. package/dist/react/index.d.ts +3 -0
  80. package/dist/react/index.js +36 -0
  81. package/dist/react/index.js.map +1 -0
  82. package/dist/sandbox/index.d.ts +16 -0
  83. package/dist/sandbox/index.js +8 -0
  84. package/dist/sandbox/index.js.map +1 -0
  85. package/dist/schema.d.ts +20 -0
  86. package/dist/server/index.d.ts +73 -0
  87. package/dist/server/index.js +235 -0
  88. package/dist/server/index.js.map +1 -0
  89. package/dist/svelte/index.d.ts +29 -0
  90. package/dist/svelte/index.js +13 -0
  91. package/dist/svelte/index.js.map +1 -0
  92. package/dist/testing/index.d.ts +27 -0
  93. package/dist/testing/index.js +312 -0
  94. package/dist/testing/index.js.map +1 -0
  95. package/dist/testing/render-artifact.d.ts +35 -0
  96. package/dist/types.d.ts +65 -0
  97. package/dist/vite/index.d.ts +23 -0
  98. package/dist/vite/index.js +211 -0
  99. package/dist/vite/index.js.map +1 -0
  100. package/dist/vue/index.d.ts +51 -0
  101. package/dist/vue/index.js +93 -0
  102. package/dist/vue/index.js.map +1 -0
  103. package/package.json +162 -20
  104. package/templates/react/.gitignore.tmpl +4 -0
  105. package/templates/react/README.md.tmpl +15 -0
  106. package/templates/react/mcp.fixtures.json.tmpl +4 -0
  107. package/templates/react/package.json.tmpl +27 -0
  108. package/templates/react/server.mjs.tmpl +70 -0
  109. package/templates/react/src/view.tsx.tmpl +160 -0
  110. package/templates/react/tsconfig.json.tmpl +15 -0
  111. package/templates/react/vite.config.ts.tmpl +22 -0
  112. package/templates/svelte/.gitignore.tmpl +4 -0
  113. package/templates/svelte/README.md.tmpl +14 -0
  114. package/templates/svelte/mcp.fixtures.json.tmpl +4 -0
  115. package/templates/svelte/package.json.tmpl +26 -0
  116. package/templates/svelte/server.mjs.tmpl +70 -0
  117. package/templates/svelte/src/Dashboard.svelte.tmpl +158 -0
  118. package/templates/svelte/src/view.ts.tmpl +14 -0
  119. package/templates/svelte/tsconfig.json.tmpl +14 -0
  120. package/templates/svelte/vite.config.ts.tmpl +22 -0
  121. package/templates/vue/.gitignore.tmpl +4 -0
  122. package/templates/vue/README.md.tmpl +14 -0
  123. package/templates/vue/mcp.fixtures.json.tmpl +4 -0
  124. package/templates/vue/package.json.tmpl +26 -0
  125. package/templates/vue/server.mjs.tmpl +70 -0
  126. package/templates/vue/src/Dashboard.vue.tmpl +159 -0
  127. package/templates/vue/src/view.ts.tmpl +14 -0
  128. package/templates/vue/tsconfig.json.tmpl +15 -0
  129. package/templates/vue/vite.config.ts.tmpl +22 -0
  130. package/dist/chunk-BUGGLMNA.js +0 -132
  131. package/dist/chunk-BUGGLMNA.js.map +0 -1
  132. package/dist/types-B9JAWm4W.d.ts +0 -62
@@ -0,0 +1,70 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { readMcpAppManifest } from "mountly-mcp/artifact";
3
+ import { registerMcpApps } from "mountly-mcp/server";
4
+ import { z } from "zod";
5
+
6
+ const URI = "{{uri}}";
7
+
8
+ function sampleCards() {
9
+ return [
10
+ { label: "Requests", value: Math.floor(1200 + Math.random() * 200) },
11
+ { label: "Latency", value: `${Math.floor(30 + Math.random() * 20)} ms` },
12
+ { label: "Uptime", value: "99.9%" },
13
+ ];
14
+ }
15
+
16
+ /** Default export for `mountly-mcp dev --server ./server.mjs`. */
17
+ export default async function createServer() {
18
+ const server = new McpServer(
19
+ { name: "{{slug}}", version: "0.0.1" },
20
+ { capabilities: { tools: {}, resources: {} } },
21
+ );
22
+
23
+ const { artifacts } = await readMcpAppManifest("dist/mountly-mcp.manifest.json");
24
+
25
+ await registerMcpApps(server, {
26
+ views: artifacts.map((artifact) => ({ artifact })),
27
+ tools: [
28
+ {
29
+ name: "showDashboard",
30
+ resourceUri: URI,
31
+ config: {
32
+ description: "Show an interactive metrics dashboard",
33
+ inputSchema: {
34
+ topic: z.string().describe("Dashboard topic"),
35
+ },
36
+ },
37
+ handler: async ({ topic }) => ({
38
+ structuredContent: {
39
+ topic,
40
+ cards: [
41
+ { label: "Requests", value: 1284 },
42
+ { label: "Latency", value: "42 ms" },
43
+ { label: "Uptime", value: "99.9%" },
44
+ ],
45
+ },
46
+ }),
47
+ },
48
+ {
49
+ name: "refreshDashboard",
50
+ resourceUri: URI,
51
+ visibility: ["app"],
52
+ config: {
53
+ description: "Refresh dashboard metrics (app-only)",
54
+ inputSchema: {
55
+ reason: z.string().optional(),
56
+ },
57
+ },
58
+ handler: async ({ reason }) => ({
59
+ structuredContent: {
60
+ refreshedAt: new Date().toISOString(),
61
+ cards: sampleCards(),
62
+ reason: reason ?? null,
63
+ },
64
+ }),
65
+ },
66
+ ],
67
+ });
68
+
69
+ return server;
70
+ }
@@ -0,0 +1,160 @@
1
+ import { useEffect, useState } from "react";
2
+ import {
3
+ createMcpView,
4
+ useHostStyles,
5
+ useMcpApp,
6
+ useToolResult,
7
+ } from "mountly-mcp/react";
8
+
9
+ interface DashboardCard {
10
+ label: string;
11
+ value: string | number;
12
+ }
13
+
14
+ interface DashboardData {
15
+ topic?: string;
16
+ refreshedAt?: string;
17
+ cards?: DashboardCard[];
18
+ }
19
+
20
+ interface DashboardResult {
21
+ structuredContent?: DashboardData;
22
+ }
23
+
24
+ const styles = `
25
+ :root {
26
+ color-scheme: light dark;
27
+ font-family: system-ui, -apple-system, sans-serif;
28
+ background: var(--color-background-primary, #ffffff);
29
+ color: var(--color-text-primary, #171717);
30
+ }
31
+ body { margin: 0; padding: 20px; }
32
+ .card {
33
+ border: 1px solid var(--color-border-primary, #e5e5e5);
34
+ border-radius: 12px;
35
+ padding: 20px;
36
+ background: var(--color-background-secondary, #fafafa);
37
+ }
38
+ .eyebrow {
39
+ margin: 0 0 8px;
40
+ color: var(--color-text-secondary, #737373);
41
+ font-size: 11px;
42
+ text-transform: uppercase;
43
+ letter-spacing: .1em;
44
+ }
45
+ h1 { margin: 0 0 4px; font-size: 20px; font-weight: 600; }
46
+ .subtitle {
47
+ color: var(--color-text-secondary, #737373);
48
+ font-size: 14px;
49
+ margin: 0 0 16px;
50
+ }
51
+ .grid {
52
+ display: grid;
53
+ grid-template-columns: repeat(3, 1fr);
54
+ gap: 10px;
55
+ margin-bottom: 16px;
56
+ }
57
+ .metric {
58
+ border: 1px solid var(--color-border-primary, #e5e5e5);
59
+ border-radius: 8px;
60
+ padding: 12px;
61
+ background: var(--color-background-primary, #ffffff);
62
+ }
63
+ .label {
64
+ color: var(--color-text-secondary, #737373);
65
+ font-size: 11px;
66
+ text-transform: uppercase;
67
+ letter-spacing: .05em;
68
+ }
69
+ .value { margin-top: 4px; font-size: 22px; font-weight: 600; }
70
+ .row { display: flex; align-items: center; gap: 10px; }
71
+ button {
72
+ border: 1px solid var(--color-border-primary, #e5e5e5);
73
+ border-radius: 8px;
74
+ padding: 8px 14px;
75
+ background: var(--color-background-primary, #ffffff);
76
+ color: inherit;
77
+ cursor: pointer;
78
+ font-size: 13px;
79
+ }
80
+ button:hover:not(:disabled) { background: var(--color-background-secondary, #f5f5f5); }
81
+ button:disabled { opacity: 0.6; cursor: wait; }
82
+ .status { font-size: 12px; color: var(--color-text-secondary, #737373); }
83
+ `;
84
+
85
+ function Dashboard() {
86
+ useHostStyles();
87
+ const app = useMcpApp();
88
+ const result = useToolResult<DashboardResult>();
89
+ const [data, setData] = useState<DashboardData | undefined>(result?.structuredContent);
90
+ const [busy, setBusy] = useState(false);
91
+ const [status, setStatus] = useState("Connected.");
92
+
93
+ useEffect(() => {
94
+ if (result?.structuredContent) setData(result.structuredContent);
95
+ }, [result]);
96
+
97
+ const cards = data?.cards ?? [];
98
+ const title = data?.topic ? `${data.topic} Dashboard` : "Dashboard";
99
+
100
+ async function refresh() {
101
+ setBusy(true);
102
+ setStatus("Refreshing…");
103
+ try {
104
+ const next = await app.callServerTool({
105
+ name: "refreshDashboard",
106
+ arguments: { reason: "User clicked refresh" },
107
+ });
108
+ const content = (next as DashboardResult).structuredContent;
109
+ if (content) {
110
+ setData((prev) => ({
111
+ ...prev,
112
+ ...content,
113
+ topic: content.topic ?? prev?.topic,
114
+ }));
115
+ }
116
+ setStatus(
117
+ content?.refreshedAt
118
+ ? `Refreshed ${new Date(content.refreshedAt).toLocaleTimeString()}`
119
+ : "Refreshed.",
120
+ );
121
+ } catch (error) {
122
+ setStatus(error instanceof Error ? error.message : "Refresh failed.");
123
+ } finally {
124
+ setBusy(false);
125
+ }
126
+ }
127
+
128
+ return (
129
+ <div className="card">
130
+ <p className="eyebrow">MCP App · Mountly</p>
131
+ <h1>{title}</h1>
132
+ <p className="subtitle">
133
+ Built with <code>mountly-mcp</code> — try Refresh to call an app-only tool.
134
+ </p>
135
+ <div className="grid">
136
+ {cards.length === 0
137
+ ? [1, 2, 3].map((i) => (
138
+ <div className="metric" key={i}>
139
+ <div className="label">—</div>
140
+ <div className="value">—</div>
141
+ </div>
142
+ ))
143
+ : cards.map((c) => (
144
+ <div className="metric" key={c.label}>
145
+ <div className="label">{c.label}</div>
146
+ <div className="value">{String(c.value)}</div>
147
+ </div>
148
+ ))}
149
+ </div>
150
+ <div className="row">
151
+ <button type="button" disabled={busy} onClick={() => void refresh()}>
152
+ Refresh data
153
+ </button>
154
+ <span className="status">{status}</span>
155
+ </div>
156
+ </div>
157
+ );
158
+ }
159
+
160
+ createMcpView(Dashboard, { shadow: true, styles });
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "jsx": "react-jsx",
8
+ "strict": true,
9
+ "skipLibCheck": true,
10
+ "noEmit": true,
11
+ "isolatedModules": true,
12
+ "resolveJsonModule": true
13
+ },
14
+ "include": ["src"]
15
+ }
@@ -0,0 +1,22 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+ import { mountlyMcpViews } from "mountly-mcp/vite";
4
+
5
+ export default defineConfig({
6
+ plugins: [
7
+ react(),
8
+ mountlyMcpViews({
9
+ apps: [
10
+ {
11
+ entry: "src/view.tsx",
12
+ uri: "{{uri}}",
13
+ name: "dashboard",
14
+ description: "Interactive metrics dashboard",
15
+ displayModes: ["inline", "fullscreen"],
16
+ prefersBorder: true,
17
+ awaitToolResult: true,
18
+ },
19
+ ],
20
+ }),
21
+ ],
22
+ });
@@ -0,0 +1,4 @@
1
+ node_modules
2
+ dist
3
+ .DS_Store
4
+ *.log
@@ -0,0 +1,14 @@
1
+ # {{name}}
2
+
3
+ MCP App scaffolded with `mountly-mcp create --framework svelte`.
4
+
5
+ ```bash
6
+ pnpm install
7
+ pnpm dev
8
+ pnpm verify
9
+ ```
10
+
11
+ - View: `src/view.ts` + `src/Dashboard.svelte`
12
+ - Server: `server.mjs` (`registerMcpApps`)
13
+
14
+ Do not clone the Mountly monorepo for greenfield apps — this project is the canonical path.
@@ -0,0 +1,4 @@
1
+ {
2
+ "server-usage": { "topic": "server usage" },
3
+ "billing": { "topic": "billing" }
4
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "vite build",
8
+ "dev": "mountly-mcp dev --server ./server.mjs",
9
+ "verify": "mountly-mcp verify",
10
+ "verify:strict": "mountly-mcp verify --strict",
11
+ "typecheck": "svelte-check --tsconfig ./tsconfig.json"
12
+ },
13
+ "dependencies": {
14
+ "@modelcontextprotocol/sdk": "^1.30.0",
15
+ "mountly-mcp": "^3.1.0",
16
+ "mountly-svelte": "^2.0.3",
17
+ "svelte": "^5.56.9",
18
+ "zod": "^4.4.3"
19
+ },
20
+ "devDependencies": {
21
+ "@sveltejs/vite-plugin-svelte": "^7.3.0",
22
+ "svelte-check": "^4.0.0",
23
+ "typescript": "^5.9.0",
24
+ "vite": "^8.2.1"
25
+ }
26
+ }
@@ -0,0 +1,70 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { readMcpAppManifest } from "mountly-mcp/artifact";
3
+ import { registerMcpApps } from "mountly-mcp/server";
4
+ import { z } from "zod";
5
+
6
+ const URI = "{{uri}}";
7
+
8
+ function sampleCards() {
9
+ return [
10
+ { label: "Requests", value: Math.floor(1200 + Math.random() * 200) },
11
+ { label: "Latency", value: `${Math.floor(30 + Math.random() * 20)} ms` },
12
+ { label: "Uptime", value: "99.9%" },
13
+ ];
14
+ }
15
+
16
+ /** Default export for `mountly-mcp dev --server ./server.mjs`. */
17
+ export default async function createServer() {
18
+ const server = new McpServer(
19
+ { name: "{{slug}}", version: "0.0.1" },
20
+ { capabilities: { tools: {}, resources: {} } },
21
+ );
22
+
23
+ const { artifacts } = await readMcpAppManifest("dist/mountly-mcp.manifest.json");
24
+
25
+ await registerMcpApps(server, {
26
+ views: artifacts.map((artifact) => ({ artifact })),
27
+ tools: [
28
+ {
29
+ name: "showDashboard",
30
+ resourceUri: URI,
31
+ config: {
32
+ description: "Show an interactive metrics dashboard",
33
+ inputSchema: {
34
+ topic: z.string().describe("Dashboard topic"),
35
+ },
36
+ },
37
+ handler: async ({ topic }) => ({
38
+ structuredContent: {
39
+ topic,
40
+ cards: [
41
+ { label: "Requests", value: 1284 },
42
+ { label: "Latency", value: "42 ms" },
43
+ { label: "Uptime", value: "99.9%" },
44
+ ],
45
+ },
46
+ }),
47
+ },
48
+ {
49
+ name: "refreshDashboard",
50
+ resourceUri: URI,
51
+ visibility: ["app"],
52
+ config: {
53
+ description: "Refresh dashboard metrics (app-only)",
54
+ inputSchema: {
55
+ reason: z.string().optional(),
56
+ },
57
+ },
58
+ handler: async ({ reason }) => ({
59
+ structuredContent: {
60
+ refreshedAt: new Date().toISOString(),
61
+ cards: sampleCards(),
62
+ reason: reason ?? null,
63
+ },
64
+ }),
65
+ },
66
+ ],
67
+ });
68
+
69
+ return server;
70
+ }
@@ -0,0 +1,158 @@
1
+ <script lang="ts">
2
+ import type { McpViewProps } from "mountly-mcp";
3
+
4
+ interface DashboardCard {
5
+ label: string;
6
+ value: string | number;
7
+ }
8
+
9
+ interface DashboardData {
10
+ topic?: string;
11
+ refreshedAt?: string;
12
+ cards?: DashboardCard[];
13
+ }
14
+
15
+ interface DashboardResult {
16
+ structuredContent?: DashboardData;
17
+ }
18
+
19
+ let { mcp, toolResult }: McpViewProps = $props();
20
+
21
+ let data = $state<DashboardData | undefined>();
22
+ let busy = $state(false);
23
+ let status = $state("Connected.");
24
+
25
+ $effect(() => {
26
+ const next = (toolResult as DashboardResult | undefined)?.structuredContent;
27
+ if (next) data = next;
28
+ });
29
+
30
+ const cards = $derived(data?.cards ?? []);
31
+ const title = $derived(data?.topic ? `${data.topic} Dashboard` : "Dashboard");
32
+
33
+ async function refresh() {
34
+ busy = true;
35
+ status = "Refreshing…";
36
+ try {
37
+ const next = (await mcp.callServerTool({
38
+ name: "refreshDashboard",
39
+ arguments: { reason: "User clicked refresh" },
40
+ })) as DashboardResult;
41
+ const content = next.structuredContent;
42
+ if (content) {
43
+ data = {
44
+ ...data,
45
+ ...content,
46
+ topic: content.topic ?? data?.topic,
47
+ };
48
+ }
49
+ status = content?.refreshedAt
50
+ ? `Refreshed ${new Date(content.refreshedAt).toLocaleTimeString()}`
51
+ : "Refreshed.";
52
+ } catch (error) {
53
+ status = error instanceof Error ? error.message : "Refresh failed.";
54
+ } finally {
55
+ busy = false;
56
+ }
57
+ }
58
+ </script>
59
+
60
+ <div class="card">
61
+ <p class="eyebrow">MCP App · Mountly</p>
62
+ <h1>{title}</h1>
63
+ <p class="subtitle">
64
+ Built with <code>mountly-mcp</code> — try Refresh to call an app-only tool.
65
+ </p>
66
+ <div class="grid">
67
+ {#if cards.length === 0}
68
+ {#each [1, 2, 3] as i (i)}
69
+ <div class="metric">
70
+ <div class="label">—</div>
71
+ <div class="value">—</div>
72
+ </div>
73
+ {/each}
74
+ {:else}
75
+ {#each cards as c (c.label)}
76
+ <div class="metric">
77
+ <div class="label">{c.label}</div>
78
+ <div class="value">{c.value}</div>
79
+ </div>
80
+ {/each}
81
+ {/if}
82
+ </div>
83
+ <div class="row">
84
+ <button type="button" disabled={busy} onclick={refresh}>Refresh data</button>
85
+ <span class="status">{status}</span>
86
+ </div>
87
+ </div>
88
+
89
+ <style>
90
+ .card {
91
+ border: 1px solid var(--color-border-primary, #e5e5e5);
92
+ border-radius: 12px;
93
+ padding: 20px;
94
+ background: var(--color-background-secondary, #fafafa);
95
+ }
96
+ .eyebrow {
97
+ margin: 0 0 8px;
98
+ color: var(--color-text-secondary, #737373);
99
+ font-size: 11px;
100
+ text-transform: uppercase;
101
+ letter-spacing: 0.1em;
102
+ }
103
+ h1 {
104
+ margin: 0 0 4px;
105
+ font-size: 20px;
106
+ font-weight: 600;
107
+ }
108
+ .subtitle {
109
+ color: var(--color-text-secondary, #737373);
110
+ font-size: 14px;
111
+ margin: 0 0 16px;
112
+ }
113
+ .grid {
114
+ display: grid;
115
+ grid-template-columns: repeat(3, 1fr);
116
+ gap: 10px;
117
+ margin-bottom: 16px;
118
+ }
119
+ .metric {
120
+ border: 1px solid var(--color-border-primary, #e5e5e5);
121
+ border-radius: 8px;
122
+ padding: 12px;
123
+ background: var(--color-background-primary, #ffffff);
124
+ }
125
+ .label {
126
+ color: var(--color-text-secondary, #737373);
127
+ font-size: 11px;
128
+ text-transform: uppercase;
129
+ letter-spacing: 0.05em;
130
+ }
131
+ .value {
132
+ margin-top: 4px;
133
+ font-size: 22px;
134
+ font-weight: 600;
135
+ }
136
+ .row {
137
+ display: flex;
138
+ align-items: center;
139
+ gap: 10px;
140
+ }
141
+ button {
142
+ border: 1px solid var(--color-border-primary, #e5e5e5);
143
+ border-radius: 8px;
144
+ padding: 8px 14px;
145
+ background: var(--color-background-primary, #ffffff);
146
+ color: inherit;
147
+ cursor: pointer;
148
+ font-size: 13px;
149
+ }
150
+ button:disabled {
151
+ opacity: 0.6;
152
+ cursor: wait;
153
+ }
154
+ .status {
155
+ font-size: 12px;
156
+ color: var(--color-text-secondary, #737373);
157
+ }
158
+ </style>
@@ -0,0 +1,14 @@
1
+ import { createMcpView } from "mountly-mcp/svelte";
2
+ import Dashboard from "./Dashboard.svelte";
3
+
4
+ const styles = `
5
+ :root {
6
+ color-scheme: light dark;
7
+ font-family: system-ui, -apple-system, sans-serif;
8
+ background: var(--color-background-primary, #ffffff);
9
+ color: var(--color-text-primary, #171717);
10
+ }
11
+ body { margin: 0; padding: 20px; }
12
+ `;
13
+
14
+ createMcpView(Dashboard, { shadow: true, styles });
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "noEmit": true,
10
+ "isolatedModules": true,
11
+ "resolveJsonModule": true
12
+ },
13
+ "include": ["src/**/*.ts", "src/**/*.svelte"]
14
+ }
@@ -0,0 +1,22 @@
1
+ import { defineConfig } from "vite";
2
+ import { svelte } from "@sveltejs/vite-plugin-svelte";
3
+ import { mountlyMcpViews } from "mountly-mcp/vite";
4
+
5
+ export default defineConfig({
6
+ plugins: [
7
+ svelte(),
8
+ mountlyMcpViews({
9
+ apps: [
10
+ {
11
+ entry: "src/view.ts",
12
+ uri: "{{uri}}",
13
+ name: "dashboard",
14
+ description: "Interactive metrics dashboard",
15
+ displayModes: ["inline", "fullscreen"],
16
+ prefersBorder: true,
17
+ awaitToolResult: true,
18
+ },
19
+ ],
20
+ }),
21
+ ],
22
+ });
@@ -0,0 +1,4 @@
1
+ node_modules
2
+ dist
3
+ .DS_Store
4
+ *.log
@@ -0,0 +1,14 @@
1
+ # {{name}}
2
+
3
+ MCP App scaffolded with `mountly-mcp create --framework vue`.
4
+
5
+ ```bash
6
+ pnpm install
7
+ pnpm dev
8
+ pnpm verify
9
+ ```
10
+
11
+ - View: `src/view.ts` + `src/Dashboard.vue`
12
+ - Server: `server.mjs` (`registerMcpApps`)
13
+
14
+ Do not clone the Mountly monorepo for greenfield apps — this project is the canonical path.
@@ -0,0 +1,4 @@
1
+ {
2
+ "server-usage": { "topic": "server usage" },
3
+ "billing": { "topic": "billing" }
4
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "vite build",
8
+ "dev": "mountly-mcp dev --server ./server.mjs",
9
+ "verify": "mountly-mcp verify",
10
+ "verify:strict": "mountly-mcp verify --strict",
11
+ "typecheck": "vue-tsc --noEmit"
12
+ },
13
+ "dependencies": {
14
+ "@modelcontextprotocol/sdk": "^1.30.0",
15
+ "mountly-mcp": "^3.1.0",
16
+ "mountly-vue": "^2.0.3",
17
+ "vue": "^3.5.41",
18
+ "zod": "^4.4.3"
19
+ },
20
+ "devDependencies": {
21
+ "@vitejs/plugin-vue": "^6.0.8",
22
+ "typescript": "^5.9.0",
23
+ "vite": "^8.2.1",
24
+ "vue-tsc": "^2.2.0"
25
+ }
26
+ }