@tempad-dev/mcp 0.4.3 → 0.4.4

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-Bubiv0fy.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-DAXLxrSM.mjs";
3
3
  import { spawn } from "node:child_process";
4
4
  import { connect } from "node:net";
5
5
  import { join } from "node:path";
package/dist/hub.mjs CHANGED
@@ -1,4 +1,4 @@
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-Bubiv0fy.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-DAXLxrSM.mjs";
2
2
  import { createServer } from "node:net";
3
3
  import { join } from "node:path";
4
4
  import { URL } from "node:url";
@@ -790,10 +790,7 @@ const extensions = [];
790
790
  let consumerCount = 0;
791
791
  let autoActivateTimer = null;
792
792
  let selectedWsPort = 0;
793
- const mcp = new McpServer({
794
- name: "tempad-dev-mcp",
795
- version: PACKAGE_VERSION
796
- }, instructions_default ? { instructions: instructions_default } : void 0);
793
+ const consumerSessions = /* @__PURE__ */ new Set();
797
794
  function getRecordProperty(record, key) {
798
795
  if (!record || typeof record !== "object") return;
799
796
  return Reflect.get(record, key);
@@ -890,21 +887,25 @@ function buildAssetDescriptor(record) {
890
887
  height: record.metadata?.height
891
888
  };
892
889
  }
