Skill v1.0.1
currentAutomated scan100/100+16 new
version: "1.0.1" name: mcp-builder description: "Build MCP servers in Python with FastMCP. Define tools / resources / prompts, build the server, test locally, deploy to FastMCP Cloud or Docker. Use whenever the user mentions building an MCP server, exposing tools to LLMs, FastMCP, building a Claude integration, or troubleshooting FastMCP module-level server, storage, lifespan, middleware, OAuth, or deployment errors." compatibility: claude-code-only
MCP Builder
Build a working MCP server from a description of the tools you need. Produces a deployable Python server using FastMCP.
Workflow
Step 1: Define What to Expose
Ask what the server needs to provide:
- Tools -- Functions Claude can call (API wrappers, calculations, file operations)
- Resources -- Data Claude can read (database records, config, documents)
- Prompts -- Reusable prompt templates with parameters
A brief like "MCP server for querying our customer database" is enough.
Step 2: Scaffold the Server
pip install fastmcp
Create the server file. The server instance MUST be at module level:
from fastmcp import FastMCP# MUST be at module level for FastMCP Cloudmcp = FastMCP("My Server")@mcp.tool()async def search_customers(query: str) -> str:"""Search customers by name or email."""# Implementation herereturn f"Found customers matching: {query}"@mcp.resource("customers://{customer_id}")async def get_customer(customer_id: str) -> str:"""Get customer details by ID."""return f"Customer {customer_id} details"if __name__ == "__main__":mcp.run()
Step 3: Add Companion CLI Scripts (Optional)
For Claude Code terminal use, add scripts alongside the MCP server:
my-mcp-server/├── src/index.ts # MCP server (for Claude.ai)├── scripts/│ ├── search.ts # CLI version of search tool│ └── _shared.ts # Shared auth/config├── SCRIPTS.md # Documents available scripts└── package.json
CLI scripts provide file I/O, batch processing, and richer output that MCP can't. See assets/SCRIPTS-TEMPLATE.md and assets/script-template.ts for TypeScript templates.
Step 4: Test Locally
Quick test -- run directly:
python server.py
Dev mode with inspector UI (recommended):
fastmcp dev server.py# Opens inspector at http://localhost:5173# Hot reload, detailed logging, tool/resource inspection
HTTP mode for remote clients:
python server.py --transport http --port 8000
Automated test script using FastMCP Client:
import asynciofrom fastmcp import Clientasync def test_server(server_path):async with Client(server_path) as client:# List everythingtools = await client.list_tools()resources = await client.list_resources()prompts = await client.list_prompts()print(f"Tools: {[t.name for t in tools]}")print(f"Resources: {[r.uri for r in resources]}")print(f"Prompts: {[p.name for p in prompts]}")# Call first toolif tools:result = await client.call_tool(tools[0].name, {})print(f"Tool result: {result}")# Read first resourceif resources:data = await client.read_resource(resources[0].uri)print(f"Resource data: {data}")asyncio.run(test_server("server.py"))
Step 5: Pre-Deploy Checklist
Run these checks before deploying. All required checks must pass.
Required (will cause deploy failure):
- Server file exists
- Python syntax valid:
python3 -m py_compile server.py - Module-level server object (not inside a function):
``bash grep -q "^mcp = FastMCP\|^server = FastMCP\|^app = FastMCP" server.py ``
requirements.txtexists with PyPI packages only (nogit+,-e,.whl,.tar.gz)- No hardcoded secrets (check for
api_key = "..."patterns excludingos.getenv/os.environ)
Advisory (warnings):
fastmcplisted in requirements.txt.gitignoreincludes.env- No circular imports
- Git repository initialised with remote
- Server can load:
timeout 5 fastmcp inspect server.py
Step 6: Deploy
FastMCP Cloud (simplest):
git add . && git commit -m "Ready for deployment"git push -u origin main# Visit https://fastmcp.cloud, connect repo, add env vars, deploy# URL: https://your-project.fastmcp.app/mcp
Cloud requirements:
- Module-level server object named
mcp,server, orapp - PyPI dependencies only in
requirements.txt - Public GitHub repository
- Environment variables for secrets (no hardcoded values)
- Auto-deploys on push to main, PR preview deployments
Docker (self-hosted):
FROM python:3.12-slimWORKDIR /appCOPY requirements.txt .RUN pip install -r requirements.txtCOPY . .EXPOSE 8000CMD ["python", "server.py", "--transport", "http", "--port", "8000"]
Cloudflare Workers (edge): Build it in TypeScript with Cloudflare's agents SDK (McpAgent); Cloudflare's remote MCP server docs carry the current template.
Critical Patterns
Module-Level Server Instance
FastMCP Cloud requires the server instance at module level:
# CORRECTmcp = FastMCP("My Server")@mcp.tool()def my_tool(): ...# WRONG -- Cloud can't find the serverdef create_server():mcp = FastMCP("My Server")return mcp# FIX for factory pattern -- export at module leveldef create_server() -> FastMCP:mcp = FastMCP("server")return mcpmcp = create_server()
Type Annotations Required
FastMCP uses type annotations to generate tool schemas:
@mcp.tool()async def search(query: str, # Required parameterlimit: int = 10, # Optional with defaulttags: list[str] = [] # Complex types supported) -> str:"""Docstring becomes the tool description."""...
Error Handling
Return errors as strings, don't raise exceptions:
@mcp.tool()async def get_data(id: str) -> str:try:result = await fetch_data(id)return json.dumps(result)except NotFoundError:return f"Error: No data found for ID {id}"
Cloud-Ready Server Pattern
import osfrom fastmcp import FastMCPmcp = FastMCP("production-server")API_KEY = os.getenv("API_KEY")@mcp.tool()async def production_tool(data: str) -> dict:if not API_KEY:return {"error": "API_KEY not configured"}return {"status": "success", "data": data}if __name__ == "__main__":mcp.run()
Common Errors and Fixes
These are the errors you will hit. Fix them before deploying.
| Error | Cause | Fix | ||
|---|---|---|---|---|
RuntimeError: No server object found at module level | Server inside a function | Export mcp = FastMCP(...) at module level | ||
RuntimeError: no running event loop | Missing async/await | Use async def for async operations | ||
TypeError: missing required argument 'context' | Context not type-hinted | Add context: Context with type hint | ||
ValueError: Invalid resource URI | Missing URI scheme | Use data://, file://, info://, api:// | ||
| Resource template parameter mismatch | Name mismatch | user://{user_id} needs def get_user(user_id: str) | ||
| Pydantic validation error | Wrong type hints | Ensure hints match actual data types | ||
| Transport mismatch | Client/server protocol differ | Match both to stdio or both to http | ||
| Import errors with editable package | Package not installed | pip install -e . or add to PYTHONPATH | ||
DeprecationWarning: mcp.settings | Old API | Use os.getenv() instead | ||
| Port already in use | Stale process | `lsof -ti:8000 \ | xargs kill -9` | |
| Schema generation failure | Non-JSON types | Use JSON-compatible types (no NumPy arrays) | ||
| JSON serialization error | datetime/bytes in response | Convert to .isoformat() or string | ||
| Circular import | Factory in __init__.py | Use direct imports, avoid factory pattern | ||
| Python 3.12+ datetime warning | datetime.utcnow() deprecated | Use datetime.now(timezone.utc) | ||
| Import-time execution | Async resource at module level | Use lazy init pattern |
Production Patterns
Self-Contained Server
Keep all utilities in one file to avoid circular imports:
from fastmcp import FastMCPimport osmcp = FastMCP("my-server")# Configclass Config:API_KEY = os.getenv("API_KEY", "")BASE_URL = os.getenv("BASE_URL", "https://api.example.com")# Helpersdef format_success(data): return {"status": "success", "data": data}def format_error(msg): return {"status": "error", "message": msg}@mcp.tool()async def my_tool(query: str) -> dict:if not Config.API_KEY:return format_error("API_KEY not configured")return format_success({"query": query})
Lazy Initialisation
Don't create async resources at module level. Initialise on first use:
_db = Noneasync def get_db():global _dbif _db is None:_db = await create_connection(Config.DB_URL)return _db
Health Check Resource
@mcp.resource("health://status")async def health_check() -> dict:return {"status": "healthy","version": "1.0.0","checks": {"api": "connected","database": "connected"}}
Connection Pooling
import httpx_client = Nonedef get_client() -> httpx.AsyncClient:global _clientif _client is None:_client = httpx.AsyncClient(base_url=Config.BASE_URL,headers={"Authorization": f"Bearer {Config.API_KEY}"},limits=httpx.Limits(max_connections=20, max_keepalive_connections=5),timeout=30.0)return _client
Retry with Backoff
async def retry_with_backoff(func, max_retries=3, initial_delay=1.0):for attempt in range(max_retries):try:return await func()except Exception as e:if attempt == max_retries - 1:raisedelay = initial_delay * (2 ** attempt)await asyncio.sleep(delay)
Context Features (Advanced)
Context Injection
from fastmcp import Context@mcp.tool()async def tool_with_context(param: str, context: Context) -> dict:# Context parameter MUST have type hintpass
Progress Tracking
@mcp.tool()async def long_task(items: list[str], context: Context) -> str:for i, item in enumerate(items):await context.report_progress(i + 1, len(items), f"Processing {item}")await process(item)return "Done"
Sampling (LLM from within tools)
@mcp.tool()async def summarise(text: str, context: Context) -> str:result = await context.request_sampling(messages=[{"role": "user", "content": f"Summarise: {text}"}],max_tokens=200)return result
CLI Quick Reference
fastmcp dev server.py # Dev mode with inspector UIfastmcp run server.py # Run (stdio)fastmcp run server.py --transport http --port 8000 # Run (HTTP)fastmcp inspect server.py # Inspect without runningfastmcp install server.py # Install to Claude Desktopfastmcp deploy server.py --name my-server # Deploy to Cloud
Environment variables: FASTMCP_LOG_LEVEL (DEBUG/INFO/WARNING/ERROR), FASTMCP_ENV (development/staging/production).
Integration Patterns (Optional)
For specific integration approaches, see references/integration-patterns.md:
- Manual API --
httpx.AsyncClientwith reusable client - OpenAPI auto-generation --
FastMCP.from_openapi(spec, client, route_maps=[...]) - FastAPI conversion --
FastMCP.from_fastapi(app)
Asset Files
assets/basic-server.py-- Minimal FastMCP server templateassets/self-contained-server.py-- Server with storage and middlewareassets/tools-examples.py-- Tool patterns and type annotationsassets/resources-examples.py-- Resource URI patternsassets/prompts-examples.py-- Prompt template patternsassets/client-example.py-- MCP client usageassets/SCRIPTS-TEMPLATE.md-- CLI companion docs templateassets/script-template.ts-- TypeScript CLI script template