Skip to content

Commit 0e25485

Browse files
authored
add get_style_macro_property_values to s2 mcp server (#9336)
1 parent 07c8177 commit 0e25485

File tree

3 files changed

+182
-1
lines changed

3 files changed

+182
-1
lines changed

packages/dev/mcp/s2/scripts/build-data.mjs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
#!/usr/bin/env node
2+
import {createRequire} from 'module';
23
import fg from 'fast-glob';
34
import {fileURLToPath, pathToFileURL} from 'url';
45
import fs from 'fs';
56
import path from 'path';
7+
// eslint-disable-next-line rulesdir/imports
8+
import * as ts from 'typescript';
69

710
const __filename = fileURLToPath(import.meta.url);
811
const __dirname = path.dirname(__filename);
@@ -13,6 +16,7 @@ const ICONS_DIR = path.resolve(REPO_ROOT, 'packages/@react-spectrum/s2/s2wf-icon
1316
const ILLUSTRATIONS_DIR = path.resolve(REPO_ROOT, 'packages/@react-spectrum/s2/spectrum-illustrations/linear');
1417
const ICON_ALIASES_JS = path.resolve(REPO_ROOT, 'packages/dev/s2-docs/src/iconAliases.js');
1518
const ILLUSTRATION_ALIASES_JS = path.resolve(REPO_ROOT, 'packages/dev/s2-docs/src/illustrationAliases.js');
19+
const STYLE_PROPERTIES_TS = path.resolve(REPO_ROOT, 'packages/dev/s2-docs/src/styleProperties.ts');
1620

1721
function ensureDir(p) {
1822
fs.mkdirSync(p, {recursive: true});
@@ -24,6 +28,23 @@ function writeJson(file, data) {
2428
console.log('Wrote', path.relative(REPO_ROOT, file));
2529
}
2630

31+
async function importTsModule(tsFilePath) {
32+
if (!fs.existsSync(tsFilePath)) {
33+
throw new Error(`TS module not found: ${tsFilePath}`);
34+
}
35+
const sourceText = fs.readFileSync(tsFilePath, 'utf8');
36+
const result = ts.transpileModule(sourceText, {
37+
fileName: tsFilePath,
38+
compilerOptions: {
39+
target: ts.ScriptTarget.ES2022,
40+
module: ts.ModuleKind.ESNext,
41+
esModuleInterop: true
42+
}
43+
});
44+
const url = `data:text/javascript;base64,${Buffer.from(result.outputText, 'utf8').toString('base64')}`;
45+
return import(url);
46+
}
47+
2748
function buildIconNames() {
2849
if (!fs.existsSync(ICONS_DIR)) {
2950
throw new Error(`Icons directory not found: ${ICONS_DIR}`);
@@ -61,6 +82,107 @@ async function loadAliases(modPath, exportName) {
6182
return mod[exportName] ?? {};
6283
}
6384

85+
function buildBaseColorKeysFromSpectrumTokens(tokens) {
86+
const keys = new Set();
87+
88+
// Matches spectrum-theme.ts
89+
keys.add('transparent');
90+
keys.add('black');
91+
keys.add('white');
92+
93+
const addScale = (scale) => {
94+
const re = new RegExp(`^${scale}-\\d+$`);
95+
for (const tokenName of Object.keys(tokens)) {
96+
if (re.test(tokenName)) {
97+
// Match @react-spectrum/s2/style/tokens.ts behavior: strip "-color" in the middle.
98+
keys.add(tokenName.replace('-color', ''));
99+
}
100+
}
101+
};
102+
103+
// Global color scales
104+
for (const scale of [
105+
'gray', 'blue', 'red', 'orange', 'yellow', 'chartreuse', 'celery', 'green',
106+
'seafoam', 'cyan', 'indigo', 'purple', 'fuchsia', 'magenta', 'pink',
107+
'turquoise', 'brown', 'silver', 'cinnamon'
108+
]) {
109+
addScale(scale);
110+
}
111+
112+
// Semantic color scales
113+
for (const scale of ['accent-color', 'informative-color', 'negative-color', 'notice-color', 'positive-color']) {
114+
addScale(scale);
115+
}
116+
117+
// Simple transparent scales (names remain unchanged)
118+
for (const scale of ['transparent-white', 'transparent-black']) {
119+
const re = new RegExp(`^${scale}-\\d+$`);
120+
for (const tokenName of Object.keys(tokens)) {
121+
if (re.test(tokenName)) {
122+
keys.add(tokenName);
123+
}
124+
}
125+
}
126+
127+
// Overlay scale keys (derived in tokens.ts, we only need the names here)
128+
for (const n of [25, 50, 75, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000]) {
129+
keys.add(`transparent-overlay-${n}`);
130+
}
131+
132+
// High contrast keywords (matches spectrum-theme.ts)
133+
for (const k of ['Background', 'ButtonBorder', 'ButtonFace', 'ButtonText', 'Field', 'Highlight', 'HighlightText', 'GrayText', 'Mark', 'LinkText']) {
134+
keys.add(k);
135+
}
136+
137+
return Array.from(keys).sort((a, b) => a.localeCompare(b));
138+
}
139+
140+
function buildExpandedStyleMacroPropertyValues(styleProperties, spacingTypeValues, baseColorKeys) {
141+
const out = {};
142+
143+
for (const [propertyName, def] of Object.entries(styleProperties)) {
144+
const values = [];
145+
const seen = new Set();
146+
147+
const pushUnique = (items) => {
148+
for (const v of items) {
149+
const s = String(v);
150+
if (!seen.has(s)) {
151+
seen.add(s);
152+
values.push(s);
153+
}
154+
}
155+
};
156+
157+
// Expand 'baseColors' placeholder into actual color token names.
158+
const expandedBase = [];
159+
for (const v of def.values ?? []) {
160+
if (v === 'baseColors') {
161+
expandedBase.push(...baseColorKeys);
162+
} else {
163+
expandedBase.push(v);
164+
}
165+
}
166+
pushUnique(expandedBase);
167+
168+
// Expand spacing type placeholders into the actual numeric values shown in docs.
169+
const additionalTypes = Array.isArray(def.additionalTypes) ? def.additionalTypes : [];
170+
if (additionalTypes.includes('baseSpacing')) {
171+
pushUnique(spacingTypeValues?.baseSpacing ?? []);
172+
}
173+
if (additionalTypes.includes('negativeSpacing')) {
174+
pushUnique(spacingTypeValues?.negativeSpacing ?? []);
175+
}
176+
177+
out[propertyName] = {
178+
values,
179+
additionalTypes
180+
};
181+
}
182+
183+
return out;
184+
}
185+
64186
async function main() {
65187
const icons = buildIconNames();
66188
const illustrations = buildIllustrationNames();
@@ -71,6 +193,22 @@ async function main() {
71193
writeJson(path.join(OUT_DIR, 'illustrations.json'), illustrations);
72194
writeJson(path.join(OUT_DIR, 'iconAliases.json'), iconAliases);
73195
writeJson(path.join(OUT_DIR, 'illustrationAliases.json'), illustrationAliases);
196+
197+
// Style macro property definitions
198+
const stylePropsMod = await importTsModule(STYLE_PROPERTIES_TS);
199+
const propertyCategories = ['color', 'dimensions', 'text', 'effects', 'layout', 'misc', 'conditions'];
200+
const styleProperties = {};
201+
for (const category of propertyCategories) {
202+
Object.assign(styleProperties, stylePropsMod.getPropertyDefinitions(category));
203+
}
204+
Object.assign(styleProperties, stylePropsMod.getShorthandDefinitions());
205+
writeJson(path.join(OUT_DIR, 'styleProperties.json'), styleProperties);
206+
207+
const require = createRequire(import.meta.url);
208+
const spectrumTokens = require('@adobe/spectrum-tokens/dist/json/variables.json');
209+
const baseColorKeys = buildBaseColorKeysFromSpectrumTokens(spectrumTokens);
210+
const expanded = buildExpandedStyleMacroPropertyValues(styleProperties, stylePropsMod.spacingTypeValues, baseColorKeys);
211+
writeJson(path.join(OUT_DIR, 'styleMacroPropertyValues.json'), expanded);
74212
}
75213

76214
main().catch((err) => {

packages/dev/mcp/s2/src/index.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env node
22
/// <reference types="node" />
33
import {errorToString} from '../../shared/src/utils.js';
4-
import {listIconNames, listIllustrationNames, loadIconAliases, loadIllustrationAliases} from './s2-data.js';
4+
import {listIconNames, listIllustrationNames, loadIconAliases, loadIllustrationAliases, loadStyleMacroPropertyValues} from './s2-data.js';
55
import type {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
66
import {startServer} from '../../shared/src/server.js';
77
import {z} from 'zod';
@@ -87,6 +87,39 @@ import {z} from 'zod';
8787
return {content: [{type: 'text', text: JSON.stringify(Array.from(results).sort((a, b) => a.localeCompare(b)), null, 2)}]};
8888
}
8989
);
90+
91+
server.registerTool(
92+
'get_style_macro_property_values',
93+
{
94+
title: 'Get style macro property values',
95+
description: 'Returns the allowed values for a given S2 style macro property (including expanded color/spacing value lists where applicable).',
96+
inputSchema: {propertyName: z.string()}
97+
},
98+
async ({propertyName}) => {
99+
const name = String(propertyName ?? '').trim();
100+
if (!name) {
101+
throw new Error('Provide a non-empty propertyName.');
102+
}
103+
104+
const all = loadStyleMacroPropertyValues();
105+
let def = all[name];
106+
if (!def) {
107+
// fallback to case-insensitive lookup
108+
const lower = name.toLowerCase();
109+
const matchKey = Object.keys(all).find(k => k.toLowerCase() === lower);
110+
if (matchKey) {
111+
def = all[matchKey];
112+
}
113+
}
114+
115+
if (!def) {
116+
const available = Object.keys(all).sort((a, b) => a.localeCompare(b));
117+
throw new Error(`Unknown style macro property '${name}'. Available properties: ${available.join(', ')}`);
118+
}
119+
120+
return {content: [{type: 'text', text: JSON.stringify(def, null, 2)}]};
121+
}
122+
);
90123
});
91124
} catch (err) {
92125
console.error(errorToString(err));

packages/dev/mcp/s2/src/s2-data.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ let iconIdCache: string[] | null = null;
99
let illustrationIdCache: string[] | null = null;
1010
let iconAliasesCache: Record<string, string[]> | null = null;
1111
let illustrationAliasesCache: Record<string, string[]> | null = null;
12+
let styleMacroPropertyValuesCache: Record<string, {values: string[], additionalTypes?: string[]}> | null = null;
1213

1314
function readBundledJson(filename: string): any | null {
1415
try {
@@ -45,3 +46,12 @@ export async function loadIllustrationAliases(): Promise<Record<string, string[]
4546
const bundled = readBundledJson('illustrationAliases.json');
4647
return (illustrationAliasesCache = (bundled && typeof bundled === 'object') ? bundled : {});
4748
}
49+
50+
export function loadStyleMacroPropertyValues(): Record<string, {values: string[], additionalTypes?: string[]}> {
51+
if (styleMacroPropertyValuesCache) {return styleMacroPropertyValuesCache;}
52+
const bundled = readBundledJson('styleMacroPropertyValues.json');
53+
if (!bundled || typeof bundled !== 'object' || Array.isArray(bundled)) {
54+
return (styleMacroPropertyValuesCache = {});
55+
}
56+
return (styleMacroPropertyValuesCache = bundled as any);
57+
}

0 commit comments

Comments
 (0)