If you've been exploring AI development, you've probably heard about the Model Context Protocol (MCP). But what exactly is it, and how do you build your own MCP server? In this guide, we'll walk through creating your first MCP server from scratch, no prior experience required.
What is MCP?
The Model Context Protocol is an open standard that enables AI assistants like Claude to securely connect with external data sources and tools. Think of it as a universal adapter that lets AI models interact with your applications, databases, and services in a standardized way.
Before MCP, integrating AI with different tools meant building custom solutions for each integration. MCP provides a unified protocol that makes these connections simpler and more maintainable.
What We're Building
We'll create a simple weather MCP server that provides current weather information. This server will expose a tool that Claude can use to fetch weather data for any city. It's a perfect first project because it's practical, straightforward, and demonstrates all the core MCP concepts.
Prerequisites
Before we start, make sure you have:
- Node.js installed (version 18 or higher)
- Basic knowledge of JavaScript/TypeScript
- A code editor (VS Code recommended)
- An API key from OpenWeatherMap (free tier works fine)
Step 1: Set Up Your Project
First, create a new directory and initialize a Node.js project:
mkdir weather-mcp-server
cd weather-mcp-server
npm init -y
Install the required dependencies:
npm install @modelcontextprotocol/sdk
npm install --save-dev typescript @types/node
Create a tsconfig.json file:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
Step 2: Understanding MCP Server Structure
An MCP server has three main components:
- Resources: Read-only data that the AI can access (like files or database records)
- Tools: Functions that the AI can execute (like searching, creating, or updating data)
- Prompts: Pre-built prompt templates the AI can use
For our weather server, we'll focus on creating a tool since we want Claude to actively fetch weather data.
Step 3: Create the Server
Create a src directory and add an index.ts file:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const WEATHER_API_KEY = process.env.OPENWEATHER_API_KEY;
if (!WEATHER_API_KEY) {
throw new Error("OPENWEATHER_API_KEY environment variable is required");
}
// Create the MCP server
const server = new Server(
{
name: "weather-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_weather",
description: "Get current weather information for a city",
inputSchema: {
type: "object",
properties: {
city: {
type: "string",
description: "The city name (e.g., 'London', 'New York')",
},
},
required: ["city"],
},
},
],
};
});
// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "get_weather") {
const city = request.params.arguments?.city as string;
if (!city) {
throw new Error("City parameter is required");
}
try {
// Fetch weather data from OpenWeatherMap API
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(
city
)}&appid=${WEATHER_API_KEY}&units=metric`
);
if (!response.ok) {
throw new Error(`Weather API error: ${response.statusText}`);
}
const data = await response.json();
return {
content: [
{
type: "text",
text: JSON.stringify(
{
city: data.name,
temperature: data.main.temp,
feels_like: data.main.feels_like,
humidity: data.main.humidity,
description: data.weather[0].description,
wind_speed: data.wind.speed,
},
null,
2
),
},
],
};
} catch (error) {
throw new Error(
`Failed to fetch weather data: ${
error instanceof Error ? error.message : String(error)
}`
);
}
}
throw new Error(`Unknown tool: ${request.params.name}`);
});
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Weather MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
Step 4: Understanding the Code
Let's break down what's happening:
Server Initialization: We create a new MCP server with a name and version, specifying that it has tool capabilities.
ListToolsRequestSchema Handler: This tells Claude what tools are available. We define a get_weather tool with a clear description and input schema that specifies it needs a city name.
CallToolRequestSchema Handler: This is where the actual work happens. When Claude calls our tool, we fetch data from the OpenWeatherMap API and return formatted results.
Transport: We use StdioServerTransport which allows the server to communicate via standard input/output, making it easy to integrate with Claude.
Step 5: Build and Configure
Add build scripts to your package.json:
{
"scripts": {
"build": "tsc",
"start": "node build/index.js"
},
"type": "module"
}
Build your server:
npm run build
Step 6: Get Your API Key
- Go to OpenWeatherMap
- Sign up for a free account
- Navigate to your API keys section
- Copy your API key
Step 7: Connect to Claude Desktop
To use your MCP server with Claude Desktop, you need to configure it. Create or edit the Claude Desktop configuration file:
On macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
On Windows: %APPDATA%\Claude\claude_desktop_config.json
Add your server configuration:
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["/absolute/path/to/your/weather-mcp-server/build/index.js"],
"env": {
"OPENWEATHER_API_KEY": "your_api_key_here"
}
}
}
}
Replace /absolute/path/to/your/weather-mcp-server with the actual path to your project and your_api_key_here with your OpenWeatherMap API key.
Step 8: Test Your Server
Restart Claude Desktop. You should now be able to ask Claude questions like:
- "What's the weather in Tokyo?"
- "Can you check the current temperature in Paris?"
- "Tell me about the weather conditions in Lagos"
Claude will automatically use your MCP server to fetch real-time weather data!
Understanding What Just Happened
When you ask Claude about the weather:
- Claude recognizes it needs weather information
- It discovers your
get_weathertool through the MCP protocol - It calls the tool with the appropriate city parameter
- Your server fetches data from OpenWeatherMap
- The results are returned to Claude
- Claude presents the information in a natural, conversational way
Common Issues and Solutions
Server not appearing in Claude: Make sure you've restarted Claude Desktop after editing the config file, and check that the path to your server is absolute, not relative.
API errors: Verify your OpenWeatherMap API key is correct and that you've activated it (it can take a few minutes after signing up).
TypeScript errors: Ensure you're using Node.js 18 or higher and that all dependencies are installed correctly.
Next Steps
Congratulations! You've built your first MCP server. Here are some ways to extend it:
- Add more weather endpoints (forecast, air quality, historical data)
- Implement caching to reduce API calls
- Add error handling for invalid city names
- Create additional tools for related functionality
- Add resources to provide weather data access patterns
Where to Go From Here
The MCP ecosystem is growing rapidly. Here are some resources to continue your journey:
Building MCP servers opens up endless possibilities for enhancing AI capabilities. Whether you're connecting to databases, APIs or custom business logic, the pattern you've learned here applies universally.
Top comments (1)
One question; are there any specific troubleshooting tips for common issues that might arise during the setup process?