What is FineMCP?
MCP (Model Context Protocol) is an open protocol that lets LLM applications — Claude, Cursor, VS Code Copilot, and others — discover and invoke tools, resources, and prompts exposed by a server over JSON-RPC 2.0.
FineMCP is a production-grade Go framework for building MCP servers and clients. It goes beyond protocol plumbing: it provides 16 production middleware, typed tool handlers with automatic JSON Schema generation, streaming responses, client resilience patterns (circuit breaker, load balancer, retry), OpenTelemetry observability, and a zero-network test harness — capabilities that no other MCP implementation offers in any language.
MCP Spec: 2025-11-25 with backward compatibility for 2025-06-18, 2025-03-26, and 2024-11-05.
Why FineMCP?
Most MCP libraries hand you protocol primitives and leave the rest to you. FineMCP treats the server as a system — security, observability, resilience, and correctness are built in from day one.
- Security — RBAC per tool, token-bucket rate limiting, JSON Schema input validation, execution sandbox
- Observability — OpenTelemetry traces and metrics, structured logging, audit log, cost tracking
- Resilience — Circuit breaker, retry with exponential backoff, panic recovery
- Correctness — Auto JSON Schema generation, typed generic handlers, compile-time safety
- Testability — In-process test harness with assertion helpers and golden file diffing
🎯 Flexible Architecture
FineMCP is one framework, three use cases with zero bloat:
- Build MCP servers — Import
github.com/finemcp/finemcp(no client code in your binary) - Build MCP clients — Import
github.com/finemcp/finemcp/client(no server overhead) - Build proxies/gateways — Import both (shared dependencies optimized)
Go's package system ensures you only compile what you import. A server-only binary doesn't include any client code, and vice versa. This architectural separation means you get the power of a unified framework without the cost of unused dependencies.
How FineMCP Compares
| Feature | FineMCP | mcp-go | mcp-golang | official go-sdk |
|---|---|---|---|---|
| Middleware pipeline | ✅ 16 built-in | ❌ | ❌ | ❌ |
| Circuit breaker | ✅ | ❌ | ❌ | ❌ |
| Rate limiting | ✅ | ❌ | ❌ | ❌ |
| Audit log | ✅ | ❌ | ❌ | ❌ |
| Cost tracking | ✅ | ❌ | ❌ | ❌ |
| Sandbox / dry-run mode | ✅ | ❌ | ❌ | ❌ |
| OpenTelemetry | ✅ | ❌ | ❌ | ❌ |
| Client SDK | ✅ full | ❌ | ⚠️ partial | ✅ |
| Client resilience | ✅ retry, LB, backoff | ❌ | ❌ | ❌ |
| Streaming tools | ✅ all transports | ❌ | ❌ | ❌ |
| Multi-tenancy | ✅ | ❌ | ❌ | ❌ |
| WebSocket transport | ✅ | ❌ | ❌ | ❌ |
| Streamable HTTP | ✅ | ❌ | ❌ | ✅ |
| Elicitation / Sampling | ✅ | ❌ | ❌ | ✅ |
| Async tasks | ✅ | ❌ | ❌ | ❌ |
| CLI tools | ✅ repl, call, inspect | ❌ | ❌ | ❌ |
| Zero-network test harness | ✅ | ❌ | ❌ | ❌ |
| Typed handlers + auto schema | ✅ | ✅ | ✅ | ✅ |
| MCP spec 2025-11-25 | ✅ | ✅ | ⚠️ partial | ✅ |
When to Use FineMCP
Choose FineMCP if you need:
- Production-grade security (RBAC, rate limiting, multi-tenancy)
- High performance — see Benchmarks for real numbers
- Enterprise features (audit logs, cost tracking, compliance)
- Type safety and compile-time guarantees
- Single binary deployment
Features
Full MCP Protocol Coverage
| Category | Methods |
|---|---|
| Core | initialize, ping, notifications/initialized, notifications/cancelled |
| Tools | tools/list, tools/call, notifications/tools/list_changed |
| Resources | resources/list, resources/read, resources/templates/list, resources/subscribe, resources/unsubscribe, notifications/resources/list_changed, notifications/resources/updated |
| Prompts | prompts/list, prompts/get, notifications/prompts/list_changed |
| Tasks | tasks/get, tasks/result, tasks/cancel, tasks/list |
| Completions | completion/complete |
| Sampling | sampling/createMessage |
| Elicitation | elicitation/create |
| Roots | roots/list, notifications/roots/list_changed |
| Logging | logging/setLevel, notifications/message |
| Progress | notifications/progress |
Five Transports
| Transport | Description |
|---|---|
| Stdio | Newline-delimited JSON-RPC with signal handling |
| HTTP | Embeddable http.Handler, 204 for notifications |
| SSE | Session management, keepalive, backpressure, auto-cleanup |
| Streamable HTTP | MCP spec 2025-03-26+ primary transport with Mcp-Session-Id and resumability |
| WebSocket | Full-duplex with ping/pong and graceful close |
16 Built-in Middleware
Every tool call flows through an ordered, composable middleware chain:
| Middleware | Description |
|---|---|
| Recovery | Catches panics, returns clean JSON-RPC error |
| Logging | Structured logging — request ID, tool name, duration |
| Auth | Bearer token and API key validation with pluggable TokenVerifier |
| RBAC | Per-tool role-based access control |
| RateLimit | Token-bucket rate limiter, per-tool and per-user bucketing |
| Validation | JSON Schema validation — type, required, nested objects, arrays |
| Sandbox | Execution timeout and output size limits |
| OTel | OpenTelemetry spans, counters, and histograms per tool call |
| Async | Background job execution with state machine and polling |
| Caching | LRU result cache with TTL, input hash keying, per-tool control |
| CircuitBreaker | Three-state machine (closed/open/half-open) with configurable thresholds |
| Retry | Exponential backoff with jitter, context-aware sleep, custom retry classifier |
| AuditLog | SHA-256 input hashing, per-tool include/exclude, compliance-ready trail |
| CostTracking | Per-call cost tracking with custom cost functions and metadata |
| MultiTenant | Per-tenant tool visibility, rate limit buckets, and RBAC policies |
| Simulation | Dry-run mode via _meta.dryRun: true; result marked simulated: true |
Custom middleware is a single func(ToolHandler) ToolHandler.
Authentication & Multi-tenancy: Use HTTPAuth transport middleware + RequireAuth protocol checker for HTTP-layer authentication. Use TenantResolver for multi-tenant tool visibility and resource isolation. See Transports Guide.
Tool System
- Raw handlers —
NewTool()with functional options (WithDescription,WithInputSchema,WithRoles,WithAnnotations) - Typed generic handlers —
NewTypedTool[In, Out]()with automatic JSON marshal/unmarshal - Auto JSON Schema generation — struct tags (
json,description,omitempty) generate schemas automatically - Tool annotations —
readOnlyHint,destructiveHint,idempotentHint,openWorldHint,title - Tool composition —
Pipeline(sequential),Parallel(concurrent fan-out),FanOutFanIn(parallel + merge) - Bitmap name validation — O(1) per character,
[A-Za-z0-9_-.]
Client SDK
FineMCP includes a production-grade MCP client SDK with resilience patterns, observability, and multi-server capabilities:
Core Client
- Type-safe client with automatic request/response handling
- Transport abstraction supporting stdio, HTTP, SSE, WebSocket
- REPL for interactive debugging and testing
Resilience Patterns
- Circuit breaker — fail fast when servers are unhealthy
- Retry with exponential backoff — automatic retry on transient failures
- Reconnect logic — automatic reconnection with configurable strategies
- Load balancer — distribute requests across multiple servers (round-robin, least-connections, random)
- Request coalescing — deduplicate identical in-flight requests
Observability
- OpenTelemetry integration — traces, metrics, and logs for all client operations
- Structured logging — consistent log format across all client operations
Multi-Server Patterns
- Aggregator client — query multiple MCP servers and merge results
- Context propagation — propagate trace context across server boundaries
Caching & Optimization
- Response caching — cache tool/resource/prompt responses with TTL
- Pagination helpers — automatic pagination for large result sets
Multi-Tenancy
FineMCP has first-class multi-tenancy built into the dispatch layer. A TenantResolver runs on every request to identify the tenant and apply per-tenant tool/resource visibility, rate-limit buckets, and RBAC policies — with zero application code changes.
import (
"github.com/finemcp/finemcp"
"github.com/finemcp/finemcp/middleware"
)
// Define per-tenant configurations
configs := map[string]*middleware.TenantConfig{
"tenant-free": {
ToolFilter: func(t *finemcp.Tool) bool {
return t.Annotations != nil && t.Annotations.Title == "basic"
},
},
"tenant-pro": {
// nil filters = all tools/resources visible
},
}
store := middleware.NewStaticTenantStore(configs)
// Identify tenant from JWT subject (or any auth header)
resolver := middleware.NewTenantResolver(
middleware.TenantFromAuthSubject(),
store,
middleware.WithFallbackTenant("tenant-free"),
)
server := finemcp.NewServer("my-server", "1.0.0")
server.SetTenantResolver(resolver)
// Each request now automatically scopes tools to the caller's tenant.
// Read tenant ID in any handler or middleware:
// tenantID := finemcp.TenantIDFromCtx(ctx)
Authentication
- OAuth2 helpers — built-in support for OAuth2 token refresh
- Bearer token auth — automatic token injection and refresh
Example:
import (
"context"
"log"
"github.com/finemcp/finemcp/client"
"github.com/finemcp/finemcp/client/circuitbreaker"
"github.com/finemcp/finemcp/client/retry"
"github.com/finemcp/finemcp/transport"
)
// Create stdio transport to MCP server
stdioTransport, err := transport.NewStdioTransport("./server-binary")
if err != nil {
log.Fatal(err)
}
// Client with circuit breaker and retry
c, err := client.NewClient(
client.WithTransport(stdioTransport),
circuitbreaker.WithCircuitBreaker(),
retry.WithRetry(retry.DefaultConfig()),
)
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Call tool on remote server
ctx := context.Background()
result, err := c.CallTool(ctx, "calculator", map[string]any{
"expression": "2 + 2",
})
if err != nil {
log.Fatal(err)
}
log.Printf("Result: %v", result)
For complete examples, see finemcp.dev/learn.
Streaming Tool Responses
Tool handlers can stream incremental content chunks to the client during execution via notifications/progress:
stream := finemcp.StreamFromCtx(ctx)
if stream != nil {
stream.SendText("processing row 42…")
stream.Send(finemcp.ImageContent{Data: img, MimeType: "image/png"})
}
return finalResult, nil
- Backpressure:
Sendblocks when the buffer is full - Ordering: sequence numbers reflect only chunks actually delivered
- Flush on close: all buffered chunks are flushed before close returns
- Nil safety: typed-nil pointers are rejected with a descriptive error
Transport note:
StreamFromCtxreturnsnilonly for plain HTTP (transport/http), which has no persistent connection for notifications. Stdio, WebSocket, SSE, and Streamable HTTP all fully support streaming.
Resources & Prompts
- Static resources (text + blob) and URI templates (RFC 6570)
- Per-client subscription tracking with update and list-changed notifications
- Prompt registration with named arguments, handler dispatch, and list-changed notifications
Async Tasks
- Spec-compliant task lifecycle:
pending → running → complete / failed / cancelled tasks/get,tasks/result,tasks/cancel,tasks/listwith ownership-based access control- Crypto-random task IDs, panic recovery, timeouts, cancellation, graceful shutdown drain
Protocol Highlights
- JSON-RPC 2.0 with notification detection and request ID normalization
- Protocol version negotiation across four spec versions
_metaextension on all request/result types with_meta.progressTokensupport- Cursor-based pagination for all list methods (default page size 50, max 1000)
- Init gate (
atomic.Bool) rejects pre-init requests - Graceful shutdown with in-flight request draining
Test Harness (mcptest)
NewServer(t, ...Option)— zero-network, in-process server- Request helpers: Initialize, CallTool, ListTools, ListResources, ReadResource, ListPrompts, GetPrompt, Notify, RawCall
- Assertion helpers:
AssertToolResult,AssertToolCount,AssertError,AssertResourceText,AssertPromptMessage - Fixture loading and golden file diffing
Installation
Install the FineMCP framework to build MCP servers and clients:
go get github.com/finemcp/finemcp
Go version: Go 1.25+ required (latest two stable releases supported), following the Go release policy.
Packages
| Package | Import Path | Purpose |
|---|---|---|
| Core | github.com/finemcp/finemcp |
Server, tools, protocol, middleware chain |
| Middleware | github.com/finemcp/finemcp/middleware |
16 built-in middleware (recovery, logging, RBAC, rate limiting, validation, sandbox, OpenTelemetry, async tasks, caching, circuit breaker, retry, audit logging, cost tracking, simulation, auth, multi-tenancy) |
| Client | github.com/finemcp/finemcp/client |
Client SDK with 14 modules for resilience (circuit breaker, retry, load balancer, reconnect), observability (OpenTelemetry, logging), optimization (caching, coalescing, pagination), multi-server patterns (aggregator, context propagation), and authentication (OAuth2) |
| Transport | github.com/finemcp/finemcp/transport |
Stdio, HTTP, SSE, Streamable HTTP, WebSocket |
| Test Harness | github.com/finemcp/finemcp/mcptest |
In-process testing helpers |
CLI Tools
For debugging, testing, and inspecting MCP servers, install the finemcp CLI:
Go install (recommended for Go developers):
go install github.com/finemcp/finemcp/cmd/finemcp@latest
macOS:
curl -L https://github.com/finemcp/finemcp/releases/latest/download/finemcp_darwin_amd64.tar.gz | tar xz
sudo mv finemcp /usr/local/bin/
Linux:
curl -L https://github.com/finemcp/finemcp/releases/latest/download/finemcp_linux_amd64.tar.gz | tar xz
sudo mv finemcp /usr/local/bin/
Windows (PowerShell):
# Download and extract latest release (no admin required)
$url = "https://github.com/finemcp/finemcp/releases/latest/download/finemcp_windows_amd64.zip"
Invoke-WebRequest -Uri $url -OutFile finemcp.zip
Expand-Archive finemcp.zip -DestinationPath .
# Install to user-local bin (no admin needed)
$userBin = "$env:USERPROFILE\.local\bin"
New-Item -ItemType Directory -Force -Path $userBin | Out-Null
Move-Item finemcp.exe $userBin -Force
# Add to PATH permanently (current user only)
$currentPath = [Environment]::GetEnvironmentVariable("Path", "User")
if ($currentPath -notlike "*$userBin*") {
[Environment]::SetEnvironmentVariable("Path", "$currentPath;$userBin", "User")
}
Write-Host "Installed to $userBin - restart your terminal to use 'finemcp'"
Or system-wide (requires Administrator):
# After extracting finemcp.exe:
Move-Item finemcp.exe C:\Windows\System32\
Or download manually:
Download the .zip file from GitHub Releases, extract finemcp.exe, and add it to your PATH.
Quick test:
finemcp serve --http :8080 # Start test server
finemcp inspect http http://localhost:8080 # Inspect server capabilities
finemcp repl http http://localhost:8080 # Interactive debugging REPL
Windows (PowerShell):
finemcp serve --http :8080 # Start test server
finemcp inspect http http://localhost:8080 # Inspect server capabilities
finemcp repl http http://localhost:8080 # Interactive debugging REPL
💡 When to use CLI: Debug tool visibility issues with Claude/Cursor, inspect server capabilities before deploying, test connectivity, and run interactive REPL sessions for development and troubleshooting.
CLI Commands
The finemcp CLI provides essential debugging and testing utilities:
| Command | Description | Example |
|---|---|---|
| serve | Launch test ping server | finemcp serve --http :8080 |
| repl | Interactive REPL session | finemcp repl stdio ./server |
| list | List tools/resources/prompts | finemcp list http http://localhost:8080 |
| call | Call a tool and see result | finemcp call http http://localhost:8080 toolName |
| inspect | Full JSON capability dump | finemcp inspect http http://localhost:8080 |
| version | Show version info | finemcp version |
Common use cases:
- 🐛 Debug why Claude isn't seeing your tools →
finemcp inspect - 🧪 Test a server before deploying →
finemcp servethen connect Claude Desktop - 📊 Verify tool schemas →
finemcp listshows all registered capabilities - ⚡ Quick demos without code →
finemcp serve --http :8080gives you a working server instantly - 🔍 Troubleshoot stdio transport →
finemcp repl stdio ./myservershows real-time communication
Run finemcp <command> --help for detailed usage.
Quick Start
Get a server running in under 10 lines:
package main
import (
"context"
"log"
"github.com/finemcp/finemcp"
"github.com/finemcp/finemcp/transport"
)
type Input struct{ Name string `json:"name"` }
func main() {
s := finemcp.NewServer("myapp", "1.0")
tool, _ := finemcp.NewTypedTool("greet", func(_ context.Context, in Input) (string, error) {
return "Hello, " + in.Name + "!", nil
})
s.RegisterTool(tool)
log.Fatal(transport.StartHTTP(s, ":8080"))
}
Add safety and observability with middleware:
package main
import (
"context"
"log"
"github.com/finemcp/finemcp"
"github.com/finemcp/finemcp/middleware"
"github.com/finemcp/finemcp/transport"
)
type appLogger struct{}
func (appLogger) Info(msg string, kv ...any) { log.Println(append([]any{msg}, kv...)...) }
func (appLogger) Error(msg string, kv ...any) { log.Println(append([]any{"ERROR", msg}, kv...)...) }
type Input struct {
Name string `json:"name" description:"Name to greet"`
}
func main() {
s := finemcp.NewServer("myapp", "1.0")
// Add production middleware
s.Use(middleware.Recovery()) // Catch panics
s.Use(middleware.Logging(appLogger{})) // Structured logs
s.Use(middleware.Validation()) // JSON Schema validation
tool, err := finemcp.NewTypedTool("greet",
func(_ context.Context, in Input) (string, error) {
return "Hello, " + in.Name + "!", nil
},
finemcp.WithDescription("Greets someone by name"),
)
if err != nil {
log.Fatal(err)
}
s.RegisterTool(tool)
log.Fatal(transport.StartHTTP(s, ":8080"))
}
Full production setup with security, rate limiting, and observability:
package main
import (
"context"
"log"
"time"
"github.com/finemcp/finemcp"
"github.com/finemcp/finemcp/middleware"
"github.com/finemcp/finemcp/transport"
)
type appLogger struct{}
func (appLogger) Info(msg string, kv ...any) { log.Println(append([]any{msg}, kv...)...) }
func (appLogger) Error(msg string, kv ...any) { log.Println(append([]any{"ERROR", msg}, kv...)...) }
type Input struct {
Name string `json:"name" description:"Name to greet"`
}
func main() {
s := finemcp.NewServer("myapp", "1.0")
// Production middleware stack
s.Use(middleware.Recovery()) // Panic recovery
s.Use(middleware.Logging(appLogger{})) // Structured logging
s.Use(middleware.RateLimit(100)) // 100 req/sec rate limit
s.Use(middleware.RBAC()) // Role-based access (roles set per-tool with WithRoles)
s.Use(middleware.Validation()) // Input validation
s.Use(middleware.Sandbox( // Timeout & output size limits
middleware.WithTimeout(5*time.Second),
middleware.WithMaxOutputSize(1<<20),
))
s.Use(middleware.OTel()) // OpenTelemetry (configure providers via WithTracerProvider/WithMeterProvider)
s.Use(middleware.AuditLog( // Audit logging
middleware.WithAuditSink(middleware.AuditSinkFunc(func(ctx context.Context, e middleware.AuditEntry) {
log.Printf("[AUDIT] tool=%s duration=%v", e.ToolName, e.Duration)
})),
))
// Register typed tool with full options
tool, err := finemcp.NewTypedTool("greet",
func(_ context.Context, in Input) (string, error) {
return "Hello, " + in.Name + "!", nil
},
finemcp.WithDescription("Greets someone by name"),
finemcp.WithRoles("user", "admin"),
finemcp.WithReadOnly(),
finemcp.WithIdempotent(),
)
if err != nil {
log.Fatal(err)
}
s.RegisterTool(tool)
log.Printf("Starting MCP server on :8080")
log.Fatal(transport.StartHTTP(s, ":8080"))
}
💡 Tip: Start simple and add middleware as you need it. Each middleware is opt-in and composable.
Benchmarks
All numbers are from the bundled benchmark suite (go test -bench=. -benchmem ./client/...) using an in-process mock transport on Apple M1 — no real network, so actual HTTP/stdio/WebSocket throughput will vary by network and payload size.
| Benchmark | ns/op | ops/sec | Memory |
|---|---|---|---|
CallTool_Small (100B, single goroutine) |
12,617 | ~79k | 5 KB / 98 allocs |
CallTool_Parallel (GOMAXPROCS goroutines) |
4,391 | ~228k | 5 KB / 98 allocs |
ListTools |
9,019 | ~111k | 4 KB / 94 allocs |
StdioTransport round-trip |
8,477 | ~118k | 4 KB / 98 allocs |
HTTPTransport round-trip |
8,790 | ~114k | 4 KB / 98 allocs |
WebSocketTransport round-trip |
8,611 | ~116k | 4 KB / 98 allocs |
StreamableHTTP round-trip |
8,540 | ~117k | 4 KB / 98 allocs |
CallTool_WithCaching (cache hit) |
7,449 | ~134k | 3 KB / 78 allocs |
Streaming 10 chunks |
43,991 | ~23k | 27 KB / 438 allocs |
Concurrent_100 (100 goroutines) |
677k total | — | 461 KB / 9.9k allocs |
Run the benchmarks yourself:
go test -bench=. -benchmem -run='^$' ./client/...
MCP Spec Roadmap
FineMCP targets MCP spec 2025-11-25 — the current stable release. As the spec evolves we track changes here:
| Spec version | Status | Notes |
|---|---|---|
2024-11-05 |
✅ Supported | Legacy compat |
2025-03-26 |
✅ Supported | |
2025-06-18 |
✅ Supported | |
2025-11-25 |
✅ Current | Full compliance |
Protocol negotiation is handled automatically — the server advertises the highest version it supports and downgrades gracefully when a client requests an older version.
Documentation
📚 Complete Documentation — Guides, API reference, architecture, middleware, transports, and best practices
🎓 Examples & Tutorials — Working code samples and step-by-step tutorials
| Document | Description |
|---|---|
| Contributing | Development workflow, commit guidelines, coding style |
| Security | Security policy and vulnerability reporting |
Contributing
Contributions welcome. See CONTRIBUTING.md for guidelines.
License
MIT License. See LICENSE.
No comments yet
Be the first to share your take.