1.8 KiB
1.8 KiB
Python MCP Server Best Practices (FastMCP)
Setup
uv init mcp-server-name
uv add "mcp[cli]"
Implementation
- ✓ Use type hints (FastMCP reads them!)
- ✓ Write detailed docstrings (becomes tool descriptions)
- ✓ Use async functions for I/O
- ✓ Handle exceptions gracefully
- ✓ Log to stderr only in STDIO mode
Tool Definition
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("server-name")
@mcp.tool()
async def search_database(query: str, limit: int = 10) -> str:
"""
Search the database for records matching the query.
Args:
query: Search terms to match against records
limit: Maximum number of results to return (default: 10)
Returns:
JSON string of matching records
"""
results = await db.search(query, limit=limit)
return json.dumps(results)
Resource Definition
@mcp.resource("db://tables/{table_name}/schema")
async def get_table_schema(table_name: str) -> str:
"""Get the schema for a database table."""
schema = await db.get_schema(table_name)
return json.dumps(schema)
Critical Rules
STDIO Transport
import sys
# WRONG - corrupts JSON-RPC
print("Debug info")
# CORRECT - safe for STDIO
print("Debug info", file=sys.stderr)
# Or use logging
import logging
logging.basicConfig(level=logging.INFO, stream=sys.stderr)
Error Handling
@mcp.tool()
async def risky_operation(param: str) -> str:
"""Perform an operation that might fail."""
try:
result = await external_api.call(param)
return str(result)
except Exception as e:
logging.error(f"Operation failed: {e}")
raise ValueError(f"Operation failed: {e}")
Run
# With uv
uv run main.py
# Direct
python main.py