<< All versions

Skill v1.0.1

currentAutomated scan100/100
jezweb/claude-skills/mcp-builder

+16 new

──Details
PublishedOctober 2, 2026 at 01:20 PM
Content Hashsha256:93a8afb4f50417f2...
Git SHAee91a87a5c5e
Bump Typepatch
Compare with v1.0.0
──Files
Files (1 file, 12.1 KB)
SKILL.md12.1 KBactive
SKILL.md · 443 lines · 12.1 KB

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

bash
pip install fastmcp

Create the server file. The server instance MUST be at module level:

python
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:

bash
python server.py

Dev mode with inspector UI (recommended):

bash
fastmcp dev server.py
# Opens inspector at http://localhost:5173
# Hot reload, detailed logging, tool/resource inspection

HTTP mode for remote clients:

bash
python server.py --transport http --port 8000

Automated test script using FastMCP Client:

python
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):

  1. Server file exists
  2. Python syntax valid: python3 -m py_compile server.py
  3. Module-level server object (not inside a function):

``bash grep -q "^mcp = FastMCP\|^server = FastMCP\|^app = FastMCP" server.py ``

  1. requirements.txt exists with PyPI packages only (no git+, -e, .whl, .tar.gz)
  2. No hardcoded secrets (check for api_key = "..." patterns excluding os.getenv/os.environ)

Advisory (warnings):

  1. fastmcp listed in requirements.txt
  2. .gitignore includes .env
  3. No circular imports
  4. Git repository initialised with remote
  5. Server can load: timeout 5 fastmcp inspect server.py

Step 6: Deploy

FastMCP Cloud (simplest):

bash
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):

dockerfile
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): 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:

python
# 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:

python
@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:

python
@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

python
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.

ErrorCauseFix
RuntimeError: No server object found at module levelServer inside a functionExport mcp = FastMCP(...) at module level
RuntimeError: no running event loopMissing async/awaitUse async def for async operations
TypeError: missing required argument 'context'Context not type-hintedAdd context: Context with type hint
ValueError: Invalid resource URIMissing URI schemeUse data://, file://, info://, api://
Resource template parameter mismatchName mismatchuser://{user_id} needs def get_user(user_id: str)
Pydantic validation errorWrong type hintsEnsure hints match actual data types
Transport mismatchClient/server protocol differMatch both to stdio or both to http
Import errors with editable packagePackage not installedpip install -e . or add to PYTHONPATH
DeprecationWarning: mcp.settingsOld APIUse os.getenv() instead
Port already in useStale process`lsof -ti:8000 \xargs kill -9`
Schema generation failureNon-JSON typesUse JSON-compatible types (no NumPy arrays)
JSON serialization errordatetime/bytes in responseConvert to .isoformat() or string
Circular importFactory in __init__.pyUse direct imports, avoid factory pattern
Python 3.12+ datetime warningdatetime.utcnow() deprecatedUse datetime.now(timezone.utc)
Import-time executionAsync resource at module levelUse lazy init pattern

Production Patterns

Self-Contained Server

Keep all utilities in one file to avoid circular imports:

python
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:

python
_db = None
async def get_db():
global _db
if _db is None:
_db = await create_connection(Config.DB_URL)
return _db

Health Check Resource

python
@mcp.resource("health://status")
async def health_check() -> dict:
return {
"status": "healthy",
"version": "1.0.0",
"checks": {
"api": "connected",
"database": "connected"
}
}

Connection Pooling

python
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

python
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

python
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

python
@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)

python
@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

bash
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
← v1.0.0All versions