Skip to content
Lesson 2 of 5

Building Your First MCP Server

3 min read

Setting Up the Project

Let's build a real MCP server from scratch using TypeScript. Start by initializing your project:

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

Set "module": "node16" and "moduleResolution": "node16" in your tsconfig.json. MCP servers use ES modules, so add "type": "module" to your package.json.

Defining Your First Tool

Create src/index.ts. The SDK provides a McpServer class that handles all protocol details:

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: "my-first-server",
  version: "1.0.0",
});

server.tool(
  "get-weather",
  "Get current weather for a city",
  { city: z.string().describe("City name") },
  async ({ city }) => {
    // In production, call a real weather API
    return {
      content: [
        { type: "text", text: `Weather in ${city}: 22°C, partly cloudy` },
      ],
    };
  }
);

The server.tool() method takes four arguments: a name, a description (used by the AI model to decide when to call it), a Zod schema for input validation, and an async handler function.

Adding Resources

Resources give the model read access to data. Let's add a configuration resource:

server.resource(
  "config",
  "app://config/current",
  { description: "Current application configuration" },
  async () => ({
    contents: [
      {
        uri: "app://config/current",
        text: JSON.stringify({ env: "production", debug: false }),
        mimeType: "application/json",
      },
    ],
  })
);

Connecting the Transport

The final step is wiring the transport and starting the server:

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

Build with npx tsc and your server is ready.

Testing with MCP Inspector

Before connecting to a real host, test with the MCP Inspector — a browser-based tool for debugging MCP servers:

npx @modelcontextprotocol/inspector node dist/index.js

The Inspector shows all registered tools, resources, and prompts. You can invoke them interactively and see the full request/response cycle.

Connecting to Claude Desktop

Add your server to Claude Desktop's configuration file (claude_desktop_config.json):

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

Restart Claude Desktop and your tools appear automatically. Claude will call them when relevant to the conversation.

Connecting to Claude Code

For Claude Code, add the server to your project's .mcp.json:

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

Claude Code picks it up immediately — your tools are available in the coding workflow.

Production Examples

Simple weather servers are fine for learning, but MCP servers scale to serious production use. MCP-Vanguard exposes 89 security tools — from vulnerability scanning to threat intelligence — all through a single MCP server. InfraOps-MCP provides 92 infrastructure management tools for Docker, system monitoring, networking, and more.

These projects demonstrate the pattern: take your team's existing tooling and expertise, wrap it in well-typed MCP tools with clear descriptions, and suddenly your entire AI-powered workflow has access to it.

In the next lesson, we'll explore how to design the agents that use these tools effectively.