@atomixstudio/mcp 1.0.21 → 1.0.23

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.
@@ -10,6 +10,7 @@ var BRIDGE_METHODS = [
10
10
  "create_paint_styles",
11
11
  "create_text_styles",
12
12
  "create_number_variables",
13
+ "create_effect_styles",
13
14
  "apply_fill_to_selection",
14
15
  "create_design_placeholder",
15
16
  "design_create_frame",
@@ -41,4 +42,4 @@ export {
41
42
  normalizeBridgeMethod,
42
43
  isAllowedMethod
43
44
  };
44
- //# sourceMappingURL=chunk-FFAGTYRZ.js.map
45
+ //# sourceMappingURL=chunk-BXWOCXYT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/figma-bridge-protocol.ts"],"sourcesContent":["/**\n * Bridge wire protocol (merged from figma-bridge).\n * All messages are JSON. Only whitelisted methods are allowed.\n * Used by the in-process Figma bridge (WebSocket server in mcp-user).\n */\n\nexport const BRIDGE_METHODS = [\n \"get_document_info\",\n \"get_selection\",\n \"get_node_info\",\n \"get_figma_variables_and_styles\",\n \"create_color_variables\",\n \"create_paint_styles\",\n \"create_text_styles\",\n \"create_number_variables\",\n \"create_effect_styles\",\n \"apply_fill_to_selection\",\n \"create_design_placeholder\",\n \"design_create_frame\",\n \"design_create_text\",\n \"design_create_rectangle\",\n \"design_set_auto_layout\",\n \"design_set_layout_constraints\",\n \"design_append_child\",\n \"get_design_screenshot\",\n \"finalize_design_frame\",\n] as const;\n\nexport type BridgeMethod = (typeof BRIDGE_METHODS)[number];\n\nexport interface BridgeRequest {\n id: string;\n method: string;\n params?: Record<string, unknown>;\n}\n\nexport interface BridgeSuccessResponse {\n id: string;\n result: unknown;\n}\n\nexport interface BridgeErrorResponse {\n id: string;\n error: string;\n}\n\nexport type BridgeResponse = BridgeSuccessResponse | BridgeErrorResponse;\n\nexport function isBridgeRequest(msg: unknown): msg is BridgeRequest {\n if (!msg || typeof msg !== \"object\") return false;\n const m = msg as Record<string, unknown>;\n return (\n typeof m.id === \"string\" &&\n m.id.length > 0 &&\n typeof m.method === \"string\" &&\n m.method.length > 0\n );\n}\n\n/** Convert camelCase to snake_case for bridge (e.g. getFigmaVariablesAndStyles -> get_figma_variables_and_styles). */\nexport function normalizeBridgeMethod(method: string): string {\n if (typeof method !== \"string\" || !method) return method;\n return method.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);\n}\n\nexport function isAllowedMethod(method: string): method is BridgeMethod {\n const normalized = normalizeBridgeMethod(method);\n return (BRIDGE_METHODS as readonly string[]).includes(normalized);\n}\n"],"mappings":";;;AAMO,IAAM,iBAAiB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAsBO,SAAS,gBAAgB,KAAoC;AAClE,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,IAAI;AACV,SACE,OAAO,EAAE,OAAO,YAChB,EAAE,GAAG,SAAS,KACd,OAAO,EAAE,WAAW,YACpB,EAAE,OAAO,SAAS;AAEtB;AAGO,SAAS,sBAAsB,QAAwB;AAC5D,MAAI,OAAO,WAAW,YAAY,CAAC,OAAQ,QAAO;AAClD,SAAO,OAAO,QAAQ,UAAU,CAAC,MAAM,IAAI,EAAE,YAAY,CAAC,EAAE;AAC9D;AAEO,SAAS,gBAAgB,QAAwC;AACtE,QAAM,aAAa,sBAAsB,MAAM;AAC/C,SAAQ,eAAqC,SAAS,UAAU;AAClE;","names":[]}
@@ -3,7 +3,7 @@
3
3
  * All messages are JSON. Only whitelisted methods are allowed.
4
4
  * Used by the in-process Figma bridge (WebSocket server in mcp-user).
5
5
  */
6
- declare const BRIDGE_METHODS: readonly ["get_document_info", "get_selection", "get_node_info", "get_figma_variables_and_styles", "create_color_variables", "create_paint_styles", "create_text_styles", "create_number_variables", "apply_fill_to_selection", "create_design_placeholder", "design_create_frame", "design_create_text", "design_create_rectangle", "design_set_auto_layout", "design_set_layout_constraints", "design_append_child", "get_design_screenshot", "finalize_design_frame"];
6
+ declare const BRIDGE_METHODS: readonly ["get_document_info", "get_selection", "get_node_info", "get_figma_variables_and_styles", "create_color_variables", "create_paint_styles", "create_text_styles", "create_number_variables", "create_effect_styles", "apply_fill_to_selection", "create_design_placeholder", "design_create_frame", "design_create_text", "design_create_rectangle", "design_set_auto_layout", "design_set_layout_constraints", "design_append_child", "get_design_screenshot", "finalize_design_frame"];
7
7
  type BridgeMethod = (typeof BRIDGE_METHODS)[number];
8
8
  interface BridgeRequest {
9
9
  id: string;
@@ -4,7 +4,7 @@ import {
4
4
  isAllowedMethod,
5
5
  isBridgeRequest,
6
6
  normalizeBridgeMethod
7
- } from "./chunk-FFAGTYRZ.js";
7
+ } from "./chunk-BXWOCXYT.js";
8
8
  export {
9
9
  BRIDGE_METHODS,
10
10
  isAllowedMethod,
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  isAllowedMethod,
4
4
  normalizeBridgeMethod
5
- } from "./chunk-FFAGTYRZ.js";
5
+ } from "./chunk-BXWOCXYT.js";
6
6
 
