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:

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.

GET/v1/forecast

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.

ParamTypeDescription
latitudefloat, required4–21 (PH bounding box)
longitudefloat, required114–131 (PH bounding box)
timeframestring, 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}
  ]
}
GET/v1/advisories

Active PAGASA advisories: tropical cyclone, rainfall, thunderstorm, gale, plus "other" (e.g. General Flood Advisories). All advisories are PAGASA-official.

ParamTypeDescription
latitude, longitudefloat, optionalmust 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.

GET/v1/hazards

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.

ParamTypeDescription
latitudefloat, required4–21
longitudefloat, required114–131

Returns {"flood": …, "landslide": …, "storm_surge": …, "attribution": {…}} — levels per hazard level semantics.

GET/v1/resolve

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).

ParamTypeDescription
latitudefloat, required4–21
longitudefloat, required114–131
GET/healthz

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):

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).

SourceRoleOfficial?
PAGASA Official PH government forecasts (station meteograms), current observations, advisories, daily regional outlook yesofficial: 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:

StatusMeaningShape
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.