# How to Build an MCP Server in TypeScript

> Write a one-tool MCP server with the official TypeScript SDK, serve it over stdio, and call it from Claude Code. Copy-paste.

- Canonical: https://guides-ai.pages.dev/guides/build-mcp-server-typescript/
- Plate 05.06 · Topic: MCP (https://guides-ai.pages.dev/topics/mcp/)
- Published: 06 Sept 2026 · 4 min read
- Source site: guides-ai — https://guides-ai.pages.dev/

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

```bash
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`:

```typescript
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:

```bash
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:

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

```bash
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:

```powershell
claude mcp list
```

## 5. Call it

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

---

Next: [build the same server in Python](/guides/build-mcp-server-python/),
[the best MCP servers to start with](/guides/best-mcp-servers-to-start/), or generate the
`claude mcp add` command with the [MCP config generator](/tools/mcp-config/).
