I wrote the first book on building production MCP servers with Go
<p>Most MCP tutorials use Python. That's fine for prototypes. But when you need a server that handles thousands of concurrent connections on 128 MB of RAM, starts in 50ms, and deploys as a single binary — you need Go.</p> <p>I spent the last few months building MCP servers in Go for production systems. Eight different servers, 4,000+ lines of production code, handling real workloads across project management, browser automation, knowledge bases, and multi-agent orchestration.</p> <p>Then I realized: <strong>there is no book on this.</strong> Not one. The MCP docs cover the protocol. There are Python quickstarts. TypeScript examples. But nothing that shows you how to build a production Go MCP server with authentication, database integration, deployment, and billing.</p> <p>So I wrote one.</
Most MCP tutorials use Python. That's fine for prototypes. But when you need a server that handles thousands of concurrent connections on 128 MB of RAM, starts in 50ms, and deploys as a single binary — you need Go.
I spent the last few months building MCP servers in Go for production systems. Eight different servers, 4,000+ lines of production code, handling real workloads across project management, browser automation, knowledge bases, and multi-agent orchestration.
Then I realized: there is no book on this. Not one. The MCP docs cover the protocol. There are Python quickstarts. TypeScript examples. But nothing that shows you how to build a production Go MCP server with authentication, database integration, deployment, and billing.
So I wrote one.
Why Go for MCP Servers?
Python TypeScript Go
Memory ~50-100 MB ~30-60 MB ~5-15 MB
Startup 1-3s 0.5-1s <50ms
Concurrency asyncio event loop goroutines
Deployment venv + pip node_modules single binary
Cross-compile
painful
painful
GOOS=linux go build
The numbers matter when you run multiple MCP servers. A Go MCP server uses 10x less memory than Python, starts 50x faster, and deploys as a single file with zero dependencies.
What I learned from production
Here are patterns that aren't in any tutorial:
1. Serve SSE and Streamable HTTP on the same port
Different AI clients use different transports. Claude uses SSE. Codex uses Streamable HTTP. Don't make users configure which one — serve both:
mux := http.NewServeMux() mux.Handle("/sse", sseHandler) // Claude, Gemini mux.Handle("/mcp", streamHandler) // Codex, newer clients mux.HandleFunc("/health", healthCheck) http.ListenAndServe(":8080", mux)mux := http.NewServeMux() mux.Handle("/sse", sseHandler) // Claude, Gemini mux.Handle("/mcp", streamHandler) // Codex, newer clients mux.HandleFunc("/health", healthCheck) http.ListenAndServe(":8080", mux)Enter fullscreen mode
Exit fullscreen mode
One port. Every client works.
2. Business errors vs. system errors
This is the #1 mistake in MCP server code. Tool handlers return two things: a result and an error. They mean different things:
// Business error — the AI sees this and can retry return mcp.NewToolResultError("user not found"), nil// Business error — the AI sees this and can retry return mcp.NewToolResultError("user not found"), nil// System error — crashes the request (database down, etc.) return nil, fmt.Errorf("connection lost")`
Enter fullscreen mode
Exit fullscreen mode
Return NewToolResultError for "that didn't work, try something else." Return Go error for "something is fundamentally broken." The AI handles the first kind gracefully. The second kind may close the connection.
3. Bearer token auth with browser fallback
Browser EventSource API cannot set custom headers. Period. So when a browser-based MCP client connects via SSE, the token goes in the URL:
func authMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { auth := r.Header.Get("Authorization") if auth == "" { // Browser fallback — EventSource can't set headers auth = "Bearer " + r.URL.Query().Get("token") } if auth != "Bearer "+expectedToken { http.Error(w, "Unauthorized", 401) return } next.ServeHTTP(w, r) }) }func authMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { auth := r.Header.Get("Authorization") if auth == "" { // Browser fallback — EventSource can't set headers auth = "Bearer " + r.URL.Query().Get("token") } if auth != "Bearer "+expectedToken { http.Error(w, "Unauthorized", 401) return } next.ServeHTTP(w, r) }) }Enter fullscreen mode
Exit fullscreen mode
Yes, the token appears in server logs. Use HTTPS, rotate tokens, and strip query params from access logs.
4. Session cleanup via MCP hooks
MCP clients hold long-lived connections. When they disconnect (laptop closes, network drops), you need to clean up. The mcp-go library fires lifecycle hooks:
hooks.AddOnUnregisterSession(func(ctx context.Context, session server.ClientSession) { sessionID := session.SessionID() if agentID, ok := sessionAgents.LoadAndDelete(sessionID); ok { disconnectAgent(agentID.(string)) } })hooks.AddOnUnregisterSession(func(ctx context.Context, session server.ClientSession) { sessionID := session.SessionID() if agentID, ok := sessionAgents.LoadAndDelete(sessionID); ok { disconnectAgent(agentID.(string)) } })Enter fullscreen mode
Exit fullscreen mode
If you skip this, you leak memory. Every disconnected session stays in your maps forever.
5. Symlinks break your path validation
Most file-handling tools do this:
// LOOKS safe but ISN'T abs := filepath.Abs(filepath.Join(root, userPath)) if !strings.HasPrefix(abs, root) { return error }// LOOKS safe but ISN'T abs := filepath.Abs(filepath.Join(root, userPath)) if !strings.HasPrefix(abs, root) { return error }Enter fullscreen mode
Exit fullscreen mode
An attacker creates a symlink workspace/data → /etc and requests data/shadow. The prefix check passes. The symlink resolves to /etc/shadow.
Fix: call filepath.EvalSymlinks before the prefix check.
What the book covers
12 chapters, 110+ pages, every example from production:
-
MCP Protocol — architecture, transports, JSON-RPC flow
-
Quick Start — running server in 5 minutes
-
Server Scaffold — dual transport, health checks, graceful shutdown
-
Tool Development — schemas, validation, rate limiting, long-running ops
-
Resources & Prompts — fixed/template resources, context-bundling prompts
-
Authentication & Security — bearer tokens, symlink defense, risk classification, API keys
-
Database Integration — pgxpool, embedded migrations, pgvector semantic search
-
Testing — unit, integration, testcontainers, CI/CD
-
Deployment — multi-stage Docker, Compose, Caddy HTTPS, Prometheus
-
Production Patterns — sessions, events, multi-tenant, circuit breakers, "mistakes I made"
-
Monetization — Stripe billing, pricing models, distribution, case study ($10K MRR in 6 weeks)
-
Appendix — client compatibility matrix, quick reference, LLM uncertainty handling
The MCP economy is wide open
17,000+ MCP servers exist. Less than 5% are monetized. The SDK gets 97 million monthly downloads. This is the mobile app store in 2009 — massive developer activity, almost no established business models.
The book's final chapter covers how to monetize: freemium, usage-based, hybrid pricing, Stripe metering integration, and distribution across MCP marketplaces.
Get the book
Production MCP Servers with Go — $39 on Gumroad.
PDF + EPUB. 110 pages. 12 chapters. All code from production systems.
Questions? Drop them in the comments. I'll answer everything about building MCP servers with Go.
DEV Community
https://dev.to/david_shawn_e308bed98c45b/i-wrote-the-first-book-on-building-production-mcp-servers-with-go-14b9Sign in to highlight and annotate this article

Conversation starters
Daily AI Digest
Get the top 5 AI stories delivered to your inbox every morning.
More about
claudegeminimodel
Adobe and NVIDIA Announce Strategic Partnership to Deliver the Next Generation of Firefly Models and Creative, Marketing and Agentic Workflows - NVIDIA Newsroom
Adobe and NVIDIA Announce Strategic Partnership to Deliver the Next Generation of Firefly Models and Creative, Marketing and Agentic Workflows NVIDIA Newsroom
Knowledge Map
Connected Articles — Knowledge Graph
This article is connected to other articles through shared AI topics and tags.




Discussion
Sign in to join the discussion
No comments yet — be the first to share your thoughts!