Gin for MCP servers in Go
mcp-go is a framework for building Model Context Protocol servers. It provides typed handlers, automatic schema generation, middleware, and production-ready defaults.
package main
import (
"context"
"log"
"go.klarlabs.de/mcp"
)
type SearchInput struct {
Query string `json:"query" jsonschema:"required"`
Limit int `json:"limit" jsonschema:"maximum=100"`
}
func main() {
srv := mcp.NewServer(mcp.ServerInfo{
Name: "search-server",
Version: "1.0.0",
})
srv.Tool("search").
Description("Search for documents").
Handler(func(ctx context.Context, in SearchInput) ([]string, error) {
// search is your custom search function
return search(in.Query, in.Limit), nil
})
log.Fatal(mcp.ServeStdio(context.Background(), srv))
} Why mcp-go?
Building MCP servers directly on SDKs means solving the same problems repeatedly. mcp-go solves them once—idiomatically and safely.
Typed Handlers
Define inputs as Go structs. Invalid data never reaches your handler.
Automatic JSON Schema
Schemas are derived from struct tags and exposed via MCP introspection.
Middleware
Gin-style middleware for auth, timeouts, logging, and rate limiting.
Multiple Transports
Same server runs over stdio, HTTP+SSE, WebSocket, or gRPC. Switch with one line.
Production Defaults
Panic recovery, request IDs, graceful shutdown. Safe by default.
MCP Compliant
Implements the full MCP spec including tools, resources, and prompts.
MCP Apps
Attach interactive UIs to tools with UIResource(). Hosts render your app alongside results.
MCP Apps — Interactive UIs for Tools
Turn tool results into live experiences. Attach interactive UIs — dashboards, visualizers, editors — that hosts render alongside your tool output.
Declare a UI resource
.UIResource("ui://my-app/viz") Host calls your tool
_meta.ui.resourceUri in response
Host renders your app
text/html resource in iframe
srv.Tool("visualize").
Description("Visualize data").
UIResource("ui://my-app/dashboard").
Handler(func(ctx context.Context, in Input) (any, error) {
return fetchData(in.ID), nil
}) srv.Resource("ui://my-app/dashboard").
Name("Dashboard").
MimeType("text/html").
Handler(func(ctx context.Context, uri string,
params map[string]string,
) (*mcp.ResourceContent, error) {
return &mcp.ResourceContent{
URI: uri, MimeType: "text/html",
Text: dashboardHTML,
}, nil
}) Bidirectional Communication
Apps aren't static. The embedded UI communicates back to the host via postMessage JSON-RPC.
App → Host
- Call other MCP tools
- Push state into conversation
- Read server resources
Host → App
- Pass tool results and data
- Sync theme (light/dark)
- Notify of resize events
Enterprise Features
Production-ready features for scaling and securing MCP servers in enterprise environments.
Horizontal Scaling
SessionStore interface for load-balanced deployments. Redis-backed persistence with TTL support.
Server Discovery
Clients discover servers via /.well-known/mcp endpoint with protocol and capability info.
Tasks
Async task execution with create, get, list, and cancel operations for long-running operations.
Enterprise Middleware
Audit logging, distributed tracing with correlation IDs, rate limiting, and size limits.
store := redis.NewSessionStore(client, 24*time.Hour)
mcp.ServeHTTP(ctx, srv, ":8080",
mcp.WithSessionStore(store),
) srv.RegisterTask("build", "Run a build",
func(ctx, input) (*TaskResult, error) {
return build(ctx, input)
})
task, _ := srv.Tasks().CreateTask(ctx,
CreateTaskRequest{Name: "build"}) Dynamic MCP — Structured Content, Elicitation & Channels
Tools return typed structured data. Servers ask users for input mid-task. Push messages flow without polling.
Structured Content
Tools declare OutputSchema and return typed StructuredResult alongside text content blocks.
Dynamic Registration
RemoveTool(), RemoveResource(), RemovePrompt() with listChanged notifications.
Elicitation
Request structured input from users mid-task via ElicitFromContext(ctx) with JSON Schema forms.
Channels
Push messages proactively via ChannelFromContext(ctx). No polling — servers notify clients of DOM changes, network events, and navigation.
srv.Tool("extract_table").
OutputSchema(TableOutput{}).
Handler(func(ctx context.Context, in Input) (mcp.StructuredResult, error) {
return mcp.StructuredResult{
Content: []mcp.Content{mcp.NewTextContent("Found 3 rows")},
StructuredContent: map[string]any{
"headers": []string{"name", "age"},
},
}, nil
}) // Ask user for clarification
elicitor := mcp.ElicitFromContext(ctx)
result, _ := elicitor.Elicit(ctx, &mcp.ElicitRequest{
Message: "Which field?",
})
// Push events to AI session
channel := mcp.ChannelFromContext(ctx)
channel.SendText("nav", "Page changed to /dashboard") How it fits into the MCP ecosystem
mcp-go is a framework, not an SDK. It builds on the MCP specification to provide application structure.
| Project | What it is | Analogy |
|---|---|---|
| MCP Go SDK | Low-level protocol implementation | net/http |
| mark3labs/mcp-go | Community SDK with helpers | HTTP client library |
| mcp-go | Full application framework | Gin |
We build on top of the MCP spec—not instead of it. If you need raw protocol access, use an SDK. If you're building real services, use mcp-go.
Ready to get started?
Build your first MCP server in under 5 minutes.