@atomixstudio/mcp 1.0.30 → 1.0.32

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/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  isAllowedMethod,
4
4
  normalizeBridgeMethod
5
- } from "./chunk-BXWOCXYT.js";
5
+ } from "./chunk-CE6J5MJX.js";
6
6
 
7
7
  // src/index.ts
8
8
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -1289,6 +1289,159 @@ function getTokenStats(data) {
1289
1289
  }
1290
1290
 
1291
1291
  // ../figma-tools/dist/index.js
1292
+ var LEGACY_GROUP_PREFIX = {
1293
+ background: "bg",
1294
+ text: "text",
1295
+ icon: "icon",
1296
+ border: "border",
1297
+ brand: "brand",
1298
+ action: "action",
1299
+ feedback: "feedback"
1300
+ };
1301
+ var RESERVED_COLOR_KEYS = /* @__PURE__ */ new Set(["customScales", "neutral"]);
1302
+ function getColorGroupOrder(storedColors) {
1303
+ if (Array.isArray(storedColors._groupOrder) && storedColors._groupOrder.length > 0) {
1304
+ return storedColors._groupOrder;
1305
+ }
1306
+ return Object.keys(storedColors).filter(
1307
+ (k) => !k.startsWith("_") && !RESERVED_COLOR_KEYS.has(k) && typeof storedColors[k] === "object"
1308
+ );
1309
+ }
1310
+ function getGroupPrefix(storedColors, groupName) {
1311
+ const custom = storedColors._groupPrefix;
1312
+ if (custom && typeof custom[groupName] === "string") return custom[groupName];
1313
+ return LEGACY_GROUP_PREFIX[groupName] ?? groupName;
1314
+ }
1315
+ function refScaleToFigmaDisplayName(scaleFromRef) {
1316
+ const s = scaleFromRef.trim();
1317
+ if (!s) return s;
1318
+ return s.charAt(0).toUpperCase() + s.slice(1);
1319
+ }
1320
+ function referenceToFigmaPrimitiveName(ref, primitiveNames) {
1321
+ if (!ref || typeof ref !== "string") return null;
1322
+ const r = ref.trim();
1323
+ const scaleStep = /^([a-zA-Z]+)\.(\d+|[a-z]+)$/.exec(r);
1324
+ if (scaleStep) {
1325
+ const [, scale, step] = scaleStep;
1326
+ const scaleDisplay = refScaleToFigmaDisplayName(scale);
1327
+ const name = `${scaleDisplay} / ${step}`;
1328
+ return primitiveNames.has(name) ? name : null;
1329
+ }
1330
+ const radixMatch = /^radix\.([a-zA-Z]+)\.(\d+)$/.exec(r);
1331
+ if (radixMatch) {
1332
+ const [, family, step] = radixMatch;
1333
+ const scaleName = refScaleToFigmaDisplayName(family);
1334
+ const name = `${scaleName} / ${step}`;
1335
+ return primitiveNames.has(name) ? name : null;
1336
+ }
1337
+ const oneOffMatch = /^(?:new|oneOff)\.(.+)$/.exec(r);
1338
+ if (oneOffMatch) {
1339
+ const name = `One-off / ${oneOffMatch[1]}`;
1340
+ return primitiveNames.has(name) ? name : null;
1341
+ }
1342
+ const whiteAlpha = /^whiteAlpha\.(.+)$/.exec(r);
1343
+ if (whiteAlpha) {
1344
+ const name = `WhiteAlpha / ${whiteAlpha[1]}`;
1345
+ return primitiveNames.has(name) ? name : null;
1346
+ }
1347
+ const blackAlpha = /^blackAlpha\.(.+)$/.exec(r);
1348
+ if (blackAlpha) {
1349
+ const name = `BlackAlpha / ${blackAlpha[1]}`;
1350
+ return primitiveNames.has(name) ? name : null;
1351
+ }
1352
+ return null;
1353
+ }
1354
+ function referenceToScaleName(ref) {
1355
+ if (!ref || typeof ref !== "string") return null;
1356
+ const r = ref.trim();
1357
+ const scaleStep = /^([a-zA-Z]+)\.(\d+|[a-z]+)$/.exec(r);
1358
+ if (scaleStep) return refScaleToFigmaDisplayName(scaleStep[1]);
1359
+ const radixMatch = /^radix\.([a-zA-Z]+)\.(\d+)$/.exec(r);
1360
+ if (radixMatch) return refScaleToFigmaDisplayName(radixMatch[1]);
1361
+ return null;
1362
+ }
1363
+ function getReferencedScaleNames(storedColors) {
1364
+ const names = /* @__PURE__ */ new Set();
1365
+ const groupOrder = getColorGroupOrder(storedColors);
1366
+ for (const groupName of groupOrder) {
1367
+ const group = storedColors[groupName];
1368
+ if (!group || typeof group !== "object") continue;
1369
+ const keys = Object.keys(group).filter((k) => !k.startsWith("_") && k !== "governance");
1370
+ for (const key of keys) {
1371
+ const value = group[key];
1372
+ if (value === void 0 || typeof value !== "object" || value === null) continue;
1373
+ const theme = value;
1374
+ const lightScale = theme.light?.reference ? referenceToScaleName(theme.light.reference) : null;
1375
+ const darkScale = theme.dark?.reference ? referenceToScaleName(theme.dark.reference) : null;
1376
+ if (lightScale) names.add(lightScale);
1377
+ if (darkScale) names.add(darkScale);
1378
+ }
1379
+ }
1380
+ return names;
1381
+ }
1382
+ function getFullScaleFromStored(storedColors, scaleName) {
1383
+ const key = scaleName.toLowerCase();
1384
+ if (key === "neutral" || key === "gray") {
1385
+ const neutral = storedColors.neutral;
1386
+ if (!neutral?.steps || typeof neutral.steps !== "object") return null;
1387
+ const out = {};
1388
+ for (const [step, stepValue] of Object.entries(neutral.steps)) {
1389
+ const hex = stepValue?.hex;
1390
+ if (typeof hex === "string" && /^#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/i.test(hex)) {
1391
+ out[step] = hex.startsWith("#") ? hex : `#${hex}`;
1392
+ }
1393
+ }
1394
+ return Object.keys(out).length > 0 ? out : null;
1395
+ }
1396
+ const customScales = storedColors.customScales;
1397
+ if (Array.isArray(customScales)) {
1398
+ const keyNorm = key.replace(/\s+/g, "");
1399
+ const scale = customScales.find(
1400
+ (s) => s.name?.toLowerCase() === key || s.name?.toLowerCase().replace(/\s+/g, "") === keyNorm
1401
+ );
1402
+ if (scale?.steps && typeof scale.steps === "object") {
1403
+ const out = {};
1404
+ for (const [step, stepValue] of Object.entries(scale.steps)) {
1405
+ const hex = stepValue?.hex;
1406
+ if (typeof hex === "string" && /^#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/i.test(hex)) {
1407
+ out[step] = hex.startsWith("#") ? hex : `#${hex}`;
1408
+ }
1409
+ }
1410
+ if (Object.keys(out).length > 0) return out;
1411
+ }
1412
+ }
1413
+ return null;
1414
+ }
1415
+ function buildSemanticRefMap(storedColors, primitiveNames) {
1416
+ const out = {};
1417
+ const groupOrder = getColorGroupOrder(storedColors);
1418
+ for (const groupName of groupOrder) {
1419
+ const group = storedColors[groupName];
1420
+ if (!group || typeof group !== "object") continue;
1421
+ const prefix = getGroupPrefix(storedColors, groupName);
1422
+ const rowOrder = Array.isArray(group._rowOrder) ? group._rowOrder : void 0;
1423
+ const keys = Array.isArray(rowOrder) ? rowOrder : Object.keys(group).filter((k) => !k.startsWith("_") && k !== "governance");
1424
+ const toKebab = prefix === "action" || prefix === "brand" || prefix === "feedback";
1425
+ for (const key of keys) {
1426
+ const value = group[key];
1427
+ if (value === void 0 || typeof value !== "object" || value === null) continue;
1428
+ const theme = value;
1429
+ let cssKey = key === "app" ? "page" : key;
1430
+ if (toKebab) {
1431
+ cssKey = String(key).replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
1432
+ }
1433
+ const fullKey = `${prefix}-${cssKey}`;
1434
+ const lightRef = theme.light?.reference ? referenceToFigmaPrimitiveName(theme.light.reference, primitiveNames) : null;
1435
+ const darkRef = theme.dark?.reference ? referenceToFigmaPrimitiveName(theme.dark.reference, primitiveNames) : null;
1436
+ if (lightRef || darkRef) {
1437
+ out[fullKey] = {};
1438
+ if (lightRef) out[fullKey].Light = lightRef;
1439
+ if (darkRef) out[fullKey].Dark = darkRef;
1440
+ }
1441
+ }
1442
+ }
1443
+ return out;
1444
+ }
1292
1445
  function figmaColorNameWithGroup(key) {
1293
1446
  if (key.includes("/")) {
1294
1447
  const [group2, ...rest] = key.split("/");
@@ -1415,17 +1568,114 @@ function buildFigmaPayloadsFromDS(data) {
1415
1568
  const tokens = data.tokens;
1416
1569
  const colors = tokens?.colors;
1417
1570
  const typography = tokens?.typography;
1418
- const modes = [];
1419
- const variables = [];
1420
- const paintStyles = [];
1421
1571
  const hexRe = /^#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/;
1422
- const addedNames = /* @__PURE__ */ new Set();
1572
+ const modes = [];
1423
1573
  if (colors?.modes) {
1424
1574
  const light = colors.modes.light ?? {};
1425
1575
  const dark = colors.modes.dark ?? {};
1426
1576
  if (Object.keys(light).length > 0) modes.push("Light");
1427
1577
  if (Object.keys(dark).length > 0 && !modes.includes("Dark")) modes.push("Dark");
1428
- if (modes.length === 0) modes.push("Light");
1578
+ }
1579
+ if (modes.length === 0) modes.push("Light");
1580
+ const primitiveModes = ["Value"];
1581
+ const dsName = data.meta?.name;
1582
+ const collectionPrefix = dsName ? `${dsName} ` : "";
1583
+ const referencedScaleNames = data.storedColors ? getReferencedScaleNames(data.storedColors) : /* @__PURE__ */ new Set();
1584
+ const primitiveVariables = [];
1585
+ const primitiveNames = /* @__PURE__ */ new Set();
1586
+ const oneOffHexToFigmaName = /* @__PURE__ */ new Map();
1587
+ const alphaScales = colors?.alphaScales;
1588
+ if (alphaScales?.whiteAlpha && typeof alphaScales.whiteAlpha === "object") {
1589
+ for (const [step, value] of Object.entries(alphaScales.whiteAlpha)) {
1590
+ if (typeof value !== "string") continue;
1591
+ const hex = colorTo8DigitHex(value);
1592
+ if (!hex || !hexRe.test(hex)) continue;
1593
+ const figmaName = `WhiteAlpha / ${step}`;
1594
+ if (primitiveNames.has(figmaName)) continue;
1595
+ primitiveNames.add(figmaName);
1596
+ const values = {};
1597
+ for (const m of primitiveModes) values[m] = hex;
1598
+ primitiveVariables.push({ name: figmaName, values });
1599
+ }
1600
+ }
1601
+ if (alphaScales?.blackAlpha && typeof alphaScales.blackAlpha === "object") {
1602
+ for (const [step, value] of Object.entries(alphaScales.blackAlpha)) {
1603
+ if (typeof value !== "string") continue;
1604
+ const hex = colorTo8DigitHex(value);
1605
+ if (!hex || !hexRe.test(hex)) continue;
1606
+ const figmaName = `BlackAlpha / ${step}`;
1607
+ if (primitiveNames.has(figmaName)) continue;
1608
+ primitiveNames.add(figmaName);
1609
+ const values = {};
1610
+ for (const m of primitiveModes) values[m] = hex;
1611
+ primitiveVariables.push({ name: figmaName, values });
1612
+ }
1613
+ }
1614
+ if (colors?.scales && typeof colors.scales === "object") {
1615
+ for (const [scaleName, steps] of Object.entries(colors.scales)) {
1616
+ if (!steps || typeof steps !== "object") continue;
1617
+ let groupDisplay = scaleName.charAt(0).toUpperCase() + scaleName.slice(1);
1618
+ if (scaleName.toLowerCase() === "neutral" && data.storedColors) {
1619
+ const neutral = data.storedColors.neutral;
1620
+ if (neutral?.baseHueFamily) {
1621
+ groupDisplay = refScaleToFigmaDisplayName(neutral.baseHueFamily);
1622
+ }
1623
+ }
1624
+ for (const [step, hexVal] of Object.entries(steps)) {
1625
+ if (typeof hexVal !== "string") continue;
1626
+ const hex = colorTo8DigitHex(hexVal) ?? (hexRe.test(hexVal) ? hexVal : null);
1627
+ if (!hex || !hexRe.test(hex)) continue;
1628
+ const figmaName = `${groupDisplay} / ${step}`;
1629
+ if (primitiveNames.has(figmaName)) continue;
1630
+ primitiveNames.add(figmaName);
1631
+ const values = {};
1632
+ for (const m of primitiveModes) values[m] = hex;
1633
+ primitiveVariables.push({ name: figmaName, values });
1634
+ }
1635
+ }
1636
+ }
1637
+ const radixVariables = [];
1638
+ const radixCollectionName = `${collectionPrefix}Colors Radix`;
1639
+ for (const scaleName of referencedScaleNames) {
1640
+ if (primitiveNames.has(`${scaleName} / 50`) || primitiveNames.has(`${scaleName} / 100`)) continue;
1641
+ const fromStored = data.storedColors ? getFullScaleFromStored(data.storedColors, scaleName) : null;
1642
+ const scaleData = fromStored ?? data.getRadixScale?.(scaleName) ?? null;
1643
+ if (!scaleData) continue;
1644
+ for (const [step, hexVal] of Object.entries(scaleData)) {
1645
+ const figmaName = `${scaleName} / ${step}`;
1646
+ if (primitiveNames.has(figmaName)) continue;
1647
+ const hex = colorTo8DigitHex(hexVal) ?? (hexRe.test(hexVal) ? hexVal : null);
1648
+ if (!hex || !hexRe.test(hex)) continue;
1649
+ primitiveNames.add(figmaName);
1650
+ const values = {};
1651
+ for (const m of primitiveModes) values[m] = hex;
1652
+ primitiveVariables.push({ name: figmaName, values });
1653
+ }
1654
+ }
1655
+ if (colors?.oneOffs && Array.isArray(colors.oneOffs)) {
1656
+ for (const oneOff of colors.oneOffs) {
1657
+ if (!oneOff || typeof oneOff !== "object" || typeof oneOff.hex !== "string") continue;
1658
+ const hex = colorTo8DigitHex(oneOff.hex) ?? (hexRe.test(oneOff.hex) ? oneOff.hex : null);
1659
+ if (!hex || !hexRe.test(hex)) continue;
1660
+ const name = typeof oneOff.name === "string" && oneOff.name ? oneOff.name : oneOff.id ?? "unnamed";
1661
+ const figmaName = `One-off / ${name}`;
1662
+ if (primitiveNames.has(figmaName)) continue;
1663
+ primitiveNames.add(figmaName);
1664
+ const values = {};
1665
+ for (const m of primitiveModes) values[m] = hex;
1666
+ primitiveVariables.push({ name: figmaName, values });
1667
+ const normalizedHex = hex.replace(/^#/, "").toUpperCase();
1668
+ const key8 = normalizedHex.length === 6 ? normalizedHex + "FF" : normalizedHex;
1669
+ oneOffHexToFigmaName.set(key8, figmaName);
1670
+ }
1671
+ }
1672
+ const semanticRefMap = data.storedColors && primitiveNames.size > 0 ? buildSemanticRefMap(data.storedColors, primitiveNames) : {};
1673
+ const semanticVariables = [];
1674
+ const semanticNames = /* @__PURE__ */ new Set();
1675
+ const primitivesCollectionName = `${collectionPrefix}Colors Primitives`;
1676
+ if (colors?.modes) {
1677
+ const light = colors.modes.light ?? {};
1678
+ const dark = colors.modes.dark ?? {};
1429
1679
  const orderedKeys = [...Object.keys(light)];
1430
1680
  for (const k of Object.keys(dark)) {
1431
1681
  if (!orderedKeys.includes(k)) orderedKeys.push(k);
@@ -1436,14 +1686,34 @@ function buildFigmaPayloadsFromDS(data) {
1436
1686
  const lightHex = typeof lightVal === "string" ? colorTo8DigitHex(lightVal) ?? (hexRe.test(lightVal) ? lightVal : null) : null;
1437
1687
  if (lightHex && hexRe.test(lightHex)) {
1438
1688
  const figmaName = figmaColorNameWithGroup(key);
1439
- if (addedNames.has(figmaName)) continue;
1440
- addedNames.add(figmaName);
1689
+ if (semanticNames.has(figmaName)) continue;
1690
+ semanticNames.add(figmaName);
1441
1691
  const darkHex = typeof darkVal === "string" ? colorTo8DigitHex(darkVal) ?? (hexRe.test(darkVal) ? darkVal : null) : null;
1442
- const values = {};
1443
- if (modes.includes("Light")) values.Light = lightHex;
1444
- if (modes.includes("Dark")) values.Dark = darkHex && hexRe.test(darkHex) ? darkHex : lightHex;
1445
- variables.push({ name: figmaName, values });
1446
- paintStyles.push({ name: figmaName, color: lightHex });
1692
+ const refs = semanticRefMap[key];
1693
+ const values = {
1694
+ ...modes.includes("Light") && { Light: lightHex },
1695
+ ...modes.includes("Dark") && { Dark: darkHex && hexRe.test(darkHex) ? darkHex : lightHex }
1696
+ };
1697
+ const aliasByMode = {};
1698
+ for (const m of modes) {
1699
+ const aliasFromRef = m === "Light" ? refs?.Light : refs?.Dark;
1700
+ if (aliasFromRef && primitiveNames.has(aliasFromRef)) {
1701
+ aliasByMode[m] = aliasFromRef;
1702
+ continue;
1703
+ }
1704
+ const hexForMode = m === "Light" ? lightHex : darkHex && hexRe.test(darkHex) ? darkHex : lightHex;
1705
+ const norm = hexForMode.replace(/^#/, "").toUpperCase();
1706
+ const key8 = norm.length === 6 ? norm + "FF" : norm;
1707
+ const oneOffAlias = oneOffHexToFigmaName.get(key8);
1708
+ if (oneOffAlias) {
1709
+ aliasByMode[m] = oneOffAlias;
1710
+ }
1711
+ }
1712
+ semanticVariables.push({
1713
+ name: figmaName,
1714
+ values,
1715
+ ...Object.keys(aliasByMode).length > 0 ? { aliasByMode } : {}
1716
+ });
1447
1717
  }
1448
1718
  }
1449
1719
  }
@@ -1451,50 +1721,43 @@ function buildFigmaPayloadsFromDS(data) {
1451
1721
  for (const [key, hex] of Object.entries(colors.static.brand)) {
1452
1722
  if (typeof hex !== "string" || !hexRe.test(hex)) continue;
1453
1723
  const figmaName = figmaColorNameWithGroup(`brand/${key}`);
1454
- if (addedNames.has(figmaName)) continue;
1455
- addedNames.add(figmaName);
1456
- paintStyles.push({ name: figmaName, color: hex });
1457
- if (modes.length === 0) modes.push("Light");
1724
+ if (semanticNames.has(figmaName)) continue;
1725
+ semanticNames.add(figmaName);
1458
1726
  const values = {};
1459
1727
  for (const m of modes) values[m] = hex;
1460
- variables.push({ name: figmaName, values });
1728
+ semanticVariables.push({ name: figmaName, values });
1461
1729
  }
1462
1730
  }
1463
- const alphaScales = colors?.alphaScales;
1464
- if (alphaScales?.whiteAlpha && typeof alphaScales.whiteAlpha === "object") {
1465
- if (modes.length === 0) modes.push("Light");
1466
- for (const [step, value] of Object.entries(alphaScales.whiteAlpha)) {
1467
- if (typeof value !== "string") continue;
1468
- const hex = colorTo8DigitHex(value);
1469
- if (!hex || !hexRe.test(hex)) continue;
1470
- const figmaName = `WhiteAlpha / ${step}`;
1471
- if (addedNames.has(figmaName)) continue;
1472
- addedNames.add(figmaName);
1473
- const values = {};
1474
- for (const m of modes) values[m] = hex;
1475
- variables.push({ name: figmaName, values });
1476
- paintStyles.push({ name: figmaName, color: hex });
1477
- }
1731
+ const colorVariableCollections = [];
1732
+ if (primitiveVariables.length > 0) {
1733
+ colorVariableCollections.push({
1734
+ collectionName: primitivesCollectionName,
1735
+ modes: primitiveModes,
1736
+ variables: primitiveVariables,
1737
+ applyScopes: false
1738
+ });
1478
1739
  }
1479
- if (alphaScales?.blackAlpha && typeof alphaScales.blackAlpha === "object") {
1480
- if (modes.length === 0) modes.push("Light");
1481
- for (const [step, value] of Object.entries(alphaScales.blackAlpha)) {
1482
- if (typeof value !== "string") continue;
1483
- const hex = colorTo8DigitHex(value);
1484
- if (!hex || !hexRe.test(hex)) continue;
1485
- const figmaName = `BlackAlpha / ${step}`;
1486
- if (addedNames.has(figmaName)) continue;
1487
- addedNames.add(figmaName);
1488
- const values = {};
1489
- for (const m of modes) values[m] = hex;
1490
- variables.push({ name: figmaName, values });
1491
- paintStyles.push({ name: figmaName, color: hex });
1492
- }
1740
+ if (radixVariables.length > 0) {
1741
+ colorVariableCollections.push({
1742
+ collectionName: radixCollectionName,
1743
+ modes,
1744
+ variables: radixVariables,
1745
+ applyScopes: false
1746
+ });
1493
1747
  }
1494
- if (variables.length === 0 && modes.length === 0) modes.push("Light");
1495
- const dsName = data.meta?.name;
1496
- const collectionPrefix = dsName ? `${dsName} ` : "";
1497
- const colorCollectionName = `${collectionPrefix}Colors`;
1748
+ if (semanticVariables.length > 0) {
1749
+ const primitiveCollections = [];
1750
+ if (primitiveVariables.length > 0) primitiveCollections.push(primitivesCollectionName);
1751
+ if (radixVariables.length > 0) primitiveCollections.push(radixCollectionName);
1752
+ colorVariableCollections.push({
1753
+ collectionName: `${collectionPrefix}Colors Semantic`,
1754
+ modes,
1755
+ variables: semanticVariables,
1756
+ applyScopes: true,
1757
+ ...primitiveCollections.length > 1 ? { primitiveCollectionNames: primitiveCollections } : primitiveCollections.length === 1 ? { primitiveCollectionName: primitiveCollections[0] } : {}
1758
+ });
1759
+ }
1760
+ const paintStyles = [];
1498
1761
  const textStyles = [];
1499
1762
  const sizeToPx = (val, basePx = 16) => {
1500
1763
  if (typeof val === "number") return Math.round(val);
@@ -1720,7 +1983,7 @@ function buildFigmaPayloadsFromDS(data) {
1720
1983
  return nameA.localeCompare(nameB);
1721
1984
  });
1722
1985
  return {
1723
- colorVariables: { collectionName: colorCollectionName, modes, variables },
1986
+ colorVariableCollections,
1724
1987
  paintStyles,
1725
1988
  textStyles,
1726
1989
  numberVariableCollections,
@@ -1930,7 +2193,7 @@ function parseArgs() {
1930
2193
  }
1931
2194
  var cliArgs = parseArgs();
1932
2195
  var { dsId, apiKey, accessToken } = cliArgs;
1933
- var apiBase = cliArgs.apiBase || "https://atomixstudio.eu";
2196
+ var apiBase = cliArgs.apiBase || "https://atomix.studio";
1934
2197
  var cachedData = null;
1935
2198
  var cachedETag = null;
1936
2199
  var cachedMcpTier = null;
@@ -1994,6 +2257,10 @@ function formatValidationBlock(entries) {
1994
2257
  async function fetchDesignSystemForMCP(forceRefresh = false) {
1995
2258
  if (!dsId) throw new Error("Missing --ds-id. Usage: npx @atomixstudio/mcp --ds-id <id> --atomix-token <token>");
1996
2259
  if (!accessToken) throw new Error("Missing --atomix-token. Get your token from the Export modal or Settings.");
2260
+ if (forceRefresh) {
2261
+ cachedData = null;
2262
+ cachedETag = null;
2263
+ }
1997
2264
  const result = await fetchDesignSystem({
1998
2265
  dsId,
1999
2266
  accessToken,
@@ -2056,7 +2323,7 @@ function buildTypesetsList(typography, cssPrefix = "atmx") {
2056
2323
  var server = new Server(
2057
2324
  {
2058
2325
  name: "atomix-mcp-user",
2059
- version: "1.0.29"
2326
+ version: "1.0.30"
2060
2327
  },
2061
2328
  {
2062
2329
  capabilities: {
@@ -2355,8 +2622,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2355
2622
  const totalChanges = lightChanges + darkChanges + deprecatedCount;
2356
2623
  if (totalChanges === 0 && !dryRun) {
2357
2624
  const lastUpdated = designSystemData.meta.exportedAt ? new Date(designSystemData.meta.exportedAt).toLocaleString() : "N/A";
2625
+ lastSyncAffectedTokens = { modified: [], removed: [], added: [], format, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
2358
2626
  return {
2359
- responseText: `\u2713 Already up to date!
2627
+ responseText: `\u2713 Already up to date (checked DB v${designSystemData.meta.version}, exported ${lastUpdated}).
2360
2628
 
2361
2629
  File: ${output}
2362
2630
  Tokens: ${tokenCount}
@@ -2367,8 +2635,9 @@ Last updated: ${lastUpdated}`,
2367
2635
  };
2368
2636
  }
2369
2637
  if (totalChanges === 0 && dryRun) {
2638
+ lastSyncAffectedTokens = { modified: [], removed: [], added: [], format, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
2370
2639
  return {
2371
- responseText: `[DRY RUN] Already up to date. No changes would be written.
2640
+ responseText: `[DRY RUN] Already up to date (checked DB v${designSystemData.meta.version}). No changes would be written.
2372
2641
 
2373
2642
  File: ${output}
2374
2643
  Tokens: ${tokenCount}
@@ -3009,9 +3278,7 @@ ${tokenResponseText}${validationBlock}` : `${summary}${validationBlock}`);
3009
3278
  out.agentInstruction = agentStartBridge;
3010
3279
  out.userInstruction = `If the bridge still does not connect: ${userSteps}`;
3011
3280
  out.figmaPayload = {
3012
- collectionName: payloads.colorVariables.collectionName,
3013
- modes: payloads.colorVariables.modes,
3014
- variables: payloads.colorVariables.variables,
3281
+ colorVariableCollections: payloads.colorVariableCollections,
3015
3282
  paintStyles: payloads.paintStyles,
3016
3283
  textStyles: payloads.textStyles,
3017
3284
  numberVariableCollections: payloads.numberVariableCollections,
@@ -3021,13 +3288,21 @@ ${tokenResponseText}${validationBlock}` : `${summary}${validationBlock}`);
3021
3288
  content: [{ type: "text", text: JSON.stringify(out, null, 2) }]
3022
3289
  };
3023
3290
  }
3024
- if (payloads.colorVariables.variables.length > 0) {
3025
- out.colorVariables = await sendBridgeRequest("create_color_variables", {
3026
- collectionName: payloads.colorVariables.collectionName,
3027
- modes: payloads.colorVariables.modes,
3028
- variables: payloads.colorVariables.variables,
3029
- removeVariablesNotInPayload: true
3030
- });
3291
+ const colorResults = [];
3292
+ for (const coll of payloads.colorVariableCollections) {
3293
+ if (coll.variables.length > 0) {
3294
+ const result = await sendBridgeRequest("create_color_variables", {
3295
+ collectionName: coll.collectionName,
3296
+ modes: coll.modes,
3297
+ variables: coll.variables,
3298
+ removeVariablesNotInPayload: true,
3299
+ applyScopes: coll.applyScopes
3300
+ });
3301
+ colorResults.push({ collectionName: coll.collectionName, result });
3302
+ }
3303
+ }
3304
+ if (colorResults.length > 0) {
3305
+ out.colorVariables = colorResults.length === 1 ? colorResults[0].result : colorResults;
3031
3306
  }
3032
3307
  if (payloads.paintStyles.length > 0) {
3033
3308
  out.paintStyles = await sendBridgeRequest("create_paint_styles", {
@@ -3069,9 +3344,7 @@ ${tokenResponseText}${validationBlock}` : `${summary}${validationBlock}`);
3069
3344
  });
3070
3345
  }
3071
3346
  out.figmaPayload = {
3072
- collectionName: payloads.colorVariables.collectionName,
3073
- modes: payloads.colorVariables.modes,
3074
- variables: payloads.colorVariables.variables,
3347
+ colorVariableCollections: payloads.colorVariableCollections,
3075
3348
  paintStyles: payloads.paintStyles,
3076
3349
  textStyles: payloads.textStyles,
3077
3350
  numberVariableCollections: payloads.numberVariableCollections,
@@ -3080,9 +3353,7 @@ ${tokenResponseText}${validationBlock}` : `${summary}${validationBlock}`);
3080
3353
  } catch (e) {
3081
3354
  out.error = e instanceof Error ? e.message : String(e);
3082
3355
  out.figmaPayload = {
3083
- collectionName: payloads.colorVariables.collectionName,
3084
- modes: payloads.colorVariables.modes,
3085
- variables: payloads.colorVariables.variables,
3356
+ colorVariableCollections: payloads.colorVariableCollections,
3086
3357
  paintStyles: payloads.paintStyles,
3087
3358
  textStyles: payloads.textStyles,
3088
3359
  numberVariableCollections: payloads.numberVariableCollections,
@@ -3108,11 +3379,21 @@ ${tokenResponseText}${validationBlock}` : `${summary}${validationBlock}`);
3108
3379
  }
3109
3380
  const summaryParts = [];
3110
3381
  const colorResult = out.colorVariables;
3111
- if (colorResult && (colorResult.variableNames?.length || (colorResult.removed ?? 0) > 0)) {
3112
- const parts = [];
3113
- if (colorResult.variableNames?.length) parts.push(`${colorResult.variableNames.length} synced`);
3114
- if ((colorResult.removed ?? 0) > 0) parts.push(`${colorResult.removed} removed`);
3115
- summaryParts.push(`Colors: ${parts.join(", ")}.`);
3382
+ if (colorResult) {
3383
+ const results = Array.isArray(colorResult) ? colorResult : [{ result: colorResult }];
3384
+ let totalSynced = 0;
3385
+ let totalRemoved = 0;
3386
+ for (const r of results) {
3387
+ const res = r.result;
3388
+ if (res?.variableNames?.length) totalSynced += res.variableNames.length;
3389
+ if ((res?.removed ?? 0) > 0) totalRemoved += res.removed ?? 0;
3390
+ }
3391
+ if (totalSynced > 0 || totalRemoved > 0) {
3392
+ const parts = [];
3393
+ if (totalSynced > 0) parts.push(`${totalSynced} synced`);
3394
+ if (totalRemoved > 0) parts.push(`${totalRemoved} removed`);
3395
+ summaryParts.push(`Colors: ${parts.join(", ")}.`);
3396
+ }
3116
3397
  }
3117
3398
  const paintResult = out.paintStyles;
3118
3399
  if (paintResult) {
@@ -3340,7 +3621,7 @@ var SHOWCASE_HTML_TEMPLATE = `<!DOCTYPE html>
3340
3621
  <li>Run <code>/--rules</code> to load governance rules; run <code>/--sync</code> and <code>/--refactor</code> after you change tokens in Atomix Studio</li>
3341
3622
  </ul>
3342
3623
  </div>
3343
- <p class="tips">Keep the source of truth at <a href="https://atomixstudio.eu" target="_blank" rel="noopener">atomixstudio.eu</a> \u2014 avoid editing token values in this repo.</p>
3624
+ <p class="tips">Keep the source of truth at <a href="https://atomix.studio" target="_blank" rel="noopener">atomix.studio</a> \u2014 avoid editing token values in this repo.</p>
3344
3625
  </div>
3345
3626
  </body>
3346
3627
  </html>
@@ -3464,7 +3745,7 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
3464
3745
  };
3465
3746
  }
3466
3747
  const canonicalName = name === "--hello" ? "hello" : name === "--get-started" ? "atomix-setup" : name === "--rules" ? "design-system-rules" : name === "--sync" ? "sync" : name === "--refactor" ? "refactor" : name === "--sync-to-figma" || name === "syncToFigma" ? "sync-to-figma" : name;
3467
- const shouldForceRefresh = canonicalName === "sync";
3748
+ const shouldForceRefresh = canonicalName === "sync" || canonicalName === "refactor";
3468
3749
  let data = null;
3469
3750
  let stats = null;
3470
3751
  try {
@@ -3481,7 +3762,7 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
3481
3762
  content: {
3482
3763
  type: "text",
3483
3764
  text: `The MCP server isn't configured. To use design system prompts, you need to configure the server with:
3484
- - \`--ds-id\`: Your design system ID (get it from https://atomixstudio.eu/ds/[your-ds-id])
3765
+ - \`--ds-id\`: Your design system ID (get it from https://atomix.studio/ds/[your-ds-id])
3485
3766
  - \`--atomix-token\`: Your access token (get it from the Export modal or Settings \u2192 Regenerate Atomix access token)
3486
3767
 
3487
3768
  Both are required. Configure the MCP server in your AI tool's MCP settings, then restart your AI tool.`
@@ -3795,42 +4076,59 @@ Format the response as a markdown table with columns: Token Name | Value | CSS V
3795
4076
  };
3796
4077
  }
3797
4078
  case "refactor": {
3798
- if (!lastSyncAffectedTokens) {
4079
+ const refactorOutput = args?.output || "./tokens.css";
4080
+ const refactorFormat = args?.format || "css";
4081
+ const refactorOutputPath = path2.resolve(process.cwd(), refactorOutput);
4082
+ const refactorFileExists = fs2.existsSync(refactorOutputPath);
4083
+ if (!data) {
3799
4084
  return {
3800
4085
  description: "Refactor codebase for deprecated tokens",
3801
- messages: [
3802
- {
3803
- role: "user",
3804
- content: {
3805
- type: "text",
3806
- text: `No recent sync data available. Please run \`/sync\` first to update your token file, then run \`/refactor\` to scan your codebase for deprecated token usage.`
3807
- }
3808
- }
3809
- ]
4086
+ messages: [{
4087
+ role: "user",
4088
+ content: { type: "text", text: `Failed to fetch design system from DB. Check your --ds-id and --atomix-token configuration.` }
4089
+ }]
3810
4090
  };
3811
4091
  }
3812
- if (lastSyncAffectedTokens.removed.length === 0) {
4092
+ if (!refactorFileExists) {
3813
4093
  return {
3814
4094
  description: "Refactor codebase for deprecated tokens",
3815
- messages: [
3816
- {
3817
- role: "user",
3818
- content: {
3819
- type: "text",
3820
- text: `\u2713 No deprecated tokens found in last sync.
3821
-
3822
- Your codebase is up to date! The last sync on ${new Date(lastSyncAffectedTokens.timestamp).toLocaleString()} did not remove any tokens.`
3823
- }
3824
- }
3825
- ]
4095
+ messages: [{
4096
+ role: "user",
4097
+ content: { type: "text", text: `No token file found at \`${refactorOutput}\`. Please run \`/--sync\` first to create your token file, then run \`/--refactor\` to scan your codebase for deprecated token usage.` }
4098
+ }]
3826
4099
  };
3827
4100
  }
3828
- const format = lastSyncAffectedTokens.format;
4101
+ const deprecatedTokens = /* @__PURE__ */ new Map();
4102
+ if (["css", "scss", "less"].includes(refactorFormat)) {
4103
+ const oldContent = fs2.readFileSync(refactorOutputPath, "utf-8");
4104
+ const oldVarPattern = /(?:^|\n)\s*(?:\/\*[^*]*\*+(?:[^/*][^*]*\*+)*\/\s*)?(--[a-zA-Z0-9-]+):\s*([^;]+);/gm;
4105
+ let match;
4106
+ while ((match = oldVarPattern.exec(oldContent)) !== null) {
4107
+ const varName = match[1];
4108
+ const varValue = match[2].trim();
4109
+ if (!(varName in data.cssVariables)) deprecatedTokens.set(varName, varValue);
4110
+ }
4111
+ }
4112
+ const dsVersion = data.meta.version ?? "?";
4113
+ const dsExportedAt = data.meta.exportedAt ? new Date(data.meta.exportedAt).toLocaleString() : "N/A";
4114
+ if (deprecatedTokens.size === 0) {
4115
+ return {
4116
+ description: "Refactor codebase for deprecated tokens",
4117
+ messages: [{
4118
+ role: "user",
4119
+ content: { type: "text", text: `\u2713 No deprecated tokens found.
4120
+
4121
+ Your token file \`${refactorOutput}\` is aligned with the design system (v${dsVersion}, exported ${dsExportedAt}). No tokens need migration.` }
4122
+ }]
4123
+ };
4124
+ }
4125
+ const format = refactorFormat;
3829
4126
  const isNativeFormat = ["swift", "kotlin", "dart"].includes(format);
4127
+ const removed = Array.from(deprecatedTokens.entries()).map(([token, lastValue]) => ({ token, lastValue }));
3830
4128
  let searchPatterns = "";
3831
4129
  let fileExtensions = "";
3832
4130
  if (isNativeFormat) {
3833
- const nativeTokens = lastSyncAffectedTokens.removed.map((r) => {
4131
+ const nativeTokens = removed.map((r) => {
3834
4132
  const withoutPrefix = r.token.replace(/^--atmx-/, "");
3835
4133
  const camelCase = withoutPrefix.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
3836
4134
  const pascalCase = camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
@@ -3848,15 +4146,20 @@ Your codebase is up to date! The last sync on ${new Date(lastSyncAffectedTokens.
3848
4146
  }
3849
4147
  } else {
3850
4148
  fileExtensions = ".tsx, .ts, .jsx, .js, .css, .scss, .less, .vue, .svelte files";
3851
- searchPatterns = lastSyncAffectedTokens.removed.map(
4149
+ searchPatterns = removed.map(
3852
4150
  (r) => ` \u2022 \`var(${r.token})\` or \`"${r.token}"\` (was: ${r.lastValue})`
3853
4151
  ).join("\n");
3854
4152
  }
3855
4153
  const instructions = `Scan the codebase for deprecated design tokens and help update them.
3856
4154
 
3857
- ## Deprecated Tokens (${lastSyncAffectedTokens.removed.length})
4155
+ ## Design System (fresh from DB)
4156
+ - **Version:** ${dsVersion}
4157
+ - **Last exported:** ${dsExportedAt}
4158
+ - **Token file:** ${refactorOutput}
4159
+
4160
+ ## Deprecated Tokens (${removed.length})
3858
4161
 
3859
- The following tokens have been removed from the design system:
4162
+ The following tokens exist in your local token file but have been removed from the design system:
3860
4163
 
3861
4164
  ${searchPatterns}
3862
4165
 
@@ -4025,7 +4328,7 @@ ${tokenSummary}
4025
4328
 
4026
4329
  ---
4027
4330
 
4028
- *Atomix Studio \u2014 https://atomixstudio.eu | DS: https://atomixstudio.eu/ds/${dsId}*
4331
+ *Atomix Studio \u2014 https://atomix.studio | DS: https://atomix.studio/ds/${dsId}*
4029
4332
  `;
4030
4333
  }
4031
4334
  async function startServer() {