smart-weather
Philippine weather forecasts and hazard data for software agents, over MCP and REST.
It composes PAGASA official government forecasts and advisories, UP NOAH hazard susceptibility
(flood / landslide / storm surge), and Open-Meteo as the gridded backbone for horizon extension and
outage fallback, with PSGC reverse geocoding for locality names. Every field that leaves the service
is provenance-tagged — source, url, fetched_at, and an
official flag distinguishing PAGASA-sourced data from everything else. All user-facing
times are Asia/Manila (PHT, UTC+8).
Quick start · MCP · REST reference · Timeframes & coverage · Provenance & sources · Errors
Quick start
Base URL: https://smart-weather.b11.dev
Every request except GET / (this page) and GET /healthz requires a
bearer key — one shared key guards REST and MCP alike:
Authorization: Bearer <API_KEY>
Minimal working call — tomorrow's forecast for Manila:
curl -H "Authorization: Bearer <API_KEY>" \
"https://smart-weather.b11.dev/v1/forecast?latitude=14.5995&longitude=120.9842&timeframe=tomorrow"
MCP
Endpoint: https://smart-weather.b11.dev/mcp (Streamable HTTP transport, bearer key in
the Authorization header as above).
Typical client config JSON:
{
"mcpServers": {
"smart-weather": {
"type": "http",
"url": "https://smart-weather.b11.dev/mcp",
"headers": {
"Authorization": "Bearer <API_KEY>"
}
}
}
}
Four tools are exposed:
get_forecast— composed forecast for a pointget_advisories— active PAGASA advisoriesget_hazard_profile— UP NOAH hazard susceptibility at a pointresolve_location— reverse-geocode a point to PSGC locality + nearest PAGASA station
Python (fastmcp client):
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport
transport = StreamableHttpTransport(
url="https://smart-weather.b11.dev/mcp",
headers={"Authorization": "Bearer <API_KEY>"},
)
async with Client(transport) as client:
tools = await client.list_tools()
result = await client.call_tool(
"get_forecast",
{"latitude": 14.5995, "longitude": 120.9842, "timeframe": "tomorrow"},
)
REST reference
The same four operations exist as REST endpoints. All endpoints return JSON; parameters are query strings.
Composed forecast for a point: locality, anchoring PAGASA station, per-day summaries, hourly points (windows ≤ 3 days), hazard overlay when rain is forecast, and full provenance.
| Param | Type | Description |
|---|---|---|
latitude | float, required | 4–21 (PH bounding box) |
longitude | float, required | 114–131 (PH bounding box) |
timeframe | string, default "today" | see timeframe formats |
Example (Manila, tomorrow, captured 2026-07-31 — trimmed; hourly has
24 points and sources one entry per upstream fetch):
{
"timeframe": "tomorrow",
"window": {"start": "2026-08-01T00:00:00+08:00", "end": "2026-08-02T00:00:00+08:00"},
"latitude": 14.5995, "longitude": 120.9842,
"generated_at": "2026-07-31T13:07:08.969882Z",
"locality": {"region": "National Capital Region", "municipality": "City of Manila", "method": "polygon"},
"station": {"site_id": "430", "name": "SCIENCE GARDEN", "distance_km": 8.15},
"degraded": false,
"days": [
{
"date": "2026-08-01",
"temperature_min_c": 24.95, "temperature_max_c": 30.85,
"precipitation_mm": 101.3,
"condition_code": "01d", "condition_text": "Clear sky",
"wind_max_mps": 6.3,
"sources": ["PAGASA"], "official": true
}
],
"hourly": [ { "time": "2026-08-01T00:00:00+08:00", "temperature_c": 26.35, ... } ],
"hazards": {"flood": 2, "landslide": 0, "storm_surge": 3, ...},
"hazard_note": "NOAH hazard maps indicate medium flood susceptibility and high storm-surge susceptibility at this location, and rain is forecast within this window. ...",
"sources": [
{"source": "PAGASA", "url": "https://www.pagasa.dost.gov.ph/api/meteogram/430/1", "official": true},
{"source": "Open-Meteo", "url": "https://api.open-meteo.com/v1/forecast?...", "official": false}
]
}
Active PAGASA advisories: tropical cyclone, rainfall, thunderstorm, gale, plus "other" (e.g. General Flood Advisories). All advisories are PAGASA-official.
| Param | Type | Description |
|---|---|---|
latitude, longitude | float, optional | must be given together, or neither |
With coordinates, locality-relevant advisories sort first (relevant: true);
national-level items carry relevant: null. Without coordinates: all active advisories,
newest first.
UP NOAH hazard susceptibility at a point (strict point-in-polygon: 100-year flood, landslide, 5 m storm-surge advisory layers). Near-static data, cached 7 days.
| Param | Type | Description |
|---|---|---|
latitude | float, required | 4–21 |
longitude | float, required | 114–131 |
Returns {"flood": …, "landslide": …, "storm_surge": …, "attribution": {…}} —
levels per hazard level semantics.
Reverse-geocode a point: PSGC region / province / municipality (strict point-in-polygon against
bundled adm3 boundaries; offshore points fall back to nearest centroid with
method: "centroid") plus the nearest PAGASA forecast station with
distance_km (null when none within 200 km).
| Param | Type | Description |
|---|---|---|
latitude | float, required | 4–21 |
longitude | float, required | 114–131 |
Liveness probe — process up + cache DB writable, no upstream calls. Unauthenticated.
Returns {"status": "ok"}.
Timeframes & coverage
The timeframe parameter of get_forecast / /v1/forecast
accepts (case-insensitive):
"now"— current conditions + next 6 h"today","tomorrow"— PHT calendar days"this week"— through Sunday PHT"next N days"— N = 1..16, including today- ISO date —
"2026-08-02"= that PHT calendar day - ISO datetime —
"2026-08-02T14:00"= that instant + 6 h (naive read as PHT)
Windows past the 16-day forecast horizon are rejected with a 400.
Coordinates are validated against a loose Philippine bounding box — latitude 4–21, longitude 114–131. Anything outside returns a 400 (REST) / tool error (MCP).
Provenance & data sources
official semantics: fields derived from PAGASA carry
official: true (per-day official is true only when every contributing
hourly point is PAGASA-sourced). Open-Meteo-sourced fields are official: false. Treat
official as "comes from the Philippine government's weather agency".
degraded semantics: degraded: true on a forecast means
PAGASA failed and Open-Meteo covers the window alone — data is still valid, but none of it is
official. Watch for this in monitoring.
Hazard levels (get_hazard_profile, and the hazard_note
overlay on forecasts): -1 unavailable · 0 little-to-none ·
1 low · 2 medium · 3 high. Points in gaps between hazard
polygons can report little-to-none even with hazards nearby (radius=0 strict lookup).
| Source | Role | Official? |
|---|---|---|
| PAGASA | Official PH government forecasts (station meteograms), current observations, advisories, daily regional outlook | yes — official: true |
| Open-Meteo | Gridded backbone: fills gaps beyond PAGASA's 5-day horizon and covers PAGASA outages | no |
| UP NOAH (UPRI Mapbox tilequery) | Flood / landslide / storm-surge susceptibility | no (research project data) |
| PSGC adm3 boundaries (bundled GeoJSON) | Reverse geocoding to municipality | n/a (static, bundled) |
Errors
Error responses are JSON with a single error key:
| Status | Meaning | Shape |
|---|---|---|
400 |
Bad input: coordinates outside the PH bounding box, unparseable timeframe, window past the 16-day horizon | {"error": "latitude 40.0 is outside the Philippine bounds (4..21)"} |
401 |
Missing or wrong bearer key (WWW-Authenticate: Bearer header set) |
{"error": "unauthorized: a valid 'Authorization: Bearer <key>' header is required"} |
502 |
All upstream weather sources unavailable (PAGASA and Open-Meteo both failing) | {"error": "all upstream weather sources are unavailable right now — try again later"} |
A PAGASA-only outage is not an error: forecasts still answer with
degraded: true, official: false.