7
7
  // src/index.ts
8
8
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -1480,6 +1480,25 @@ function figmaColorNameWithGroup(key) {
1480
1480
  const groupDisplay = group.charAt(0).toUpperCase() + group.slice(1).toLowerCase();
1481
1481
  return `${groupDisplay} / ${name}`;
1482
1482
  }
1483
+ var FIGMA_COLOR_GROUP_ORDER = {
1484
+ background: 0,
1485
+ text: 1,
1486
+ border: 2,
1487
+ icon: 3,
1488
+ brand: 4,
1489
+ action: 5,
1490
+ feedback: 6
1491
+ };
1492
+ var FIGMA_SHADOW_ORDER = {
1493
+ none: 0,
1494
+ xs: 1,
1495
+ sm: 2,
1496
+ md: 3,
1497
+ lg: 4,
1498
+ xl: 5,
1499
+ "2xl": 6,
1500
+ focus: 7
1501
+ };
1483
1502
  function tokenValueToNumber(s) {
1484
1503
  if (typeof s !== "string" || !s.trim()) return 0;
1485
1504
  const t = s.trim();
@@ -1593,6 +1612,25 @@ function buildFigmaPayloadsFromDS(data) {
1593
1612
  }
1594
1613
  }
1595
1614
  if (variables.length === 0 && modes.length === 0) modes.push("Light");
1615
+ const colorSortKey = (figmaName) => {
1616
+ const slash = figmaName.indexOf(" / ");
1617
+ const group = slash >= 0 ? figmaName.slice(0, slash).toLowerCase() : "";
1618
+ const name = slash >= 0 ? figmaName.slice(slash + 3) : figmaName;
1619
+ const order = FIGMA_COLOR_GROUP_ORDER[group] ?? 999;
1620
+ return [order, name];
1621
+ };
1622
+ variables.sort((a, b) => {
1623
+ const [oA, nA] = colorSortKey(a.name);
1624
+ const [oB, nB] = colorSortKey(b.name);
1625
+ if (oA !== oB) return oA - oB;
1626
+ return nA.localeCompare(nB);
1627
+ });
1628
+ paintStyles.sort((a, b) => {
1629
+ const [oA, nA] = colorSortKey(a.name);
1630
+ const [oB, nB] = colorSortKey(b.name);
1631
+ if (oA !== oB) return oA - oB;
1632
+ return nA.localeCompare(nB);
1633
+ });
1596
1634
  const collectionName = data.meta?.name ? `${data.meta.name} Colors` : "Atomix Colors";
