title: 'Server Setup with TypeScript' description: 'Building your first MCP server with the TypeScript SDK'
Server Setup with TypeScript
In this lesson, we'll build a complete MCP server using the TypeScript SDK. By the end, you'll have a working server that exposes tools and resources.
Prerequisites
Before starting, ensure you have:
- Node.js 18 or higher installed
- Basic TypeScript knowledge
- A code editor (VS Code recommended)
Project Setup
Create a new directory and initialize a Node.js project:
mkdir my-mcp-server
cd my-mcp-server
npm init -y
Install the MCP SDK and dependencies:
npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node tsx
Create a tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
Building Your First Server
Create src/index.ts with a basic server structure:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
// Create server instance
const server = new Server(
{
name: "my-first-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_current_time",
description: "Get the current time in a specified timezone",
inputSchema: {
type: "object",
properties: {
timezone: {
type: "string",
description: "IANA timezone (e.g., 'America/New_York')",
},
},
required: ["timezone"],
},
},
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "get_current_time") {
const timezone = args.timezone as string;
const now = new Date().toLocaleString("en-US", { timeZone: timezone });
return {
content: [
{
type: "text",
text: `Current time in ${timezone}: ${now}`,
},
],
};
}
throw new Error(`Unknown tool: ${name}`);
});
// Start server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP server running on stdio");
}
main().catch((error) => {
console.error("Server error:", error);
process.exit(1);
});
Understanding the Code
Server Initialization
const server = new Server(
{
name: "my-first-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
We create a server instance with metadata and declare capabilities. Here we're only enabling tools.
Request Handlers
MCP uses a request-handler pattern. We register handlers for different request types:
ListToolsRequestSchema: Returns available tools and their schemas
CallToolRequestSchema: Executes tool calls with provided arguments
Transport Layer
const transport = new StdioServerTransport();
await server.connect(transport);
We use stdio transport, meaning the server communicates via standard input/output. This is ideal for local development.
Adding More Tools
Let's add a calculation tool:
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_current_time",
description: "Get the current time in a specified timezone",
inputSchema: {
type: "object",
properties: {
timezone: { type: "string", description: "IANA timezone" },
},
required: ["timezone"],
},
},
{
name: "calculate",
description: "Perform basic arithmetic calculations",
inputSchema: {
type: "object",
properties: {
operation: {
type: "string",
enum: ["add", "subtract", "multiply", "divide"],
description: "The operation to perform",
},
a: { type: "number", description: "First operand" },
b: { type: "number", description: "Second operand" },
},
required: ["operation", "a", "b"],
},
},
],
};
});
// Update tool handler
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "get_current_time") {
// ... existing time tool code
}
if (name === "calculate") {
const { operation, a, b } = args as {
operation: string;
a: number;
b: number;
};
let result: number;
switch (operation) {
case "add":
result = a + b;
break;
case "subtract":
result = a - b;
break;
case "multiply":
result = a * b;
break;
case "divide":
if (b === 0) throw new Error("Division by zero");
result = a / b;
break;
default:
throw new Error(`Unknown operation: ${operation}`);
}
return {
content: [
{
type: "text",
text: `${a} ${operation} ${b} = ${result}`,
},
],
};
}
throw new Error(`Unknown tool: ${name}`);
});
Testing Your Server
Add a build script to package.json:
{
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "tsx src/index.ts"
}
}
Run in development mode:
npm run dev
The server is now waiting for JSON-RPC messages on stdin. You can test it manually by sending:
{"jsonrpc":"2.0","id":1,"method":"tools/list"}
Or integrate it with Claude Desktop by adding to your MCP configuration file.
Error Handling
Always validate inputs and handle errors gracefully:
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
const { name, arguments: args } = request.params;
// Validate args exist
if (!args) {
throw new Error("Missing arguments");
}
// Tool logic here...
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`,
},
],
isError: true,
};
}
});
Next Steps
You now have a working MCP server in TypeScript! In the next lesson, we'll build the same concepts using Python, then dive deeper into implementing more complex tools.
Key takeaways:
- MCP servers use a request-handler pattern
- Tools are defined with JSON Schema for inputs
- stdio transport is great for local development
- Always validate inputs and handle errors