MCP Weather Agent
# MCP Weather Agent
An MCP (Model Context Protocol) server that turns a public REST API into
tools an AI agent can call directly — geocode a place name, then pull
current conditions or a multi-day forecast, with typed inputs and typed,
structured outputs instead of free text.
This is a small, self-contained example of the same pattern used to build
production agent-tool servers for enterprise systems: typed tool schemas,
input validation, retry/timeout handling on outbound calls, and structured
logging, so an agent's tool calls are predictable and debuggable.
## Why this exists
Most "AI agent" demos either hardcode a single API call or let the model
free-form parse HTML. Neither scales past a toy example. This project shows
the pattern that does: each capability is a small, independently testable
tool with a strict input/output contract, and the server itself knows
nothing about *how* an agent decides to call it — that separation is what
lets the same server work behind Claude Desktop, Claude Code, or a custom
LangGraph agent without changes.
## Architecture
```
Agent (Claude Desktop / Claude Code / LangGraph, etc.)
│ MCP protocol (stdio transport)
▼
FastMCP server (server.py)
├─ geocode_location(location_name) → GeocodeResponse
├─ get_current_weather(lat, lon) → CurrentWeather
└─ get_forecast(lat, lon, days) → ForecastResponse
│ validated params → httpx call w/ retry+timeout
▼
Open-Meteo REST API (geocoding + forecast, no API key required)
```
Each tool's return value is a Pydantic model, not a string — so a calling
agent (or a downstream function in a larger pipeline) can read
`result.temperature_c` directly instead of re-parsing prose.
## Design decisions & trade-offs
- **Open-Meteo over a keyed provider**: no API key means anyone cloning this
repo can run it in under a minute. A production system would swap in
whatever provider the business already pays for.
- **Structured Pydantic outputs over raw JSON passthrough**: costs a bit of
mapping code per tool, but means malformed upstream responses fail loudly
at the boundary instead of silently confusing the agent three steps later.
- **Bounded retries in `_get_json`, not a retry library**: for a 3-tool demo
a dependency like `tenacity` is unnecessary weight; the same slot is where
you'd add exponential backoff or circuit-breaking for a heavier-traffic
service.
- **stdio transport by default**: simplest to run locally via Claude
Desktop/Claude Code. Swapping to Streamable HTTP (see `mcp.server.fastmcp`
docs) is a one-line change to `mcp.run(transport=...)` when you need a
server other machines can call.
## Running it
```bash
python -m venv .venv
source .venv/bin/activate # .venv\Scripts\activate on Windows
pip install -e ".[dev]"
pytest # run the offline unit test suite
python -m mcp_weather_agent.server # run the server over stdio
```
### Connecting it to Claude Desktop or Claude Code
Add to your MCP config (see `examples/claude_desktop_config.json`):
```json
{
"mcpServers": {
"weather-agent": {
"command": "python",
"args": ["-m", "mcp_weather_agent.server"],
"cwd": "/absolute/path/to/mcp-weather-agent"
}
}
}
```
Restart the client, and the three tools (`geocode_location`,
`get_current_weather`, `get_forecast`) become available for the agent to call.
## Tools
| Tool | Input | Output |
|---|---|---|
| `geocode_location` | `location_name: str`, `max_results: int` | Ranked list of name/country/lat/lon candidates |
| `get_current_weather` | `latitude: float`, `longitude: float` | Current temperature, apparent temp, wind, precipitation |
| `get_forecast` | `latitude: float`, `longitude: float`, `days: int` | Daily high/low/precipitation for up to 16 days |
## Testing
`tests/test_server.py` monkeypatches the HTTP layer so the suite runs fully
offline and fast — useful for CI, and for making sure tool-shape regressions
(a renamed field, a missing key) get caught before an agent ever sees them.
## Possible extensions
- Swap stdio for Streamable HTTP transport and deploy behind auth for
multi-client access.
- Add a `severe_weather_alerts` tool against a provider that supports it.
- Add response caching (short TTL) to cut duplicate calls when an agent
re-checks the same location across a multi-step plan.
## License
MIT — see `LICENSE`.
TDQS
Scored across 3 tools
Each tool has a clearly distinct purpose: geocoding a place name, retrieving current conditions, and retrieving a forecast. There is no meaningful overlap between geocode_location and the weather retrieval tools, and current vs. forecast are well differentiated by their descriptions.
All tool names use lowercase snake_case with a verb_noun style: geocode_location, get_current_weather, get_forecast. The two retrieval tools follow a uniform get_* pattern, and geocode_location fits the same verb-first convention.
Three tools is an appropriate, well-scoped size for a weather agent. Each tool covers a necessary step in the core workflow: resolve location, get current conditions, get forecast, with no redundancy.
The core weather lookup workflow is complete: geocode a place, then retrieve current or forecast weather. Minor gaps such as lack of alerts, historical data, or reverse geocoding are not essential for the stated purpose.