1597
1635
  const textStyles = [];
1598
1636
  const sizeToPx = (val, basePx = 16) => {
@@ -1619,8 +1657,8 @@ function buildFigmaPayloadsFromDS(data) {
1619
1657
  };
1620
1658
  const firstFont = (obj) => {
1621
1659
  if (typeof obj === "string") {
1622
- const match = obj.match(/['"]?([^'",\s]+)['"]?/);
1623
- return match ? match[1] : "Inter";
1660
+ const primary = obj.split(",")[0].trim().replace(/^['"]|['"]$/g, "");
1661
+ return primary || "Inter";
1624
1662
  }
1625
1663
  if (obj && typeof obj === "object" && !Array.isArray(obj)) {
1626
1664
  const v = obj.body ?? obj.heading ?? obj.display ?? Object.values(obj)[0];
@@ -1693,49 +1731,70 @@ function buildFigmaPayloadsFromDS(data) {
1693
1731
  textStyles.push(payload);
1694
1732
  }
1695
1733
  }
1696
- const numberVariables = [];
1734
+ textStyles.sort((a, b) => {
1735
+ if (a.fontSize !== b.fontSize) return a.fontSize - b.fontSize;
1736
+ return (a.name || "").localeCompare(b.name || "");
1737
+ });
1697
1738
  const dsName = data.meta?.name ?? "Atomix";
1698
- const primitivesCollectionName = `${dsName} Primitives`;
1739
+ const numberVariableCollections = [];
1699
1740
  const spacing = tokens?.spacing;
1700
1741
  if (spacing?.scale && typeof spacing.scale === "object") {
1742
+ const variables2 = [];
1701
1743
  for (const [key, val] of Object.entries(spacing.scale)) {
1702
1744
  const n = tokenValueToNumber(val);
1703
- if (n >= 0) numberVariables.push({ name: `Spacing / ${key}`, value: n });
1745
+ if (n >= 0) variables2.push({ name: key, value: n });
1704
1746
  }
1747
+ variables2.sort((a, b) => a.value - b.value);
1748
+ if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${dsName} Spacing`, categoryKey: "Spacing", variables: variables2, scopes: ["GAP"] });
1705
1749
  }
1706
1750
  const radius = tokens?.radius;
1707
1751
  if (radius?.scale && typeof radius.scale === "object") {
1752
+ const variables2 = [];
1708
1753
  for (const [key, val] of Object.entries(radius.scale)) {
1709
1754
  const n = tokenValueToNumber(val);
1710
- if (n >= 0) numberVariables.push({ name: `Radius / ${key}`, value: n });
1755
+ if (n >= 0) variables2.push({ name: key, value: n });
1711
1756
  }
1757
+ variables2.sort((a, b) => a.value - b.value);
1758
+ if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${dsName} Radius`, categoryKey: "Radius", variables: variables2, scopes: ["CORNER_RADIUS"] });
1712
1759
  }
1713
1760
  const borders = tokens?.borders;
1714
1761
  if (borders?.width && typeof borders.width === "object") {
1762
+ const variables2 = [];
1715
1763
  for (const [key, val] of Object.entries(borders.width)) {
1716
1764
  const n = tokenValueToNumber(val);
1717
- if (n >= 0) numberVariables.push({ name: `Borders / ${key}`, value: n });
1765
+ if (n >= 0) variables2.push({ name: key, value: n });
1718
1766
  }
1767
+ variables2.sort((a, b) => a.value - b.value);
1768
+ if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${dsName} Borders`, categoryKey: "Borders", variables: variables2, scopes: ["STROKE_FLOAT"] });
1719
1769
  }
1720
1770
  const sizing = tokens?.sizing;
1721
1771
  if (sizing?.height && typeof sizing.height === "object") {
1772
+ const variables2 = [];
1722
1773
  for (const [key, val] of Object.entries(sizing.height)) {
1723
1774
  const n = tokenValueToNumber(val);
1724
- if (n >= 0) numberVariables.push({ name: `Height / ${key}`, value: n });
1775
+ if (n >= 0) variables2.push({ name: key, value: n });
1725
1776
  }
1777
+ variables2.sort((a, b) => a.value - b.value);
1778
+ if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${dsName} Height`, categoryKey: "Height", variables: variables2, scopes: ["WIDTH_HEIGHT"] });
1726
1779
  }
