From 560a72eaf6c7ea19f770f96c0eec727562d05aeb Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Sat, 21 Feb 2026 21:26:57 -0700 Subject: [PATCH] Add README with real API response examples Every example uses actual NOAA data captured from the live API. Larger responses linked as JSON files in examples/ directory. --- README.md | 248 +++++++++++++++++++++++++ examples/station-detail-seattle.json | 194 +++++++++++++++++++ examples/tide-predictions-seattle.json | 24 +++ examples/water-levels-seattle.json | 213 +++++++++++++++++++++ examples/wind-providence.json | 82 ++++++++ pyproject.toml | 1 + 6 files changed, 762 insertions(+) create mode 100644 README.md create mode 100644 examples/station-detail-seattle.json create mode 100644 examples/tide-predictions-seattle.json create mode 100644 examples/water-levels-seattle.json create mode 100644 examples/wind-providence.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..6773111 --- /dev/null +++ b/README.md @@ -0,0 +1,248 @@ +# noaa-tides + +MCP server for [NOAA CO-OPS Tides and Currents](https://tidesandcurrents.noaa.gov/). Exposes tide predictions, observed water levels, and meteorological data from ~301 U.S. coastal stations as tools, resources, and prompts via [FastMCP 3.0](https://gofastmcp.com/). + +Built for marine planning — fishing trips, boating, crabbing, safety checks. + +## Install + +```bash +# Run directly (no install needed) +uvx noaa-tides + +# Or add to Claude Code +claude mcp add noaa-tides -- uvx noaa-tides + +# Local development +uv run noaa-tides +``` + +## Tools + +### `search_stations` — Find stations by name, state, or type + +``` +search_stations(state="WA") +``` + +Returns up to 50 matching stations: + +```json +[ + {"id": "9447130", "name": "Seattle", "state": "WA", "lat": 47.6026, "lng": -122.3393, "tidal": true}, + {"id": "9446484", "name": "Tacoma", "state": "WA", "lat": 47.2671, "lng": -122.4132, "tidal": true}, + {"id": "9444900", "name": "Port Townsend", "state": "WA", "lat": 48.1129, "lng": -122.7595, "tidal": true} +] +``` + +Also supports `query` (name search) and `is_tidal` (filter tidal vs non-tidal). + +### `find_nearest_stations` — Proximity search by coordinates + +``` +find_nearest_stations(latitude=47.6, longitude=-122.34, limit=3) +``` + +Distances in nautical miles: + +```json +[ + {"id": "9447130", "name": "Seattle", "state": "WA", "lat": 47.6026, "lng": -122.3393, "distance_nm": 0.2}, + {"id": "9446484", "name": "Tacoma", "state": "WA", "lat": 47.2671, "lng": -122.4132, "distance_nm": 20.1}, + {"id": "9445958", "name": "Bremerton", "state": "WA", "lat": 47.5615, "lng": -122.6225, "distance_nm": 14.7} +] +``` + +### `get_station_info` — Expanded station metadata + +``` +get_station_info(station_id="9447130") +``` + +Returns sensors, datums, products, and station details. Seattle has been operating since 1899. + +
+Full response (194 lines) + +See [`examples/station-detail-seattle.json`](examples/station-detail-seattle.json) +
+ +### `get_tide_predictions` — High/low tide times + +``` +get_tide_predictions(station_id="9447130", hours=24) +``` + +Defaults to `hilo` interval (high/low times only) — the most useful format for planning: + +```json +{ + "predictions": [ + {"t": "2026-02-21 00:49", "v": "2.658", "type": "L"}, + {"t": "2026-02-21 07:08", "v": "12.261", "type": "H"}, + {"t": "2026-02-21 13:43", "v": "1.167", "type": "L"}, + {"t": "2026-02-21 19:54", "v": "9.857", "type": "H"} + ] +} +``` + +`type`: `H` = high tide, `L` = low tide. Values in feet above MLLW. + +Other intervals: `"h"` (hourly), `"6"` (6-minute). Datum options: `MLLW`, `MSL`, `NAVD`, `STND`. + +### `get_observed_water_levels` — Actual readings + +``` +get_observed_water_levels(station_id="9447130", hours=3) +``` + +6-minute interval observations. Compare with predictions to see how reality diverges: + +```json +{ + "metadata": {"id": "9447130", "name": "Seattle", "lat": "47.6026", "lon": "-122.3393"}, + "data": [ + {"t": "2026-02-21 17:24", "v": "7.41", "s": "0.059", "f": "0,0,0,0", "q": "p"}, + {"t": "2026-02-21 17:30", "v": "7.62", "s": "0.072", "f": "0,0,0,0", "q": "p"}, + {"t": "2026-02-21 17:36", "v": "7.803", "s": "0.069", "f": "0,0,0,0", "q": "p"} + ] +} +``` + +`q`: `"p"` = preliminary (real-time), `"v"` = verified (post-processed). `s` = standard deviation. + +
+Full 3-hour response (213 lines) + +See [`examples/water-levels-seattle.json`](examples/water-levels-seattle.json) +
+ +### `get_meteorological_data` — Weather observations + +One tool, 8 products. Select via `product` parameter: + +| Product | Fields | Units | +|---------|--------|-------| +| `air_temperature` | `v` | deg F | +| `water_temperature` | `v` | deg F | +| `wind` | `s` (speed), `d` (dir deg), `dr` (compass), `g` (gust) | knots | +| `air_pressure` | `v` | millibars | +| `conductivity` | `v` | mS/cm | +| `visibility` | `v` | nautical miles | +| `humidity` | `v` | percent | +| `salinity` | `v` | PSU | + +``` +get_meteorological_data(station_id="8454000", product="wind", hours=1) +``` + +```json +{ + "metadata": {"id": "8454000", "name": "Providence", "lat": "41.8072", "lon": "-71.4007"}, + "data": [ + {"t": "2026-02-21 22:24", "s": "2.72", "d": "109.0", "dr": "ESE", "g": "3.3", "f": "0,0"}, + {"t": "2026-02-21 22:30", "s": "3.3", "d": "103.0", "dr": "ESE", "g": "3.89", "f": "0,0"}, + {"t": "2026-02-21 22:36", "s": "2.72", "d": "108.0", "dr": "ESE", "g": "3.5", "f": "0,0"} + ] +} +``` + +Not all stations support all products — use `get_station_info` to check available sensors. + +### `marine_conditions_snapshot` — Everything at once + +``` +marine_conditions_snapshot(station_id="9447130") +``` + +Fires 6 parallel API calls (predictions, water level, water temp, air temp, wind, pressure) and returns a combined snapshot. Products that aren't available at a station appear under `unavailable` instead of failing the whole request: + +```json +{ + "station_id": "9447130", + "fetched_utc": "2026-02-22T04:15:38.291Z", + "predictions": {"predictions": [{"t": "...", "v": "12.261", "type": "H"}, "..."]}, + "water_level": {"data": [{"t": "...", "v": "10.22", "s": "0.053", "f": "0,0,0,0", "q": "p"}, "..."]}, + "air_pressure": {"data": [{"t": "...", "v": "1012.4", "f": "0,0,0"}, "..."]}, + "unavailable": { + "water_temperature": "ValueError: No data was found...", + "air_temperature": "ValueError: No data was found...", + "wind": "ValueError: No data was found..." + } +} +``` + +Best starting point for trip planning or safety assessments. + +## Resources + +| URI | Description | +|-----|-------------| +| `noaa://stations` | Full station catalog (~301 stations) | +| `noaa://stations/{station_id}` | Expanded metadata for one station | +| `noaa://stations/{station_id}/nearby` | Stations within 50 nm | + +## Prompts + +### `plan_fishing_trip` + +Guides the LLM through station discovery, tide analysis, and weather assessment to recommend optimal fishing windows. + +``` +plan_fishing_trip(location="Narragansett Bay", target_species="striped bass", date="20260615") +``` + +### `marine_safety_check` + +GO / CAUTION / NO-GO assessment based on wind, visibility, water temperature, and pressure trends. + +``` +marine_safety_check(station_id="9447130") +``` + +## Response field reference + +| Field | Meaning | +|-------|---------| +| `t` | Timestamp (local station time) | +| `v` | Value (water level ft, temp F, pressure mb, etc.) | +| `type` | `H` (high) or `L` (low) — predictions only | +| `s` | Speed (wind, knots) or sigma (water level, std deviation) | +| `d` | Wind direction in degrees true | +| `dr` | Wind compass direction (N, NE, SW, etc.) | +| `g` | Wind gust speed (knots) | +| `f` | Data quality flags (comma-separated) | +| `q` | QA level: `p` = preliminary, `v` = verified | + +## Development + +```bash +git clone && cd noaa-tides +uv sync --dev + +# Run tests (13 tests, mock client, no network) +uv run pytest tests/ -v + +# Lint +uv run ruff check src/ + +# Start server locally +uv run noaa-tides + +# Headless test with Claude +claude -p "Search for tide stations in Rhode Island" \ + --mcp-config .mcp.json \ + --allowedTools "mcp__noaa-tides__*" +``` + +## Data source + +All data from [NOAA Center for Operational Oceanographic Products and Services (CO-OPS)](https://tidesandcurrents.noaa.gov/). No API key required. Station IDs are 7-digit numbers (e.g. `9447130` for Seattle, `8454000` for Providence). + +Two separate APIs are used: +- **Data API** — observations and predictions (`api.tidesandcurrents.noaa.gov/api/prod/datagetter`) +- **Metadata API** — station info, sensors, datums (`api.tidesandcurrents.noaa.gov/mdapi/prod/webapi`) + +## License + +MIT diff --git a/examples/station-detail-seattle.json b/examples/station-detail-seattle.json new file mode 100644 index 0000000..531e1ee --- /dev/null +++ b/examples/station-detail-seattle.json @@ -0,0 +1,194 @@ +{ + "count": 1, + "units": null, + "stations": [ + { + "tidal": true, + "greatlakes": false, + "shefcode": "EBSW1", + "details": { + "id": "9447130", + "established": "1899-01-01 00:00:00", + "removed": "", + "noaachart": "18450", + "timemeridian": -120, + "timezone": -8.0, + "origyear": "1988-09-13 00:00:00", + "self": "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/9447130/details.json" + }, + "sensors": { + "units": "feet", + "sensors": [ + { + "status": 1, + "refdatum": "MSL", + "sensorID": "F1", + "name": "Barometric Pressure", + "elevation": 18.2221, + "message": "", + "dcp": 1 + }, + { + "status": 1, + "refdatum": "MSL", + "sensorID": "site", + "name": "site", + "elevation": 12.480629, + "message": "", + "dcp": 0 + }, + { + "status": 1, + "refdatum": "", + "sensorID": "U1", + "name": "Tsunami WL", + "elevation": null, + "message": "", + "dcp": 1 + }, + { + "status": 1, + "refdatum": "", + "sensorID": "Y1", + "name": "Microwave WL", + "elevation": null, + "message": "", + "dcp": 1 + } + ], + "self": "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/9447130/sensors.json" + }, + "floodlevels": { + "self": "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/9447130/floodlevels.json" + }, + "datums": { + "self": "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/9447130/datums.json" + }, + "supersededdatums": { + "self": "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/9447130/supersededdatums.json" + }, + "harmonicConstituents": { + "self": "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/9447130/harcon.json" + }, + "benchmarks": { + "self": "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/9447130/benchmarks.json" + }, + "tidePredOffsets": { + "self": "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/9447130/tidepredoffsets.json" + }, + "ofsMapOffsets": { + "self": "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/9447130/ofsmapoffsets.json" + }, + "state": "WA", + "timezone": "PST", + "timezonecorr": -8, + "observedst": true, + "stormsurge": false, + "nearby": { + "self": "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/9447130/nearby.json" + }, + "forecast": true, + "outlook": true, + "HTFhistorical": true, + "HTFmonthly": true, + "nonNavigational": false, + "id": "9447130", + "name": "Seattle", + "lat": 47.60264, + "lng": -122.3393, + "affiliations": "NWLON", + "portscode": null, + "products": { + "products": [ + { + "name": "Water Levels", + "value": "https://tidesandcurrents.noaa.gov/waterlevels.html?id=9447130" + }, + { + "name": "Reports", + "value": "https://tidesandcurrents.noaa.gov/reports.html?id=9447130" + }, + { + "name": "Tide Predictions", + "value": "https://tidesandcurrents.noaa.gov/noaatidepredictions.html?id=9447130" + }, + { + "name": "Meteorological", + "value": "https://tidesandcurrents.noaa.gov/met.html?id=9447130" + }, + { + "name": "OFS", + "value": "https://tidesandcurrents.noaa.gov/ofs/ofs_station.html?stname=Seattle&ofs=ssc&stnid=9447130&subdomain=0_cp" + }, + { + "name": "Benchmarks", + "value": "https://tidesandcurrents.noaa.gov/benchmarks.html?id=9447130" + }, + { + "name": "Superseded Benchmarks", + "value": "https://tidesandcurrents.noaa.gov/benchmarks.html?id=9447130&type=superseded" + }, + { + "name": "Datums", + "value": "https://tidesandcurrents.noaa.gov/datums.html?id=9447130" + }, + { + "name": "Superseded Datums", + "value": "https://tidesandcurrents.noaa.gov/datums.html?id=9447130&epoch=1" + }, + { + "name": "Harmonic", + "value": "https://tidesandcurrents.noaa.gov/harcon.html?id=9447130" + }, + { + "name": "Inundation Analysis", + "value": "https://tidesandcurrents.noaa.gov/inundation/AnalysisParams?id=9447130" + }, + { + "name": "Sea Level Trends", + "value": "https://tidesandcurrents.noaa.gov/sltrends/sltrends_station.shtml?id=9447130" + }, + { + "name": "Extreme Water Levels", + "value": "https://tidesandcurrents.noaa.gov/est/est_station.shtml?stnid=9447130" + }, + { + "name": "Physical Oceanagraphy", + "value": "https://tidesandcurrents.noaa.gov/physocean.html?id=9447130" + }, + { + "name": "Coastal Inundation Dashboard", + "value": "https://tidesandcurrents.noaa.gov/inundationdb/inundation.html?id=9447130" + }, + { + "name": "Annual High Tide Flood Outlook", + "value": "https://tidesandcurrents.noaa.gov/high-tide-flooding/annual-outlook.html?station=9447130" + }, + { + "name": "Monthly High Tide Flood Outlook", + "value": "https://tidesandcurrents.noaa.gov/high-tide-flooding/monthly-outlook.html?station=9447130" + }, + { + "name": "OFS Code", + "value": "ssc" + }, + { + "name": "OFS Name", + "value": "Salish Sea and Columbia River" + } + ], + "self": "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/9447130/products.json" + }, + "disclaimers": { + "self": "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/9447130/disclaimers.json" + }, + "notices": { + "self": "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/9447130/notices.json" + }, + "self": "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/9447130.json", + "expand": "details,sensors,floodlevels,datums,harcon,tidepredoffsets,ofsmapoffsets,products,disclaimers,notices", + "tideType": "Mixed" + } + ], + "self": null +} diff --git a/examples/tide-predictions-seattle.json b/examples/tide-predictions-seattle.json new file mode 100644 index 0000000..293ea67 --- /dev/null +++ b/examples/tide-predictions-seattle.json @@ -0,0 +1,24 @@ +{ + "predictions": [ + { + "t": "2026-02-21 00:49", + "v": "2.658", + "type": "L" + }, + { + "t": "2026-02-21 07:08", + "v": "12.261", + "type": "H" + }, + { + "t": "2026-02-21 13:43", + "v": "1.167", + "type": "L" + }, + { + "t": "2026-02-21 19:54", + "v": "9.857", + "type": "H" + } + ] +} diff --git a/examples/water-levels-seattle.json b/examples/water-levels-seattle.json new file mode 100644 index 0000000..89d63f9 --- /dev/null +++ b/examples/water-levels-seattle.json @@ -0,0 +1,213 @@ +{ + "metadata": { + "id": "9447130", + "name": "Seattle", + "lat": "47.6026", + "lon": "-122.3393" + }, + "data": [ + { + "t": "2026-02-21 17:24", + "v": "7.41", + "s": "0.059", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 17:30", + "v": "7.62", + "s": "0.072", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 17:36", + "v": "7.803", + "s": "0.069", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 17:42", + "v": "7.987", + "s": "0.062", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 17:48", + "v": "8.164", + "s": "0.062", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 17:54", + "v": "8.335", + "s": "0.059", + "f": "1,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 18:00", + "v": "8.499", + "s": "0.062", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 18:06", + "v": "8.683", + "s": "0.059", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 18:12", + "v": "8.824", + "s": "0.062", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 18:18", + "v": "8.985", + "s": "0.056", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 18:24", + "v": "9.158", + "s": "0.052", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 18:30", + "v": "9.296", + "s": "0.052", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 18:36", + "v": "9.421", + "s": "0.043", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 18:42", + "v": "9.546", + "s": "0.043", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 18:48", + "v": "9.667", + "s": "0.046", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 18:54", + "v": "9.785", + "s": "0.033", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 19:00", + "v": "9.857", + "s": "0.03", + "f": "1,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 19:06", + "v": "9.926", + "s": "0.043", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 19:12", + "v": "9.995", + "s": "0.043", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 19:18", + "v": "10.044", + "s": "0.036", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 19:24", + "v": "10.11", + "s": "0.03", + "f": "1,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 19:30", + "v": "10.179", + "s": "0.039", + "f": "1,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 19:36", + "v": "10.202", + "s": "0.026", + "f": "1,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 19:42", + "v": "10.215", + "s": "0.03", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 19:48", + "v": "10.228", + "s": "0.023", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 19:54", + "v": "10.225", + "s": "0.033", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 20:00", + "v": "10.218", + "s": "0.026", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 20:06", + "v": "10.218", + "s": "0.026", + "f": "0,0,0,0", + "q": "p" + }, + { + "t": "2026-02-21 20:12", + "v": "10.231", + "s": "0.03", + "f": "1,0,0,0", + "q": "p" + } + ] +} diff --git a/examples/wind-providence.json b/examples/wind-providence.json new file mode 100644 index 0000000..94fd1c3 --- /dev/null +++ b/examples/wind-providence.json @@ -0,0 +1,82 @@ +{ + "metadata": { + "id": "8454000", + "name": "Providence", + "lat": "41.8072", + "lon": "-71.4007" + }, + "data": [ + { + "t": "2026-02-21 22:24", + "s": "2.72", + "d": "109.0", + "dr": "ESE", + "g": "3.3", + "f": "0,0" + }, + { + "t": "2026-02-21 22:30", + "s": "3.3", + "d": "103.0", + "dr": "ESE", + "g": "3.89", + "f": "0,0" + }, + { + "t": "2026-02-21 22:36", + "s": "2.72", + "d": "108.0", + "dr": "ESE", + "g": "3.5", + "f": "0,0" + }, + { + "t": "2026-02-21 22:42", + "s": "4.08", + "d": "93.0", + "dr": "E", + "g": "5.64", + "f": "0,0" + }, + { + "t": "2026-02-21 22:48", + "s": "3.89", + "d": "81.0", + "dr": "E", + "g": "6.22", + "f": "0,0" + }, + { + "t": "2026-02-21 22:54", + "s": "4.08", + "d": "77.0", + "dr": "ENE", + "g": "5.64", + "f": "0,0" + }, + { + "t": "2026-02-21 23:00", + "s": "3.5", + "d": "101.0", + "dr": "E", + "g": "6.22", + "f": "0,0" + }, + { + "t": "2026-02-21 23:06", + "s": "3.69", + "d": "81.0", + "dr": "E", + "g": "4.86", + "f": "0,0" + }, + { + "t": "2026-02-21 23:12", + "s": "3.3", + "d": "76.0", + "dr": "ENE", + "g": "4.47", + "f": "0,0" + } + ] +} diff --git a/pyproject.toml b/pyproject.toml index b4d0529..9424e6f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,7 @@ description = "FastMCP server for NOAA CO-OPS Tides and Currents API" authors = [{ name = "Ryan Malloy", email = "ryan@supported.systems" }] requires-python = ">=3.12" dependencies = ["fastmcp>=3.0.1"] +readme = "README.md" license = "MIT" [project.scripts]