MCP Servers: Connecting AI to Real-Time Data
MCP servers are revolutionizing how language models interact with external data sources. Think of them as sophisticated plugins that extend AI capabilities beyond their training data. In this comprehensive tutorial, we'll build a weather MCP server that connects Claude, GPT-4, or any LLM to real-time weather information using Python and the Open-Meteo API.
MCP servers extend language model capabilities by connecting them to data sources and services. They're agnostic applications that facilitate integration with any data or service you can imagine. Think function calling, but the functions are plugins that can do virtually anything.
MCP servers expose three main primitives:
- Resources (client-controlled): Passive data exposure for context
- Tools (model-controlled): Executable functionality for actions
- Prompts (user-controlled): Reusable workflows and templates
Weather data provides an excellent example for MCP server development because it demonstrates real-world API integration with practical applications. Language models excel at transforming raw weather data (temperature, wind speed, humidity) into natural language descriptions and actionable recommendations.
We'll use the Open-Meteo API because it's:
- Free for non-commercial use
- No API key required
- Easily configurable through query parameters
- Perfect for LLM integration
Before diving into code, ensure you have the necessary tools installed. We'll use uv
, a Rust-based Python package manager that makes dependency management seamless.
# Create project directory
mkdir mcp-server-weather
cd mcp-server-weather
# Initialize uv project
uv init
# Create and activate virtual environment
uv venv
source .venv/bin/activate
# Install dependencies
uv add "mcp[cli]" httpx
[{"type": "text", "text": "Forecast for 40.7128, -74.006:\n\nToday:\nTemperature: 66\u00b0F\nWind: 13 to 16 mph W\nMostly Sunny\n---\nTonight:\nTemperature: 55\u00b0F\nWind: 5 to 13 mph W\nPartly Cloudy\n---\nMonday:\nTemperature: 71\u00b0F\nWind: 8 mph W\nSunny\n---\nMonday Night:\nTemperature: 59\u00b0F\nWind: 3 to 8 mph W\nMostly Clear\n---\nTuesday:\nTemperature: 77\u00b0F\nWind: 3 to 7 mph SW\nSunny\n---\nTuesday Night:\nTemperature: 65\u00b0F\nWind: 5 to 8 mph S\nMostly Clear\n---\nWednesday:\nTemperature: 82\u00b0F\nWind: 5 to 12 mph SW\nSunny\n---\nWednesday Night:\nTemperature: 69\u00b0F\nWind: 8 to 12 mph SW\nMostly Clear\n---\nThursday:\nTemperature: 85\u00b0F\nWind: 7 to 12 mph SW\nMostly Sunny\n---\nThursday Night:\nTemperature: 72\u00b0F\nWind: 7 to 12 mph SW\nPartly Cloudy\n---\nFriday:\nTemperature: 82\u00b0F\nWind: 7 to 12 mph SW\nChance Rain Showers\n---\nFriday Night:\nTemperature: 70\u00b0F\nWind: 7 to 12 mph SW\nChance Rain Showers\n---\nSaturday:\nTemperature: 76\u00b0F\nWind: 9 mph W\nChance Rain Showers\n---\nSaturday Night:\nTemperature: 67\u00b0F\nWind: 6 to 9 mph NW\nChance Rain Showers\n---", "uuid": "fb196843-938b-4a75-b1a5-b660892f9817"}]
Here you can see an MCP Server get-weather API response!
Start by creating the server scaffolding. This establishes the foundation for our MCP server using FastMCP, which simplifies server creation and management.
FastMCP provides a streamlined interface for defining tools, resources, and prompts. By leveraging its built-in features, you can focus on implementing functionality without worrying about low-level server details.
from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP
# Initialize FastMCP server
mcp = FastMCP("weather")
# Constants
OPENMETEO_API_BASE = "https://api.open-meteo.com/v1"
USER_AGENT = "weather-app/1.0"
# Helper function for API requests
async def make_openmeteo_request(url: str) -> dict[str, Any] | None:
"""Make a request to the Open-Meteo API with proper error handling."""
headers = {
"User-Agent": USER_AGENT,
"Accept": "application/json"
}
async with httpx.AsyncClient() as client:
try:
response = await client.get(url, headers=headers, timeout=30.0)
response.raise_for_status()
return response.json()
except Exception:
return None
if __name__ == "__main__":
mcp.run(transport='stdio')
Tools are the heart of MCP servers. They expose executable functionality that language models can invoke. Our weather tool will fetch current conditions and forecasts based on geographic coordinates.
@mcp.tool()
async def get_current_weather(latitude: float, longitude: float) -> str:
"""Get current weather for a location.
Args:
latitude: Latitude of the location
longitude: Longitude of the location
"""
url = f"{OPENMETEO_API_BASE}/forecast?latitude={latitude}&longitude={longitude}¤t=temperature_2m,is_day,showers,cloud_cover,wind_speed_10m,wind_direction_10m,pressure_msl,snowfall,precipitation,relative_humidity_2m,apparent_temperature,rain,weather_code,surface_pressure,wind_gusts_10m"
data = await make_openmeteo_request(url)
if not data:
return "Unable to fetch current weather data for this location."
return data
@mcp.tool()
async def get_forecast(latitude: float, longitude: float, days: int = 7) -> str:
"""Get weather forecast for a location.
Args:
latitude: Latitude of the location
longitude: Longitude of the location
days: Number of days to forecast (1-16)
"""
url = f"{OPENMETEO_API_BASE}/forecast?latitude={latitude}&longitude={longitude}&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,wind_speed_10m_max,weather_code&forecast_days={days}"
data = await make_openmeteo_request(url)
if not data:
return "Unable to fetch forecast data for this location."
return data
The MCP Inspector provides a web-based interface for testing your server before integrating it with language models. This tool is invaluable for debugging and validation.
# Start MCP server in development mode
mcp dev server.py
# Open browser to http://localhost:5173
# Connect to the server
# Navigate to Tools tab
# Test get_current_weather with coordinates
# Example: latitude=63.4463991, longitude=10.8127596
When building MCP servers, resist the urge to format returned data within your tools. Instead, return raw data and let the language model process and format it appropriately. This approach leverages the LLM's natural language capabilities while maintaining data integrity.
Best Practices:
- Return complete datasets rather than filtered summaries
- Use descriptive tool names and documentation
- Implement proper error handling for API failures
- Keep tools focused on single responsibilities
- Leverage async/await for better performance
Once you have a basic weather server running, consider adding these enhancements:
Location Services: Integrate the Open-Meteo Geocoding API to convert city names to coordinates, reducing reliance on the LLM for coordinate generation.
Historical Data: Add tools for accessing historical weather patterns and climate data.
Alerts and Warnings: Implement weather alert monitoring for severe weather conditions.
Multiple Locations: Support batch requests for comparing weather across multiple locations.
@mcp.tool()
async def get_location(city: str, country: str = "") -> str:
"""Get coordinates for a city using geocoding.
Args:
city: Name of the city
country: Optional country name for disambiguation
"""
query = f"{city},{country}" if country else city
url = f"https://geocoding-api.open-meteo.com/v1/search?name={query}&count=1&language=en&format=json"
data = await make_openmeteo_request(url)
if not data or not data.get('results'):
return "Location not found."
return data['results'][0]
Your MCP server can integrate with various language models including Claude Desktop, GPT-4 through compatible clients, and other MCP-compatible applications. The server communicates through standard input/output (stdio), making it universally compatible.
To use with Claude Desktop, add your server configuration to the MCP settings file, typically located at ~/Library/Application Support/Claude/claude_desktop_config.json
on macOS.
{
"mcpServers": {
"weather": {
"command": "uv",
"args": ["run", "python", "server.py"],
"cwd": "/path/to/your/mcp-server-weather"
}
}
}
Building MCP servers opens new possibilities for AI integration with real-world data sources. This weather server demonstrates fundamental concepts that apply to any external API or service integration. The Model Context Protocol represents a significant step toward more capable and connected AI systems.
The complete source code for this tutorial is available in the exercise files repository. Experiment with different APIs, add new tools, and explore the endless possibilities of MCP server development.
Security Practices for MCP Using JSON-RPC
Critical security issues, best solutions, and practical tools for robust and secure MCP systems using JSON-RPC.
MCP Servers: Risks of Using in Claude Desktop
Understand the core security, privacy, and operational risks of enabling custom MCP integrations in Claude Desktop on macOS or Windows.
Security Practices for MCP Using JSON-RPC
Critical security issues, best solutions, and practical tools for robust and secure MCP systems using JSON-RPC.
MCP Servers: Risks of Using in Claude Desktop
Understand the core security, privacy, and operational risks of enabling custom MCP integrations in Claude Desktop on macOS or Windows.