1727
1780
  if (sizing?.icon && typeof sizing.icon === "object") {
1781
+ const variables2 = [];
1728
1782
  for (const [key, val] of Object.entries(sizing.icon)) {
1729
1783
  const n = tokenValueToNumber(val);
1730
- if (n >= 0) numberVariables.push({ name: `Icon / ${key}`, value: n });
1784
+ if (n >= 0) variables2.push({ name: key, value: n });
1731
1785
  }
1786
+ variables2.sort((a, b) => a.value - b.value);
1787
+ if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${dsName} Icon`, categoryKey: "Icon", variables: variables2, scopes: ["WIDTH_HEIGHT"] });
1732
1788
  }
1733
1789
  const layout = tokens?.layout;
1734
1790
  if (layout?.breakpoint && typeof layout.breakpoint === "object") {
1791
+ const variables2 = [];
1735
1792
  for (const [key, val] of Object.entries(layout.breakpoint)) {
1736
1793
  const n = tokenValueToNumber(val);
1737
- if (n >= 0) numberVariables.push({ name: `Breakpoint / ${key}`, value: n });
1794
+ if (n >= 0) variables2.push({ name: key, value: n });
1738
1795
  }
1796
+ variables2.sort((a, b) => a.value - b.value);
1797
+ if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${dsName} Breakpoint`, categoryKey: "Breakpoint", variables: variables2, scopes: ["WIDTH_HEIGHT"] });
1739
1798
  }
1740
1799
  const effectStyles = [];
1741
1800
  const shadows = tokens?.shadows;
@@ -1757,22 +1816,33 @@ function buildFigmaPayloadsFromDS(data) {
1757
1816
  effectStyles.push({ name: "Shadow / focus", effects });
1758
1817
  }
1759
1818
  }
1819
+ effectStyles.sort((a, b) => {
1820
+ const nameA = a.name.startsWith("Shadow / ") ? a.name.slice(9) : a.name;
1821
+ const nameB = b.name.startsWith("Shadow / ") ? b.name.slice(9) : b.name;
1822
+ const orderA = FIGMA_SHADOW_ORDER[nameA] ?? 100;
1823
+ const orderB = FIGMA_SHADOW_ORDER[nameB] ?? 100;
1824
+ if (orderA !== orderB) return orderA - orderB;
1825
+ return nameA.localeCompare(nameB);
1826
+ });
1760
1827
  return {
1761
1828
  colorVariables: { collectionName, modes, variables },
1762
1829
  paintStyles,
1763
1830
  textStyles,
1764
- numberVariables: { collectionName: primitivesCollectionName, variables: numberVariables },
1831
+ numberVariableCollections,
1765
1832
  effectStyles
1766
1833
  };
1767
1834
  }
1768
1835
  function getExpectedFigmaNamesFromDS(data) {
1769
1836
  const payloads = buildFigmaPayloadsFromDS(data);
1837
+ const numberVariableNames = payloads.numberVariableCollections.flatMap(
1838
+ (c) => c.variables.map((v) => `${c.categoryKey} / ${v.name}`)
1839
+ );
1770
1840
  return {
1771
1841
  colorVariableNames: payloads.colorVariables.variables.map((v) => v.name),
1772
1842
  paintStyleNames: payloads.paintStyles.map((s) => s.name),
1773
1843
  textStyleNames: payloads.textStyles.map((s) => s.name),
1774
1844
  effectStyleNames: payloads.effectStyles.map((s) => s.name),
1775
- numberVariableNames: payloads.numberVariables.variables.map((v) => v.name)
1845
+ numberVariableNames
1776
1846
  };
1777
1847
  }
