@modelcontextprotocol/sdk package with Zod for schema validation and TypeScript for end-to-end type safety.
- Basic (stdio)
- Advanced
Basic calculator server
Located at03-GettingStarted/samples/typescript, this is a strongly-typed MCP server with four arithmetic tools.Clone and navigate
git clone https://github.com/microsoft/mcp-for-beginners.git
cd mcp-for-beginners/03-GettingStarted/samples/typescript
package.json
package.json
{
"name": "tutorial-mcp",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "tsc && node ./build/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": ">=1.26.0",
"zod": "^3.24.2"
},
"devDependencies": {
"@types/node": "^22.13.17",
"typescript": "^5.8.2"
}
}
Server code
src/index.ts
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: "Calculator MCP Server",
version: "1.0.0"
});
server.tool(
"add",
{ a: z.number(), b: z.number() },
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }]
})
);
server.tool(
"subtract",
{ a: z.number(), b: z.number() },
async ({ a, b }) => ({
content: [{ type: "text", text: String(a - b) }]
})
);
server.tool(
"multiply",
{ a: z.number(), b: z.number() },
async ({ a, b }) => ({
content: [{ type: "text", text: String(a * b) }]
})
);
server.tool(
"divide",
{ a: z.number(), b: z.number() },
async ({ a, b }) => {
if (b === 0) {
return {
content: [{ type: "text", text: "Error: Cannot divide by zero" }],
isError: true
};
}
return {
content: [{ type: "text", text: String(a / b) }]
};
}
);
const transport = new StdioServerTransport();
server.connect(transport).catch(console.error);
console.log("Calculator MCP Server started");
Advanced server with ResourceTemplate and event system
Located at04-PracticalImplementation/samples/typescript, this sample organises the server as a typed class with private methods for registration, a completion tool, and a dynamic search resource using ResourceTemplate.Server code
import { EventEmitter } from 'events';
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
export class ExtendedMcpServer {
private serverName: string;
private models: string[];
private events: EventEmitter;
private mcpServer: McpServer;
constructor(options: {
serverName?: string;
version?: string;
models?: string[];
} = {}) {
this.serverName = options.serverName || 'TypeScript MCP Server';
this.models = options.models || ['gpt-4', 'llama-3-70b', 'claude-3-sonnet'];
this.events = new EventEmitter();
this.mcpServer = new McpServer({
name: this.serverName,
version: options.version || '1.0.0'
});
this.registerCompletionTool();
this.registerSearchResource();
}
public async connect(): Promise<void> {
const transport = new StdioServerTransport();
await this.mcpServer.connect(transport);
}
public on(event: string, listener: (...args: any[]) => void): void {
this.events.on(event, listener);
}
}
Key TypeScript patterns
| Pattern | Detail |
|---|---|
| Private registration methods | registerCompletionTool() and registerSearchResource() keep the constructor clean |
ResourceTemplate | Declares a URI template (test://{query}) with typed parameters |
EventEmitter | Typed events for request/completion metrics |
| Strict typing | models: string[], options: { temperature?: number } — all inferred by the compiler |