diff --git a/.changeset/update-mcp-scenario-patterns.md b/.changeset/update-mcp-scenario-patterns.md new file mode 100644 index 00000000000..b5b3729e80f --- /dev/null +++ b/.changeset/update-mcp-scenario-patterns.md @@ -0,0 +1,5 @@ +--- +'@primer/mcp': minor +--- + +MCP server: Add Create, Customize, Delegate, Edit and View scenario patterns to the `list_patterns` and `get_pattern` tools. diff --git a/packages/mcp/src/primer.ts b/packages/mcp/src/primer.ts index 6a1dfbb13a8..55dd80cded6 100644 --- a/packages/mcp/src/primer.ts +++ b/packages/mcp/src/primer.ts @@ -51,11 +51,31 @@ const patterns: Array = [ name: 'Copy', category: 'scenario', }, + { + id: 'create', + name: 'Create', + category: 'scenario', + }, + { + id: 'customize', + name: 'Customize', + category: 'scenario', + }, + { + id: 'delegate', + name: 'Delegate', + category: 'scenario', + }, { id: 'delete', name: 'Delete', category: 'scenario', }, + { + id: 'edit', + name: 'Edit', + category: 'scenario', + }, { id: 'filter', name: 'Filter', @@ -66,6 +86,11 @@ const patterns: Array = [ name: 'Search', category: 'scenario', }, + { + id: 'view', + name: 'View', + category: 'scenario', + }, { id: 'data-visualization', name: 'Data Visualization', diff --git a/packages/mcp/src/server.test.ts b/packages/mcp/src/server.test.ts index a20e6a97429..0e47205d692 100644 --- a/packages/mcp/src/server.test.ts +++ b/packages/mcp/src/server.test.ts @@ -72,6 +72,44 @@ describe('MCP server', () => { ) }) + it('lists every published scenario pattern', async () => { + const result = await client.callTool({name: 'list_patterns'}) + const scenarioPatterns = ['Copy', 'Create', 'Customize', 'Delegate', 'Delete', 'Edit', 'Filter', 'Search', 'View'] + + expect(result.content).toContainEqual( + expect.objectContaining({ + type: 'text', + text: expect.stringContaining( + `## Scenario patterns\n\n${scenarioPatterns.map(name => `- ${name}`).join('\n')}`, + ), + }), + ) + }) + + it.each([ + {name: 'Create', id: 'create'}, + {name: 'Customize', id: 'customize'}, + {name: 'Delegate', id: 'delegate'}, + {name: 'Edit', id: 'edit'}, + {name: 'View', id: 'view'}, + ])('retrieves the $name scenario pattern from its published URL', async ({name, id}) => { + vi.mocked(fetch).mockResolvedValue(new Response(`

${name}

`)) + + const result = await client.callTool({ + name: 'get_pattern', + arguments: {name}, + }) + + expect(fetch).toHaveBeenCalledTimes(1) + expect(vi.mocked(fetch).mock.calls[0]?.[0].toString()).toBe(`https://primer.style/product/scenario-patterns/${id}`) + expect(result.content).toContainEqual( + expect.objectContaining({ + type: 'text', + text: expect.stringContaining(`Here are the guidelines for the \`${name}\` pattern for Primer:`), + }), + ) + }) + it('requires between 2 and 10 names', async () => { const tooFew = await callBatch(['Button']) const tooMany = await callBatch(Array.from({length: 11}, (_, index) => `Component${index}`)) diff --git a/packages/mcp/src/server.ts b/packages/mcp/src/server.ts index f1e031a27ed..7fb21e7a897 100644 --- a/packages/mcp/src/server.ts +++ b/packages/mcp/src/server.ts @@ -441,7 +441,7 @@ ${text}`, 'list_patterns', { 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.', + '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.', annotations: {readOnlyHint: true}, }, async () => { @@ -471,7 +471,7 @@ ${ui.join('\n')}`, 'get_pattern', { 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.', + '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.', inputSchema: z.object({ name: z.string().describe('The name of the pattern to retrieve'), }),