Build a working MCP server for Claude Code in about 30 minutes with Node.js, stdio transport, tool wiring, tests, and setup today. Read now.
What you are building
An MCP server is a small process that exposes tools Claude Code can call while you work. Instead of pasting context into chat or switching apps, you define a few actions—read a file, run a command, query a service—and Claude Code discovers and invokes them through a standard protocol. This tutorial walks you through a working server in about 30 minutes: Node.js project setup, stdio transport, tool wiring, and a basic test path so you know the server is honest before you connect it.
You do not need a full product architecture. The goal is a minimal, inspectable server you can extend. Once the shape is clear—how tools are declared, how arguments are validated, how results return—you can add real capabilities without relearning the plumbing.
Project setup and stdio transport
Start with a Node.js package, a clear entry file, and a dependency for the MCP server SDK your stack uses. Keep the process single-purpose: start, advertise tools, handle requests, exit cleanly. For Claude Code, stdio transport is the usual path. The host launches your server as a child process and talks over standard input and output. That means no open ports, no separate HTTP server, and fewer moving parts when you are iterating locally.
Wire the process so logs go to stderr, not stdout. Stdout is the message channel; printing debug text there will break framing. On start, register your tool list and keep handlers pure enough to unit-test: input in, structured result out, errors as explicit failures rather than crashes. A short README with the launch command and required env vars saves you from rediscovering setup later.
Tool wiring that Claude Code can use
Each tool needs a stable name, a short description Claude Code can reason about, and a schema for arguments. Descriptions matter more than clever implementation names. Write them so a model can decide when to call the tool and what to pass. Validate inputs at the boundary: reject missing fields early, coerce types only when safe, and return messages that help the model retry instead of failing silently.
- Keep tools small and single-purpose so composition stays predictable.
- Return structured data (JSON-friendly objects) rather than long free-form dumps when you can.
- Surface failures with clear error text; hide secrets and raw stack traces from the model by default.
- Prefer idempotent operations for anything that mutates state, or document side effects explicitly in the tool description.
Map each handler to real work carefully. If a tool shells out, constrain the command surface. If it reads files, restrict paths. Claude Code will call what you expose; treat the tool list as an API surface with the same care you give any internal service.
Tests, local checks, and connecting Claude Code
Before you point Claude Code at the server, exercise handlers with fixed inputs and assert on shapes and error paths. A few focused tests beat a long manual script: valid call succeeds, bad args fail cleanly, and edge cases (empty strings, large payloads) behave as designed. Then run the server once by hand and confirm it stays quiet on stdout except for protocol traffic.
Finally, register the server in Claude Code’s MCP configuration with the command and args that start your process. Restart or reload so the client rediscovers tools, then try a natural request that should trigger one of them. If nothing happens, check the tool description, argument schema, and stderr logs first—most early failures are discovery or framing issues, not business logic. Once one tool works end to end, adding the next ones is mostly more handlers and tests, not another transport redesign.