Middleware
Demonstrates 16 middleware types that wrap tool handlers with cross-cutting concerns.
View source on GitHub →Demonstrates 16 middleware types that wrap tool handlers with cross-cutting concerns.
How Middleware Works
Middleware is applied to the server and wraps all tool invocations:
s := finemcp.NewServer("my-server", "1.0.0")
s.Use(middleware.Recovery())
s.Use(middleware.Logging(logger))
s.Use(middleware.RateLimit(10, middleware.WithBurst(20)))
Middleware executes in order: first applied = outermost wrapper.
Middleware Reference
recovery/
Catches panics in tool handlers and converts them to error responses.
s.Use(middleware.Recovery())
logging/
Logs every tool invocation with duration and result status.
s.Use(middleware.Logging(logger))
// logger implements: Info(msg string, keysAndValues ...any), Error(msg string, keysAndValues ...any)
auth/
HTTP-level authentication with bearer tokens and API keys.
verifier := middleware.ChainVerifiers(
middleware.StaticBearerTokenVerifier(map[string]string{"token-123": "user1"}),
middleware.StaticAPIKeyVerifier(map[string]string{"key-abc": "service1"}),
)
handler := middleware.HTTPAuth(verifier, transport.Handler(s))
// Test with: curl -H "Authorization: Bearer token-123" ...
rbac/
Role-based access control — restricts tools to specific user roles.
s.Use(middleware.RBAC())
// Tools declare required roles:
finemcp.NewTool("admin-tool", handler, finemcp.WithRoles("admin"))
// Tools without roles are public
ratelimit/
Limits tool invocation rate with token bucket algorithm.
s.Use(middleware.RateLimit(10, middleware.WithBurst(20)))
// 10 requests/sec, burst up to 20
cache/
Caches tool results for a configurable TTL.
s.Use(middleware.Cache(middleware.WithCacheTTL(5 * time.Minute)))
retry/
Automatically retries failed tool invocations.
s.Use(middleware.Retry(middleware.WithMaxAttempts(3)))
circuitbreaker/
Opens circuit after repeated failures to prevent cascading issues.
s.Use(middleware.CircuitBreaker(
middleware.WithFailureThreshold(5),
middleware.WithSuccessThreshold(3),
))
sandbox/
Restricts tool execution with timeout and output size limits.
s.Use(middleware.Sandbox(
middleware.WithTimeout(5 * time.Second),
middleware.WithMaxOutputSize(1024 * 1024), // 1MB
))
validation/
Validates tool inputs against their JSON schemas before invoking the handler.
s.Use(middleware.Validation())
// Tools must have: finemcp.WithInputSchema(map[string]any{...})
async/
Converts long-running tools into background tasks.
store := finemcp.NewTaskStore()
asyncMiddleware, waiter := middleware.Async()
s.Use(asyncMiddleware)
// Server options: finemcp.WithTaskStore(store)
auditlog/
Logs all tool invocations for audit/compliance.
s.Use(middleware.AuditLog(middleware.WithAuditSink(func(entry middleware.AuditEntry) {
fmt.Printf("[AUDIT] tool=%s duration=%v success=%v\n", entry.ToolName, entry.Duration, entry.Success)
})))
costtracking/
Tracks per-tool cost and usage metrics.
s.Use(middleware.CostTracking(middleware.WithCostCollector(func(record middleware.CostRecord) {
fmt.Printf("[COST] tool=%s duration=%v\n", record.ToolName, record.Duration)
})))
simulation/
Dry-run mode for destructive tools.
s.Use(middleware.Simulation())
// Tools marked destructive run in simulation mode:
finemcp.NewTool("delete-db", handler, finemcp.WithDestructive())
// Inside handler, check: finemcp.IsSimulatedFromCtx(ctx)
multitenant/
Tenant isolation with per-tenant tool access control.
resolver := middleware.NewTenantResolver(
middleware.TenantFromAuthMeta("tenant_id"),
middleware.NewStaticTenantStore(map[string]middleware.TenantConfig{
"tenant-a": { ToolFilter: func(name string) bool { return name == "safe-tool" } },
}),
)
s.SetTenantResolver(resolver)
otel/
OpenTelemetry distributed tracing and metrics.
s.Use(middleware.OTel())
Stacking Middleware
Middleware composes naturally — stack multiple layers:
s.Use(middleware.Recovery()) // outermost: catch panics
s.Use(middleware.Logging(logger)) // log all calls
s.Use(middleware.RateLimit(10)) // throttle
s.Use(middleware.Validation()) // validate input schemas
s.Use(middleware.Cache(middleware.WithCacheTTL(5 * time.Minute)))
Testing with curl
# Most middleware examples
go run ./13-middleware/recovery
curl -X POST http://localhost:8080 -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0", "id": 1, "method": "initialize",
"params": { "protocolVersion": "2025-03-26", "clientInfo": { "name": "curl", "version": "1.0.0" }, "capabilities": {} }
}'
# Auth example (requires token)
go run ./13-middleware/auth
curl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer admin-token" \
-d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }'