@primer/mcp 0.3.3 → 0.4.0-rc.4d4be073a

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
@@ -1,4 +1,4 @@
1
- export { s as server } from './server-C2QaEv-c.js';
1
+ export { s as server } from './server-CkSoZOVD.js';
2
2
  import '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import 'cheerio';
4
4
  import 'zod';
@@ -40,36 +40,63 @@ const components = Object.entries(componentsMetadata.components).map(([id, compo
40
40
  function listComponents() {
41
41
  return components;
42
42
  }
43
+ // Scenario patterns are listed first so name resolution favours them over UI patterns.
43
44
  const patterns = [{
45
+ id: 'copy',
46
+ name: 'Copy',
47
+ category: 'scenario'
48
+ }, {
49
+ id: 'delete',
50
+ name: 'Delete',
51
+ category: 'scenario'
52
+ }, {
53
+ id: 'filter',
54
+ name: 'Filter',
55
+ category: 'scenario'
56
+ }, {
57
+ id: 'search',
58
+ name: 'Search',
59
+ category: 'scenario'
60
+ }, {
44
61
  id: 'data-visualization',
45
- name: 'Data Visualization'
62
+ name: 'Data Visualization',
63
+ category: 'ui'
46
64
  }, {
47
65
  id: 'degraded-experiences',
48
- name: 'Degraded Experiences'
66
+ name: 'Degraded Experiences',
67
+ category: 'ui'
49
68
  }, {
50
69
  id: 'empty-states',
51
- name: 'Empty States'
70
+ name: 'Empty States',
71
+ category: 'ui'
52
72
  }, {
53
73
  id: 'feature-onboarding',
54
- name: 'Feature Onboarding'
74
+ name: 'Feature Onboarding',
75
+ category: 'ui'
55
76
  }, {
56
77
  id: 'forms',
57
- name: 'Forms'
78
+ name: 'Forms',
79
+ category: 'ui'
58
80
  }, {
59
81
  id: 'loading',
60
- name: 'Loading'
82
+ name: 'Loading',
83
+ category: 'ui'
61
84
  }, {
62
85
  id: 'navigation',
63
- name: 'Navigation'
86
+ name: 'Navigation',
87
+ category: 'ui'
64
88
  }, {
65
89
  id: 'notification-messaging',
66
- name: 'Notification message'
90
+ name: 'Notification message',
91
+ category: 'ui'
67
92
  }, {
68
93
  id: 'progressive-disclosure',
69
- name: 'Progressive disclosure'
94
+ name: 'Progressive disclosure',
95
+ category: 'ui'
70
96
  }, {
71
97
  id: 'saving',
72
- name: 'Saving'
98
+ name: 'Saving',
99
+ category: 'ui'
73
100
  }];
74
101
  function listPatterns() {
75
102
  return patterns;
@@ -736,7 +763,7 @@ function runStylelint(css) {
736
763
  });
737
764
  }
738
765
 
739
- var version = "0.3.3";
766
+ var version = "0.4.0";
740
767
  var packageJson = {
741
768
  version: version};
742
769
 
@@ -753,7 +780,10 @@ const allTokensWithGuidelines = loadAllTokensWithGuidelines();
753
780
  // Project setup
754
781
  // -----------------------------------------------------------------------------
755
782
  server.registerTool('init', {
756
- description: 'Setup or create a project that includes Primer React'
783
+ description: 'Setup or create a project that includes Primer React',
784
+ annotations: {
785
+ readOnlyHint: true
786
+ }
757
787
  }, async () => {
758
788
  const url = new URL(`/product/getting-started/react`, 'https://primer.style');
759
789
  const response = await fetch(url);
@@ -798,7 +828,10 @@ ${text}
798
828
  // Components
799
829
  // -----------------------------------------------------------------------------
800
830
  server.registerTool('list_components', {
801
- description: 'List all of the components available from Primer React'
831
+ description: 'List all of the components available from Primer React',
832
+ annotations: {
833
+ readOnlyHint: true
834
+ }
802
835
  }, async () => {
803
836
  const components = listComponents().map(component => {
804
837
  return `- ${component.name}`;
@@ -818,6 +851,9 @@ server.registerTool('get_component', {
818
851
  description: 'Retrieve documentation and usage details for a specific React component from the @primer/react package by its name. This tool provides the official Primer documentation for any listed component, making it easy to inspect, reuse, or integrate components in your project.',
819
852
  inputSchema: {
820
853
  name: z.string().describe('The name of the component to retrieve')
854
+ },
855
+ annotations: {
856
+ readOnlyHint: true
821
857
  }
822
858
  }, async ({
823
859
  name
@@ -858,6 +894,9 @@ server.registerTool('get_component_examples', {
858
894
  description: 'Get examples for how to use a component from Primer React',
859
895
  inputSchema: {
860
896
  name: z.string().describe('The name of the component to retrieve')
897
+ },
898
+ annotations: {
899
+ readOnlyHint: true
861
900
  }
862
901
  }, async ({
863
902
  name
@@ -906,6 +945,9 @@ server.registerTool('get_component_usage_guidelines', {
906
945
  description: 'Get usage information for how to use a component from Primer',
907
946
  inputSchema: {
908
947
  name: z.string().describe('The name of the component to retrieve')
948
+ },
949
+ annotations: {
950
+ readOnlyHint: true
909
951
  }
910
952
  }, async ({
911
953
  name
@@ -962,6 +1004,9 @@ server.registerTool('get_component_accessibility_guidelines', {
962
1004
  description: 'Retrieve accessibility guidelines and best practices for a specific component from the @primer/react package by its name. Use this tool to get official accessibility recommendations, usage tips, and requirements to ensure your UI components are inclusive and meet accessibility standards.',
963
1005
  inputSchema: {
964
1006
  name: z.string().describe('The name of the component to retrieve')
1007
+ },
1008
+ annotations: {
1009
+ readOnlyHint: true
965
1010
  }
966
1011
  }, async ({
967
1012
  name
@@ -1019,32 +1064,43 @@ ${text}`
1019
1064
  // Patterns
1020
1065
  // -----------------------------------------------------------------------------
1021
1066
  server.registerTool('list_patterns', {
1022
- description: 'List all of the patterns available from Primer React'
1067
+ description: 'List all of the patterns available from Primer React. Scenario patterns describe specific user tasks (copy, delete, filter, search). Prefer a scenario pattern when one fits the task, and fall back to the more generic UI patterns otherwise.',
1068
+ annotations: {
1069
+ readOnlyHint: true
1070
+ }
1023
1071
  }, async () => {
1024
- const patterns = listPatterns().map(pattern => {
1025
- return `- ${pattern.name}`;
1026
- });
1072
+ const all = listPatterns();
1073
+ const scenario = all.filter(pattern => pattern.category === 'scenario').map(pattern => `- ${pattern.name}`);
1074
+ const ui = all.filter(pattern => pattern.category === 'ui').map(pattern => `- ${pattern.name}`);
1027
1075
  return {
1028
1076
  content: [{
1029
1077
  type: 'text',
1030
- text: `The following patterns are available in the @primer/react in TypeScript projects:
1078
+ text: `The following patterns are available from \`@primer/react\` for use in TypeScript projects. Scenario patterns describe specific user tasks. Prefer a scenario pattern when one fits the task, and fall back to the UI patterns otherwise.
1079
+
1080
+ ## Scenario patterns
1081
+
1082
+ ${scenario.join('\n')}
1031
1083
 
1032
- ${patterns.join('\n')}`
1084
+ ## UI patterns
1085
+
1086
+ ${ui.join('\n')}`
1033
1087
  }]
1034
1088
  };
1035
1089
  });
1036
1090
  server.registerTool('get_pattern', {
1037
- description: 'Get a specific pattern by name',
1091
+ description: 'Get a specific pattern by name. Scenario patterns describe specific user tasks (copy, delete, filter, search). Prefer a scenario pattern when one fits the task, and fall back to the more generic UI patterns otherwise.',
1038
1092
  inputSchema: {
1039
1093
  name: z.string().describe('The name of the pattern to retrieve')
1094
+ },
1095
+ annotations: {
1096
+ readOnlyHint: true
1040
1097
  }
1041
1098
  }, async ({
1042
1099
  name
1043
1100
  }) => {
1044
1101
  const patterns = listPatterns();
1045
- const match = patterns.find(pattern => {
1046
- return pattern.name === name;
1047
- });
1102
+ // Resolve scenario patterns first so a name clash favours the scenario pattern.
1103
+ const match = patterns.find(pattern => pattern.category === 'scenario' && pattern.name === name) ?? patterns.find(pattern => pattern.name === name);
1048
1104
  if (!match) {
1049
1105
  return {
1050
1106
  content: [{
@@ -1053,7 +1109,8 @@ server.registerTool('get_pattern', {
1053
1109
  }]
1054
1110
  };
1055
1111
  }
1056
- const url = new URL(`/product/ui-patterns/${match.id}`, 'https://primer.style');
1112
+ const basePath = match.category === 'scenario' ? 'scenario-patterns' : 'ui-patterns';
1113
+ const url = new URL(`/product/${basePath}/${match.id}`, 'https://primer.style');
1057
1114
  const response = await fetch(url);
1058
1115
  if (!response.ok) {
1059
1116
  throw new Error(`Failed to fetch ${url} - ${response.statusText}`);
@@ -1091,6 +1148,9 @@ server.registerTool('find_tokens', {
1091
1148
  query: z.string().optional().default('').describe('Search keywords (e.g., "danger border", "success background")'),
1092
1149
  group: z.string().optional().describe('Filter by group (e.g., "fgColor", "border")'),
1093
1150
  limit: z.number().int().min(1).max(100).optional().default(15).describe('Maximum results to return to stay within context limits')
1151
+ },
1152
+ annotations: {
1153
+ readOnlyHint: true
1094
1154
  }
1095
1155
  }, async ({
1096
1156
  query,
@@ -1172,6 +1232,9 @@ server.registerTool('get_token_group_bundle', {
1172
1232
  description: "PREFERRED FOR COMPONENTS. Fetch all tokens for complex UI (e.g., Dialogs, Cards) in one call by providing an array of groups like ['overlay', 'shadow']. Use this instead of multiple find_tokens calls to save context.",
1173
1233
  inputSchema: {
1174
1234
  groups: z.array(z.string()).describe('Array of group names (e.g., ["overlay", "shadow", "focus"])')
1235
+ },
1236
+ annotations: {
1237
+ readOnlyHint: true
1175
1238
  }
1176
1239
  }, async ({
1177
1240
  groups
@@ -1206,7 +1269,10 @@ server.registerTool('get_token_group_bundle', {
1206
1269
  };
1207
1270
  });
1208
1271
  server.registerTool('get_design_token_specs', {
1209
- description: 'CRITICAL: CALL THIS FIRST. Provides the logic matrix and the list of valid group names. You cannot search accurately without this map.'
1272
+ description: 'CRITICAL: CALL THIS FIRST. Provides the logic matrix and the list of valid group names. You cannot search accurately without this map.',
1273
+ annotations: {
1274
+ readOnlyHint: true
1275
+ }
1210
1276
  }, async () => {
1211
1277
  const groups = listTokenGroups();
1212
1278
  const customRules = getDesignTokenSpecsText(groups);
@@ -1225,7 +1291,10 @@ server.registerTool('get_design_token_specs', {
1225
1291
  };
1226
1292
  });
1227
1293
  server.registerTool('get_token_usage_patterns', {
1228
- description: 'Provides "Golden Example" CSS for core patterns: Button (Interactions) and Stack (Layout). Use this to understand how to apply the Logic Matrix, Motion, and Spacing scales.'
1294
+ description: 'Provides "Golden Example" CSS for core patterns: Button (Interactions) and Stack (Layout). Use this to understand how to apply the Logic Matrix, Motion, and Spacing scales.',
1295
+ annotations: {
1296
+ readOnlyHint: true
1297
+ }
1229
1298
  }, async () => {
1230
1299
  const customPatterns = getTokenUsagePatternsText();
1231
1300
  let text;
@@ -1251,6 +1320,9 @@ server.registerTool('lint_css', {
1251
1320
  description: 'REQUIRED FINAL STEP. Use this to validate your CSS. You cannot complete a task involving CSS without a successful run of this tool.',
1252
1321
  inputSchema: {
1253
1322
  css: z.string()
1323
+ },
1324
+ annotations: {
1325
+ readOnlyHint: true
1254
1326
  }
1255
1327
  }, async ({
1256
1328
  css
@@ -1282,7 +1354,10 @@ server.registerTool('lint_css', {
1282
1354
  // Foundations
1283
1355
  // -----------------------------------------------------------------------------
1284
1356
  server.registerTool('get_color_usage', {
1285
- description: 'Get the guidelines for how to apply color to a user interface'
1357
+ description: 'Get the guidelines for how to apply color to a user interface',
1358
+ annotations: {
1359
+ readOnlyHint: true
1360
+ }
1286
1361
  }, async () => {
1287
1362
  const url = new URL(`/product/getting-started/foundations/color-usage`, 'https://primer.style');
1288
1363
  const response = await fetch(url);
@@ -1311,7 +1386,10 @@ server.registerTool('get_color_usage', {
1311
1386
  };
1312
1387
  });
1313
1388
  server.registerTool('get_typography_usage', {
1314
- description: 'Get the guidelines for how to apply typography to a user interface'
1389
+ description: 'Get the guidelines for how to apply typography to a user interface',
1390
+ annotations: {
1391
+ readOnlyHint: true
1392
+ }
1315
1393
  }, async () => {
1316
1394
  const url = new URL(`/product/getting-started/foundations/typography`, 'https://primer.style');
1317
1395
  const response = await fetch(url);
@@ -1344,7 +1422,10 @@ server.registerTool('get_typography_usage', {
1344
1422
  // Icons
1345
1423
  // -----------------------------------------------------------------------------
1346
1424
  server.registerTool('list_icons', {
1347
- description: 'List all of the icons (octicons) available from Primer Octicons React'
1425
+ description: 'List all of the icons (octicons) available from Primer Octicons React',
1426
+ annotations: {
1427
+ readOnlyHint: true
1428
+ }
1348
1429
  }, async () => {
1349
1430
  const icons = listIcons().map(icon => {
1350
1431
  const keywords = icon.keywords.map(keyword => {
@@ -1371,6 +1452,9 @@ server.registerTool('get_icon', {
1371
1452
  inputSchema: {
1372
1453
  name: z.string().describe('The name of the icon to retrieve'),
1373
1454
  size: z.string().optional().describe('The size of the icon to retrieve, e.g. "16"').default('16')
1455
+ },
1456
+ annotations: {
1457
+ readOnlyHint: true
1374
1458
  }
1375
1459
  }, async ({
1376
1460
  name,
@@ -1420,7 +1504,10 @@ ${text}`
1420
1504
  // Coding guidelines
1421
1505
  // -----------------------------------------------------------------------------
1422
1506
  server.registerTool('primer_coding_guidelines', {
1423
- description: 'Get the guidelines when writing code that uses Primer or for UI code that you are creating'
1507
+ description: 'Get the guidelines when writing code that uses Primer or for UI code that you are creating',
1508
+ annotations: {
1509
+ readOnlyHint: true
1510
+ }
1424
1511
  }, async () => {
1425
1512
  return {
1426
1513
  content: [{
@@ -1471,6 +1558,9 @@ server.registerTool('review_alt_text', {
1471
1558
  surroundingText: z.string().describe('Text surrounding the image, relevant to the image.'),
1472
1559
  alt: z.string().describe('The alt text of the image being evaluated'),
1473
1560
  image: z.string().describe('The image URL or file path being evaluated')
1561
+ },
1562
+ annotations: {
1563
+ readOnlyHint: true
1474
1564
  }
1475
1565
  }, async ({
1476
1566
  surroundingText,
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-C2QaEv-c.js';
2
+ import { s as server } from './server-CkSoZOVD.js';
3
3
  import '@modelcontextprotocol/sdk/server/mcp.js';
4
4
  import 'cheerio';
5
5
  import 'zod';
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.3",
4
+ "version": "0.4.0-rc.4d4be073a",
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.20.0",
40
+ "@primer/react": "^38.30.0",
41
41
  "cheerio": "^1.0.0",
42
42
  "turndown": "^7.2.0",
43
43
  "zod": "^4.3.5"
package/src/primer.ts CHANGED
@@ -40,48 +40,81 @@ function listComponents(): Array<Component> {
40
40
  type Pattern = {
41
41
  id: string
42
42
  name: string
43
+ // 'scenario' maps to the /product/scenario-patterns/ base path, 'ui' to /product/ui-patterns/
44
+ category: 'scenario' | 'ui'
43
45
  }
44
46
 
47
+ // Scenario patterns are listed first so name resolution favours them over UI patterns.
45
48
  const patterns: Array<Pattern> = [
49
+ {
50
+ id: 'copy',
51
+ name: 'Copy',
52
+ category: 'scenario',
53
+ },
54
+ {
55
+ id: 'delete',
56
+ name: 'Delete',
57
+ category: 'scenario',
58
+ },
59
+ {
60
+ id: 'filter',
61
+ name: 'Filter',
62
+ category: 'scenario',
63
+ },
64
+ {
65
+ id: 'search',
66
+ name: 'Search',
67
+ category: 'scenario',
68
+ },
46
69
  {
47
70
  id: 'data-visualization',
48
71
  name: 'Data Visualization',
72
+ category: 'ui',
49
73
  },
50
74
  {
51
75
  id: 'degraded-experiences',
52
76
  name: 'Degraded Experiences',
77
+ category: 'ui',
53
78
  },
54
79
  {
55
80
  id: 'empty-states',
56
81
  name: 'Empty States',
82
+ category: 'ui',
57
83
  },
58
84
  {
59
85
  id: 'feature-onboarding',
60
86
  name: 'Feature Onboarding',
87
+ category: 'ui',
61
88
  },
62
89
  {
63
90
  id: 'forms',
64
91
  name: 'Forms',
92
+ category: 'ui',
65
93
  },
66
94
  {
67
95
  id: 'loading',
68
96
  name: 'Loading',
97
+ category: 'ui',
69
98
  },
70
99
  {
71
100
  id: 'navigation',
72
101
  name: 'Navigation',
102
+ category: 'ui',
73
103
  },
74
104
  {
75
105
  id: 'notification-messaging',
76
106
  name: 'Notification message',
107
+ category: 'ui',
77
108
  },
78
109
  {
79
110
  id: 'progressive-disclosure',
80
111
  name: 'Progressive disclosure',
112
+ category: 'ui',
81
113
  },
82
114
  {
83
115
  id: 'saving',
84
116
  name: 'Saving',
117
+ category: 'ui',
85
118
  },
86
119
  ]
87
120
 
package/src/server.ts CHANGED
@@ -39,6 +39,7 @@ server.registerTool(
39
39
  'init',
40
40
  {
41
41
  description: 'Setup or create a project that includes Primer React',
42
+ annotations: {readOnlyHint: true},
42
43
  },
43
44
  async () => {
44
45
  const url = new URL(`/product/getting-started/react`, 'https://primer.style')
@@ -92,7 +93,7 @@ ${text}
92
93
  // -----------------------------------------------------------------------------
93
94
  server.registerTool(
94
95
  'list_components',
95
- {description: 'List all of the components available from Primer React'},
96
+ {description: 'List all of the components available from Primer React', annotations: {readOnlyHint: true}},
96
97
  async () => {
97
98
  const components = listComponents().map(component => {
98
99
  return `- ${component.name}`
@@ -120,6 +121,7 @@ server.registerTool(
120
121
  inputSchema: {
121
122
  name: z.string().describe('The name of the component to retrieve'),
122
123
  },
124
+ annotations: {readOnlyHint: true},
123
125
  },
124
126
  async ({name}) => {
125
127
  const components = listComponents()
@@ -166,6 +168,7 @@ server.registerTool(
166
168
  inputSchema: {
167
169
  name: z.string().describe('The name of the component to retrieve'),
168
170
  },
171
+ annotations: {readOnlyHint: true},
169
172
  },
170
173
  async ({name}) => {
171
174
  const components = listComponents()
@@ -226,6 +229,7 @@ server.registerTool(
226
229
  inputSchema: {
227
230
  name: z.string().describe('The name of the component to retrieve'),
228
231
  },
232
+ annotations: {readOnlyHint: true},
229
233
  },
230
234
  async ({name}) => {
231
235
  const components = listComponents()
@@ -298,6 +302,7 @@ server.registerTool(
298
302
  inputSchema: {
299
303
  name: z.string().describe('The name of the component to retrieve'),
300
304
  },
305
+ annotations: {readOnlyHint: true},
301
306
  },
302
307
  async ({name}) => {
303
308
  const components = listComponents()
@@ -367,18 +372,28 @@ ${text}`,
367
372
  // -----------------------------------------------------------------------------
368
373
  server.registerTool(
369
374
  'list_patterns',
370
- {description: 'List all of the patterns available from Primer React'},
375
+ {
376
+ description:
377
+ 'List all of the patterns available from Primer React. Scenario patterns describe specific user tasks (copy, delete, filter, search). Prefer a scenario pattern when one fits the task, and fall back to the more generic UI patterns otherwise.',
378
+ annotations: {readOnlyHint: true},
379
+ },
371
380
  async () => {
372
- const patterns = listPatterns().map(pattern => {
373
- return `- ${pattern.name}`
374
- })
381
+ const all = listPatterns()
382
+ const scenario = all.filter(pattern => pattern.category === 'scenario').map(pattern => `- ${pattern.name}`)
383
+ const ui = all.filter(pattern => pattern.category === 'ui').map(pattern => `- ${pattern.name}`)
375
384
  return {
376
385
  content: [
377
386
  {
378
387
  type: 'text',
379
- text: `The following patterns are available in the @primer/react in TypeScript projects:
388
+ text: `The following patterns are available from \`@primer/react\` for use in TypeScript projects. Scenario patterns describe specific user tasks. Prefer a scenario pattern when one fits the task, and fall back to the UI patterns otherwise.
380
389
 
381
- ${patterns.join('\n')}`,
390
+ ## Scenario patterns
391
+
392
+ ${scenario.join('\n')}
393
+
394
+ ## UI patterns
395
+
396
+ ${ui.join('\n')}`,
382
397
  },
383
398
  ],
384
399
  }
@@ -388,16 +403,19 @@ ${patterns.join('\n')}`,
388
403
  server.registerTool(
389
404
  'get_pattern',
390
405
  {
391
- description: 'Get a specific pattern by name',
406
+ description:
407
+ 'Get a specific pattern by name. Scenario patterns describe specific user tasks (copy, delete, filter, search). Prefer a scenario pattern when one fits the task, and fall back to the more generic UI patterns otherwise.',
392
408
  inputSchema: {
393
409
  name: z.string().describe('The name of the pattern to retrieve'),
394
410
  },
411
+ annotations: {readOnlyHint: true},
395
412
  },
396
413
  async ({name}) => {
397
414
  const patterns = listPatterns()
398
- const match = patterns.find(pattern => {
399
- return pattern.name === name
400
- })
415
+ // Resolve scenario patterns first so a name clash favours the scenario pattern.
416
+ const match =
417
+ patterns.find(pattern => pattern.category === 'scenario' && pattern.name === name) ??
418
+ patterns.find(pattern => pattern.name === name)
401
419
  if (!match) {
402
420
  return {
403
421
  content: [
@@ -409,7 +427,8 @@ server.registerTool(
409
427
  }
410
428
  }
411
429
 
412
- const url = new URL(`/product/ui-patterns/${match.id}`, 'https://primer.style')
430
+ const basePath = match.category === 'scenario' ? 'scenario-patterns' : 'ui-patterns'
431
+ const url = new URL(`/product/${basePath}/${match.id}`, 'https://primer.style')
413
432
  const response = await fetch(url)
414
433
  if (!response.ok) {
415
434
  throw new Error(`Failed to fetch ${url} - ${response.statusText}`)
@@ -469,6 +488,7 @@ server.registerTool(
469
488
  .default(15)
470
489
  .describe('Maximum results to return to stay within context limits'),
471
490
  },
491
+ annotations: {readOnlyHint: true},
472
492
  },
473
493
  async ({query, group, limit}) => {
474
494
  // Resolve group via aliases
@@ -566,6 +586,7 @@ server.registerTool(
566
586
  inputSchema: {
567
587
  groups: z.array(z.string()).describe('Array of group names (e.g., ["overlay", "shadow", "focus"])'),
568
588
  },
589
+ annotations: {readOnlyHint: true},
569
590
  },
570
591
  async ({groups}) => {
571
592
  // Normalize and resolve aliases
@@ -608,6 +629,7 @@ server.registerTool(
608
629
  {
609
630
  description:
610
631
  'CRITICAL: CALL THIS FIRST. Provides the logic matrix and the list of valid group names. You cannot search accurately without this map.',
632
+ annotations: {readOnlyHint: true},
611
633
  },
612
634
  async () => {
613
635
  const groups = listTokenGroups()
@@ -631,6 +653,7 @@ server.registerTool(
631
653
  {
632
654
  description:
633
655
  'Provides "Golden Example" CSS for core patterns: Button (Interactions) and Stack (Layout). Use this to understand how to apply the Logic Matrix, Motion, and Spacing scales.',
656
+ annotations: {readOnlyHint: true},
634
657
  },
635
658
  async () => {
636
659
  const customPatterns = getTokenUsagePatternsText()
@@ -659,6 +682,7 @@ server.registerTool(
659
682
  description:
660
683
  'REQUIRED FINAL STEP. Use this to validate your CSS. You cannot complete a task involving CSS without a successful run of this tool.',
661
684
  inputSchema: {css: z.string()},
685
+ annotations: {readOnlyHint: true},
662
686
  },
663
687
  async ({css}) => {
664
688
  try {
@@ -694,7 +718,7 @@ server.registerTool(
694
718
  // -----------------------------------------------------------------------------
695
719
  server.registerTool(
696
720
  'get_color_usage',
697
- {description: 'Get the guidelines for how to apply color to a user interface'},
721
+ {description: 'Get the guidelines for how to apply color to a user interface', annotations: {readOnlyHint: true}},
698
722
  async () => {
699
723
  const url = new URL(`/product/getting-started/foundations/color-usage`, 'https://primer.style')
700
724
  const response = await fetch(url)
@@ -732,7 +756,10 @@ server.registerTool(
732
756
 
733
757
  server.registerTool(
734
758
  'get_typography_usage',
735
- {description: 'Get the guidelines for how to apply typography to a user interface'},
759
+ {
760
+ description: 'Get the guidelines for how to apply typography to a user interface',
761
+ annotations: {readOnlyHint: true},
762
+ },
736
763
  async () => {
737
764
  const url = new URL(`/product/getting-started/foundations/typography`, 'https://primer.style')
738
765
  const response = await fetch(url)
@@ -773,7 +800,10 @@ server.registerTool(
773
800
  // -----------------------------------------------------------------------------
774
801
  server.registerTool(
775
802
  'list_icons',
776
- {description: 'List all of the icons (octicons) available from Primer Octicons React'},
803
+ {
804
+ description: 'List all of the icons (octicons) available from Primer Octicons React',
805
+ annotations: {readOnlyHint: true},
806
+ },
777
807
  async () => {
778
808
  const icons = listIcons().map(icon => {
779
809
  const keywords = icon.keywords.map(keyword => {
@@ -808,6 +838,7 @@ server.registerTool(
808
838
  name: z.string().describe('The name of the icon to retrieve'),
809
839
  size: z.string().optional().describe('The size of the icon to retrieve, e.g. "16"').default('16'),
810
840
  },
841
+ annotations: {readOnlyHint: true},
811
842
  },
812
843
  async ({name, size}) => {
813
844
  const icons = listIcons()
@@ -865,7 +896,10 @@ ${text}`,
865
896
  // -----------------------------------------------------------------------------
866
897
  server.registerTool(
867
898
  'primer_coding_guidelines',
868
- {description: 'Get the guidelines when writing code that uses Primer or for UI code that you are creating'},
899
+ {
900
+ description: 'Get the guidelines when writing code that uses Primer or for UI code that you are creating',
901
+ annotations: {readOnlyHint: true},
902
+ },
869
903
  async () => {
870
904
  return {
871
905
  content: [
@@ -922,6 +956,7 @@ server.registerTool(
922
956
  alt: z.string().describe('The alt text of the image being evaluated'),
923
957
  image: z.string().describe('The image URL or file path being evaluated'),
924
958
  },
959
+ annotations: {readOnlyHint: true},
925
960
  },
926
961
  async ({surroundingText, alt, image}) => {
927
962
  // Call the LLM through MCP sampling