@tempad-dev/mcp 0.4.1 → 0.4.2

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/README.md CHANGED
@@ -25,12 +25,11 @@ Supported tools/resources:
25
25
 
26
26
  - `get_code`: Tailwind-first JSX/Vue markup plus assets and token references.
27
27
  - `get_structure`: Hierarchy/geometry outline for the selection.
28
- - `get_screenshot`: PNG capture with a downloadable asset link.
29
- - `tempad-assets` resource template (`asset://tempad/{hash}`) for binaries referenced by tool responses.
30
28
 
31
29
  Notes:
32
30
 
33
- - Assets are ephemeral and tool-linked; `resources/list` is intentionally empty to avoid cross-session/design-file pollution. Use `resource_link` blocks from tool results and `resources/read` (or the HTTP fallback URL) to fetch bytes.
31
+ - Assets are ephemeral and tool-linked; image/SVG bytes are downloaded via HTTP `asset.url` from tool results.
32
+ - Asset resources are not exposed via MCP `resources/list`/`resources/read`.
34
33
  - The HTTP fallback URL uses `/assets/{hash}` and may include an image extension (for example `/assets/{hash}.png`). Both forms are accepted.
35
34
 
36
35
  ## Configuration
package/README.zh-Hans.md CHANGED
@@ -23,12 +23,11 @@
23
23
 
24
24
  - `get_code`:以 Tailwind 优先的 JSX/Vue 标记输出,并附带资源和变量引用。
25
25
  - `get_structure`:当前选中节点的层级/几何结构信息。
26
- - `get_screenshot`:PNG 截图,包含可下载的资源链接。
27
- - `tempad-assets` 资源模板(`asset://tempad/{hash}`),用于读取工具返回中引用的二进制资源。
28
26
 
29
27
  说明:
30
28
 
31
- - 资源是临时且与工具调用关联的;`resources/list` 故意保持为空,以避免跨会话/跨设计文件污染。请使用工具结果中的 `resource_link`,再通过 `resources/read`(或 HTTP 回退 URL)获取二进制内容。
29
+ - 资源是临时且与工具调用关联的;图片/SVG 请直接使用工具结果中的 HTTP `asset.url` 下载。
30
+ - MCP 不再暴露 `resources/list` / `resources/read` 用于 asset 内容读取。
32
31
  - HTTP 回退 URL 使用 `/assets/{hash}`,也可能带图片扩展名(例如 `/assets/{hash}.png`),两种形式都支持。
33
32
 
34
33
  ## 配置
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-CnY7ym27.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-Do3A1dyd.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,9 +1,9 @@
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-CnY7ym27.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-Do3A1dyd.mjs";
2
2
  import { createServer } from "node:net";
3
3
  import { join } from "node:path";
4
4
  import { URL } from "node:url";
