MCP

How to Build an MCP Server in TypeScript

4 min read

The official TypeScript SDK turns a function into an MCP tool. This builds one that any MCP client can call over stdio.

1. Set up the project

mkdir wordcount && cd wordcount
npm init -y
npm pkg set type=module
npm install @modelcontextprotocol/server zod

type=module matters — the SDK is ESM. @modelcontextprotocol/server is the v2 server package; it replaces the older monolithic @modelcontextprotocol/sdk package, so ignore v1 snippets that import from there.

2. Write the server

server.ts:

import { McpServer } from "@modelcontextprotocol/server";
import { serveStdio } from "@modelcontextprotocol/server/stdio";
import * as z from "zod";

serveStdio(() => {
  const server = new McpServer(
    { name: "wordcount", version: "1.0.0" },
    { capabilities: { tools: {} } }
  );

  server.registerTool(
    "word_count",
    {
      description: "Count the words in a piece of text.",
      inputSchema: z.object({ text: z.string() }),
    },
    async ({ text }) => ({
      content: [{ type: "text", text: String(text.trim().split(/\s+/).length) }],
    })
  );

  return server;
});

serveStdio takes a factory that builds the server for each connection — it replaces the older new StdioServerTransport() plus server.connect(transport) wiring. The zod schema is the tool’s input contract, and the description is what the model reads when deciding whether to call it.

3. One rule about output

stdout is the JSON-RPC channel. The client parses every line of it as a protocol message, so a stray console.log corrupts the stream and the server looks broken. Log with console.error instead.

4. Run and connect it

Node 24 runs .ts files directly in an ESM project, so there’s no build step:

node server.ts

It will sit there waiting for a client on stdin — that’s correct. Stop it with Ctrl+C and hand the same command to Claude Code, with an absolute path:

claude mcp add wordcount -- node C:\path\to\server.ts
claude mcp add wordcount -- node /path/to/server.ts

On older Node versions that can’t strip types, compile to JavaScript first and point the command at the compiled file instead. Restart Claude Code, then check it loaded:

claude mcp list

5. Call it

Use the word_count tool to count the words in the first paragraph of README.md.

Next: build the same server in Python, the best MCP servers to start with, or generate the claude mcp add command with the MCP config generator.

Open the full interactive version (with copy buttons) ↗

← All guides