1778
1848
  function parseArgs() {
@@ -1861,7 +1931,7 @@ var TOKEN_CATEGORIES = ["colors", "typography", "spacing", "sizing", "shadows",
1861
1931
  var server = new Server(
1862
1932
  {
1863
1933
  name: "atomix-mcp-user",
1864
- version: "1.0.21"
1934
+ version: "1.0.22"
1865
1935
  },
1866
1936
  {
1867
1937
  capabilities: {
@@ -2881,7 +2951,7 @@ ${tokenResponseText}` : summary;
2881
2951
  variables: payloads.colorVariables.variables,
2882
2952
  paintStyles: payloads.paintStyles,
2883
2953
  textStyles: payloads.textStyles,
2884
- numberVariables: payloads.numberVariables,
2954
+ numberVariableCollections: payloads.numberVariableCollections,
2885
2955
  effectStyles: payloads.effectStyles
2886
2956
  };
2887
2957
  return {
@@ -2898,12 +2968,18 @@ ${tokenResponseText}` : summary;
2898
2968
  if (payloads.paintStyles.length > 0) {
2899
2969
  out.paintStyles = await sendBridgeRequest("create_paint_styles", { styles: payloads.paintStyles });
2900
2970
  }
2901
- if (payloads.numberVariables.variables.length > 0) {
2971
+ if (payloads.numberVariableCollections.length > 0) {
2972
+ const numberResults = [];
2902
2973
  try {
2903
- out.numberVariables = await sendBridgeRequest("create_number_variables", {
2904
- collectionName: payloads.numberVariables.collectionName,
2905
- variables: payloads.numberVariables.variables
2906
- });
2974
+ for (const coll of payloads.numberVariableCollections) {
2975
+ const result = await sendBridgeRequest("create_number_variables", {
2976
+ collectionName: coll.collectionName,
2977
+ variables: coll.variables,
2978
+ scopes: coll.scopes
2979
+ });
2980
+ numberResults.push({ collectionName: coll.collectionName, result });
2981
+ }
2982
+ out.numberVariables = numberResults;
2907
2983
  } catch (e) {
2908
2984
  const msg = e instanceof Error ? e.message : String(e);
2909
2985
  out.numberVariables = { error: msg };
@@ -2924,7 +3000,7 @@ ${tokenResponseText}` : summary;
2924
3000
  variables: payloads.colorVariables.variables,
2925
3001
  paintStyles: payloads.paintStyles,
2926
3002
  textStyles: payloads.textStyles,
2927
- numberVariables: payloads.numberVariables,
3003
+ numberVariableCollections: payloads.numberVariableCollections,
2928
3004
  effectStyles: payloads.effectStyles
2929
3005
  };
2930
3006
  } catch (e) {
@@ -2935,7 +3011,7 @@ ${tokenResponseText}` : summary;
2935
3011
  variables: payloads.colorVariables.variables,
2936
3012
  paintStyles: payloads.paintStyles,
2937
3013
  textStyles: payloads.textStyles,
2938
- numberVariables: payloads.numberVariables,
3014
+ numberVariableCollections: payloads.numberVariableCollections,
2939
3015
  effectStyles: payloads.effectStyles
2940
3016
  };
2941
3017
  const errMsg = out.error.toLowerCase();
@@ -3050,9 +3126,20 @@ ${tokenResponseText}` : summary;
3050
3126
  const response = raw;
3051
3127
  const variableByName = /* @__PURE__ */ new Map();
3052
3128
  if (response.variableCollections) {
3129
+ const numberCategorySuffixes = [" Spacing", " Radius", " Borders", " Height", " Icon", " Breakpoint"];
3053
3130
  for (const coll of response.variableCollections) {
3054
- for (const v of coll.variables || []) {
3055
- variableByName.set(v.name, v.id);
3131
+ if (coll.name.endsWith(" Colors")) {
3132
+ for (const v of coll.variables || []) {
3133
+ variableByName.set(v.name, v.id);
3134
+ }
3135
+ continue;
3136
+ }
3137
+ const numberSuffix = numberCategorySuffixes.find((s) => coll.name.endsWith(s));
3138
+ if (numberSuffix) {
3139
+ const category = numberSuffix.slice(1);
3140
+ for (const v of coll.variables || []) {
3141
+ variableByName.set(`${category} / ${v.name}`, v.id);
3142
+ }
3056
3143
  }
3057
3144
  }
3058
3145
  }