The Model Context Protocol (MCP) has a fragmentation problem in its documentation: most examples are either too minimal to be useful, or they bury the key concepts under framework boilerplate. This article walks through building a real MCP server in TypeScript — one that implements all three primitives (Tools, Resources, Prompts) and runs over both stdio and HTTP transport.

The code is production-shaped, not toy-shaped. By the end you'll understand not just what to write, but why each piece is the way it is.

The One Thing You Must Know About stdio

Before any code: if you use stdio transport, all logging must go to stderr. Not console.log — console.error.

stdio transport uses stdout as the protocol channel for JSON-RPC frames. A single console.log corrupts the frame and causes host parsing failures. The SDK won't warn you. The host just silently breaks.