Weather MCP Server
by Lystopad6763
README.md
# Weather MCP Server 🌦️
A from-scratch **Model Context Protocol (MCP)** server that gives Claude Desktop
(or any MCP host) **live weather data**. It wraps the free
[Open-Meteo](https://open-meteo.com) API (no API key required), keeps a small
SQLite-backed list of favorite cities, and can optionally push weather to a
Telegram bot.
- **Language / SDK:** Python 3.10+ with the official [`mcp`](https://pypi.org/project/mcp/) package (FastMCP high-level API)
- **Transport:** `stdio`
- **Weather API:** Open-Meteo (geocoding + forecast) — **keyless, free**
- **Storage:** local SQLite file (`favorites.db`)
---
## Capabilities
### 🛠 Tools
| Tool | Arguments | Kind | Description |
|------|-----------|------|-------------|
| `get_current_weather` | `city` *(required)* | live API | Current conditions for a city: temperature, feels-like, humidity, precipitation, wind, sky condition. |
| `get_forecast` | `city` *(required)*, `days` *(optional, default `3`, 1–16)* | live API | Multi-day forecast: per-day min/max temperature, precipitation sum, max wind, sky condition. |
| `add_favorite_city` | `city` *(required)* | write → SQLite | Geocodes a city and saves it to the favorites list (deduplicated). |
| `remove_favorite_city` | `city` *(required)* | write → SQLite | Removes a city from favorites (case-insensitive, with a geocoding fallback). |
| `send_telegram_message` | `text` *(required)* | outbound API | Sends an arbitrary text message to a Telegram chat. *(optional — needs `.env`)* |
| `send_weather_to_telegram` | `city` *(required)* | live + outbound | Fetches current weather and pushes it to a Telegram chat in one step. *(optional — needs `.env`)* |
### 📄 Resource
| Resource URI | Returns | Logic |
|--------------|---------|-------|
| `weather://favorites` | Markdown list of saved cities (name, country, coordinates, when added) | Reads **live** from the SQLite database on every read — not a stub. |
All weather is fetched **live** on every call (no hard-coded responses), and the
resource always reflects the real, current contents of the database.
> **Note on resources vs. tools:** an MCP host calls *tools* automatically, but a
> *resource* is content you attach to the conversation. In Claude Desktop, open
> `+ → <weather> → Add from weather → Favorites resource` to load the current
> favorites into context.
---
## Requirements
- Python 3.10+
- The dependencies in [`requirements.txt`](requirements.txt) (`mcp[cli]`, `httpx`, `python-dotenv`)
- An MCP host such as Claude Desktop (optional — you can also test with the MCP Inspector)
---
## Setup
### 1. Clone & create a virtual environment
```powershell
git clone https://github.com/<your-user>/<repo>.git
cd <repo>
python -m venv .venv
.\.venv\Scripts\Activate.ps1 # Windows PowerShell
# source .venv/bin/activate # macOS / Linux
```
### 2. Install dependencies
```powershell
pip install -r requirements.txt
```
### 3. (Optional) Try it locally with the MCP Inspector
The Inspector speaks the same JSON-RPC/stdio protocol and gives you a UI to list
tools, call them, and read resources — the fastest way to verify the server
before wiring it into a host.
```powershell
mcp dev server.py
```
Open the printed `http://localhost:...` URL, click **List Tools**, call
`get_current_weather` with `{"city": "Kyiv"}`, and read the
`weather://favorites` resource.
---
## Connect to Claude Desktop
1. Open (or create) the Claude Desktop config file:
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
*(for the Microsoft Store build it lives under
`%LOCALAPPDATA%\Packages\Claude_*\LocalCache\Roaming\Claude\`)*
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
2. Add a `weather` server entry with **absolute paths** to the venv's Python and
to `server.py` (see [`claude_desktop_config.json`](claude_desktop_config.json)
for a ready example):
```json
{
"mcpServers": {
"weather": {
"command": "C:\\path\\to\\mcp\\.venv\\Scripts\\python.exe",
"args": ["C:\\path\\to\\mcp\\server.py"]
}
}
}
```
> On Windows, escape backslashes (`\\`) in JSON. The venv Python is used so
> that `mcp` and `httpx` are on the path; no `cwd` is needed because the
> database path is resolved relative to `server.py`.
3. **Fully quit and restart Claude Desktop.** The `weather` server should appear
in the tools menu (🔌 / hammer icon), and you can ask Claude about the weather.
> ℹ️ The [`claude_desktop_config.json`](claude_desktop_config.json) in this
> repository is a **template/example only**. Claude Desktop reads the file in the
> **system location** from step 1 — *not* the copy in the project folder. Copy the
> `mcpServers` block into that system file (or merge it if the file already exists).
---
## Telegram integration (optional)
The `send_telegram_message` and `send_weather_to_telegram` tools forward weather
to a Telegram bot. They stay **disabled** until credentials are provided, so the
server runs fine without this step.
1. **Create a bot:** message [@BotFather](https://t.me/BotFather), send `/newbot`,
and copy the **bot token**.
2. **Find your chat id:** message [@userinfobot](https://t.me/userinfobot) (it
replies with your numeric id), or open
`https://api.telegram.org/bot<token>/getUpdates` after messaging your bot.
3. **Create `.env`** next to `server.py` (copy from [`.env.example`](.env.example)):
```env
TELEGRAM_BOT_TOKEN=123456789:AA-your-real-token
TELEGRAM_CHAT_ID=123456789
```
4. Restart Claude Desktop, then ask: *"Send the current weather in Kyiv to my
Telegram."* The message is delivered in Ukrainian.
> 🔒 `.env` is listed in [`.gitignore`](.gitignore) and is never committed — the
> token stays only on your machine.
---
## Example dialogs
> The assistant replies in the user's language; the prompts below match the
> screenshots in [`examples/`](examples/).
### 1. Current weather — *required argument*
```
You: Яка зараз погода в Києві?
Claude: → get_current_weather(city="Kyiv")
Зараз у Києві ☁️ хмарно, +22.7 °C (відчувається як +23.4 °C),
вологість 58 %, вітер слабкий — 3.6 км/год, без опадів.
```
### 2. Forecast — *optional `days` argument*
```
You: Який прогноз погоди у Львові на найближчі 5 днів?
Claude: → get_forecast(city="Lviv", days=5)
• 21.06 — ☁️ хмарно, +20…+31 °C
• 22.06 — 🌧️ помірний дощ, +21…+30 °C, опади 4.9 мм
• ...
```
### 3. Favorites + resource
```
You: Додай Київ і Львів у мої улюблені, потім прибери Львів.
Claude: → add_favorite_city("Kyiv") → add_favorite_city("Lviv")
→ remove_favorite_city("Lviv")
Готово. (Attach "Favorites resource" to see the live list: Київ.)
```
### 4. Weather → Telegram *(optional)*
```
You: Скинь мені поточну погоду в Києві в Telegram.
Claude: → send_weather_to_telegram(city="Kyiv")
Готово — повідомлення відправлено у твій Telegram-чат. ✅
```
Screenshots of these conversations are in [`examples/`](examples/).
---
## Project structure
```
mcp/
├── server.py # the MCP server (tools + resource)
├── requirements.txt # pinned dependencies
├── claude_desktop_config.json # example host config
├── .env.example # template for Telegram secrets
├── README.md
├── examples/ # screenshots of Claude Desktop dialogs
├── .env # your Telegram secrets (git-ignored, optional)
└── favorites.db # created at runtime (git-ignored)
```
---
## Known limitations
- **City disambiguation is naive.** The geocoder takes the *top* match for a
name. Ambiguous names (e.g. "Springfield") may resolve to the wrong place; the
tools surface the resolved `City, Region, Country` so it is visible.
- **Forecast horizon.** `days` is clamped to Open-Meteo's supported range (1–16);
out-of-range values are clamped, not rejected.
- **Network-dependent.** Every weather call hits Open-Meteo live; offline or
rate-limited requests fail with an error surfaced back to the host.
- **No caching.** Repeated identical questions re-fetch from the API.
- **`favorites.db` is local and unencrypted.** It only stores public city
coordinates and is not synced or backed up.
- **Telegram is optional and single-chat.** The `send_*` tools target one
pre-configured `TELEGRAM_CHAT_ID`; no per-message recipient, no retry, no rate
limiting. Without `.env` they fail with a clear "not configured" message.
- **stdio only.** No HTTP/SSE transport and no authentication — intended for a
local, single-user host, not a shared/remote deployment.
### Security notes
- The server writes **only** JSON-RPC to `stdout`; all diagnostics go to
`stderr`, as the stdio transport requires.
- No shell execution and no string interpolation into commands → no command
injection surface.
- API arguments are passed as typed query parameters via `httpx`, never
concatenated into URLs by hand.
- The only secret (the Telegram token) lives in `.env`, which is git-ignored; the
weather API itself is keyless.
---
## Credits
- Weather & geocoding data: [Open-Meteo](https://open-meteo.com) (CC BY 4.0).
- Protocol: [Model Context Protocol](https://modelcontextprotocol.io).This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues