@primer/mcp 0.0.2 → 0.0.3
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/README.md +3 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/primer.d.ts +8 -1
- package/dist/primer.d.ts.map +1 -1
- package/dist/server-BdIvJFTO.js +645 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/stdio.js +7 -519
- package/package.json +12 -7
- package/src/index.ts +1 -0
- package/src/primer.ts +22 -1
- package/src/server.ts +139 -6
package/src/server.ts
CHANGED
|
@@ -3,7 +3,7 @@ import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
|
3
3
|
import * as cheerio from 'cheerio'
|
|
4
4
|
import {z} from 'zod'
|
|
5
5
|
import TurndownService from 'turndown'
|
|
6
|
-
import {listComponents, listPatterns} from './primer'
|
|
6
|
+
import {listComponents, listPatterns, listIcons} from './primer'
|
|
7
7
|
import packageJson from '../package.json' with {type: 'json'}
|
|
8
8
|
|
|
9
9
|
const server = new McpServer({
|
|
@@ -13,10 +13,59 @@ const server = new McpServer({
|
|
|
13
13
|
|
|
14
14
|
const turndownService = new TurndownService()
|
|
15
15
|
|
|
16
|
+
// -----------------------------------------------------------------------------
|
|
17
|
+
// Project setup
|
|
18
|
+
// -----------------------------------------------------------------------------
|
|
19
|
+
server.tool('init', 'Setup or create a project that includes Primer React', async () => {
|
|
20
|
+
const url = new URL(`/product/getting-started/react`, 'https://primer.style')
|
|
21
|
+
const response = await fetch(url)
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
throw new Error(`Failed to fetch ${url}: ${response.statusText}`)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const html = await response.text()
|
|
27
|
+
if (!html) {
|
|
28
|
+
return {
|
|
29
|
+
content: [],
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const $ = cheerio.load(html)
|
|
34
|
+
const source = $('main').html()
|
|
35
|
+
if (!source) {
|
|
36
|
+
return {
|
|
37
|
+
content: [],
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const text = turndownService.turndown(source)
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
content: [
|
|
45
|
+
{
|
|
46
|
+
type: 'text',
|
|
47
|
+
text: `The getting started documentation for Primer React is included below. It's important that the project:
|
|
48
|
+
|
|
49
|
+
- Is using a tool like Vite, Next.js, etc that supports TypeScript and React. If the project does not have support for that, generate an appropriate project scaffold
|
|
50
|
+
- Installs the latest version of \`@primer/react\` from \`npm\`
|
|
51
|
+
- Correctly adds the \`ThemeProvider\` and \`BaseStyles\` components to the root of the application
|
|
52
|
+
- Includes an import to a theme from \`@primer/primitives\`
|
|
53
|
+
- If the project wants to use icons, also install the \`@primer/octicons-react\` from \`npm\`
|
|
54
|
+
- Add appropriate agent instructions (like for copilot) to the project to prefer using components, tokens, icons, and more from Primer packages
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
${text}
|
|
59
|
+
`,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
|
|
16
65
|
// -----------------------------------------------------------------------------
|
|
17
66
|
// Components
|
|
18
67
|
// -----------------------------------------------------------------------------
|
|
19
|
-
server.tool('
|
|
68
|
+
server.tool('list_components', 'List all of the components available from Primer React', async () => {
|
|
20
69
|
const components = listComponents().map(component => {
|
|
21
70
|
return `- ${component.name}`
|
|
22
71
|
})
|
|
@@ -43,7 +92,7 @@ server.tool(
|
|
|
43
92
|
async ({name}) => {
|
|
44
93
|
const components = listComponents()
|
|
45
94
|
const match = components.find(component => {
|
|
46
|
-
return component.name === name
|
|
95
|
+
return component.name === name || component.name.toLowerCase() === name.toLowerCase()
|
|
47
96
|
})
|
|
48
97
|
if (!match) {
|
|
49
98
|
return {
|
|
@@ -56,7 +105,7 @@ server.tool(
|
|
|
56
105
|
}
|
|
57
106
|
}
|
|
58
107
|
|
|
59
|
-
const url = new URL(`/product/components/${match.
|
|
108
|
+
const url = new URL(`/product/components/${match.slug}`, 'https://primer.style')
|
|
60
109
|
const response = await fetch(url)
|
|
61
110
|
if (!response.ok) {
|
|
62
111
|
throw new Error(`Failed to fetch ${url}: ${response.statusText}`)
|
|
@@ -772,10 +821,8 @@ const tokens: Array<Token> = [
|
|
|
772
821
|
|
|
773
822
|
function serialize(token: Token): string {
|
|
774
823
|
if (typeof token === 'string') {
|
|
775
|
-
// eslint-disable-next-line github/unescaped-html-literal
|
|
776
824
|
return `<token name="${token}"></token>`
|
|
777
825
|
}
|
|
778
|
-
// eslint-disable-next-line github/unescaped-html-literal
|
|
779
826
|
return `<token-category name="${token.category}">\n${token.tokens.map(serialize).join('\n')}\n</token-category>`
|
|
780
827
|
}
|
|
781
828
|
|
|
@@ -872,4 +919,90 @@ server.tool('get_typography_usage', 'Get the guidelines for how to apply typogra
|
|
|
872
919
|
}
|
|
873
920
|
})
|
|
874
921
|
|
|
922
|
+
// -----------------------------------------------------------------------------
|
|
923
|
+
// Icons
|
|
924
|
+
// -----------------------------------------------------------------------------
|
|
925
|
+
server.tool('list_icons', 'List all of the icons (octicons) available from Primer Octicons React', async () => {
|
|
926
|
+
const icons = listIcons().map(icon => {
|
|
927
|
+
const keywords = icon.keywords.map(keyword => {
|
|
928
|
+
return `<keyword>${keyword}</keyword>`
|
|
929
|
+
})
|
|
930
|
+
const sizes = icon.heights.map(height => {
|
|
931
|
+
return `<size value="${height}"></size>`
|
|
932
|
+
})
|
|
933
|
+
return [`<icon name="${icon.name}">`, ...keywords, ...sizes, `</icon>`].join('\n')
|
|
934
|
+
})
|
|
935
|
+
|
|
936
|
+
return {
|
|
937
|
+
content: [
|
|
938
|
+
{
|
|
939
|
+
type: 'text',
|
|
940
|
+
text: `The following icons are available in the @primer/octicons-react package in TypeScript projects:
|
|
941
|
+
|
|
942
|
+
${icons.join('\n')}
|
|
943
|
+
|
|
944
|
+
You can use the \`get_icon\` tool to get more information about a specific icon. You can use these components from the @primer/octicons-react package.`,
|
|
945
|
+
},
|
|
946
|
+
],
|
|
947
|
+
}
|
|
948
|
+
})
|
|
949
|
+
|
|
950
|
+
server.tool(
|
|
951
|
+
'get_icon',
|
|
952
|
+
'Get a specific icon (octicon) by name from Primer',
|
|
953
|
+
{
|
|
954
|
+
name: z.string().describe('The name of the icon to retrieve'),
|
|
955
|
+
size: z.string().optional().describe('The size of the icon to retrieve, e.g. "16"').default('16'),
|
|
956
|
+
},
|
|
957
|
+
async ({name, size}) => {
|
|
958
|
+
const icons = listIcons()
|
|
959
|
+
const match = icons.find(icon => {
|
|
960
|
+
return icon.name === name || icon.name.toLowerCase() === name.toLowerCase()
|
|
961
|
+
})
|
|
962
|
+
if (!match) {
|
|
963
|
+
return {
|
|
964
|
+
content: [
|
|
965
|
+
{
|
|
966
|
+
type: 'text',
|
|
967
|
+
text: `There is no icon named \`${name}\` in the @primer/octicons-react package. For a full list of icons, use the \`get_icon\` tool.`,
|
|
968
|
+
},
|
|
969
|
+
],
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
const url = new URL(`/octicons/icon/${match.name}-${size}`, 'https://primer.style')
|
|
974
|
+
const response = await fetch(url)
|
|
975
|
+
if (!response.ok) {
|
|
976
|
+
throw new Error(`Failed to fetch ${url}: ${response.statusText}`)
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
const html = await response.text()
|
|
980
|
+
if (!html) {
|
|
981
|
+
return {
|
|
982
|
+
content: [],
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
const $ = cheerio.load(html)
|
|
987
|
+
const source = $('main').html()
|
|
988
|
+
if (!source) {
|
|
989
|
+
return {
|
|
990
|
+
content: [],
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
const text = turndownService.turndown(source)
|
|
995
|
+
|
|
996
|
+
return {
|
|
997
|
+
content: [
|
|
998
|
+
{
|
|
999
|
+
type: 'text',
|
|
1000
|
+
text: `Here is the documentation for the \`${name}\` icon at size: \`${size}\`:
|
|
1001
|
+
${text}`,
|
|
1002
|
+
},
|
|
1003
|
+
],
|
|
1004
|
+
}
|
|
1005
|
+
},
|
|
1006
|
+
)
|
|
1007
|
+
|
|
875
1008
|
export {server}
|