@primer/mcp 1.0.0 → 1.1.0-rc.052483c40
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 +1 -1
- package/dist/{server-MtARfEmu.js → server-CnGefk-j.js} +36 -7
- package/dist/stdio.js +1 -1
- package/package.json +3 -3
- package/src/primer.ts +25 -0
- package/src/server.test.ts +38 -0
- package/src/server.ts +2 -2
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as server, t as createServer } from "./server-
|
|
1
|
+
import { n as server, t as createServer } from "./server-CnGefk-j.js";
|
|
2
2
|
export { createServer, server };
|
|
@@ -40,11 +40,31 @@ const patterns = [
|
|
|
40
40
|
name: "Copy",
|
|
41
41
|
category: "scenario"
|
|
42
42
|
},
|
|
43
|
+
{
|
|
44
|
+
id: "create",
|
|
45
|
+
name: "Create",
|
|
46
|
+
category: "scenario"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "customize",
|
|
50
|
+
name: "Customize",
|
|
51
|
+
category: "scenario"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "delegate",
|
|
55
|
+
name: "Delegate",
|
|
56
|
+
category: "scenario"
|
|
57
|
+
},
|
|
43
58
|
{
|
|
44
59
|
id: "delete",
|
|
45
60
|
name: "Delete",
|
|
46
61
|
category: "scenario"
|
|
47
62
|
},
|
|
63
|
+
{
|
|
64
|
+
id: "edit",
|
|
65
|
+
name: "Edit",
|
|
66
|
+
category: "scenario"
|
|
67
|
+
},
|
|
48
68
|
{
|
|
49
69
|
id: "filter",
|
|
50
70
|
name: "Filter",
|
|
@@ -55,6 +75,11 @@ const patterns = [
|
|
|
55
75
|
name: "Search",
|
|
56
76
|
category: "scenario"
|
|
57
77
|
},
|
|
78
|
+
{
|
|
79
|
+
id: "view",
|
|
80
|
+
name: "View",
|
|
81
|
+
category: "scenario"
|
|
82
|
+
},
|
|
58
83
|
{
|
|
59
84
|
id: "data-visualization",
|
|
60
85
|
name: "Data Visualization",
|
|
@@ -395,10 +420,12 @@ function loadAllTokensWithGuidelines() {
|
|
|
395
420
|
}
|
|
396
421
|
}
|
|
397
422
|
function loadDesignTokensGuide() {
|
|
398
|
-
|
|
423
|
+
const guidePath = createRequire(import.meta.url).resolve("@primer/primitives/DESIGN_TOKENS_GUIDE.md");
|
|
424
|
+
return readFileSync(guidePath, "utf-8");
|
|
399
425
|
}
|
|
400
426
|
function loadDesignTokensSpec() {
|
|
401
|
-
|
|
427
|
+
const specPath = createRequire(import.meta.url).resolve("@primer/primitives/DESIGN_TOKENS_SPEC.md");
|
|
428
|
+
return readFileSync(specPath, "utf-8");
|
|
402
429
|
}
|
|
403
430
|
function getDesignTokenSpecsText(groups) {
|
|
404
431
|
return `
|
|
@@ -690,7 +717,7 @@ function runStylelint(css) {
|
|
|
690
717
|
}
|
|
691
718
|
//#endregion
|
|
692
719
|
//#region package.json
|
|
693
|
-
var version = "1.
|
|
720
|
+
var version = "1.1.0";
|
|
694
721
|
//#endregion
|
|
695
722
|
//#region src/server.ts
|
|
696
723
|
const turndownService = new TurndownService();
|
|
@@ -898,7 +925,7 @@ ${turndownService.turndown(source)}`
|
|
|
898
925
|
}] };
|
|
899
926
|
});
|
|
900
927
|
server.registerTool("list_patterns", {
|
|
901
|
-
description: "List all of the patterns available from Primer React. Scenario patterns describe specific user tasks
|
|
928
|
+
description: "List all of the patterns available from Primer React. Scenario patterns describe specific user tasks. Prefer a scenario pattern when one fits the task, and fall back to the more generic UI patterns otherwise.",
|
|
902
929
|
annotations: { readOnlyHint: true }
|
|
903
930
|
}, async () => {
|
|
904
931
|
const all = listPatterns();
|
|
@@ -918,7 +945,7 @@ ${ui.join("\n")}`
|
|
|
918
945
|
}] };
|
|
919
946
|
});
|
|
920
947
|
server.registerTool("get_pattern", {
|
|
921
|
-
description: "Get a specific pattern by name. Scenario patterns describe specific user tasks
|
|
948
|
+
description: "Get a specific pattern by name. Scenario patterns describe specific user tasks. Prefer a scenario pattern when one fits the task, and fall back to the more generic UI patterns otherwise.",
|
|
922
949
|
inputSchema: z.object({ name: z.string().describe("The name of the pattern to retrieve") }),
|
|
923
950
|
annotations: { readOnlyHint: true }
|
|
924
951
|
}, async ({ name }) => {
|
|
@@ -957,7 +984,8 @@ ${turndownService.turndown(source)}`
|
|
|
957
984
|
let effectiveGroup = resolvedGroup;
|
|
958
985
|
const filteredKeywords = [];
|
|
959
986
|
for (const kw of rawKeywords) {
|
|
960
|
-
const
|
|
987
|
+
const normalized = kw.replace(/\s+/g, "");
|
|
988
|
+
const aliasMatch = GROUP_ALIASES[normalized];
|
|
961
989
|
if (aliasMatch && !effectiveGroup) effectiveGroup = aliasMatch;
|
|
962
990
|
else filteredKeywords.push(kw);
|
|
963
991
|
}
|
|
@@ -1002,7 +1030,8 @@ ${validGroups}
|
|
|
1002
1030
|
annotations: { readOnlyHint: true }
|
|
1003
1031
|
}, async ({ groups }) => {
|
|
1004
1032
|
const resolvedGroups = groups.map((g) => {
|
|
1005
|
-
|
|
1033
|
+
const normalized = g.toLowerCase().replace(/\s+/g, "");
|
|
1034
|
+
return GROUP_ALIASES[normalized] || g;
|
|
1006
1035
|
});
|
|
1007
1036
|
const matched = allTokensWithGuidelines.filter((token) => resolvedGroups.some((rg) => tokenMatchesGroup(token, rg)));
|
|
1008
1037
|
if (matched.length === 0) {
|
package/dist/stdio.js
CHANGED
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": "1.0.
|
|
4
|
+
"version": "1.1.0-rc.052483c40",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"mcp": "./bin/mcp.js"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
42
42
|
"@primer/octicons": "^19.15.5",
|
|
43
43
|
"@primer/primitives": "10.x || 11.x",
|
|
44
|
-
"@primer/react": "^38.
|
|
44
|
+
"@primer/react": "^38.38.0",
|
|
45
45
|
"cheerio": "^1.0.0",
|
|
46
46
|
"turndown": "^7.2.0",
|
|
47
47
|
"zod": "^4.3.5"
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@primer/vitest-config": "^0.0.0",
|
|
52
52
|
"@types/turndown": "^5.0.5",
|
|
53
53
|
"rimraf": "^6.0.1",
|
|
54
|
-
"rolldown": "^1.2.
|
|
54
|
+
"rolldown": "^1.2.6",
|
|
55
55
|
"rolldown-plugin-dts": "^0.26.0",
|
|
56
56
|
"typescript": "^6.0.3"
|
|
57
57
|
}
|
package/src/primer.ts
CHANGED
|
@@ -51,11 +51,31 @@ const patterns: Array<Pattern> = [
|
|
|
51
51
|
name: 'Copy',
|
|
52
52
|
category: 'scenario',
|
|
53
53
|
},
|
|
54
|
+
{
|
|
55
|
+
id: 'create',
|
|
56
|
+
name: 'Create',
|
|
57
|
+
category: 'scenario',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'customize',
|
|
61
|
+
name: 'Customize',
|
|
62
|
+
category: 'scenario',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: 'delegate',
|
|
66
|
+
name: 'Delegate',
|
|
67
|
+
category: 'scenario',
|
|
68
|
+
},
|
|
54
69
|
{
|
|
55
70
|
id: 'delete',
|
|
56
71
|
name: 'Delete',
|
|
57
72
|
category: 'scenario',
|
|
58
73
|
},
|
|
74
|
+
{
|
|
75
|
+
id: 'edit',
|
|
76
|
+
name: 'Edit',
|
|
77
|
+
category: 'scenario',
|
|
78
|
+
},
|
|
59
79
|
{
|
|
60
80
|
id: 'filter',
|
|
61
81
|
name: 'Filter',
|
|
@@ -66,6 +86,11 @@ const patterns: Array<Pattern> = [
|
|
|
66
86
|
name: 'Search',
|
|
67
87
|
category: 'scenario',
|
|
68
88
|
},
|
|
89
|
+
{
|
|
90
|
+
id: 'view',
|
|
91
|
+
name: 'View',
|
|
92
|
+
category: 'scenario',
|
|
93
|
+
},
|
|
69
94
|
{
|
|
70
95
|
id: 'data-visualization',
|
|
71
96
|
name: 'Data Visualization',
|
package/src/server.test.ts
CHANGED
|
@@ -72,6 +72,44 @@ describe('MCP server', () => {
|
|
|
72
72
|
)
|
|
73
73
|
})
|
|
74
74
|
|
|
75
|
+
it('lists every published scenario pattern', async () => {
|
|
76
|
+
const result = await client.callTool({name: 'list_patterns'})
|
|
77
|
+
const scenarioPatterns = ['Copy', 'Create', 'Customize', 'Delegate', 'Delete', 'Edit', 'Filter', 'Search', 'View']
|
|
78
|
+
|
|
79
|
+
expect(result.content).toContainEqual(
|
|
80
|
+
expect.objectContaining({
|
|
81
|
+
type: 'text',
|
|
82
|
+
text: expect.stringContaining(
|
|
83
|
+
`## Scenario patterns\n\n${scenarioPatterns.map(name => `- ${name}`).join('\n')}`,
|
|
84
|
+
),
|
|
85
|
+
}),
|
|
86
|
+
)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it.each([
|
|
90
|
+
{name: 'Create', id: 'create'},
|
|
91
|
+
{name: 'Customize', id: 'customize'},
|
|
92
|
+
{name: 'Delegate', id: 'delegate'},
|
|
93
|
+
{name: 'Edit', id: 'edit'},
|
|
94
|
+
{name: 'View', id: 'view'},
|
|
95
|
+
])('retrieves the $name scenario pattern from its published URL', async ({name, id}) => {
|
|
96
|
+
vi.mocked(fetch).mockResolvedValue(new Response(`<main><h1>${name}</h1></main>`))
|
|
97
|
+
|
|
98
|
+
const result = await client.callTool({
|
|
99
|
+
name: 'get_pattern',
|
|
100
|
+
arguments: {name},
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
expect(fetch).toHaveBeenCalledTimes(1)
|
|
104
|
+
expect(vi.mocked(fetch).mock.calls[0]?.[0].toString()).toBe(`https://primer.style/product/scenario-patterns/${id}`)
|
|
105
|
+
expect(result.content).toContainEqual(
|
|
106
|
+
expect.objectContaining({
|
|
107
|
+
type: 'text',
|
|
108
|
+
text: expect.stringContaining(`Here are the guidelines for the \`${name}\` pattern for Primer:`),
|
|
109
|
+
}),
|
|
110
|
+
)
|
|
111
|
+
})
|
|
112
|
+
|
|
75
113
|
it('requires between 2 and 10 names', async () => {
|
|
76
114
|
const tooFew = await callBatch(['Button'])
|
|
77
115
|
const tooMany = await callBatch(Array.from({length: 11}, (_, index) => `Component${index}`))
|
package/src/server.ts
CHANGED
|
@@ -441,7 +441,7 @@ ${text}`,
|
|
|
441
441
|
'list_patterns',
|
|
442
442
|
{
|
|
443
443
|
description:
|
|
444
|
-
'List all of the patterns available from Primer React. Scenario patterns describe specific user tasks
|
|
444
|
+
'List all of the patterns available from Primer React. Scenario patterns describe specific user tasks. Prefer a scenario pattern when one fits the task, and fall back to the more generic UI patterns otherwise.',
|
|
445
445
|
annotations: {readOnlyHint: true},
|
|
446
446
|
},
|
|
447
447
|
async () => {
|
|
@@ -471,7 +471,7 @@ ${ui.join('\n')}`,
|
|
|
471
471
|
'get_pattern',
|
|
472
472
|
{
|
|
473
473
|
description:
|
|
474
|
-
'Get a specific pattern by name. Scenario patterns describe specific user tasks
|
|
474
|
+
'Get a specific pattern by name. Scenario patterns describe specific user tasks. Prefer a scenario pattern when one fits the task, and fall back to the more generic UI patterns otherwise.',
|
|
475
475
|
inputSchema: z.object({
|
|
476
476
|
name: z.string().describe('The name of the pattern to retrieve'),
|
|
477
477
|
}),
|