893
- function registerTools() {
890
+ function createMcpServer() {
891
+ const mcp = new McpServer({
892
+ name: "tempad-dev-mcp",
893
+ version: PACKAGE_VERSION
894
+ }, instructions_default ? { instructions: instructions_default } : void 0);
894
895
  const registered = [];
895
896
  for (const tool of TOOL_DEFINITIONS) {
896
897
  if ("exposed" in tool && tool.exposed === false) continue;
897
- registerTool(tool);
898
+ registerTool(mcp, tool);
898
899
  registered.push(tool.name);
899
900
  }
900
901
  log.info({ tools: registered }, "Registered tools.");
902
+ return mcp;
901
903
  }
902
- registerTools();
903
- function registerTool(tool) {
904
- if (tool.target === "extension") registerProxiedTool(tool);
905
- else registerLocalTool(tool);
904
+ function registerTool(mcp, tool) {
905
+ if (tool.target === "extension") registerProxiedTool(mcp, tool);
906
+ else registerLocalTool(mcp, tool);
906
907
  }
907
- function registerProxiedTool(tool) {
908
+ function registerProxiedTool(mcp, tool) {
908
909
  const registerToolFn = mcp.registerTool.bind(mcp);
909
910
  const schema = tool.parameters;
910
911
  const handler = async (args) => {
@@ -947,7 +948,7 @@ function registerProxiedTool(tool) {
947
948
  inputSchema: schema
948
949
  }, handler);
949
950
  }
950
- function registerLocalTool(tool) {
951
+ function registerLocalTool(mcp, tool) {
951
952
  const schema = tool.parameters;
952
953
  const handler = tool.handler;
953
954
  const registerToolFn = mcp.registerTool.bind(mcp);
@@ -1064,6 +1065,12 @@ function rawDataToBuffer(raw) {
1064
1065
  }
1065
1066
  function shutdown() {
1066
1067
  log.info("Hub is shutting down...");
1068
+ consumerSessions.forEach((session) => {
1069
+ session.close().catch((err) => {
1070
+ log.warn({ err }, "Failed to close MCP session during shutdown.");
1071
+ });
1072
+ });
1073
+ consumerSessions.clear();
1067
1074
  assetStore.flush();
1068
1075
  assetHttpServer.stop();
1069
1076
  netServer.close(() => log.info("Net server closed."));
@@ -1085,11 +1092,15 @@ try {
1085
1092
  process.exit(1);
1086
1093
  }
1087
1094
  const netServer = createServer((sock) => {
1095
+ const mcp = createMcpServer();
1096
+ consumerSessions.add(mcp);
1088
1097
  consumerCount++;
1089
1098
  log.info(`Consumer connected. Total: ${consumerCount}`);
1090
1099
  const transport = new StdioServerTransport(sock, sock);
1091
1100
  mcp.connect(transport).catch((err) => {
1092
1101
  log.error({ err }, "Failed to attach MCP transport.");
1102
+ consumerSessions.delete(mcp);
1103
+ mcp.close().catch((closeErr) => log.warn({ err: closeErr }, "MCP session close failed."));
1093
1104
  transport.close().catch((closeErr) => log.warn({ err: closeErr }, "Transport close failed."));
1094
1105
  sock.destroy();
1095
1106
  });
@@ -1098,7 +1109,9 @@ const netServer = createServer((sock) => {
1098
1109
  transport.close().catch((closeErr) => log.warn({ err: closeErr }, "Transport close failed."));
1099
1110
  });
1100
1111
  sock.on("close", async () => {
1101
- await transport.close();
1112
+ await transport.close().catch((closeErr) => log.warn({ err: closeErr }, "Transport close failed."));
1113
+ await mcp.close().catch((closeErr) => log.warn({ err: closeErr }, "MCP session close failed."));
1114
+ consumerSessions.delete(mcp);
1102
1115
  consumerCount--;
1103
1116
  log.info(`Consumer disconnected. Remaining: ${consumerCount}`);
1104
1117
  if (consumerCount === 0) {
package/dist/hub.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hub.mjs","names":["createServer","getRecordProperty","MCP_INSTRUCTIONS"],"sources":["../../shared/dist/index.js","../src/asset-utils.ts","../src/config.ts","../src/asset-http-server.ts","../src/asset-store.ts","../src/instructions.md?raw","../src/request.ts","../src/tools.ts","../src/hub.ts"],"sourcesContent":["import { z } from \"zod\";\n\n//#region src/mcp/constants.ts\nconst MCP_PORT_CANDIDATES = [\n\t6220,\n\t7431,\n\t8127\n];\nconst MCP_MAX_PAYLOAD_BYTES = 4 * 1024 * 1024;\nconst MCP_TOOL_TIMEOUT_MS = 15e3;\nconst MCP_AUTO_ACTIVATE_GRACE_MS = 1500;\nconst MCP_MAX_ASSET_BYTES = 8 * 1024 * 1024;\nconst MCP_ASSET_TTL_MS = 720 * 60 * 60 * 1e3;\nconst MCP_HASH_HEX_LENGTH = 8;\nconst MCP_HASH_PATTERN = new RegExp(`^[a-f0-9]{${MCP_HASH_HEX_LENGTH}}$`, \"i\");\n\n//#endregion\n//#region src/mcp/errors.ts\nconst TEMPAD_MCP_ERROR_CODES = {\n\tNO_ACTIVE_EXTENSION: \"NO_ACTIVE_EXTENSION\",\n\tEXTENSION_TIMEOUT: \"EXTENSION_TIMEOUT\",\n\tEXTENSION_DISCONNECTED: \"EXTENSION_DISCONNECTED\",\n\tINVALID_SELECTION: \"INVALID_SELECTION\",\n\tNODE_NOT_VISIBLE: \"NODE_NOT_VISIBLE\",\n\tASSET_SERVER_NOT_CONFIGURED: \"ASSET_SERVER_NOT_CONFIGURED\",\n\tTRANSPORT_NOT_CONNECTED: \"TRANSPORT_NOT_CONNECTED\"\n};\n\n//#endregion\n//#region src/mcp/protocol.ts\nconst RegisteredMessageSchema = z.object({\n\ttype: z.literal(\"registered\"),\n\tid: z.string()\n});\nconst StateMessageSchema = z.object({\n\ttype: z.literal(\"state\"),\n\tactiveId: z.string().nullable(),\n\tcount: z.number().nonnegative(),\n\tport: z.number().positive(),\n\tassetServerUrl: z.string().url()\n});\nconst ToolCallPayloadSchema = z.object({\n\tname: z.string(),\n\targs: z.unknown()\n});\nconst ToolCallMessageSchema = z.object({\n\ttype: z.literal(\"toolCall\"),\n\tid: z.string(),\n\tpayload: ToolCallPayloadSchema\n});\nconst MessageToExtensionSchema = z.discriminatedUnion(\"type\", [\n\tRegisteredMessageSchema,\n\tStateMessageSchema,\n\tToolCallMessageSchema\n]);\nconst ActivateMessageSchema = z.object({ type: z.literal(\"activate\") });\nconst ToolResultMessageSchema = z.object({\n\ttype: z.literal(\"toolResult\"),\n\tid: z.string(),\n\tpayload: z.unknown().optional(),\n\terror: z.unknown().optional()\n});\nconst MessageFromExtensionSchema = z.discriminatedUnion(\"type\", [ActivateMessageSchema, ToolResultMessageSchema]);\nfunction parseJsonWithSchema(data, schema) {\n\tlet parsed;\n\ttry {\n\t\tparsed = JSON.parse(data);\n\t} catch {\n\t\treturn null;\n\t}\n\tconst result = schema.safeParse(parsed);\n\treturn result.success ? result.data : null;\n}\nfunction parseMessageToExtension(data) {\n\treturn parseJsonWithSchema(data, MessageToExtensionSchema);\n}\nfunction parseMessageFromExtension(data) {\n\treturn parseJsonWithSchema(data, MessageFromExtensionSchema);\n}\n\n//#endregion\n//#region src/mcp/tools.ts\nconst AssetDescriptorSchema = z.object({\n\thash: z.string().min(1),\n\turl: z.string().url(),\n\tmimeType: z.string().min(1),\n\tsize: z.number().int().nonnegative(),\n\twidth: z.number().int().positive().optional(),\n\theight: z.number().int().positive().optional()\n});\nconst GetCodeParametersSchema = z.object({\n\tnodeId: z.string().describe(\"Optional target node id; omit to use the current single selection when pulling the baseline snapshot.\").optional(),\n\tpreferredLang: z.enum([\"jsx\", \"vue\"]).describe(\"Preferred output language to bias the snapshot; otherwise uses the design’s hint/detected language, then falls back to JSX.\").optional(),\n\tresolveTokens: z.boolean().describe(\"Inline token values instead of references for quick renders; default false returns token metadata so you can map into your theming system. When true, values are resolved per-node (mode-aware).\").optional()\n});\nconst GetTokenDefsParametersSchema = z.object({\n\tnames: z.array(z.string().regex(/^--[a-zA-Z0-9-_]+$/)).min(1).describe(\"Canonical token names (CSS variable form) from Object.keys(get_code.tokens) or your own list to resolve, e.g., --color-primary.\"),\n\tincludeAllModes: z.boolean().describe(\"Include all token modes (light/dark/etc.) instead of just the active one to mirror responsive tokens; default false.\").optional()\n});\nconst GetScreenshotParametersSchema = z.object({ nodeId: z.string().describe(\"Optional node id to screenshot; defaults to the current single selection. Useful when layout/overlap is uncertain (auto-layout none/inferred).\").optional() });\nconst GetStructureParametersSchema = z.object({\n\tnodeId: z.string().describe(\"Optional node id to outline; defaults to the current single selection. Useful when auto-layout hints are none/inferred or you need explicit geometry for refactors.\").optional(),\n\toptions: z.object({ depth: z.number().int().positive().describe(\"Limit traversal depth; defaults to full tree (subject to safety caps).\").optional() }).optional()\n});\nconst GetAssetsParametersSchema = z.object({ hashes: z.array(z.string().regex(MCP_HASH_PATTERN)).min(1).describe(\"Asset hashes returned from get_code (or other tools) to download/resolve exact bytes for rasterized images or SVGs before routing through your asset pipeline.\") });\nconst GetAssetsResultSchema = z.object({\n\tassets: z.array(AssetDescriptorSchema),\n\tmissing: z.array(z.string().min(1))\n});\n\n//#endregion\n//#region src/figma/color.ts\n/**\n* Color utilities for Figma styles\n*/\n/**\n* Formats a Figma color with opacity to hex notation\n* @param color RGB color object with values 0-1\n* @param opacity Optional opacity value 0-1\n* @returns Hex color string (e.g., \"#FF0000\" or \"#FF0000CC\")\n*/\nfunction formatHexAlpha(color, opacity = 1) {\n\tconst toHex = (n) => {\n\t\treturn Math.min(255, Math.max(0, Math.round(n * 255))).toString(16).padStart(2, \"0\").toUpperCase();\n\t};\n\tconst r = toHex(color.r);\n\tconst g = toHex(color.g);\n\tconst b = toHex(color.b);\n\tif (opacity >= .99) {\n\t\tif (r[0] === r[1] && g[0] === g[1] && b[0] === b[1]) return `#${r[0]}${g[0]}${b[0]}`;\n\t\treturn `#${r}${g}${b}`;\n\t}\n\tconst a = toHex(opacity);\n\tif (r[0] === r[1] && g[0] === g[1] && b[0] === b[1] && a[0] === a[1]) return `#${r[0]}${g[0]}${b[0]}${a[0]}`;\n\treturn `#${r}${g}${b}${a}`;\n}\n\n//#endregion\n//#region src/figma/gradient.ts\nconst RE_NON_ASCII = /\\P{ASCII}+/gu;\nconst RE_QUOTES = /['\"]/g;\nconst RE_SLASH = /\\//g;\nconst RE_SPACE_TAB = /[ \\t]+/g;\nconst RE_WHITESPACE = /\\s+/g;\nconst RE_FAST_PATH = /^[A-Za-z0-9_-]+$/;\nconst RE_BOUND_NON_ALPHANUM = /[^A-Za-z0-9]+/g;\nconst RE_HYPHENS = /-+/g;\nconst RE_BOUND_DIGIT = /([A-Za-z])([0-9])|([0-9])([A-Za-z])/g;\nconst RE_BOUND_CASE = /([a-z])([A-Z])|([A-Z])([A-Z][a-z])/g;\nconst RE_DIGIT = /^\\d+$/;\nconst RE_CAPS = /^[A-Z]+$/;\nconst RE_SINGLE = /^[A-Za-z]$/;\nfunction isVisiblePaint(paint) {\n\treturn !!paint && paint.visible !== false;\n}\nfunction isGradientPaint(paint) {\n\treturn \"gradientStops\" in paint && Array.isArray(paint.gradientStops);\n}\nfunction isSolidPaint(paint) {\n\treturn paint.type === \"SOLID\";\n}\nfunction hasGradientHandlePositions(paint) {\n\treturn \"gradientHandlePositions\" in paint && Array.isArray(paint.gradientHandlePositions);\n}\n/**\n* Resolves gradient from paint array\n* Returns CSS gradient string or null\n*/\nfunction resolveGradientFromPaints(paints, size) {\n\tif (!paints || !Array.isArray(paints)) return null;\n\tconst gradientPaint = paints.find((paint) => isVisiblePaint(paint) && isGradientPaint(paint));\n\tif (!gradientPaint) return null;\n\tconst fillOpacity = typeof gradientPaint.opacity === \"number\" ? gradientPaint.opacity : 1;\n\tconst stops = gradientPaint.gradientStops.map((stop) => {\n\t\tconst pct = formatPercent(stop.position);\n\t\treturn `${formatGradientStopColor(stop, fillOpacity)} ${pct}`;\n\t});\n\tswitch (gradientPaint.type) {\n\t\tcase \"GRADIENT_LINEAR\": {\n\t\t\tconst angle = resolveLinearGradientAngle(gradientPaint, size);\n\t\t\treturn `linear-gradient(${(angle == null ? stops : [`${angle}deg`, ...stops]).join(\", \")})`;\n\t\t}\n\t\tcase \"GRADIENT_RADIAL\":\n\t\tcase \"GRADIENT_DIAMOND\": return `radial-gradient(${stops.join(\", \")})`;\n\t\tcase \"GRADIENT_ANGULAR\": return `conic-gradient(${stops.join(\", \")})`;\n\t\tdefault: return null;\n\t}\n}\n/**\n* Resolves solid color from paint array\n* Returns hex color string or null\n*/\nfunction resolveSolidFromPaints(paints) {\n\tif (!paints || !Array.isArray(paints)) return null;\n\tconst solidPaint = paints.find((paint) => isVisiblePaint(paint) && isSolidPaint(paint));\n\tif (!solidPaint || !solidPaint.color) return null;\n\tconst bound = solidPaint.boundVariables?.color;\n\tif (bound && typeof bound === \"object\" && \"id\" in bound && bound.id) try {\n\t\tconst variable = figma.variables.getVariableById(bound.id);\n\t\tif (variable) {\n\t\t\tconst fallback = formatHexAlpha(solidPaint.color, solidPaint.opacity);\n\t\t\treturn `var(${getVariableCssCustomPropertyName(variable)}, ${fallback})`;\n\t\t}\n\t} catch {}\n\treturn formatHexAlpha(solidPaint.color, solidPaint.opacity);\n}\n/**\n* Formats a gradient stop color\n*/\nfunction formatGradientStopColor(stop, fillOpacity) {\n\tconst baseAlpha = stop.color?.a ?? 1;\n\tconst alpha = Math.max(0, Math.min(1, baseAlpha * fillOpacity));\n\tconst bound = stop.boundVariables?.color;\n\tif (bound && typeof bound === \"object\" && \"id\" in bound && bound.id) try {\n\t\tconst v = figma.variables.getVariableById(bound.id);\n\t\tif (v) {\n\t\t\tconst fallbackOpaque = formatHexAlpha(stop.color, 1);\n\t\t\tconst fallbackAlpha = formatHexAlpha(stop.color, alpha);\n\t\t\tconst cssVarName = getVariableCssCustomPropertyName(v);\n\t\t\tconst varName = `var(${cssVarName}, ${fallbackOpaque})`;\n\t\t\tif (alpha >= .99) return `var(${cssVarName}, ${fallbackAlpha})`;\n\t\t\treturn `color-mix(in srgb, ${varName} ${Math.round(alpha * 1e4) / 100}%, transparent)`;\n\t\t}\n\t} catch {}\n\treturn formatHexAlpha(stop.color, alpha);\n}\n/**\n* Resolves linear gradient angle from gradient paint\n*/\nfunction resolveLinearGradientAngle(paint, size) {\n\tif (hasGradientHandlePositions(paint) && paint.gradientHandlePositions.length >= 2) {\n\t\tconst start = paint.gradientHandlePositions[0];\n\t\tconst end = paint.gradientHandlePositions[1];\n\t\tif (start && end) {\n\t\t\tconst { width, height } = getGradientSize(size);\n\t\t\tconst angle = normalizeGradientAngle((end.x - start.x) * width, (end.y - start.y) * height);\n\t\t\tif (angle != null) return angle;\n\t\t}\n\t}\n\tconst extracted = extractLinearGradientVectorFromTransform(paint.gradientTransform, size);\n\tif (!extracted) return null;\n\tconst { dx, dy } = extracted;\n\treturn normalizeGradientAngle(dx, dy);\n}\nfunction extractLinearGradientVectorFromTransform(transform, size) {\n\tif (!transform || !Array.isArray(transform) || transform.length < 2) return null;\n\tconst row0 = transform[0];\n\tconst row1 = transform[1];\n\tif (!Array.isArray(row0) || !Array.isArray(row1) || row0.length < 2 || row1.length < 2) return null;\n\tconst a = row0[0];\n\tconst c = row0[1];\n\tconst e = row0[2] ?? 0;\n\tconst b = row1[0];\n\tconst d = row1[1];\n\tconst f = row1[2] ?? 0;\n\tif (![\n\t\ta,\n\t\tb,\n\t\tc,\n\t\td,\n\t\te,\n\t\tf\n\t].every((value) => Number.isFinite(value))) return null;\n\tconst det = a * d - b * c;\n\tif (!Number.isFinite(det) || Math.abs(det) < 1e-8) return null;\n\tconst invA = d / det;\n\tconst invC = -c / det;\n\tconst invE = (c * f - d * e) / det;\n\tconst invB = -b / det;\n\tconst invD = a / det;\n\tconst invF = (b * e - a * f) / det;\n\tconst start = applyTransform(invA, invC, invE, invB, invD, invF, 0, .5);\n\tconst end = applyTransform(invA, invC, invE, invB, invD, invF, 1, .5);\n\tconst { width, height } = getGradientSize(size);\n\treturn {\n\t\tdx: (end.x - start.x) * width,\n\t\tdy: (end.y - start.y) * height\n\t};\n}\nfunction applyTransform(a, c, e, b, d, f, x, y) {\n\treturn {\n\t\tx: a * x + c * y + e,\n\t\ty: b * x + d * y + f\n\t};\n}\nfunction getGradientSize(size) {\n\tconst width = size?.width;\n\tconst height = size?.height;\n\treturn {\n\t\twidth: typeof width === \"number\" && Number.isFinite(width) && width > 0 ? width : 1,\n\t\theight: typeof height === \"number\" && Number.isFinite(height) && height > 0 ? height : 1\n\t};\n}\n/**\n* Normalizes gradient angle to degrees\n*/\nfunction normalizeGradientAngle(dx, dy) {\n\tif (!Number.isFinite(dx) || !Number.isFinite(dy)) return null;\n\tif (dx === 0 && dy === 0) return null;\n\tlet angle = Math.atan2(dy, dx) * 180 / Math.PI + 90;\n\tangle = (angle % 360 + 360) % 360;\n\treturn Math.round(angle * 100) / 100;\n}\n/**\n* Formats position as percentage\n*/\nfunction formatPercent(pos) {\n\treturn `${Math.round(pos * 1e4) / 100}%`;\n}\n/**\n* Variable naming must match MCP token indexing semantics exactly.\n*/\nfunction getVariableCssCustomPropertyName(variable) {\n\treturn normalizeFigmaVarName(getVariableRawName(variable));\n}\nfunction getVariableRawName(variable) {\n\tconst cs = variable.codeSyntax?.WEB;\n\tif (typeof cs === \"string\" && cs.trim()) {\n\t\tconst canonical = canonicalizeVarName(cs.trim());\n\t\tif (canonical) return canonical.slice(2);\n\t\tconst ident = cs.trim();\n\t\tif (/^[A-Za-z0-9_-]+$/.test(ident)) return ident;\n\t}\n\tconst raw = variable.name?.trim?.() ?? \"\";\n\tif (raw.startsWith(\"--\")) return raw.slice(2);\n\treturn raw;\n}\nfunction canonicalizeVarName(value) {\n\tconst cleaned = value.trim();\n\tconst varMatch = cleaned.match(/^var\\(\\s*(--[A-Za-z0-9_-]+)(?:\\s*,[\\s\\S]*)?\\)$/);\n\tif (varMatch?.[1]) return normalizeCustomPropertyName(varMatch[1]);\n\tif (cleaned.startsWith(\"--\")) return normalizeCustomPropertyName(cleaned);\n\treturn null;\n}\nfunction normalizeCustomPropertyBody(name) {\n\tif (!name) return \"var\";\n\tlet raw = name.trim();\n\tif (raw.startsWith(\"--\")) raw = raw.slice(2);\n\traw = raw.replace(/^-+/, \"\");\n\traw = raw.replace(/[^A-Za-z0-9_-]/g, \"\");\n\treturn raw || \"var\";\n}\nfunction normalizeCustomPropertyName(name) {\n\treturn `--${normalizeCustomPropertyBody(name)}`;\n}\nfunction normalizeFigmaVarName(input) {\n\tlet raw = (input ?? \"\").trim();\n\tif (!raw) return \"--unnamed\";\n\tconst canonical = canonicalizeVarName(raw);\n\tif (canonical) return canonical;\n\tif (raw.startsWith(\"--\")) raw = raw.slice(2).trim();\n\traw = raw.replace(RE_NON_ASCII, \"\").replace(RE_QUOTES, \"\").replace(RE_SLASH, \"-\").replace(RE_SPACE_TAB, \"-\").replace(RE_WHITESPACE, \"\");\n\tif (RE_FAST_PATH.test(raw)) return `--${raw}`;\n\tconst parts = raw.replace(RE_BOUND_NON_ALPHANUM, \"-\").replace(RE_HYPHENS, \"-\").replace(RE_BOUND_DIGIT, \"$1-$2$3-$4\").replace(RE_BOUND_CASE, \"$1$3-$2$4\").split(\"-\").filter(Boolean);\n\tconst stack = [];\n\tfor (const part of parts) {\n\t\tconst prev = stack[stack.length - 1];\n\t\tif (prev && RE_DIGIT.test(prev) && RE_DIGIT.test(part)) stack[stack.length - 1] += part;\n\t\telse if (prev && RE_CAPS.test(prev) && RE_CAPS.test(part)) stack[stack.length - 1] += part;\n\t\telse stack.push(part);\n\t}\n\tconst merged = [];\n\tfor (let i = 0; i < stack.length;) {\n\t\tif (i === 0) {\n\t\t\tmerged.push(stack[0]);\n\t\t\ti += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (RE_SINGLE.test(stack[i])) {\n\t\t\tlet j = i + 1;\n\t\t\twhile (j < stack.length && RE_SINGLE.test(stack[j])) j += 1;\n\t\t\tconst run = stack.slice(i, j);\n\t\t\tmerged.push(run.length >= 2 ? run.join(\"\") : stack[i]);\n\t\t\ti = j;\n\t\t\tcontinue;\n\t\t}\n\t\tmerged.push(stack[i]);\n\t\ti += 1;\n\t}\n\tconst out = merged.join(\"-\").toLowerCase();\n\treturn out ? `--${out}` : \"--unnamed\";\n}\n\n//#endregion\n//#region src/figma/style-resolver.ts\nconst BG_URL_LIGHTGRAY_RE = /url\\(.*?\\)\\s+lightgray/i;\nconst BG_URL_RE = /url\\(/i;\nfunction hasStyleId(value) {\n\treturn typeof value === \"string\" && value.length > 0;\n}\nfunction isPaintStyle(style) {\n\treturn !!style && \"paints\" in style && Array.isArray(style.paints);\n}\nfunction resolvePaintStyleFromPaints(paints, size) {\n\tif (!paints) return null;\n\tconst gradient = resolveGradientFromPaints(paints, size);\n\tif (gradient) return { gradient };\n\tconst solidColor = resolveSolidFromPaints(paints);\n\treturn solidColor ? { solidColor } : null;\n}\nfunction resolvePaintStyleFromStyleId(styleId, kind, size) {\n\tif (!hasStyleId(styleId)) return null;\n\ttry {\n\t\tconst style = figma.getStyleById(styleId);\n\t\tif (!isPaintStyle(style)) return null;\n\t\treturn resolvePaintStyleFromPaints(style.paints, size);\n\t} catch (error) {\n\t\tconsole.warn(`Failed to resolve ${kind} style:`, error);\n\t\treturn null;\n\t}\n}\nfunction getNodeFillStyleId(node) {\n\treturn \"fillStyleId\" in node ? node.fillStyleId : null;\n}\nfunction getNodeStrokeStyleId(node) {\n\treturn \"strokeStyleId\" in node ? node.strokeStyleId : null;\n}\nfunction getNodeFillPaints(node) {\n\tif (\"fills\" in node && Array.isArray(node.fills)) return node.fills;\n\treturn null;\n}\nfunction getNodeStrokePaints(node) {\n\tif (\"strokes\" in node && Array.isArray(node.strokes)) return node.strokes;\n\treturn null;\n}\nfunction resolveNodePaintStyle(styleId, paints, kind, size) {\n\treturn resolvePaintStyleFromStyleId(styleId, kind, size) ?? resolvePaintStyleFromPaints(paints, size);\n}\nfunction getNodeDimensions(node) {\n\tif (!(\"width\" in node) || !(\"height\" in node)) return void 0;\n\tconst width = node.width;\n\tconst height = node.height;\n\tif (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return;\n\treturn {\n\t\twidth,\n\t\theight\n\t};\n}\nfunction splitByTopLevelWhitespace$1(input) {\n\tconst out = [];\n\tlet depth = 0;\n\tlet quote = null;\n\tlet buffer = \"\";\n\tfor (let i = 0; i < input.length; i++) {\n\t\tconst ch = input[i];\n\t\tif (quote) {\n\t\t\tif (ch === \"\\\\\") {\n\t\t\t\tbuffer += ch;\n\t\t\t\ti++;\n\t\t\t\tif (i < input.length) buffer += input[i];\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ch === quote) quote = null;\n\t\t\tbuffer += ch;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \"\\\"\" || ch === \"'\") {\n\t\t\tquote = ch;\n\t\t\tbuffer += ch;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \"(\") depth++;\n\t\telse if (ch === \")\") depth = Math.max(0, depth - 1);\n\t\tif (/\\s/.test(ch) && depth === 0) {\n\t\t\tif (buffer) {\n\t\t\t\tout.push(buffer);\n\t\t\t\tbuffer = \"\";\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tbuffer += ch;\n\t}\n\tif (buffer) out.push(buffer);\n\treturn out;\n}\nfunction isVarFunctionToken(value) {\n\tconst trimmed = value.trim();\n\treturn trimmed.startsWith(\"var(\") && trimmed.endsWith(\")\");\n}\nfunction patchBorderVarColor$1(borderValue, color) {\n\tconst borderParts = splitByTopLevelWhitespace$1(borderValue);\n\tif (!borderParts.length) return null;\n\tconst lastIndex = borderParts.length - 1;\n\tif (!isVarFunctionToken(borderParts[lastIndex])) return null;\n\tborderParts[lastIndex] = color;\n\treturn borderParts.join(\" \");\n}\nfunction hasBorderChannels(style) {\n\treturn Object.entries(style).some(([key, value]) => {\n\t\tif (!value?.trim()) return false;\n\t\tif (!/^border(?:$|-)/.test(key)) return false;\n\t\tif (key.includes(\"radius\") || key === \"border-image\" || key === \"border-image-slice\") return false;\n\t\treturn true;\n\t});\n}\n/**\n* Resolves fill style for a Figma node\n* Handles both fillStyleId and direct fills\n*/\nfunction resolveFillStyleForNode(node) {\n\treturn resolveNodePaintStyle(getNodeFillStyleId(node), getNodeFillPaints(node), \"fill\", getNodeDimensions(node));\n}\n/**\n* Resolves stroke style for a Figma node\n* Handles both strokeStyleId and direct strokes\n*/\nfunction resolveStrokeStyleForNode(node) {\n\treturn resolveNodePaintStyle(getNodeStrokeStyleId(node), getNodeStrokePaints(node), \"stroke\", getNodeDimensions(node));\n}\n/**\n* Main function to resolve all styles from a node\n* Replaces CSS variable references with actual values\n*/\nasync function resolveStylesFromNode(cssStyles, node) {\n\tconst processed = { ...cssStyles };\n\tconst fillPaints = getNodeFillPaints(node);\n\tif (processed.background && BG_URL_LIGHTGRAY_RE.test(processed.background) && fillPaints) {\n\t\tconst solidFill = resolveSolidFromPaints(fillPaints);\n\t\tif (solidFill) processed[\"background-color\"] = solidFill;\n\t\tprocessed.background = processed.background.replace(/\\s*,?\\s*lightgray\\b/i, \"\").trim();\n\t}\n\tconst resolvedFill = resolveFillStyleForNode(node);\n\tconst hasUrlBackground = typeof processed.background === \"string\" && BG_URL_RE.test(processed.background);\n\tif (resolvedFill?.gradient) {\n\t\tif (processed.background && !hasUrlBackground) processed.background = resolvedFill.gradient;\n\t\telse if (processed[\"background-color\"]) {\n\t\t\tprocessed.background = resolvedFill.gradient;\n\t\t\tdelete processed[\"background-color\"];\n\t\t}\n\t} else if (resolvedFill?.solidColor) {\n\t\tif (processed.background && !hasUrlBackground) {\n\t\t\tprocessed[\"background-color\"] = resolvedFill.solidColor;\n\t\t\tdelete processed.background;\n\t\t}\n\t\tif (processed[\"background-color\"]) processed[\"background-color\"] = resolvedFill.solidColor;\n\t\tif (processed.color) processed.color = resolvedFill.solidColor;\n\t\tif (processed.fill) processed.fill = resolvedFill.solidColor;\n\t}\n\tconst resolvedStroke = resolveStrokeStyleForNode(node);\n\tif (resolvedStroke?.gradient && hasBorderChannels(processed)) {\n\t\tprocessed[\"border-image\"] = resolvedStroke.gradient;\n\t\tprocessed[\"border-image-slice\"] = \"1\";\n\t} else if (resolvedStroke?.solidColor) {\n\t\tif (processed[\"border-color\"]) processed[\"border-color\"] = resolvedStroke.solidColor;\n\t\telse if (processed.border) {\n\t\t\tconst patched = patchBorderVarColor$1(processed.border, resolvedStroke.solidColor);\n\t\t\tif (patched) processed.border = patched;\n\t\t}\n\t}\n\tif (processed.stroke && resolvedStroke?.gradient) processed.stroke = resolvedStroke.gradient;\n\telse if (processed.stroke && resolvedStroke?.solidColor) processed.stroke = resolvedStroke.solidColor;\n\treturn processed;\n}\n\n//#endregion\n//#region src/figma/stroke.ts\nfunction splitByTopLevelWhitespace(input) {\n\tconst out = [];\n\tlet depth = 0;\n\tlet quote = null;\n\tlet buffer = \"\";\n\tfor (let i = 0; i < input.length; i++) {\n\t\tconst ch = input[i];\n\t\tif (quote) {\n\t\t\tif (ch === \"\\\\\") {\n\t\t\t\tbuffer += ch;\n\t\t\t\ti++;\n\t\t\t\tif (i < input.length) buffer += input[i];\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ch === quote) quote = null;\n\t\t\tbuffer += ch;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \"\\\"\" || ch === \"'\") {\n\t\t\tquote = ch;\n\t\t\tbuffer += ch;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \"(\") depth++;\n\t\telse if (ch === \")\") depth = Math.max(0, depth - 1);\n\t\tif (/\\s/.test(ch) && depth === 0) {\n\t\t\tif (buffer) {\n\t\t\t\tout.push(buffer);\n\t\t\t\tbuffer = \"\";\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tbuffer += ch;\n\t}\n\tif (buffer) out.push(buffer);\n\treturn out;\n}\nfunction patchBorderVarColor(borderValue, color) {\n\tconst borderParts = splitByTopLevelWhitespace(borderValue);\n\tif (!borderParts.length) return null;\n\tconst lastIndex = borderParts.length - 1;\n\tconst tail = borderParts[lastIndex].trim();\n\tif (!tail.startsWith(\"var(\") || !tail.endsWith(\")\")) return null;\n\tborderParts[lastIndex] = color;\n\treturn borderParts.join(\" \");\n}\n/**\n* Resolves stroke styles from paint array\n* Can handle both solid colors and gradients\n* @param paints Array of paint objects from strokes or stroke style\n* @returns Object with solidColor or gradient, or null\n*/\nfunction resolveStrokeFromPaints(paints) {\n\tif (!paints || !Array.isArray(paints)) return null;\n\tconst gradient = resolveGradientFromPaints(paints);\n\tif (gradient) return { gradient };\n\tconst solidColor = resolveSolidFromPaints(paints);\n\tif (solidColor) return { solidColor };\n\treturn null;\n}\n/**\n* Applies resolved stroke styles to CSS properties\n* Handles different CSS properties for stroke (border, stroke, outline)\n*/\nfunction applyStrokeToCSS(styles, resolved) {\n\tif (!resolved) return styles;\n\tconst processed = { ...styles };\n\tconst borderHasVar = processed.border?.includes(\"var(--\") ?? false;\n\tconst borderColorHasVar = processed[\"border-color\"]?.includes(\"var(--\") ?? false;\n\tif (borderHasVar || borderColorHasVar) {\n\t\tif (resolved.gradient) {\n\t\t\tprocessed[\"border-image\"] = `${resolved.gradient} 1`;\n\t\t\tprocessed[\"border-image-slice\"] = \"1\";\n\t\t\tdelete processed[\"border-color\"];\n\t\t} else if (resolved.solidColor) if (borderColorHasVar) processed[\"border-color\"] = resolved.solidColor;\n\t\telse {\n\t\t\tconst patched = patchBorderVarColor(processed.border, resolved.solidColor);\n\t\t\tif (patched) processed.border = patched;\n\t\t}\n\t}\n\tif (processed.stroke?.includes(\"var(--\")) {\n\t\tif (resolved.gradient) processed.stroke = resolved.gradient;\n\t\telse if (resolved.solidColor) processed.stroke = resolved.solidColor;\n\t}\n\tif (processed[\"outline-color\"]?.includes(\"var(--\")) {\n\t\tif (resolved.solidColor) processed[\"outline-color\"] = resolved.solidColor;\n\t}\n\treturn processed;\n}\n\n//#endregion\nexport { ActivateMessageSchema, AssetDescriptorSchema, GetAssetsParametersSchema, GetAssetsResultSchema, GetCodeParametersSchema, GetScreenshotParametersSchema, GetStructureParametersSchema, GetTokenDefsParametersSchema, MCP_ASSET_TTL_MS, MCP_AUTO_ACTIVATE_GRACE_MS, MCP_HASH_HEX_LENGTH, MCP_HASH_PATTERN, MCP_MAX_ASSET_BYTES, MCP_MAX_PAYLOAD_BYTES, MCP_PORT_CANDIDATES, MCP_TOOL_TIMEOUT_MS, MessageFromExtensionSchema, MessageToExtensionSchema, RegisteredMessageSchema, StateMessageSchema, TEMPAD_MCP_ERROR_CODES, ToolCallMessageSchema, ToolCallPayloadSchema, ToolResultMessageSchema, applyStrokeToCSS, formatHexAlpha, parseMessageFromExtension, parseMessageToExtension, resolveFillStyleForNode, resolveGradientFromPaints, resolveSolidFromPaints, resolveStrokeFromPaints, resolveStrokeStyleForNode, resolveStylesFromNode };","import { MCP_HASH_HEX_LENGTH } from '@tempad-dev/shared'\n\nconst HASH_FILENAME_PATTERN = new RegExp(\n `^([a-f0-9]{${MCP_HASH_HEX_LENGTH}})(?:\\\\.[a-z0-9]+)?$`,\n 'i'\n)\n\nconst MIME_EXTENSION_OVERRIDES = new Map<string, string>([['image/jpeg', 'jpg']])\n\nexport function normalizeMimeType(mimeType: string | undefined): string {\n if (!mimeType) return 'application/octet-stream'\n const [normalized] = mimeType.split(';', 1)\n return (normalized || 'application/octet-stream').trim().toLowerCase()\n}\n\nexport function getImageExtension(mimeType: string): string {\n const normalized = normalizeMimeType(mimeType)\n if (!normalized.startsWith('image/')) return ''\n const override = MIME_EXTENSION_OVERRIDES.get(normalized)\n if (override) return `.${override}`\n const subtype = normalized.slice('image/'.length)\n if (!subtype) return ''\n const ext = subtype.split('+', 1)[0] || subtype\n return `.${ext}`\n}\n\nexport function buildAssetFilename(hash: string, mimeType: string): string {\n const ext = getImageExtension(mimeType)\n return ext ? `${hash}${ext}` : hash\n}\n\nexport function getHashFromAssetFilename(filename: string): string | null {\n const match = HASH_FILENAME_PATTERN.exec(filename)\n return match ? match[1] : null\n}\n","import {\n MCP_AUTO_ACTIVATE_GRACE_MS,\n MCP_ASSET_TTL_MS,\n MCP_MAX_ASSET_BYTES,\n MCP_MAX_PAYLOAD_BYTES,\n MCP_PORT_CANDIDATES,\n MCP_TOOL_TIMEOUT_MS\n} from '@tempad-dev/shared'\n\nfunction parsePositiveInt(envValue: string | undefined, fallback: number): number {\n const parsed = envValue ? Number.parseInt(envValue, 10) : Number.NaN\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback\n}\n\nfunction parseNonNegativeInt(envValue: string | undefined, fallback: number): number {\n const parsed = envValue ? Number.parseInt(envValue, 10) : Number.NaN\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback\n}\n\nfunction resolveToolTimeoutMs(): number {\n return parsePositiveInt(process.env.TEMPAD_MCP_TOOL_TIMEOUT, MCP_TOOL_TIMEOUT_MS)\n}\n\nfunction resolveAutoActivateGraceMs(): number {\n return parsePositiveInt(process.env.TEMPAD_MCP_AUTO_ACTIVATE_GRACE, MCP_AUTO_ACTIVATE_GRACE_MS)\n}\n\nfunction resolveMaxAssetSizeBytes(): number {\n return parsePositiveInt(process.env.TEMPAD_MCP_MAX_ASSET_BYTES, MCP_MAX_ASSET_BYTES)\n}\n\nfunction resolveAssetTtlMs(): number {\n return parseNonNegativeInt(process.env.TEMPAD_MCP_ASSET_TTL_MS, MCP_ASSET_TTL_MS)\n}\n\nexport function getMcpServerConfig() {\n return {\n wsPortCandidates: [...MCP_PORT_CANDIDATES],\n toolTimeoutMs: resolveToolTimeoutMs(),\n maxPayloadBytes: MCP_MAX_PAYLOAD_BYTES,\n autoActivateGraceMs: resolveAutoActivateGraceMs(),\n maxAssetSizeBytes: resolveMaxAssetSizeBytes(),\n assetTtlMs: resolveAssetTtlMs()\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\n\nimport { MCP_HASH_HEX_LENGTH } from '@tempad-dev/shared'\nimport { nanoid } from 'nanoid'\nimport { createHash } from 'node:crypto'\nimport {\n createReadStream,\n createWriteStream,\n existsSync,\n renameSync,\n statSync,\n unlinkSync\n} from 'node:fs'\nimport { createServer } from 'node:http'\nimport { join } from 'node:path'\nimport { pipeline, Transform } from 'node:stream'\nimport { URL } from 'node:url'\n\nimport type { AssetStore } from './asset-store'\n\nimport { buildAssetFilename, getHashFromAssetFilename, normalizeMimeType } from './asset-utils'\nimport { getMcpServerConfig } from './config'\nimport { ASSET_DIR, log } from './shared'\n\nconst LOOPBACK_HOST = '127.0.0.1'\nconst HASH_HEX_PATTERN = new RegExp(`^[a-f0-9]{${MCP_HASH_HEX_LENGTH}}$`, 'i')\nconst { maxAssetSizeBytes } = getMcpServerConfig()\n\nexport interface AssetHttpServer {\n start(): Promise<void>\n stop(): void\n getBaseUrl(): string\n}\n\nexport function createAssetHttpServer(store: AssetStore): AssetHttpServer {\n const server = createServer(handleRequest)\n let port: number | null = null\n\n async function start(): Promise<void> {\n if (port !== null) return\n await new Promise<void>((resolve, reject) => {\n const onError = (error: Error) => {\n server.off('listening', onListening)\n reject(error)\n }\n const onListening = () => {\n server.off('error', onError)\n const address = server.address()\n if (address && typeof address === 'object') {\n port = address.port\n resolve()\n } else {\n reject(new Error('Failed to determine HTTP server port.'))\n }\n }\n server.once('error', onError)\n server.once('listening', onListening)\n server.listen(0, LOOPBACK_HOST)\n })\n log.info({ port }, 'Asset HTTP server ready.')\n }\n\n function stop(): void {\n if (port === null) return\n server.close()\n port = null\n }\n\n function getBaseUrl(): string {\n if (port === null) throw new Error('Asset HTTP server is not running.')\n return `http://${LOOPBACK_HOST}:${port}`\n }\n\n function handleRequest(req: IncomingMessage, res: ServerResponse): void {\n const startedAt = Date.now()\n res.on('finish', () => {\n log.info(\n {\n method: req.method,\n url: req.url,\n status: res.statusCode,\n durationMs: Date.now() - startedAt\n },\n 'HTTP asset request completed.'\n )\n })\n\n res.setHeader('Access-Control-Allow-Origin', '*')\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Asset-Width, X-Asset-Height')\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204)\n res.end()\n return\n }\n\n if (!req.url) {\n sendError(res, 400, 'Missing URL')\n return\n }\n\n const url = new URL(req.url, getBaseUrl())\n const segments = url.pathname.split('/').filter(Boolean)\n if (segments.length !== 2 || segments[0] !== 'assets') {\n sendError(res, 404, 'Not Found')\n return\n }\n\n const filename = segments[1]\n const hash = getHashFromAssetFilename(filename)\n if (!hash) {\n sendError(res, 404, 'Not Found')\n return\n }\n\n if (req.method === 'POST') {\n handleUpload(req, res, hash)\n return\n }\n\n if (req.method === 'GET') {\n handleDownload(req, res, hash)\n return\n }\n\n sendError(res, 405, 'Method Not Allowed')\n }\n\n function handleDownload(req: IncomingMessage, res: ServerResponse, hash: string): void {\n const record = store.get(hash)\n if (!record) {\n sendError(res, 404, 'Asset Not Found')\n return\n }\n\n let stat\n try {\n stat = statSync(record.filePath)\n } catch (error) {\n const err = error as NodeJS.ErrnoException\n if (err.code === 'ENOENT') {\n store.remove(hash, { removeFile: false })\n sendError(res, 404, 'Asset Not Found')\n } else {\n log.error({ error, hash }, 'Failed to stat asset file.')\n sendError(res, 500, 'Internal Server Error')\n }\n return\n }\n\n res.writeHead(200, {\n 'Content-Type': record.mimeType,\n 'Content-Length': stat.size.toString(),\n 'Cache-Control': 'public, max-age=31536000, immutable'\n })\n\n const stream = createReadStream(record.filePath)\n stream.on('error', (error) => {\n log.warn({ error, hash }, 'Failed to stream asset file.')\n if (!res.headersSent) {\n sendError(res, 500, 'Internal Server Error')\n } else {\n res.end()\n }\n })\n stream.on('open', () => {\n store.touch(hash)\n })\n stream.pipe(res)\n }\n\n function handleUpload(req: IncomingMessage, res: ServerResponse, hash: string): void {\n if (!HASH_HEX_PATTERN.test(hash)) {\n req.resume()\n sendError(res, 400, 'Invalid Hash')\n return\n }\n\n const contentTypeHeader = req.headers['content-type']\n const mimeType = normalizeMimeType(\n Array.isArray(contentTypeHeader) ? contentTypeHeader[0] : contentTypeHeader\n )\n const filename = buildAssetFilename(hash, mimeType)\n const filePath = join(ASSET_DIR, filename)\n\n const width = parseInt(req.headers['x-asset-width'] as string, 10)\n const height = parseInt(req.headers['x-asset-height'] as string, 10)\n const metadata =\n !isNaN(width) && !isNaN(height) && width > 0 && height > 0 ? { width, height } : undefined\n\n const existing = store.get(hash)\n if (existing) {\n let existingPath = existing.filePath\n if (!existsSync(existingPath) && existsSync(filePath)) {\n existing.filePath = filePath\n existingPath = filePath\n }\n\n if (existsSync(existingPath)) {\n if (existingPath !== filePath) {\n try {\n renameSync(existingPath, filePath)\n existing.filePath = filePath\n } catch (error) {\n log.warn({ error, hash }, 'Failed to rename existing asset to include extension.')\n }\n }\n\n // Drain request to ensure connection is clean\n req.resume()\n\n if (metadata) existing.metadata = metadata\n if (existing.mimeType !== mimeType) existing.mimeType = mimeType\n existing.lastAccess = Date.now()\n store.upsert(existing)\n sendOk(res, 200, 'Asset Already Exists')\n return\n }\n }\n\n const tmpPath = `${filePath}.tmp.${nanoid()}`\n const writeStream = createWriteStream(tmpPath)\n const hasher = createHash('sha256')\n let size = 0\n\n const cleanup = () => {\n if (existsSync(tmpPath)) {\n try {\n unlinkSync(tmpPath)\n } catch (e) {\n log.warn({ error: e, tmpPath }, 'Failed to cleanup temp file.')\n }\n }\n }\n\n const monitor = new Transform({\n transform(chunk, encoding, callback) {\n size += chunk.length\n if (size > maxAssetSizeBytes) {\n callback(new Error('PayloadTooLarge'))\n return\n }\n hasher.update(chunk)\n callback(null, chunk)\n }\n })\n\n pipeline(req, monitor, writeStream, (err) => {\n if (err) {\n cleanup()\n if (err.message === 'PayloadTooLarge') {\n sendError(res, 413, 'Payload Too Large')\n } else if (err.code === 'ERR_STREAM_PREMATURE_CLOSE') {\n log.warn({ hash }, 'Upload request closed prematurely.')\n sendError(res, 400, 'Upload Incomplete')\n } else {\n log.error({ error: err, hash }, 'Upload pipeline failed.')\n if (!res.headersSent) {\n sendError(res, 500, 'Internal Server Error')\n }\n }\n return\n }\n\n const computedHash = hasher.digest('hex').slice(0, MCP_HASH_HEX_LENGTH)\n if (computedHash !== hash) {\n cleanup()\n sendError(res, 400, 'Hash Mismatch')\n return\n }\n\n try {\n renameSync(tmpPath, filePath)\n } catch (error) {\n log.error({ error, hash }, 'Failed to rename temp file to asset.')\n cleanup()\n sendError(res, 500, 'Internal Server Error')\n return\n }\n\n store.upsert({\n hash,\n filePath,\n mimeType,\n size,\n metadata\n })\n log.info({ hash, size }, 'Stored uploaded asset via HTTP.')\n sendOk(res, 201, 'Created', { hash, size })\n })\n }\n\n function sendError(\n res: ServerResponse,\n status: number,\n message: string,\n details?: Record<string, unknown>\n ): void {\n if (!res.headersSent) {\n res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' })\n }\n res.end(\n JSON.stringify({\n error: message,\n ...details\n })\n )\n }\n\n function sendOk(\n res: ServerResponse,\n status: number,\n message: string,\n data?: Record<string, unknown>\n ): void {\n if (!res.headersSent) {\n res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' })\n }\n res.end(\n JSON.stringify({\n message,\n ...data\n })\n )\n }\n\n return {\n start,\n stop,\n getBaseUrl\n }\n}\n","import { existsSync, readFileSync, rmSync, writeFileSync, readdirSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport type { AssetRecord } from './types'\n\nimport { getHashFromAssetFilename } from './asset-utils'\nimport { ASSET_DIR, ensureDir, ensureFile, log } from './shared'\n\nconst INDEX_FILENAME = 'assets.json'\nconst DEFAULT_INDEX_PATH = join(ASSET_DIR, INDEX_FILENAME)\n\nexport interface AssetStoreOptions {\n indexPath?: string\n}\n\nexport interface AssetStore {\n list(): AssetRecord[]\n has(hash: string): boolean\n get(hash: string): AssetRecord | undefined\n getMany(hashes: string[]): AssetRecord[]\n upsert(\n input: Omit<AssetRecord, 'uploadedAt' | 'lastAccess'> &\n Partial<Pick<AssetRecord, 'uploadedAt' | 'lastAccess'>>\n ): AssetRecord\n touch(hash: string): AssetRecord | undefined\n remove(hash: string, opts?: { removeFile?: boolean }): void\n reconcile(): void\n flush(): void\n}\n\nfunction readIndex(indexPath: string): AssetRecord[] {\n if (!existsSync(indexPath)) return []\n try {\n const raw = readFileSync(indexPath, 'utf8').trim()\n if (!raw) return []\n const parsed = JSON.parse(raw)\n return Array.isArray(parsed) ? (parsed as AssetRecord[]) : []\n } catch (error) {\n log.warn({ error, indexPath }, 'Failed to read asset catalog; starting fresh.')\n return []\n }\n}\n\nfunction writeIndex(indexPath: string, values: AssetRecord[]): void {\n const payload = JSON.stringify(values, null, 2)\n writeFileSync(indexPath, payload, 'utf8')\n}\n\nexport function createAssetStore(options: AssetStoreOptions = {}): AssetStore {\n ensureDir(ASSET_DIR)\n const indexPath = options.indexPath ?? DEFAULT_INDEX_PATH\n ensureFile(indexPath)\n const records = new Map<string, AssetRecord>()\n let persistTimer: NodeJS.Timeout | null = null\n\n function loadExisting(): void {\n const list = readIndex(indexPath)\n for (const record of list) {\n if (record?.hash && record?.filePath) {\n records.set(record.hash, record)\n }\n }\n }\n\n function persist(): void {\n if (persistTimer) return\n persistTimer = setTimeout(() => {\n persistTimer = null\n writeIndex(indexPath, [...records.values()])\n }, 5000)\n if (typeof persistTimer.unref === 'function') {\n persistTimer.unref()\n }\n }\n\n function flush(): void {\n if (persistTimer) {\n clearTimeout(persistTimer)\n persistTimer = null\n }\n writeIndex(indexPath, [...records.values()])\n }\n\n function list(): AssetRecord[] {\n return [...records.values()]\n }\n\n function has(hash: string): boolean {\n return records.has(hash)\n }\n\n function get(hash: string): AssetRecord | undefined {\n return records.get(hash)\n }\n\n function getMany(hashes: string[]): AssetRecord[] {\n return hashes\n .map((hash) => records.get(hash))\n .filter((record): record is AssetRecord => !!record)\n }\n\n function upsert(\n input: Omit<AssetRecord, 'uploadedAt' | 'lastAccess'> &\n Partial<Pick<AssetRecord, 'uploadedAt' | 'lastAccess'>>\n ): AssetRecord {\n const now = Date.now()\n const record: AssetRecord = {\n ...input,\n uploadedAt: input.uploadedAt ?? now,\n lastAccess: input.lastAccess ?? now\n }\n records.set(record.hash, record)\n persist()\n return record\n }\n\n function touch(hash: string): AssetRecord | undefined {\n const existing = records.get(hash)\n if (!existing) return undefined\n existing.lastAccess = Date.now()\n persist()\n return existing\n }\n\n function remove(hash: string, { removeFile = true } = {}): void {\n const record = records.get(hash)\n if (!record) return\n records.delete(hash)\n persist()\n\n if (removeFile) {\n try {\n rmSync(record.filePath, { force: true })\n } catch (error) {\n log.warn({ hash, error }, 'Failed to remove asset file on delete.')\n }\n }\n }\n\n function reconcile(): void {\n let changed = false\n for (const [hash, record] of records) {\n if (!existsSync(record.filePath)) {\n records.delete(hash)\n changed = true\n }\n }\n\n try {\n const files = readdirSync(ASSET_DIR)\n const now = Date.now()\n for (const file of files) {\n if (file === INDEX_FILENAME) continue\n\n // Cleanup stale tmp files (> 1 hour)\n if (file.includes('.tmp.')) {\n try {\n const filePath = join(ASSET_DIR, file)\n const stat = statSync(filePath)\n if (now - stat.mtimeMs > 3600 * 1000) {\n rmSync(filePath, { force: true })\n log.info({ file }, 'Cleaned up stale temp file.')\n }\n } catch (e) {\n // Ignore errors during cleanup\n log.debug({ error: e, file }, 'Failed to cleanup stale temp file.')\n }\n continue\n }\n\n const hash = getHashFromAssetFilename(file)\n if (!hash) continue\n\n if (!records.has(hash)) {\n const filePath = join(ASSET_DIR, file)\n try {\n const stat = statSync(filePath)\n records.set(hash, {\n hash,\n filePath,\n mimeType: 'application/octet-stream',\n size: stat.size,\n uploadedAt: stat.birthtimeMs,\n lastAccess: stat.atimeMs\n })\n changed = true\n log.info({ hash }, 'Recovered orphan asset file.')\n } catch (e) {\n log.warn({ error: e, file }, 'Failed to stat orphan file.')\n }\n }\n }\n } catch (error) {\n log.warn({ error }, 'Failed to scan asset directory for orphans.')\n }\n\n if (changed) flush()\n }\n\n loadExisting()\n reconcile()\n\n return {\n list,\n has,\n get,\n getMany,\n upsert,\n touch,\n remove,\n reconcile,\n flush\n }\n}\n","export default \"You are connected to a Figma design file via TemPad Dev MCP.\\n\\nTreat tool outputs as design facts. Refactor only to match the user’s repo conventions; do not invent key style values.\\n\\nRules:\\n\\n- Never output any `data-hint-*` attributes from tool outputs (hints only).\\n- If `get_code` warns `depth-cap`, call `get_code` again for each listed `nodeId` before implementing.\\n- Use `get_structure` only to resolve layout/overlap uncertainty; do not derive numeric values from images.\\n- Tokens: `get_code.tokens` keys are canonical names (`--...`). Multi‑mode values use `${collectionName}:${modeName}`. Nodes may hint per-node overrides via `data-hint-variable-mode=\\\"Collection=Mode;...\\\"`.\\n- Assets: download bytes via `asset.url`. Asset resources are not exposed via MCP `resources/read`. Preserve SVG/vector assets exactly; never redraw vectors.\\n\"","import type { TempadMcpErrorCode } from '@tempad-dev/shared'\n\nimport { TEMPAD_MCP_ERROR_CODES } from '@tempad-dev/shared'\nimport { nanoid } from 'nanoid'\n\nimport type { PendingToolCall } from './types'\n\nimport { log } from './shared'\n\nconst pendingCalls = new Map<string, PendingToolCall>()\n\nfunction createToolError(\n code: TempadMcpErrorCode,\n message: string\n): Error & { code: TempadMcpErrorCode } {\n const err = new Error(message) as Error & { code: TempadMcpErrorCode }\n err.code = code\n return err\n}\n\nexport function register<T>(\n extensionId: string,\n timeout: number\n): { promise: Promise<T>; requestId: string } {\n const requestId = nanoid()\n const promise = new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingCalls.delete(requestId)\n reject(\n createToolError(\n TEMPAD_MCP_ERROR_CODES.EXTENSION_TIMEOUT,\n `Extension did not respond within ${timeout / 1000}s.`\n )\n )\n }, timeout)\n\n pendingCalls.set(requestId, {\n resolve: resolve as (value: unknown) => void,\n reject,\n timer,\n extensionId\n })\n })\n return { promise, requestId }\n}\n\nexport function resolve(requestId: string, payload: unknown): void {\n const call = pendingCalls.get(requestId)\n if (call) {\n const { timer, resolve: finish } = call\n clearTimeout(timer)\n finish(payload)\n pendingCalls.delete(requestId)\n } else {\n log.warn({ reqId: requestId }, 'Received result for unknown/timed-out call.')\n }\n}\n\nexport function reject(requestId: string, error: Error): void {\n const call = pendingCalls.get(requestId)\n if (call) {\n const { timer, reject: fail } = call\n clearTimeout(timer)\n fail(error)\n pendingCalls.delete(requestId)\n } else {\n log.warn({ reqId: requestId }, 'Received error for unknown/timed-out call.')\n }\n}\n\nexport function cleanupForExtension(extensionId: string): void {\n for (const [reqId, call] of pendingCalls.entries()) {\n const { timer, reject: fail, extensionId: extId } = call\n if (extId === extensionId) {\n clearTimeout(timer)\n fail(\n createToolError(\n TEMPAD_MCP_ERROR_CODES.EXTENSION_DISCONNECTED,\n 'Extension disconnected before providing a result.'\n )\n )\n pendingCalls.delete(reqId)\n log.warn({ reqId, extId: extensionId }, 'Rejected pending call from disconnected extension.')\n }\n }\n}\n\nexport function cleanupAll(): void {\n pendingCalls.forEach((call, reqId) => {\n const { timer, reject: fail } = call\n clearTimeout(timer)\n fail(new Error('Hub is shutting down.'))\n log.debug({ reqId }, 'Rejected pending tool call due to shutdown.')\n })\n pendingCalls.clear()\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport type {\n GetScreenshotResult,\n TempadMcpErrorCode,\n ToolName,\n ToolResultMap,\n ToolSchema\n} from '@tempad-dev/shared'\nimport type { ZodType } from 'zod'\n\nimport {\n GetAssetsParametersSchema,\n GetAssetsResultSchema,\n GetCodeParametersSchema,\n GetScreenshotParametersSchema,\n GetStructureParametersSchema,\n GetTokenDefsParametersSchema,\n TEMPAD_MCP_ERROR_CODES,\n type TempadMcpErrorPayload\n} from '@tempad-dev/shared'\n\nexport type {\n AssetDescriptor,\n GetAssetsParametersInput,\n GetAssetsResult,\n GetCodeParametersInput,\n GetCodeResult,\n GetScreenshotParametersInput,\n GetScreenshotResult,\n GetStructureParametersInput,\n GetStructureResult,\n GetTokenDefsParametersInput,\n GetTokenDefsResult,\n TokenEntry,\n ToolName,\n ToolResultMap,\n ToolSchema\n} from '@tempad-dev/shared'\n\ntype BaseToolMetadata<Name extends ToolName, Schema extends ZodType> = ToolSchema<Name> & {\n parameters: Schema\n format?: (payload: ToolResultMap[Name]) => CallToolResult\n}\n\ntype ExtensionToolMetadata<Name extends ToolName, Schema extends ZodType> = BaseToolMetadata<\n Name,\n Schema\n> & {\n target: 'extension'\n}\n\ntype HubToolMetadata<Name extends ToolName, Schema extends ZodType> = BaseToolMetadata<\n Name,\n Schema\n> & {\n target: 'hub'\n outputSchema?: ZodType\n}\n\nfunction getRecordProperty(record: unknown, key: string): unknown {\n if (!record || typeof record !== 'object') {\n return undefined\n }\n return Reflect.get(record, key)\n}\n\nfunction extTool<Name extends ToolName, Schema extends ZodType>(\n definition: ExtensionToolMetadata<Name, Schema>\n): ExtensionToolMetadata<Name, Schema> {\n return definition\n}\n\nfunction hubTool<Name extends ToolName, Schema extends ZodType>(\n definition: HubToolMetadata<Name, Schema>\n): HubToolMetadata<Name, Schema> {\n return definition\n}\n\nexport const TOOL_DEFS = [\n extTool({\n name: 'get_code',\n description:\n 'High-fidelity code snapshot for nodeId/current single selection (omit nodeId to use selection): JSX/Vue markup + Tailwind-like classes, plus assets/tokens metadata and codegen config. Start here, then refactor into repo conventions while preserving values/intent; strip any data-hint-* attributes (hints only). If warnings include depth-cap, call get_code again for each listed nodeId. If warnings include auto-layout (inferred), use get_structure to confirm hierarchy/overlap (do not derive numeric values from pixels). Tokens are keyed by canonical names like `--color-primary` (multi-mode keys use `${collection}:${mode}`; node overrides may appear as data-hint-variable-mode).',\n parameters: GetCodeParametersSchema,\n target: 'extension',\n format: createCodeToolResponse\n }),\n extTool({\n name: 'get_token_defs',\n description:\n 'Resolve canonical token names to literal values (optionally including all modes) for tokens referenced by get_code.',\n parameters: GetTokenDefsParametersSchema,\n target: 'extension',\n exposed: false\n }),\n extTool({\n name: 'get_screenshot',\n description:\n 'Capture a rendered PNG screenshot for nodeId/current single selection for visual verification (layering/overlap/masks/effects).',\n parameters: GetScreenshotParametersSchema,\n target: 'extension',\n format: createScreenshotToolResponse,\n exposed: false\n }),\n extTool({\n name: 'get_structure',\n description:\n 'Get a compact structural + geometry outline for nodeId/current single selection to understand hierarchy and layout intent.',\n parameters: GetStructureParametersSchema,\n target: 'extension'\n }),\n hubTool({\n name: 'get_assets',\n description:\n 'Resolve asset hashes to downloadable URLs and metadata for assets referenced by tool responses (preserve vectors exactly).',\n parameters: GetAssetsParametersSchema,\n target: 'hub',\n outputSchema: GetAssetsResultSchema,\n exposed: false\n })\n] as const\n\nfunction extractToolErrorCode(error: unknown): TempadMcpErrorCode | undefined {\n const code = getRecordProperty(error, 'code')\n if (typeof code === 'string') {\n return code as TempadMcpErrorCode\n }\n const cause = getRecordProperty(error, 'cause')\n const causeCode = getRecordProperty(cause, 'code')\n if (typeof causeCode === 'string') {\n return causeCode as TempadMcpErrorCode\n }\n return undefined\n}\n\nfunction extractToolErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message || 'Unknown error occurred.'\n if (typeof error === 'string') return error\n if (error && typeof error === 'object') {\n const candidate = error as Partial<TempadMcpErrorPayload & Record<string, unknown>>\n if (typeof candidate.message === 'string' && candidate.message.trim()) return candidate.message\n }\n return 'Unknown error occurred.'\n}\n\nfunction createToolErrorResponse(toolName: string, error: unknown): CallToolResult {\n const message = extractToolErrorMessage(error)\n const code = extractToolErrorCode(error)\n const codeLabel = code ? ` [${code}]` : ''\n\n const troubleshooting = (() => {\n const help: string[] = []\n\n const isConnectivityError =\n code === TEMPAD_MCP_ERROR_CODES.NO_ACTIVE_EXTENSION ||\n code === TEMPAD_MCP_ERROR_CODES.EXTENSION_TIMEOUT ||\n code === TEMPAD_MCP_ERROR_CODES.EXTENSION_DISCONNECTED ||\n code === TEMPAD_MCP_ERROR_CODES.ASSET_SERVER_NOT_CONFIGURED ||\n code === TEMPAD_MCP_ERROR_CODES.TRANSPORT_NOT_CONNECTED ||\n /no active tempad dev extension/i.test(message) ||\n /asset server url is not configured/i.test(message) ||\n /mcp transport is not connected/i.test(message) ||\n /websocket/i.test(message)\n\n if (isConnectivityError) {\n help.push(\n 'Troubleshooting:',\n '- In Figma, open TemPad Dev panel and enable MCP (Preferences → MCP server).',\n '- If multiple Figma tabs are open, click the MCP badge to activate this tab.',\n '- Keep the Figma tab active/foreground while running MCP tools.'\n )\n }\n\n const isSelectionError =\n code === TEMPAD_MCP_ERROR_CODES.INVALID_SELECTION ||\n code === TEMPAD_MCP_ERROR_CODES.NODE_NOT_VISIBLE ||\n /select exactly one visible node/i.test(message) ||\n /no visible node found/i.test(message)\n\n if (isSelectionError) {\n help.push('Tip: Select exactly one visible node, or pass nodeId.')\n }\n\n return help.length ? `\\n\\n${help.join('\\n')}` : ''\n })()\n\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: `Tool \"${toolName}\" failed${codeLabel}: ${message}${troubleshooting}`\n }\n ]\n }\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nexport function createCodeToolResponse(payload: ToolResultMap['get_code']): CallToolResult {\n if (!isCodeResult(payload)) {\n throw new Error('Invalid get_code payload received from extension.')\n }\n\n const summary: string[] = []\n const codeSize = Buffer.byteLength(payload.code, 'utf8')\n summary.push(`Generated \\`${payload.lang}\\` snippet (${formatBytes(codeSize)}).`)\n if (payload.warnings?.length) {\n const warningText = payload.warnings.map((warning) => warning.message).join(' ')\n summary.push(warningText)\n }\n summary.push(\n payload.assets?.length\n ? `Assets attached: ${payload.assets.length}. Download bytes from each asset.url.`\n : 'No binary assets were attached to this response.'\n )\n const tokenCount = payload.tokens ? Object.keys(payload.tokens).length : 0\n if (tokenCount) {\n summary.push(`Token references included: ${tokenCount}.`)\n }\n summary.push('Read structuredContent for the full code string and asset metadata.')\n\n return {\n content: [\n {\n type: 'text' as const,\n text: summary.join('\\n')\n }\n ],\n structuredContent: payload\n }\n}\n\nexport function createScreenshotToolResponse(\n payload: ToolResultMap['get_screenshot']\n): CallToolResult {\n if (!isScreenshotResult(payload)) {\n throw new Error('Invalid get_screenshot payload received from extension.')\n }\n\n const descriptionBlock = {\n type: 'text' as const,\n text: `${describeScreenshot(payload)} - Download: ${payload.asset.url}`\n }\n\n return {\n content: [descriptionBlock],\n structuredContent: payload\n }\n}\n\nfunction describeScreenshot(result: GetScreenshotResult): string {\n return `Screenshot ${result.width}x${result.height} @${result.scale}x (${formatBytes(result.bytes)})`\n}\n\nfunction isScreenshotResult(payload: unknown): payload is GetScreenshotResult {\n if (typeof payload !== 'object' || !payload) return false\n const candidate = payload as Partial<GetScreenshotResult & Record<string, unknown>>\n return (\n typeof candidate.asset === 'object' &&\n candidate.asset !== null &&\n typeof candidate.width === 'number' &&\n typeof candidate.height === 'number' &&\n typeof candidate.scale === 'number' &&\n typeof candidate.bytes === 'number' &&\n typeof candidate.format === 'string'\n )\n}\n\nfunction isCodeResult(payload: unknown): payload is ToolResultMap['get_code'] {\n if (typeof payload !== 'object' || !payload) return false\n const candidate = payload as Partial<ToolResultMap['get_code'] & Record<string, unknown>>\n return (\n typeof candidate.code === 'string' &&\n typeof candidate.lang === 'string' &&\n (candidate.assets === undefined || Array.isArray(candidate.assets))\n )\n}\n\nexport function coercePayloadToToolResponse(payload: unknown): CallToolResult {\n if (\n payload &&\n typeof payload === 'object' &&\n Array.isArray((payload as CallToolResult).content)\n ) {\n return payload as CallToolResult\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: typeof payload === 'string' ? payload : JSON.stringify(payload, null, 2)\n }\n ]\n }\n}\n\nexport { createToolErrorResponse }\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport type {\n AssetDescriptor,\n GetAssetsParametersInput,\n GetAssetsResult,\n RegisteredMessage,\n StateMessage,\n ToolCallMessage,\n ToolName,\n ToolResultMap,\n ToolResultMessage\n} from '@tempad-dev/shared'\nimport type { RawData } from 'ws'\nimport type { ZodType } from 'zod'\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport {\n GetAssetsResultSchema,\n MessageFromExtensionSchema,\n TEMPAD_MCP_ERROR_CODES,\n type TempadMcpErrorCode\n} from '@tempad-dev/shared'\nimport { nanoid } from 'nanoid'\nimport { existsSync, rmSync, chmodSync } from 'node:fs'\nimport { createServer } from 'node:net'\nimport { WebSocketServer } from 'ws'\n\nimport type { AssetRecord, ExtensionConnection } from './types'\n\nimport { createAssetHttpServer } from './asset-http-server'\nimport { createAssetStore } from './asset-store'\nimport { buildAssetFilename } from './asset-utils'\nimport { getMcpServerConfig } from './config'\nimport MCP_INSTRUCTIONS from './instructions.md?raw'\nimport { register, resolve, reject, cleanupForExtension, cleanupAll } from './request'\nimport { PACKAGE_VERSION, log, RUNTIME_DIR, SOCK_PATH, ensureDir } from './shared'\nimport { TOOL_DEFS, coercePayloadToToolResponse, createToolErrorResponse } from './tools'\n\nconst SHUTDOWN_TIMEOUT = 2000\nconst { wsPortCandidates, toolTimeoutMs, maxPayloadBytes, autoActivateGraceMs, assetTtlMs } =\n getMcpServerConfig()\n\nlog.info({ version: PACKAGE_VERSION }, 'TemPad MCP Hub starting...')\n\nconst extensions: ExtensionConnection[] = []\nlet consumerCount = 0\ntype TimeoutHandle = ReturnType<typeof setTimeout>\nlet autoActivateTimer: TimeoutHandle | null = null\nlet selectedWsPort = 0\n\nconst mcp = new McpServer(\n { name: 'tempad-dev-mcp', version: PACKAGE_VERSION },\n MCP_INSTRUCTIONS ? { instructions: MCP_INSTRUCTIONS } : undefined\n)\ntype McpInputSchema = Parameters<typeof mcp.registerTool>[1]['inputSchema']\ntype McpOutputSchema = Parameters<typeof mcp.registerTool>[1]['outputSchema']\ntype ToolResponse = CallToolResult\ntype SchemaOutput<Schema extends ZodType> = Schema['_output']\ntype ToolMetadataEntry = (typeof TOOL_DEFS)[number]\ntype ExtensionToolMetadata = Extract<ToolMetadataEntry, { target: 'extension' }>\ntype HubToolMetadata = Extract<ToolMetadataEntry, { target: 'hub' }>\n\ntype HubToolWithHandler<T extends HubToolMetadata = HubToolMetadata> = T & {\n handler: (args: SchemaOutput<T['parameters']>) => Promise<ToolResponse>\n}\n\nfunction getRecordProperty(record: unknown, key: string): unknown {\n if (!record || typeof record !== 'object') {\n return undefined\n }\n return Reflect.get(record, key)\n}\n\ntype RegisteredToolDefinition = ExtensionToolMetadata | HubToolWithHandler\n\nfunction enrichToolDefinition(tool: ToolMetadataEntry): RegisteredToolDefinition {\n if (tool.target === 'extension') {\n return tool\n }\n\n switch (tool.name) {\n case 'get_assets':\n return {\n ...tool,\n handler: handleGetAssets\n } satisfies HubToolWithHandler\n default:\n throw new Error('No handler configured for hub tool.')\n }\n}\n\nconst TOOL_DEFINITIONS: ReadonlyArray<RegisteredToolDefinition> = TOOL_DEFS.map((tool) =>\n enrichToolDefinition(tool)\n)\n\ntype RegisteredTool = (typeof TOOL_DEFINITIONS)[number]\ntype ExtensionTool = Extract<RegisteredTool, { target: 'extension' }>\ntype HubOnlyTool = Extract<RegisteredTool, { target: 'hub' }>\n\nfunction createCodedError(code: TempadMcpErrorCode, message: string): Error & { code: string } {\n const err = new Error(message) as Error & { code: string }\n err.code = code\n return err\n}\n\nfunction coerceToolError(error: unknown): Error {\n if (error instanceof Error) return error\n if (typeof error === 'string') return new Error(error)\n const messageValue = getRecordProperty(error, 'message')\n const codeValue = getRecordProperty(error, 'code')\n if (error && typeof error === 'object') {\n const message = typeof messageValue === 'string' ? messageValue : safeStringify(error)\n const err = new Error(message) as Error & { code?: string }\n if (typeof codeValue === 'string') err.code = codeValue\n return err\n }\n return new Error(String(error))\n}\n\nfunction safeStringify(input: unknown): string {\n try {\n return JSON.stringify(input)\n } catch {\n return String(input)\n }\n}\n\nfunction hasFormatter(tool: RegisteredToolDefinition): tool is ExtensionTool & {\n format: (payload: unknown) => ToolResponse\n} {\n return tool.target === 'extension' && 'format' in tool\n}\n\ntype ToolDefinitionByName = {\n [T in RegisteredToolDefinition as T['name']]: T\n}\n\nconst TOOL_BY_NAME: ToolDefinitionByName = Object.fromEntries(\n TOOL_DEFINITIONS.map((tool) => [tool.name, tool] as const)\n) as ToolDefinitionByName\n\nfunction getToolDefinition<Name extends ToolName>(name: Name): ToolDefinitionByName[Name] {\n return TOOL_BY_NAME[name]\n}\n\nconst assetStore = createAssetStore()\nconst assetHttpServer = createAssetHttpServer(assetStore)\nawait assetHttpServer.start()\nscheduleAssetCleanup()\n\nfunction scheduleAssetCleanup(): void {\n if (assetTtlMs <= 0) {\n log.info('Asset TTL cleanup disabled (TEMPAD_MCP_ASSET_TTL_MS=0).')\n return\n }\n pruneExpiredAssets(assetTtlMs)\n const intervalMs = Math.min(assetTtlMs, 24 * 60 * 60 * 1000)\n const timer = setInterval(() => {\n pruneExpiredAssets(assetTtlMs)\n }, intervalMs)\n unrefTimer(timer)\n log.info({ ttlMs: assetTtlMs, intervalMs }, 'Asset TTL cleanup enabled.')\n}\n\nfunction pruneExpiredAssets(ttlMs: number): void {\n const now = Date.now()\n let removed = 0\n let checked = 0\n for (const record of assetStore.list()) {\n checked += 1\n const lastAccess = Number.isFinite(record.lastAccess) ? record.lastAccess : record.uploadedAt\n if (!lastAccess) continue\n if (now - lastAccess > ttlMs) {\n assetStore.remove(record.hash)\n removed += 1\n }\n }\n log.info({ checked, removed, ttlMs }, 'Asset TTL sweep completed.')\n}\n\nfunction buildAssetDescriptor(record: AssetRecord): AssetDescriptor {\n const filename = buildAssetFilename(record.hash, record.mimeType)\n return {\n hash: record.hash,\n url: `${assetHttpServer.getBaseUrl()}/assets/${filename}`,\n mimeType: record.mimeType,\n size: record.size,\n width: record.metadata?.width,\n height: record.metadata?.height\n }\n}\n\nfunction registerTools(): void {\n const registered: string[] = []\n for (const tool of TOOL_DEFINITIONS) {\n if ('exposed' in tool && tool.exposed === false) continue\n registerTool(tool)\n registered.push(tool.name)\n }\n log.info({ tools: registered }, 'Registered tools.')\n}\n\nregisterTools()\nfunction registerTool(tool: RegisteredTool): void {\n if (tool.target === 'extension') {\n registerProxiedTool(tool)\n } else {\n registerLocalTool(tool)\n }\n}\n\nfunction registerProxiedTool<T extends ExtensionTool>(tool: T): void {\n type Name = T['name']\n type Result = ToolResultMap[Name]\n\n const registerToolFn = mcp.registerTool.bind(mcp) as (\n name: string,\n options: { description: string; inputSchema: ZodType; outputSchema?: ZodType },\n handler: (args: unknown) => Promise<CallToolResult>\n ) => unknown\n\n const schema = tool.parameters\n const handler = async (args: unknown) => {\n let requestId: string | undefined\n try {\n const parsedArgs = schema.parse(args)\n const activeExt = extensions.find((e) => e.active)\n if (!activeExt) {\n throw createCodedError(\n TEMPAD_MCP_ERROR_CODES.NO_ACTIVE_EXTENSION,\n 'No active TemPad Dev extension available.'\n )\n }\n\n const registration = register<Result>(activeExt.id, toolTimeoutMs)\n requestId = registration.requestId\n\n const message: ToolCallMessage = {\n type: 'toolCall',\n id: registration.requestId,\n payload: {\n name: tool.name,\n args: parsedArgs\n }\n }\n activeExt.ws.send(JSON.stringify(message))\n log.info(\n { tool: tool.name, req: registration.requestId, extId: activeExt.id },\n 'Forwarded tool call.'\n )\n\n const payload = await registration.promise\n return createToolResponse(tool.name, payload)\n } catch (error) {\n const normalized = coerceToolError(error)\n log.error(\n {\n tool: tool.name,\n req: requestId,\n code: getRecordProperty(normalized, 'code'),\n message: normalized.message\n },\n 'Tool invocation failed.'\n )\n return createToolErrorResponse(tool.name, normalized)\n }\n }\n\n registerToolFn(\n tool.name,\n {\n description: tool.description,\n inputSchema: schema as unknown as McpInputSchema\n },\n handler\n )\n}\n\nfunction registerLocalTool(tool: HubOnlyTool): void {\n const schema = tool.parameters\n const handler = tool.handler\n\n const registerToolFn = mcp.registerTool.bind(mcp) as (\n name: string,\n options: { description: string; inputSchema: ZodType; outputSchema?: ZodType },\n handler: (args: unknown) => Promise<CallToolResult>\n ) => unknown\n\n const registrationOptions: {\n description: string\n inputSchema: McpInputSchema\n outputSchema?: McpOutputSchema\n } = {\n description: tool.description,\n inputSchema: schema as unknown as McpInputSchema\n }\n\n if (tool.outputSchema) {\n registrationOptions.outputSchema = tool.outputSchema as unknown as McpOutputSchema\n }\n\n const registerHandler = async (args: unknown) => {\n try {\n const parsed = schema.parse(args)\n return await handler(parsed)\n } catch (error) {\n log.error({ tool: tool.name, error }, 'Local tool invocation failed.')\n return createToolErrorResponse(tool.name, error)\n }\n }\n\n registerToolFn(tool.name, registrationOptions, registerHandler)\n}\n\nfunction createToolResponse<Name extends ToolName>(\n toolName: Name,\n payload: ToolResultMap[Name]\n): ToolResponse {\n const definition = getToolDefinition(toolName)\n if (definition && hasFormatter(definition)) {\n try {\n const formatter = definition.format as (input: ToolResultMap[Name]) => ToolResponse\n return formatter(payload)\n } catch (error) {\n log.warn({ tool: toolName, error }, 'Failed to format tool result; returning raw payload.')\n return coercePayloadToToolResponse(payload)\n }\n }\n\n return coercePayloadToToolResponse(payload)\n}\n\nasync function handleGetAssets({ hashes }: GetAssetsParametersInput): Promise<ToolResponse> {\n if (hashes.length > 100) {\n throw new Error('Too many hashes requested. Limit is 100.')\n }\n const unique = Array.from(new Set(hashes))\n const records = assetStore.getMany(unique).filter((record) => {\n if (existsSync(record.filePath)) return true\n assetStore.remove(record.hash, { removeFile: false })\n return false\n })\n const found = new Set(records.map((record) => record.hash))\n const payload: GetAssetsResult = GetAssetsResultSchema.parse({\n assets: records.map((record) => buildAssetDescriptor(record)),\n missing: unique.filter((hash) => !found.has(hash))\n })\n\n const summary: string[] = []\n summary.push(\n payload.assets.length\n ? `Resolved ${payload.assets.length} asset${payload.assets.length === 1 ? '' : 's'}.`\n : 'No assets were resolved for the requested hashes.'\n )\n if (payload.missing.length) {\n summary.push(`Missing: ${payload.missing.join(', ')}`)\n }\n summary.push('Download bytes from each asset.url.')\n\n const content = [\n {\n type: 'text' as const,\n text: summary.join('\\n')\n }\n ]\n\n return {\n content,\n structuredContent: payload\n }\n}\n\nfunction getActiveId(): string | null {\n return extensions.find((e) => e.active)?.id ?? null\n}\n\nfunction setActive(targetId: string | null): void {\n extensions.forEach((e) => {\n e.active = targetId !== null && e.id === targetId\n })\n}\n\nfunction clearAutoActivateTimer(): void {\n if (autoActivateTimer) {\n clearTimeout(autoActivateTimer)\n autoActivateTimer = null\n }\n}\n\nfunction scheduleAutoActivate(): void {\n clearAutoActivateTimer()\n\n if (extensions.length !== 1 || getActiveId()) {\n return\n }\n\n const target = extensions[0]\n autoActivateTimer = setTimeout(() => {\n autoActivateTimer = null\n if (extensions.length === 1 && !getActiveId()) {\n setActive(target.id)\n log.info({ id: target.id }, 'Auto-activated sole extension after grace period.')\n broadcastState()\n }\n }, autoActivateGraceMs)\n}\n\nfunction unrefTimer(timer: TimeoutHandle): void {\n if (typeof timer === 'object' && timer !== null) {\n const handle = timer as NodeJS.Timeout\n if (typeof handle.unref === 'function') {\n handle.unref()\n }\n }\n}\n\nfunction broadcastState(): void {\n const activeId = getActiveId()\n const message: StateMessage = {\n type: 'state',\n activeId,\n count: extensions.length,\n port: selectedWsPort,\n assetServerUrl: assetHttpServer.getBaseUrl()\n }\n extensions.forEach((ext) => ext.ws.send(JSON.stringify(message)))\n log.debug({ activeId, count: extensions.length }, 'Broadcasted state.')\n}\n\nfunction rawDataToBuffer(raw: RawData): Buffer {\n if (typeof raw === 'string') return Buffer.from(raw)\n if (Buffer.isBuffer(raw)) return raw\n if (raw instanceof ArrayBuffer) return Buffer.from(raw)\n return Buffer.concat(raw)\n}\n\nfunction shutdown(): void {\n log.info('Hub is shutting down...')\n assetStore.flush()\n assetHttpServer.stop()\n netServer.close(() => log.info('Net server closed.'))\n wss?.close(() => log.info('WebSocket server closed.'))\n cleanupAll()\n const timer = setTimeout(() => {\n log.warn('Shutdown timed out. Forcing exit.')\n process.exit(1)\n }, SHUTDOWN_TIMEOUT)\n unrefTimer(timer)\n}\n\ntry {\n ensureDir(RUNTIME_DIR)\n if (process.platform !== 'win32' && existsSync(SOCK_PATH)) {\n log.warn({ sock: SOCK_PATH }, 'Removing stale socket file.')\n rmSync(SOCK_PATH)\n }\n} catch (error: unknown) {\n log.error({ err: error }, 'Failed to initialize runtime environment.')\n process.exit(1)\n}\n\nconst netServer = createServer((sock) => {\n consumerCount++\n log.info(`Consumer connected. Total: ${consumerCount}`)\n const transport = new StdioServerTransport(sock, sock)\n mcp.connect(transport).catch((err) => {\n log.error({ err }, 'Failed to attach MCP transport.')\n transport.close().catch((closeErr) => log.warn({ err: closeErr }, 'Transport close failed.'))\n sock.destroy()\n })\n sock.on('error', (err) => {\n log.warn({ err }, 'Consumer socket error.')\n transport.close().catch((closeErr) => log.warn({ err: closeErr }, 'Transport close failed.'))\n })\n sock.on('close', async () => {\n await transport.close()\n consumerCount--\n log.info(`Consumer disconnected. Remaining: ${consumerCount}`)\n if (consumerCount === 0) {\n log.info('Last consumer disconnected. Shutting down.')\n shutdown()\n }\n })\n})\nnetServer.on('error', (err) => {\n log.error({ err }, 'Net server error.')\n process.exit(1)\n})\nnetServer.listen(SOCK_PATH, () => {\n try {\n if (process.platform !== 'win32') chmodSync(SOCK_PATH, 0o600)\n } catch (err) {\n log.error({ err }, 'Failed to set socket permissions. Shutting down.')\n process.exit(1)\n }\n log.info({ sock: SOCK_PATH }, 'Hub socket ready.')\n})\n\nasync function startWebSocketServer(): Promise<{ wss: WebSocketServer; port: number }> {\n for (const candidate of wsPortCandidates) {\n const server = new WebSocketServer({\n host: '127.0.0.1',\n port: candidate,\n maxPayload: maxPayloadBytes\n })\n\n try {\n await new Promise<void>((resolve, reject) => {\n const onError = (err: NodeJS.ErrnoException) => {\n server.off('listening', onListening)\n reject(err)\n }\n const onListening = () => {\n server.off('error', onError)\n resolve()\n }\n server.once('error', onError)\n server.once('listening', onListening)\n })\n return { wss: server, port: candidate }\n } catch (err) {\n server.close()\n const errno = err as NodeJS.ErrnoException\n if (errno.code === 'EADDRINUSE') {\n log.warn({ port: candidate }, 'WebSocket port in use, trying next candidate.')\n continue\n }\n log.error({ err: errno, port: candidate }, 'Failed to start WebSocket server.')\n process.exit(1)\n }\n }\n\n log.error(\n { candidates: wsPortCandidates },\n 'Unable to start WebSocket server on any candidate port.'\n )\n process.exit(1)\n}\n\nconst { wss, port } = await startWebSocketServer()\nselectedWsPort = port\n\n// Add an error handler to prevent crashes from port conflicts, etc.\nwss.on('error', (err) => {\n log.error({ err }, 'WebSocket server critical error. Exiting.')\n process.exit(1)\n})\n\nwss.on('connection', (ws) => {\n const ext: ExtensionConnection = { id: nanoid(), ws, active: false }\n extensions.push(ext)\n log.info({ id: ext.id }, `Extension connected. Total: ${extensions.length}`)\n\n const message: RegisteredMessage = { type: 'registered', id: ext.id }\n ws.send(JSON.stringify(message))\n broadcastState()\n scheduleAutoActivate()\n\n ws.on('message', (raw: RawData, isBinary: boolean) => {\n if (isBinary) {\n log.warn({ extId: ext.id }, 'Unexpected binary message received.')\n return\n }\n\n const messageBuffer = rawDataToBuffer(raw)\n\n let parsedJson: unknown\n try {\n parsedJson = JSON.parse(messageBuffer.toString('utf-8'))\n } catch (e: unknown) {\n log.warn({ err: e, extId: ext.id }, 'Failed to parse message.')\n return\n }\n\n const parseResult = MessageFromExtensionSchema.safeParse(parsedJson)\n if (!parseResult.success) {\n log.warn({ error: parseResult.error.flatten(), extId: ext.id }, 'Invalid message shape.')\n return\n }\n const msg = parseResult.data\n\n switch (msg.type) {\n case 'activate': {\n setActive(ext.id)\n log.info({ id: ext.id }, 'Extension activated.')\n broadcastState()\n scheduleAutoActivate()\n break\n }\n case 'toolResult': {\n const { id, payload, error } = msg as ToolResultMessage\n if (error) {\n const normalized = coerceToolError(error)\n log.warn(\n {\n toolReq: id,\n extId: ext.id,\n code: getRecordProperty(normalized, 'code'),\n message: normalized.message\n },\n 'Received tool error from extension.'\n )\n reject(id, normalized)\n } else {\n resolve(id, payload)\n }\n break\n }\n }\n })\n\n ws.on('close', () => {\n const index = extensions.findIndex((e) => e.id === ext.id)\n if (index > -1) extensions.splice(index, 1)\n\n log.info({ id: ext.id }, `Extension disconnected. Remaining: ${extensions.length}`)\n cleanupForExtension(ext.id)\n\n if (ext.active) {\n log.warn({ id: ext.id }, 'Active extension disconnected.')\n setActive(null)\n }\n\n broadcastState()\n scheduleAutoActivate()\n })\n})\n\nlog.info({ port: selectedWsPort }, 'WebSocket server ready.')\n\nprocess.on('SIGINT', shutdown)\nprocess.on('SIGTERM', shutdown)\n"],"mappings":"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"}
1
+ {"version":3,"file":"hub.mjs","names":["createServer","getRecordProperty","MCP_INSTRUCTIONS"],"sources":["../../shared/dist/index.js","../src/asset-utils.ts","../src/config.ts","../src/asset-http-server.ts","../src/asset-store.ts","../src/instructions.md?raw","../src/request.ts","../src/tools.ts","../src/hub.ts"],"sourcesContent":["import { z } from \"zod\";\n\n//#region src/mcp/constants.ts\nconst MCP_PORT_CANDIDATES = [\n\t6220,\n\t7431,\n\t8127\n];\nconst MCP_MAX_PAYLOAD_BYTES = 4 * 1024 * 1024;\nconst MCP_TOOL_TIMEOUT_MS = 15e3;\nconst MCP_AUTO_ACTIVATE_GRACE_MS = 1500;\nconst MCP_MAX_ASSET_BYTES = 8 * 1024 * 1024;\nconst MCP_ASSET_TTL_MS = 720 * 60 * 60 * 1e3;\nconst MCP_HASH_HEX_LENGTH = 8;\nconst MCP_HASH_PATTERN = new RegExp(`^[a-f0-9]{${MCP_HASH_HEX_LENGTH}}$`, \"i\");\n\n//#endregion\n//#region src/mcp/errors.ts\nconst TEMPAD_MCP_ERROR_CODES = {\n\tNO_ACTIVE_EXTENSION: \"NO_ACTIVE_EXTENSION\",\n\tEXTENSION_TIMEOUT: \"EXTENSION_TIMEOUT\",\n\tEXTENSION_DISCONNECTED: \"EXTENSION_DISCONNECTED\",\n\tINVALID_SELECTION: \"INVALID_SELECTION\",\n\tNODE_NOT_VISIBLE: \"NODE_NOT_VISIBLE\",\n\tASSET_SERVER_NOT_CONFIGURED: \"ASSET_SERVER_NOT_CONFIGURED\",\n\tTRANSPORT_NOT_CONNECTED: \"TRANSPORT_NOT_CONNECTED\"\n};\n\n//#endregion\n//#region src/mcp/protocol.ts\nconst RegisteredMessageSchema = z.object({\n\ttype: z.literal(\"registered\"),\n\tid: z.string()\n});\nconst StateMessageSchema = z.object({\n\ttype: z.literal(\"state\"),\n\tactiveId: z.string().nullable(),\n\tcount: z.number().nonnegative(),\n\tport: z.number().positive(),\n\tassetServerUrl: z.string().url()\n});\nconst ToolCallPayloadSchema = z.object({\n\tname: z.string(),\n\targs: z.unknown()\n});\nconst ToolCallMessageSchema = z.object({\n\ttype: z.literal(\"toolCall\"),\n\tid: z.string(),\n\tpayload: ToolCallPayloadSchema\n});\nconst MessageToExtensionSchema = z.discriminatedUnion(\"type\", [\n\tRegisteredMessageSchema,\n\tStateMessageSchema,\n\tToolCallMessageSchema\n]);\nconst ActivateMessageSchema = z.object({ type: z.literal(\"activate\") });\nconst ToolResultMessageSchema = z.object({\n\ttype: z.literal(\"toolResult\"),\n\tid: z.string(),\n\tpayload: z.unknown().optional(),\n\terror: z.unknown().optional()\n});\nconst MessageFromExtensionSchema = z.discriminatedUnion(\"type\", [ActivateMessageSchema, ToolResultMessageSchema]);\nfunction parseJsonWithSchema(data, schema) {\n\tlet parsed;\n\ttry {\n\t\tparsed = JSON.parse(data);\n\t} catch {\n\t\treturn null;\n\t}\n\tconst result = schema.safeParse(parsed);\n\treturn result.success ? result.data : null;\n}\nfunction parseMessageToExtension(data) {\n\treturn parseJsonWithSchema(data, MessageToExtensionSchema);\n}\nfunction parseMessageFromExtension(data) {\n\treturn parseJsonWithSchema(data, MessageFromExtensionSchema);\n}\n\n//#endregion\n//#region src/mcp/tools.ts\nconst AssetDescriptorSchema = z.object({\n\thash: z.string().min(1),\n\turl: z.string().url(),\n\tmimeType: z.string().min(1),\n\tsize: z.number().int().nonnegative(),\n\twidth: z.number().int().positive().optional(),\n\theight: z.number().int().positive().optional()\n});\nconst GetCodeParametersSchema = z.object({\n\tnodeId: z.string().describe(\"Optional target node id; omit to use the current single selection when pulling the baseline snapshot.\").optional(),\n\tpreferredLang: z.enum([\"jsx\", \"vue\"]).describe(\"Preferred output language to bias the snapshot; otherwise uses the design’s hint/detected language, then falls back to JSX.\").optional(),\n\tresolveTokens: z.boolean().describe(\"Inline token values instead of references for quick renders; default false returns token metadata so you can map into your theming system. When true, values are resolved per-node (mode-aware).\").optional()\n});\nconst GetTokenDefsParametersSchema = z.object({\n\tnames: z.array(z.string().regex(/^--[a-zA-Z0-9-_]+$/)).min(1).describe(\"Canonical token names (CSS variable form) from Object.keys(get_code.tokens) or your own list to resolve, e.g., --color-primary.\"),\n\tincludeAllModes: z.boolean().describe(\"Include all token modes (light/dark/etc.) instead of just the active one to mirror responsive tokens; default false.\").optional()\n});\nconst GetScreenshotParametersSchema = z.object({ nodeId: z.string().describe(\"Optional node id to screenshot; defaults to the current single selection. Useful when layout/overlap is uncertain (auto-layout none/inferred).\").optional() });\nconst GetStructureParametersSchema = z.object({\n\tnodeId: z.string().describe(\"Optional node id to outline; defaults to the current single selection. Useful when auto-layout hints are none/inferred or you need explicit geometry for refactors.\").optional(),\n\toptions: z.object({ depth: z.number().int().positive().describe(\"Limit traversal depth; defaults to full tree (subject to safety caps).\").optional() }).optional()\n});\nconst GetAssetsParametersSchema = z.object({ hashes: z.array(z.string().regex(MCP_HASH_PATTERN)).min(1).describe(\"Asset hashes returned from get_code (or other tools) to download/resolve exact bytes for rasterized images or SVGs before routing through your asset pipeline.\") });\nconst GetAssetsResultSchema = z.object({\n\tassets: z.array(AssetDescriptorSchema),\n\tmissing: z.array(z.string().min(1))\n});\n\n//#endregion\n//#region src/figma/color.ts\n/**\n* Color utilities for Figma styles\n*/\n/**\n* Formats a Figma color with opacity to hex notation\n* @param color RGB color object with values 0-1\n* @param opacity Optional opacity value 0-1\n* @returns Hex color string (e.g., \"#FF0000\" or \"#FF0000CC\")\n*/\nfunction formatHexAlpha(color, opacity = 1) {\n\tconst toHex = (n) => {\n\t\treturn Math.min(255, Math.max(0, Math.round(n * 255))).toString(16).padStart(2, \"0\").toUpperCase();\n\t};\n\tconst r = toHex(color.r);\n\tconst g = toHex(color.g);\n\tconst b = toHex(color.b);\n\tif (opacity >= .99) {\n\t\tif (r[0] === r[1] && g[0] === g[1] && b[0] === b[1]) return `#${r[0]}${g[0]}${b[0]}`;\n\t\treturn `#${r}${g}${b}`;\n\t}\n\tconst a = toHex(opacity);\n\tif (r[0] === r[1] && g[0] === g[1] && b[0] === b[1] && a[0] === a[1]) return `#${r[0]}${g[0]}${b[0]}${a[0]}`;\n\treturn `#${r}${g}${b}${a}`;\n}\n\n//#endregion\n//#region src/figma/gradient.ts\nconst RE_NON_ASCII = /\\P{ASCII}+/gu;\nconst RE_QUOTES = /['\"]/g;\nconst RE_SLASH = /\\//g;\nconst RE_SPACE_TAB = /[ \\t]+/g;\nconst RE_WHITESPACE = /\\s+/g;\nconst RE_FAST_PATH = /^[A-Za-z0-9_-]+$/;\nconst RE_BOUND_NON_ALPHANUM = /[^A-Za-z0-9]+/g;\nconst RE_HYPHENS = /-+/g;\nconst RE_BOUND_DIGIT = /([A-Za-z])([0-9])|([0-9])([A-Za-z])/g;\nconst RE_BOUND_CASE = /([a-z])([A-Z])|([A-Z])([A-Z][a-z])/g;\nconst RE_DIGIT = /^\\d+$/;\nconst RE_CAPS = /^[A-Z]+$/;\nconst RE_SINGLE = /^[A-Za-z]$/;\nfunction isVisiblePaint(paint) {\n\treturn !!paint && paint.visible !== false;\n}\nfunction isGradientPaint(paint) {\n\treturn \"gradientStops\" in paint && Array.isArray(paint.gradientStops);\n}\nfunction isSolidPaint(paint) {\n\treturn paint.type === \"SOLID\";\n}\nfunction hasGradientHandlePositions(paint) {\n\treturn \"gradientHandlePositions\" in paint && Array.isArray(paint.gradientHandlePositions);\n}\n/**\n* Resolves gradient from paint array\n* Returns CSS gradient string or null\n*/\nfunction resolveGradientFromPaints(paints, size) {\n\tif (!paints || !Array.isArray(paints)) return null;\n\tconst gradientPaint = paints.find((paint) => isVisiblePaint(paint) && isGradientPaint(paint));\n\tif (!gradientPaint) return null;\n\tconst fillOpacity = typeof gradientPaint.opacity === \"number\" ? gradientPaint.opacity : 1;\n\tconst stops = gradientPaint.gradientStops.map((stop) => {\n\t\tconst pct = formatPercent(stop.position);\n\t\treturn `${formatGradientStopColor(stop, fillOpacity)} ${pct}`;\n\t});\n\tswitch (gradientPaint.type) {\n\t\tcase \"GRADIENT_LINEAR\": {\n\t\t\tconst angle = resolveLinearGradientAngle(gradientPaint, size);\n\t\t\treturn `linear-gradient(${(angle == null ? stops : [`${angle}deg`, ...stops]).join(\", \")})`;\n\t\t}\n\t\tcase \"GRADIENT_RADIAL\":\n\t\tcase \"GRADIENT_DIAMOND\": return `radial-gradient(${stops.join(\", \")})`;\n\t\tcase \"GRADIENT_ANGULAR\": return `conic-gradient(${stops.join(\", \")})`;\n\t\tdefault: return null;\n\t}\n}\n/**\n* Resolves solid color from paint array\n* Returns hex color string or null\n*/\nfunction resolveSolidFromPaints(paints) {\n\tif (!paints || !Array.isArray(paints)) return null;\n\tconst solidPaint = paints.find((paint) => isVisiblePaint(paint) && isSolidPaint(paint));\n\tif (!solidPaint || !solidPaint.color) return null;\n\tconst bound = solidPaint.boundVariables?.color;\n\tif (bound && typeof bound === \"object\" && \"id\" in bound && bound.id) try {\n\t\tconst variable = figma.variables.getVariableById(bound.id);\n\t\tif (variable) {\n\t\t\tconst fallback = formatHexAlpha(solidPaint.color, solidPaint.opacity);\n\t\t\treturn `var(${getVariableCssCustomPropertyName(variable)}, ${fallback})`;\n\t\t}\n\t} catch {}\n\treturn formatHexAlpha(solidPaint.color, solidPaint.opacity);\n}\n/**\n* Formats a gradient stop color\n*/\nfunction formatGradientStopColor(stop, fillOpacity) {\n\tconst baseAlpha = stop.color?.a ?? 1;\n\tconst alpha = Math.max(0, Math.min(1, baseAlpha * fillOpacity));\n\tconst bound = stop.boundVariables?.color;\n\tif (bound && typeof bound === \"object\" && \"id\" in bound && bound.id) try {\n\t\tconst v = figma.variables.getVariableById(bound.id);\n\t\tif (v) {\n\t\t\tconst fallbackOpaque = formatHexAlpha(stop.color, 1);\n\t\t\tconst fallbackAlpha = formatHexAlpha(stop.color, alpha);\n\t\t\tconst cssVarName = getVariableCssCustomPropertyName(v);\n\t\t\tconst varName = `var(${cssVarName}, ${fallbackOpaque})`;\n\t\t\tif (alpha >= .99) return `var(${cssVarName}, ${fallbackAlpha})`;\n\t\t\treturn `color-mix(in srgb, ${varName} ${Math.round(alpha * 1e4) / 100}%, transparent)`;\n\t\t}\n\t} catch {}\n\treturn formatHexAlpha(stop.color, alpha);\n}\n/**\n* Resolves linear gradient angle from gradient paint\n*/\nfunction resolveLinearGradientAngle(paint, size) {\n\tif (hasGradientHandlePositions(paint) && paint.gradientHandlePositions.length >= 2) {\n\t\tconst start = paint.gradientHandlePositions[0];\n\t\tconst end = paint.gradientHandlePositions[1];\n\t\tif (start && end) {\n\t\t\tconst { width, height } = getGradientSize(size);\n\t\t\tconst angle = normalizeGradientAngle((end.x - start.x) * width, (end.y - start.y) * height);\n\t\t\tif (angle != null) return angle;\n\t\t}\n\t}\n\tconst extracted = extractLinearGradientVectorFromTransform(paint.gradientTransform, size);\n\tif (!extracted) return null;\n\tconst { dx, dy } = extracted;\n\treturn normalizeGradientAngle(dx, dy);\n}\nfunction extractLinearGradientVectorFromTransform(transform, size) {\n\tif (!transform || !Array.isArray(transform) || transform.length < 2) return null;\n\tconst row0 = transform[0];\n\tconst row1 = transform[1];\n\tif (!Array.isArray(row0) || !Array.isArray(row1) || row0.length < 2 || row1.length < 2) return null;\n\tconst a = row0[0];\n\tconst c = row0[1];\n\tconst e = row0[2] ?? 0;\n\tconst b = row1[0];\n\tconst d = row1[1];\n\tconst f = row1[2] ?? 0;\n\tif (![\n\t\ta,\n\t\tb,\n\t\tc,\n\t\td,\n\t\te,\n\t\tf\n\t].every((value) => Number.isFinite(value))) return null;\n\tconst det = a * d - b * c;\n\tif (!Number.isFinite(det) || Math.abs(det) < 1e-8) return null;\n\tconst invA = d / det;\n\tconst invC = -c / det;\n\tconst invE = (c * f - d * e) / det;\n\tconst invB = -b / det;\n\tconst invD = a / det;\n\tconst invF = (b * e - a * f) / det;\n\tconst start = applyTransform(invA, invC, invE, invB, invD, invF, 0, .5);\n\tconst end = applyTransform(invA, invC, invE, invB, invD, invF, 1, .5);\n\tconst { width, height } = getGradientSize(size);\n\treturn {\n\t\tdx: (end.x - start.x) * width,\n\t\tdy: (end.y - start.y) * height\n\t};\n}\nfunction applyTransform(a, c, e, b, d, f, x, y) {\n\treturn {\n\t\tx: a * x + c * y + e,\n\t\ty: b * x + d * y + f\n\t};\n}\nfunction getGradientSize(size) {\n\tconst width = size?.width;\n\tconst height = size?.height;\n\treturn {\n\t\twidth: typeof width === \"number\" && Number.isFinite(width) && width > 0 ? width : 1,\n\t\theight: typeof height === \"number\" && Number.isFinite(height) && height > 0 ? height : 1\n\t};\n}\n/**\n* Normalizes gradient angle to degrees\n*/\nfunction normalizeGradientAngle(dx, dy) {\n\tif (!Number.isFinite(dx) || !Number.isFinite(dy)) return null;\n\tif (dx === 0 && dy === 0) return null;\n\tlet angle = Math.atan2(dy, dx) * 180 / Math.PI + 90;\n\tangle = (angle % 360 + 360) % 360;\n\treturn Math.round(angle * 100) / 100;\n}\n/**\n* Formats position as percentage\n*/\nfunction formatPercent(pos) {\n\treturn `${Math.round(pos * 1e4) / 100}%`;\n}\n/**\n* Variable naming must match MCP token indexing semantics exactly.\n*/\nfunction getVariableCssCustomPropertyName(variable) {\n\treturn normalizeFigmaVarName(getVariableRawName(variable));\n}\nfunction getVariableRawName(variable) {\n\tconst cs = variable.codeSyntax?.WEB;\n\tif (typeof cs === \"string\" && cs.trim()) {\n\t\tconst canonical = canonicalizeVarName(cs.trim());\n\t\tif (canonical) return canonical.slice(2);\n\t\tconst ident = cs.trim();\n\t\tif (/^[A-Za-z0-9_-]+$/.test(ident)) return ident;\n\t}\n\tconst raw = variable.name?.trim?.() ?? \"\";\n\tif (raw.startsWith(\"--\")) return raw.slice(2);\n\treturn raw;\n}\nfunction canonicalizeVarName(value) {\n\tconst cleaned = value.trim();\n\tconst varMatch = cleaned.match(/^var\\(\\s*(--[A-Za-z0-9_-]+)(?:\\s*,[\\s\\S]*)?\\)$/);\n\tif (varMatch?.[1]) return normalizeCustomPropertyName(varMatch[1]);\n\tif (cleaned.startsWith(\"--\")) return normalizeCustomPropertyName(cleaned);\n\treturn null;\n}\nfunction normalizeCustomPropertyBody(name) {\n\tif (!name) return \"var\";\n\tlet raw = name.trim();\n\tif (raw.startsWith(\"--\")) raw = raw.slice(2);\n\traw = raw.replace(/^-+/, \"\");\n\traw = raw.replace(/[^A-Za-z0-9_-]/g, \"\");\n\treturn raw || \"var\";\n}\nfunction normalizeCustomPropertyName(name) {\n\treturn `--${normalizeCustomPropertyBody(name)}`;\n}\nfunction normalizeFigmaVarName(input) {\n\tlet raw = (input ?? \"\").trim();\n\tif (!raw) return \"--unnamed\";\n\tconst canonical = canonicalizeVarName(raw);\n\tif (canonical) return canonical;\n\tif (raw.startsWith(\"--\")) raw = raw.slice(2).trim();\n\traw = raw.replace(RE_NON_ASCII, \"\").replace(RE_QUOTES, \"\").replace(RE_SLASH, \"-\").replace(RE_SPACE_TAB, \"-\").replace(RE_WHITESPACE, \"\");\n\tif (RE_FAST_PATH.test(raw)) return `--${raw}`;\n\tconst parts = raw.replace(RE_BOUND_NON_ALPHANUM, \"-\").replace(RE_HYPHENS, \"-\").replace(RE_BOUND_DIGIT, \"$1-$2$3-$4\").replace(RE_BOUND_CASE, \"$1$3-$2$4\").split(\"-\").filter(Boolean);\n\tconst stack = [];\n\tfor (const part of parts) {\n\t\tconst prev = stack[stack.length - 1];\n\t\tif (prev && RE_DIGIT.test(prev) && RE_DIGIT.test(part)) stack[stack.length - 1] += part;\n\t\telse if (prev && RE_CAPS.test(prev) && RE_CAPS.test(part)) stack[stack.length - 1] += part;\n\t\telse stack.push(part);\n\t}\n\tconst merged = [];\n\tfor (let i = 0; i < stack.length;) {\n\t\tif (i === 0) {\n\t\t\tmerged.push(stack[0]);\n\t\t\ti += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (RE_SINGLE.test(stack[i])) {\n\t\t\tlet j = i + 1;\n\t\t\twhile (j < stack.length && RE_SINGLE.test(stack[j])) j += 1;\n\t\t\tconst run = stack.slice(i, j);\n\t\t\tmerged.push(run.length >= 2 ? run.join(\"\") : stack[i]);\n\t\t\ti = j;\n\t\t\tcontinue;\n\t\t}\n\t\tmerged.push(stack[i]);\n\t\ti += 1;\n\t}\n\tconst out = merged.join(\"-\").toLowerCase();\n\treturn out ? `--${out}` : \"--unnamed\";\n}\n\n//#endregion\n//#region src/figma/style-resolver.ts\nconst BG_URL_LIGHTGRAY_RE = /url\\(.*?\\)\\s+lightgray/i;\nconst BG_URL_RE = /url\\(/i;\nfunction hasStyleId(value) {\n\treturn typeof value === \"string\" && value.length > 0;\n}\nfunction isPaintStyle(style) {\n\treturn !!style && \"paints\" in style && Array.isArray(style.paints);\n}\nfunction resolvePaintStyleFromPaints(paints, size) {\n\tif (!paints) return null;\n\tconst gradient = resolveGradientFromPaints(paints, size);\n\tif (gradient) return { gradient };\n\tconst solidColor = resolveSolidFromPaints(paints);\n\treturn solidColor ? { solidColor } : null;\n}\nfunction resolvePaintStyleFromStyleId(styleId, kind, size) {\n\tif (!hasStyleId(styleId)) return null;\n\ttry {\n\t\tconst style = figma.getStyleById(styleId);\n\t\tif (!isPaintStyle(style)) return null;\n\t\treturn resolvePaintStyleFromPaints(style.paints, size);\n\t} catch (error) {\n\t\tconsole.warn(`Failed to resolve ${kind} style:`, error);\n\t\treturn null;\n\t}\n}\nfunction getNodeFillStyleId(node) {\n\treturn \"fillStyleId\" in node ? node.fillStyleId : null;\n}\nfunction getNodeStrokeStyleId(node) {\n\treturn \"strokeStyleId\" in node ? node.strokeStyleId : null;\n}\nfunction getNodeFillPaints(node) {\n\tif (\"fills\" in node && Array.isArray(node.fills)) return node.fills;\n\treturn null;\n}\nfunction getNodeStrokePaints(node) {\n\tif (\"strokes\" in node && Array.isArray(node.strokes)) return node.strokes;\n\treturn null;\n}\nfunction resolveNodePaintStyle(styleId, paints, kind, size) {\n\treturn resolvePaintStyleFromStyleId(styleId, kind, size) ?? resolvePaintStyleFromPaints(paints, size);\n}\nfunction getNodeDimensions(node) {\n\tif (!(\"width\" in node) || !(\"height\" in node)) return void 0;\n\tconst width = node.width;\n\tconst height = node.height;\n\tif (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return;\n\treturn {\n\t\twidth,\n\t\theight\n\t};\n}\nfunction splitByTopLevelWhitespace$1(input) {\n\tconst out = [];\n\tlet depth = 0;\n\tlet quote = null;\n\tlet buffer = \"\";\n\tfor (let i = 0; i < input.length; i++) {\n\t\tconst ch = input[i];\n\t\tif (quote) {\n\t\t\tif (ch === \"\\\\\") {\n\t\t\t\tbuffer += ch;\n\t\t\t\ti++;\n\t\t\t\tif (i < input.length) buffer += input[i];\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ch === quote) quote = null;\n\t\t\tbuffer += ch;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \"\\\"\" || ch === \"'\") {\n\t\t\tquote = ch;\n\t\t\tbuffer += ch;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \"(\") depth++;\n\t\telse if (ch === \")\") depth = Math.max(0, depth - 1);\n\t\tif (/\\s/.test(ch) && depth === 0) {\n\t\t\tif (buffer) {\n\t\t\t\tout.push(buffer);\n\t\t\t\tbuffer = \"\";\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tbuffer += ch;\n\t}\n\tif (buffer) out.push(buffer);\n\treturn out;\n}\nfunction isVarFunctionToken(value) {\n\tconst trimmed = value.trim();\n\treturn trimmed.startsWith(\"var(\") && trimmed.endsWith(\")\");\n}\nfunction patchBorderVarColor$1(borderValue, color) {\n\tconst borderParts = splitByTopLevelWhitespace$1(borderValue);\n\tif (!borderParts.length) return null;\n\tconst lastIndex = borderParts.length - 1;\n\tif (!isVarFunctionToken(borderParts[lastIndex])) return null;\n\tborderParts[lastIndex] = color;\n\treturn borderParts.join(\" \");\n}\nfunction hasBorderChannels(style) {\n\treturn Object.entries(style).some(([key, value]) => {\n\t\tif (!value?.trim()) return false;\n\t\tif (!/^border(?:$|-)/.test(key)) return false;\n\t\tif (key.includes(\"radius\") || key === \"border-image\" || key === \"border-image-slice\") return false;\n\t\treturn true;\n\t});\n}\n/**\n* Resolves fill style for a Figma node\n* Handles both fillStyleId and direct fills\n*/\nfunction resolveFillStyleForNode(node) {\n\treturn resolveNodePaintStyle(getNodeFillStyleId(node), getNodeFillPaints(node), \"fill\", getNodeDimensions(node));\n}\n/**\n* Resolves stroke style for a Figma node\n* Handles both strokeStyleId and direct strokes\n*/\nfunction resolveStrokeStyleForNode(node) {\n\treturn resolveNodePaintStyle(getNodeStrokeStyleId(node), getNodeStrokePaints(node), \"stroke\", getNodeDimensions(node));\n}\n/**\n* Main function to resolve all styles from a node\n* Replaces CSS variable references with actual values\n*/\nasync function resolveStylesFromNode(cssStyles, node) {\n\tconst processed = { ...cssStyles };\n\tconst fillPaints = getNodeFillPaints(node);\n\tif (processed.background && BG_URL_LIGHTGRAY_RE.test(processed.background) && fillPaints) {\n\t\tconst solidFill = resolveSolidFromPaints(fillPaints);\n\t\tif (solidFill) processed[\"background-color\"] = solidFill;\n\t\tprocessed.background = processed.background.replace(/\\s*,?\\s*lightgray\\b/i, \"\").trim();\n\t}\n\tconst resolvedFill = resolveFillStyleForNode(node);\n\tconst hasUrlBackground = typeof processed.background === \"string\" && BG_URL_RE.test(processed.background);\n\tif (resolvedFill?.gradient) {\n\t\tif (processed.background && !hasUrlBackground) processed.background = resolvedFill.gradient;\n\t\telse if (processed[\"background-color\"]) {\n\t\t\tprocessed.background = resolvedFill.gradient;\n\t\t\tdelete processed[\"background-color\"];\n\t\t}\n\t} else if (resolvedFill?.solidColor) {\n\t\tif (processed.background && !hasUrlBackground) {\n\t\t\tprocessed[\"background-color\"] = resolvedFill.solidColor;\n\t\t\tdelete processed.background;\n\t\t}\n\t\tif (processed[\"background-color\"]) processed[\"background-color\"] = resolvedFill.solidColor;\n\t\tif (processed.color) processed.color = resolvedFill.solidColor;\n\t\tif (processed.fill) processed.fill = resolvedFill.solidColor;\n\t}\n\tconst resolvedStroke = resolveStrokeStyleForNode(node);\n\tif (resolvedStroke?.gradient && hasBorderChannels(processed)) {\n\t\tprocessed[\"border-image\"] = resolvedStroke.gradient;\n\t\tprocessed[\"border-image-slice\"] = \"1\";\n\t} else if (resolvedStroke?.solidColor) {\n\t\tif (processed[\"border-color\"]) processed[\"border-color\"] = resolvedStroke.solidColor;\n\t\telse if (processed.border) {\n\t\t\tconst patched = patchBorderVarColor$1(processed.border, resolvedStroke.solidColor);\n\t\t\tif (patched) processed.border = patched;\n\t\t}\n\t}\n\tif (processed.stroke && resolvedStroke?.gradient) processed.stroke = resolvedStroke.gradient;\n\telse if (processed.stroke && resolvedStroke?.solidColor) processed.stroke = resolvedStroke.solidColor;\n\treturn processed;\n}\n\n//#endregion\n//#region src/figma/stroke.ts\nfunction splitByTopLevelWhitespace(input) {\n\tconst out = [];\n\tlet depth = 0;\n\tlet quote = null;\n\tlet buffer = \"\";\n\tfor (let i = 0; i < input.length; i++) {\n\t\tconst ch = input[i];\n\t\tif (quote) {\n\t\t\tif (ch === \"\\\\\") {\n\t\t\t\tbuffer += ch;\n\t\t\t\ti++;\n\t\t\t\tif (i < input.length) buffer += input[i];\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ch === quote) quote = null;\n\t\t\tbuffer += ch;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \"\\\"\" || ch === \"'\") {\n\t\t\tquote = ch;\n\t\t\tbuffer += ch;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \"(\") depth++;\n\t\telse if (ch === \")\") depth = Math.max(0, depth - 1);\n\t\tif (/\\s/.test(ch) && depth === 0) {\n\t\t\tif (buffer) {\n\t\t\t\tout.push(buffer);\n\t\t\t\tbuffer = \"\";\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tbuffer += ch;\n\t}\n\tif (buffer) out.push(buffer);\n\treturn out;\n}\nfunction patchBorderVarColor(borderValue, color) {\n\tconst borderParts = splitByTopLevelWhitespace(borderValue);\n\tif (!borderParts.length) return null;\n\tconst lastIndex = borderParts.length - 1;\n\tconst tail = borderParts[lastIndex].trim();\n\tif (!tail.startsWith(\"var(\") || !tail.endsWith(\")\")) return null;\n\tborderParts[lastIndex] = color;\n\treturn borderParts.join(\" \");\n}\n/**\n* Resolves stroke styles from paint array\n* Can handle both solid colors and gradients\n* @param paints Array of paint objects from strokes or stroke style\n* @returns Object with solidColor or gradient, or null\n*/\nfunction resolveStrokeFromPaints(paints) {\n\tif (!paints || !Array.isArray(paints)) return null;\n\tconst gradient = resolveGradientFromPaints(paints);\n\tif (gradient) return { gradient };\n\tconst solidColor = resolveSolidFromPaints(paints);\n\tif (solidColor) return { solidColor };\n\treturn null;\n}\n/**\n* Applies resolved stroke styles to CSS properties\n* Handles different CSS properties for stroke (border, stroke, outline)\n*/\nfunction applyStrokeToCSS(styles, resolved) {\n\tif (!resolved) return styles;\n\tconst processed = { ...styles };\n\tconst borderHasVar = processed.border?.includes(\"var(--\") ?? false;\n\tconst borderColorHasVar = processed[\"border-color\"]?.includes(\"var(--\") ?? false;\n\tif (borderHasVar || borderColorHasVar) {\n\t\tif (resolved.gradient) {\n\t\t\tprocessed[\"border-image\"] = `${resolved.gradient} 1`;\n\t\t\tprocessed[\"border-image-slice\"] = \"1\";\n\t\t\tdelete processed[\"border-color\"];\n\t\t} else if (resolved.solidColor) if (borderColorHasVar) processed[\"border-color\"] = resolved.solidColor;\n\t\telse {\n\t\t\tconst patched = patchBorderVarColor(processed.border, resolved.solidColor);\n\t\t\tif (patched) processed.border = patched;\n\t\t}\n\t}\n\tif (processed.stroke?.includes(\"var(--\")) {\n\t\tif (resolved.gradient) processed.stroke = resolved.gradient;\n\t\telse if (resolved.solidColor) processed.stroke = resolved.solidColor;\n\t}\n\tif (processed[\"outline-color\"]?.includes(\"var(--\")) {\n\t\tif (resolved.solidColor) processed[\"outline-color\"] = resolved.solidColor;\n\t}\n\treturn processed;\n}\n\n//#endregion\nexport { ActivateMessageSchema, AssetDescriptorSchema, GetAssetsParametersSchema, GetAssetsResultSchema, GetCodeParametersSchema, GetScreenshotParametersSchema, GetStructureParametersSchema, GetTokenDefsParametersSchema, MCP_ASSET_TTL_MS, MCP_AUTO_ACTIVATE_GRACE_MS, MCP_HASH_HEX_LENGTH, MCP_HASH_PATTERN, MCP_MAX_ASSET_BYTES, MCP_MAX_PAYLOAD_BYTES, MCP_PORT_CANDIDATES, MCP_TOOL_TIMEOUT_MS, MessageFromExtensionSchema, MessageToExtensionSchema, RegisteredMessageSchema, StateMessageSchema, TEMPAD_MCP_ERROR_CODES, ToolCallMessageSchema, ToolCallPayloadSchema, ToolResultMessageSchema, applyStrokeToCSS, formatHexAlpha, parseMessageFromExtension, parseMessageToExtension, resolveFillStyleForNode, resolveGradientFromPaints, resolveSolidFromPaints, resolveStrokeFromPaints, resolveStrokeStyleForNode, resolveStylesFromNode };","import { MCP_HASH_HEX_LENGTH } from '@tempad-dev/shared'\n\nconst HASH_FILENAME_PATTERN = new RegExp(\n `^([a-f0-9]{${MCP_HASH_HEX_LENGTH}})(?:\\\\.[a-z0-9]+)?$`,\n 'i'\n)\n\nconst MIME_EXTENSION_OVERRIDES = new Map<string, string>([['image/jpeg', 'jpg']])\n\nexport function normalizeMimeType(mimeType: string | undefined): string {\n if (!mimeType) return 'application/octet-stream'\n const [normalized] = mimeType.split(';', 1)\n return (normalized || 'application/octet-stream').trim().toLowerCase()\n}\n\nexport function getImageExtension(mimeType: string): string {\n const normalized = normalizeMimeType(mimeType)\n if (!normalized.startsWith('image/')) return ''\n const override = MIME_EXTENSION_OVERRIDES.get(normalized)\n if (override) return `.${override}`\n const subtype = normalized.slice('image/'.length)\n if (!subtype) return ''\n const ext = subtype.split('+', 1)[0] || subtype\n return `.${ext}`\n}\n\nexport function buildAssetFilename(hash: string, mimeType: string): string {\n const ext = getImageExtension(mimeType)\n return ext ? `${hash}${ext}` : hash\n}\n\nexport function getHashFromAssetFilename(filename: string): string | null {\n const match = HASH_FILENAME_PATTERN.exec(filename)\n return match ? match[1] : null\n}\n","import {\n MCP_AUTO_ACTIVATE_GRACE_MS,\n MCP_ASSET_TTL_MS,\n MCP_MAX_ASSET_BYTES,\n MCP_MAX_PAYLOAD_BYTES,\n MCP_PORT_CANDIDATES,\n MCP_TOOL_TIMEOUT_MS\n} from '@tempad-dev/shared'\n\nfunction parsePositiveInt(envValue: string | undefined, fallback: number): number {\n const parsed = envValue ? Number.parseInt(envValue, 10) : Number.NaN\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback\n}\n\nfunction parseNonNegativeInt(envValue: string | undefined, fallback: number): number {\n const parsed = envValue ? Number.parseInt(envValue, 10) : Number.NaN\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback\n}\n\nfunction resolveToolTimeoutMs(): number {\n return parsePositiveInt(process.env.TEMPAD_MCP_TOOL_TIMEOUT, MCP_TOOL_TIMEOUT_MS)\n}\n\nfunction resolveAutoActivateGraceMs(): number {\n return parsePositiveInt(process.env.TEMPAD_MCP_AUTO_ACTIVATE_GRACE, MCP_AUTO_ACTIVATE_GRACE_MS)\n}\n\nfunction resolveMaxAssetSizeBytes(): number {\n return parsePositiveInt(process.env.TEMPAD_MCP_MAX_ASSET_BYTES, MCP_MAX_ASSET_BYTES)\n}\n\nfunction resolveAssetTtlMs(): number {\n return parseNonNegativeInt(process.env.TEMPAD_MCP_ASSET_TTL_MS, MCP_ASSET_TTL_MS)\n}\n\nexport function getMcpServerConfig() {\n return {\n wsPortCandidates: [...MCP_PORT_CANDIDATES],\n toolTimeoutMs: resolveToolTimeoutMs(),\n maxPayloadBytes: MCP_MAX_PAYLOAD_BYTES,\n autoActivateGraceMs: resolveAutoActivateGraceMs(),\n maxAssetSizeBytes: resolveMaxAssetSizeBytes(),\n assetTtlMs: resolveAssetTtlMs()\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\n\nimport { MCP_HASH_HEX_LENGTH } from '@tempad-dev/shared'\nimport { nanoid } from 'nanoid'\nimport { createHash } from 'node:crypto'\nimport {\n createReadStream,\n createWriteStream,\n existsSync,\n renameSync,\n statSync,\n unlinkSync\n} from 'node:fs'\nimport { createServer } from 'node:http'\nimport { join } from 'node:path'\nimport { pipeline, Transform } from 'node:stream'\nimport { URL } from 'node:url'\n\nimport type { AssetStore } from './asset-store'\n\nimport { buildAssetFilename, getHashFromAssetFilename, normalizeMimeType } from './asset-utils'\nimport { getMcpServerConfig } from './config'\nimport { ASSET_DIR, log } from './shared'\n\nconst LOOPBACK_HOST = '127.0.0.1'\nconst HASH_HEX_PATTERN = new RegExp(`^[a-f0-9]{${MCP_HASH_HEX_LENGTH}}$`, 'i')\nconst { maxAssetSizeBytes } = getMcpServerConfig()\n\nexport interface AssetHttpServer {\n start(): Promise<void>\n stop(): void\n getBaseUrl(): string\n}\n\nexport function createAssetHttpServer(store: AssetStore): AssetHttpServer {\n const server = createServer(handleRequest)\n let port: number | null = null\n\n async function start(): Promise<void> {\n if (port !== null) return\n await new Promise<void>((resolve, reject) => {\n const onError = (error: Error) => {\n server.off('listening', onListening)\n reject(error)\n }\n const onListening = () => {\n server.off('error', onError)\n const address = server.address()\n if (address && typeof address === 'object') {\n port = address.port\n resolve()\n } else {\n reject(new Error('Failed to determine HTTP server port.'))\n }\n }\n server.once('error', onError)\n server.once('listening', onListening)\n server.listen(0, LOOPBACK_HOST)\n })\n log.info({ port }, 'Asset HTTP server ready.')\n }\n\n function stop(): void {\n if (port === null) return\n server.close()\n port = null\n }\n\n function getBaseUrl(): string {\n if (port === null) throw new Error('Asset HTTP server is not running.')\n return `http://${LOOPBACK_HOST}:${port}`\n }\n\n function handleRequest(req: IncomingMessage, res: ServerResponse): void {\n const startedAt = Date.now()\n res.on('finish', () => {\n log.info(\n {\n method: req.method,\n url: req.url,\n status: res.statusCode,\n durationMs: Date.now() - startedAt\n },\n 'HTTP asset request completed.'\n )\n })\n\n res.setHeader('Access-Control-Allow-Origin', '*')\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Asset-Width, X-Asset-Height')\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204)\n res.end()\n return\n }\n\n if (!req.url) {\n sendError(res, 400, 'Missing URL')\n return\n }\n\n const url = new URL(req.url, getBaseUrl())\n const segments = url.pathname.split('/').filter(Boolean)\n if (segments.length !== 2 || segments[0] !== 'assets') {\n sendError(res, 404, 'Not Found')\n return\n }\n\n const filename = segments[1]\n const hash = getHashFromAssetFilename(filename)\n if (!hash) {\n sendError(res, 404, 'Not Found')\n return\n }\n\n if (req.method === 'POST') {\n handleUpload(req, res, hash)\n return\n }\n\n if (req.method === 'GET') {\n handleDownload(req, res, hash)\n return\n }\n\n sendError(res, 405, 'Method Not Allowed')\n }\n\n function handleDownload(req: IncomingMessage, res: ServerResponse, hash: string): void {\n const record = store.get(hash)\n if (!record) {\n sendError(res, 404, 'Asset Not Found')\n return\n }\n\n let stat\n try {\n stat = statSync(record.filePath)\n } catch (error) {\n const err = error as NodeJS.ErrnoException\n if (err.code === 'ENOENT') {\n store.remove(hash, { removeFile: false })\n sendError(res, 404, 'Asset Not Found')\n } else {\n log.error({ error, hash }, 'Failed to stat asset file.')\n sendError(res, 500, 'Internal Server Error')\n }\n return\n }\n\n res.writeHead(200, {\n 'Content-Type': record.mimeType,\n 'Content-Length': stat.size.toString(),\n 'Cache-Control': 'public, max-age=31536000, immutable'\n })\n\n const stream = createReadStream(record.filePath)\n stream.on('error', (error) => {\n log.warn({ error, hash }, 'Failed to stream asset file.')\n if (!res.headersSent) {\n sendError(res, 500, 'Internal Server Error')\n } else {\n res.end()\n }\n })\n stream.on('open', () => {\n store.touch(hash)\n })\n stream.pipe(res)\n }\n\n function handleUpload(req: IncomingMessage, res: ServerResponse, hash: string): void {\n if (!HASH_HEX_PATTERN.test(hash)) {\n req.resume()\n sendError(res, 400, 'Invalid Hash')\n return\n }\n\n const contentTypeHeader = req.headers['content-type']\n const mimeType = normalizeMimeType(\n Array.isArray(contentTypeHeader) ? contentTypeHeader[0] : contentTypeHeader\n )\n const filename = buildAssetFilename(hash, mimeType)\n const filePath = join(ASSET_DIR, filename)\n\n const width = parseInt(req.headers['x-asset-width'] as string, 10)\n const height = parseInt(req.headers['x-asset-height'] as string, 10)\n const metadata =\n !isNaN(width) && !isNaN(height) && width > 0 && height > 0 ? { width, height } : undefined\n\n const existing = store.get(hash)\n if (existing) {\n let existingPath = existing.filePath\n if (!existsSync(existingPath) && existsSync(filePath)) {\n existing.filePath = filePath\n existingPath = filePath\n }\n\n if (existsSync(existingPath)) {\n if (existingPath !== filePath) {\n try {\n renameSync(existingPath, filePath)\n existing.filePath = filePath\n } catch (error) {\n log.warn({ error, hash }, 'Failed to rename existing asset to include extension.')\n }\n }\n\n // Drain request to ensure connection is clean\n req.resume()\n\n if (metadata) existing.metadata = metadata\n if (existing.mimeType !== mimeType) existing.mimeType = mimeType\n existing.lastAccess = Date.now()\n store.upsert(existing)\n sendOk(res, 200, 'Asset Already Exists')\n return\n }\n }\n\n const tmpPath = `${filePath}.tmp.${nanoid()}`\n const writeStream = createWriteStream(tmpPath)\n const hasher = createHash('sha256')\n let size = 0\n\n const cleanup = () => {\n if (existsSync(tmpPath)) {\n try {\n unlinkSync(tmpPath)\n } catch (e) {\n log.warn({ error: e, tmpPath }, 'Failed to cleanup temp file.')\n }\n }\n }\n\n const monitor = new Transform({\n transform(chunk, encoding, callback) {\n size += chunk.length\n if (size > maxAssetSizeBytes) {\n callback(new Error('PayloadTooLarge'))\n return\n }\n hasher.update(chunk)\n callback(null, chunk)\n }\n })\n\n pipeline(req, monitor, writeStream, (err) => {\n if (err) {\n cleanup()\n if (err.message === 'PayloadTooLarge') {\n sendError(res, 413, 'Payload Too Large')\n } else if (err.code === 'ERR_STREAM_PREMATURE_CLOSE') {\n log.warn({ hash }, 'Upload request closed prematurely.')\n sendError(res, 400, 'Upload Incomplete')\n } else {\n log.error({ error: err, hash }, 'Upload pipeline failed.')\n if (!res.headersSent) {\n sendError(res, 500, 'Internal Server Error')\n }\n }\n return\n }\n\n const computedHash = hasher.digest('hex').slice(0, MCP_HASH_HEX_LENGTH)\n if (computedHash !== hash) {\n cleanup()\n sendError(res, 400, 'Hash Mismatch')\n return\n }\n\n try {\n renameSync(tmpPath, filePath)\n } catch (error) {\n log.error({ error, hash }, 'Failed to rename temp file to asset.')\n cleanup()\n sendError(res, 500, 'Internal Server Error')\n return\n }\n\n store.upsert({\n hash,\n filePath,\n mimeType,\n size,\n metadata\n })\n log.info({ hash, size }, 'Stored uploaded asset via HTTP.')\n sendOk(res, 201, 'Created', { hash, size })\n })\n }\n\n function sendError(\n res: ServerResponse,\n status: number,\n message: string,\n details?: Record<string, unknown>\n ): void {\n if (!res.headersSent) {\n res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' })\n }\n res.end(\n JSON.stringify({\n error: message,\n ...details\n })\n )\n }\n\n function sendOk(\n res: ServerResponse,\n status: number,\n message: string,\n data?: Record<string, unknown>\n ): void {\n if (!res.headersSent) {\n res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' })\n }\n res.end(\n JSON.stringify({\n message,\n ...data\n })\n )\n }\n\n return {\n start,\n stop,\n getBaseUrl\n }\n}\n","import { existsSync, readFileSync, rmSync, writeFileSync, readdirSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport type { AssetRecord } from './types'\n\nimport { getHashFromAssetFilename } from './asset-utils'\nimport { ASSET_DIR, ensureDir, ensureFile, log } from './shared'\n\nconst INDEX_FILENAME = 'assets.json'\nconst DEFAULT_INDEX_PATH = join(ASSET_DIR, INDEX_FILENAME)\n\nexport interface AssetStoreOptions {\n indexPath?: string\n}\n\nexport interface AssetStore {\n list(): AssetRecord[]\n has(hash: string): boolean\n get(hash: string): AssetRecord | undefined\n getMany(hashes: string[]): AssetRecord[]\n upsert(\n input: Omit<AssetRecord, 'uploadedAt' | 'lastAccess'> &\n Partial<Pick<AssetRecord, 'uploadedAt' | 'lastAccess'>>\n ): AssetRecord\n touch(hash: string): AssetRecord | undefined\n remove(hash: string, opts?: { removeFile?: boolean }): void\n reconcile(): void\n flush(): void\n}\n\nfunction readIndex(indexPath: string): AssetRecord[] {\n if (!existsSync(indexPath)) return []\n try {\n const raw = readFileSync(indexPath, 'utf8').trim()\n if (!raw) return []\n const parsed = JSON.parse(raw)\n return Array.isArray(parsed) ? (parsed as AssetRecord[]) : []\n } catch (error) {\n log.warn({ error, indexPath }, 'Failed to read asset catalog; starting fresh.')\n return []\n }\n}\n\nfunction writeIndex(indexPath: string, values: AssetRecord[]): void {\n const payload = JSON.stringify(values, null, 2)\n writeFileSync(indexPath, payload, 'utf8')\n}\n\nexport function createAssetStore(options: AssetStoreOptions = {}): AssetStore {\n ensureDir(ASSET_DIR)\n const indexPath = options.indexPath ?? DEFAULT_INDEX_PATH\n ensureFile(indexPath)\n const records = new Map<string, AssetRecord>()\n let persistTimer: NodeJS.Timeout | null = null\n\n function loadExisting(): void {\n const list = readIndex(indexPath)\n for (const record of list) {\n if (record?.hash && record?.filePath) {\n records.set(record.hash, record)\n }\n }\n }\n\n function persist(): void {\n if (persistTimer) return\n persistTimer = setTimeout(() => {\n persistTimer = null\n writeIndex(indexPath, [...records.values()])\n }, 5000)\n if (typeof persistTimer.unref === 'function') {\n persistTimer.unref()\n }\n }\n\n function flush(): void {\n if (persistTimer) {\n clearTimeout(persistTimer)\n persistTimer = null\n }\n writeIndex(indexPath, [...records.values()])\n }\n\n function list(): AssetRecord[] {\n return [...records.values()]\n }\n\n function has(hash: string): boolean {\n return records.has(hash)\n }\n\n function get(hash: string): AssetRecord | undefined {\n return records.get(hash)\n }\n\n function getMany(hashes: string[]): AssetRecord[] {\n return hashes\n .map((hash) => records.get(hash))\n .filter((record): record is AssetRecord => !!record)\n }\n\n function upsert(\n input: Omit<AssetRecord, 'uploadedAt' | 'lastAccess'> &\n Partial<Pick<AssetRecord, 'uploadedAt' | 'lastAccess'>>\n ): AssetRecord {\n const now = Date.now()\n const record: AssetRecord = {\n ...input,\n uploadedAt: input.uploadedAt ?? now,\n lastAccess: input.lastAccess ?? now\n }\n records.set(record.hash, record)\n persist()\n return record\n }\n\n function touch(hash: string): AssetRecord | undefined {\n const existing = records.get(hash)\n if (!existing) return undefined\n existing.lastAccess = Date.now()\n persist()\n return existing\n }\n\n function remove(hash: string, { removeFile = true } = {}): void {\n const record = records.get(hash)\n if (!record) return\n records.delete(hash)\n persist()\n\n if (removeFile) {\n try {\n rmSync(record.filePath, { force: true })\n } catch (error) {\n log.warn({ hash, error }, 'Failed to remove asset file on delete.')\n }\n }\n }\n\n function reconcile(): void {\n let changed = false\n for (const [hash, record] of records) {\n if (!existsSync(record.filePath)) {\n records.delete(hash)\n changed = true\n }\n }\n\n try {\n const files = readdirSync(ASSET_DIR)\n const now = Date.now()\n for (const file of files) {\n if (file === INDEX_FILENAME) continue\n\n // Cleanup stale tmp files (> 1 hour)\n if (file.includes('.tmp.')) {\n try {\n const filePath = join(ASSET_DIR, file)\n const stat = statSync(filePath)\n if (now - stat.mtimeMs > 3600 * 1000) {\n rmSync(filePath, { force: true })\n log.info({ file }, 'Cleaned up stale temp file.')\n }\n } catch (e) {\n // Ignore errors during cleanup\n log.debug({ error: e, file }, 'Failed to cleanup stale temp file.')\n }\n continue\n }\n\n const hash = getHashFromAssetFilename(file)\n if (!hash) continue\n\n if (!records.has(hash)) {\n const filePath = join(ASSET_DIR, file)\n try {\n const stat = statSync(filePath)\n records.set(hash, {\n hash,\n filePath,\n mimeType: 'application/octet-stream',\n size: stat.size,\n uploadedAt: stat.birthtimeMs,\n lastAccess: stat.atimeMs\n })\n changed = true\n log.info({ hash }, 'Recovered orphan asset file.')\n } catch (e) {\n log.warn({ error: e, file }, 'Failed to stat orphan file.')\n }\n }\n }\n } catch (error) {\n log.warn({ error }, 'Failed to scan asset directory for orphans.')\n }\n\n if (changed) flush()\n }\n\n loadExisting()\n reconcile()\n\n return {\n list,\n has,\n get,\n getMany,\n upsert,\n touch,\n remove,\n reconcile,\n flush\n }\n}\n","export default \"You are connected to a Figma design file via TemPad Dev MCP.\\n\\nTreat tool outputs as design facts. Refactor only to match the user’s repo conventions; do not invent key style values.\\n\\nRules:\\n\\n- Never output any `data-hint-*` attributes from tool outputs (hints only).\\n- If `get_code` warns `depth-cap`, call `get_code` again for each listed `nodeId` before implementing.\\n- Use `get_structure` only to resolve layout/overlap uncertainty; do not derive numeric values from images.\\n- Tokens: `get_code.tokens` keys are canonical names (`--...`). Multi‑mode values use `${collectionName}:${modeName}`. Nodes may hint per-node overrides via `data-hint-variable-mode=\\\"Collection=Mode;...\\\"`.\\n- Assets: download bytes via `asset.url`. Asset resources are not exposed via MCP `resources/read`. Preserve SVG/vector assets exactly; never redraw vectors.\\n\"","import type { TempadMcpErrorCode } from '@tempad-dev/shared'\n\nimport { TEMPAD_MCP_ERROR_CODES } from '@tempad-dev/shared'\nimport { nanoid } from 'nanoid'\n\nimport type { PendingToolCall } from './types'\n\nimport { log } from './shared'\n\nconst pendingCalls = new Map<string, PendingToolCall>()\n\nfunction createToolError(\n code: TempadMcpErrorCode,\n message: string\n): Error & { code: TempadMcpErrorCode } {\n const err = new Error(message) as Error & { code: TempadMcpErrorCode }\n err.code = code\n return err\n}\n\nexport function register<T>(\n extensionId: string,\n timeout: number\n): { promise: Promise<T>; requestId: string } {\n const requestId = nanoid()\n const promise = new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingCalls.delete(requestId)\n reject(\n createToolError(\n TEMPAD_MCP_ERROR_CODES.EXTENSION_TIMEOUT,\n `Extension did not respond within ${timeout / 1000}s.`\n )\n )\n }, timeout)\n\n pendingCalls.set(requestId, {\n resolve: resolve as (value: unknown) => void,\n reject,\n timer,\n extensionId\n })\n })\n return { promise, requestId }\n}\n\nexport function resolve(requestId: string, payload: unknown): void {\n const call = pendingCalls.get(requestId)\n if (call) {\n const { timer, resolve: finish } = call\n clearTimeout(timer)\n finish(payload)\n pendingCalls.delete(requestId)\n } else {\n log.warn({ reqId: requestId }, 'Received result for unknown/timed-out call.')\n }\n}\n\nexport function reject(requestId: string, error: Error): void {\n const call = pendingCalls.get(requestId)\n if (call) {\n const { timer, reject: fail } = call\n clearTimeout(timer)\n fail(error)\n pendingCalls.delete(requestId)\n } else {\n log.warn({ reqId: requestId }, 'Received error for unknown/timed-out call.')\n }\n}\n\nexport function cleanupForExtension(extensionId: string): void {\n for (const [reqId, call] of pendingCalls.entries()) {\n const { timer, reject: fail, extensionId: extId } = call\n if (extId === extensionId) {\n clearTimeout(timer)\n fail(\n createToolError(\n TEMPAD_MCP_ERROR_CODES.EXTENSION_DISCONNECTED,\n 'Extension disconnected before providing a result.'\n )\n )\n pendingCalls.delete(reqId)\n log.warn({ reqId, extId: extensionId }, 'Rejected pending call from disconnected extension.')\n }\n }\n}\n\nexport function cleanupAll(): void {\n pendingCalls.forEach((call, reqId) => {\n const { timer, reject: fail } = call\n clearTimeout(timer)\n fail(new Error('Hub is shutting down.'))\n log.debug({ reqId }, 'Rejected pending tool call due to shutdown.')\n })\n pendingCalls.clear()\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport type {\n GetScreenshotResult,\n TempadMcpErrorCode,\n ToolName,\n ToolResultMap,\n ToolSchema\n} from '@tempad-dev/shared'\nimport type { ZodType } from 'zod'\n\nimport {\n GetAssetsParametersSchema,\n GetAssetsResultSchema,\n GetCodeParametersSchema,\n GetScreenshotParametersSchema,\n GetStructureParametersSchema,\n GetTokenDefsParametersSchema,\n TEMPAD_MCP_ERROR_CODES,\n type TempadMcpErrorPayload\n} from '@tempad-dev/shared'\n\nexport type {\n AssetDescriptor,\n GetAssetsParametersInput,\n GetAssetsResult,\n GetCodeParametersInput,\n GetCodeResult,\n GetScreenshotParametersInput,\n GetScreenshotResult,\n GetStructureParametersInput,\n GetStructureResult,\n GetTokenDefsParametersInput,\n GetTokenDefsResult,\n TokenEntry,\n ToolName,\n ToolResultMap,\n ToolSchema\n} from '@tempad-dev/shared'\n\ntype BaseToolMetadata<Name extends ToolName, Schema extends ZodType> = ToolSchema<Name> & {\n parameters: Schema\n format?: (payload: ToolResultMap[Name]) => CallToolResult\n}\n\ntype ExtensionToolMetadata<Name extends ToolName, Schema extends ZodType> = BaseToolMetadata<\n Name,\n Schema\n> & {\n target: 'extension'\n}\n\ntype HubToolMetadata<Name extends ToolName, Schema extends ZodType> = BaseToolMetadata<\n Name,\n Schema\n> & {\n target: 'hub'\n outputSchema?: ZodType\n}\n\nfunction getRecordProperty(record: unknown, key: string): unknown {\n if (!record || typeof record !== 'object') {\n return undefined\n }\n return Reflect.get(record, key)\n}\n\nfunction extTool<Name extends ToolName, Schema extends ZodType>(\n definition: ExtensionToolMetadata<Name, Schema>\n): ExtensionToolMetadata<Name, Schema> {\n return definition\n}\n\nfunction hubTool<Name extends ToolName, Schema extends ZodType>(\n definition: HubToolMetadata<Name, Schema>\n): HubToolMetadata<Name, Schema> {\n return definition\n}\n\nexport const TOOL_DEFS = [\n extTool({\n name: 'get_code',\n description:\n 'High-fidelity code snapshot for nodeId/current single selection (omit nodeId to use selection): JSX/Vue markup + Tailwind-like classes, plus assets/tokens metadata and codegen config. Start here, then refactor into repo conventions while preserving values/intent; strip any data-hint-* attributes (hints only). If warnings include depth-cap, call get_code again for each listed nodeId. If warnings include auto-layout (inferred), use get_structure to confirm hierarchy/overlap (do not derive numeric values from pixels). Tokens are keyed by canonical names like `--color-primary` (multi-mode keys use `${collection}:${mode}`; node overrides may appear as data-hint-variable-mode).',\n parameters: GetCodeParametersSchema,\n target: 'extension',\n format: createCodeToolResponse\n }),\n extTool({\n name: 'get_token_defs',\n description:\n 'Resolve canonical token names to literal values (optionally including all modes) for tokens referenced by get_code.',\n parameters: GetTokenDefsParametersSchema,\n target: 'extension',\n exposed: false\n }),\n extTool({\n name: 'get_screenshot',\n description:\n 'Capture a rendered PNG screenshot for nodeId/current single selection for visual verification (layering/overlap/masks/effects).',\n parameters: GetScreenshotParametersSchema,\n target: 'extension',\n format: createScreenshotToolResponse,\n exposed: false\n }),\n extTool({\n name: 'get_structure',\n description:\n 'Get a compact structural + geometry outline for nodeId/current single selection to understand hierarchy and layout intent.',\n parameters: GetStructureParametersSchema,\n target: 'extension'\n }),\n hubTool({\n name: 'get_assets',\n description:\n 'Resolve asset hashes to downloadable URLs and metadata for assets referenced by tool responses (preserve vectors exactly).',\n parameters: GetAssetsParametersSchema,\n target: 'hub',\n outputSchema: GetAssetsResultSchema,\n exposed: false\n })\n] as const\n\nfunction extractToolErrorCode(error: unknown): TempadMcpErrorCode | undefined {\n const code = getRecordProperty(error, 'code')\n if (typeof code === 'string') {\n return code as TempadMcpErrorCode\n }\n const cause = getRecordProperty(error, 'cause')\n const causeCode = getRecordProperty(cause, 'code')\n if (typeof causeCode === 'string') {\n return causeCode as TempadMcpErrorCode\n }\n return undefined\n}\n\nfunction extractToolErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message || 'Unknown error occurred.'\n if (typeof error === 'string') return error\n if (error && typeof error === 'object') {\n const candidate = error as Partial<TempadMcpErrorPayload & Record<string, unknown>>\n if (typeof candidate.message === 'string' && candidate.message.trim()) return candidate.message\n }\n return 'Unknown error occurred.'\n}\n\nfunction createToolErrorResponse(toolName: string, error: unknown): CallToolResult {\n const message = extractToolErrorMessage(error)\n const code = extractToolErrorCode(error)\n const codeLabel = code ? ` [${code}]` : ''\n\n const troubleshooting = (() => {\n const help: string[] = []\n\n const isConnectivityError =\n code === TEMPAD_MCP_ERROR_CODES.NO_ACTIVE_EXTENSION ||\n code === TEMPAD_MCP_ERROR_CODES.EXTENSION_TIMEOUT ||\n code === TEMPAD_MCP_ERROR_CODES.EXTENSION_DISCONNECTED ||\n code === TEMPAD_MCP_ERROR_CODES.ASSET_SERVER_NOT_CONFIGURED ||\n code === TEMPAD_MCP_ERROR_CODES.TRANSPORT_NOT_CONNECTED ||\n /no active tempad dev extension/i.test(message) ||\n /asset server url is not configured/i.test(message) ||\n /mcp transport is not connected/i.test(message) ||\n /websocket/i.test(message)\n\n if (isConnectivityError) {\n help.push(\n 'Troubleshooting:',\n '- In Figma, open TemPad Dev panel and enable MCP (Preferences → MCP server).',\n '- If multiple Figma tabs are open, click the MCP badge to activate this tab.',\n '- Keep the Figma tab active/foreground while running MCP tools.'\n )\n }\n\n const isSelectionError =\n code === TEMPAD_MCP_ERROR_CODES.INVALID_SELECTION ||\n code === TEMPAD_MCP_ERROR_CODES.NODE_NOT_VISIBLE ||\n /select exactly one visible node/i.test(message) ||\n /no visible node found/i.test(message)\n\n if (isSelectionError) {\n help.push('Tip: Select exactly one visible node, or pass nodeId.')\n }\n\n return help.length ? `\\n\\n${help.join('\\n')}` : ''\n })()\n\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: `Tool \"${toolName}\" failed${codeLabel}: ${message}${troubleshooting}`\n }\n ]\n }\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nexport function createCodeToolResponse(payload: ToolResultMap['get_code']): CallToolResult {\n if (!isCodeResult(payload)) {\n throw new Error('Invalid get_code payload received from extension.')\n }\n\n const summary: string[] = []\n const codeSize = Buffer.byteLength(payload.code, 'utf8')\n summary.push(`Generated \\`${payload.lang}\\` snippet (${formatBytes(codeSize)}).`)\n if (payload.warnings?.length) {\n const warningText = payload.warnings.map((warning) => warning.message).join(' ')\n summary.push(warningText)\n }\n summary.push(\n payload.assets?.length\n ? `Assets attached: ${payload.assets.length}. Download bytes from each asset.url.`\n : 'No binary assets were attached to this response.'\n )\n const tokenCount = payload.tokens ? Object.keys(payload.tokens).length : 0\n if (tokenCount) {\n summary.push(`Token references included: ${tokenCount}.`)\n }\n summary.push('Read structuredContent for the full code string and asset metadata.')\n\n return {\n content: [\n {\n type: 'text' as const,\n text: summary.join('\\n')\n }\n ],\n structuredContent: payload\n }\n}\n\nexport function createScreenshotToolResponse(\n payload: ToolResultMap['get_screenshot']\n): CallToolResult {\n if (!isScreenshotResult(payload)) {\n throw new Error('Invalid get_screenshot payload received from extension.')\n }\n\n const descriptionBlock = {\n type: 'text' as const,\n text: `${describeScreenshot(payload)} - Download: ${payload.asset.url}`\n }\n\n return {\n content: [descriptionBlock],\n structuredContent: payload\n }\n}\n\nfunction describeScreenshot(result: GetScreenshotResult): string {\n return `Screenshot ${result.width}x${result.height} @${result.scale}x (${formatBytes(result.bytes)})`\n}\n\nfunction isScreenshotResult(payload: unknown): payload is GetScreenshotResult {\n if (typeof payload !== 'object' || !payload) return false\n const candidate = payload as Partial<GetScreenshotResult & Record<string, unknown>>\n return (\n typeof candidate.asset === 'object' &&\n candidate.asset !== null &&\n typeof candidate.width === 'number' &&\n typeof candidate.height === 'number' &&\n typeof candidate.scale === 'number' &&\n typeof candidate.bytes === 'number' &&\n typeof candidate.format === 'string'\n )\n}\n\nfunction isCodeResult(payload: unknown): payload is ToolResultMap['get_code'] {\n if (typeof payload !== 'object' || !payload) return false\n const candidate = payload as Partial<ToolResultMap['get_code'] & Record<string, unknown>>\n return (\n typeof candidate.code === 'string' &&\n typeof candidate.lang === 'string' &&\n (candidate.assets === undefined || Array.isArray(candidate.assets))\n )\n}\n\nexport function coercePayloadToToolResponse(payload: unknown): CallToolResult {\n if (\n payload &&\n typeof payload === 'object' &&\n Array.isArray((payload as CallToolResult).content)\n ) {\n return payload as CallToolResult\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: typeof payload === 'string' ? payload : JSON.stringify(payload, null, 2)\n }\n ]\n }\n}\n\nexport { createToolErrorResponse }\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport type {\n AssetDescriptor,\n GetAssetsParametersInput,\n GetAssetsResult,\n RegisteredMessage,\n StateMessage,\n ToolCallMessage,\n ToolName,\n ToolResultMap,\n ToolResultMessage\n} from '@tempad-dev/shared'\nimport type { RawData } from 'ws'\nimport type { ZodType } from 'zod'\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport {\n GetAssetsResultSchema,\n MessageFromExtensionSchema,\n TEMPAD_MCP_ERROR_CODES,\n type TempadMcpErrorCode\n} from '@tempad-dev/shared'\nimport { nanoid } from 'nanoid'\nimport { existsSync, rmSync, chmodSync } from 'node:fs'\nimport { createServer } from 'node:net'\nimport { WebSocketServer } from 'ws'\n\nimport type { AssetRecord, ExtensionConnection } from './types'\n\nimport { createAssetHttpServer } from './asset-http-server'\nimport { createAssetStore } from './asset-store'\nimport { buildAssetFilename } from './asset-utils'\nimport { getMcpServerConfig } from './config'\nimport MCP_INSTRUCTIONS from './instructions.md?raw'\nimport { register, resolve, reject, cleanupForExtension, cleanupAll } from './request'\nimport { PACKAGE_VERSION, log, RUNTIME_DIR, SOCK_PATH, ensureDir } from './shared'\nimport { TOOL_DEFS, coercePayloadToToolResponse, createToolErrorResponse } from './tools'\n\nconst SHUTDOWN_TIMEOUT = 2000\nconst { wsPortCandidates, toolTimeoutMs, maxPayloadBytes, autoActivateGraceMs, assetTtlMs } =\n getMcpServerConfig()\n\nlog.info({ version: PACKAGE_VERSION }, 'TemPad MCP Hub starting...')\n\nconst extensions: ExtensionConnection[] = []\nlet consumerCount = 0\ntype TimeoutHandle = ReturnType<typeof setTimeout>\nlet autoActivateTimer: TimeoutHandle | null = null\nlet selectedWsPort = 0\nconst consumerSessions = new Set<McpServer>()\ntype RegisterToolOptions = Parameters<McpServer['registerTool']>[1]\ntype McpInputSchema = RegisterToolOptions['inputSchema']\ntype McpOutputSchema = RegisterToolOptions['outputSchema']\ntype ToolResponse = CallToolResult\ntype SchemaOutput<Schema extends ZodType> = Schema['_output']\ntype ToolMetadataEntry = (typeof TOOL_DEFS)[number]\ntype ExtensionToolMetadata = Extract<ToolMetadataEntry, { target: 'extension' }>\ntype HubToolMetadata = Extract<ToolMetadataEntry, { target: 'hub' }>\n\ntype HubToolWithHandler<T extends HubToolMetadata = HubToolMetadata> = T & {\n handler: (args: SchemaOutput<T['parameters']>) => Promise<ToolResponse>\n}\n\nfunction getRecordProperty(record: unknown, key: string): unknown {\n if (!record || typeof record !== 'object') {\n return undefined\n }\n return Reflect.get(record, key)\n}\n\ntype RegisteredToolDefinition = ExtensionToolMetadata | HubToolWithHandler\n\nfunction enrichToolDefinition(tool: ToolMetadataEntry): RegisteredToolDefinition {\n if (tool.target === 'extension') {\n return tool\n }\n\n switch (tool.name) {\n case 'get_assets':\n return {\n ...tool,\n handler: handleGetAssets\n } satisfies HubToolWithHandler\n default:\n throw new Error('No handler configured for hub tool.')\n }\n}\n\nconst TOOL_DEFINITIONS: ReadonlyArray<RegisteredToolDefinition> = TOOL_DEFS.map((tool) =>\n enrichToolDefinition(tool)\n)\n\ntype RegisteredTool = (typeof TOOL_DEFINITIONS)[number]\ntype ExtensionTool = Extract<RegisteredTool, { target: 'extension' }>\ntype HubOnlyTool = Extract<RegisteredTool, { target: 'hub' }>\n\nfunction createCodedError(code: TempadMcpErrorCode, message: string): Error & { code: string } {\n const err = new Error(message) as Error & { code: string }\n err.code = code\n return err\n}\n\nfunction coerceToolError(error: unknown): Error {\n if (error instanceof Error) return error\n if (typeof error === 'string') return new Error(error)\n const messageValue = getRecordProperty(error, 'message')\n const codeValue = getRecordProperty(error, 'code')\n if (error && typeof error === 'object') {\n const message = typeof messageValue === 'string' ? messageValue : safeStringify(error)\n const err = new Error(message) as Error & { code?: string }\n if (typeof codeValue === 'string') err.code = codeValue\n return err\n }\n return new Error(String(error))\n}\n\nfunction safeStringify(input: unknown): string {\n try {\n return JSON.stringify(input)\n } catch {\n return String(input)\n }\n}\n\nfunction hasFormatter(tool: RegisteredToolDefinition): tool is ExtensionTool & {\n format: (payload: unknown) => ToolResponse\n} {\n return tool.target === 'extension' && 'format' in tool\n}\n\ntype ToolDefinitionByName = {\n [T in RegisteredToolDefinition as T['name']]: T\n}\n\nconst TOOL_BY_NAME: ToolDefinitionByName = Object.fromEntries(\n TOOL_DEFINITIONS.map((tool) => [tool.name, tool] as const)\n) as ToolDefinitionByName\n\nfunction getToolDefinition<Name extends ToolName>(name: Name): ToolDefinitionByName[Name] {\n return TOOL_BY_NAME[name]\n}\n\nconst assetStore = createAssetStore()\nconst assetHttpServer = createAssetHttpServer(assetStore)\nawait assetHttpServer.start()\nscheduleAssetCleanup()\n\nfunction scheduleAssetCleanup(): void {\n if (assetTtlMs <= 0) {\n log.info('Asset TTL cleanup disabled (TEMPAD_MCP_ASSET_TTL_MS=0).')\n return\n }\n pruneExpiredAssets(assetTtlMs)\n const intervalMs = Math.min(assetTtlMs, 24 * 60 * 60 * 1000)\n const timer = setInterval(() => {\n pruneExpiredAssets(assetTtlMs)\n }, intervalMs)\n unrefTimer(timer)\n log.info({ ttlMs: assetTtlMs, intervalMs }, 'Asset TTL cleanup enabled.')\n}\n\nfunction pruneExpiredAssets(ttlMs: number): void {\n const now = Date.now()\n let removed = 0\n let checked = 0\n for (const record of assetStore.list()) {\n checked += 1\n const lastAccess = Number.isFinite(record.lastAccess) ? record.lastAccess : record.uploadedAt\n if (!lastAccess) continue\n if (now - lastAccess > ttlMs) {\n assetStore.remove(record.hash)\n removed += 1\n }\n }\n log.info({ checked, removed, ttlMs }, 'Asset TTL sweep completed.')\n}\n\nfunction buildAssetDescriptor(record: AssetRecord): AssetDescriptor {\n const filename = buildAssetFilename(record.hash, record.mimeType)\n return {\n hash: record.hash,\n url: `${assetHttpServer.getBaseUrl()}/assets/${filename}`,\n mimeType: record.mimeType,\n size: record.size,\n width: record.metadata?.width,\n height: record.metadata?.height\n }\n}\n\nfunction createMcpServer(): McpServer {\n const mcp = new McpServer(\n { name: 'tempad-dev-mcp', version: PACKAGE_VERSION },\n MCP_INSTRUCTIONS ? { instructions: MCP_INSTRUCTIONS } : undefined\n )\n\n const registered: string[] = []\n for (const tool of TOOL_DEFINITIONS) {\n if ('exposed' in tool && tool.exposed === false) continue\n registerTool(mcp, tool)\n registered.push(tool.name)\n }\n log.info({ tools: registered }, 'Registered tools.')\n\n return mcp\n}\n\nfunction registerTool(mcp: McpServer, tool: RegisteredTool): void {\n if (tool.target === 'extension') {\n registerProxiedTool(mcp, tool)\n } else {\n registerLocalTool(mcp, tool)\n }\n}\n\nfunction registerProxiedTool<T extends ExtensionTool>(mcp: McpServer, tool: T): void {\n type Name = T['name']\n type Result = ToolResultMap[Name]\n\n const registerToolFn = mcp.registerTool.bind(mcp) as (\n name: string,\n options: { description: string; inputSchema: ZodType; outputSchema?: ZodType },\n handler: (args: unknown) => Promise<CallToolResult>\n ) => unknown\n\n const schema = tool.parameters\n const handler = async (args: unknown) => {\n let requestId: string | undefined\n try {\n const parsedArgs = schema.parse(args)\n const activeExt = extensions.find((e) => e.active)\n if (!activeExt) {\n throw createCodedError(\n TEMPAD_MCP_ERROR_CODES.NO_ACTIVE_EXTENSION,\n 'No active TemPad Dev extension available.'\n )\n }\n\n const registration = register<Result>(activeExt.id, toolTimeoutMs)\n requestId = registration.requestId\n\n const message: ToolCallMessage = {\n type: 'toolCall',\n id: registration.requestId,\n payload: {\n name: tool.name,\n args: parsedArgs\n }\n }\n activeExt.ws.send(JSON.stringify(message))\n log.info(\n { tool: tool.name, req: registration.requestId, extId: activeExt.id },\n 'Forwarded tool call.'\n )\n\n const payload = await registration.promise\n return createToolResponse(tool.name, payload)\n } catch (error) {\n const normalized = coerceToolError(error)\n log.error(\n {\n tool: tool.name,\n req: requestId,\n code: getRecordProperty(normalized, 'code'),\n message: normalized.message\n },\n 'Tool invocation failed.'\n )\n return createToolErrorResponse(tool.name, normalized)\n }\n }\n\n registerToolFn(\n tool.name,\n {\n description: tool.description,\n inputSchema: schema as unknown as McpInputSchema\n },\n handler\n )\n}\n\nfunction registerLocalTool(mcp: McpServer, tool: HubOnlyTool): void {\n const schema = tool.parameters\n const handler = tool.handler\n\n const registerToolFn = mcp.registerTool.bind(mcp) as (\n name: string,\n options: { description: string; inputSchema: ZodType; outputSchema?: ZodType },\n handler: (args: unknown) => Promise<CallToolResult>\n ) => unknown\n\n const registrationOptions: {\n description: string\n inputSchema: McpInputSchema\n outputSchema?: McpOutputSchema\n } = {\n description: tool.description,\n inputSchema: schema as unknown as McpInputSchema\n }\n\n if (tool.outputSchema) {\n registrationOptions.outputSchema = tool.outputSchema as unknown as McpOutputSchema\n }\n\n const registerHandler = async (args: unknown) => {\n try {\n const parsed = schema.parse(args)\n return await handler(parsed)\n } catch (error) {\n log.error({ tool: tool.name, error }, 'Local tool invocation failed.')\n return createToolErrorResponse(tool.name, error)\n }\n }\n\n registerToolFn(tool.name, registrationOptions, registerHandler)\n}\n\nfunction createToolResponse<Name extends ToolName>(\n toolName: Name,\n payload: ToolResultMap[Name]\n): ToolResponse {\n const definition = getToolDefinition(toolName)\n if (definition && hasFormatter(definition)) {\n try {\n const formatter = definition.format as (input: ToolResultMap[Name]) => ToolResponse\n return formatter(payload)\n } catch (error) {\n log.warn({ tool: toolName, error }, 'Failed to format tool result; returning raw payload.')\n return coercePayloadToToolResponse(payload)\n }\n }\n\n return coercePayloadToToolResponse(payload)\n}\n\nasync function handleGetAssets({ hashes }: GetAssetsParametersInput): Promise<ToolResponse> {\n if (hashes.length > 100) {\n throw new Error('Too many hashes requested. Limit is 100.')\n }\n const unique = Array.from(new Set(hashes))\n const records = assetStore.getMany(unique).filter((record) => {\n if (existsSync(record.filePath)) return true\n assetStore.remove(record.hash, { removeFile: false })\n return false\n })\n const found = new Set(records.map((record) => record.hash))\n const payload: GetAssetsResult = GetAssetsResultSchema.parse({\n assets: records.map((record) => buildAssetDescriptor(record)),\n missing: unique.filter((hash) => !found.has(hash))\n })\n\n const summary: string[] = []\n summary.push(\n payload.assets.length\n ? `Resolved ${payload.assets.length} asset${payload.assets.length === 1 ? '' : 's'}.`\n : 'No assets were resolved for the requested hashes.'\n )\n if (payload.missing.length) {\n summary.push(`Missing: ${payload.missing.join(', ')}`)\n }\n summary.push('Download bytes from each asset.url.')\n\n const content = [\n {\n type: 'text' as const,\n text: summary.join('\\n')\n }\n ]\n\n return {\n content,\n structuredContent: payload\n }\n}\n\nfunction getActiveId(): string | null {\n return extensions.find((e) => e.active)?.id ?? null\n}\n\nfunction setActive(targetId: string | null): void {\n extensions.forEach((e) => {\n e.active = targetId !== null && e.id === targetId\n })\n}\n\nfunction clearAutoActivateTimer(): void {\n if (autoActivateTimer) {\n clearTimeout(autoActivateTimer)\n autoActivateTimer = null\n }\n}\n\nfunction scheduleAutoActivate(): void {\n clearAutoActivateTimer()\n\n if (extensions.length !== 1 || getActiveId()) {\n return\n }\n\n const target = extensions[0]\n autoActivateTimer = setTimeout(() => {\n autoActivateTimer = null\n if (extensions.length === 1 && !getActiveId()) {\n setActive(target.id)\n log.info({ id: target.id }, 'Auto-activated sole extension after grace period.')\n broadcastState()\n }\n }, autoActivateGraceMs)\n}\n\nfunction unrefTimer(timer: TimeoutHandle): void {\n if (typeof timer === 'object' && timer !== null) {\n const handle = timer as NodeJS.Timeout\n if (typeof handle.unref === 'function') {\n handle.unref()\n }\n }\n}\n\nfunction broadcastState(): void {\n const activeId = getActiveId()\n const message: StateMessage = {\n type: 'state',\n activeId,\n count: extensions.length,\n port: selectedWsPort,\n assetServerUrl: assetHttpServer.getBaseUrl()\n }\n extensions.forEach((ext) => ext.ws.send(JSON.stringify(message)))\n log.debug({ activeId, count: extensions.length }, 'Broadcasted state.')\n}\n\nfunction rawDataToBuffer(raw: RawData): Buffer {\n if (typeof raw === 'string') return Buffer.from(raw)\n if (Buffer.isBuffer(raw)) return raw\n if (raw instanceof ArrayBuffer) return Buffer.from(raw)\n return Buffer.concat(raw)\n}\n\nfunction shutdown(): void {\n log.info('Hub is shutting down...')\n consumerSessions.forEach((session) => {\n session.close().catch((err) => {\n log.warn({ err }, 'Failed to close MCP session during shutdown.')\n })\n })\n consumerSessions.clear()\n assetStore.flush()\n assetHttpServer.stop()\n netServer.close(() => log.info('Net server closed.'))\n wss?.close(() => log.info('WebSocket server closed.'))\n cleanupAll()\n const timer = setTimeout(() => {\n log.warn('Shutdown timed out. Forcing exit.')\n process.exit(1)\n }, SHUTDOWN_TIMEOUT)\n unrefTimer(timer)\n}\n\ntry {\n ensureDir(RUNTIME_DIR)\n if (process.platform !== 'win32' && existsSync(SOCK_PATH)) {\n log.warn({ sock: SOCK_PATH }, 'Removing stale socket file.')\n rmSync(SOCK_PATH)\n }\n} catch (error: unknown) {\n log.error({ err: error }, 'Failed to initialize runtime environment.')\n process.exit(1)\n}\n\nconst netServer = createServer((sock) => {\n const mcp = createMcpServer()\n consumerSessions.add(mcp)\n consumerCount++\n log.info(`Consumer connected. Total: ${consumerCount}`)\n const transport = new StdioServerTransport(sock, sock)\n mcp.connect(transport).catch((err) => {\n log.error({ err }, 'Failed to attach MCP transport.')\n consumerSessions.delete(mcp)\n mcp.close().catch((closeErr) => log.warn({ err: closeErr }, 'MCP session close failed.'))\n transport.close().catch((closeErr) => log.warn({ err: closeErr }, 'Transport close failed.'))\n sock.destroy()\n })\n sock.on('error', (err) => {\n log.warn({ err }, 'Consumer socket error.')\n transport.close().catch((closeErr) => log.warn({ err: closeErr }, 'Transport close failed.'))\n })\n sock.on('close', async () => {\n await transport\n .close()\n .catch((closeErr) => log.warn({ err: closeErr }, 'Transport close failed.'))\n await mcp.close().catch((closeErr) => log.warn({ err: closeErr }, 'MCP session close failed.'))\n consumerSessions.delete(mcp)\n consumerCount--\n log.info(`Consumer disconnected. Remaining: ${consumerCount}`)\n if (consumerCount === 0) {\n log.info('Last consumer disconnected. Shutting down.')\n shutdown()\n }\n })\n})\nnetServer.on('error', (err) => {\n log.error({ err }, 'Net server error.')\n process.exit(1)\n})\nnetServer.listen(SOCK_PATH, () => {\n try {\n if (process.platform !== 'win32') chmodSync(SOCK_PATH, 0o600)\n } catch (err) {\n log.error({ err }, 'Failed to set socket permissions. Shutting down.')\n process.exit(1)\n }\n log.info({ sock: SOCK_PATH }, 'Hub socket ready.')\n})\n\nasync function startWebSocketServer(): Promise<{ wss: WebSocketServer; port: number }> {\n for (const candidate of wsPortCandidates) {\n const server = new WebSocketServer({\n host: '127.0.0.1',\n port: candidate,\n maxPayload: maxPayloadBytes\n })\n\n try {\n await new Promise<void>((resolve, reject) => {\n const onError = (err: NodeJS.ErrnoException) => {\n server.off('listening', onListening)\n reject(err)\n }\n const onListening = () => {\n server.off('error', onError)\n resolve()\n }\n server.once('error', onError)\n server.once('listening', onListening)\n })\n return { wss: server, port: candidate }\n } catch (err) {\n server.close()\n const errno = err as NodeJS.ErrnoException\n if (errno.code === 'EADDRINUSE') {\n log.warn({ port: candidate }, 'WebSocket port in use, trying next candidate.')\n continue\n }\n log.error({ err: errno, port: candidate }, 'Failed to start WebSocket server.')\n process.exit(1)\n }\n }\n\n log.error(\n { candidates: wsPortCandidates },\n 'Unable to start WebSocket server on any candidate port.'\n )\n process.exit(1)\n}\n\nconst { wss, port } = await startWebSocketServer()\nselectedWsPort = port\n\n// Add an error handler to prevent crashes from port conflicts, etc.\nwss.on('error', (err) => {\n log.error({ err }, 'WebSocket server critical error. Exiting.')\n process.exit(1)\n})\n\nwss.on('connection', (ws) => {\n const ext: ExtensionConnection = { id: nanoid(), ws, active: false }\n extensions.push(ext)\n log.info({ id: ext.id }, `Extension connected. Total: ${extensions.length}`)\n\n const message: RegisteredMessage = { type: 'registered', id: ext.id }\n ws.send(JSON.stringify(message))\n broadcastState()\n scheduleAutoActivate()\n\n ws.on('message', (raw: RawData, isBinary: boolean) => {\n if (isBinary) {\n log.warn({ extId: ext.id }, 'Unexpected binary message received.')\n return\n }\n\n const messageBuffer = rawDataToBuffer(raw)\n\n let parsedJson: unknown\n try {\n parsedJson = JSON.parse(messageBuffer.toString('utf-8'))\n } catch (e: unknown) {\n log.warn({ err: e, extId: ext.id }, 'Failed to parse message.')\n return\n }\n\n const parseResult = MessageFromExtensionSchema.safeParse(parsedJson)\n if (!parseResult.success) {\n log.warn({ error: parseResult.error.flatten(), extId: ext.id }, 'Invalid message shape.')\n return\n }\n const msg = parseResult.data\n\n switch (msg.type) {\n case 'activate': {\n setActive(ext.id)\n log.info({ id: ext.id }, 'Extension activated.')\n broadcastState()\n scheduleAutoActivate()\n break\n }\n case 'toolResult': {\n const { id, payload, error } = msg as ToolResultMessage\n if (error) {\n const normalized = coerceToolError(error)\n log.warn(\n {\n toolReq: id,\n extId: ext.id,\n code: getRecordProperty(normalized, 'code'),\n message: normalized.message\n },\n 'Received tool error from extension.'\n )\n reject(id, normalized)\n } else {\n resolve(id, payload)\n }\n break\n }\n }\n })\n\n ws.on('close', () => {\n const index = extensions.findIndex((e) => e.id === ext.id)\n if (index > -1) extensions.splice(index, 1)\n\n log.info({ id: ext.id }, `Extension disconnected. Remaining: ${extensions.length}`)\n cleanupForExtension(ext.id)\n\n if (ext.active) {\n log.warn({ id: ext.id }, 'Active extension disconnected.')\n setActive(null)\n }\n\n broadcastState()\n scheduleAutoActivate()\n })\n})\n\nlog.info({ port: selectedWsPort }, 'WebSocket server ready.')\n\nprocess.on('SIGINT', shutdown)\nprocess.on('SIGTERM', shutdown)\n"],"mappings":"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"}
@@ -6,7 +6,7 @@ import pino from "pino";
6
6
  //#region package.json
7
7
  var package_default = {
8
8
  name: "@tempad-dev/mcp",
9
- version: "0.4.3",
9
+ version: "0.4.4",
10
10
  description: "MCP server for TemPad Dev.",
11
11
  repository: {
12
12
  "type": "git",
@@ -114,4 +114,4 @@ const SOCK_PATH = resolveSockPath();
114
114
 
115
115
  //#endregion
116
116
  export { SOCK_PATH as a, log as c, RUNTIME_DIR as i, LOCK_PATH as n, ensureDir as o, PACKAGE_VERSION as r, ensureFile as s, ASSET_DIR as t };
117
- //# sourceMappingURL=shared-Bubiv0fy.mjs.map
117
+ //# sourceMappingURL=shared-DAXLxrSM.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared-Bubiv0fy.mjs","names":["packageJson"],"sources":["../package.json","../src/shared.ts"],"sourcesContent":["","import { closeSync, mkdirSync, openSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\nimport pino from 'pino'\n\nimport packageJson from '../package.json' assert { type: 'json' }\n\nexport function normalizePackageVersion(version: unknown): string {\n return typeof version === 'string' ? version : '0.0.0'\n}\n\nexport function ensureDir(dirPath: string): void {\n mkdirSync(dirPath, { recursive: true, mode: 0o700 })\n}\n\nfunction getRecordProperty(record: unknown, key: string): unknown {\n if (!record || typeof record !== 'object') {\n return undefined\n }\n return Reflect.get(record, key)\n}\n\nexport const PACKAGE_VERSION = normalizePackageVersion(getRecordProperty(packageJson, 'version'))\n\nexport function resolveRuntimeDir(\n env: NodeJS.ProcessEnv = process.env,\n systemTmpDir: string = tmpdir()\n): string {\n if (env.TEMPAD_MCP_RUNTIME_DIR) return env.TEMPAD_MCP_RUNTIME_DIR\n return join(systemTmpDir, 'tempad-dev', 'run')\n}\n\nexport function resolveLogDir(\n env: NodeJS.ProcessEnv = process.env,\n systemTmpDir: string = tmpdir()\n): string {\n if (env.TEMPAD_MCP_LOG_DIR) return env.TEMPAD_MCP_LOG_DIR\n return join(systemTmpDir, 'tempad-dev', 'log')\n}\n\nexport function resolveAssetDir(\n env: NodeJS.ProcessEnv = process.env,\n systemTmpDir: string = tmpdir()\n): string {\n if (env.TEMPAD_MCP_ASSET_DIR) return env.TEMPAD_MCP_ASSET_DIR\n return join(systemTmpDir, 'tempad-dev', 'assets')\n}\n\nexport function resolveLogLevel(\n debugValue: string | undefined = process.env.DEBUG\n): 'debug' | 'info' {\n return debugValue ? 'debug' : 'info'\n}\n\nexport function resolveSockPath(\n platform: NodeJS.Platform = process.platform,\n runtimeDir: string = RUNTIME_DIR\n): string {\n return platform === 'win32' ? '\\\\\\\\.\\\\pipe\\\\tempad-mcp' : join(runtimeDir, 'mcp.sock')\n}\n\nexport const RUNTIME_DIR = resolveRuntimeDir()\nexport const LOG_DIR = resolveLogDir()\nexport const ASSET_DIR = resolveAssetDir()\n\nensureDir(RUNTIME_DIR)\nensureDir(LOG_DIR)\nensureDir(ASSET_DIR)\n\nexport function ensureFile(filePath: string): void {\n const fd = openSync(filePath, 'a')\n closeSync(fd)\n}\n\nexport const LOCK_PATH = join(RUNTIME_DIR, 'mcp.lock')\nensureFile(LOCK_PATH)\n\nconst timestamp = new Date().toISOString().replaceAll(':', '-').replaceAll('.', '-')\nconst pid = process.pid\nconst LOG_FILE = join(LOG_DIR, `mcp-${timestamp}-${pid}.log`)\n\nconst prettyTransport = pino.transport({\n target: 'pino-pretty',\n options: {\n translateTime: 'SYS:HH:MM:ss',\n destination: LOG_FILE\n }\n})\n\nexport const log = pino(\n {\n level: resolveLogLevel(),\n msgPrefix: '[tempad-dev/mcp] '\n },\n prettyTransport\n)\n\nexport const SOCK_PATH = resolveSockPath()\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACOA,SAAgB,wBAAwB,SAA0B;AAChE,QAAO,OAAO,YAAY,WAAW,UAAU;;AAGjD,SAAgB,UAAU,SAAuB;AAC/C,WAAU,SAAS;EAAE,WAAW;EAAM,MAAM;EAAO,CAAC;;AAGtD,SAAS,kBAAkB,QAAiB,KAAsB;AAChE,KAAI,CAAC,UAAU,OAAO,WAAW,SAC/B;AAEF,QAAO,QAAQ,IAAI,QAAQ,IAAI;;AAGjC,MAAa,kBAAkB,wBAAwB,kBAAkBA,iBAAa,UAAU,CAAC;AAEjG,SAAgB,kBACd,MAAyB,QAAQ,KACjC,eAAuB,QAAQ,EACvB;AACR,KAAI,IAAI,uBAAwB,QAAO,IAAI;AAC3C,QAAO,KAAK,cAAc,cAAc,MAAM;;AAGhD,SAAgB,cACd,MAAyB,QAAQ,KACjC,eAAuB,QAAQ,EACvB;AACR,KAAI,IAAI,mBAAoB,QAAO,IAAI;AACvC,QAAO,KAAK,cAAc,cAAc,MAAM;;AAGhD,SAAgB,gBACd,MAAyB,QAAQ,KACjC,eAAuB,QAAQ,EACvB;AACR,KAAI,IAAI,qBAAsB,QAAO,IAAI;AACzC,QAAO,KAAK,cAAc,cAAc,SAAS;;AAGnD,SAAgB,gBACd,aAAiC,QAAQ,IAAI,OAC3B;AAClB,QAAO,aAAa,UAAU;;AAGhC,SAAgB,gBACd,WAA4B,QAAQ,UACpC,aAAqB,aACb;AACR,QAAO,aAAa,UAAU,4BAA4B,KAAK,YAAY,WAAW;;AAGxF,MAAa,cAAc,mBAAmB;AAC9C,MAAa,UAAU,eAAe;AACtC,MAAa,YAAY,iBAAiB;AAE1C,UAAU,YAAY;AACtB,UAAU,QAAQ;AAClB,UAAU,UAAU;AAEpB,SAAgB,WAAW,UAAwB;AAEjD,WADW,SAAS,UAAU,IAAI,CACrB;;AAGf,MAAa,YAAY,KAAK,aAAa,WAAW;AACtD,WAAW,UAAU;AAErB,MAAM,6BAAY,IAAI,MAAM,EAAC,aAAa,CAAC,WAAW,KAAK,IAAI,CAAC,WAAW,KAAK,IAAI;AACpF,MAAM,MAAM,QAAQ;AACpB,MAAM,WAAW,KAAK,SAAS,OAAO,UAAU,GAAG,IAAI,MAAM;AAE7D,MAAM,kBAAkB,KAAK,UAAU;CACrC,QAAQ;CACR,SAAS;EACP,eAAe;EACf,aAAa;EACd;CACF,CAAC;AAEF,MAAa,MAAM,KACjB;CACE,OAAO,iBAAiB;CACxB,WAAW;CACZ,EACD,gBACD;AAED,MAAa,YAAY,iBAAiB"}
1
+ {"version":3,"file":"shared-DAXLxrSM.mjs","names":["packageJson"],"sources":["../package.json","../src/shared.ts"],"sourcesContent":["","import { closeSync, mkdirSync, openSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\nimport pino from 'pino'\n\nimport packageJson from '../package.json' assert { type: 'json' }\n\nexport function normalizePackageVersion(version: unknown): string {\n return typeof version === 'string' ? version : '0.0.0'\n}\n\nexport function ensureDir(dirPath: string): void {\n mkdirSync(dirPath, { recursive: true, mode: 0o700 })\n}\n\nfunction getRecordProperty(record: unknown, key: string): unknown {\n if (!record || typeof record !== 'object') {\n return undefined\n }\n return Reflect.get(record, key)\n}\n\nexport const PACKAGE_VERSION = normalizePackageVersion(getRecordProperty(packageJson, 'version'))\n\nexport function resolveRuntimeDir(\n env: NodeJS.ProcessEnv = process.env,\n systemTmpDir: string = tmpdir()\n): string {\n if (env.TEMPAD_MCP_RUNTIME_DIR) return env.TEMPAD_MCP_RUNTIME_DIR\n return join(systemTmpDir, 'tempad-dev', 'run')\n}\n\nexport function resolveLogDir(\n env: NodeJS.ProcessEnv = process.env,\n systemTmpDir: string = tmpdir()\n): string {\n if (env.TEMPAD_MCP_LOG_DIR) return env.TEMPAD_MCP_LOG_DIR\n return join(systemTmpDir, 'tempad-dev', 'log')\n}\n\nexport function resolveAssetDir(\n env: NodeJS.ProcessEnv = process.env,\n systemTmpDir: string = tmpdir()\n): string {\n if (env.TEMPAD_MCP_ASSET_DIR) return env.TEMPAD_MCP_ASSET_DIR\n return join(systemTmpDir, 'tempad-dev', 'assets')\n}\n\nexport function resolveLogLevel(\n debugValue: string | undefined = process.env.DEBUG\n): 'debug' | 'info' {\n return debugValue ? 'debug' : 'info'\n}\n\nexport function resolveSockPath(\n platform: NodeJS.Platform = process.platform,\n runtimeDir: string = RUNTIME_DIR\n): string {\n return platform === 'win32' ? '\\\\\\\\.\\\\pipe\\\\tempad-mcp' : join(runtimeDir, 'mcp.sock')\n}\n\nexport const RUNTIME_DIR = resolveRuntimeDir()\nexport const LOG_DIR = resolveLogDir()\nexport const ASSET_DIR = resolveAssetDir()\n\nensureDir(RUNTIME_DIR)\nensureDir(LOG_DIR)\nensureDir(ASSET_DIR)\n\nexport function ensureFile(filePath: string): void {\n const fd = openSync(filePath, 'a')\n closeSync(fd)\n}\n\nexport const LOCK_PATH = join(RUNTIME_DIR, 'mcp.lock')\nensureFile(LOCK_PATH)\n\nconst timestamp = new Date().toISOString().replaceAll(':', '-').replaceAll('.', '-')\nconst pid = process.pid\nconst LOG_FILE = join(LOG_DIR, `mcp-${timestamp}-${pid}.log`)\n\nconst prettyTransport = pino.transport({\n target: 'pino-pretty',\n options: {\n translateTime: 'SYS:HH:MM:ss',\n destination: LOG_FILE\n }\n})\n\nexport const log = pino(\n {\n level: resolveLogLevel(),\n msgPrefix: '[tempad-dev/mcp] '\n },\n prettyTransport\n)\n\nexport const SOCK_PATH = resolveSockPath()\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACOA,SAAgB,wBAAwB,SAA0B;AAChE,QAAO,OAAO,YAAY,WAAW,UAAU;;AAGjD,SAAgB,UAAU,SAAuB;AAC/C,WAAU,SAAS;EAAE,WAAW;EAAM,MAAM;EAAO,CAAC;;AAGtD,SAAS,kBAAkB,QAAiB,KAAsB;AAChE,KAAI,CAAC,UAAU,OAAO,WAAW,SAC/B;AAEF,QAAO,QAAQ,IAAI,QAAQ,IAAI;;AAGjC,MAAa,kBAAkB,wBAAwB,kBAAkBA,iBAAa,UAAU,CAAC;AAEjG,SAAgB,kBACd,MAAyB,QAAQ,KACjC,eAAuB,QAAQ,EACvB;AACR,KAAI,IAAI,uBAAwB,QAAO,IAAI;AAC3C,QAAO,KAAK,cAAc,cAAc,MAAM;;AAGhD,SAAgB,cACd,MAAyB,QAAQ,KACjC,eAAuB,QAAQ,EACvB;AACR,KAAI,IAAI,mBAAoB,QAAO,IAAI;AACvC,QAAO,KAAK,cAAc,cAAc,MAAM;;AAGhD,SAAgB,gBACd,MAAyB,QAAQ,KACjC,eAAuB,QAAQ,EACvB;AACR,KAAI,IAAI,qBAAsB,QAAO,IAAI;AACzC,QAAO,KAAK,cAAc,cAAc,SAAS;;AAGnD,SAAgB,gBACd,aAAiC,QAAQ,IAAI,OAC3B;AAClB,QAAO,aAAa,UAAU;;AAGhC,SAAgB,gBACd,WAA4B,QAAQ,UACpC,aAAqB,aACb;AACR,QAAO,aAAa,UAAU,4BAA4B,KAAK,YAAY,WAAW;;AAGxF,MAAa,cAAc,mBAAmB;AAC9C,MAAa,UAAU,eAAe;AACtC,MAAa,YAAY,iBAAiB;AAE1C,UAAU,YAAY;AACtB,UAAU,QAAQ;AAClB,UAAU,UAAU;AAEpB,SAAgB,WAAW,UAAwB;AAEjD,WADW,SAAS,UAAU,IAAI,CACrB;;AAGf,MAAa,YAAY,KAAK,aAAa,WAAW;AACtD,WAAW,UAAU;AAErB,MAAM,6BAAY,IAAI,MAAM,EAAC,aAAa,CAAC,WAAW,KAAK,IAAI,CAAC,WAAW,KAAK,IAAI;AACpF,MAAM,MAAM,QAAQ;AACpB,MAAM,WAAW,KAAK,SAAS,OAAO,UAAU,GAAG,IAAI,MAAM;AAE7D,MAAM,kBAAkB,KAAK,UAAU;CACrC,QAAQ;CACR,SAAS;EACP,eAAe;EACf,aAAa;EACd;CACF,CAAC;AAEF,MAAa,MAAM,KACjB;CACE,OAAO,iBAAiB;CACxB,WAAW;CACZ,EACD,gBACD;AAED,MAAa,YAAY,iBAAiB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tempad-dev/mcp",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "MCP server for TemPad Dev.",
5
5
  "repository": {
6
6
  "type": "git",