{"components":{"parameters":{"city":{"description":"Exact city, optionally state-qualified. Ambiguity returns 409 with choices.","in":"query","name":"city","schema":{"example":"Seattle, WA","type":"string"}},"cityId":{"description":"GeoNames ID from city search. Alternative to city or coordinates.","in":"query","name":"cityId","schema":{"minimum":1,"type":"integer"}},"lat":{"description":"Latitude. The NWS grid lookup uses the value rounded to two decimals (see location.gridLookupPoint); alerts use four decimals.","in":"query","name":"lat","schema":{"maximum":90,"minimum":-90,"type":"number"}},"lon":{"description":"Longitude. The NWS grid lookup uses the value rounded to two decimals (see location.gridLookupPoint); alerts use four decimals.","in":"query","name":"lon","schema":{"maximum":180,"minimum":-180,"type":"number"}},"units":{"in":"query","name":"units","schema":{"default":"us","enum":["us","metric"],"type":"string"}}},"responses":{"AmbiguousCity":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"Ambiguous city (AMBIGUOUS_CITY); select one of choices"},"Busy":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"Too many in-flight requests (BUSY)"},"CityNotFound":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"City not found (CITY_NOT_FOUND); suggestions in choices when available"},"Forbidden":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"Request did not come through the public address (FORBIDDEN). Only when the server is configured with an origin-verify value."},"InvalidQuery":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"Invalid query (INVALID_LOCATION)"},"OutsideCoverage":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"Outside NWS coverage (OUTSIDE_COVERAGE)"},"Timeout":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"Weather lookup deadline exceeded (UPSTREAM_TIMEOUT)"},"UpstreamUnavailable":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"NWS unavailable (UPSTREAM_UNAVAILABLE)"}},"schemas":{"ActiveAlerts":{"additionalProperties":false,"description":"Active alerts and whether they could be checked.","properties":{"alerts":{"items":{"$ref":"#/components/schemas/Alert"},"type":"array"},"alertsStatus":{"description":"unavailable means alerts could not be checked, not that there are none.","enum":["checked","unavailable"],"type":"string"},"assembledAt":{"description":"When this response was assembled, not when upstream data were observed.","format":"date-time","type":"string"},"cacheMaxAgeSeconds":{"description":"Upstream responses are cached up to this many seconds.","type":"integer"},"location":{"$ref":"#/components/schemas/Location"},"sources":{"$ref":"#/components/schemas/AlertSources"},"units":{"$ref":"#/components/schemas/Units"},"warnings":{"description":"Human-readable notes. Source failures make the response partial (Cache-Control: no-store).","items":{"type":"string"},"type":"array"}},"required":["location","units","alerts","alertsStatus","sources","warnings","assembledAt","cacheMaxAgeSeconds"],"type":"object"},"ActiveAlertsEnvelope":{"additionalProperties":false,"properties":{"data":{"$ref":"#/components/schemas/ActiveAlerts"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["data","meta"],"type":"object"},"Alert":{"additionalProperties":false,"description":"One active NWS alert. Text fields are the original NWS wording.","properties":{"area":{"type":["string","null"]},"description":{"type":["string","null"]},"effectiveAt":{"format":"date-time","type":["string","null"]},"event":{"type":["string","null"]},"expiresAt":{"format":"date-time","type":["string","null"]},"headline":{"type":["string","null"]},"id":{"type":["string","null"]},"instruction":{"description":"Original NWS instruction text.","type":["string","null"]},"severity":{"type":["string","null"]}},"required":["id","event","severity","headline","description","instruction","effectiveAt","expiresAt","area"],"type":"object"},"AlertSources":{"additionalProperties":false,"properties":{"alerts":{"$ref":"#/components/schemas/QuerySource"}},"required":["alerts"],"type":"object"},"AlertsStatus":{"description":"checked: alerts were checked (an empty list means none are active). unavailable: the alert check failed; this does not mean there are no alerts. not-checked: this response does not check alerts by design.","enum":["checked","unavailable","not-checked"],"type":"string"},"Attribution":{"additionalProperties":false,"properties":{"license":{"description":"Present when the source requires a license notice.","type":"string"},"name":{"type":"string"},"url":{"type":"string"}},"required":["name","url"],"type":"object"},"CitiesEnvelope":{"additionalProperties":false,"properties":{"data":{"items":{"$ref":"#/components/schemas/City"},"maxItems":10,"type":"array"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["data","meta"],"type":"object"},"City":{"additionalProperties":false,"description":"A city from the GeoNames-derived index (CC BY 4.0).","properties":{"asciiName":{"type":"string"},"country":{"type":"string"},"id":{"description":"GeoNames ID; use as cityId.","type":"integer"},"latitude":{"type":"number"},"longitude":{"type":"number"},"name":{"type":"string"},"population":{"type":"integer"},"state":{"description":"Two-letter state or territory code.","type":"string"},"stateName":{"type":"string"},"timeZone":{"type":"string"}},"required":["id","name","asciiName","state","stateName","country","latitude","longitude","population","timeZone"],"type":"object"},"ErrorBody":{"additionalProperties":false,"properties":{"errors":{"items":{"$ref":"#/components/schemas/ErrorObject"},"minItems":1,"type":"array"}},"required":["errors"],"type":"object"},"ErrorCode":{"description":"Each code has one HTTP status: INVALID_LOCATION 400, FORBIDDEN 403, CITY_NOT_FOUND 404, AMBIGUOUS_CITY 409, OUTSIDE_COVERAGE 422, UPSTREAM_UNAVAILABLE 502, BUSY 503, UPSTREAM_TIMEOUT 504.","enum":["INVALID_LOCATION","CITY_NOT_FOUND","AMBIGUOUS_CITY","OUTSIDE_COVERAGE","UPSTREAM_UNAVAILABLE","UPSTREAM_TIMEOUT","BUSY","FORBIDDEN"],"type":"string"},"ErrorObject":{"additionalProperties":false,"properties":{"choices":{"description":"Candidates for AMBIGUOUS_CITY and suggestions for CITY_NOT_FOUND; otherwise null.","items":{"$ref":"#/components/schemas/City"},"type":["array","null"]},"code":{"$ref":"#/components/schemas/ErrorCode"},"detail":{"type":"string"},"status":{"description":"HTTP status as a string, e.g. \"409\".","type":"string"}},"required":["status","code","detail","choices"],"type":"object"},"GridPoint":{"additionalProperties":false,"description":"Point sent to the NWS /points grid lookup: the location rounded to two decimals (about 1 km) so nearby requests share the grid lookup and its forecasts. Alerts are not rounded; they use the location to four decimals.","properties":{"latitude":{"type":"number"},"longitude":{"type":"number"}},"required":["latitude","longitude"],"type":"object"},"HourlyForecast":{"additionalProperties":false,"description":"Next 24 hourly forecast periods. Alerts are not checked.","properties":{"alertsStatus":{"description":"This endpoint does not check alerts; use /v1/alerts.","enum":["not-checked"],"type":"string"},"assembledAt":{"description":"When this response was assembled, not when upstream data were observed.","format":"date-time","type":"string"},"cacheMaxAgeSeconds":{"description":"Upstream responses are cached up to this many seconds.","type":"integer"},"hourly":{"items":{"$ref":"#/components/schemas/Period"},"maxItems":24,"type":"array"},"location":{"$ref":"#/components/schemas/Location"},"sources":{"$ref":"#/components/schemas/HourlySources"},"units":{"$ref":"#/components/schemas/Units"},"warnings":{"description":"Always includes a fixed note that alerts were not checked; that note alone does not make the response partial.","items":{"type":"string"},"type":"array"}},"required":["location","units","hourly","alertsStatus","sources","warnings","assembledAt","cacheMaxAgeSeconds"],"type":"object"},"HourlyForecastEnvelope":{"additionalProperties":false,"properties":{"data":{"$ref":"#/components/schemas/HourlyForecast"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["data","meta"],"type":"object"},"HourlySources":{"additionalProperties":false,"properties":{"hourly":{"$ref":"#/components/schemas/IssuedSource"}},"required":["hourly"],"type":"object"},"IssuedSource":{"additionalProperties":false,"description":"An NWS document and when it was issued.","properties":{"issuedAt":{"description":"NWS update time; null when unavailable.","format":"date-time","type":["string","null"]},"url":{"type":"string"}},"required":["url","issuedAt"],"type":"object"},"Location":{"additionalProperties":false,"properties":{"cityId":{"description":"GeoNames ID when the location came from the city index.","type":["integer","null"]},"gridLookupPoint":{"$ref":"#/components/schemas/GridPoint"},"latitude":{"description":"City center, or the latitude supplied.","type":"number"},"longitude":{"description":"City center, or the longitude supplied.","type":"number"},"name":{"type":"string"},"precision":{"$ref":"#/components/schemas/Precision"},"timeZone":{"type":["string","null"]}},"required":["name","latitude","longitude","timeZone","cityId","precision","gridLookupPoint"],"type":"object"},"Meta":{"additionalProperties":false,"properties":{"attribution":{"items":{"$ref":"#/components/schemas/Attribution"},"type":"array"}},"required":["attribution"],"type":"object"},"Observation":{"additionalProperties":false,"description":"Station observation, never a forecast. Missing quantities are null.","properties":{"ageSeconds":{"description":"Seconds since observedAt; null when the timestamp is invalid.","type":["integer","null"]},"condition":{"type":["string","null"]},"humidityPercent":{"type":["number","null"]},"observedAt":{"format":"date-time","type":"string"},"sourceUrl":{"description":"NWS URL of this observation.","type":"string"},"stale":{"description":"True when over 2 hours old or the timestamp is invalid.","type":"boolean"},"station":{"description":"NWS station identifier.","type":"string"},"stationDistanceKm":{"type":"number"},"temperature":{"anyOf":[{"$ref":"#/components/schemas/Quantity"},{"type":"null"}]},"windDirectionDegrees":{"type":["number","null"]},"windSpeed":{"anyOf":[{"$ref":"#/components/schemas/Quantity"},{"type":"null"}]}},"required":["observedAt","ageSeconds","stale","station","stationDistanceKm","condition","temperature","humidityPercent","windSpeed","windDirectionDegrees","sourceUrl"],"type":"object"},"Period":{"additionalProperties":false,"description":"One NWS forecast period (daily or hourly).","properties":{"condition":{"type":["string","null"]},"detail":{"description":"Official NWS forecast text, verbatim; measurements keep the original NWS units.","type":["string","null"]},"endsAt":{"format":"date-time","type":["string","null"]},"isDaytime":{"type":["boolean","null"]},"name":{"type":["string","null"]},"precipitationProbabilityPercent":{"type":["number","null"]},"startsAt":{"format":"date-time","type":["string","null"]},"temperature":{"anyOf":[{"$ref":"#/components/schemas/Quantity"},{"type":"null"}]},"wind":{"description":"Speed and direction in the selected units, e.g. 8–16 km/h NW.","type":"string"}},"required":["name","startsAt","endsAt","isDaytime","temperature","precipitationProbabilityPercent","wind","condition","detail"],"type":"object"},"Precision":{"description":"city-center when resolved from the city index; coordinates when supplied by the caller.","enum":["city-center","coordinates"],"type":"string"},"Quantity":{"additionalProperties":false,"description":"A measurement in the selected units.","properties":{"unit":{"example":"°F","type":"string"},"value":{"description":"Rounded to one decimal.","type":"number"}},"required":["value","unit"],"type":"object"},"QuerySource":{"additionalProperties":false,"description":"An NWS query URL.","properties":{"url":{"type":"string"}},"required":["url"],"type":"object"},"ReportSources":{"additionalProperties":false,"properties":{"alerts":{"$ref":"#/components/schemas/QuerySource"},"forecast":{"$ref":"#/components/schemas/IssuedSource"},"hourly":{"$ref":"#/components/schemas/IssuedSource"}},"required":["forecast","hourly","alerts"],"type":"object"},"Units":{"description":"Unit system for numeric temperatures and winds.","enum":["us","metric"],"type":"string"},"WeatherReport":{"additionalProperties":false,"description":"Full weather report: observation, forecast, hourly periods and alerts.","properties":{"alerts":{"items":{"$ref":"#/components/schemas/Alert"},"type":"array"},"alertsStatus":{"description":"See AlertsStatus. unavailable means alerts could not be checked.","enum":["checked","unavailable"],"type":"string"},"assembledAt":{"description":"When this response was assembled, not when upstream data were observed.","format":"date-time","type":"string"},"cacheMaxAgeSeconds":{"description":"Upstream responses are cached up to this many seconds.","type":"integer"},"current":{"anyOf":[{"$ref":"#/components/schemas/Observation"},{"type":"null"}],"description":"Station observation or null; never a forecast."},"forecast":{"items":{"$ref":"#/components/schemas/Period"},"maxItems":14,"type":"array"},"hourly":{"description":"Empty when the hourly forecast failed (see warnings).","items":{"$ref":"#/components/schemas/Period"},"maxItems":24,"type":"array"},"location":{"$ref":"#/components/schemas/Location"},"sources":{"$ref":"#/components/schemas/ReportSources"},"summary":{"description":"Official forecast text for the first period.","type":"string"},"units":{"$ref":"#/components/schemas/Units"},"warnings":{"description":"Human-readable notes. Source failures make the response partial (Cache-Control: no-store).","items":{"type":"string"},"type":"array"}},"required":["location","units","summary","current","forecast","hourly","alerts","alertsStatus","warnings","sources","assembledAt","cacheMaxAgeSeconds"],"type":"object"},"WeatherReportEnvelope":{"additionalProperties":false,"properties":{"data":{"$ref":"#/components/schemas/WeatherReport"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["data","meta"],"type":"object"}}},"info":{"description":"Friendly NWS weather with local GeoNames city lookup. MCP at /mcp uses Streamable HTTP, not REST. GET and HEAD on /v1/* and /openapi.json send Access-Control-Allow-Origin: * (public, read-only, no credentials), so browser clients on any origin can call the API. Responses carry Cache-Control: complete reports are public with max-age and s-maxage equal to the remaining life of their oldest upstream source (at most 120 s); 400/404/409 errors are public, max-age=60; city search is public, max-age=86400; reports with alertsStatus unavailable or a partial-source warning, 422 and 5xx errors are no-store.","title":"Weather Bridge","version":"0.1.0"},"openapi":"3.1.0","paths":{"/v1/alerts":{"get":{"description":"Supply exactly one location mode: city, cityId, or both lat and lon. Fetches only NWS active alerts, independent of forecast availability. alertsStatus unavailable means alerts could not be checked, not that there are none. Cached up to 120 seconds. US/territory coverage.","operationId":"getActiveAlerts","parameters":[{"$ref":"#/components/parameters/city"},{"$ref":"#/components/parameters/cityId"},{"$ref":"#/components/parameters/lat"},{"$ref":"#/components/parameters/lon"},{"$ref":"#/components/parameters/units"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActiveAlertsEnvelope"}}},"description":"Active alerts and alert-check status; an alert-check failure is alertsStatus unavailable with a warning."},"400":{"$ref":"#/components/responses/InvalidQuery"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/CityNotFound"},"409":{"$ref":"#/components/responses/AmbiguousCity"},"422":{"$ref":"#/components/responses/OutsideCoverage"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/Busy"},"504":{"$ref":"#/components/responses/Timeout"}},"summary":"Active official alerts; inspect alertsStatus"}},"/v1/cities":{"get":{"operationId":"searchCities","parameters":[{"in":"query","name":"q","required":true,"schema":{"example":"Springfield, IL","maxLength":120,"minLength":2,"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CitiesEnvelope"}}},"description":"Up to 10 exact or prefix matches ordered by population"},"400":{"$ref":"#/components/responses/InvalidQuery","description":"Invalid search query (INVALID_LOCATION)"},"403":{"$ref":"#/components/responses/Forbidden"}},"summary":"Search US city names or prefixes; optional state qualifier"}},"/v1/forecast/hourly":{"get":{"description":"Supply exactly one location mode: city, cityId, or both lat and lon. Fetches only the NWS grid lookup and hourly forecast; alerts are not checked (alertsStatus is not-checked). Hourly data are cached up to 120 seconds and grid lookups up to six hours. US/territory coverage.","operationId":"getHourlyForecast","parameters":[{"$ref":"#/components/parameters/city"},{"$ref":"#/components/parameters/cityId"},{"$ref":"#/components/parameters/lat"},{"$ref":"#/components/parameters/lon"},{"$ref":"#/components/parameters/units"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HourlyForecastEnvelope"}}},"description":"Hourly forecast periods. A failed hourly fetch returns 502."},"400":{"$ref":"#/components/responses/InvalidQuery"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/CityNotFound"},"409":{"$ref":"#/components/responses/AmbiguousCity"},"422":{"$ref":"#/components/responses/OutsideCoverage"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/Busy"},"504":{"$ref":"#/components/responses/Timeout"}},"summary":"Next 24 hourly forecast periods"}},"/v1/weather":{"get":{"description":"Supply exactly one location mode: city, cityId, or both lat and lon. Forecasts, observations and alerts are cached up to 120 seconds and NWS grid lookups up to six hours; city centers approximate a point. US/territory coverage.","operationId":"getWeather","parameters":[{"$ref":"#/components/parameters/city"},{"$ref":"#/components/parameters/cityId"},{"$ref":"#/components/parameters/lat"},{"$ref":"#/components/parameters/lon"},{"$ref":"#/components/parameters/units"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WeatherReportEnvelope"}}},"description":"Weather report; may contain partial-source warnings."},"400":{"$ref":"#/components/responses/InvalidQuery"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/CityNotFound"},"409":{"$ref":"#/components/responses/AmbiguousCity"},"422":{"$ref":"#/components/responses/OutsideCoverage"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/Busy"},"504":{"$ref":"#/components/responses/Timeout"}},"summary":"Weather by city or coordinates"}}},"servers":[{"description":"The Weather Bridge server that serves this document","url":"/"}]}