Intermediate

Building MCP Servers

Build an MCP server from scratch using the official TypeScript and Python SDKs. Complete working examples included.

Official SDKs

Anthropic provides official SDKs for building MCP servers in two languages:

SDK Package Install Command
TypeScript @modelcontextprotocol/sdk npm install @modelcontextprotocol/sdk
Python mcp pip install mcp

TypeScript: Project Setup

  1. Create the project

    Terminal
    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
  2. Configure tsconfig.json

    tsconfig.json
    {
      "compilerOptions": {
        "target": "ES2022",
        "module": "Node16",
        "moduleResolution": "Node16",
        "outDir": "./dist",
        "rootDir": "./src",
        "strict": true
      }
    }
  3. Create the server entry point

    Create src/index.ts with the server implementation below.

TypeScript: Complete Server Example

Here is a complete MCP server that provides a weather lookup tool and a greeting prompt:

TypeScript - src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Create the MCP server
const server = new McpServer({
  name: "weather-server",
  version: "1.0.0",
  description: "A simple weather MCP server"
});

// Define a tool: get_weather
server.tool(
  "get_weather",
  "Get current weather for a city",
  {
    city: z.string().describe("City name"),
    units: z.enum(["celsius", "fahrenheit"])
           .optional()
           .default("celsius")
  },
  async ({ city, units }) => {
    // In production, call a real weather API here
    const temp = units === "celsius" ? "22°C" : "72°F";
    return {
      content: [{
        type: "text",
        text: `Weather in ${city}: ${temp}, partly cloudy`
      }]
    };
  }
);

// Define a resource: server info
server.resource(
  "server-info",
  "info://server",
  "Information about this MCP server",
  async () => ({
    contents: [{
      uri: "info://server",
      text: "Weather MCP Server v1.0.0 - Provides weather data",
      mimeType: "text/plain"
    }]
  })
);

// Define a prompt: weather_report
server.prompt(
  "weather_report",
  "Generate a weather report for a city",
  { city: z.string().describe("City name") },
  async ({ city }) => ({
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: `Please generate a detailed weather report for ${city}. Use the get_weather tool to fetch current data, then provide a summary with recommendations.`
      }
    }]
  })
);

// Start the server with stdio transport
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Weather MCP server running on stdio");

Python: Complete Server Example

The same server built with the Python MCP SDK:

Terminal
pip install mcp
Python - server.py
from mcp.server.fastmcp import FastMCP

# Create the MCP server
mcp = FastMCP("weather-server")

# Define a tool: get_weather
@mcp.tool()
def get_weather(city: str, units: str = "celsius") -> str:
    """Get current weather for a city.

    Args:
        city: City name to get weather for
        units: Temperature units (celsius or fahrenheit)
    """
    # In production, call a real weather API here
    temp = "22°C" if units == "celsius" else "72°F"
    return f"Weather in {city}: {temp}, partly cloudy"

# Define a resource: server info
@mcp.resource("info://server")
def server_info() -> str:
    """Information about this MCP server."""
    return "Weather MCP Server v1.0.0 - Provides weather data"

# Define a prompt: weather_report
@mcp.prompt()
def weather_report(city: str) -> str:
    """Generate a weather report for a city."""
    return f"Please generate a detailed weather report for {city}. " \
           f"Use the get_weather tool to fetch current data, " \
           f"then provide a summary with recommendations."

# Run the server
if __name__ == "__main__":
    mcp.run()
Python FastMCP: The FastMCP class provides a high-level, decorator-based API that is the easiest way to build MCP servers in Python. It automatically generates JSON Schema from type hints and docstrings.

Server Structure

A typical MCP server project has this structure:

Project Structure
# TypeScript project
my-mcp-server/
  ├── src/
  │   ├── index.ts          # Server entry point
  │   ├── tools/             # Tool implementations
  │   │   ├── weather.ts
  │   │   └── search.ts
  │   ├── resources/         # Resource handlers
  │   │   └── files.ts
  │   └── prompts/           # Prompt templates
  │       └── report.ts
  ├── package.json
  ├── tsconfig.json
  └── README.md

# Python project
my-mcp-server/
  ├── server.py              # Server entry point
  ├── tools/                 # Tool implementations
  │   ├── weather.py
  │   └── search.py
  ├── resources/             # Resource handlers
  │   └── files.py
  ├── pyproject.toml
  └── README.md

Error Handling

Proper error handling is critical in MCP servers. Tools should return meaningful errors:

TypeScript - Error Handling
server.tool(
  "read_file",
  "Read contents of a file",
  { path: z.string().describe("File path") },
  async ({ path }) => {
    try {
      const content = await fs.readFile(path, "utf-8");
      return {
        content: [{ type: "text", text: content }]
      };
    } catch (error) {
      return {
        content: [{
          type: "text",
          text: `Error reading file: ${error.message}`
        }],
        isError: true
      };
    }
  }
);
Python - Error Handling
@mcp.tool()
def read_file(path: str) -> str:
    """Read contents of a file.

    Args:
        path: File path to read
    """
    try:
        with open(path, "r") as f:
            return f.read()
    except FileNotFoundError:
        raise ValueError(f"File not found: {path}")
    except PermissionError:
        raise ValueError(f"Permission denied: {path}")

Testing Your Server

You can test your MCP server using the MCP Inspector, a development tool provided by Anthropic:

Terminal
# Install and run the MCP Inspector
npx @modelcontextprotocol/inspector

# Or test directly with a TypeScript server:
npx @modelcontextprotocol/inspector node dist/index.js

# Or test a Python server:
npx @modelcontextprotocol/inspector python server.py
📚
MCP Inspector: Opens a web UI where you can list tools, call them with test inputs, view resources, and debug the JSON-RPC messages. It is the fastest way to verify your server works correctly before connecting it to Claude.

What's Next?

Now that you can build a basic server, the next lesson goes deeper into implementing tools, resources, and prompts with detailed schemas and patterns.