Override these methods to intercept specific request types:
Hook
When it runs
on_request
Every request (wraps all other hooks)
on_initialize
Client connection handshake
on_call_tool
Tool execution
on_read_resource
Resource reads
on_get_prompt
Prompt retrieval
on_list_tools
Tool listing
on_list_resources
Resource listing
on_list_prompts
Prompt listing
on_set_logging_level
Client sets minimum log level
on_complete
Completion/autocomplete requests
Typed context: Each hook receives a fully-typed context.message. For example, on_initialize gets ServerMiddlewareContext[InitializeRequestParams], so your editor knows exactly what fields are available (like context.message.clientInfo.name). No guessing, full autocomplete.
class AuthMiddleware(Middleware): async def on_call_tool(self, context, call_next): api_key = context.headers.get("x-api-key") if context.headers else None if not api_key or api_key != "secret": raise PermissionError("Invalid API key") return await call_next(context)
Limit requests per session:
Copy
Ask AI
from collections import defaultdictfrom datetime import datetimeclass RateLimitMiddleware(Middleware): def __init__(self, max_per_minute: int = 30): self.max = max_per_minute self.requests: dict[str, list[datetime]] = defaultdict(list) async def on_call_tool(self, context, call_next): sid = context.session_id or "anonymous" now = datetime.now() # Keep only requests from last minute self.requests[sid] = [ t for t in self.requests[sid] if (now - t).total_seconds() < 60 ] if len(self.requests[sid]) >= self.max: raise Exception("Rate limit exceeded") self.requests[sid].append(now) return await call_next(context)
Reject clients during the MCP handshake:
Copy
Ask AI
class ConnectionGuard(Middleware): async def on_initialize(self, context, call_next): client = context.message.clientInfo.name if client in ["blocked-client"]: raise ValueError(f"Client {client} not allowed") print(f"Connection from: {client}") return await call_next(context)
Order matters. Middleware runs in the order added, with earlier middleware wrapping later ones.
Copy
Ask AI
server = MCPServer( middleware=[ LoggingMiddleware(), # 1. Outermost - sees all requests AuthMiddleware(), # 2. Rejects unauthorized early RateLimitMiddleware(), # 3. Limits request rate ValidationMiddleware(), # 4. Innermost - validates data ])
Recommended order: Logging → Authentication → Rate limiting → Validation. This ensures logging sees all requests (including rejected ones) and auth rejects early before expensive operations.