open-mcp-app 0.1.0 → 0.1.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.
@@ -0,0 +1,69 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ /**
4
+ * HMR Client Script
5
+ *
6
+ * This script is injected into MCP App HTML during development mode.
7
+ * It connects to Vite's HMR WebSocket and notifies the parent frame
8
+ * when a full reload is needed.
9
+ *
10
+ * The parent frame (Creature host) will then:
11
+ * 1. Save current widget state
12
+ * 2. Re-fetch fresh HTML from the MCP server
13
+ * 3. Reload the iframe with new content
14
+ * 4. Restore widget state
15
+ */
16
+ /**
17
+ * Generate the HMR client script as a string.
18
+ * The port is injected at generation time.
19
+ */
20
+ declare function generateHmrClientScript(port: number): string;
21
+ /**
22
+ * Generate a script tag with the HMR client code.
23
+ */
24
+ declare function generateHmrClientScriptTag(port: number): string;
25
+
26
+ interface CreaturePluginOptions {
27
+ uiDir?: string;
28
+ outDir?: string;
29
+ hmrPort?: number;
30
+ /**
31
+ * Generate a JS module exporting bundled HTML for serverless deployments.
32
+ * When enabled, creates `dist/ui/bundle.js` with named exports for each page.
33
+ *
34
+ * @example
35
+ * // In server code:
36
+ * import { main } from "./dist/ui/bundle.js";
37
+ * app.resource({ html: main });
38
+ *
39
+ * @default false
40
+ */
41
+ generateBundle?: boolean;
42
+ }
43
+ interface HmrConfig {
44
+ port: number;
45
+ }
46
+ /**
47
+ * Offset added to MCP_PORT to derive the HMR port.
48
+ * When MCP_PORT is set (by Creature), both Vite and the SDK server
49
+ * can independently calculate the same HMR port without coordination.
50
+ */
51
+ declare const HMR_PORT_OFFSET = 1000;
52
+ /**
53
+ * Vite plugin for Creature MCP Apps.
54
+ *
55
+ * Just write page.tsx files - no HTML or entry files needed.
56
+ *
57
+ * ```
58
+ * src/ui/
59
+ * ├── page.tsx → dist/ui/main.html
60
+ * ├── inline/page.tsx → dist/ui/inline.html
61
+ * └── _components/ → ignored
62
+ * ```
63
+ *
64
+ * When using vite-plugin-singlefile, multiple pages are built automatically
65
+ * via sequential builds (singlefile requires single entry per build).
66
+ */
67
+ declare function creature(options?: CreaturePluginOptions): Plugin;
68
+
69
+ export { type CreaturePluginOptions, HMR_PORT_OFFSET, type HmrConfig, creature, creature as default, generateHmrClientScript, generateHmrClientScriptTag };
@@ -0,0 +1,362 @@
1
+ // src/vite/index.ts
2
+ import { resolve, join, relative } from "path";
3
+ import { readdirSync, statSync, existsSync, writeFileSync, mkdirSync, rmSync, readFileSync } from "fs";
4
+ import { createServer as createNetServer } from "net";
5
+ import { createServer as createHttpServer } from "http";
6
+ import { createHash } from "crypto";
7
+ import { spawnSync } from "child_process";
8
+
9
+ // src/vite/hmr-client.ts
10
+ function generateHmrClientScript(port) {
11
+ return `
12
+ (function() {
13
+ if (window.parent === window) {
14
+ console.log('[Creature HMR] Not in iframe, skipping HMR client');
15
+ return;
16
+ }
17
+ if (window.__CREATURE_HMR_CONNECTED__) {
18
+ console.log('[Creature HMR] Already connected, skipping');
19
+ return;
20
+ }
21
+ window.__CREATURE_HMR_CONNECTED__ = true;
22
+
23
+ var HMR_PORT = ${port};
24
+ var reconnectAttempts = 0;
25
+ var maxReconnectAttempts = 10;
26
+ var reconnectDelay = 1000;
27
+
28
+ console.log('[Creature HMR] Initializing HMR client, will connect to port ' + HMR_PORT);
29
+
30
+ function connect() {
31
+ if (reconnectAttempts >= maxReconnectAttempts) {
32
+ console.log('[Creature HMR] Max reconnection attempts reached, giving up');
33
+ return;
34
+ }
35
+
36
+ console.log('[Creature HMR] Attempting to connect to ws://localhost:' + HMR_PORT + ' (attempt ' + (reconnectAttempts + 1) + ')');
37
+ var ws = new WebSocket('ws://localhost:' + HMR_PORT);
38
+
39
+ ws.onopen = function() {
40
+ console.log('[Creature HMR] Connected to HMR server on port ' + HMR_PORT);
41
+ reconnectAttempts = 0;
42
+ };
43
+
44
+ ws.onmessage = function(event) {
45
+ console.log('[Creature HMR] Received message:', event.data);
46
+ try {
47
+ var data = JSON.parse(event.data);
48
+
49
+ if (data.type === 'full-reload') {
50
+ console.log('[Creature HMR] Full reload triggered, notifying parent');
51
+ notifyParent();
52
+ } else if (data.type === 'update') {
53
+ console.log('[Creature HMR] Update detected, notifying parent');
54
+ notifyParent();
55
+ } else if (data.type === 'connected') {
56
+ console.log('[Creature HMR] Server acknowledged connection');
57
+ }
58
+ } catch (e) {
59
+ console.log('[Creature HMR] Failed to parse message:', e);
60
+ }
61
+ };
62
+
63
+ ws.onclose = function() {
64
+ console.log('[Creature HMR] Disconnected, reconnecting in ' + reconnectDelay + 'ms...');
65
+ reconnectAttempts++;
66
+ setTimeout(connect, reconnectDelay);
67
+ };
68
+
69
+ ws.onerror = function(err) {
70
+ console.log('[Creature HMR] WebSocket error:', err);
71
+ };
72
+ }
73
+
74
+ function notifyParent() {
75
+ console.log('[Creature HMR] Sending hmr-reload to parent frame');
76
+ window.parent.postMessage({
77
+ jsonrpc: '2.0',
78
+ method: 'ui/notifications/hmr-reload',
79
+ params: {}
80
+ }, '*');
81
+ }
82
+
83
+ // Start connection
84
+ connect();
85
+ })();
86
+ `.trim();
87
+ }
88
+ function generateHmrClientScriptTag(port) {
89
+ return `<script>${generateHmrClientScript(port)}</script>`;
90
+ }
91
+
92
+ // src/vite/index.ts
93
+ var HMR_PORT_OFFSET = 1e3;
94
+ function findAvailablePort(startPort) {
95
+ return new Promise((resolve2) => {
96
+ const server = createNetServer();
97
+ server.listen(startPort, () => {
98
+ const port = server.address().port;
99
+ server.close(() => resolve2(port));
100
+ });
101
+ server.on("error", () => {
102
+ resolve2(findAvailablePort(startPort + 1));
103
+ });
104
+ });
105
+ }
106
+ function findPages(dir, baseDir) {
107
+ const entries = [];
108
+ if (!existsSync(dir)) return entries;
109
+ const items = readdirSync(dir);
110
+ if (items.includes("page.tsx")) {
111
+ const relativePath = dir.slice(baseDir.length + 1);
112
+ entries.push({
113
+ name: relativePath || "main",
114
+ pagePath: join(dir, "page.tsx")
115
+ });
116
+ }
117
+ for (const item of items) {
118
+ const fullPath = join(dir, item);
119
+ if (statSync(fullPath).isDirectory() && !item.startsWith("_") && item !== "node_modules") {
120
+ entries.push(...findPages(fullPath, baseDir));
121
+ }
122
+ }
123
+ return entries;
124
+ }
125
+ var hmrServer = null;
126
+ var hmrClients = /* @__PURE__ */ new Set();
127
+ function sendWebSocketFrame(socket, data) {
128
+ const payload = Buffer.from(data);
129
+ const length = payload.length;
130
+ let frame;
131
+ if (length < 126) {
132
+ frame = Buffer.alloc(2 + length);
133
+ frame[0] = 129;
134
+ frame[1] = length;
135
+ payload.copy(frame, 2);
136
+ } else if (length < 65536) {
137
+ frame = Buffer.alloc(4 + length);
138
+ frame[0] = 129;
139
+ frame[1] = 126;
140
+ frame.writeUInt16BE(length, 2);
141
+ payload.copy(frame, 4);
142
+ } else {
143
+ frame = Buffer.alloc(10 + length);
144
+ frame[0] = 129;
145
+ frame[1] = 127;
146
+ frame.writeBigUInt64BE(BigInt(length), 2);
147
+ payload.copy(frame, 10);
148
+ }
149
+ socket.write(frame);
150
+ }
151
+ function startHmrServer(port) {
152
+ if (hmrServer) return;
153
+ hmrServer = createHttpServer((_req, res) => {
154
+ res.writeHead(200);
155
+ res.end("Creature HMR Server");
156
+ });
157
+ hmrServer.on("upgrade", (req, socket, head) => {
158
+ const key = req.headers["sec-websocket-key"];
159
+ if (!key) {
160
+ socket.destroy();
161
+ return;
162
+ }
163
+ const acceptKey = createHash("sha1").update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64");
164
+ socket.write(
165
+ `HTTP/1.1 101 Switching Protocols\r
166
+ Upgrade: websocket\r
167
+ Connection: Upgrade\r
168
+ Sec-WebSocket-Accept: ${acceptKey}\r
169
+ \r
170
+ `
171
+ );
172
+ hmrClients.add(socket);
173
+ sendWebSocketFrame(socket, JSON.stringify({ type: "connected" }));
174
+ socket.on("close", () => {
175
+ hmrClients.delete(socket);
176
+ });
177
+ socket.on("error", () => {
178
+ hmrClients.delete(socket);
179
+ });
180
+ });
181
+ hmrServer.listen(port);
182
+ }
183
+ function notifyHmrClients() {
184
+ const message = JSON.stringify({ type: "full-reload" });
185
+ const toRemove = [];
186
+ for (const client of hmrClients) {
187
+ try {
188
+ if (!client.destroyed) {
189
+ sendWebSocketFrame(client, message);
190
+ } else {
191
+ toRemove.push(client);
192
+ }
193
+ } catch {
194
+ toRemove.push(client);
195
+ }
196
+ }
197
+ for (const client of toRemove) {
198
+ hmrClients.delete(client);
199
+ }
200
+ if (hmrClients.size > 0) {
201
+ console.log("App UI reloaded");
202
+ }
203
+ }
204
+ function creature(options = {}) {
205
+ const uiDir = options.uiDir || "src/ui";
206
+ const outDir = options.outDir || "dist/ui";
207
+ const preferredHmrPort = options.hmrPort || 5899;
208
+ const generateBundle = options.generateBundle || false;
209
+ let root;
210
+ let tempDir;
211
+ let entries = [];
212
+ let hasSingleFilePlugin = false;
213
+ let hmrPort = null;
214
+ let isWatchMode = false;
215
+ let remainingPages = [];
216
+ return {
217
+ name: "creature",
218
+ async config(config) {
219
+ root = config.root || process.cwd();
220
+ const uiPath = resolve(root, uiDir);
221
+ entries = findPages(uiPath, uiPath);
222
+ if (entries.length === 0) {
223
+ return;
224
+ }
225
+ const plugins = config.plugins?.flat() || [];
226
+ hasSingleFilePlugin = plugins.some((p) => p && typeof p === "object" && "name" in p && p.name === "vite:singlefile");
227
+ tempDir = resolve(root, "node_modules/.creature");
228
+ const selectedPage = process.env.CREATURE_PAGE;
229
+ if (!selectedPage) {
230
+ rmSync(tempDir, { recursive: true, force: true });
231
+ }
232
+ mkdirSync(tempDir, { recursive: true });
233
+ for (const entry of entries) {
234
+ const relativePagePath = relative(tempDir, entry.pagePath).replace(/\\/g, "/");
235
+ writeFileSync(
236
+ join(tempDir, `${entry.name}.entry.tsx`),
237
+ `import { createElement } from "react";
238
+ import { createRoot } from "react-dom/client";
239
+ import Page from "${relativePagePath.replace(/\.tsx$/, "")}";
240
+ createRoot(document.getElementById("root")!).render(createElement(Page));
241
+ `
242
+ );
243
+ writeFileSync(
244
+ join(tempDir, `${entry.name}.html`),
245
+ `<!DOCTYPE html>
246
+ <html lang="en">
247
+ <head>
248
+ <meta charset="UTF-8" />
249
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
250
+ <title>${entry.name}</title>
251
+ </head>
252
+ <body>
253
+ <div id="root"></div>
254
+ <script type="module" src="./${entry.name}.entry.tsx"></script>
255
+ </body>
256
+ </html>`
257
+ );
258
+ }
259
+ let inputs;
260
+ if (hasSingleFilePlugin) {
261
+ const targetEntry = selectedPage ? entries.find((e) => e.name === selectedPage) : entries[0];
262
+ if (!targetEntry) {
263
+ console.error(`Page "${selectedPage}" not found`);
264
+ return;
265
+ }
266
+ inputs = { [targetEntry.name]: join(tempDir, `${targetEntry.name}.html`) };
267
+ if (!selectedPage && entries.length > 1) {
268
+ remainingPages = entries.slice(1).map((e) => e.name);
269
+ }
270
+ } else {
271
+ inputs = Object.fromEntries(
272
+ entries.map((e) => [e.name, join(tempDir, `${e.name}.html`)])
273
+ );
274
+ }
275
+ return {
276
+ root: tempDir,
277
+ publicDir: false,
278
+ logLevel: "silent",
279
+ build: {
280
+ outDir: resolve(root, outDir),
281
+ emptyOutDir: !selectedPage,
282
+ rollupOptions: { input: inputs },
283
+ reportCompressedSize: false
284
+ }
285
+ };
286
+ },
287
+ async buildStart() {
288
+ if (!tempDir) return;
289
+ isWatchMode = this.meta.watchMode === true;
290
+ if (isWatchMode && !hmrServer) {
291
+ const mcpPort = process.env.MCP_PORT ? parseInt(process.env.MCP_PORT, 10) : null;
292
+ hmrPort = mcpPort ? mcpPort + HMR_PORT_OFFSET : await findAvailablePort(preferredHmrPort);
293
+ startHmrServer(hmrPort);
294
+ mkdirSync(tempDir, { recursive: true });
295
+ const hmrConfig = { port: hmrPort };
296
+ writeFileSync(
297
+ join(tempDir, "hmr.json"),
298
+ JSON.stringify(hmrConfig, null, 2)
299
+ );
300
+ }
301
+ },
302
+ writeBundle() {
303
+ if (!hasSingleFilePlugin || remainingPages.length === 0) {
304
+ if (isWatchMode) notifyHmrClients();
305
+ return;
306
+ }
307
+ for (const pageName of remainingPages) {
308
+ spawnSync("npx", ["vite", "build"], {
309
+ cwd: root,
310
+ env: { ...process.env, CREATURE_PAGE: pageName },
311
+ stdio: "inherit"
312
+ });
313
+ }
314
+ if (isWatchMode) {
315
+ notifyHmrClients();
316
+ } else {
317
+ remainingPages = [];
318
+ }
319
+ },
320
+ closeBundle() {
321
+ if (isWatchMode) return;
322
+ if (generateBundle && entries.length > 0 && !process.env.CREATURE_PAGE) {
323
+ const bundleOutputDir = resolve(root, outDir);
324
+ const exports = [];
325
+ for (const entry of entries) {
326
+ const htmlPath = join(bundleOutputDir, `${entry.name}.html`);
327
+ if (existsSync(htmlPath)) {
328
+ const html = readFileSync(htmlPath, "utf-8");
329
+ const escaped = html.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
330
+ exports.push(`export const ${entry.name.replace(/-/g, "_")} = \`${escaped}\`;`);
331
+ }
332
+ }
333
+ if (exports.length > 0) {
334
+ const bundleContent = `/**
335
+ * Auto-generated UI bundle for serverless deployments.
336
+ * Import these exports in your MCP server for Vercel/Lambda.
337
+ *
338
+ * @example
339
+ * import { main } from "./dist/ui/bundle.js";
340
+ * app.resource({ html: main });
341
+ */
342
+ ${exports.join("\n\n")}
343
+ `;
344
+ writeFileSync(join(bundleOutputDir, "bundle.js"), bundleContent);
345
+ console.log(`Generated ${outDir}/bundle.js for serverless`);
346
+ }
347
+ }
348
+ if (tempDir && existsSync(tempDir) && !process.env.CREATURE_PAGE) {
349
+ rmSync(tempDir, { recursive: true, force: true });
350
+ }
351
+ }
352
+ };
353
+ }
354
+ var vite_default = creature;
355
+ export {
356
+ HMR_PORT_OFFSET,
357
+ creature,
358
+ vite_default as default,
359
+ generateHmrClientScript,
360
+ generateHmrClientScriptTag
361
+ };
362
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/vite/index.ts","../../src/vite/hmr-client.ts"],"sourcesContent":["import { resolve, join, relative } from \"node:path\";\nimport { readdirSync, statSync, existsSync, writeFileSync, mkdirSync, rmSync, readFileSync } from \"node:fs\";\nimport { createServer as createNetServer } from \"node:net\";\nimport { createServer as createHttpServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { createHash } from \"node:crypto\";\nimport { spawnSync } from \"node:child_process\";\nimport type { Duplex } from \"node:stream\";\nimport type { Plugin, UserConfig } from \"vite\";\n\nexport interface CreaturePluginOptions {\n uiDir?: string;\n outDir?: string;\n hmrPort?: number;\n /**\n * Generate a JS module exporting bundled HTML for serverless deployments.\n * When enabled, creates `dist/ui/bundle.js` with named exports for each page.\n * \n * @example\n * // In server code:\n * import { main } from \"./dist/ui/bundle.js\";\n * app.resource({ html: main });\n * \n * @default false\n */\n generateBundle?: boolean;\n}\n\nexport interface HmrConfig {\n port: number;\n}\n\n/**\n * Offset added to MCP_PORT to derive the HMR port.\n * When MCP_PORT is set (by Creature), both Vite and the SDK server\n * can independently calculate the same HMR port without coordination.\n */\nexport const HMR_PORT_OFFSET = 1000;\n\nfunction findAvailablePort(startPort: number): Promise<number> {\n return new Promise((resolve) => {\n const server = createNetServer();\n server.listen(startPort, () => {\n const port = (server.address() as { port: number }).port;\n server.close(() => resolve(port));\n });\n server.on(\"error\", () => {\n resolve(findAvailablePort(startPort + 1));\n });\n });\n}\n\ninterface EntryPoint {\n name: string;\n pagePath: string;\n}\n\nfunction findPages(dir: string, baseDir: string): EntryPoint[] {\n const entries: EntryPoint[] = [];\n if (!existsSync(dir)) return entries;\n\n const items = readdirSync(dir);\n\n if (items.includes(\"page.tsx\")) {\n const relativePath = dir.slice(baseDir.length + 1);\n entries.push({\n name: relativePath || \"main\",\n pagePath: join(dir, \"page.tsx\"),\n });\n }\n\n for (const item of items) {\n const fullPath = join(dir, item);\n if (statSync(fullPath).isDirectory() && !item.startsWith(\"_\") && item !== \"node_modules\") {\n entries.push(...findPages(fullPath, baseDir));\n }\n }\n\n return entries;\n}\n\nlet hmrServer: ReturnType<typeof createHttpServer> | null = null;\nlet hmrClients: Set<Duplex> = new Set();\n\nfunction sendWebSocketFrame(socket: Duplex, data: string): void {\n const payload = Buffer.from(data);\n const length = payload.length;\n \n let frame: Buffer;\n if (length < 126) {\n frame = Buffer.alloc(2 + length);\n frame[0] = 0x81;\n frame[1] = length;\n payload.copy(frame, 2);\n } else if (length < 65536) {\n frame = Buffer.alloc(4 + length);\n frame[0] = 0x81;\n frame[1] = 126;\n frame.writeUInt16BE(length, 2);\n payload.copy(frame, 4);\n } else {\n frame = Buffer.alloc(10 + length);\n frame[0] = 0x81;\n frame[1] = 127;\n frame.writeBigUInt64BE(BigInt(length), 2);\n payload.copy(frame, 10);\n }\n \n socket.write(frame);\n}\n\nfunction startHmrServer(port: number): void {\n if (hmrServer) return;\n \n hmrServer = createHttpServer((_req: IncomingMessage, res: ServerResponse) => {\n res.writeHead(200);\n res.end(\"Creature HMR Server\");\n });\n \n hmrServer.on(\"upgrade\", (req: IncomingMessage, socket: Duplex, head: Buffer) => {\n const key = req.headers[\"sec-websocket-key\"];\n if (!key) {\n socket.destroy();\n return;\n }\n \n const acceptKey = createHash(\"sha1\")\n .update(key + \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\")\n .digest(\"base64\");\n \n socket.write(\n \"HTTP/1.1 101 Switching Protocols\\r\\n\" +\n \"Upgrade: websocket\\r\\n\" +\n \"Connection: Upgrade\\r\\n\" +\n `Sec-WebSocket-Accept: ${acceptKey}\\r\\n` +\n \"\\r\\n\"\n );\n \n hmrClients.add(socket);\n sendWebSocketFrame(socket, JSON.stringify({ type: \"connected\" }));\n \n socket.on(\"close\", () => {\n hmrClients.delete(socket);\n });\n \n socket.on(\"error\", () => {\n hmrClients.delete(socket);\n });\n });\n \n hmrServer.listen(port);\n}\n\nfunction notifyHmrClients(): void {\n const message = JSON.stringify({ type: \"full-reload\" });\n const toRemove: Duplex[] = [];\n \n for (const client of hmrClients) {\n try {\n if (!client.destroyed) {\n sendWebSocketFrame(client, message);\n } else {\n toRemove.push(client);\n }\n } catch {\n toRemove.push(client);\n }\n }\n \n for (const client of toRemove) {\n hmrClients.delete(client);\n }\n \n if (hmrClients.size > 0) {\n console.log(\"App UI reloaded\");\n }\n}\n\n/**\n * Vite plugin for Creature MCP Apps.\n * \n * Just write page.tsx files - no HTML or entry files needed.\n * \n * ```\n * src/ui/\n * ├── page.tsx → dist/ui/main.html\n * ├── inline/page.tsx → dist/ui/inline.html\n * └── _components/ → ignored\n * ```\n * \n * When using vite-plugin-singlefile, multiple pages are built automatically\n * via sequential builds (singlefile requires single entry per build).\n */\nexport function creature(options: CreaturePluginOptions = {}): Plugin {\n const uiDir = options.uiDir || \"src/ui\";\n const outDir = options.outDir || \"dist/ui\";\n const preferredHmrPort = options.hmrPort || 5899;\n const generateBundle = options.generateBundle || false;\n \n let root: string;\n let tempDir: string;\n let entries: EntryPoint[] = [];\n let hasSingleFilePlugin = false;\n let hmrPort: number | null = null;\n let isWatchMode = false;\n let remainingPages: string[] = [];\n\n return {\n name: \"creature\",\n \n async config(config) {\n root = config.root || process.cwd();\n const uiPath = resolve(root, uiDir);\n entries = findPages(uiPath, uiPath);\n\n if (entries.length === 0) {\n return;\n }\n\n const plugins = config.plugins?.flat() || [];\n hasSingleFilePlugin = plugins.some(p => p && typeof p === 'object' && 'name' in p && p.name === 'vite:singlefile');\n\n tempDir = resolve(root, \"node_modules/.creature\");\n \n const selectedPage = process.env.CREATURE_PAGE;\n \n if (!selectedPage) {\n rmSync(tempDir, { recursive: true, force: true });\n }\n mkdirSync(tempDir, { recursive: true });\n\n for (const entry of entries) {\n const relativePagePath = relative(tempDir, entry.pagePath).replace(/\\\\/g, \"/\");\n writeFileSync(join(tempDir, `${entry.name}.entry.tsx`), \n`import { createElement } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport Page from \"${relativePagePath.replace(/\\.tsx$/, \"\")}\";\ncreateRoot(document.getElementById(\"root\")!).render(createElement(Page));\n`);\n\n writeFileSync(join(tempDir, `${entry.name}.html`),\n`<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${entry.name}</title>\n</head>\n<body>\n <div id=\"root\"></div>\n <script type=\"module\" src=\"./${entry.name}.entry.tsx\"></script>\n</body>\n</html>`);\n }\n \n let inputs: Record<string, string>;\n \n if (hasSingleFilePlugin) {\n const targetEntry = selectedPage \n ? entries.find(e => e.name === selectedPage)\n : entries[0];\n \n if (!targetEntry) {\n console.error(`Page \"${selectedPage}\" not found`);\n return;\n }\n \n inputs = { [targetEntry.name]: join(tempDir, `${targetEntry.name}.html`) };\n \n if (!selectedPage && entries.length > 1) {\n remainingPages = entries.slice(1).map(e => e.name);\n }\n } else {\n inputs = Object.fromEntries(\n entries.map(e => [e.name, join(tempDir, `${e.name}.html`)])\n );\n }\n\n return {\n root: tempDir,\n publicDir: false,\n logLevel: \"silent\" as const,\n build: {\n outDir: resolve(root, outDir),\n emptyOutDir: !selectedPage,\n rollupOptions: { input: inputs },\n reportCompressedSize: false,\n },\n } satisfies UserConfig;\n },\n\n async buildStart() {\n if (!tempDir) return;\n \n isWatchMode = this.meta.watchMode === true;\n \n if (isWatchMode && !hmrServer) {\n // Derive HMR port from MCP_PORT when available (set by Creature).\n // This allows the SDK server to know the HMR port immediately without\n // waiting for hmr.json to be written, eliminating the race condition.\n const mcpPort = process.env.MCP_PORT ? parseInt(process.env.MCP_PORT, 10) : null;\n hmrPort = mcpPort ? mcpPort + HMR_PORT_OFFSET : await findAvailablePort(preferredHmrPort);\n startHmrServer(hmrPort);\n \n // Still write hmr.json for non-Creature environments (manual npm run dev)\n mkdirSync(tempDir, { recursive: true });\n const hmrConfig: HmrConfig = { port: hmrPort };\n writeFileSync(\n join(tempDir, \"hmr.json\"),\n JSON.stringify(hmrConfig, null, 2)\n );\n }\n },\n\n writeBundle() {\n if (!hasSingleFilePlugin || remainingPages.length === 0) {\n if (isWatchMode) notifyHmrClients();\n return;\n }\n \n for (const pageName of remainingPages) {\n spawnSync(\"npx\", [\"vite\", \"build\"], {\n cwd: root,\n env: { ...process.env, CREATURE_PAGE: pageName },\n stdio: \"inherit\",\n });\n }\n \n if (isWatchMode) {\n notifyHmrClients();\n } else {\n remainingPages = [];\n }\n },\n\n closeBundle() {\n if (isWatchMode) return;\n \n // Generate bundle.js for serverless deployments\n if (generateBundle && entries.length > 0 && !process.env.CREATURE_PAGE) {\n const bundleOutputDir = resolve(root, outDir);\n const exports: string[] = [];\n \n for (const entry of entries) {\n const htmlPath = join(bundleOutputDir, `${entry.name}.html`);\n if (existsSync(htmlPath)) {\n const html = readFileSync(htmlPath, \"utf-8\");\n // Escape backticks and ${} for template literal\n const escaped = html.replace(/\\\\/g, \"\\\\\\\\\").replace(/`/g, \"\\\\`\").replace(/\\$\\{/g, \"\\\\${\");\n exports.push(`export const ${entry.name.replace(/-/g, \"_\")} = \\`${escaped}\\`;`);\n }\n }\n \n if (exports.length > 0) {\n const bundleContent = `/**\n * Auto-generated UI bundle for serverless deployments.\n * Import these exports in your MCP server for Vercel/Lambda.\n * \n * @example\n * import { main } from \"./dist/ui/bundle.js\";\n * app.resource({ html: main });\n */\n${exports.join(\"\\n\\n\")}\n`;\n writeFileSync(join(bundleOutputDir, \"bundle.js\"), bundleContent);\n console.log(`Generated ${outDir}/bundle.js for serverless`);\n }\n }\n \n if (tempDir && existsSync(tempDir) && !process.env.CREATURE_PAGE) {\n rmSync(tempDir, { recursive: true, force: true });\n }\n },\n };\n}\n\nexport { generateHmrClientScript, generateHmrClientScriptTag } from \"./hmr-client.js\";\n\nexport default creature;\n","/**\n * HMR Client Script\n * \n * This script is injected into MCP App HTML during development mode.\n * It connects to Vite's HMR WebSocket and notifies the parent frame\n * when a full reload is needed.\n * \n * The parent frame (Creature host) will then:\n * 1. Save current widget state\n * 2. Re-fetch fresh HTML from the MCP server\n * 3. Reload the iframe with new content\n * 4. Restore widget state\n */\n\n/**\n * Generate the HMR client script as a string.\n * The port is injected at generation time.\n */\nexport function generateHmrClientScript(port: number): string {\n return `\n(function() {\n if (window.parent === window) {\n console.log('[Creature HMR] Not in iframe, skipping HMR client');\n return;\n }\n if (window.__CREATURE_HMR_CONNECTED__) {\n console.log('[Creature HMR] Already connected, skipping');\n return;\n }\n window.__CREATURE_HMR_CONNECTED__ = true;\n\n var HMR_PORT = ${port};\n var reconnectAttempts = 0;\n var maxReconnectAttempts = 10;\n var reconnectDelay = 1000;\n\n console.log('[Creature HMR] Initializing HMR client, will connect to port ' + HMR_PORT);\n\n function connect() {\n if (reconnectAttempts >= maxReconnectAttempts) {\n console.log('[Creature HMR] Max reconnection attempts reached, giving up');\n return;\n }\n\n console.log('[Creature HMR] Attempting to connect to ws://localhost:' + HMR_PORT + ' (attempt ' + (reconnectAttempts + 1) + ')');\n var ws = new WebSocket('ws://localhost:' + HMR_PORT);\n\n ws.onopen = function() {\n console.log('[Creature HMR] Connected to HMR server on port ' + HMR_PORT);\n reconnectAttempts = 0;\n };\n\n ws.onmessage = function(event) {\n console.log('[Creature HMR] Received message:', event.data);\n try {\n var data = JSON.parse(event.data);\n \n if (data.type === 'full-reload') {\n console.log('[Creature HMR] Full reload triggered, notifying parent');\n notifyParent();\n } else if (data.type === 'update') {\n console.log('[Creature HMR] Update detected, notifying parent');\n notifyParent();\n } else if (data.type === 'connected') {\n console.log('[Creature HMR] Server acknowledged connection');\n }\n } catch (e) {\n console.log('[Creature HMR] Failed to parse message:', e);\n }\n };\n\n ws.onclose = function() {\n console.log('[Creature HMR] Disconnected, reconnecting in ' + reconnectDelay + 'ms...');\n reconnectAttempts++;\n setTimeout(connect, reconnectDelay);\n };\n\n ws.onerror = function(err) {\n console.log('[Creature HMR] WebSocket error:', err);\n };\n }\n\n function notifyParent() {\n console.log('[Creature HMR] Sending hmr-reload to parent frame');\n window.parent.postMessage({\n jsonrpc: '2.0',\n method: 'ui/notifications/hmr-reload',\n params: {}\n }, '*');\n }\n\n // Start connection\n connect();\n})();\n`.trim();\n}\n\n/**\n * Generate a script tag with the HMR client code.\n */\nexport function generateHmrClientScriptTag(port: number): string {\n return `<script>${generateHmrClientScript(port)}</script>`;\n}\n"],"mappings":"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","names":["resolve"]}
package/package.json CHANGED
@@ -1,32 +1,86 @@
1
1
  {
2
2
  "name": "open-mcp-app",
3
- "version": "0.1.0",
4
- "description": "A small Node.js SDK skeleton for working with open MCP apps.",
5
- "main": "index.js",
3
+ "version": "0.1.1",
4
+ "description": "SDK for building MCP Apps that work on both Creature and ChatGPT",
5
+ "type": "module",
6
6
  "exports": {
7
- ".": "./index.js"
7
+ "./server": {
8
+ "types": "./dist/server/index.d.ts",
9
+ "import": "./dist/server/index.js",
10
+ "require": "./dist/server/index.js",
11
+ "default": "./dist/server/index.js"
12
+ },
13
+ "./core": {
14
+ "types": "./dist/core/index.d.ts",
15
+ "import": "./dist/core/index.js",
16
+ "require": "./dist/core/index.js",
17
+ "default": "./dist/core/index.js"
18
+ },
19
+ "./react": {
20
+ "types": "./dist/react/index.d.ts",
21
+ "import": "./dist/react/index.js",
22
+ "require": "./dist/react/index.js",
23
+ "default": "./dist/react/index.js"
24
+ },
25
+ "./vite": {
26
+ "types": "./dist/vite/index.d.ts",
27
+ "import": "./dist/vite/index.js",
28
+ "require": "./dist/vite/index.js",
29
+ "default": "./dist/vite/index.js"
30
+ }
8
31
  },
9
32
  "files": [
10
- "index.js",
11
- "README.md",
12
- "LICENSE"
33
+ "dist",
34
+ "README.md"
13
35
  ],
14
36
  "scripts": {
15
- "test": "node -e \"require('./index.js')\"",
16
- "prepublishOnly": "npm test"
37
+ "build": "tsup",
38
+ "dev": "tsup --watch",
39
+ "typecheck": "tsc --noEmit",
40
+ "clean": "rm -rf dist"
41
+ },
42
+ "dependencies": {
43
+ "@modelcontextprotocol/ext-apps": "^0.2.2",
44
+ "@modelcontextprotocol/sdk": "^1.24.0",
45
+ "express": "^5.1.0",
46
+ "ws": "^8.18.0",
47
+ "zod": "^3.24.4"
48
+ },
49
+ "devDependencies": {
50
+ "@types/express": "^5.0.2",
51
+ "@types/node": "^22.15.21",
52
+ "@types/react": "^19.1.6",
53
+ "@types/ws": "^8.5.13",
54
+ "cac": "^6.7.14",
55
+ "react": "^19.1.0",
56
+ "tsup": "^8.5.1",
57
+ "typescript": "^5.8.3",
58
+ "vite": "^6.3.5"
59
+ },
60
+ "peerDependencies": {
61
+ "react": ">=18.0.0"
62
+ },
63
+ "peerDependenciesMeta": {
64
+ "react": {
65
+ "optional": true
66
+ }
17
67
  },
18
68
  "keywords": [
19
69
  "mcp",
20
70
  "model-context-protocol",
21
- "sdk",
22
- "apps",
23
- "open"
71
+ "chatgpt",
72
+ "creature",
73
+ "claude",
74
+ "ai-tools"
24
75
  ],
25
76
  "license": "MIT",
26
- "engines": {
27
- "node": ">=18"
77
+ "repository": {
78
+ "type": "git",
79
+ "url": "https://github.com/creature-run/creature.git",
80
+ "directory": "sdk"
28
81
  },
29
- "publishConfig": {
30
- "access": "public"
82
+ "homepage": "https://creature.run/docs",
83
+ "bugs": {
84
+ "url": "https://github.com/creature-run/creature/issues"
31
85
  }
32
86
  }
package/LICENSE DELETED
@@ -1,22 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
22
-
package/README.md DELETED
@@ -1,36 +0,0 @@
1
- # open-mcp-app
2
-
3
- A small Node.js SDK skeleton for working with open MCP apps.
4
-
5
- This package is intentionally lightweight: it includes a generic HTTP `request` primitive and a few convenience methods that demonstrate typical SDK call shapes against an "open apps" API surface.
6
-
7
- ## Install
8
-
9
- ```bash
10
- npm install open-mcp-app
11
- ```
12
-
13
- ## Usage
14
-
15
- ```js
16
- const { createMcpAppsClient } = require('open-mcp-app');
17
-
18
- const client = createMcpAppsClient({
19
- baseUrl: 'https://api.example.com',
20
- });
21
-
22
- async function main() {
23
- const apps = await client.listApps({});
24
- console.log(apps);
25
- }
26
-
27
- main().catch((err) => {
28
- console.error(err);
29
- process.exitCode = 1;
30
- });
31
- ```
32
-
33
- ## License
34
-
35
- MIT
36
-