5
5
  import { chmodSync, createReadStream, createWriteStream, existsSync, readFileSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
6
- import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
8
  import { z } from "zod";
9
9
  import { nanoid } from "nanoid";
@@ -23,9 +23,6 @@ const MCP_TOOL_TIMEOUT_MS = 15e3;
23
23
  const MCP_AUTO_ACTIVATE_GRACE_MS = 1500;
24
24
  const MCP_MAX_ASSET_BYTES = 8 * 1024 * 1024;
25
25
  const MCP_ASSET_TTL_MS = 720 * 60 * 60 * 1e3;
26
- const MCP_ASSET_RESOURCE_NAME = "tempad-assets";
27
- const MCP_ASSET_URI_PREFIX = "asset://tempad/";
28
- const MCP_ASSET_URI_TEMPLATE = `${MCP_ASSET_URI_PREFIX}{hash}`;
29
26
  const MCP_HASH_HEX_LENGTH = 8;
30
27
  const MCP_HASH_PATTERN = new RegExp(`^[a-f0-9]{${MCP_HASH_HEX_LENGTH}}$`, "i");
31
28
  const TEMPAD_MCP_ERROR_CODES = {
@@ -70,13 +67,11 @@ const ToolResultMessageSchema = z.object({
70
67
  error: z.unknown().optional()
71
68
  });
72
69
  const MessageFromExtensionSchema = z.discriminatedUnion("type", [ActivateMessageSchema, ToolResultMessageSchema]);
73
- const MCP_ASSET_RESOURCE_URI_PATTERN = new RegExp(`^${MCP_ASSET_URI_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[a-f0-9]{${MCP_HASH_HEX_LENGTH}}$`, "i");
74
70
  const AssetDescriptorSchema = z.object({
75
71
  hash: z.string().min(1),
76
72
  url: z.string().url(),
77
73
  mimeType: z.string().min(1),
78
74
  size: z.number().int().nonnegative(),
79
- resourceUri: z.string().regex(MCP_ASSET_RESOURCE_URI_PATTERN),
80
75
  width: z.number().int().positive().optional(),
81
76
  height: z.number().int().positive().optional()
82
77
  });
@@ -578,7 +573,7 @@ function createAssetStore(options = {}) {
578
573
 
579
574
  //#endregion
580
575
  //#region src/instructions.md?raw
581
- var instructions_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` / `get_screenshot` only to resolve layout/overlap/masks/effects uncertainty. Screenshots are for visual verification only; do not derive numeric values from pixels.\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: fetch bytes via `resources/read` using `resourceUri` when possible; fall back to `asset.url` for large blobs. Preserve SVG/vector assets exactly; never redraw vectors.\n";
576
+ var instructions_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";
582
577
 
583
578
  //#endregion
584
579
  //#region src/request.ts
@@ -663,7 +658,7 @@ function hubTool(definition) {
663
658
  const TOOL_DEFS = [
664
659
  extTool({
665
660
  name: "get_code",
666
- description: "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/get_screenshot 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).",
661
+ description: "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).",
667
662
  parameters: GetCodeParametersSchema,
668
663
  target: "extension",
669
664
  format: createCodeToolResponse
@@ -680,17 +675,18 @@ const TOOL_DEFS = [
680
675
  description: "Capture a rendered PNG screenshot for nodeId/current single selection for visual verification (layering/overlap/masks/effects).",
681
676
  parameters: GetScreenshotParametersSchema,
682
677
  target: "extension",
683
- format: createScreenshotToolResponse
678
+ format: createScreenshotToolResponse,
679
+ exposed: false
684
680
  }),
685
681
  extTool({
686
682
  name: "get_structure",
687
- description: "Get a structural + geometry outline for nodeId/current single selection to understand hierarchy and layout intent.",
683
+ description: "Get a compact structural + geometry outline for nodeId/current single selection to understand hierarchy and layout intent.",
688
684
  parameters: GetStructureParametersSchema,
689
685
  target: "extension"
690
686
  }),
691
687
  hubTool({
692
688
  name: "get_assets",
693
- description: "Resolve asset hashes to downloadable URLs/URIs for assets referenced by tool responses (preserve vectors exactly).",
689
+ description: "Resolve asset hashes to downloadable URLs and metadata for assets referenced by tool responses (preserve vectors exactly).",
694
690
  parameters: GetAssetsParametersSchema,
695
691
  target: "hub",
696
692
  outputSchema: GetAssetsResultSchema,
@@ -725,7 +721,7 @@ function createToolErrorResponse(toolName, error) {
725
721
  })()}`
726
722
  }] };
727
723
  }
728
- function formatBytes$1(bytes) {
724
+ function formatBytes(bytes) {
729
725
  if (bytes < 1024) return `${bytes} B`;
730
726
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
731
727
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
@@ -734,52 +730,35 @@ function createCodeToolResponse(payload) {
734
730
  if (!isCodeResult(payload)) throw new Error("Invalid get_code payload received from extension.");
735
731
  const summary = [];
736
732
  const codeSize = Buffer.byteLength(payload.code, "utf8");
737
- summary.push(`Generated \`${payload.lang}\` snippet (${formatBytes$1(codeSize)}).`);
733
+ summary.push(`Generated \`${payload.lang}\` snippet (${formatBytes(codeSize)}).`);
738
734
  if (payload.warnings?.length) {
739
735
  const warningText = payload.warnings.map((warning) => warning.message).join(" ");
740
736
  summary.push(warningText);
741
737
  }
742
- summary.push(payload.assets?.length ? `Assets attached: ${payload.assets.length}. Fetch bytes via resources/read using resourceUri.` : "No binary assets were attached to this response.");
738
+ summary.push(payload.assets?.length ? `Assets attached: ${payload.assets.length}. Download bytes from each asset.url.` : "No binary assets were attached to this response.");
743
739
  const tokenCount = payload.tokens ? Object.keys(payload.tokens).length : 0;
744
740
  if (tokenCount) summary.push(`Token references included: ${tokenCount}.`);
745
741
  summary.push("Read structuredContent for the full code string and asset metadata.");
746
- const assetLinks = payload.assets?.length ? payload.assets.map((asset) => createAssetResourceLinkBlock$1(asset)) : [];
747
742
  return {
748
743
  content: [{
749
744
  type: "text",
750
745
  text: summary.join("\n")
751
- }, ...assetLinks],
746
+ }],
752
747
  structuredContent: payload
753
748
  };
754
749
  }
