Skip to main content
MCP Directory

Build Your First MCP Server: A Step-by-Step Tutorial

Bottom line: Building an MCP server is simpler than it looks. In this tutorial you will create a tiny todo tool server, connect it to Claude Desktop, and see the model call your code in real time.

What you will build

We will build a command-line MCP server that exposes one tool: manage_todos. The tool can add, list, and remove tasks. The server runs over stdio, which is the default transport for local MCP tools.

By the end you will understand the three pieces every MCP server needs: a tool definition, a handler, and a transport.

Prerequisites

  • Node.js 18 or newer installed on your machine.
  • A code editor and a terminal.
  • Claude Desktop, Cursor, or another MCP-compatible client installed.
  • Basic familiarity with TypeScript and JSON.

Step 1: Initialize the project

Create a new directory and initialize a Node.js project. Install the official SDK and Zod for input validation.

mkdir my-first-mcp-server
cd my-first-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
npx tsc --init

Add a build script to your package.json so you can compile TypeScript before running the server.

Step 2: Define the tool schema

MCP tools need a name, description, and input schema. The description is especially important because it tells the model when to use the tool.

import { z } from 'zod';

const ManageTodosSchema = z.object({
  action: z.enum(['add', 'list', 'remove']),
  text: z.string().optional(),
  id: z.number().optional()
});

Step 3: Implement the handler

The handler receives the validated arguments and returns a result. Keep it simple and avoid side effects beyond what the tool promises.

const todos: string[] = [];

function manageTodos(args: z.infer<typeof ManageTodosSchema>) {
  switch (args.action) {
    case 'add':
      if (!args.text) throw new Error('Text is required when adding a todo.');
      todos.push(args.text);
      return { message: `Added: ${args.text}`, total: todos.length };
    case 'list':
      return { todos };
    case 'remove':
      if (args.id === undefined) throw new Error('ID is required when removing a todo.');
      const removed = todos.splice(args.id, 1);
      return { message: `Removed: ${removed[0] || 'nothing'}`, total: todos.length };
  }
}

Step 4: Wire up the server

Create a server, register the tool, and connect stdio transport. This is the entry point that your client will launch.

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
  import ArticleMeta from '$lib/components/ArticleMeta.svelte';

const server = new Server(
  { name: 'todo-server', version: '1.0.0' },
  { capabilities: { tools: {} } }
);

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: 'manage_todos',
      description: 'Add, list, or remove todo items.',
      inputSchema: zodToJsonSchema(ManageTodosSchema)
    }
  ]
}));

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const args = ManageTodosSchema.parse(request.params.arguments);
  const result = manageTodos(args);
  return { content: [{ type: 'text', text: JSON.stringify(result) }] };
});

const transport = new StdioServerTransport();
await server.connect(transport);

Step 5: Build and test

Compile the project, then add the server to your Claude Desktop configuration file.

{
  "mcpServers": {
    "todos": {
      "command": "node",
      "args": ["/absolute/path/to/my-first-mcp-server/dist/index.js"]
    }
  }
}

Restart Claude Desktop and ask: "Add 'buy milk' to my todos, then list them." Claude should call your tool and show the result.

What to build next

  • Add a resource that exposes a read-only view of the todo list.
  • Replace the in-memory array with a JSON file or SQLite database.
  • Expose a prompt template that helps users plan their week.
  • Package your server and publish it to npm or GitHub.

Published 2026-06-12

Related Resources

MCP Server Development

Skill

Create high-quality Model Context Protocol (MCP) servers to enable AI agents to interact with external APIs and services. Follow best practices for tool design, authentication, error handling, and testing.

Mcp

MCP Server

Catalog of official Microsoft MCP (Model Context Protocol) server implementations for AI-powered data access and tool integration

MCP Tool Orchestrator

Prompt

Design and orchestrate complex multi-tool workflows using the Model Context Protocol (MCP). Build intelligent agent systems that coordinate multiple MCP servers for sophisticated automation tasks.

MCP

Glossary

MCP stands for Model Context Protocol. It is an open standard that lets AI clients connect to external tools, data sources, and prompts through a single, consistent interface. Anthropic introduced MCP in late 2024, and it has since been adopted by Claude Desktop, Cursor, Cline, VS Code, Windsurf, and a growing list of community clients. An MCP server is a small program that exposes three things: tools the model can call, resources the client can read, and prompts that help users accomplish common tasks. An MCP client discovers those capabilities and decides when to invoke them. Transport is usually stdio for local servers or Server-Sent Events for remote ones. For developers, MCP removes the need to build a custom integration for every API. You write one server, and any compatible client can use it. For users, it means AI assistants can securely access files, databases, SaaS tools, and web services without each client reinventing the wheel.

devbrother2024-typescript-mcp-server-boilerplate

MCP Server

Kickstart development with a customizable TypeScript template featuring sample tools for greeting,…