mcp-figma-toolkit 1.0.9 → 1.0.11

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/plugin/plugin.js +156 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-figma-toolkit",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "MCP server that enables AI agents to manipulate Figma documents - create shapes, text, styles, components, variables, and more",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",
package/plugin/plugin.js CHANGED
@@ -27,16 +27,73 @@ function serializePaints(paints) {
27
27
  base.hex = rgbToHex(p.color);
28
28
  } else if (p.type && p.type.indexOf("GRADIENT") === 0 && p.gradientStops) {
29
29
  base.gradientStops = p.gradientStops.map(function(s) {
30
- return {
30
+ var stop = {
31
31
  position: s.position,
32
32
  hex: s.color ? rgbToHex(s.color) : null,
33
33
  opacity: s.color && s.color.a != null ? s.color.a : 1
34
34
  };
35
+ if (s.boundVariables) {
36
+ var sbv = {};
37
+ var stopBVKeys = ["color"];
38
+ for (var sk in s.boundVariables) {
39
+ var sb = s.boundVariables[sk];
40
+ if (sb && sb.id) {
41
+ sbv[sk] = { type: sb.type, id: sb.id };
42
+ try {
43
+ var sv = figma.variables.getVariableById(sb.id);
44
+ if (sv) { sbv[sk].name = sv.name; sbv[sk].resolvedType = sv.resolvedType; }
45
+ } catch (_) {}
46
+ }
47
+ }
48
+ for (var si = 0; si < stopBVKeys.length; si++) {
49
+ var spk = stopBVKeys[si];
50
+ if (!(spk in sbv)) {
51
+ try {
52
+ var spb = s.boundVariables[spk];
53
+ if (spb && spb.id) {
54
+ sbv[spk] = { type: spb.type, id: spb.id };
55
+ var spv = figma.variables.getVariableById(spb.id);
56
+ if (spv) { sbv[spk].name = spv.name; sbv[spk].resolvedType = spv.resolvedType; }
57
+ }
58
+ } catch (_) {}
59
+ }
60
+ }
61
+ if (Object.keys(sbv).length > 0) stop.boundVariables = sbv;
62
+ }
63
+ return stop;
35
64
  });
36
65
  } else if (p.type === "IMAGE") {
37
66
  base.scaleMode = p.scaleMode;
38
67
  base.imageHash = p.imageHash;
39
68
  }
69
+ if (p.boundVariables) {
70
+ var pbv = {};
71
+ var paintBVKeys = ["color", "opacity"];
72
+ for (var pk in p.boundVariables) {
73
+ var pb = p.boundVariables[pk];
74
+ if (pb && pb.id) {
75
+ pbv[pk] = { type: pb.type, id: pb.id };
76
+ try {
77
+ var pv = figma.variables.getVariableById(pb.id);
78
+ if (pv) { pbv[pk].name = pv.name; pbv[pk].resolvedType = pv.resolvedType; }
79
+ } catch (_) {}
80
+ }
81
+ }
82
+ for (var pi = 0; pi < paintBVKeys.length; pi++) {
83
+ var ppk = paintBVKeys[pi];
84
+ if (!(ppk in pbv)) {
85
+ try {
86
+ var ppb = p.boundVariables[ppk];
87
+ if (ppb && ppb.id) {
88
+ pbv[ppk] = { type: ppb.type, id: ppb.id };
89
+ var ppv = figma.variables.getVariableById(ppb.id);
90
+ if (ppv) { pbv[ppk].name = ppv.name; pbv[ppk].resolvedType = ppv.resolvedType; }
91
+ }
92
+ } catch (_) {}
93
+ }
94
+ }
95
+ if (Object.keys(pbv).length > 0) base.boundVariables = pbv;
96
+ }
40
97
  return base;
41
98
  });
42
99
  }
@@ -53,6 +110,22 @@ function serializeEffects(effects) {
53
110
  base.hex = rgbToHex(e.color);
54
111
  base.opacity = e.color.a != null ? e.color.a : 1;
55
112
  }
113
+ if (e.boundVariables) {
114
+ var ebv = {};
115
+ var ebvKeys = ["radius", "spread", "color", "offsetX", "offsetY"];
116
+ for (var ei = 0; ei < ebvKeys.length; ei++) {
117
+ var ek = ebvKeys[ei];
118
+ try {
119
+ var eb = e.boundVariables[ek];
120
+ if (eb && eb.id) {
121
+ ebv[ek] = { type: eb.type, id: eb.id };
122
+ var ev = figma.variables.getVariableById(eb.id);
123
+ if (ev) { ebv[ek].name = ev.name; ebv[ek].resolvedType = ev.resolvedType; }
124
+ }
125
+ } catch (_) {}
126
+ }
127
+ if (Object.keys(ebv).length > 0) base.boundVariables = ebv;
128
+ }
56
129
  return base;
57
130
  });
58
131
  }
@@ -718,6 +791,45 @@ function getNodeInfo({ nodeId }) {
718
791
  try { info.effects = serializeEffects(node.effects); } catch (_) {}
719
792
  }
720
793
 
