EMY Weather MCP Server
# emy-mcp
An MCP server for **Greek weather data** from [EMY](https://emy.gr) — the Hellenic
National Meteorological Service (Εθνική Μετεωρολογική Υπηρεσία).
It wraps EMY's public (`api.emy.gr`) endpoints as MCP tools. No API key is
required, and **nothing is hardcoded** — locations and data are always fetched
live from EMY.
## Tools
| Tool | What it returns |
|------|-----------------|
| `list_locations` | The live directory of ~1035 forecast locations (id, Greek/English name, prefecture, coordinates). Optional name/prefecture filter. |
| `get_forecast` | Today + up to 3 days for one location, resolved by **name**, **id**, or **coordinates** (nearest point). Temp max/min (°C), wind, precipitation probability, sky condition. |
| `get_alerts` | Structured CAP alerts (event, severity, urgency, onset/expiry, area). |
| `get_marine` | Marine bulletin for Greek seas (METAREA 3): forecast + warnings text. |
| `get_warnings` | National emergency weather bulletins (text, Greek only — EMY publishes no English variant). |
| `get_climate_records` | All-time Greek climate extremes (temperature, precipitation, wind). |
## Resolving a location
This is the tricky part. EMY stores names transliterated from Greek with a
prefecture suffix, e.g. `KORINTHOS (M. KORINTHIAS)`. Three independent problems:
1. **Exonyms vs transliteration** — "Corinth" ≠ "Korinthos", "Corfu" ≠ "Kerkyra".
2. **Prefecture suffix** on every name — `(m. Korinthias)`.
3. **Greek script + accents** — `Κόρινθος` vs `KORINTHOS`.
How they're handled:
- **Greek or Latin name** → `get_forecast(location=...)` or `list_locations(query=...)`.
Names are accent-folded, suffix-stripped, and **Greek is transliterated to
Latin** using EMY's own scheme, so `Κόρινθος`, `korinthos` and `KORINTHOS` all
match. Fuzzy ranking handles typos and returns alternatives.
- **English exonyms / anything ambiguous** → resolve the place to **coordinates**
and call `get_forecast(latitude=..., longitude=...)`. This returns the nearest
forecast point regardless of spelling or language (e.g. Corinth's coordinates →
*Korinthos*, 5 km away). An LLM client typically knows the coordinates already.
## Live data, caching & resilience
Everything is fetched live. Per endpoint:
1. **Fresh window** — cached payload is served without refetch for **30 minutes**
(EMY's forecasts update ~twice daily).
2. After the window, a refetch is attempted.
3. If EMY is **unreachable**, the **last-known-good** copy is served for up to
**24 hours**, flagged `stale` with its age.
4. After 24 hours with no successful fetch, the call **fails** with a clear error.
Every tool response includes a `source` block with `fetched_at`, `age_minutes`
and `stale`.
### TLS note
`api.emy.gr` serves a valid `*.emy.gr` certificate but omits the intermediate CA
from the handshake, so standard clients fail with *"unable to get local issuer
certificate"*. The client fetches the intermediate (`RapidSSL TLS RSA CA G1`)
from the certificate's own AIA URL on first use and caches it under
`~/.cache/emy-mcp/`. Because a fetched cert is installed as a *trusted anchor*,
it is **pinned by SHA-256 fingerprint** — a fetched or cached cert is only
trusted if its fingerprint matches the known-good value, which makes the fetch
safe against tampering and self-heals a corrupt cache. Certificate verification
is **not** disabled; if EMY ever rotates this CA the pin must be updated.
## Data caveats
- **Sky condition** comes from a small icon set (6 values): Sunny, Hot, Partly
cloudy, Scattered showers, Cloudy with rain, Thunderstorms — so it's coarse.
- **Wind speed** units are reported by EMY as a bare number; they appear to be
**m/s** and are labelled as such, but EMY does not document this.
- There is **no live "current observations"** feed in EMY's public API — only
forecasts, alerts, marine and climate. This server reflects that.
## Install & run
```bash
pip install -e . # or: pip install httpx certifi "mcp>=1.2"
# stdio (Claude Desktop, Claude Code, local assistants)
python -m emy_mcp
# streamable-HTTP (hostable)
python -m emy_mcp --http --host 0.0.0.0 --port 8000
```
### Claude Desktop / Claude Code config (stdio)
```json
{
"mcpServers": {
"emy-weather": {
"command": "python",
"args": ["-m", "emy_mcp"]
}
}
}
```
## License
MIT
TDQS
Scored across 6 tools
Each tool targets a distinct weather data type: alerts (CAP format), climate records (historical extremes), forecasts (short-term per location), marine bulletin, text warnings, and location listing. Overlap between alerts and warnings is mitigated by clear descriptions of format differences.
All tool names consistently use snake_case with 'get_' or 'list_' prefixes, and each clearly indicates the resource (e.g., get_alerts, list_locations). No mixed conventions or ambiguous verbs.
With 6 tools covering forecasts, alerts, warnings, marine, climate records, and location listing, the set is well-scoped for a national weather service. Each tool serves a clear purpose without redundancy.
The tool surface covers core weather data needs: forecasts, alerts, warnings, marine, and climate extremes. Missing a dedicated current conditions tool, but forecast provides daily summary. Otherwise, no significant gaps for its domain.