Your first MCP server in TypeScript — Commented code
Curso MCP Engineering · M2
Complete reference guide for the lesson m2 · TypeScript server with the official SDK. It's meant to be read top to bottom and to copy each block in order: first you set up the project, then you write index.ts explained line by line, and finally you run it and connect it to Claude Code. Don't worry if you've never written TypeScript: we define each term as it comes up.
Step 0 — The minimum vocabulary
- Node.js: the program that runs JavaScript/TypeScript outside the browser, on your machine.
- npm: Node's package manager. It installs libraries (other people's code) for you.
- TypeScript: it's JavaScript with types — labels that say what kind of data each thing expects, so errors pop up before the program runs.
- SDK: Software Development Kit, an official toolbox that saves you from writing the protocol by hand. Ours is
@modelcontextprotocol/sdk. - Zod: a library for validating data. We use it to describe what each tool receives (for example, "two numbers") so the SDK rejects anything that doesn't fit.
- stdio: standard input/output, the "pipe" through which your server and the host talk when they run on the same machine.
Step 1 — Start the project
# Create the project folder and enter it
mkdir mi-servidor-mcp
cd mi-servidor-mcp
# Start a Node project with no questions (-y = "yes to everything")
npm init -y
npm init -y creates a package.json file: your project's "record", where Node notes the name, the version and —most important for us— the list of dependencies.
Step 2 — Enable modern modules (import/export)
Open package.json and add the line "type": "module". This tells Node that you're going to use import (the modern syntax) instead of require (the old one). The official SDK uses import, so this step is mandatory.
{
"name": "mi-servidor-mcp",
"version": "1.0.0",
"type": "module"
}
Step 3 — Install dependencies
# The official MCP SDK + Zod (to validate the tools' data)
npm install @modelcontextprotocol/sdk zod
# Optional: only if you're going to read environment variables (keys, tokens, etc.)
npm install dotenv
npm install downloads the libraries into the node_modules folder and notes them in your package.json. The SDK brings everything needed to speak MCP; Zod validates the inputs.
Step 4 — The complete server (index.ts)
Create an index.ts file with this content. Below we explain it block by block.
// ─────────────────────────────────────────────────────────────
// index.ts — Your first MCP server in TypeScript
// ─────────────────────────────────────────────────────────────
// We import the pieces from the official SDK.
// McpServer: the class that represents your server.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
// StdioServerTransport: the "pipe" (stdio) through which the host and the server talk.
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
// z (Zod): describes and validates what data each tool receives.
import { z } from "zod";
// 1) We create the server.
// name and version identify it to the host (Claude, an IDE, etc.).
const server = new McpServer({
name: "demo",
version: "1.0.0",
});
// 2) We register a tool with server.tool(name, schema, handler):
// - name : how the model calls the tool ("suma").
// - schema : what it receives, described with Zod ({ a, b } are two numbers).
// - handler: the async function that runs and RETURNS the result.
//
// The result ALWAYS has the shape { content: [ ... ] }.
// Here we return a text block with the sum.
server.tool(
"suma",
{ a: z.number(), b: z.number() },
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }],
})
);
// 3) We open the stdio transport and connect the server.
// After connect(), the server stays LISTENING for messages.
const transport = new StdioServerTransport();
await server.connect(transport);
What each block does
- The three
imports — bring in the pieces you're going to use.McpServeris your server;StdioServerTransportis the communication channel;zis Zod. Notice that the paths end in.js: it's a quirk of modern modules, even though the real file is TypeScript. Copy them exactly as they are. new McpServer({ name, version })— creates the server. Thename("demo") and theversionare how it presents itself to the host.server.tool(name, schema, handler)— registers a tool. It has three parts:- name (
"suma"): how the model calls it. - Zod schema (
{ a: z.number(), b: z.number() }): describes what data comes in.z.number()means "a number goes here". If the model sends text instead of a number, Zod rejects it automatically before your code runs. That's the value of Zod: it validates the inputs for you and prevents errors. - handler (the
async ({ a, b }) => ...function): what runs when the model calls the tool. It receives the already-validated data (aandb) and returns the result. { content: [{ type: "text", text: String(a + b) }] }— the shape of the result. In MCP, a tool's response is always an object{ content: [...] }with a list of blocks. Here we return a single text block. We useString(...)because the text block expects a string, not a number.new StdioServerTransport()+await server.connect(transport)— opens the stdio "pipe" and connects the server. Afterconnect(), your server stays listening.
Notice the pattern: create the server → register tools → connect a transport. That's the skeleton of any MCP server in TypeScript.
Step 5 — Run the server
# tsx runs TypeScript directly, without compiling by hand.
# npx downloads and runs it on the fly.
npx tsx index.ts
You won't see output on screen: the server stays waiting over stdio. That's normal. Close it with Ctrl+C.
Step 6 — Register the server in Claude Code
# Use the ABSOLUTE PATH to your index.ts.
claude mcp add demo -- npx tsx /ruta/absoluta/a/index.ts
# Check that it was registered:
claude mcp list
The absolute path matters: Claude starts your server from any folder, so a relative path (like ./index.ts) might not be found. On macOS/Linux an absolute path starts with /; on Windows, with something like C:\.
Step 7 — Test it
Inside Claude Code, ask something like:
Use the "suma" tool from the demo server to add 21 and 21.
You should see Claude invoke your tool and answer 42.
Common errors (and how to fix them)
Cannot use import statement outside a module→ you're missing"type": "module"inpackage.json(Step 2).Cannot find module '@modelcontextprotocol/sdk/...'→ runnpm installagain (Step 3) and confirm you're in the correct project folder.npx: command not foundortsxfails → make sure you have Node.js installed;npxcomes with npm.tsxdownloads itself the first time, it may take a few seconds.- Claude doesn't see the tool → confirm the absolute path in
claude mcp add, check withclaude mcp listand restart the Claude Code session.
The pattern you take away
Create the server → register tools → connect a transport. That's the skeleton of any MCP server in TypeScript; the rest are variations on the same scheme.