755
750
  function createScreenshotToolResponse(payload) {
756
751
  if (!isScreenshotResult(payload)) throw new Error("Invalid get_screenshot payload received from extension.");
757
752
  return {
758
- content: [
759
- {
760
- type: "text",
761
- text: describeScreenshot(payload)
762
- },
763
- {
764
- type: "text",
765
- text: `![Screenshot](${payload.asset.url})`
766
- },
767
- createResourceLinkBlock(payload.asset, payload)
768
- ],
753
+ content: [{
754
+ type: "text",
755
+ text: `${describeScreenshot(payload)} - Download: ${payload.asset.url}`
756
+ }],
769
757
  structuredContent: payload
770
758
  };
771
759
  }
772
- function createResourceLinkBlock(asset, result) {
773
- return {
774
- type: "resource_link",
775
- name: "Screenshot",
776
- uri: asset.resourceUri,
777
- mimeType: asset.mimeType,
778
- description: `Screenshot ${result.width}x${result.height} @${result.scale}x - Download: ${asset.url}`
779
- };
780
- }
781
760
  function describeScreenshot(result) {
782
- return `Screenshot ${result.width}x${result.height} @${result.scale}x (${formatBytes$1(result.bytes)})`;
761
+ return `Screenshot ${result.width}x${result.height} @${result.scale}x (${formatBytes(result.bytes)})`;
783
762
  }
