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. 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: js 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 https://leamoreau1988.gumroad.com/l/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.