@primer/mcp 1.1.0-rc.fdfbe71e5 → 1.1.1-rc.700f75ee0
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-CnGefk-j.js → server-malh_dny.js} +3 -2
- package/dist/stdio.js +1 -1
- package/package.json +2 -2
- package/src/primer.ts +4 -0
- package/src/server.test.ts +438 -0
- package/src/server.ts +1 -1
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-malh_dny.js";
|
|
2
2
|
export { createServer, server };
|
|
@@ -19,6 +19,7 @@ import functionalTypography from "@primer/primitives/dist/docs/functional/typogr
|
|
|
19
19
|
//#region src/primer.ts
|
|
20
20
|
function idToSlug(id) {
|
|
21
21
|
if (id === "actionbar") return "action-bar";
|
|
22
|
+
if (id === "confirmationdialog") return "confirmation-dialog";
|
|
22
23
|
if (id === "tooltip-v2") return "tooltip";
|
|
23
24
|
if (id === "dialog_v2") return "dialog";
|
|
24
25
|
return id.replaceAll("_", "-");
|
|
@@ -717,7 +718,7 @@ function runStylelint(css) {
|
|
|
717
718
|
}
|
|
718
719
|
//#endregion
|
|
719
720
|
//#region package.json
|
|
720
|
-
var version = "1.1.
|
|
721
|
+
var version = "1.1.1";
|
|
721
722
|
//#endregion
|
|
722
723
|
//#region src/server.ts
|
|
723
724
|
const turndownService = new TurndownService();
|
|
@@ -872,7 +873,7 @@ ${turndownService.turndown(source)}`
|
|
|
872
873
|
type: "text",
|
|
873
874
|
text: `There is no component named \`${name}\` in the @primer/react package. For a full list of components, use the \`get_components\` tool.`
|
|
874
875
|
}] };
|
|
875
|
-
const url = new URL(`/product/components/${match.
|
|
876
|
+
const url = new URL(`/product/components/${match.slug}/guidelines`, "https://primer.style");
|
|
876
877
|
const response = await fetch(url);
|
|
877
878
|
if (!response.ok) {
|
|
878
879
|
if (response.status >= 400 && response.status < 500 || response.status >= 300 && response.status < 400) return { content: [{
|
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.1.
|
|
4
|
+
"version": "1.1.1-rc.700f75ee0",
|
|
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.42.0",
|
|
45
45
|
"cheerio": "^1.0.0",
|
|
46
46
|
"turndown": "^7.2.0",
|
|
47
47
|
"zod": "^4.3.5"
|
package/src/primer.ts
CHANGED
package/src/server.test.ts
CHANGED
|
@@ -2,6 +2,302 @@ import {Client, InMemoryTransport} from '@modelcontextprotocol/client'
|
|
|
2
2
|
import {afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi} from 'vitest'
|
|
3
3
|
import {server} from './server'
|
|
4
4
|
|
|
5
|
+
interface UsageGuidelineRoute {
|
|
6
|
+
name: string
|
|
7
|
+
expectedPath: string
|
|
8
|
+
rawIdPath?: string
|
|
9
|
+
publication: 'published' | 'unpublished'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const usageGuidelineRoutes: ReadonlyArray<UsageGuidelineRoute> = [
|
|
13
|
+
{
|
|
14
|
+
name: 'ActionBar',
|
|
15
|
+
expectedPath: '/product/components/action-bar/guidelines',
|
|
16
|
+
rawIdPath: '/product/components/actionbar/guidelines',
|
|
17
|
+
publication: 'published',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'ActionList',
|
|
21
|
+
expectedPath: '/product/components/action-list/guidelines',
|
|
22
|
+
rawIdPath: '/product/components/action_list/guidelines',
|
|
23
|
+
publication: 'published',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'ActionMenu',
|
|
27
|
+
expectedPath: '/product/components/action-menu/guidelines',
|
|
28
|
+
rawIdPath: '/product/components/action_menu/guidelines',
|
|
29
|
+
publication: 'published',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'AnchoredOverlay',
|
|
33
|
+
expectedPath: '/product/components/anchored-overlay/guidelines',
|
|
34
|
+
rawIdPath: '/product/components/anchored_overlay/guidelines',
|
|
35
|
+
publication: 'unpublished',
|
|
36
|
+
},
|
|
37
|
+
{name: 'Autocomplete', expectedPath: '/product/components/autocomplete/guidelines', publication: 'published'},
|
|
38
|
+
{name: 'Avatar', expectedPath: '/product/components/avatar/guidelines', publication: 'unpublished'},
|
|
39
|
+
{
|
|
40
|
+
name: 'AvatarStack',
|
|
41
|
+
expectedPath: '/product/components/avatar-stack/guidelines',
|
|
42
|
+
rawIdPath: '/product/components/avatar_stack/guidelines',
|
|
43
|
+
publication: 'unpublished',
|
|
44
|
+
},
|
|
45
|
+
{name: 'Banner', expectedPath: '/product/components/banner/guidelines', publication: 'published'},
|
|
46
|
+
{name: 'Blankslate', expectedPath: '/product/components/blankslate/guidelines', publication: 'published'},
|
|
47
|
+
{
|
|
48
|
+
name: 'BranchName',
|
|
49
|
+
expectedPath: '/product/components/branch-name/guidelines',
|
|
50
|
+
rawIdPath: '/product/components/branch_name/guidelines',
|
|
51
|
+
publication: 'unpublished',
|
|
52
|
+
},
|
|
53
|
+
{name: 'Breadcrumbs', expectedPath: '/product/components/breadcrumbs/guidelines', publication: 'published'},
|
|
54
|
+
{name: 'Button', expectedPath: '/product/components/button/guidelines', publication: 'published'},
|
|
55
|
+
{
|
|
56
|
+
name: 'IconButton',
|
|
57
|
+
expectedPath: '/product/components/icon-button/guidelines',
|
|
58
|
+
rawIdPath: '/product/components/icon_button/guidelines',
|
|
59
|
+
publication: 'published',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'ButtonGroup',
|
|
63
|
+
expectedPath: '/product/components/button-group/guidelines',
|
|
64
|
+
rawIdPath: '/product/components/button_group/guidelines',
|
|
65
|
+
publication: 'published',
|
|
66
|
+
},
|
|
67
|
+
{name: 'Card', expectedPath: '/product/components/card/guidelines', publication: 'unpublished'},
|
|
68
|
+
{name: 'Checkbox', expectedPath: '/product/components/checkbox/guidelines', publication: 'unpublished'},
|
|
69
|
+
{
|
|
70
|
+
name: 'CheckboxGroup',
|
|
71
|
+
expectedPath: '/product/components/checkbox-group/guidelines',
|
|
72
|
+
rawIdPath: '/product/components/checkbox_group/guidelines',
|
|
73
|
+
publication: 'published',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'CircleBadge',
|
|
77
|
+
expectedPath: '/product/components/circle-badge/guidelines',
|
|
78
|
+
rawIdPath: '/product/components/circle_badge/guidelines',
|
|
79
|
+
publication: 'unpublished',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'ConfirmationDialog',
|
|
83
|
+
expectedPath: '/product/components/confirmation-dialog/guidelines',
|
|
84
|
+
rawIdPath: '/product/components/confirmationdialog/guidelines',
|
|
85
|
+
publication: 'published',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: 'CounterLabel',
|
|
89
|
+
expectedPath: '/product/components/counter-label/guidelines',
|
|
90
|
+
rawIdPath: '/product/components/counter_label/guidelines',
|
|
91
|
+
publication: 'unpublished',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'DataTable',
|
|
95
|
+
expectedPath: '/product/components/data-table/guidelines',
|
|
96
|
+
rawIdPath: '/product/components/data_table/guidelines',
|
|
97
|
+
publication: 'published',
|
|
98
|
+
},
|
|
99
|
+
{name: 'Details', expectedPath: '/product/components/details/guidelines', publication: 'unpublished'},
|
|
100
|
+
{
|
|
101
|
+
name: 'Dialog',
|
|
102
|
+
expectedPath: '/product/components/dialog/guidelines',
|
|
103
|
+
rawIdPath: '/product/components/dialog_v2/guidelines',
|
|
104
|
+
publication: 'published',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'FeatureFlags',
|
|
108
|
+
expectedPath: '/product/components/feature-flags/guidelines',
|
|
109
|
+
rawIdPath: '/product/components/feature_flags/guidelines',
|
|
110
|
+
publication: 'unpublished',
|
|
111
|
+
},
|
|
112
|
+
{name: 'Flash', expectedPath: '/product/components/flash/guidelines', publication: 'unpublished'},
|
|
113
|
+
{
|
|
114
|
+
name: 'FormControl',
|
|
115
|
+
expectedPath: '/product/components/form-control/guidelines',
|
|
116
|
+
rawIdPath: '/product/components/form_control/guidelines',
|
|
117
|
+
publication: 'unpublished',
|
|
118
|
+
},
|
|
119
|
+
{name: 'Header', expectedPath: '/product/components/header/guidelines', publication: 'unpublished'},
|
|
120
|
+
{name: 'Heading', expectedPath: '/product/components/heading/guidelines', publication: 'unpublished'},
|
|
121
|
+
{name: 'Hidden', expectedPath: '/product/components/hidden/guidelines', publication: 'unpublished'},
|
|
122
|
+
{
|
|
123
|
+
name: 'InlineMessage',
|
|
124
|
+
expectedPath: '/product/components/inline-message/guidelines',
|
|
125
|
+
rawIdPath: '/product/components/inline_message/guidelines',
|
|
126
|
+
publication: 'unpublished',
|
|
127
|
+
},
|
|
128
|
+
{name: 'KeybindingHint', expectedPath: '/product/components/KeybindingHint/guidelines', publication: 'unpublished'},
|
|
129
|
+
{name: 'Label', expectedPath: '/product/components/label/guidelines', publication: 'unpublished'},
|
|
130
|
+
{
|
|
131
|
+
name: 'LabelGroup',
|
|
132
|
+
expectedPath: '/product/components/label-group/guidelines',
|
|
133
|
+
rawIdPath: '/product/components/label_group/guidelines',
|
|
134
|
+
publication: 'unpublished',
|
|
135
|
+
},
|
|
136
|
+
{name: 'Link', expectedPath: '/product/components/link/guidelines', publication: 'published'},
|
|
137
|
+
{
|
|
138
|
+
name: 'NavList',
|
|
139
|
+
expectedPath: '/product/components/nav-list/guidelines',
|
|
140
|
+
rawIdPath: '/product/components/nav_list/guidelines',
|
|
141
|
+
publication: 'unpublished',
|
|
142
|
+
},
|
|
143
|
+
{name: 'Octicon', expectedPath: '/product/components/octicon/guidelines', publication: 'unpublished'},
|
|
144
|
+
{name: 'Overlay', expectedPath: '/product/components/overlay/guidelines', publication: 'unpublished'},
|
|
145
|
+
{
|
|
146
|
+
name: 'PageHeader',
|
|
147
|
+
expectedPath: '/product/components/page-header/guidelines',
|
|
148
|
+
rawIdPath: '/product/components/page_header/guidelines',
|
|
149
|
+
publication: 'published',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'PageLayout',
|
|
153
|
+
expectedPath: '/product/components/page-layout/guidelines',
|
|
154
|
+
rawIdPath: '/product/components/page_layout/guidelines',
|
|
155
|
+
publication: 'unpublished',
|
|
156
|
+
},
|
|
157
|
+
{name: 'Pagehead', expectedPath: '/product/components/pagehead/guidelines', publication: 'unpublished'},
|
|
158
|
+
{name: 'Pagination', expectedPath: '/product/components/pagination/guidelines', publication: 'published'},
|
|
159
|
+
{name: 'Popover', expectedPath: '/product/components/popover/guidelines', publication: 'published'},
|
|
160
|
+
{name: 'Portal', expectedPath: '/product/components/portal/guidelines', publication: 'unpublished'},
|
|
161
|
+
{
|
|
162
|
+
name: 'ProgressBar',
|
|
163
|
+
expectedPath: '/product/components/progress-bar/guidelines',
|
|
164
|
+
rawIdPath: '/product/components/progress_bar/guidelines',
|
|
165
|
+
publication: 'unpublished',
|
|
166
|
+
},
|
|
167
|
+
{name: 'Radio', expectedPath: '/product/components/radio/guidelines', publication: 'unpublished'},
|
|
168
|
+
{
|
|
169
|
+
name: 'RadioGroup',
|
|
170
|
+
expectedPath: '/product/components/radio-group/guidelines',
|
|
171
|
+
rawIdPath: '/product/components/radio_group/guidelines',
|
|
172
|
+
publication: 'published',
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: 'RelativeTime',
|
|
176
|
+
expectedPath: '/product/components/relative-time/guidelines',
|
|
177
|
+
rawIdPath: '/product/components/relative_time/guidelines',
|
|
178
|
+
publication: 'published',
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: 'SegmentedControl',
|
|
182
|
+
expectedPath: '/product/components/segmented-control/guidelines',
|
|
183
|
+
rawIdPath: '/product/components/segmented_control/guidelines',
|
|
184
|
+
publication: 'published',
|
|
185
|
+
},
|
|
186
|
+
{name: 'Select', expectedPath: '/product/components/select/guidelines', publication: 'published'},
|
|
187
|
+
{
|
|
188
|
+
name: 'SelectPanel',
|
|
189
|
+
expectedPath: '/product/components/select-panel/guidelines',
|
|
190
|
+
rawIdPath: '/product/components/select_panel/guidelines',
|
|
191
|
+
publication: 'published',
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: 'SkeletonBox',
|
|
195
|
+
expectedPath: '/product/components/skeleton-box/guidelines',
|
|
196
|
+
rawIdPath: '/product/components/skeleton_box/guidelines',
|
|
197
|
+
publication: 'unpublished',
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
name: 'SkeletonAvatar',
|
|
201
|
+
expectedPath: '/product/components/skeleton-avatar/guidelines',
|
|
202
|
+
rawIdPath: '/product/components/skeleton_avatar/guidelines',
|
|
203
|
+
publication: 'unpublished',
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
name: 'SkeletonText',
|
|
207
|
+
expectedPath: '/product/components/skeleton-text/guidelines',
|
|
208
|
+
rawIdPath: '/product/components/skeleton_text/guidelines',
|
|
209
|
+
publication: 'unpublished',
|
|
210
|
+
},
|
|
211
|
+
{name: 'Spinner', expectedPath: '/product/components/spinner/guidelines', publication: 'unpublished'},
|
|
212
|
+
{
|
|
213
|
+
name: 'SplitPageLayout',
|
|
214
|
+
expectedPath: '/product/components/split-page-layout/guidelines',
|
|
215
|
+
rawIdPath: '/product/components/split_page_layout/guidelines',
|
|
216
|
+
publication: 'unpublished',
|
|
217
|
+
},
|
|
218
|
+
{name: 'Stack', expectedPath: '/product/components/stack/guidelines', publication: 'unpublished'},
|
|
219
|
+
{
|
|
220
|
+
name: 'StateLabel',
|
|
221
|
+
expectedPath: '/product/components/state-label/guidelines',
|
|
222
|
+
rawIdPath: '/product/components/state_label/guidelines',
|
|
223
|
+
publication: 'unpublished',
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: 'SubNav',
|
|
227
|
+
expectedPath: '/product/components/sub-nav/guidelines',
|
|
228
|
+
rawIdPath: '/product/components/sub_nav/guidelines',
|
|
229
|
+
publication: 'unpublished',
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
name: 'TabNav',
|
|
233
|
+
expectedPath: '/product/components/tab-nav/guidelines',
|
|
234
|
+
rawIdPath: '/product/components/tab_nav/guidelines',
|
|
235
|
+
publication: 'unpublished',
|
|
236
|
+
},
|
|
237
|
+
{name: 'Text', expectedPath: '/product/components/text/guidelines', publication: 'unpublished'},
|
|
238
|
+
{
|
|
239
|
+
name: 'TextInput',
|
|
240
|
+
expectedPath: '/product/components/text-input/guidelines',
|
|
241
|
+
rawIdPath: '/product/components/text_input/guidelines',
|
|
242
|
+
publication: 'published',
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
name: 'TextInputWithTokens',
|
|
246
|
+
expectedPath: '/product/components/text-input-with-tokens/guidelines',
|
|
247
|
+
rawIdPath: '/product/components/text_input_with_tokens/guidelines',
|
|
248
|
+
publication: 'published',
|
|
249
|
+
},
|
|
250
|
+
{name: 'Textarea', expectedPath: '/product/components/textarea/guidelines', publication: 'published'},
|
|
251
|
+
{name: 'Timeline', expectedPath: '/product/components/timeline/guidelines', publication: 'unpublished'},
|
|
252
|
+
{
|
|
253
|
+
name: 'ToggleSwitch',
|
|
254
|
+
expectedPath: '/product/components/toggle-switch/guidelines',
|
|
255
|
+
rawIdPath: '/product/components/toggle_switch/guidelines',
|
|
256
|
+
publication: 'published',
|
|
257
|
+
},
|
|
258
|
+
{name: 'Token', expectedPath: '/product/components/token/guidelines', publication: 'published'},
|
|
259
|
+
{name: 'Tooltip', expectedPath: '/product/components/tooltip/guidelines', publication: 'published'},
|
|
260
|
+
{
|
|
261
|
+
name: 'TopicTag',
|
|
262
|
+
expectedPath: '/product/components/topic-tag/guidelines',
|
|
263
|
+
rawIdPath: '/product/components/topic_tag/guidelines',
|
|
264
|
+
publication: 'unpublished',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: 'TreeView',
|
|
268
|
+
expectedPath: '/product/components/tree-view/guidelines',
|
|
269
|
+
rawIdPath: '/product/components/tree_view/guidelines',
|
|
270
|
+
publication: 'published',
|
|
271
|
+
},
|
|
272
|
+
{name: 'Truncate', expectedPath: '/product/components/truncate/guidelines', publication: 'unpublished'},
|
|
273
|
+
{
|
|
274
|
+
name: 'UnderlineNav',
|
|
275
|
+
expectedPath: '/product/components/underline-nav/guidelines',
|
|
276
|
+
rawIdPath: '/product/components/underline_nav/guidelines',
|
|
277
|
+
publication: 'published',
|
|
278
|
+
},
|
|
279
|
+
{name: 'AriaAlert', expectedPath: '/product/components/aria-alert/guidelines', publication: 'unpublished'},
|
|
280
|
+
{name: 'AriaStatus', expectedPath: '/product/components/aria-status/guidelines', publication: 'unpublished'},
|
|
281
|
+
{
|
|
282
|
+
name: 'FilteredSearch',
|
|
283
|
+
expectedPath: '/product/components/filtered-search/guidelines',
|
|
284
|
+
rawIdPath: '/product/components/filtered_search/guidelines',
|
|
285
|
+
publication: 'unpublished',
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
name: 'IssueLabel',
|
|
289
|
+
expectedPath: '/product/components/issue-label/guidelines',
|
|
290
|
+
rawIdPath: '/product/components/issue_label/guidelines',
|
|
291
|
+
publication: 'unpublished',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
name: 'UnderlinePanels',
|
|
295
|
+
expectedPath: '/product/components/underline-panels/guidelines',
|
|
296
|
+
rawIdPath: '/product/components/underline_panels/guidelines',
|
|
297
|
+
publication: 'published',
|
|
298
|
+
},
|
|
299
|
+
]
|
|
300
|
+
|
|
5
301
|
describe('MCP server', () => {
|
|
6
302
|
const client = new Client(
|
|
7
303
|
{name: 'mcp-server-test', version: '1.0.0'},
|
|
@@ -50,6 +346,12 @@ describe('MCP server', () => {
|
|
|
50
346
|
})
|
|
51
347
|
}
|
|
52
348
|
|
|
349
|
+
const responseWithFinalUrl = (body: string, url: string) => {
|
|
350
|
+
const response = new Response(body)
|
|
351
|
+
Object.defineProperty(response, 'url', {value: url})
|
|
352
|
+
return response
|
|
353
|
+
}
|
|
354
|
+
|
|
53
355
|
it('documents which component tool to use for each supported count', async () => {
|
|
54
356
|
const {tools} = await client.listTools()
|
|
55
357
|
const getComponent = tools.find(tool => tool.name === 'get_component')
|
|
@@ -110,6 +412,142 @@ describe('MCP server', () => {
|
|
|
110
412
|
)
|
|
111
413
|
})
|
|
112
414
|
|
|
415
|
+
it('covers every advertised component with a named usage-guidelines routing case', async () => {
|
|
416
|
+
const result = await client.callTool({name: 'list_components'})
|
|
417
|
+
expect(result.isError).not.toBe(true)
|
|
418
|
+
expect(result.content).toHaveLength(1)
|
|
419
|
+
|
|
420
|
+
const [content] = result.content
|
|
421
|
+
if (content.type !== 'text') {
|
|
422
|
+
throw new Error('Expected list_components to return a text component list')
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const names = content.text
|
|
426
|
+
.split('\n')
|
|
427
|
+
.filter(line => line.startsWith('- '))
|
|
428
|
+
.map(line => line.slice(2))
|
|
429
|
+
|
|
430
|
+
expect([...new Set(names)].sort()).toEqual(usageGuidelineRoutes.map(route => route.name).sort())
|
|
431
|
+
expect(names.filter((name, index) => names.indexOf(name) !== index).sort()).toEqual([
|
|
432
|
+
'Dialog',
|
|
433
|
+
'SelectPanel',
|
|
434
|
+
'Tooltip',
|
|
435
|
+
'UnderlineNav',
|
|
436
|
+
])
|
|
437
|
+
expect(fetch).not.toHaveBeenCalled()
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
it.each(usageGuidelineRoutes)(
|
|
441
|
+
'$name usage request follows its expected route (guidelines $publication)',
|
|
442
|
+
async ({name, expectedPath, rawIdPath}) => {
|
|
443
|
+
const expectedUrl = `https://primer.style${expectedPath}`
|
|
444
|
+
const rawIdUrl = rawIdPath ? `https://primer.style${rawIdPath}` : undefined
|
|
445
|
+
const responseBody = `synthetic-${name.toLowerCase()}-usage-body`
|
|
446
|
+
const homepageBody = `homepage-body-not-${name.toLowerCase()}-guidance`
|
|
447
|
+
const fetchMock = vi.mocked(fetch)
|
|
448
|
+
fetchMock.mockImplementation(async input => {
|
|
449
|
+
const url = input.toString()
|
|
450
|
+
|
|
451
|
+
if (url === expectedUrl) {
|
|
452
|
+
return responseWithFinalUrl(`<main><p>${responseBody}</p></main>`, expectedUrl)
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (url === rawIdUrl) {
|
|
456
|
+
return responseWithFinalUrl(`<main><nav>${name}</nav><p>${homepageBody}</p></main>`, 'https://primer.style/')
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
throw new Error(`Unexpected fetch request: ${url}`)
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
const result = await client.callTool({
|
|
463
|
+
name: 'get_component_usage_guidelines',
|
|
464
|
+
arguments: {name},
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
expect(result.isError).not.toBe(true)
|
|
468
|
+
expect(result.content).toContainEqual(
|
|
469
|
+
expect.objectContaining({
|
|
470
|
+
type: 'text',
|
|
471
|
+
text: expect.stringContaining(responseBody),
|
|
472
|
+
}),
|
|
473
|
+
)
|
|
474
|
+
expect(result.content).not.toContainEqual(
|
|
475
|
+
expect.objectContaining({
|
|
476
|
+
type: 'text',
|
|
477
|
+
text: expect.stringContaining(homepageBody),
|
|
478
|
+
}),
|
|
479
|
+
)
|
|
480
|
+
expect(fetchMock).toHaveBeenCalledTimes(1)
|
|
481
|
+
expect(fetchMock.mock.calls[0]?.[0].toString()).toBe(expectedUrl)
|
|
482
|
+
},
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
it.each(['ConfirmationDialog', 'confirmationdialog'])(
|
|
486
|
+
'retrieves ConfirmationDialog documentation for %s from its published URL',
|
|
487
|
+
async name => {
|
|
488
|
+
const url = 'https://primer.style/product/components/confirmation-dialog/llms.txt'
|
|
489
|
+
const body = '# ConfirmationDialog\n\nSynthetic component documentation.'
|
|
490
|
+
const fetchMock = vi.mocked(fetch)
|
|
491
|
+
fetchMock.mockImplementation(async input => {
|
|
492
|
+
if (input.toString() === url) {
|
|
493
|
+
return new Response(body)
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (input.toString() === 'https://primer.style/product/components/confirmationdialog/llms.txt') {
|
|
497
|
+
return new Response(null, {status: 404})
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
throw new Error(`Unexpected fetch request: ${input}`)
|
|
501
|
+
})
|
|
502
|
+
|
|
503
|
+
const result = await client.callTool({name: 'get_component', arguments: {name}})
|
|
504
|
+
|
|
505
|
+
expect(result.isError).not.toBe(true)
|
|
506
|
+
expect(result.content).toEqual([{type: 'text', text: body}])
|
|
507
|
+
expect(fetchMock).toHaveBeenCalledTimes(1)
|
|
508
|
+
expect(fetchMock.mock.calls[0]?.[0].toString()).toBe(url)
|
|
509
|
+
},
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
it.each(['ConfirmationDialog', 'confirmationdialog'])(
|
|
513
|
+
'retrieves ConfirmationDialog documentation in a mixed batch for %s from its published URL',
|
|
514
|
+
async name => {
|
|
515
|
+
const confirmationUrl = 'https://primer.style/product/components/confirmation-dialog/llms.txt'
|
|
516
|
+
const buttonUrl = 'https://primer.style/product/components/button/llms.txt'
|
|
517
|
+
const confirmationBody = '# ConfirmationDialog\n\nSynthetic component documentation.'
|
|
518
|
+
const buttonBody = '# Button\n\nSynthetic component documentation.'
|
|
519
|
+
const fetchMock = vi.mocked(fetch)
|
|
520
|
+
fetchMock.mockImplementation(async input => {
|
|
521
|
+
const url = input.toString()
|
|
522
|
+
if (url === confirmationUrl) {
|
|
523
|
+
return new Response(confirmationBody)
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (url === buttonUrl) {
|
|
527
|
+
return new Response(buttonBody)
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (url === 'https://primer.style/product/components/confirmationdialog/llms.txt') {
|
|
531
|
+
return new Response(null, {status: 404})
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
throw new Error(`Unexpected fetch request: ${url}`)
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
const result = await callBatch([name, 'Button'])
|
|
538
|
+
|
|
539
|
+
expect(result.isError).not.toBe(true)
|
|
540
|
+
expect(result.content).toEqual([
|
|
541
|
+
{type: 'text', text: confirmationBody},
|
|
542
|
+
{type: 'text', text: buttonBody},
|
|
543
|
+
])
|
|
544
|
+
expect(fetchMock).toHaveBeenCalledTimes(2)
|
|
545
|
+
expect(fetchMock.mock.calls.map(([input]) => input.toString()).sort()).toEqual(
|
|
546
|
+
[confirmationUrl, buttonUrl].sort(),
|
|
547
|
+
)
|
|
548
|
+
},
|
|
549
|
+
)
|
|
550
|
+
|
|
113
551
|
it('requires between 2 and 10 names', async () => {
|
|
114
552
|
const tooFew = await callBatch(['Button'])
|
|
115
553
|
const tooMany = await callBatch(Array.from({length: 11}, (_, index) => `Component${index}`))
|
package/src/server.ts
CHANGED
|
@@ -314,7 +314,7 @@ ${text}`,
|
|
|
314
314
|
}
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
-
const url = new URL(`/product/components/${match.
|
|
317
|
+
const url = new URL(`/product/components/${match.slug}/guidelines`, 'https://primer.style')
|
|
318
318
|
const response = await fetch(url)
|
|
319
319
|
if (!response.ok) {
|
|
320
320
|
if ((response.status >= 400 && response.status < 500) || (response.status >= 300 && response.status < 400)) {
|