@primer/mcp 0.3.0 → 0.3.1
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 +2 -1
- package/dist/primitives.d.ts +80 -4
- package/dist/primitives.d.ts.map +1 -1
- package/dist/{server-Cwz0naYT.js → server-owUZMSeG.js} +130 -49
- package/dist/server.d.ts.map +1 -1
- package/dist/stdio.js +2 -1
- package/package.json +2 -2
- package/src/primitives.ts +113 -45
- package/src/server.ts +38 -1
- package/dist/server-CjO5UCV7.js +0 -766
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { s as server } from './server-
|
|
1
|
+
export { s as server } from './server-owUZMSeG.js';
|
|
2
2
|
import '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import 'cheerio';
|
|
4
4
|
import 'zod';
|
|
@@ -7,6 +7,7 @@ import '@primer/react/generated/components.json' with { type: 'json' };
|
|
|
7
7
|
import '@primer/octicons/build/data.json' with { type: 'json' };
|
|
8
8
|
import 'node:fs';
|
|
9
9
|
import 'node:module';
|
|
10
|
+
import 'child_process';
|
|
10
11
|
import '@primer/primitives/dist/docs/base/motion/motion.json' with { type: 'json' };
|
|
11
12
|
import '@primer/primitives/dist/docs/base/size/size.json' with { type: 'json' };
|
|
12
13
|
import '@primer/primitives/dist/docs/base/typography/typography.json' with { type: 'json' };
|
package/dist/primitives.d.ts
CHANGED
|
@@ -3,7 +3,43 @@ declare const categories: {
|
|
|
3
3
|
readonly motion: {
|
|
4
4
|
name: string;
|
|
5
5
|
type: string;
|
|
6
|
-
value:
|
|
6
|
+
value: number[] | {
|
|
7
|
+
value: number;
|
|
8
|
+
unit: string;
|
|
9
|
+
} | {
|
|
10
|
+
value: number;
|
|
11
|
+
unit: string;
|
|
12
|
+
} | {
|
|
13
|
+
value: number;
|
|
14
|
+
unit: string;
|
|
15
|
+
} | {
|
|
16
|
+
value: number;
|
|
17
|
+
unit: string;
|
|
18
|
+
} | {
|
|
19
|
+
value: number;
|
|
20
|
+
unit: string;
|
|
21
|
+
} | {
|
|
22
|
+
value: number;
|
|
23
|
+
unit: string;
|
|
24
|
+
} | {
|
|
25
|
+
value: number;
|
|
26
|
+
unit: string;
|
|
27
|
+
} | {
|
|
28
|
+
value: number;
|
|
29
|
+
unit: string;
|
|
30
|
+
} | {
|
|
31
|
+
value: number;
|
|
32
|
+
unit: string;
|
|
33
|
+
} | {
|
|
34
|
+
value: number;
|
|
35
|
+
unit: string;
|
|
36
|
+
} | {
|
|
37
|
+
value: number;
|
|
38
|
+
unit: string;
|
|
39
|
+
} | {
|
|
40
|
+
value: number;
|
|
41
|
+
unit: string;
|
|
42
|
+
};
|
|
7
43
|
}[];
|
|
8
44
|
readonly size: {
|
|
9
45
|
name: string;
|
|
@@ -13,7 +49,7 @@ declare const categories: {
|
|
|
13
49
|
readonly typography: {
|
|
14
50
|
name: string;
|
|
15
51
|
type: string;
|
|
16
|
-
value: number;
|
|
52
|
+
value: string | number;
|
|
17
53
|
}[];
|
|
18
54
|
};
|
|
19
55
|
readonly functional: {
|
|
@@ -59,7 +95,43 @@ declare const categories: {
|
|
|
59
95
|
declare const tokens: ({
|
|
60
96
|
name: string;
|
|
61
97
|
type: string;
|
|
62
|
-
value:
|
|
98
|
+
value: number[] | {
|
|
99
|
+
value: number;
|
|
100
|
+
unit: string;
|
|
101
|
+
} | {
|
|
102
|
+
value: number;
|
|
103
|
+
unit: string;
|
|
104
|
+
} | {
|
|
105
|
+
value: number;
|
|
106
|
+
unit: string;
|
|
107
|
+
} | {
|
|
108
|
+
value: number;
|
|
109
|
+
unit: string;
|
|
110
|
+
} | {
|
|
111
|
+
value: number;
|
|
112
|
+
unit: string;
|
|
113
|
+
} | {
|
|
114
|
+
value: number;
|
|
115
|
+
unit: string;
|
|
116
|
+
} | {
|
|
117
|
+
value: number;
|
|
118
|
+
unit: string;
|
|
119
|
+
} | {
|
|
120
|
+
value: number;
|
|
121
|
+
unit: string;
|
|
122
|
+
} | {
|
|
123
|
+
value: number;
|
|
124
|
+
unit: string;
|
|
125
|
+
} | {
|
|
126
|
+
value: number;
|
|
127
|
+
unit: string;
|
|
128
|
+
} | {
|
|
129
|
+
value: number;
|
|
130
|
+
unit: string;
|
|
131
|
+
} | {
|
|
132
|
+
value: number;
|
|
133
|
+
unit: string;
|
|
134
|
+
};
|
|
63
135
|
} | {
|
|
64
136
|
name: string;
|
|
65
137
|
type: string;
|
|
@@ -104,5 +176,9 @@ declare function formatBundle(bundleTokens: TokenWithGuidelines[]): string;
|
|
|
104
176
|
*/
|
|
105
177
|
declare function getValidGroupsList(validTokens: TokenWithGuidelines[]): string;
|
|
106
178
|
declare const groupHints: Record<string, string>;
|
|
107
|
-
|
|
179
|
+
declare function runStylelint(css: string): Promise<{
|
|
180
|
+
stdout: string;
|
|
181
|
+
stderr: string;
|
|
182
|
+
}>;
|
|
183
|
+
export { parseDesignTokensSpec, findTokens, buildAllTokens, expandTokenPattern, loadAllTokensWithGuidelines, loadDesignTokensGuide, loadDesignTokensSpec, getDesignTokenSpecsText, getTokenUsagePatternsText, getValidGroupsList, searchTokens, formatBundle, groupHints, GROUP_ALIASES, GROUP_LABELS, tokenMatchesGroup, runStylelint, type TokenWithGuidelines, };
|
|
108
184
|
//# sourceMappingURL=primitives.d.ts.map
|
package/dist/primitives.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../src/primitives.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../src/primitives.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@ import componentsMetadata from '@primer/react/generated/components.json' with {
|
|
|
6
6
|
import octicons from '@primer/octicons/build/data.json' with { type: 'json' };
|
|
7
7
|
import { readFileSync } from 'node:fs';
|
|
8
8
|
import { createRequire } from 'node:module';
|
|
9
|
+
import { spawn } from 'child_process';
|
|
9
10
|
import baseMotion from '@primer/primitives/dist/docs/base/motion/motion.json' with { type: 'json' };
|
|
10
11
|
import baseSize from '@primer/primitives/dist/docs/base/size/size.json' with { type: 'json' };
|
|
11
12
|
import baseTypography from '@primer/primitives/dist/docs/base/typography/typography.json' with { type: 'json' };
|
|
@@ -174,7 +175,7 @@ const categories = {
|
|
|
174
175
|
const tokens = [...categories.base.motion, ...categories.base.size, ...categories.base.typography, ...categories.functional.border, ...categories.functional.radius, ...categories.functional.sizeCoarse, ...categories.functional.sizeFine, ...categories.functional.size, ...categories.functional.themes.light, ...categories.functional.typography];
|
|
175
176
|
|
|
176
177
|
// Semantic group prefixes that apply to any element
|
|
177
|
-
const SEMANTIC_PREFIXES = ['bgColor', 'fgColor', 'border', 'borderColor', 'shadow', 'focus', 'color'];
|
|
178
|
+
const SEMANTIC_PREFIXES = ['bgColor', 'fgColor', 'border', 'borderColor', 'shadow', 'focus', 'color', 'animation', 'duration'];
|
|
178
179
|
function listTokenGroups() {
|
|
179
180
|
// Use the full token set so non-theme groups (stack, text, borderRadius, etc.) are included
|
|
180
181
|
const allTokens = tokens;
|
|
@@ -414,51 +415,42 @@ function getDesignTokenSpecsText(groups) {
|
|
|
414
415
|
# Design Token Specifications
|
|
415
416
|
|
|
416
417
|
## 1. Core Rule & Enforcement
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
418
|
+
* **Expert Mode**: CSS expert. NEVER use raw values (hex, px, etc.). Tokens only.
|
|
419
|
+
* **Motion & Transitions:** Every interactive state change (Hover, Active) MUST include a transition. NEVER use raw values like 200ms or ease-in. Use var(--base-duration-...) and var(--base-easing-...).
|
|
420
|
+
* **Shorthand**: MUST use \`font: var(...)\`. NEVER split size/weight.
|
|
421
|
+
* **Shorthand Fallback**: If no shorthand exists (e.g. Monospace), use individual tokens for font-size, family, and line-height. NEVER raw 1.5.
|
|
422
|
+
* **States**: Define 5: Rest, Hover, Focus-visible, Active, Disabled.
|
|
423
|
+
* **Focus**: \`:focus-visible\` MUST use \`outline: var(--focus-outline)\` AND \`outline-offset: var(--outline-focus-offset)\`.
|
|
424
|
+
* **Validation**: CALL \`lint_css\` after any CSS change. Task is incomplete without a success message.
|
|
425
|
+
* **Self-Correction**: Adopt autofixes immediately. Report unfixable errors to the user.
|
|
424
426
|
|
|
425
427
|
## 2. Typography Constraints (STRICT)
|
|
426
|
-
- **Body Only**: Only
|
|
427
|
-
- **Static Shorthands**:
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
|
435
|
-
|
|
|
436
|
-
|
|
|
437
|
-
|
|
|
438
|
-
|
|
|
439
|
-
|
|
|
440
|
-
|
|
441
|
-
## 4.
|
|
442
|
-
|
|
443
|
-
- **
|
|
444
|
-
- **
|
|
445
|
-
- **
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
- **
|
|
451
|
-
|
|
452
|
-
## 6. Optimization Strategy (MANDATORY)
|
|
453
|
-
- **STOP**: Do not call \`find_tokens\` repeatedly for individual properties.
|
|
454
|
-
- **GO**: Use \`get_token_group_bundle\` to fetch relevant groups at once.
|
|
455
|
-
- *Example for Button*: \`get_token_group_bundle(groups: ["control", "button"])\`
|
|
456
|
-
- *Note*: \`control\` is for form inputs; \`button\` is for triggers.
|
|
457
|
-
|
|
458
|
-
## 7. Token Bundle Recipes (Recommended)
|
|
459
|
-
- **Forms/Inputs**: \`["control", "focus", "outline", "text", "borderRadius"]\`
|
|
460
|
-
- **Modals/Dialogs**: \`["overlay", "shadow", "outline", "borderRadius", "bgColor"]\`
|
|
461
|
-
- **Data Tables**: \`["stack", "borderColor", "text", "bgColor"]\`
|
|
428
|
+
- **Body Only**: Only \`body\` group supports size suffixes (e.g., \`body-small\`).
|
|
429
|
+
- **Static Shorthands**: NEVER add suffixes to \`caption\`, \`display\`, \`codeBlock\`, or \`codeInline\`.
|
|
430
|
+
|
|
431
|
+
## 3. Logic Matrix: Color & Semantic Mapping
|
|
432
|
+
| Input Color/Intent | Semantic Role | Background Suffix | Foreground Requirement |
|
|
433
|
+
| :--- | :--- | :--- | :--- |
|
|
434
|
+
| Blue / Interactive | \`accent\` | \`-emphasis\` (Solid) | \`fgColor-onEmphasis\` |
|
|
435
|
+
| Green / Positive | \`success\` | \`-muted\` (Light) | \`fgColor-{semantic}\` |
|
|
436
|
+
| Red / Danger | \`danger\` | \`-emphasis\` | \`fgColor-onEmphasis\` |
|
|
437
|
+
| Yellow / Warning | \`attention\` | \`-muted\` | \`fgColor-attention\` |
|
|
438
|
+
| Orange / Critical | \`severe\` | \`-emphasis\` | \`fgColor-onEmphasis\` |
|
|
439
|
+
| Purple / Done | \`done\` | Any | Match intent |
|
|
440
|
+
| Pink / Sponsors | \`sponsors\` | Any | Match intent |
|
|
441
|
+
| Grey / Neutral | \`default\` | \`bgColor-muted\` | \`fgColor-default\` (Not muted) |
|
|
442
|
+
|
|
443
|
+
## 4. Optimization & Recipes (MANDATORY)
|
|
444
|
+
**Strategy**: STOP property-by-property searching. Use \`get_token_group_bundle\` for these common patterns:
|
|
445
|
+
- **Forms**: \`["control", "focus", "outline", "text", "borderRadius", "stack", "animation"]\`
|
|
446
|
+
- **Modals/Cards**: \`["overlay", "shadow", "outline", "borderRadius", "bgColor", "stack", "animation"]\`
|
|
447
|
+
- **Tables/Lists**: \`["stack", "borderColor", "text", "bgColor", "control"]\`
|
|
448
|
+
- **Nav/Sidebars**: \`["control", "text", "accent", "stack", "focus", "animation"]\`
|
|
449
|
+
- **Status/Badges**: \`["text", "success", "danger", "attention", "severe", "stack"]\`
|
|
450
|
+
|
|
451
|
+
## 5. Available Groups
|
|
452
|
+
- **Semantic**: ${groups.semantic.map(g => `${g.name}\``).join(', ')}
|
|
453
|
+
- **Components**: ${groups.component.map(g => `\`${g.name}\``).join(', ')}
|
|
462
454
|
`.trim();
|
|
463
455
|
}
|
|
464
456
|
|
|
@@ -612,6 +604,9 @@ const GROUP_ALIASES = {
|
|
|
612
604
|
typography: 'text',
|
|
613
605
|
font: 'text',
|
|
614
606
|
text: 'text',
|
|
607
|
+
'line-height': 'text',
|
|
608
|
+
lineheight: 'text',
|
|
609
|
+
leading: 'text',
|
|
615
610
|
// Layout & Spacing
|
|
616
611
|
stack: 'stack',
|
|
617
612
|
controlstack: 'controlStack',
|
|
@@ -628,7 +623,26 @@ const GROUP_ALIASES = {
|
|
|
628
623
|
borderwidth: 'borderWidth',
|
|
629
624
|
line: 'borderColor',
|
|
630
625
|
stroke: 'borderColor',
|
|
631
|
-
separator: 'borderColor'
|
|
626
|
+
separator: 'borderColor',
|
|
627
|
+
// Color-to-Semantic Intent Mapping
|
|
628
|
+
red: 'danger',
|
|
629
|
+
green: 'success',
|
|
630
|
+
yellow: 'attention',
|
|
631
|
+
orange: 'severe',
|
|
632
|
+
blue: 'accent',
|
|
633
|
+
purple: 'done',
|
|
634
|
+
pink: 'sponsors',
|
|
635
|
+
grey: 'neutral',
|
|
636
|
+
gray: 'neutral',
|
|
637
|
+
// Descriptive Aliases
|
|
638
|
+
light: 'muted',
|
|
639
|
+
subtle: 'muted',
|
|
640
|
+
dark: 'emphasis',
|
|
641
|
+
strong: 'emphasis',
|
|
642
|
+
intense: 'emphasis',
|
|
643
|
+
bold: 'emphasis',
|
|
644
|
+
vivid: 'emphasis',
|
|
645
|
+
highlight: 'emphasis'
|
|
632
646
|
};
|
|
633
647
|
|
|
634
648
|
// Match a token against a resolved group by checking both the token name prefix and the group label
|
|
@@ -680,12 +694,49 @@ function getValidGroupsList(validTokens) {
|
|
|
680
694
|
const groupHints = {
|
|
681
695
|
control: '`control` tokens are for form inputs/checkboxes. For buttons, use the `button` group.',
|
|
682
696
|
button: '`button` tokens are for standard triggers. For form-fields, see the `control` group.',
|
|
683
|
-
text: 'STRICT: The following typography groups do NOT support size suffixes (-small, -medium, -large): `caption`, `display`, `codeBlock`, and `codeInline`. Use
|
|
697
|
+
text: 'STRICT: The following typography groups do NOT support size suffixes (-small, -medium, -large): `caption`, `display`, `codeBlock`, and `codeInline`. STRICT: Use shorthand tokens where possible. If splitting, you MUST fetch line-height tokens (e.g., --text-body-lineHeight-small) instead of using raw numbers.',
|
|
684
698
|
fgColor: 'Use `fgColor` for text. For borders, use `borderColor`.',
|
|
685
|
-
borderWidth: '`borderWidth` only has sizing values (thin, thick, thicker). For border *colors*, use the `borderColor` or `border` group.'
|
|
699
|
+
borderWidth: '`borderWidth` only has sizing values (thin, thick, thicker). For border *colors*, use the `borderColor` or `border` group.',
|
|
700
|
+
animation: 'TRANSITION RULE: Apply duration and easing to the base class, not the :hover state. Standard pairing: `transition: background-color var(--base-duration-200) var(--base-easing-easeInOut);`'
|
|
686
701
|
};
|
|
687
702
|
|
|
688
|
-
|
|
703
|
+
// -----------------------------------------------------------------------------
|
|
704
|
+
// Stylelint runner
|
|
705
|
+
// -----------------------------------------------------------------------------
|
|
706
|
+
function runStylelint(css) {
|
|
707
|
+
return new Promise((resolve, reject) => {
|
|
708
|
+
const proc = spawn('npx', ['stylelint', '--stdin', '--fix'], {
|
|
709
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
710
|
+
shell: true
|
|
711
|
+
});
|
|
712
|
+
let stdout = '';
|
|
713
|
+
let stderr = '';
|
|
714
|
+
proc.stdout.on('data', data => {
|
|
715
|
+
stdout += data.toString();
|
|
716
|
+
});
|
|
717
|
+
proc.stderr.on('data', data => {
|
|
718
|
+
stderr += data.toString();
|
|
719
|
+
});
|
|
720
|
+
proc.on('close', code => {
|
|
721
|
+
if (code === 0) {
|
|
722
|
+
resolve({
|
|
723
|
+
stdout,
|
|
724
|
+
stderr
|
|
725
|
+
});
|
|
726
|
+
} else {
|
|
727
|
+
const error = new Error(`Stylelint exited with code ${code}`);
|
|
728
|
+
error.stdout = stdout;
|
|
729
|
+
error.stderr = stderr;
|
|
730
|
+
reject(error);
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
proc.on('error', reject);
|
|
734
|
+
proc.stdin.write(css);
|
|
735
|
+
proc.stdin.end();
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
var version = "0.3.1";
|
|
689
740
|
var packageJson = {
|
|
690
741
|
version: version};
|
|
691
742
|
|
|
@@ -1056,7 +1107,7 @@ ${text}`
|
|
|
1056
1107
|
// Design Tokens
|
|
1057
1108
|
// -----------------------------------------------------------------------------
|
|
1058
1109
|
server.registerTool('find_tokens', {
|
|
1059
|
-
description:
|
|
1110
|
+
description: 'Search for specific tokens. Tip: If you only provide a \'group\' and leave \'query\' empty, it returns all tokens in that category. Avoid property-by-property searching. COLOR RESOLUTION: If a user asks for "pink" or "blue", do not search for the color name. Use the semantic intent: blue->accent, red->danger, green->success. Always check both "emphasis" and "muted" variants for background colors. After identifying tokens and writing CSS, you MUST validate the result using lint_css.',
|
|
1060
1111
|
inputSchema: {
|
|
1061
1112
|
query: z.string().optional().default('').describe('Search keywords (e.g., "danger border", "success background")'),
|
|
1062
1113
|
group: z.string().optional().describe('Filter by group (e.g., "fgColor", "border")'),
|
|
@@ -1217,6 +1268,36 @@ server.registerTool('get_token_usage_patterns', {
|
|
|
1217
1268
|
}]
|
|
1218
1269
|
};
|
|
1219
1270
|
});
|
|
1271
|
+
server.registerTool('lint_css', {
|
|
1272
|
+
description: 'REQUIRED FINAL STEP. Use this to validate your CSS. You cannot complete a task involving CSS without a successful run of this tool.',
|
|
1273
|
+
inputSchema: {
|
|
1274
|
+
css: z.string()
|
|
1275
|
+
}
|
|
1276
|
+
}, async ({
|
|
1277
|
+
css
|
|
1278
|
+
}) => {
|
|
1279
|
+
try {
|
|
1280
|
+
// --fix flag tells Stylelint to repair what it can
|
|
1281
|
+
const {
|
|
1282
|
+
stdout
|
|
1283
|
+
} = await runStylelint(css);
|
|
1284
|
+
return {
|
|
1285
|
+
content: [{
|
|
1286
|
+
type: 'text',
|
|
1287
|
+
text: stdout || '✅ Stylelint passed (or was successfully autofixed).'
|
|
1288
|
+
}]
|
|
1289
|
+
};
|
|
1290
|
+
} catch (error) {
|
|
1291
|
+
// If Stylelint still has errors it CANNOT fix, it will land here
|
|
1292
|
+
const errorOutput = error instanceof Error && 'stdout' in error ? error.stdout : String(error);
|
|
1293
|
+
return {
|
|
1294
|
+
content: [{
|
|
1295
|
+
type: 'text',
|
|
1296
|
+
text: `❌ Errors without autofix remaining:\n${errorOutput}`
|
|
1297
|
+
}]
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
});
|
|
1220
1301
|
|
|
1221
1302
|
// -----------------------------------------------------------------------------
|
|
1222
1303
|
// Foundations
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAA;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAA;AAwBjE,QAAA,MAAM,MAAM,WAGV,CAAA;AA27BF,OAAO,EAAC,MAAM,EAAC,CAAA"}
|
package/dist/stdio.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
2
|
-
import { s as server } from './server-
|
|
2
|
+
import { s as server } from './server-owUZMSeG.js';
|
|
3
3
|
import '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
4
|
import 'cheerio';
|
|
5
5
|
import 'zod';
|
|
@@ -8,6 +8,7 @@ import '@primer/react/generated/components.json' with { type: 'json' };
|
|
|
8
8
|
import '@primer/octicons/build/data.json' with { type: 'json' };
|
|
9
9
|
import 'node:fs';
|
|
10
10
|
import 'node:module';
|
|
11
|
+
import 'child_process';
|
|
11
12
|
import '@primer/primitives/dist/docs/base/motion/motion.json' with { type: 'json' };
|
|
12
13
|
import '@primer/primitives/dist/docs/base/size/size.json' with { type: 'json' };
|
|
13
14
|
import '@primer/primitives/dist/docs/base/typography/typography.json' with { type: 'json' };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primer/mcp",
|
|
3
3
|
"description": "An MCP server that connects AI tools to the Primer Design System",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"mcp": "./bin/mcp.js"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@modelcontextprotocol/sdk": "^1.24.0",
|
|
38
38
|
"@primer/octicons": "^19.15.5",
|
|
39
39
|
"@primer/primitives": "10.x || 11.x",
|
|
40
|
-
"@primer/react": "^38.
|
|
40
|
+
"@primer/react": "^38.15.0",
|
|
41
41
|
"cheerio": "^1.0.0",
|
|
42
42
|
"turndown": "^7.2.0",
|
|
43
43
|
"zod": "^4.3.5"
|
package/src/primitives.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {readFileSync} from 'node:fs'
|
|
2
2
|
import {createRequire} from 'node:module'
|
|
3
|
+
import {spawn} from 'child_process'
|
|
3
4
|
import baseMotion from '@primer/primitives/dist/docs/base/motion/motion.json' with {type: 'json'}
|
|
4
5
|
import baseSize from '@primer/primitives/dist/docs/base/size/size.json' with {type: 'json'}
|
|
5
6
|
import baseTypography from '@primer/primitives/dist/docs/base/typography/typography.json' with {type: 'json'}
|
|
@@ -121,7 +122,17 @@ function serialize(value: typeof tokens): string {
|
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
// Semantic group prefixes that apply to any element
|
|
124
|
-
const SEMANTIC_PREFIXES = [
|
|
125
|
+
const SEMANTIC_PREFIXES = [
|
|
126
|
+
'bgColor',
|
|
127
|
+
'fgColor',
|
|
128
|
+
'border',
|
|
129
|
+
'borderColor',
|
|
130
|
+
'shadow',
|
|
131
|
+
'focus',
|
|
132
|
+
'color',
|
|
133
|
+
'animation',
|
|
134
|
+
'duration',
|
|
135
|
+
]
|
|
125
136
|
|
|
126
137
|
type TokenGroup = {
|
|
127
138
|
name: string
|
|
@@ -421,51 +432,42 @@ function getDesignTokenSpecsText(groups: TokenGroups): string {
|
|
|
421
432
|
# Design Token Specifications
|
|
422
433
|
|
|
423
434
|
## 1. Core Rule & Enforcement
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
435
|
+
* **Expert Mode**: CSS expert. NEVER use raw values (hex, px, etc.). Tokens only.
|
|
436
|
+
* **Motion & Transitions:** Every interactive state change (Hover, Active) MUST include a transition. NEVER use raw values like 200ms or ease-in. Use var(--base-duration-...) and var(--base-easing-...).
|
|
437
|
+
* **Shorthand**: MUST use \`font: var(...)\`. NEVER split size/weight.
|
|
438
|
+
* **Shorthand Fallback**: If no shorthand exists (e.g. Monospace), use individual tokens for font-size, family, and line-height. NEVER raw 1.5.
|
|
439
|
+
* **States**: Define 5: Rest, Hover, Focus-visible, Active, Disabled.
|
|
440
|
+
* **Focus**: \`:focus-visible\` MUST use \`outline: var(--focus-outline)\` AND \`outline-offset: var(--outline-focus-offset)\`.
|
|
441
|
+
* **Validation**: CALL \`lint_css\` after any CSS change. Task is incomplete without a success message.
|
|
442
|
+
* **Self-Correction**: Adopt autofixes immediately. Report unfixable errors to the user.
|
|
431
443
|
|
|
432
444
|
## 2. Typography Constraints (STRICT)
|
|
433
|
-
- **Body Only**: Only
|
|
434
|
-
- **Static Shorthands**:
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
|
442
|
-
|
|
|
443
|
-
|
|
|
444
|
-
|
|
|
445
|
-
|
|
|
446
|
-
|
|
|
447
|
-
|
|
448
|
-
## 4.
|
|
449
|
-
|
|
450
|
-
- **
|
|
451
|
-
- **
|
|
452
|
-
- **
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
- **
|
|
458
|
-
|
|
459
|
-
## 6. Optimization Strategy (MANDATORY)
|
|
460
|
-
- **STOP**: Do not call \`find_tokens\` repeatedly for individual properties.
|
|
461
|
-
- **GO**: Use \`get_token_group_bundle\` to fetch relevant groups at once.
|
|
462
|
-
- *Example for Button*: \`get_token_group_bundle(groups: ["control", "button"])\`
|
|
463
|
-
- *Note*: \`control\` is for form inputs; \`button\` is for triggers.
|
|
464
|
-
|
|
465
|
-
## 7. Token Bundle Recipes (Recommended)
|
|
466
|
-
- **Forms/Inputs**: \`["control", "focus", "outline", "text", "borderRadius"]\`
|
|
467
|
-
- **Modals/Dialogs**: \`["overlay", "shadow", "outline", "borderRadius", "bgColor"]\`
|
|
468
|
-
- **Data Tables**: \`["stack", "borderColor", "text", "bgColor"]\`
|
|
445
|
+
- **Body Only**: Only \`body\` group supports size suffixes (e.g., \`body-small\`).
|
|
446
|
+
- **Static Shorthands**: NEVER add suffixes to \`caption\`, \`display\`, \`codeBlock\`, or \`codeInline\`.
|
|
447
|
+
|
|
448
|
+
## 3. Logic Matrix: Color & Semantic Mapping
|
|
449
|
+
| Input Color/Intent | Semantic Role | Background Suffix | Foreground Requirement |
|
|
450
|
+
| :--- | :--- | :--- | :--- |
|
|
451
|
+
| Blue / Interactive | \`accent\` | \`-emphasis\` (Solid) | \`fgColor-onEmphasis\` |
|
|
452
|
+
| Green / Positive | \`success\` | \`-muted\` (Light) | \`fgColor-{semantic}\` |
|
|
453
|
+
| Red / Danger | \`danger\` | \`-emphasis\` | \`fgColor-onEmphasis\` |
|
|
454
|
+
| Yellow / Warning | \`attention\` | \`-muted\` | \`fgColor-attention\` |
|
|
455
|
+
| Orange / Critical | \`severe\` | \`-emphasis\` | \`fgColor-onEmphasis\` |
|
|
456
|
+
| Purple / Done | \`done\` | Any | Match intent |
|
|
457
|
+
| Pink / Sponsors | \`sponsors\` | Any | Match intent |
|
|
458
|
+
| Grey / Neutral | \`default\` | \`bgColor-muted\` | \`fgColor-default\` (Not muted) |
|
|
459
|
+
|
|
460
|
+
## 4. Optimization & Recipes (MANDATORY)
|
|
461
|
+
**Strategy**: STOP property-by-property searching. Use \`get_token_group_bundle\` for these common patterns:
|
|
462
|
+
- **Forms**: \`["control", "focus", "outline", "text", "borderRadius", "stack", "animation"]\`
|
|
463
|
+
- **Modals/Cards**: \`["overlay", "shadow", "outline", "borderRadius", "bgColor", "stack", "animation"]\`
|
|
464
|
+
- **Tables/Lists**: \`["stack", "borderColor", "text", "bgColor", "control"]\`
|
|
465
|
+
- **Nav/Sidebars**: \`["control", "text", "accent", "stack", "focus", "animation"]\`
|
|
466
|
+
- **Status/Badges**: \`["text", "success", "danger", "attention", "severe", "stack"]\`
|
|
467
|
+
|
|
468
|
+
## 5. Available Groups
|
|
469
|
+
- **Semantic**: ${groups.semantic.map(g => `${g.name}\``).join(', ')}
|
|
470
|
+
- **Components**: ${groups.component.map(g => `\`${g.name}\``).join(', ')}
|
|
469
471
|
`.trim()
|
|
470
472
|
}
|
|
471
473
|
|
|
@@ -625,6 +627,9 @@ const GROUP_ALIASES: Record<string, string> = {
|
|
|
625
627
|
typography: 'text',
|
|
626
628
|
font: 'text',
|
|
627
629
|
text: 'text',
|
|
630
|
+
'line-height': 'text',
|
|
631
|
+
lineheight: 'text',
|
|
632
|
+
leading: 'text',
|
|
628
633
|
|
|
629
634
|
// Layout & Spacing
|
|
630
635
|
stack: 'stack',
|
|
@@ -645,6 +650,27 @@ const GROUP_ALIASES: Record<string, string> = {
|
|
|
645
650
|
line: 'borderColor',
|
|
646
651
|
stroke: 'borderColor',
|
|
647
652
|
separator: 'borderColor',
|
|
653
|
+
|
|
654
|
+
// Color-to-Semantic Intent Mapping
|
|
655
|
+
red: 'danger',
|
|
656
|
+
green: 'success',
|
|
657
|
+
yellow: 'attention',
|
|
658
|
+
orange: 'severe',
|
|
659
|
+
blue: 'accent',
|
|
660
|
+
purple: 'done',
|
|
661
|
+
pink: 'sponsors',
|
|
662
|
+
grey: 'neutral',
|
|
663
|
+
gray: 'neutral',
|
|
664
|
+
|
|
665
|
+
// Descriptive Aliases
|
|
666
|
+
light: 'muted',
|
|
667
|
+
subtle: 'muted',
|
|
668
|
+
dark: 'emphasis',
|
|
669
|
+
strong: 'emphasis',
|
|
670
|
+
intense: 'emphasis',
|
|
671
|
+
bold: 'emphasis',
|
|
672
|
+
vivid: 'emphasis',
|
|
673
|
+
highlight: 'emphasis',
|
|
648
674
|
}
|
|
649
675
|
|
|
650
676
|
// Match a token against a resolved group by checking both the token name prefix and the group label
|
|
@@ -701,10 +727,51 @@ function getValidGroupsList(validTokens: TokenWithGuidelines[]): string {
|
|
|
701
727
|
const groupHints: Record<string, string> = {
|
|
702
728
|
control: '`control` tokens are for form inputs/checkboxes. For buttons, use the `button` group.',
|
|
703
729
|
button: '`button` tokens are for standard triggers. For form-fields, see the `control` group.',
|
|
704
|
-
text: 'STRICT: The following typography groups do NOT support size suffixes (-small, -medium, -large): `caption`, `display`, `codeBlock`, and `codeInline`. Use
|
|
730
|
+
text: 'STRICT: The following typography groups do NOT support size suffixes (-small, -medium, -large): `caption`, `display`, `codeBlock`, and `codeInline`. STRICT: Use shorthand tokens where possible. If splitting, you MUST fetch line-height tokens (e.g., --text-body-lineHeight-small) instead of using raw numbers.',
|
|
705
731
|
fgColor: 'Use `fgColor` for text. For borders, use `borderColor`.',
|
|
706
732
|
borderWidth:
|
|
707
733
|
'`borderWidth` only has sizing values (thin, thick, thicker). For border *colors*, use the `borderColor` or `border` group.',
|
|
734
|
+
animation:
|
|
735
|
+
'TRANSITION RULE: Apply duration and easing to the base class, not the :hover state. Standard pairing: `transition: background-color var(--base-duration-200) var(--base-easing-easeInOut);`',
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// -----------------------------------------------------------------------------
|
|
739
|
+
// Stylelint runner
|
|
740
|
+
// -----------------------------------------------------------------------------
|
|
741
|
+
function runStylelint(css: string): Promise<{stdout: string; stderr: string}> {
|
|
742
|
+
return new Promise((resolve, reject) => {
|
|
743
|
+
const proc = spawn('npx', ['stylelint', '--stdin', '--fix'], {
|
|
744
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
745
|
+
shell: true,
|
|
746
|
+
})
|
|
747
|
+
|
|
748
|
+
let stdout = ''
|
|
749
|
+
let stderr = ''
|
|
750
|
+
|
|
751
|
+
proc.stdout.on('data', (data: Buffer) => {
|
|
752
|
+
stdout += data.toString()
|
|
753
|
+
})
|
|
754
|
+
|
|
755
|
+
proc.stderr.on('data', (data: Buffer) => {
|
|
756
|
+
stderr += data.toString()
|
|
757
|
+
})
|
|
758
|
+
|
|
759
|
+
proc.on('close', code => {
|
|
760
|
+
if (code === 0) {
|
|
761
|
+
resolve({stdout, stderr})
|
|
762
|
+
} else {
|
|
763
|
+
const error = new Error(`Stylelint exited with code ${code}`) as Error & {stdout: string; stderr: string}
|
|
764
|
+
error.stdout = stdout
|
|
765
|
+
error.stderr = stderr
|
|
766
|
+
reject(error)
|
|
767
|
+
}
|
|
768
|
+
})
|
|
769
|
+
|
|
770
|
+
proc.on('error', reject)
|
|
771
|
+
|
|
772
|
+
proc.stdin.write(css)
|
|
773
|
+
proc.stdin.end()
|
|
774
|
+
})
|
|
708
775
|
}
|
|
709
776
|
|
|
710
777
|
export {
|
|
@@ -724,5 +791,6 @@ export {
|
|
|
724
791
|
GROUP_ALIASES,
|
|
725
792
|
GROUP_LABELS,
|
|
726
793
|
tokenMatchesGroup,
|
|
794
|
+
runStylelint,
|
|
727
795
|
type TokenWithGuidelines,
|
|
728
796
|
}
|