---
name: mcp-builder
description: Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or TypeScript (MCP SDK).
triggers:
  - build an MCP server
  - create MCP tools
  - MCP integration
  - make a tool server
  - model context protocol
---

# MCP Server Builder

Build production-quality MCP servers that expose external services as tools for LLMs.

## When to Use

Trigger when user wants to:
- Build a new MCP server for an API or service
- Add tools to an existing MCP server
- Integrate an external service for LLM access
- Create a tool server for Claude, Manus, or other AI agents

## Architecture Decision

### Choose Python (FastMCP) when:
- Rapid prototyping needed
- Team is Python-first
- Heavy data processing or ML integration
- Simpler deployment requirements

### Choose TypeScript (MCP SDK) when:
- Production deployment at scale
- Strong typing requirements
- Node.js ecosystem integration
- Complex async patterns needed

## Implementation Phases

### Phase 1: Design

1. **Identify the API/service** to integrate
2. **Map capabilities to tools** — each tool should do ONE thing well
3. **Define input schemas** with clear descriptions and constraints
4. **Define output formats** — structured JSON for data, Markdown for human-readable
5. **Plan authentication** — API keys, OAuth, or service accounts

**Tool naming conventions:**
- Use verb-noun format: `get_user`, `create_issue`, `search_documents`
- Be specific: `list_open_pull_requests` not `get_prs`
- Group related tools with consistent prefixes

### Phase 2: Implementation

**Python (FastMCP) pattern:**

```python
from fastmcp import FastMCP

mcp = FastMCP("service-name")

@mcp.tool()
async def get_resource(
    resource_id: str,
    include_metadata: bool = False
) -> dict:
    """Fetch a specific resource by ID.
    
    Args:
        resource_id: The unique identifier of the resource
        include_metadata: Whether to include creation/update timestamps
    """
    # Implementation
    result = await api_client.get(f"/resources/{resource_id}")
    if include_metadata:
        return result
    return {k: v for k, v in result.items() if k != "metadata"}
```

**TypeScript (MCP SDK) pattern:**

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

const server = new McpServer({ name: "service-name", version: "1.0.0" });

server.registerTool(
  "get_resource",
  {
    description: "Fetch a specific resource by ID",
    inputSchema: z.object({
      resource_id: z.string().describe("The unique identifier"),
      include_metadata: z.boolean().default(false)
    })
  },
  async ({ resource_id, include_metadata }) => {
    // Implementation
  }
);
```

**Implementation requirements:**
- Async/await for all I/O operations
- Proper error handling with actionable messages
- Input validation via Pydantic (Python) or Zod (TypeScript)
- Pagination support for list operations
- Rate limiting awareness

### Phase 3: Quality Review

Check for:
- No duplicated code (DRY)
- Consistent error handling across all tools
- Full type coverage
- Clear, accurate tool descriptions
- Proper annotations (readOnlyHint, destructiveHint, idempotentHint)

### Phase 4: Testing

**Python:**
```bash
python -m py_compile your_server.py
# Test with MCP Inspector
npx @modelcontextprotocol/inspector
```

**TypeScript:**
```bash
npm run build
npx @modelcontextprotocol/inspector
```

## Best Practices

**Tool descriptions** are critical — they are how the LLM decides which tool to use:
- First sentence: what the tool does (concise)
- Parameters: clear descriptions with examples
- Return type: what the LLM will receive

**Error handling** — return useful errors, not stack traces:
```python
@mcp.tool()
async def get_user(user_id: str) -> dict:
    try:
        return await api.get_user(user_id)
    except NotFoundError:
        return {"error": f"User '{user_id}' not found. Check the ID and try again."}
    except RateLimitError:
        return {"error": "Rate limited. Wait 60 seconds before retrying."}
```

**Pagination** — always support it for list operations:
```python
@mcp.tool()
async def list_items(
    page: int = 1,
    per_page: int = 20,
    filter: str | None = None
) -> dict:
    """List items with pagination. Returns items and total count."""
    ...
```

## Transport Options

| Transport | Use Case |
|-----------|----------|
| stdio | Local development, single-user |
| Streamable HTTP | Production, multi-user, remote |

## Reference Documentation

- MCP Protocol: https://modelcontextprotocol.io
- Python SDK: https://github.com/modelcontextprotocol/python-sdk
- TypeScript SDK: https://github.com/modelcontextprotocol/typescript-sdk
