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.

New in v1.6

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.

1

Declare a UI resource

.UIResource("ui://my-app/viz")
2

Host calls your tool

_meta.ui.resourceUri in response
3

Host renders your app

text/html resource in iframe
Define the tool
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
    })
Serve the UI
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
State Machine Visualizer Interactive graph with live transitions
Data Dashboards Charts and filters rendered inline
Config Editors Form-based editing with validation
Workflow Builders Drag-and-drop pipeline design
New in v1.7

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.

SessionStore
store := redis.NewSessionStore(client, 24*time.Hour)
mcp.ServeHTTP(ctx, srv, ":8080",
    mcp.WithSessionStore(store),
)
Tasks
srv.RegisterTask("build", "Run a build", 
    func(ctx, input) (*TaskResult, error) {
        return build(ctx, input)
    })
    
task, _ := srv.Tasks().CreateTask(ctx, 
    CreateTaskRequest{Name: "build"})
New in v1.9

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.

Structured Content
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
    })
Elicitation & Channels
// 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.