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 Cloud
mcp = FastMCP("My Server")
@mcp.tool() async def search_customers(query: str) -> str: """Search customers by name or email.""" # Implementation here return 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 asyncio from fastmcp import Client
async def test_server(server_path): async with Client(server_path) as client: # List everything tools = 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 tool
if tools:
result = await client.call_tool(tools[0].name, {})
print(f"Tool result: {result}")
# Read first resource
if 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): grep -q "^mcp = FastMCP|^server = FastMCP|^app = FastMCP" server.py
-
requirements.txt exists with PyPI packages only (no git+ , -e , .whl , .tar.gz )
-
No hardcoded secrets (check for api_key = "..." patterns excluding os.getenv /os.environ )
Advisory (warnings):
-
fastmcp listed in requirements.txt
-
.gitignore includes .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 , or app
-
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-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . EXPOSE 8000 CMD ["python", "server.py", "--transport", "http", "--port", "8000"]
Cloudflare Workers (edge): See the cloudflare-worker-builder skill for Workers-based MCP servers.
Critical Patterns
Module-Level Server Instance
FastMCP Cloud requires the server instance at module level:
CORRECT
mcp = FastMCP("My Server")
@mcp.tool() def my_tool(): ...
WRONG -- Cloud can't find the server
def create_server(): mcp = FastMCP("My Server") return mcp
FIX for factory pattern -- export at module level
def create_server() -> FastMCP: mcp = FastMCP("server") return mcp mcp = create_server()
Type Annotations Required
FastMCP uses type annotations to generate tool schemas:
@mcp.tool() async def search( query: str, # Required parameter limit: int = 10, # Optional with default tags: 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 os from fastmcp import FastMCP
mcp = 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 FastMCP import os
mcp = FastMCP("my-server")
Config
class Config: API_KEY = os.getenv("API_KEY", "") BASE_URL = os.getenv("BASE_URL", "https://api.example.com")
Helpers
def 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 = None
async def get_db(): global _db if _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 = None
def get_client() -> httpx.AsyncClient: global _client if _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: raise delay = 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 hint pass
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 UI fastmcp run server.py # Run (stdio) fastmcp run server.py --transport http --port 8000 # Run (HTTP) fastmcp inspect server.py # Inspect without running fastmcp install server.py # Install to Claude Desktop fastmcp 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.AsyncClient with 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 template
-
assets/self-contained-server.py -- Server with storage and middleware
-
assets/tools-examples.py -- Tool patterns and type annotations
-
assets/resources-examples.py -- Resource URI patterns
-
assets/prompts-examples.py -- Prompt template patterns
-
assets/client-example.py -- MCP client usage
-
assets/SCRIPTS-TEMPLATE.md -- CLI companion docs template
-
assets/script-template.ts -- TypeScript CLI script template