We are going to build a weather server mcp app and then we're going to use claude desktop for this integration. We will query what is the weather and then claude desktop is going to ask us if we allow our server to be used to query weather data and then reports back to us.
To get started ensure you create your python environment using uv. Make sure you use uv command for this
# Create a new directory for our project
uv init weather
cd weather
# Create virtual environment and activate it
uv venv
.venv\Scripts\activate
# Install dependencies
uv add mcp[cli] httpx
Then add a file call weather.py
from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP
# Initialize FastMCP server
mcp = FastMCP("weather")
# Constants
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"
async def make_nws_request(url: str) -> dict[str, Any] | None:
"""Make a request to the NWS API with proper error handling."""
headers = {
"User-Agent": USER_AGENT,
"Accept": "application/geo+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
def format_alert(feature: dict) -> str:
"""Format an alert feature into a readable string."""
props = feature["properties"]
return f"""
Event: {props.get('event', 'Unknown')}
Area: {props.get('areaDesc', 'Unknown')}
Severity: {props.get('severity', 'Unknown')}
Description: {props.get('description', 'No description available')}
Instructions: {props.get('instruction', 'No specific instructions provided')}
"""
@mcp.tool()
async def get_alerts(state: str) -> str:
"""Get weather alerts for a US state.
Args:
state: Two-letter US state code (e.g. CA, NY)
"""
url = f"{NWS_API_BASE}/alerts/active/area/{state}"
data = await make_nws_request(url)
if not data or "features" not in data:
return "Unable to fetch alerts or no alerts found."
if not data["features"]:
return "No active alerts for this state."
alerts = [format_alert(feature) for feature in data["features"]]
return "\n---\n".join(alerts)
@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
"""Get weather forecast for a location.
Args:
latitude: Latitude of the location
longitude: Longitude of the location
"""
# First get the forecast grid endpoint
points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
points_data = await make_nws_request(points_url)
if not points_data:
return "Unable to fetch forecast data for this location."
# Get the forecast URL from the points response
forecast_url = points_data["properties"]["forecast"]
forecast_data = await make_nws_request(forecast_url)
if not forecast_data:
return "Unable to fetch detailed forecast."
# Format the periods into a readable forecast
periods = forecast_data["properties"]["periods"]
forecasts = []
for period in periods[:5]: # Only show next 5 periods
forecast = f"""
{period['name']}:
Temperature: {period['temperature']}°{period['temperatureUnit']}
Wind: {period['windSpeed']} {period['windDirection']}
Forecast: {period['detailedForecast']}
"""
forecasts.append(forecast)
return "\n---\n".join(forecasts)
if __name__ == "__main__":
# Initialize and run the server
mcp.run(transport='stdio')
And then run it using
uv run .\weather.py
Once you're done with it, download and install claude desktop. Edit the following file - using the following command.
code $env:AppData\Claude\claude_desktop_config.jsoncode
Then ensure you point it to the correct path.
{
"mcpServers": {
"weather": {
"command": "uv",
"args": [
"--directory",
"C:\\Work\\python\\mcp-server-demo",
"run",
"weather.py"
]
}
}
}
Then when your claude desktop loads, you should be able to see this if you go to Dev settings
And then type in the following command:- what is the weather near my area. you should be able to see your mcp server tools here.
When you start your conversation, then claude would ask you for permission to run.
And claude would return the outputs based on the data it obtained from the weather service.
That's it.
How does claude knows which service to call?
If I were to rename my weather service to say testserver. Will Claude be intelligent enough to call it?
Yes. Based on my testing, Claude would read the python server files and then attempt to get lat and long coordinates for Sacramento before invoking get_alerts.
You can see my code changes here. After my code change, Claude did not attempt to get an approval to use the tool.
https://github.com/mitzenjeremywoo/mcp-helloworld-server/commit/973de69848f02f89e9aa3d574cb2a9c5741cb858
Comments