@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.
- package/dist/{chunk-FFAGTYRZ.js → chunk-BXWOCXYT.js} +2 -1
- package/dist/chunk-BXWOCXYT.js.map +1 -0
- package/dist/figma-bridge-protocol.d.ts +1 -1
- package/dist/figma-bridge-protocol.js +1 -1
- package/dist/index.js +111 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-FFAGTYRZ.js.map +0 -1
|
@@ -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-
|
|
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;
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
isAllowedMethod,
|
|
4
4
|
normalizeBridgeMethod
|
|
5
|
-
} from "./chunk-
|
|
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
|
|
1623
|
-
return
|
|
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
|
-
|
|
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
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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.
|
|
2971
|
+
if (payloads.numberVariableCollections.length > 0) {
|
|
2972
|
+
const numberResults = [];
|
|
2902
2973
|
try {
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3055
|
-
|
|
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
|
}
|