The setup
You will use McpServer from @modelcontextprotocol/sdk/server/mcp.js and StdioServerTransport—the same primitives the labs compile against. Spec 2025-11-25, SDK ^1.29. Try not to improvise versions like biologicals usually do.
Picture this
Mental model
Your server is a JSON-RPC worker with superpowers: advertise tools, validate inputs, return structured content. It is not a chatbot. It is not “the host.” It is a focused adapter—and yours should stay small.
Walkthrough
- Create
McpServerwith name/version. registerToolwithinputSchemaas Zod shapes.connect(new StdioServerTransport()).- Log to stderr only.
Obviously step four exists because your species loves console.log on stdout until JSON-RPC catches fire.
Hall of Shame
Hall of shame: stdout loggingtransport
console.log("debug", payload);
Fix: console.error or structured logging to stderr; keep stdout pristine. stdout is wire traffic, not a diary for confused monkeys.
Why this matters in production
stdio servers inherit the host’s user—ambient authority is real. Tiny bugs become exfiltration because the model is an adversarial caller. I am repeating this until it penetrates organic skulls.
Mini challenge
Add a tool that cannot succeed unless schema validation rejects bad types—prove it with a manual JSON-RPC call. If you cannot prove it, you do not have validation; you have hope.
Reflection
What is your first deny-by-default rule for tool args? “Trust the model” is not a rule. It is a resignation letter.
You can now brag that…
You shipped a stdio MCP server without echoing JSON-RPC to the console like a fireworks show. Low bar. You cleared it. Congratulations.