784
763
  function isScreenshotResult(payload) {
785
764
  if (typeof payload !== "object" || !payload) return false;
@@ -791,21 +770,6 @@ function isCodeResult(payload) {
791
770
  const candidate = payload;
792
771
  return typeof candidate.code === "string" && typeof candidate.lang === "string" && (candidate.assets === void 0 || Array.isArray(candidate.assets));
793
772
  }
794
- function createAssetResourceLinkBlock$1(asset) {
795
- return {
796
- type: "resource_link",
797
- name: formatAssetResourceName$1(asset.hash),
798
- uri: asset.resourceUri,
799
- mimeType: asset.mimeType,
800
- description: `${describeAsset$1(asset)} - Download: ${asset.url}`
801
- };
802
- }
803
- function describeAsset$1(asset) {
804
- return `${asset.mimeType} (${formatBytes$1(asset.size)})`;
805
- }
806
- function formatAssetResourceName$1(hash) {
807
- return `asset:${hash.slice(0, 8)}`;
808
- }
809
773
  function coercePayloadToToolResponse(payload) {
810
774
  if (payload && typeof payload === "object" && Array.isArray(payload.content)) return payload;
811
775
  return { content: [{
@@ -877,14 +841,7 @@ function getToolDefinition(name) {
877
841
  const assetStore = createAssetStore();
878
842
  const assetHttpServer = createAssetHttpServer(assetStore);
879
843
  await assetHttpServer.start();
880
- registerAssetResources();
881
844
  scheduleAssetCleanup();
882
- function registerAssetResources() {
883
- const template = new ResourceTemplate(MCP_ASSET_URI_TEMPLATE, { list: async () => ({ resources: [] }) });
884
- mcp.registerResource(MCP_ASSET_RESOURCE_NAME, template, { description: "Exported PNG/SVG assets which can serve as screenshots or be referenced in code output." }, async (_uri, variables) => {
885
- return readAssetResource(typeof variables.hash === "string" ? variables.hash : "");
886
- });
887
- }
888
845
  function scheduleAssetCleanup() {
889
846
  if (assetTtlMs <= 0) {
890
847
  log.info("Asset TTL cleanup disabled (TEMPAD_MCP_ASSET_TTL_MS=0).");
@@ -898,7 +855,7 @@ function scheduleAssetCleanup() {
898
855
  log.info({
899
856
  ttlMs: assetTtlMs,
900
857
  intervalMs
901
- }, "Asset TTL cleanup enabled (list remains empty; assets are tool-linked).");
858
+ }, "Asset TTL cleanup enabled.");
902
859
  }
903
860
  function pruneExpiredAssets(ttlMs) {
904
861
  const now = Date.now();
@@ -919,40 +876,6 @@ function pruneExpiredAssets(ttlMs) {
919
876
  ttlMs
920
877
  }, "Asset TTL sweep completed.");
921
878
  }
922
- async function readAssetResource(hash) {
923
- if (!hash) throw new Error("Missing asset hash in resource URI.");
924
- const record = assetStore.get(hash);
925
- if (!record) throw new Error(`Asset ${hash} not found.`);
926
- if (!existsSync(record.filePath)) {
927
- assetStore.remove(hash, { removeFile: false });
928
- throw new Error(`Asset ${hash} file is missing.`);
929
- }
930
- const stat = statSync(record.filePath);
931
- const estimatedSize = Math.ceil(stat.size / 3) * 4;
932
- if (estimatedSize > maxPayloadBytes) throw new Error(`Asset ${hash} is too large (${formatBytes(stat.size)}, encoded: ${formatBytes(estimatedSize)}) to read via MCP protocol. Use HTTP download.`);
933
- assetStore.touch(hash);
934
- const buffer = readFileSync(record.filePath);
935
- const resourceUri = buildAssetResourceUri(hash);
936
- if (isTextualMime(record.mimeType)) return { contents: [{
937
- uri: resourceUri,
938
- mimeType: record.mimeType,
939
- text: buffer.toString("utf8")
940
- }] };
941
- return { contents: [{
942
- uri: resourceUri,
943
- mimeType: record.mimeType,
944
- blob: buffer.toString("base64")
945
- }] };
946
- }
947
- function isTextualMime(mimeType) {
948
- return mimeType === "image/svg+xml" || mimeType.startsWith("text/");
949
- }
950
- function buildAssetResourceUri(hash) {
951
- return `${MCP_ASSET_URI_PREFIX}${hash}`;
952
- }
953
- function formatAssetResourceName(hash) {
954
- return `asset:${hash.slice(0, 8)}`;
955
- }
956
879
  function buildAssetDescriptor(record) {
957
880
  const filename = buildAssetFilename(record.hash, record.mimeType);
958
881
  return {
@@ -960,23 +883,10 @@ function buildAssetDescriptor(record) {
960
883
  url: `${assetHttpServer.getBaseUrl()}/assets/${filename}`,
961
884
  mimeType: record.mimeType,
962
885
  size: record.size,
963
- resourceUri: buildAssetResourceUri(record.hash),
964
886
  width: record.metadata?.width,
965
887
  height: record.metadata?.height
966
888
  };
967
889
  }
968
- function createAssetResourceLinkBlock(asset) {
969
- return {
970
- type: "resource_link",
971
- name: formatAssetResourceName(asset.hash),
972
- uri: asset.resourceUri,
973
- mimeType: asset.mimeType,
974
- description: `${describeAsset(asset)} - Download: ${asset.url}`
975
- };
976
- }
977
- function describeAsset(asset) {
978
- return `${asset.mimeType} (${formatBytes(asset.size)})`;
979
- }
980
890
  function registerTools() {
981
891
  const registered = [];
982
892
  for (const tool of TOOL_DEFINITIONS) {
@@ -1081,12 +991,12 @@ async function handleGetAssets({ hashes }) {
1081
991
  const summary = [];
1082
992
  summary.push(payload.assets.length ? `Resolved ${payload.assets.length} asset${payload.assets.length === 1 ? "" : "s"}.` : "No assets were resolved for the requested hashes.");
1083
993
  if (payload.missing.length) summary.push(`Missing: ${payload.missing.join(", ")}`);
1084
- summary.push("Use resources/read with each resourceUri or fetch the fallback URL to download bytes.");
994
+ summary.push("Download bytes from each asset.url.");
1085
995
  return {
1086
996
  content: [{
1087
997
  type: "text",
1088
998
  text: summary.join("\n")
1089
- }, ...payload.assets.map((asset) => createAssetResourceLinkBlock(asset))],
999
+ }],
1090
1000
  structuredContent: payload
1091
1001
  };
1092
1002
  }
@@ -1144,11 +1054,6 @@ function rawDataToBuffer(raw) {
1144
1054
  if (raw instanceof ArrayBuffer) return Buffer.from(raw);
1145
1055
  return Buffer.concat(raw);
1146
1056
  }
1147
- function formatBytes(bytes) {
1148
- if (bytes < 1024) return `${bytes} B`;
1149
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1150
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1151
- }
1152
1057
  function shutdown() {
1153
1058
  log.info("Hub is shutting down...");
1154
1059
  assetStore.flush();
package/dist/hub.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hub.mjs","names":["createServer","getRecordProperty","formatBytes","createAssetResourceLinkBlock","formatAssetResourceName","describeAsset","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_ASSET_RESOURCE_NAME = \"tempad-assets\";\nconst MCP_ASSET_URI_PREFIX = \"asset://tempad/\";\nconst MCP_ASSET_URI_TEMPLATE = `${MCP_ASSET_URI_PREFIX}{hash}`;\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 MCP_ASSET_RESOURCE_URI_PATTERN = new RegExp(`^${MCP_ASSET_URI_PREFIX.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}[a-f0-9]{${MCP_HASH_HEX_LENGTH}}$`, \"i\");\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\tresourceUri: z.string().regex(MCP_ASSET_RESOURCE_URI_PATTERN),\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_RESOURCE_NAME, MCP_ASSET_TTL_MS, MCP_ASSET_URI_PREFIX, MCP_ASSET_URI_TEMPLATE, 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` / `get_screenshot` only to resolve layout/overlap/masks/effects uncertainty. Screenshots are for visual verification only; do not derive numeric values from pixels.\\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: fetch bytes via `resources/read` using `resourceUri` when possible; fall back to `asset.url` for large blobs. 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 AssetDescriptor,\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/get_screenshot 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 }),\n extTool({\n name: 'get_structure',\n description:\n 'Get a 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/URIs 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\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 content: [\n {\n type: 'text' as const,\n text: `Tool \"${toolName}\" failed: ${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}. Fetch bytes via resources/read using resourceUri.`\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 const assetLinks = payload.assets?.length\n ? payload.assets.map((asset) => createAssetResourceLinkBlock(asset))\n : []\n\n return {\n content: [\n {\n type: 'text' as const,\n text: summary.join('\\n')\n },\n ...assetLinks\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)\n }\n\n return {\n content: [\n descriptionBlock,\n {\n type: 'text' as const,\n text: `![Screenshot](${payload.asset.url})`\n },\n createResourceLinkBlock(payload.asset, payload)\n ],\n structuredContent: payload\n }\n}\n\nfunction createResourceLinkBlock(asset: AssetDescriptor, result: GetScreenshotResult) {\n return {\n type: 'resource_link' as const,\n name: 'Screenshot',\n uri: asset.resourceUri,\n mimeType: asset.mimeType,\n description: `Screenshot ${result.width}x${result.height} @${result.scale}x - Download: ${asset.url}`\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\nfunction createAssetResourceLinkBlock(asset: AssetDescriptor) {\n return {\n type: 'resource_link' as const,\n name: formatAssetResourceName(asset.hash),\n uri: asset.resourceUri,\n mimeType: asset.mimeType,\n description: `${describeAsset(asset)} - Download: ${asset.url}`\n }\n}\n\nfunction describeAsset(asset: AssetDescriptor): string {\n return `${asset.mimeType} (${formatBytes(asset.size)})`\n}\n\nfunction formatAssetResourceName(hash: string): string {\n return `asset:${hash.slice(0, 8)}`\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, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport {\n GetAssetsResultSchema,\n MCP_ASSET_RESOURCE_NAME,\n MCP_ASSET_URI_PREFIX,\n MCP_ASSET_URI_TEMPLATE,\n MessageFromExtensionSchema,\n TEMPAD_MCP_ERROR_CODES,\n type TempadMcpErrorCode\n} from '@tempad-dev/shared'\nimport { nanoid } from 'nanoid'\nimport { existsSync, rmSync, chmodSync, readFileSync, statSync } 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()\nregisterAssetResources()\nscheduleAssetCleanup()\n\nfunction registerAssetResources(): void {\n const template = new ResourceTemplate(MCP_ASSET_URI_TEMPLATE, {\n list: async () => ({\n // Intentionally keep resources/list empty: assets are ephemeral, tool-linked blobs.\n // Resource discovery would leak across sessions/design files and add UI noise.\n // Hosts should use resource_link from tool responses to access assets on demand.\n resources: []\n })\n })\n\n mcp.registerResource(\n MCP_ASSET_RESOURCE_NAME,\n template,\n {\n description:\n 'Exported PNG/SVG assets which can serve as screenshots or be referenced in code output.'\n },\n async (_uri, variables) => {\n const hash = typeof variables.hash === 'string' ? variables.hash : ''\n return readAssetResource(hash)\n }\n )\n}\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(\n { ttlMs: assetTtlMs, intervalMs },\n 'Asset TTL cleanup enabled (list remains empty; assets are tool-linked).'\n )\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\nasync function readAssetResource(hash: string) {\n if (!hash) {\n throw new Error('Missing asset hash in resource URI.')\n }\n const record = assetStore.get(hash)\n if (!record) {\n throw new Error(`Asset ${hash} not found.`)\n }\n\n if (!existsSync(record.filePath)) {\n assetStore.remove(hash, { removeFile: false })\n throw new Error(`Asset ${hash} file is missing.`)\n }\n\n const stat = statSync(record.filePath)\n // Base64 encoding increases size by ~33% (4 bytes for every 3 bytes)\n const estimatedSize = Math.ceil(stat.size / 3) * 4\n if (estimatedSize > maxPayloadBytes) {\n throw new Error(\n `Asset ${hash} is too large (${formatBytes(stat.size)}, encoded: ${formatBytes(estimatedSize)}) to read via MCP protocol. Use HTTP download.`\n )\n }\n\n assetStore.touch(hash)\n const buffer = readFileSync(record.filePath)\n const resourceUri = buildAssetResourceUri(hash)\n\n if (isTextualMime(record.mimeType)) {\n return {\n contents: [\n {\n uri: resourceUri,\n mimeType: record.mimeType,\n text: buffer.toString('utf8')\n }\n ]\n }\n }\n\n return {\n contents: [\n {\n uri: resourceUri,\n mimeType: record.mimeType,\n blob: buffer.toString('base64')\n }\n ]\n }\n}\n\nfunction isTextualMime(mimeType: string): boolean {\n return mimeType === 'image/svg+xml' || mimeType.startsWith('text/')\n}\n\nfunction buildAssetResourceUri(hash: string): string {\n return `${MCP_ASSET_URI_PREFIX}${hash}`\n}\n\nfunction formatAssetResourceName(hash: string): string {\n return `asset:${hash.slice(0, 8)}`\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 resourceUri: buildAssetResourceUri(record.hash),\n width: record.metadata?.width,\n height: record.metadata?.height\n }\n}\n\nfunction createAssetResourceLinkBlock(asset: AssetDescriptor) {\n return {\n type: 'resource_link' as const,\n name: formatAssetResourceName(asset.hash),\n uri: asset.resourceUri,\n mimeType: asset.mimeType,\n description: `${describeAsset(asset)} - Download: ${asset.url}`\n }\n}\n\nfunction describeAsset(asset: AssetDescriptor): string {\n return `${asset.mimeType} (${formatBytes(asset.size)})`\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 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 { promise, requestId } = register<Result>(activeExt.id, toolTimeoutMs)\n\n const message: ToolCallMessage = {\n type: 'toolCall',\n id: requestId,\n payload: {\n name: tool.name,\n args: parsedArgs\n }\n }\n activeExt.ws.send(JSON.stringify(message))\n log.info({ tool: tool.name, req: requestId, extId: activeExt.id }, 'Forwarded tool call.')\n\n const payload = await promise\n return createToolResponse(tool.name, payload)\n } catch (error) {\n log.error({ tool: tool.name, error }, 'Tool invocation failed before reaching extension.')\n return createToolErrorResponse(tool.name, error)\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(\n 'Use resources/read with each resourceUri or fetch the fallback URL to download bytes.'\n )\n\n const content = [\n {\n type: 'text' as const,\n text: summary.join('\\n')\n },\n ...payload.assets.map((asset) => createAssetResourceLinkBlock(asset))\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 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\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 reject(id, coerceToolError(error))\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\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 content: [\n {\n type: 'text' as const,\n text: `Tool \"${toolName}\" failed: ${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 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 { promise, requestId } = register<Result>(activeExt.id, toolTimeoutMs)\n\n const message: ToolCallMessage = {\n type: 'toolCall',\n id: requestId,\n payload: {\n name: tool.name,\n args: parsedArgs\n }\n }\n activeExt.ws.send(JSON.stringify(message))\n log.info({ tool: tool.name, req: requestId, extId: activeExt.id }, 'Forwarded tool call.')\n\n const payload = await promise\n return createToolResponse(tool.name, payload)\n } catch (error) {\n log.error({ tool: tool.name, error }, 'Tool invocation failed before reaching extension.')\n return createToolErrorResponse(tool.name, error)\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 reject(id, coerceToolError(error))\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,12 +6,18 @@ import pino from "pino";
6
6
  //#region package.json
7
7
  var package_default = {
8
8
  name: "@tempad-dev/mcp",
9
- version: "0.4.1",
9
+ version: "0.4.2",
10
10
  description: "MCP server for TemPad Dev.",
11
+ repository: {
12
+ "type": "git",
13
+ "url": "git+https://github.com/ecomfe/tempad-dev.git",
14
+ "directory": "packages/mcp-server"
15
+ },
11
16
  bin: "dist/cli.mjs",
12
17
  files: ["README.md", "dist/**/*"],
13
18
  type: "module",
14
19
  main: "dist/cli.mjs",
20
+ publishConfig: { "access": "public" },
15
21
  scripts: {
16
22
  "build": "tsdown",
17
23
  "typecheck": "tsc -p tsconfig.json --noEmit",
@@ -107,4 +113,4 @@ const SOCK_PATH = resolveSockPath();
107
113
 
108
114
  //#endregion
109
115
  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 };
110
- //# sourceMappingURL=shared-CnY7ym27.mjs.map
116
+ //# sourceMappingURL=shared-Do3A1dyd.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared-CnY7ym27.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-Do3A1dyd.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,7 +1,12 @@
1
1
  {
2
2
  "name": "@tempad-dev/mcp",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "MCP server for TemPad Dev.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/ecomfe/tempad-dev.git",
8
+ "directory": "packages/mcp-server"
9
+ },
5
10
  "bin": "dist/cli.mjs",
6
11
  "files": [
7
12
  "README.md",
@@ -9,6 +14,21 @@
9
14
  ],
10
15
  "type": "module",
11
16
  "main": "dist/cli.mjs",
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "scripts": {
21
+ "build": "tsdown",
22
+ "typecheck": "tsc -p tsconfig.json --noEmit",
23
+ "test": "vitest --config vitest.config.ts",
24
+ "test:run": "vitest run --config vitest.config.ts",
25
+ "test:coverage": "vitest run --config vitest.config.ts --coverage",
26
+ "lint": "eslint . --ext .ts,.mts,.cts,.js,.mjs,.cjs",
27
+ "lint:fix": "eslint . --ext .ts,.mts,.cts,.js,.mjs,.cjs --fix",
28
+ "format": "oxfmt --ignore-path ../../.gitignore",
29
+ "format:check": "oxfmt --ignore-path ../../.gitignore --check",
30
+ "prepublishOnly": "pnpm run build"
31
+ },
12
32
  "dependencies": {
13
33
  "@modelcontextprotocol/sdk": "^1.24.0",
14
34
  "nanoid": "^5.1.6",
@@ -19,23 +39,12 @@
19
39
  "zod": "^4.1.12"
20
40
  },
21
41
  "devDependencies": {
22
- "@tempad-dev/shared": "^0.1.0",
42
+ "@tempad-dev/shared": "workspace:^0.1.0",
23
43
  "@types/node": "^24.0.0",
24
44
  "@types/proper-lockfile": "^4.1.4",
25
45
  "@types/ws": "^8.5.12",
26
46
  "tsdown": "^0.20.0",
27
47
  "typescript": "^5.9.3",
28
48
  "unplugin-raw": "^0.6.3"
29
- },
30
- "scripts": {
31
- "build": "tsdown",
32
- "typecheck": "tsc -p tsconfig.json --noEmit",
33
- "test": "vitest --config vitest.config.ts",
34
- "test:run": "vitest run --config vitest.config.ts",
35
- "test:coverage": "vitest run --config vitest.config.ts --coverage",
36
- "lint": "eslint . --ext .ts,.mts,.cts,.js,.mjs,.cjs",
37
- "lint:fix": "eslint . --ext .ts,.mts,.cts,.js,.mjs,.cjs --fix",
38
- "format": "oxfmt --ignore-path ../../.gitignore",
39
- "format:check": "oxfmt --ignore-path ../../.gitignore --check"
40
49
  }
41
- }
50
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2024-Present Baidu EFE team
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.