MCP in Practice: Build Your First TypeScript Server in 30 Minutes (2026)
If you work with development and AI, you've heard of MCP. But if you haven't gotten your hands on the code yet, it's time.
In March 2026, the Model Context Protocol SDK reached 97 million monthly downloads — a growth of 4,750% since its launch in November 2024 (Digital Applied). The public registry of MCP servers jumped from ~1,200 in Q1 2025 to over 9,400 in April 2026 (Digital Applied). OpenAI, Google DeepMind, Microsoft, Cohere, and Mistral — all have adopted the protocol (WorkOS Blog, March/2026).
"MCP has done in 16 months what took REST APIs several years: become the default infrastructure layer for a new category of computing." — Digital Applied, MCP Hits 97M Downloads: Model Context Protocol Guide (March/2026)
This tutorial is practical. We will build a functional MCP server in TypeScript, connect it to three different clients, and understand why this protocol became the standard for AI integration in 2026.
1. What We Will Build
We will create a Task Manager MCP Server — a server that exposes tools for managing tasks: add, list, and complete. It seems simple, but it's enough for you to understand the entire flow:
- MCP Server with TypeScript + official SDK
- Three tools with schema validation
- Transport via stdio (for desktop) and mention of Streamable HTTP
- Connection to Claude Desktop, Cursor, and Claude Code
In the end, you will have a functional server that you can extend for any real-world case — database queries, external API calls, file manipulation.
2. Project Setup
Create a folder and initialize the project:
mkdir mcp-task-manager
cd mcp-task-manager
npm init -y
Install the official SDK:
npm install @modelcontextprotocol/sdk zod
The zod package is optional but recommended for schema validation. The MCP SDK uses it internally.
Your package.json needs the type: "module" field to use ESM:
{
"name": "mcp-task-manager",
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
}
}
Create a basic tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "dist",
"rootDir": "src",
"strict": true,
"declaration": true,
"esModuleInterop": true
},
"include": ["src/**/*"]
}
Done. The final structure will be:
mcp-task-manager/
├── package.json
├── tsconfig.json
└── src/
└── index.ts
3. Creating the Server — The Code
Now for the good part. Create src/index.ts with the complete MCP server.
The MCP SDK exports classes and schemas that follow the protocol standard. Here's the 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";
Types and State
interface Task {
id: string;
title: string;
completed: boolean;
createdAt: string;
}
// Our in-memory "database" — replace with a real DB later const tasks: Map<string, Task> = new Map(); let nextId = 1;
Server Instance
const server = new Server(
{
name: "task-manager",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
The second parameter declares the server's capabilities. Here we say it exposes tools. MCP also supports resources, prompts, and logging.
Listing the Tools
The ListToolsRequestSchema handler informs the client which tools are available and their input schemas:
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "add_task",
description: "Adds a new task to the list",
inputSchema: {
type: "object",
properties: {
title: {
type: "string",
description: "Task title",
},
},
required: ["title"],
},
},
{
name: "list_tasks",
description: "Lists registered tasks",
inputSchema: {
type: "object",
properties: {
filter: {
type: "string",
enum: ["all", "pending", "done"],
description: "Filter by status",
},
},
},
},
{
name: "complete_task",
description: "Marks a task as completed",
inputSchema: {
type: "object",
properties: {
id: {
type: "string",
description: "Task ID",
},
},
required: ["id"],
},
},
],
}));
Each tool follows the JSON Schema standard for validation — the client itself validates the parameters before calling.
Executing the Tools
The CallToolRequestSchema handler receives the tool name and arguments:
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) { case "add_task": { const id = String(nextId++); const task: Task = { id, title: args?.title as string, completed: false, createdAt: new Date().toISOString(), }; tasks.set(id, task); return { content: [{ type: "text", text: JSON.stringify(task, null, 2) }], }; }
case "list_tasks": {
const filter = (args?.filter as string) || "all";
let result = Array.from(tasks.values());
if (filter === "pending") result = result.filter((t) => !t.completed);
if (filter === "done") result = result.filter((t) => t.completed);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
}
case "complete_task": {
const id = args?.id as string;
const task = tasks.get(id);
if (!task) {
throw new Error(`Task ${id} not found`);
}
task.completed = true;
return {
content: [{ type: "text", text: JSON.stringify(task, null, 2) }],
};
}
default:
throw new Error(`Unknown tool: ${name}`);
} });
Notice the response pattern: { content: [{ type: "text", text: "..." }] }. MCP accepts multiple content types — text, image, resource, and embedded.
Initializing the Server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("✅ Task Manager MCP Server running via stdio");
}
main().catch((error) => { console.error("Fatal:", error); process.exit(1); });
⚠️ Use
console.errorfor logs.console.logis captured by the stdio transport and can break communication with the client.
Compile and test:
npx tsc
node dist/index.js
If everything went well, you'll see the green message in the terminal. The server is running and waiting for connections.
4. Transports: stdio vs Streamable HTTP
MCP supports two main transports. Each has its use case:
| Feature | stdio | Streamable HTTP |
|---|---|---|
| Connection | Child process via stdin/stdout | HTTP requests |
| Latency | Very low (same process) | Moderate (network) |
| Ideal for | Claude Desktop, Claude Code | Remote web servers, Cursor |
| Sharing | Local only | Remote (via URL) |
| Authentication | Not needed | JWT / API Key |
| Configuration | Point command + args | URL + headers |
| State | Ephemeral (dies with client) | Persistent |
In the example above we used stdio, the simplest transport. To use Streamable HTTP, install the additional package and swap the transport:
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
// Instead of StdioServerTransport: const transport = new StreamableHTTPServerTransport({ port: 3100, endpoint: "/mcp", });
Streamable HTTP allows any HTTP client to consume your server — including from other machines. Perfect for teams and enterprise integrations.
Companies using MCP in production convert pilots at a rate of 38%, compared to only 22% without the protocol — a difference of 16 percentage points (Digital Applied, The MCP Adoption Wave, April/2026).
5. Connecting to Claude Desktop
Claude Desktop was the first MCP client and remains the most used. To connect your server:
- Open Claude Desktop settings
- Go to Settings > Developer > Edit Config
- Add to
claude_desktop_config.json:
{
"mcpServers": {
"task-manager": {
"command": "node",
"args": ["C:/absolute/path/mcp-task-manager/dist/index.js"]
}
}
}
On macOS / Linux:
{
"mcpServers": {
"task-manager": {
"command": "node",
"args": ["/absolute/path/mcp-task-manager/dist/index.js"]
}
}
}
- Restart Claude Desktop
- Look in the bottom right corner — the tool icon 🔧 appears indicating the server is active
Now you can ask: "Add a task to review the monthly report" and Claude will call your add_task tool automatically.
6. Connecting to Cursor and Claude Code
Cursor
In Cursor, go to Settings > Features > MCP and add:
| Field | Value |
|---|---|
| Name | task-manager |
| Type | command |
| Command | node C:/path/mcp-task-manager/dist/index.js |
After adding it, Cursor automatically detects your server's tools and makes them available to the AI agent during development.
Tip: In Cursor, use
Cmd+Ito open the Composer and ask something like "Use the list_tasks tool to see what's pending". The AI will call your MCP server in real-time.
Claude Code
With Claude Code (CLI), the configuration is per project. Create or edit the file ~/.claude/claude.json:
{
"mcpServers": {
"task-manager": {
"command": "node",
"args": ["/path/mcp-task-manager/dist/index.js"]
}
}
}
Then, inside your project, just run claude and start using the tools.
Over 300 clients already support the MCP protocol — including VS Code / GitHub Copilot, Windsurf, and dozens of IDEs and no-code tools (Digital Applied).
7. Verification Checklist
Before you go off building the next great MCP server, check that everything is working:
- The server compiles without errors with
npx tsc - The server starts and shows the log message in the terminal
- Claude Desktop recognizes the server (🔧 icon appears)
- Cursor lists the server's tools in the MCP configuration
-
add_taskreturns a task with a unique ID -
list_tasksreturns the correct list with filters -
complete_taskmarks the task as completed and returns an error for non-existent IDs - Responses are always in the format
{ content: [{ type: "text", text: ... }] }
If any item fails, the most common problem is the absolute path in the configuration JSON — use the full path to dist/index.js.
8. What's Next?
Your first MCP server is live. The official modelcontextprotocol/servers repository has already surpassed 85,600 stars on GitHub, with the TypeScript SDK at 12,300+ stars and 94 releases (GitHub — modelcontextprotocol/servers, GitHub — typescript-sdk). The community is growing fast.
To evolve your server:
- Add resources to expose data the client can read (e.g., task statistics)
- Implement prompts for reusable templates
- Swap the in-memory database for SQLite or PostgreSQL
- Publish it on the MCP registry — the projection for Q3 2026 is 18,400 published servers and 38-46% of the Fortune 1000 with at least one MCP integration in production (Digital Applied, MCP Adoption Q3 2026 Projection)
"When Anthropic quietly shipped the Model Context Protocol in November 2024, it looked like any other internal tool that got open-sourced on a slow news day. Eighteen months later, MCP has 97 million monthly SDK downloads, over 9,400 servers, 300-plus clients, and a Linux Foundation home co-founded by Anthropic, OpenAI, and Block." — Digital Applied, MCP Adoption Statistics 2026 (April/2026)
MCP is today what REST was for the 2000s — the standard integration layer for a new category of computing. Having your own server running is not just a technical exercise. It's preparation for what's coming.
Access modelcontextprotocol.io/docs for the official documentation, and the repository modelcontextprotocol/typescript-sdk for the source code.
Let's code. 🚀
Related Articles
Related Articles
Function Calling in Practice: Python Tutorial for Chatbots with LLMs that Execute Actions in 2026
Learn how to implement function calling in Python with OpenAI, Anthropic Claude, and Google Gemini. Complete tutorial with code to integrate APIs, databases...
Fine-Tuning LLMs in 2026: LoRA vs QLoRA — Which Technique Delivers More for Less (with Code)
Practical and comparative guide to fine-tuning with LoRA and QLoRA for LLMs in 2026, with cost and performance benchmarks on consumer-grade GPUs. Includes Python code...
From Zero to Model: Build Your First Classifier with Python and scikit-learn (2026 Tutorial)
Hands-on tutorial: create your first ML classifier with Python and scikit-learn 1.8, using a real Porto Seguro dataset. Code, deployment, and production tips.