cd /news/developer-tools/i-built-a-production-ready-mcp-serve… · home topics developer-tools article
[ARTICLE · art-54031] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=· neutral

I built a production-ready MCP server template so you don't have to — here's the architecture

A developer created a production-ready MCP server template that standardizes architecture across HTTP and stdio transports. The template includes Zod-based configuration, a guarded fetch tool with SSRF protection, structured logging, and a test suite using InMemoryTransport. It is packaged as the MCP Quickstart Kit.

read3 min views1 publishedJul 10, 2026

The Model Context Protocol has a "hello world" problem. Every tutorial shows you the same thing: one little stdio tool, a happy console log, voilà. Then you decide to run a server for real — for your team, for a product, for anything with stakes — and you discover that the distance between the tutorial and production is… all of the actual work.

I've built enough MCP servers now that I got tired of re-plumbing the same parts every time. So I standardised them into a template. Here's the architecture — take it and build it yourself if you want, it fits in six ideas.

This is the one decision that makes everything else fall into place. The HTTP transport in stateless mode wants a fresh server per request, so isolation between callers is structural instead of hopeful. So the core of the codebase is one function:

export function createServer(config: Config): McpServer {
  const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
  registerTools(server, config, log);
  registerResources(server);
  registerPrompts(server);
  return server;
}

stdio calls it once at startup. HTTP calls it per request. Same code, both transports.

TRANSPORT=stdio

for editor clients (Claude Desktop, Claude Code, Cursor). TRANSPORT=http

and the same build becomes a remote Streamable HTTP server behind Express — stateless, so horizontal scaling is boring, which is the best thing scaling can be.

All config goes through Zod at startup. A bad env var should be a clear message at boot, not a mystery at 2am. Tool inputs are Zod schemas too — the SDK validates every call for you, so handlers receive typed, already-safe arguments. Free correctness, take it.

fetch()

in a trenchcoat The moment you give a model a URL-fetching tool, you've built a proxy that an LLM drives. So the template's fetch_url

enforces: http/https only, an optional hostname allowlist (SSRF is not theoretical), a byte cap so a 2GB response can't eat your process, and a timeout. That's the difference between a demo and something you can expose without holding your breath.

console.log

on stdio stdout is the JSON-RPC channel. One stray log line and the protocol stream is corrupted — and the resulting bug report will not mention that, it will say "your server randomly disconnects". All logging goes to stderr, structured. This rule has no exceptions and everyone learns it the same way (the bad way).

The SDK ships InMemoryTransport.createLinkedPair()

— you connect a real Client

to your real server, no sockets, no processes, and assert on listTools

/ callTool

results. Fast, deterministic, perfect for CI. My suite drives the actual server this way; the whole thing runs in seconds on Node 20/22/24.

That's the architecture. If you'd rather start from the finished version — both transports wired, the guarded tools, the test suite, Docker, CI, plus a Stripe appendix for making tools paid — I packaged it as the MCP Quickstart Kit. Either way, the six ideas above are the whole design.

What patterns are you using in your MCP servers? I'm genuinely curious what I've missed — tell me in the comments and I'll answer.

── more in #developer-tools 4 stories · sorted by recency
── more on @mcp 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/i-built-a-production…] indexed:0 read:3min 2026-07-10 ·