diff --git a/claude-skills/mcp-builder.md b/claude-skills/mcp-builder.md new file mode 100644 index 0000000..1f4633b --- /dev/null +++ b/claude-skills/mcp-builder.md @@ -0,0 +1,72 @@ +--- +name: mcp-builder +description: Build MCP (Model Context Protocol) servers — stdio, HTTP/SSE, or remote. Covers tool design, auth, deployment, and testing. +--- + +# MCP Builder Skill + +Use when building a new MCP server or adding tools to an existing one. + +## MCP Server Types +- **stdio** — subprocess, local only, simplest +- **HTTP/SSE** — network accessible, supports auth, deployable +- **Remote (Cloudflare Workers, etc.)** — edge-deployed, global + +## Tool Design Principles +1. **One tool = one atomic action.** Don't bundle list+get into one tool. +2. **Names are verbs**: `get_user`, `list_orders`, `create_invoice`. +3. **Required params only** — use optional sparingly with sensible defaults. +4. **Return structured JSON** — always. Never plain strings. +5. **Descriptive tool descriptions** — the model reads these to decide which tool to call. +6. **Error objects not exceptions** — return `{error: "message"}` in the result, don't throw. + +## Minimal stdio Server (Node.js) +```js +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; + +const server = new Server({ name: "my-server", version: "1.0.0" }, { + capabilities: { tools: {} } +}); + +server.setRequestHandler("tools/list", async () => ({ + tools: [{ name: "say_hello", description: "Say hello", inputSchema: { + type: "object", properties: { name: { type: "string" } }, required: ["name"] + }}] +})); + +server.setRequestHandler("tools/call", async (req) => { + if (req.params.name === "say_hello") { + return { content: [{ type: "text", text: `Hello, ${req.params.arguments.name}!` }] }; + } + throw new Error(`Unknown tool: ${req.params.name}`); +}); + +await server.connect(new StdioServerTransport()); +``` + +## Auth (HTTP servers) +- Static API key: `Authorization: Bearer ` header check +- OAuth 2.1 with PKCE for user-facing servers +- Never put secrets in tool responses + +## Testing +```bash +# Test with MCP inspector +npx @modelcontextprotocol/inspector node ./server.js + +# Test tool call directly +echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"say_hello","arguments":{"name":"world"}}}' | node server.js +``` + +## .mcp.json Registration +```json +{ + "mcpServers": { + "my-server": { + "command": "node", + "args": ["/path/to/server.js"] + } + } +} +```