The Model Context Protocol (MCP) is quickly becoming the connective tissue between AI agents, developer tools, and internal automation.
But if you're building MCP servers in Go today, you'll notice something familiar: you're handed a protocol SDK — and then you're on your own.
That's fine for experimentation. It breaks down quickly in production.
This post explains why MCP needs a real Go framework, how SDKs and frameworks differ, and why we built mcp-go.
MCP SDKs are necessary — but insufficient
SDKs do an important job:
- enforce protocol correctness
- track spec changes
- expose low-level primitives
That's exactly what they should do.
But SDKs intentionally do not:
- define application structure
- enforce input validation
- provide middleware
- offer production defaults
- optimize for developer experience
If you've used Go long enough, this pattern is familiar.
The HTTP analogy everyone understands
Go already solved this once.
net/httpgives you correctness and control- frameworks like Gin give you structure, ergonomics, and safety
Nobody argues that Gin "competes" with net/http.
They live at different layers.
MCP is no different.
The real problem: production MCP servers
Once you move beyond demos, every MCP server needs the same things:
- typed inputs
- validation before business logic
- consistent error handling
- middleware (auth, timeouts, logging, rate limits)
- multiple transports (stdio for agents, HTTP for services)
- predictable defaults
Without a framework, every team rebuilds this themselves.
That's wasted effort — and inconsistent outcomes.
Enter mcp-go
mcp-go is a Go framework for building MCP servers, designed the same way Gin was designed for HTTP.
What it focuses on:
- typed handlers
- automatic JSON Schema generation
- first-class middleware
- multiple transports from the same server
- boring, production-safe defaults
What it intentionally avoids:
- agent logic
- LLM abstractions
- "magic"
- protocol reinvention
If the MCP SDK is the kernel, mcp-go is the runtime.
A concrete example
Instead of manually decoding maps and validating input, you write:
srv.Tool("search").
Input(SearchInput{}).
Handler(func(ctx context.Context, in SearchInput) (any, error) {
return svc.Search(ctx, in.Query, in.Limit)
}) Schema generation, validation, and error mapping happen automatically.
This is not about saving keystrokes. It's about making the correct thing the easy thing.
SDKs, community libraries, and frameworks can coexist
The MCP ecosystem doesn't need "one library to rule them all".
It needs:
- a protocol SDK for correctness
- community SDKs for flexibility
- a framework for building real systems
That's where mcp-go fits.
Who mcp-go is for
- Go platform and infra teams
- AI tooling and automation services
- OSS authors building on MCP
- anyone deploying MCP servers in production
If you just want raw protocol access, an SDK is perfect. If you want structure and safety, you want a framework.
Closing
Frameworks don't replace SDKs. They unlock adoption.
That's why we built mcp-go — and why we think MCP in Go needs one.