794
+ // Constraints
795
+ if ("constraints" in node) {
796
+ info.constraints = {
797
+ horizontal: node.constraints.horizontal,
798
+ vertical: node.constraints.vertical
799
+ };
800
+ }
801
+
802
+ // Clips content (for frames)
803
+ if ("clipsContent" in node) info.clipsContent = node.clipsContent;
804
+
805
+ // Layout positioning within auto-layout parent
806
+ if ("layoutAlign" in node) info.layoutAlign = node.layoutAlign;
807
+ if ("layoutGrow" in node) info.layoutGrow = node.layoutGrow;
808
+ if ("layoutPositioning" in node) info.layoutPositioning = node.layoutPositioning;
809
+
810
+ // Style IDs (paint styles, text styles, effect styles)
811
+ if ("fillStyleId" in node && node.fillStyleId && node.fillStyleId !== "") {
812
+ info.fillStyleId = node.fillStyleId;
813
+ }
814
+ if ("strokeStyleId" in node && node.strokeStyleId && node.strokeStyleId !== "") {
815
+ info.strokeStyleId = node.strokeStyleId;
816
+ }
817
+ if ("effectStyleId" in node && node.effectStyleId && node.effectStyleId !== "") {
818
+ info.effectStyleId = node.effectStyleId;
819
+ }
820
+ if ("textStyleId" in node && node.textStyleId && node.textStyleId !== "") {
821
+ info.textStyleId = node.textStyleId;
822
+ }
823
+
824
+ // Mask
825
+ if ("isMask" in node) info.isMask = node.isMask;
826
+
827
+ // Min/max size
828
+ if ("minWidth" in node && node.minWidth != null) info.minWidth = node.minWidth;
829
+ if ("maxWidth" in node && node.maxWidth != null) info.maxWidth = node.maxWidth;
830
+ if ("minHeight" in node && node.minHeight != null) info.minHeight = node.minHeight;
831
+ if ("maxHeight" in node && node.maxHeight != null) info.maxHeight = node.maxHeight;
832
+
721
833
  // Auto-layout properties
722
834
  if ("layoutMode" in node && node.layoutMode !== "NONE") {
723
835
  info.autoLayout = {
@@ -731,7 +843,8 @@ function getNodeInfo({ nodeId }) {
731
843
  primaryAxisAlignItems: node.primaryAxisAlignItems,
732
844
  counterAxisAlignItems: node.counterAxisAlignItems,
733
845
  layoutSizingHorizontal: node.layoutSizingHorizontal,
734
- layoutSizingVertical: node.layoutSizingVertical
846
+ layoutSizingVertical: node.layoutSizingVertical,
847
+ layoutWrap: node.layoutWrap
735
848
  };
736
849
  }
737
850
 
@@ -783,10 +896,25 @@ function getNodeInfo({ nodeId }) {
783
896
  try {
784
897
  var bv = node.boundVariables;
785
898
  var bvResult = {};
786
- for (var key in bv) {
787
- var binding = bv[key];
899
+ var knownBVKeys = [
900
+ "fills", "strokes", "effects",
901
+ "layoutGrids", "componentProperties",
902
+ "fontSize", "fontFamily", "fontStyle", "fontWeight",
903
+ "lineHeight", "letterSpacing", "paragraphSpacing", "paragraphIndent",
904
+ "textCase", "textDecoration",
905
+ "visible", "opacity",
906
+ "topLeftRadius", "topRightRadius", "bottomLeftRadius", "bottomRightRadius",
907
+ "itemSpacing", "counterAxisSpacing",
908
+ "paddingLeft", "paddingRight", "paddingTop", "paddingBottom",
909
+ "strokeWeight", "strokeTopWeight", "strokeBottomWeight", "strokeLeftWeight", "strokeRightWeight",
910
+ "width", "height",
911
+ "minWidth", "maxWidth", "minHeight", "maxHeight",
912
+ "characters"
913
+ ];
914
+
915
+ function extractBinding(binding) {
788
916
  if (Array.isArray(binding)) {
789
- bvResult[key] = binding.map(function(b) {
917
+ return binding.map(function(b) {
790
918
  var entry = { type: b.type, id: b.id };
791
919
  try {
792
920
  var v = figma.variables.getVariableById(b.id);
@@ -795,10 +923,31 @@ function getNodeInfo({ nodeId }) {
795
923
  return entry;
796
924
  });
797
925
  } else if (binding && binding.id) {
798
- bvResult[key] = { type: binding.type, id: binding.id };
926
+ var single = { type: binding.type, id: binding.id };
799
927
  try {
800
928
  var v2 = figma.variables.getVariableById(binding.id);
801
- if (v2) { bvResult[key].name = v2.name; bvResult[key].resolvedType = v2.resolvedType; }
929
+ if (v2) { single.name = v2.name; single.resolvedType = v2.resolvedType; }
930
+ } catch (_) {}
931
+ return single;
932
+ }
933
+ return null;
934
+ }
935
+
936
+ // First try for...in (catches any enumerable keys)
937
+ for (var key in bv) {
938
+ var result = extractBinding(bv[key]);
939
+ if (result != null) bvResult[key] = result;
940
+ }
941
+ // Supplement with explicit key access (Figma proxies may not enumerate all keys)
942
+ for (var ki = 0; ki < knownBVKeys.length; ki++) {
943
+ var bvKey = knownBVKeys[ki];
944
+ if (!(bvKey in bvResult)) {
945
+ try {
946
+ var val = bv[bvKey];
947
+ if (val != null) {
948
+ var r = extractBinding(val);
949
+ if (r != null) bvResult[bvKey] = r;
950
+ }
802
951
  } catch (_) {}
803
952
  }
804
953
  }