GoMCP
This GoMCP server provides search, async reporting, and connectivity testing capabilities for AI applications like Claude Desktop, Cursor, and VS Code Copilot.
Keyword Search (
search@1.0): Full-text keyword search across indexed content, ranked by relevance score. Supports a configurable result limit (1–100, default 10).Semantic Search (
search@2.0): Embedding-based search that matches by intent rather than exact keywords, ranked by semantic similarity. Same limit options as keyword search.Document Search (
search.docs): Keyword search within documents, returning titles and content snippets with pagination support (1–100, default 10).Async Report Generation (
report): Generates analytics reports as a long-running async operation — returns a task ID immediately for polling viatasks/get.Hello / Connectivity Test (
hello): Greets a user by name (defaults to 'World') to verify basic server connectivity.
All search tools include built-in parameter validation (required query, constrained limit), and the versioned search tools demonstrate the server's ability to manage multiple tool versions simultaneously.
Allows importing existing Gin web framework routes as MCP tools with one line of code, enabling AI applications to interact with existing Gin-based APIs through the MCP protocol.
Provides access to the GoMCP framework repository hosted on Gitee, enabling developers to access the codebase and documentation through this Chinese Git hosting service.
Provides access to the GoMCP framework repository hosted on GitHub, enabling developers to access the codebase, releases, and documentation through the GitHub platform.
Provides distributed tracing capabilities through OpenTelemetry middleware, enabling observability and performance monitoring of MCP server operations.
Allows generating MCP tools from Swagger/OpenAPI 3.x documentation with one line of code, enabling AI applications to interact with existing REST APIs through the MCP protocol.
Provides hot-reload capabilities for tool definitions from YAML files with file watching, enabling dynamic configuration management of MCP tools without server restarts.
GoMCP
The fast, idiomatic way to build MCP servers in Go.
🚀 Quick Links
MCP Protocol: https://modelcontextprotocol.io
Related MCP server: Filesystem MCP Server
🎯 What is GoMCP?
GoMCP is a framework for building Model Context Protocol (MCP) servers — not just an SDK. Think of it as "Gin for MCP".
MCP is the open protocol that lets AI applications (Claude Desktop, Cursor, Kiro, VS Code Copilot) call external tools, read data sources, and use prompt templates. GoMCP makes building those servers trivial.
Why GoMCP?
mcp-go (mark3labs) | Official Go SDK | GoMCP | |
Level | SDK | SDK | Framework |
Schema generation | Manual |
|
|
Middleware | Basic hooks | None | Full chain (Logger, Auth, RateLimit, OTel…) |
Tool groups | No | No | Yes ( |
Import Gin routes | No | No | ✅ One line |
Import OpenAPI/Swagger | No | No | ✅ One line |
Import gRPC services | No | No | ✅ |
Built-in auth | No | No | Bearer / API Key / Basic + RBAC (Bearer = your token/JWT validator) |
Inspector UI | No | No | ✅ |
Test utilities | Basic | No | mcptest package |
🛠️ Tech Stack
Environment Requirements
Requirement | Version |
Go | ≥ 1.25 |
MCP Protocol | 2024-11-05 (backward compatible with 2025-11-25) |
Note on the Go 1.25 requirement. GoMCP's
go.moddeclaresgo 1.25.0so the project always builds with the toolchain that ships current security and runtime fixes. If you are running Go 1.21+ locally with the defaultGOTOOLCHAIN=auto, Go will automatically download and use the matching toolchain for you — no manual upgrade is needed. If you have pinnedGOTOOLCHAIN=local, install Go 1.25+ or unset the pin.
Core Dependencies
Technology | Description |
Go standard library | Framework routing, JSON-RPC, transports — no forced DB/ORM deps |
Gin | Adapter only — import existing Gin routes |
gRPC | Adapter only — import gRPC services |
OpenTelemetry | Optional — distributed tracing |
YAML v3 | Provider only — hot-reload tool definitions |
🌟 Core Features
🔧 Tool Development
Struct-tag auto schema — define parameters with Go structs and
mcptags, JSON Schema generated automaticallyTyped handlers —
func(*Context, Input) (Output, error)— no manual parameter parsingParameter validation — required, min/max, enum, pattern — checked before your handler runs
Component versioning — register multiple versions, clients call
name@versionAsync tasks — long-running tools return task ID, with polling and cancellation
🔌 Adapters (Core Differentiator)
Gin adapter — import existing Gin routes as MCP tools with one line
OpenAPI adapter — generate tools from Swagger/OpenAPI 3.x docs
gRPC adapter — import gRPC service methods as MCP tools
🔐 Security
BearerAuth — Bearer token check via your validator (JWT parsing is up to you; this library does not decode JWTs)
APIKeyAuth — API key validation via header
BasicAuth — HTTP Basic authentication
RequireRole / RequirePermission — RBAC authorization on tool groups
🧩 Framework Features
Middleware chain — Logger, Recovery, RequestID, Timeout, RateLimit, OpenTelemetry
Tool groups — organize tools with prefixes and group-level middleware
Resource & Prompt — full MCP support including URI templates and parameterized prompts
Auto-completions — suggest values for prompt/resource arguments
🚀 Production Ready
Multiple transports — stdio (Claude Desktop, Cursor, Kiro) and Streamable HTTP with SSE
MCP Inspector — built-in web debug UI for browsing and testing tools
Hot-reload — load tool definitions from YAML files with file watching
mcptest package — in-memory client for unit testing with snapshot support
Lifecycle —
Close(), session idle eviction, async concurrency — see Server lifecycle, sessions & async tasks.
🏗️ Architecture
┌──────────────────────────────────────────────────────────────┐
│ User Code │
│ s.Tool() / s.ToolFunc() / s.Resource() / s.Prompt() │
├──────────────────────────────────────────────────────────────┤
│ Framework Core │
│ Router → Middleware Chain → Validation → Handler → Result │
├────────────┬─────────────┬───────────────┬───────────────────┤
│ Schema │ Validator │ Adapters │ Observability │
│ Generator │ Engine │ Gin/OpenAPI/ │ OTel / Logger │
│ (mcp tags) │ (auto) │ gRPC │ / Inspector │
├────────────┴─────────────┴───────────────┴───────────────────┤
│ Protocol Layer │
│ JSON-RPC 2.0 / MCP / Capability Negotiation │
├──────────────────────────────────────────────────────────────┤
│ Transport Layer │
│ stdio / Streamable HTTP + SSE │
└──────────────────────────────────────────────────────────────┘Project Structure
gomcp/
├── server.go # Server core, tool/resource/prompt registration
├── context.go # Request context with typed accessors
├── group.go # Tool groups with prefix naming
├── middleware.go # Middleware chain helpers (SkipAuthForMCPMethods, handshake skips)
├── middleware_builtin.go # Logger, Recovery, RequestID, Timeout, RateLimit
├── middleware_auth.go # Bearer/API key/Basic auth, RBAC, SSE auth helpers
├── middleware_otel.go # OpenTelemetry tracing
├── schema/ # struct tag → JSON Schema generator + validator
├── transport/ # stdio + Streamable HTTP + optional CORS helper
├── adapter/ # Gin, OpenAPI, gRPC adapters
├── mcptest/ # Testing utilities
├── task.go # Async task support
├── completion.go # Auto-completions
├── inspector.go # Web debug UI
├── provider.go # Hot-reload from YAML
└── examples/ # Working examples
├── basic/ # Minimal stdio server
├── filesystem/ # Real-world file ops
├── gin-adapter/ # Import Gin routes
├── openapi-adapter/ # Import Swagger/OpenAPI
└── grpc-adapter/ # Import gRPC services📖 Cookbook
Step-by-step guides for common tasks (5 minutes each):
Build a search tool — zero to Claude Desktop
Import an existing API — Gin / OpenAPI / gRPC in one line
Add auth + RBAC — Bearer tokens + role-based access
📦 Installation
go get github.com/zhangpanda/gomcp⚡ Quick Start
5 lines to a working MCP server
package main
import (
"fmt"
"github.com/zhangpanda/gomcp"
)
type SearchInput struct {
Query string `json:"query" mcp:"required,desc=Search keyword"`
Limit int `json:"limit" mcp:"default=10,min=1,max=100"`
}
type SearchResult struct {
Items []string `json:"items"`
Total int `json:"total"`
}
func main() {
s := gomcp.New("my-server", "1.0.0")
s.ToolFunc("search", "Search documents by keyword", func(ctx *gomcp.Context, in SearchInput) (SearchResult, error) {
items := []string{fmt.Sprintf("Result for %q", in.Query)}
return SearchResult{Items: items, Total: len(items)}, nil
})
s.Stdio()
}The SearchInput struct automatically generates this JSON Schema:
{
"type": "object",
"properties": {
"query": { "type": "string", "description": "Search keyword" },
"limit": { "type": "integer", "default": 10, "minimum": 1, "maximum": 100 }
},
"required": ["query"]
}Invalid parameters are rejected before your handler runs:
validation failed: query: required; limit: must be <= 100📖 Usage Guide
Struct Tag Reference
Tag | Type | Description | Example |
| flag | Field must be provided |
|
| string | Human-readable description |
|
| any | Default value |
|
| number | Minimum value (inclusive) |
|
| number | Maximum value (inclusive) |
|
| string | Pipe-separated allowed values |
|
| string | Regex validation |
|
Combine: mcp:"required,desc=User email,pattern=^[^@]+@[^@]+$"
Supported types: string, int, float64, bool, []T, nested structs.
Tools
Simple handler:
s.Tool("hello", "Say hello", func(ctx *gomcp.Context) (*gomcp.CallToolResult, error) {
return ctx.Text("Hello, " + ctx.String("name")), nil
})Typed handler (recommended):
type Input struct {
Name string `json:"name" mcp:"required,desc=User name"`
Email string `json:"email" mcp:"required,pattern=^[^@]+@[^@]+$"`
}
s.ToolFunc("create_user", "Create user", func(ctx *gomcp.Context, in Input) (User, error) {
return db.CreateUser(in.Name, in.Email)
})Resources
// Static
s.Resource("config://app", "App config", func(ctx *gomcp.Context) (any, error) {
return map[string]any{"version": "1.0"}, nil
})
// Dynamic URI template
s.ResourceTemplate("db://{table}/{id}", "DB record", func(ctx *gomcp.Context) (any, error) {
return db.Find(ctx.String("table"), ctx.String("id")), nil
})Prompts
s.Prompt("code_review", "Code review",
[]gomcp.PromptArgument{gomcp.PromptArg("language", "Language", true)},
func(ctx *gomcp.Context) ([]gomcp.PromptMessage, error) {
return []gomcp.PromptMessage{
gomcp.UserMsg(fmt.Sprintf("Review this %s code for bugs.", ctx.String("language"))),
}, nil
},
)Middleware
s.Use(gomcp.Logger()) // Log MCP method + duration
s.Use(gomcp.Recovery()) // Recover from panics
s.Use(gomcp.RequestID()) // Unique request ID
s.Use(gomcp.Timeout(10 * time.Second)) // Deadline enforcement
s.Use(gomcp.RateLimit(100)) // 100 calls/minute
s.Use(gomcp.OpenTelemetry()) // Distributed tracing
// Pick one: BearerAuth (token required on initialize too) or BearerAuthSkipHandshake (initialize/ping anonymous).
s.Use(gomcp.BearerAuthSkipHandshake(tokenValidator))
s.Use(gomcp.APIKeyAuthSkipHandshake("X-API-Key", keyValidator))Auth error shape. When a BearerAuth / APIKeyAuth / BasicAuth / RequireRole / RequirePermission middleware rejects a call, the framework returns a regular JSON-RPC
resultwithisError = trueand the reason incontent[0].text— not a JSON-RPCerrorobject, and not an HTTP 401/403. Clients must inspect the tool-call result'sisErrorto detect auth failures.
Custom middleware:
func AuditLog() gomcp.Middleware {
return func(ctx *gomcp.Context, next func() error) error {
start := time.Now()
err := next()
log.Printf("tool=%s duration=%s err=%v", ctx.String("_tool_name"), time.Since(start), err)
return err
}
}Tool Groups
user := s.Group("user", authMiddleware)
user.Tool("get", "Get user", getUser) // → user.get
user.Tool("update", "Update user", updateUser) // → user.update
admin := user.Group("admin", gomcp.RequireRole("admin"))
admin.Tool("delete", "Delete user", deleteUser) // → user.admin.deleteAdapters
Gin — one line to import your existing API:
adapter.ImportGin(s, ginRouter, adapter.ImportOptions{
IncludePaths: []string{"/api/v1/"},
})
// GET /api/v1/users/:id → Tool get_api_v1_users_by_id (id = required param)OpenAPI — generate from Swagger docs:
adapter.ImportOpenAPI(s, "./swagger.yaml", adapter.OpenAPIOptions{
TagFilter: []string{"pets"},
ServerURL: "https://api.example.com",
})gRPC:
adapter.ImportGRPC(s, grpcConn, adapter.GRPCOptions{
Services: []string{"user.UserService"},
})Component Versioning
s.ToolFunc("search", "v1", searchV1, gomcp.Version("1.0"))
s.ToolFunc("search", "v2 with embeddings", searchV2, gomcp.Version("2.0"))
// "search" → latest, "search@1.0" → exact versionAsync Tasks
s.AsyncTool("report", "Generate report", func(ctx *gomcp.Context) (*gomcp.CallToolResult, error) {
// long-running work
return ctx.Text("done"), nil
})
// Client gets taskId immediately, polls tasks/get, can tasks/cancelHot-Reload
s.LoadDir("./tools/", gomcp.DirOptions{Watch: true})YAML tool file shape:
name: search
description: Full-text document search
version: "1.0" # OPTIONAL — non-empty renames the tool to "search@1.0"
method: GET
handler: https://example.com/search
params:
- {name: query, type: string, required: true, description: Query text}Heads up —
versionrenames the tool. A non-emptyversionfield makes the Provider register the tool asname@version(e.g.search@1.0), following the same convention asgomcp.Version(). That is the name clients must pass totools/call, and the name that shows up intools/list. Drop theversionfield entirely if you want an unversioned tool called plainsearch.
Server lifecycle, sessions & async tasks
Server.Close()— When your process or test tears down a server that usesLoadDir(..., Watch: true)or long-lived HTTP, callClose()once. It stops the YAML watch loop, the session eviction background goroutine, and the async task manager eviction loop. The call is idempotent.Sessions — Session state is in-memory only. Identifiers come from the client
Mcp-Session-Idheader (see Streamable HTTP). Sessions idle for 30 minutes (no access via that ID) are removed; the next request with the same ID gets a new empty session. Do not rely onSessionstorage across long idle periods unless the client keeps traffic or you refresh state yourself.SetMaxConcurrentTasks(n)— Set this before the firstAsyncTool/AsyncToolFunc. After the internal task manager is created, later calls are a no-op (avoids races with in-flight work).Shutdown vs async work —
Close()does not wait for async tool handlers that are still running; add your own timeout / wait if you need hard guarantees before exit.
MCP Inspector
s.Dev(":9090") // http://localhost:9090 — browse and test all toolsTesting
func TestSearch(t *testing.T) {
c := mcptest.NewClient(t, setupServer())
c.Initialize()
result := c.CallTool("search", map[string]any{"query": "golang"})
result.AssertNoError(t)
result.AssertContains(t, "golang")
mcptest.MatchSnapshot(t, "search_result", result)
}Transports
s.Stdio() // Claude Desktop, Cursor, Kiro
s.HTTP(":8080") // Remote deployment with SSE
s.Handler() // Embed in existing HTTP server
// Browser clients: wrap the handler with transport.WrapCORS(h, []string{"https://your.app"}) when neededUse with AI Clients
{
"mcpServers": {
"my-server": {
"command": "/path/to/your/binary"
}
}
}Works with Claude Desktop, Cursor, Kiro, Windsurf, VS Code Copilot, and any MCP-compatible client.
📋 Roadmap
Core: Tool, Resource, Prompt with full MCP protocol support
Struct-tag auto schema generation + parameter validation
Middleware chain (Logger, Recovery, RateLimit, Timeout, RequestID)
Auth middleware (Bearer / API Key / Basic) + RBAC authorization
Tool groups with prefix naming and nested groups
stdio + Streamable HTTP transports with SSE notifications
Gin adapter — import existing Gin routes as MCP tools
OpenAPI adapter — generate tools from Swagger/OpenAPI docs
gRPC adapter — import gRPC services as MCP tools
OpenTelemetry integration
mcptest package with snapshot testing
Component versioning + deprecation
Async tasks with polling and cancellation
MCP Inspector web debug UI
Hot-reload provider from YAML
Auto-completions for prompt/resource arguments
🤝 Feedback & Support
Bug Reports: GitHub Issues
Feature Requests: GitHub Issues
Discussions: GitHub Discussions
💡 Recommended reading: How To Ask Questions The Smart Way
🔒 Security
HTTP transport and authentication
Usemiddleware runs for every JSON-RPC method (except the notificationnotifications/initialized, which has no response):initialize,tools/list,tools/call,resources/read,prompts/get,tasks/*,completion/complete, etc. UseBearerAuth/APIKeyAuth/BasicAuthwhen exposingPOST /mcp. ForAPIKeyAuth,api_key(and other merged params) come fromtools/callarguments,prompts/getarguments, orresources/readparams JSON when no header is sent—prefer headers for production.BearerAuthSkipHandshake/APIKeyAuthSkipHandshake/BasicAuthSkipHandshake(orSkipAuthForMCPMethods) letinitializeandpingrun without credentials while keeping other methods protected—typical for MCP HTTP clients that negotiate before sending tokens.Request context propagates into tool, resource, and prompt handlers (deadlines,
Authorization, and injected headers from Streamable HTTP).SSE (
GET /mcp) does not execute MCP middleware. UseWithSSEAuthwithSSEBearerAuth,SSEAPIKeyAuth,SSEBasicAuth, or your own gate. WithoutWithSSEAuth, any client that can openGETreceives broadcast notifications.Browser
fetch: wrap your/mcphandler withtransport.WrapCORS(h, allowedOrigins)fromgithub.com/zhangpanda/gomcp/transport; never use*with credentials—only list trusted origins.
When deploying Streamable HTTP in production, combine TLS, authentication middleware on POST, and WithSSEAuth when notifications must not be public.
To report security vulnerabilities, see SECURITY.md.
⚖️ Copyright & License
Copyright © 2026 GoMCP Contributors
Licensed under the Apache License 2.0.
Important Notes
This project is open source and free for both personal and commercial use under the Apache 2.0 license.
You must retain the copyright notice, license text, and any attribution notices in all copies or substantial portions of the software.
The Apache 2.0 license includes an express grant of patent rights from contributors to users.
Contributions to this project are licensed under the same Apache 2.0 license.
Unauthorized removal of copyright notices may result in legal action.
Patent Notice
Certain features of this framework (struct-tag schema generation, HTTP-to-MCP automatic adapter, OpenAPI-to-MCP automatic adapter) are the subject of pending patent applications. The Apache 2.0 license grants you a perpetual, worldwide, royalty-free patent license to use these features as part of this software.
⭐ Star History
If you find GoMCP useful, please consider giving it a star! It helps others discover the project.
Maintenance
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/zhangpanda/gomcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server