--- name: mcp-builder description: Build a high-quality MCP server that exposes a service or API to an LLM through well-designed tools. Use when asked to create, scaffold, or add an MCP server, wrap an API as MCP tools, or expose a service to Claude/agents. Writes files, so it runs only when invoked explicitly. when_to_use: | - User asks to build, scaffold, or add an MCP server - User wants to wrap an API, service, or database as MCP tools - User says "MCP server", "expose this as tools", "make this callable by an agent" - Do NOT auto-run; this writes files disable-model-invocation: true allowed-tools: "Read, Write, Edit, Bash" --- # Build an MCP Server An MCP server is judged by one thing: whether an LLM can use its tools to finish real tasks. Tool count, endpoint coverage, and cleverness are means, not the goal. Build for the agent that will call it. ## Default stack - **TypeScript, on the MCP SDK.** Strong typing and good model familiarity. Drop to Python (FastMCP) only when the service it wraps is Python-native. - **Transport: streamable HTTP with stateless JSON for remote servers; stdio for local.** Stateless scales and debugs far more easily than session streaming. - **Resolve the SDK version at build time**, don't hardcode from memory. The current SDK README is the source of truth. ## The four phases **1. Understand the target.** Read the service's API or schema first. Identify the handful of tasks a user actually wants done through it, not every endpoint. A wrapper that mirrors 60 REST routes one-to-one is worse than 8 tools shaped around real workflows. **2. Design the tools.** This is where quality is won or lost. - **Discoverable names**: `verb_noun`, scoped by service, for example `github_create_issue`, `orders_find_by_customer`. The name should tell the model when to reach for it. - **Descriptions written for the model**, stating what the tool does and when to use it, not just what it returns. - **Explicit input and output schemas.** Constrain inputs; return structured, parseable output, not prose the model has to scrape. - **Annotations**: mark each tool `readOnlyHint`, `destructiveHint`, or `idempotentHint` so the client can reason about safety. - **Actionable errors.** "Invalid date" is useless; "expected ISO 8601, got 03/04/2026, did you mean 2026-03-04" lets the model self-correct. - **Balance coverage against workflow tools.** Comprehensive low-level tools give the agent room to compose; a few high-level workflow tools cut steps for the common path. Most good servers ship both. **3. Implement.** Shared infrastructure first (client, auth, pagination, error handling), then the tools on top. If the server touches a database, it follows the same kit rules as everything else: one shared client, data access through the adapter, StrictDB if installed otherwise the native driver, never Mongoose. **4. Test with evaluations.** Write 10 realistic, multi-step questions a user would ask, then verify an LLM can answer each using only the tools. Solve each yourself first so you know the right answer. Failing evals point at a missing tool, a vague description, or output the model can't parse, fix those, not the eval. ## Project shape src/ server.ts transport + registration, thin client.ts the upstream API/service client (the one place it's touched) tools/ one file per tool or tight tool group schemas/ input/output schemas (zod) evals/ evals.json the 10 questions and verified answers Keep `server.ts` thin, the same discipline as `api-conventions`: it wires transport and registers tools, nothing more. Tool logic lives in `tools/`, upstream calls in `client.ts`. No file over 300 lines. ## What to avoid - Dumping every endpoint as a tool with no workflow shaping. - Tool names only the author understands. - Returning raw upstream JSON the model has to guess the shape of. - Skipping evals, an MCP server you haven't watched an LLM drive is untested.