Install the MCP TypeScript SDK with npm install @modelcontextprotocol/sdk zod, then create a minimal McpServer with stdio transport. The SDK provides the McpServer class for defining tools, resources, and prompts, StdioServerTransport for local communication, and uses Zod for input validation. A working server skeleton is under 20 lines of code.
Setting Up the MCP TypeScript SDK
The TypeScript SDK (@modelcontextprotocol/sdk) is the primary way to build MCP servers. It provides a high-level McpServer class that handles protocol negotiation, capability registration, and message routing. Combined with Zod for schema validation, you get type-safe tool definitions with minimal boilerplate. This tutorial walks you through installation, project setup, and creating your first server skeleton.
Prerequisites
- Node.js 18 or later installed (check with node --version)
- npm, yarn, or pnpm package manager
- A code editor (VS Code, Cursor, or similar)
- Basic familiarity with TypeScript and npm
Step-by-step guide
Create a new project directory and initialize npm
Create a new project directory and initialize npm
Create a fresh directory for your MCP server project. Initialize it with npm init to create a package.json. Use the -y flag to accept defaults — you can edit the package.json later. This gives you a clean project to install the SDK into.
1mkdir my-mcp-server2cd my-mcp-server3npm init -yExpected result: A new directory with a package.json file ready for dependency installation.
Install the MCP SDK and Zod
Install the MCP SDK and Zod
Install @modelcontextprotocol/sdk as your main dependency and zod for input schema validation. The SDK includes the McpServer class, transport implementations, and all MCP protocol types. Zod is used to define and validate tool input schemas — the SDK integrates with Zod natively.
1npm install @modelcontextprotocol/sdk zodExpected result: Both packages appear in your package.json dependencies. You should see @modelcontextprotocol/sdk and zod in node_modules.
Install TypeScript and tsx for development
Install TypeScript and tsx for development
Install TypeScript for type checking and tsx as a dev dependency for running TypeScript files directly without a separate build step. tsx is a fast TypeScript executor that works out of the box with ESM imports, which the MCP SDK uses.
1npm install -D typescript tsx @types/node2npx tsc --initExpected result: A tsconfig.json file is created. TypeScript and tsx are available in your project.
Configure TypeScript for ESM and MCP SDK compatibility
Configure TypeScript for ESM and MCP SDK compatibility
The MCP SDK uses ESM (ECMAScript modules) with .js extensions in import paths. Update your tsconfig.json to use NodeNext module resolution, which handles ESM correctly. Also set the target to ES2022 or later for top-level await support, which MCP servers commonly use.
1// tsconfig.json2{3 "compilerOptions": {4 "target": "ES2022",5 "module": "NodeNext",6 "moduleResolution": "NodeNext",7 "outDir": "./dist",8 "rootDir": "./src",9 "strict": true,10 "esModuleInterop": true,11 "skipLibCheck": true,12 "forceConsistentCasingInFileNames": true13 },14 "include": ["src/**/*"]15}Expected result: TypeScript is configured for ESM modules with proper resolution for MCP SDK imports.
Update package.json for ESM and add scripts
Update package.json for ESM and add scripts
Add "type": "module" to package.json so Node.js treats .js files as ESM. Add scripts for development (using tsx for direct TypeScript execution) and production (compile then run). Also add a "bin" entry if you plan to publish to npm later.
1// Add these fields to package.json:2{3 "type": "module",4 "scripts": {5 "dev": "tsx src/index.ts",6 "build": "tsc",7 "start": "node dist/index.js"8 },9 "bin": {10 "my-mcp-server": "dist/index.js"11 }12}Expected result: package.json is configured for ESM with dev, build, and start scripts.
Create a minimal MCP server skeleton
Create a minimal MCP server skeleton
Create src/index.ts with a minimal MCP server. Import McpServer and StdioServerTransport from the SDK. Create a server instance with a name and version, define one example tool using Zod for input validation, and connect via stdio transport. This is the foundation you will build on — every MCP server starts with this structure.
1// src/index.ts2import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";3import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";4import { z } from "zod";56const server = new McpServer({7 name: "my-mcp-server",8 version: "1.0.0",9});1011// Example tool — replace with your own12server.tool(13 "hello",14 "Greet someone by name",15 { name: z.string().describe("Name of the person to greet") },16 async ({ name }) => ({17 content: [{ type: "text", text: `Hello, ${name}!` }],18 })19);2021// Connect via stdio transport22const transport = new StdioServerTransport();23await server.connect(transport);24console.error("Server started");Expected result: Running npm run dev starts the server process. It waits for JSON-RPC messages on stdin (you will not see visible output — that is normal for stdio servers).
Complete working example
1#!/usr/bin/env node23import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";4import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";5import { z } from "zod";67// Create the MCP server8const server = new McpServer({9 name: "my-mcp-server",10 version: "1.0.0",11});1213// Define a tool with Zod input validation14server.tool(15 "hello",16 "Greet someone by name",17 { name: z.string().describe("Name of the person to greet") },18 async ({ name }) => ({19 content: [20 {21 type: "text",22 text: `Hello, ${name}! This response comes from an MCP server.`,23 },24 ],25 })26);2728// Define a tool with multiple parameters29server.tool(30 "calculate",31 "Perform basic arithmetic",32 {33 a: z.number().describe("First number"),34 b: z.number().describe("Second number"),35 operation: z.enum(["add", "subtract", "multiply", "divide"]),36 },37 async ({ a, b, operation }) => {38 let result: number;39 switch (operation) {40 case "add": result = a + b; break;41 case "subtract": result = a - b; break;42 case "multiply": result = a * b; break;43 case "divide":44 if (b === 0) {45 return {46 content: [{ type: "text", text: "Error: Division by zero" }],47 isError: true,48 };49 }50 result = a / b;51 break;52 }53 return {54 content: [{ type: "text", text: `${a} ${operation} ${b} = ${result}` }],55 };56 }57);5859// Connect via stdio60const transport = new StdioServerTransport();61await server.connect(transport);62console.error("my-mcp-server started successfully");Common mistakes when installing the MCP TypeScript SDK
Why it's a problem: Forgetting "type": "module" in package.json
How to avoid: The MCP SDK uses ESM imports with .js extensions. Without "type": "module", Node.js treats files as CommonJS and import statements fail. Add "type": "module" to package.json.
Why it's a problem: Using .ts extensions in import paths
How to avoid: TypeScript with NodeNext resolution requires .js extensions in import paths, even for .ts source files. Write import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" — note the .js extension.
Why it's a problem: Using console.log instead of console.error
How to avoid: Stdio transport uses stdout for JSON-RPC messages. console.log writes to stdout and corrupts the protocol stream. Always use console.error for debugging and logging.
Why it's a problem: Not adding Zod descriptions to parameters
How to avoid: Without .describe() on Zod schemas, the AI model sees parameters without context. Always add descriptions: z.string().describe("Search query keywords") so the model knows what values to pass.
Best practices
- Always use Zod .describe() on every tool parameter to help the AI model understand what values to provide
- Use tsx for development (fast, no build step) and tsc + node for production
- Add #!/usr/bin/env node as the first line of your entry file if you plan to publish to npm as a CLI tool
- Log to stderr only (console.error) — stdout is reserved for MCP protocol messages in stdio servers
- Pin your @modelcontextprotocol/sdk version in package.json to avoid unexpected breaking changes
- Set "strict": true in tsconfig.json for maximum type safety in tool handlers
- Keep your server entry point clean — extract tool implementations into separate files as the server grows
- Test your server with MCP Inspector before connecting it to Claude Desktop or Cursor
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Show me how to set up a new MCP server project with TypeScript. Include the npm install commands, tsconfig.json configuration, package.json setup for ESM, and a minimal server with one tool using the @modelcontextprotocol/sdk and Zod for input validation.
Create an MCP server project from scratch. I need the complete setup: package.json with ESM, tsconfig.json, and an src/index.ts file with a McpServer that has two tools using Zod schemas. Include the correct import paths with .js extensions.
Frequently asked questions
What Node.js version do I need for the MCP SDK?
Node.js 18 or later is required. The SDK uses ESM, top-level await, and modern JavaScript features that are not available in older Node.js versions. Node.js 20 LTS or 22 LTS is recommended.
Can I use JavaScript instead of TypeScript?
Yes. The MCP SDK works with plain JavaScript. However, TypeScript is strongly recommended because Zod schemas provide type inference for tool handler parameters, giving you autocomplete and compile-time error checking.
Why does the MCP SDK use .js extensions in import paths?
The SDK is published as ESM. TypeScript's NodeNext module resolution requires explicit file extensions that match the compiled output. Since .ts files compile to .js, you use .js extensions in imports even when the source is TypeScript.
Can I use yarn or pnpm instead of npm?
Yes. yarn add @modelcontextprotocol/sdk zod or pnpm add @modelcontextprotocol/sdk zod work identically. The SDK has no npm-specific dependencies.
Do I need to install Zod separately?
Yes. While the MCP SDK depends on Zod internally, you need it as a direct dependency to define tool input schemas in your code. Install it explicitly: npm install zod.
How do I run the server once it is built?
For development, use npx tsx src/index.ts. For production, compile with npx tsc and run with node dist/index.js. MCP hosts like Claude Desktop and Cursor will launch the server process automatically based on your configuration.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation