@tempad-dev/mcp 0.3.7 → 0.3.9

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/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as SOCK_PATH, c as log, i as RUNTIME_DIR, n as LOCK_PATH, o as ensureDir, r as PACKAGE_VERSION } from "./shared-DzXJNYhZ.mjs";
2
+ import { a as SOCK_PATH, c as log, i as RUNTIME_DIR, n as LOCK_PATH, o as ensureDir, r as PACKAGE_VERSION } from "./shared-Dx5fhN-T.mjs";
3
3
  import { spawn } from "node:child_process";
4
4
  import { connect } from "node:net";
5
5
  import { join } from "node:path";
@@ -31,7 +31,7 @@ process.on("SIGTERM", () => shutdownCli("SIGTERM received."));
31
31
  const HUB_STARTUP_TIMEOUT = 5e3;
32
32
  const CONNECT_RETRY_DELAY = 200;
33
33
  const FAILED_RESTART_DELAY = 5e3;
34
- const HUB_ENTRY = join(fileURLToPath(new URL(".", import.meta.url)), "hub.js");
34
+ const HUB_ENTRY = join(fileURLToPath(new URL(".", import.meta.url)), "hub.mjs");
35
35
  ensureDir(RUNTIME_DIR);
36
36
  function bridge(socket) {
37
37
  return new Promise((resolve) => {
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":["activeSocket: Socket | null","err: unknown","releaseLock: (() => Promise<void>) | null","child: ChildProcess | null"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport type { ChildProcess } from 'node:child_process'\nimport type { Socket } from 'node:net'\n\nimport { spawn } from 'node:child_process'\nimport { connect } from 'node:net'\nimport { join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport lockfile from 'proper-lockfile'\n\nimport { PACKAGE_VERSION, log, LOCK_PATH, RUNTIME_DIR, SOCK_PATH, ensureDir } from './shared'\n\nlet activeSocket: Socket | null = null\nlet shuttingDown = false\n\nfunction closeActiveSocket() {\n if (!activeSocket) return\n try {\n activeSocket.end()\n } catch {\n // ignore\n }\n try {\n activeSocket.destroy()\n } catch {\n // ignore\n }\n activeSocket = null\n}\n\nfunction shutdownCli(reason: string) {\n if (shuttingDown) return\n shuttingDown = true\n log.info(`${reason} Shutting down CLI.`)\n closeActiveSocket()\n process.exit(0)\n}\n\nprocess.on('SIGINT', () => shutdownCli('SIGINT received.'))\nprocess.on('SIGTERM', () => shutdownCli('SIGTERM received.'))\n\nconst HUB_STARTUP_TIMEOUT = 5000\nconst CONNECT_RETRY_DELAY = 200\nconst FAILED_RESTART_DELAY = 5000\nconst HERE = fileURLToPath(new URL('.', import.meta.url))\nconst HUB_ENTRY = join(HERE, 'hub.js')\n\nensureDir(RUNTIME_DIR)\n\nfunction bridge(socket: Socket): Promise<void> {\n return new Promise((resolve) => {\n log.info('Bridge established with Hub. Forwarding I/O.')\n activeSocket = socket\n\n const onStdinEnd = () => {\n shutdownCli('Consumer stream ended.')\n }\n process.stdin.once('end', onStdinEnd)\n\n const onSocketClose = () => {\n log.warn('Connection to Hub lost. Attempting to reconnect...')\n activeSocket = null\n process.stdin.removeListener('end', onStdinEnd)\n process.stdin.unpipe(socket)\n socket.unpipe(process.stdout)\n socket.removeAllListeners()\n resolve()\n }\n socket.once('close', onSocketClose)\n socket.on('error', (err) => log.warn({ err }, 'Socket error occurred.'))\n\n // The `{ end: false }` option prevents stdin from closing the socket.\n process.stdin.pipe(socket, { end: false }).pipe(process.stdout)\n })\n}\n\nfunction connectHub(): Promise<Socket> {\n return new Promise((resolve, reject) => {\n const socket = connect(SOCK_PATH)\n socket.on('connect', () => {\n socket.removeAllListeners('error')\n resolve(socket)\n })\n socket.on('error', reject)\n })\n}\n\nasync function connectWithRetry(timeout: number): Promise<Socket> {\n const startTime = Date.now()\n let delay = CONNECT_RETRY_DELAY\n while (Date.now() - startTime < timeout) {\n try {\n return await connectHub()\n } catch (err: unknown) {\n if (\n err &&\n typeof err === 'object' &&\n 'code' in err &&\n (err.code === 'ENOENT' || err.code === 'ECONNREFUSED')\n ) {\n const remainingTime = timeout - (Date.now() - startTime)\n const waitTime = Math.min(delay, remainingTime)\n if (waitTime <= 0) break\n await new Promise((r) => setTimeout(r, waitTime))\n delay = Math.min(delay * 1.5, 1000)\n } else {\n throw err\n }\n }\n }\n throw new Error(`Failed to connect to Hub within ${timeout}ms.`)\n}\n\nfunction startHub(): ChildProcess {\n log.info('Spawning new Hub process...')\n return spawn(process.execPath, [HUB_ENTRY], {\n detached: true,\n stdio: 'ignore'\n })\n}\n\nasync function tryBecomeLeaderAndStartHub(): Promise<Socket> {\n let releaseLock: (() => Promise<void>) | null = null\n try {\n releaseLock = await lockfile.lock(LOCK_PATH, {\n retries: { retries: 5, factor: 1.2, minTimeout: 50 },\n stale: 15000\n })\n } catch {\n log.info('Another process is starting the Hub. Waiting...')\n return connectWithRetry(HUB_STARTUP_TIMEOUT)\n }\n\n log.info('Acquired lock. Starting Hub as the leader...')\n let child: ChildProcess | null = null\n try {\n try {\n return await connectHub()\n } catch {\n // If the Hub is not running, we proceed to start it.\n log.info('Hub not running. Proceeding to start it...')\n }\n child = startHub()\n child.on('error', (err) => log.error({ err }, 'Hub child process error.'))\n const socket = await connectWithRetry(HUB_STARTUP_TIMEOUT)\n child.unref()\n return socket\n } catch (err: unknown) {\n log.error({ err }, 'Failed to start or connect to the Hub.')\n if (child && !child.killed) {\n log.warn(`Killing stale Hub process (PID: ${child.pid})...`)\n child.kill('SIGTERM')\n }\n throw err\n } finally {\n if (releaseLock) await releaseLock()\n }\n}\n\nasync function main() {\n log.info({ version: PACKAGE_VERSION }, 'TemPad MCP Client starting...')\n\n while (true) {\n try {\n const socket = await connectHub().catch(() => {\n log.info('Hub not running. Initiating startup sequence...')\n return tryBecomeLeaderAndStartHub()\n })\n await bridge(socket)\n log.info('Bridge disconnected. Restarting connection process...')\n } catch (err: unknown) {\n log.error(\n { err },\n `Connection attempt failed. Retrying in ${FAILED_RESTART_DELAY / 1000}s...`\n )\n await new Promise((r) => setTimeout(r, FAILED_RESTART_DELAY))\n }\n }\n}\n\nmain()\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":["activeSocket: Socket | null","err: unknown","releaseLock: (() => Promise<void>) | null","child: ChildProcess | null"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport type { ChildProcess } from 'node:child_process'\nimport type { Socket } from 'node:net'\n\nimport { spawn } from 'node:child_process'\nimport { connect } from 'node:net'\nimport { join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport lockfile from 'proper-lockfile'\n\nimport { PACKAGE_VERSION, log, LOCK_PATH, RUNTIME_DIR, SOCK_PATH, ensureDir } from './shared'\n\nlet activeSocket: Socket | null = null\nlet shuttingDown = false\n\nfunction closeActiveSocket() {\n if (!activeSocket) return\n try {\n activeSocket.end()\n } catch {\n // ignore\n }\n try {\n activeSocket.destroy()\n } catch {\n // ignore\n }\n activeSocket = null\n}\n\nfunction shutdownCli(reason: string) {\n if (shuttingDown) return\n shuttingDown = true\n log.info(`${reason} Shutting down CLI.`)\n closeActiveSocket()\n process.exit(0)\n}\n\nprocess.on('SIGINT', () => shutdownCli('SIGINT received.'))\nprocess.on('SIGTERM', () => shutdownCli('SIGTERM received.'))\n\nconst HUB_STARTUP_TIMEOUT = 5000\nconst CONNECT_RETRY_DELAY = 200\nconst FAILED_RESTART_DELAY = 5000\nconst HERE = fileURLToPath(new URL('.', import.meta.url))\nconst HUB_ENTRY = join(HERE, 'hub.mjs')\n\nensureDir(RUNTIME_DIR)\n\nfunction bridge(socket: Socket): Promise<void> {\n return new Promise((resolve) => {\n log.info('Bridge established with Hub. Forwarding I/O.')\n activeSocket = socket\n\n const onStdinEnd = () => {\n shutdownCli('Consumer stream ended.')\n }\n process.stdin.once('end', onStdinEnd)\n\n const onSocketClose = () => {\n log.warn('Connection to Hub lost. Attempting to reconnect...')\n activeSocket = null\n process.stdin.removeListener('end', onStdinEnd)\n process.stdin.unpipe(socket)\n socket.unpipe(process.stdout)\n socket.removeAllListeners()\n resolve()\n }\n socket.once('close', onSocketClose)\n socket.on('error', (err) => log.warn({ err }, 'Socket error occurred.'))\n\n // The `{ end: false }` option prevents stdin from closing the socket.\n process.stdin.pipe(socket, { end: false }).pipe(process.stdout)\n })\n}\n\nfunction connectHub(): Promise<Socket> {\n return new Promise((resolve, reject) => {\n const socket = connect(SOCK_PATH)\n socket.on('connect', () => {\n socket.removeAllListeners('error')\n resolve(socket)\n })\n socket.on('error', reject)\n })\n}\n\nasync function connectWithRetry(timeout: number): Promise<Socket> {\n const startTime = Date.now()\n let delay = CONNECT_RETRY_DELAY\n while (Date.now() - startTime < timeout) {\n try {\n return await connectHub()\n } catch (err: unknown) {\n if (\n err &&\n typeof err === 'object' &&\n 'code' in err &&\n (err.code === 'ENOENT' || err.code === 'ECONNREFUSED')\n ) {\n const remainingTime = timeout - (Date.now() - startTime)\n const waitTime = Math.min(delay, remainingTime)\n if (waitTime <= 0) break\n await new Promise((r) => setTimeout(r, waitTime))\n delay = Math.min(delay * 1.5, 1000)\n } else {\n throw err\n }\n }\n }\n throw new Error(`Failed to connect to Hub within ${timeout}ms.`)\n}\n\nfunction startHub(): ChildProcess {\n log.info('Spawning new Hub process...')\n return spawn(process.execPath, [HUB_ENTRY], {\n detached: true,\n stdio: 'ignore'\n })\n}\n\nasync function tryBecomeLeaderAndStartHub(): Promise<Socket> {\n let releaseLock: (() => Promise<void>) | null = null\n try {\n releaseLock = await lockfile.lock(LOCK_PATH, {\n retries: { retries: 5, factor: 1.2, minTimeout: 50 },\n stale: 15000\n })\n } catch {\n log.info('Another process is starting the Hub. Waiting...')\n return connectWithRetry(HUB_STARTUP_TIMEOUT)\n }\n\n log.info('Acquired lock. Starting Hub as the leader...')\n let child: ChildProcess | null = null\n try {\n try {\n return await connectHub()\n } catch {\n // If the Hub is not running, we proceed to start it.\n log.info('Hub not running. Proceeding to start it...')\n }\n child = startHub()\n child.on('error', (err) => log.error({ err }, 'Hub child process error.'))\n const socket = await connectWithRetry(HUB_STARTUP_TIMEOUT)\n child.unref()\n return socket\n } catch (err: unknown) {\n log.error({ err }, 'Failed to start or connect to the Hub.')\n if (child && !child.killed) {\n log.warn(`Killing stale Hub process (PID: ${child.pid})...`)\n child.kill('SIGTERM')\n }\n throw err\n } finally {\n if (releaseLock) await releaseLock()\n }\n}\n\nasync function main() {\n log.info({ version: PACKAGE_VERSION }, 'TemPad MCP Client starting...')\n\n while (true) {\n try {\n const socket = await connectHub().catch(() => {\n log.info('Hub not running. Initiating startup sequence...')\n return tryBecomeLeaderAndStartHub()\n })\n await bridge(socket)\n log.info('Bridge disconnected. Restarting connection process...')\n } catch (err: unknown) {\n log.error(\n { err },\n `Connection attempt failed. Retrying in ${FAILED_RESTART_DELAY / 1000}s...`\n )\n await new Promise((r) => setTimeout(r, FAILED_RESTART_DELAY))\n }\n }\n}\n\nmain()\n"],"mappings":";;;;;;;;;AAaA,IAAIA,eAA8B;AAClC,IAAI,eAAe;AAEnB,SAAS,oBAAoB;AAC3B,KAAI,CAAC,aAAc;AACnB,KAAI;AACF,eAAa,KAAK;SACZ;AAGR,KAAI;AACF,eAAa,SAAS;SAChB;AAGR,gBAAe;;AAGjB,SAAS,YAAY,QAAgB;AACnC,KAAI,aAAc;AAClB,gBAAe;AACf,KAAI,KAAK,GAAG,OAAO,qBAAqB;AACxC,oBAAmB;AACnB,SAAQ,KAAK,EAAE;;AAGjB,QAAQ,GAAG,gBAAgB,YAAY,mBAAmB,CAAC;AAC3D,QAAQ,GAAG,iBAAiB,YAAY,oBAAoB,CAAC;AAE7D,MAAM,sBAAsB;AAC5B,MAAM,sBAAsB;AAC5B,MAAM,uBAAuB;AAE7B,MAAM,YAAY,KADL,cAAc,IAAI,IAAI,KAAK,OAAO,KAAK,IAAI,CAAC,EAC5B,UAAU;AAEvC,UAAU,YAAY;AAEtB,SAAS,OAAO,QAA+B;AAC7C,QAAO,IAAI,SAAS,YAAY;AAC9B,MAAI,KAAK,+CAA+C;AACxD,iBAAe;EAEf,MAAM,mBAAmB;AACvB,eAAY,yBAAyB;;AAEvC,UAAQ,MAAM,KAAK,OAAO,WAAW;EAErC,MAAM,sBAAsB;AAC1B,OAAI,KAAK,qDAAqD;AAC9D,kBAAe;AACf,WAAQ,MAAM,eAAe,OAAO,WAAW;AAC/C,WAAQ,MAAM,OAAO,OAAO;AAC5B,UAAO,OAAO,QAAQ,OAAO;AAC7B,UAAO,oBAAoB;AAC3B,YAAS;;AAEX,SAAO,KAAK,SAAS,cAAc;AACnC,SAAO,GAAG,UAAU,QAAQ,IAAI,KAAK,EAAE,KAAK,EAAE,yBAAyB,CAAC;AAGxE,UAAQ,MAAM,KAAK,QAAQ,EAAE,KAAK,OAAO,CAAC,CAAC,KAAK,QAAQ,OAAO;GAC/D;;AAGJ,SAAS,aAA8B;AACrC,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,SAAS,QAAQ,UAAU;AACjC,SAAO,GAAG,iBAAiB;AACzB,UAAO,mBAAmB,QAAQ;AAClC,WAAQ,OAAO;IACf;AACF,SAAO,GAAG,SAAS,OAAO;GAC1B;;AAGJ,eAAe,iBAAiB,SAAkC;CAChE,MAAM,YAAY,KAAK,KAAK;CAC5B,IAAI,QAAQ;AACZ,QAAO,KAAK,KAAK,GAAG,YAAY,QAC9B,KAAI;AACF,SAAO,MAAM,YAAY;UAClBC,KAAc;AACrB,MACE,OACA,OAAO,QAAQ,YACf,UAAU,QACT,IAAI,SAAS,YAAY,IAAI,SAAS,iBACvC;GACA,MAAM,gBAAgB,WAAW,KAAK,KAAK,GAAG;GAC9C,MAAM,WAAW,KAAK,IAAI,OAAO,cAAc;AAC/C,OAAI,YAAY,EAAG;AACnB,SAAM,IAAI,SAAS,MAAM,WAAW,GAAG,SAAS,CAAC;AACjD,WAAQ,KAAK,IAAI,QAAQ,KAAK,IAAK;QAEnC,OAAM;;AAIZ,OAAM,IAAI,MAAM,mCAAmC,QAAQ,KAAK;;AAGlE,SAAS,WAAyB;AAChC,KAAI,KAAK,8BAA8B;AACvC,QAAO,MAAM,QAAQ,UAAU,CAAC,UAAU,EAAE;EAC1C,UAAU;EACV,OAAO;EACR,CAAC;;AAGJ,eAAe,6BAA8C;CAC3D,IAAIC,cAA4C;AAChD,KAAI;AACF,gBAAc,MAAM,SAAS,KAAK,WAAW;GAC3C,SAAS;IAAE,SAAS;IAAG,QAAQ;IAAK,YAAY;IAAI;GACpD,OAAO;GACR,CAAC;SACI;AACN,MAAI,KAAK,kDAAkD;AAC3D,SAAO,iBAAiB,oBAAoB;;AAG9C,KAAI,KAAK,+CAA+C;CACxD,IAAIC,QAA6B;AACjC,KAAI;AACF,MAAI;AACF,UAAO,MAAM,YAAY;UACnB;AAEN,OAAI,KAAK,6CAA6C;;AAExD,UAAQ,UAAU;AAClB,QAAM,GAAG,UAAU,QAAQ,IAAI,MAAM,EAAE,KAAK,EAAE,2BAA2B,CAAC;EAC1E,MAAM,SAAS,MAAM,iBAAiB,oBAAoB;AAC1D,QAAM,OAAO;AACb,SAAO;UACAF,KAAc;AACrB,MAAI,MAAM,EAAE,KAAK,EAAE,yCAAyC;AAC5D,MAAI,SAAS,CAAC,MAAM,QAAQ;AAC1B,OAAI,KAAK,mCAAmC,MAAM,IAAI,MAAM;AAC5D,SAAM,KAAK,UAAU;;AAEvB,QAAM;WACE;AACR,MAAI,YAAa,OAAM,aAAa;;;AAIxC,eAAe,OAAO;AACpB,KAAI,KAAK,EAAE,SAAS,iBAAiB,EAAE,gCAAgC;AAEvE,QAAO,KACL,KAAI;AAKF,QAAM,OAJS,MAAM,YAAY,CAAC,YAAY;AAC5C,OAAI,KAAK,kDAAkD;AAC3D,UAAO,4BAA4B;IACnC,CACkB;AACpB,MAAI,KAAK,wDAAwD;UAC1DA,KAAc;AACrB,MAAI,MACF,EAAE,KAAK,EACP,0CAA0C,uBAAuB,IAAK,MACvE;AACD,QAAM,IAAI,SAAS,MAAM,WAAW,GAAG,qBAAqB,CAAC;;;AAKnE,MAAM"}
package/dist/hub.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { a as SOCK_PATH, c as log, i as RUNTIME_DIR, o as ensureDir, r as PACKAGE_VERSION, s as ensureFile, t as ASSET_DIR } from "./shared-DzXJNYhZ.mjs";
1
+ import { a as SOCK_PATH, c as log, i as RUNTIME_DIR, o as ensureDir, r as PACKAGE_VERSION, s as ensureFile, t as ASSET_DIR } from "./shared-Dx5fhN-T.mjs";
2
2
  import { createServer } from "node:net";
3
- import { dirname, join } from "node:path";
4
- import { URL as URL$1, fileURLToPath } from "node:url";
3
+ import { join } from "node:path";
4
+ import { URL as URL$1 } from "node:url";
5
5
  import { chmodSync, createReadStream, createWriteStream, existsSync, readFileSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
6
6
  import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
7
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -3237,7 +3237,7 @@ const GetCodeParametersSchema = object({
3237
3237
  resolveTokens: boolean().describe("Inline token values instead of references for quick renders; default false returns token metadata so you can map into your theming system.").optional()
3238
3238
  });
3239
3239
  const GetTokenDefsParametersSchema = object({
3240
- names: array(string().regex(/^--[a-zA-Z0-9-_]+$/)).min(1).describe("Canonical token names (CSS variable form) from get_code.usedTokens or your own list to resolve, e.g., --color-primary."),
3240
+ names: array(string().regex(/^--[a-zA-Z0-9-_]+$/)).min(1).describe("Canonical token names (CSS variable form) from Object.keys(get_code.usedTokens) or your own list to resolve, e.g., --color-primary."),
3241
3241
  includeAllModes: boolean().describe("Include all token modes (light/dark/etc.) instead of just the active one to mirror responsive tokens; default false.").optional()
3242
3242
  });
3243
3243
  const GetScreenshotParametersSchema = object({ nodeId: string().describe("Optional node id to screenshot; defaults to the current single selection. Useful when layout/overlap is uncertain (auto-layout none/inferred).").optional() });
@@ -3314,6 +3314,15 @@ function createAssetHttpServer(store) {
3314
3314
  return `http://${LOOPBACK_HOST}:${port$1}`;
3315
3315
  }
3316
3316
  function handleRequest(req, res) {
3317
+ const startedAt = Date.now();
3318
+ res.on("finish", () => {
3319
+ log.info({
3320
+ method: req.method,
3321
+ url: req.url,
3322
+ status: res.statusCode,
3323
+ durationMs: Date.now() - startedAt
3324
+ }, "HTTP asset request completed.");
3325
+ });
3317
3326
  res.setHeader("Access-Control-Allow-Origin", "*");
3318
3327
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
3319
3328
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Asset-Width, X-Asset-Height");
@@ -3323,14 +3332,12 @@ function createAssetHttpServer(store) {
3323
3332
  return;
3324
3333
  }
3325
3334
  if (!req.url) {
3326
- res.writeHead(400);
3327
- res.end("Missing URL");
3335
+ sendError(res, 400, "Missing URL");
3328
3336
  return;
3329
3337
  }
3330
3338
  const segments = new URL$1(req.url, getBaseUrl()).pathname.split("/").filter(Boolean);
3331
3339
  if (segments.length !== 2 || segments[0] !== "assets") {
3332
- res.writeHead(404);
3333
- res.end("Not Found");
3340
+ sendError(res, 404, "Not Found");
3334
3341
  return;
3335
3342
  }
3336
3343
  const hash = segments[1];
@@ -3342,14 +3349,12 @@ function createAssetHttpServer(store) {
3342
3349
  handleDownload(req, res, hash);
3343
3350
  return;
3344
3351
  }
3345
- res.writeHead(405);
3346
- res.end("Method Not Allowed");
3352
+ sendError(res, 405, "Method Not Allowed");
3347
3353
  }
3348
3354
  function handleDownload(req, res, hash) {
3349
3355
  const record = store.get(hash);
3350
3356
  if (!record) {
3351
- res.writeHead(404);
3352
- res.end("Not Found");
3357
+ sendError(res, 404, "Asset Not Found");
3353
3358
  return;
3354
3359
  }
3355
3360
  let stat;
@@ -3358,15 +3363,13 @@ function createAssetHttpServer(store) {
3358
3363
  } catch (error) {
3359
3364
  if (error.code === "ENOENT") {
3360
3365
  store.remove(hash, { removeFile: false });
3361
- res.writeHead(404);
3362
- res.end("Not Found");
3366
+ sendError(res, 404, "Asset Not Found");
3363
3367
  } else {
3364
3368
  log.error({
3365
3369
  error,
3366
3370
  hash
3367
3371
  }, "Failed to stat asset file.");
3368
- res.writeHead(500);
3369
- res.end("Internal Server Error");
3372
+ sendError(res, 500, "Internal Server Error");
3370
3373
  }
3371
3374
  return;
3372
3375
  }
@@ -3381,8 +3384,8 @@ function createAssetHttpServer(store) {
3381
3384
  error,
3382
3385
  hash
3383
3386
  }, "Failed to stream asset file.");
3384
- if (!res.headersSent) res.writeHead(500);
3385
- res.end("Internal Server Error");
3387
+ if (!res.headersSent) sendError(res, 500, "Internal Server Error");
3388
+ else res.end();
3386
3389
  });
3387
3390
  stream.on("open", () => {
3388
3391
  store.touch(hash);
@@ -3391,8 +3394,7 @@ function createAssetHttpServer(store) {
3391
3394
  }
3392
3395
  function handleUpload(req, res, hash) {
3393
3396
  if (!MCP_HASH_PATTERN.test(hash)) {
3394
- res.writeHead(400);
3395
- res.end("Invalid Hash Format");
3397
+ sendError(res, 400, "Invalid Hash Format");
3396
3398
  return;
3397
3399
  }
3398
3400
  const mimeType = req.headers["content-type"] || "application/octet-stream";
@@ -3417,8 +3419,7 @@ function createAssetHttpServer(store) {
3417
3419
  }
3418
3420
  if (changed) store.upsert(existing);
3419
3421
  store.touch(hash);
3420
- res.writeHead(200);
3421
- res.end("OK");
3422
+ sendOk(res, 200, "Asset Already Exists");
3422
3423
  return;
3423
3424
  }
3424
3425
  const tmpPath = `${filePath}.tmp.${nanoid()}`;
@@ -3446,26 +3447,22 @@ function createAssetHttpServer(store) {
3446
3447
  } }), writeStream, (err) => {
3447
3448
  if (err) {
3448
3449
  cleanup();
3449
- if (err.message === "PayloadTooLarge") {
3450
- res.writeHead(413);
3451
- res.end("Payload Too Large");
3452
- } else if (err.code === "ERR_STREAM_PREMATURE_CLOSE") log.warn({ hash }, "Upload request closed prematurely.");
3453
- else {
3450
+ if (err.message === "PayloadTooLarge") sendError(res, 413, "Payload Too Large");
3451
+ else if (err.code === "ERR_STREAM_PREMATURE_CLOSE") {
3452
+ log.warn({ hash }, "Upload request closed prematurely.");
3453
+ sendError(res, 400, "Upload Incomplete");
3454
+ } else {
3454
3455
  log.error({
3455
3456
  error: err,
3456
3457
  hash
3457
3458
  }, "Upload pipeline failed.");
3458
- if (!res.headersSent) {
3459
- res.writeHead(500);
3460
- res.end("Internal Server Error");
3461
- }
3459
+ if (!res.headersSent) sendError(res, 500, "Internal Server Error");
3462
3460
  }
3463
3461
  return;
3464
3462
  }
3465
- if (hasher.digest("hex") !== hash) {
3463
+ if (hasher.digest("hex").slice(0, MCP_HASH_HEX_LENGTH) !== hash) {
3466
3464
  cleanup();
3467
- res.writeHead(400);
3468
- res.end("Hash Mismatch");
3465
+ sendError(res, 400, "Hash Mismatch");
3469
3466
  return;
3470
3467
  }
3471
3468
  try {
@@ -3476,8 +3473,7 @@ function createAssetHttpServer(store) {
3476
3473
  hash
3477
3474
  }, "Failed to rename temp file to asset.");
3478
3475
  cleanup();
3479
- res.writeHead(500);
3480
- res.end("Internal Server Error");
3476
+ sendError(res, 500, "Internal Server Error");
3481
3477
  return;
3482
3478
  }
3483
3479
  store.upsert({
@@ -3491,10 +3487,26 @@ function createAssetHttpServer(store) {
3491
3487
  hash,
3492
3488
  size
3493
3489
  }, "Stored uploaded asset via HTTP.");
3494
- res.writeHead(201);
3495
- res.end("Created");
3490
+ sendOk(res, 201, "Created", {
3491
+ hash,
3492
+ size
3493
+ });
3496
3494
  });
3497
3495
  }
3496
+ function sendError(res, status, message, details) {
3497
+ if (!res.headersSent) res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" });
3498
+ res.end(JSON.stringify({
3499
+ error: message,
3500
+ ...details
3501
+ }));
3502
+ }
3503
+ function sendOk(res, status, message, data) {
3504
+ if (!res.headersSent) res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" });
3505
+ res.end(JSON.stringify({
3506
+ message,
3507
+ ...data
3508
+ }));
3509
+ }
3498
3510
  return {
3499
3511
  start,
3500
3512
  stop,
@@ -3662,6 +3674,10 @@ function createAssetStore(options = {}) {
3662
3674
  };
3663
3675
  }
3664
3676
 
3677
+ //#endregion
3678
+ //#region src/instructions.md?raw
3679
+ var instructions_default = "You are connected to a Figma design file via the MCP server. Convert design elements into production code, preserving design intent while fitting the user’s codebase conventions.\n\n- Start from `get_code` as the baseline, then refactor to match project conventions (components, styling system, file structure, naming).\n- Layout confidence:\n - If `get_code` contains no `data-hint-auto-layout`, it likely indicates the layout is explicit. You can be more confident implementing directly from `get_code`.\n - If any `data-hint-auto-layout` is `none` or `inferred`, the corresponding layout may be uncertain. If you feel ambiguity or uncertainty, consult `get_structure` (hierarchy + geometry) and `get_screenshot` (visual intent such as layering/overlap/masks/shadows/translucency).\n- If `data-hint-component` plus repetition supports it, extract reusable components/variants aligned with project patterns. Do not preserve hint strings in output.\n- Tokens: follow the project’s token/theming framework; if needed, use `get_code.usedToken` metadata (collection, mode) to extend a responsive token system within that framework.\n- Assets: follow the project’s existing conventions/practices (icon system, asset pipeline, import/path rules, optimization) to decide how to represent and reference assets. If `get_code` uses resource URIs, you may replace them with the project’s canonical references when appropriate without changing rendering.\n- Do not output any `data-*` attributes returned by `get_code`.\n- For SVG/vector assets: use the exact provided asset, preserving `path` data, `viewBox`, and full SVG structure. Never redraw or approximate vectors.\n";
3680
+
3665
3681
  //#endregion
3666
3682
  //#region src/request.ts
3667
3683
  const pendingCalls = /* @__PURE__ */ new Map();
@@ -3727,8 +3743,6 @@ function cleanupAll() {
3727
3743
 
3728
3744
  //#endregion
3729
3745
  //#region src/tools.ts
3730
- const HERE = dirname(fileURLToPath(import.meta.url));
3731
- const MCP_INSTRUCTIONS = readFileSync(join(HERE, "instructions.md"), "utf8");
3732
3746
  function extTool(definition) {
3733
3747
  return definition;
3734
3748
  }
@@ -3790,7 +3804,7 @@ function createCodeToolResponse(payload) {
3790
3804
  summary.push(`Generated \`${payload.lang}\` snippet (${formatBytes$1(codeSize)}).`);
3791
3805
  if (payload.message) summary.push(payload.message);
3792
3806
  summary.push(payload.assets.length ? `Assets attached: ${payload.assets.length}. Fetch bytes via resources/read using resourceUri.` : "No binary assets were attached to this response.");
3793
- const tokenCount = payload.usedTokens ? Object.keys(payload.usedTokens.tokens ?? {}).length : 0;
3807
+ const tokenCount = payload.usedTokens ? Object.keys(payload.usedTokens ?? {}).length : 0;
3794
3808
  if (tokenCount) summary.push(`Token references included: ${tokenCount}.`);
3795
3809
  summary.push("Read structuredContent for the full code string and asset metadata.");
3796
3810
  const assetLinks = payload.assets.length > 0 ? payload.assets.map((asset) => createAssetResourceLinkBlock$1(asset)) : [];
@@ -3876,7 +3890,7 @@ let selectedWsPort = 0;
3876
3890
  const mcp = new McpServer({
3877
3891
  name: "tempad-dev-mcp",
3878
3892
  version: PACKAGE_VERSION
3879
- }, MCP_INSTRUCTIONS ? { instructions: MCP_INSTRUCTIONS } : void 0);
3893
+ }, instructions_default ? { instructions: instructions_default } : void 0);
3880
3894
  function enrichToolDefinition(tool) {
3881
3895
  if (tool.target === "extension") return tool;
3882
3896
  switch (tool.name) {