# Build a Custom MCP Server in TypeScript

> A minimal stdio MCP server using @modelcontextprotocol/sdk with a zod-validated tool.

Category: MCP & Tool Use  
Tags: mcp, typescript, node, sdk  
Setup time (estimate): 20-30 minutes  
Estimated cost: Free (open-source SDK).  
Last verified: 2026-10-14

## Install

```bash
npm init -y && npm i @modelcontextprotocol/sdk zod
npm i -D typescript @types/node && npx tsc --init
```

Set `"type": "module"` in package.json.

## src/index.ts

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({ name: "unit-converter", version: "1.0.0" });

server.registerTool(
  "celsius_to_fahrenheit",
  {
    description: "Convert a temperature from Celsius to Fahrenheit",
    inputSchema: { celsius: z.number() },
  },
  async ({ celsius }) => ({
    content: [{ type: "text", text: String(celsius * 9 / 5 + 32) }],
  })
);

await server.connect(new StdioServerTransport());
```

## Notes

- Never write logs to stdout in a stdio server; it corrupts the protocol stream. Use `console.error`.
- Test with `npx @modelcontextprotocol/inspector node build/index.js`.
- `server.tool(...)` is deprecated in current SDK 1.x releases; use `registerTool` (shown above).

## Failure modes

- console.log to stdout corrupts the stdio stream; use console.error.
- Missing "type": "module" breaks the ESM imports.
- Deprecated server.tool() calls may break in a future major; use registerTool.
