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
-
Create the project
Terminalmkdir my-mcp-server cd my-mcp-server npm init -y npm install @modelcontextprotocol/sdk zod npm install -D typescript @types/node npx tsc --init
-
Configure tsconfig.json
tsconfig.json{ "compilerOptions": { "target": "ES2022", "module": "Node16", "moduleResolution": "Node16", "outDir": "./dist", "rootDir": "./src", "strict": true } } -
Create the server entry point
Create
src/index.tswith the server implementation below.
TypeScript: Complete Server Example
Here is a complete MCP server that provides a weather lookup tool and a greeting prompt:
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:
pip install mcp
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()
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:
# 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:
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 }; } } );
@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:
# 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
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.
Lilly Tech Systems