// Stage 5 (Surface), per docs/ARCHITECTURE.md. matchcn's one MCP tool, // pick_component, over stdio. No web UI, per project scope. // // This is a thin wrapper: all the actual logic (Match, Resolve, the // never-force-a-guess outcome rules) lives in src/pick.ts, which is also // what src/demo.ts calls directly to produce the test-brief report. This // file only adapts that function to the MCP tool-call protocol. import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import { pickComponent } from "./pick.js"; import { REGISTRIES } from "./registries.js"; const server = new McpServer({ name: "matchcn", version: "0.1.0", }); server.registerTool( "pick_component", { title: "Pick a shadcn-format component", description: "Given a plain-language description of a UI need, finds the best-matching component across the indexed shadcn-format registries (react-bits, magicui, aceternity, kokonutui, animate-ui, motion-primitives). Returns one of three outcomes: confident (one clear winner), shortlist (several strong candidates with the differentiating dimension named), or no_match (nothing fits, closest candidates shown but marked rejected). Never a forced best guess.", inputSchema: { brief: z.string().describe("Plain-language description of the component needed, any language"), registry: z .enum(REGISTRIES.map((r) => r.name) as [string, ...string[]]) .optional() .describe("Restrict the search to one registry"), maxResults: z.number().int().min(1).max(10).optional().describe("Max candidates to return for a shortlist or no_match outcome (default 3)"), }, }, async ({ brief, registry, maxResults }) => { const result = await pickComponent({ brief, registry, maxResults }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; }, ); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); } main().catch((err) => { console.error(err); process.exit(1); });