@cyanheads/noaa-marine-mcp-server 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +16 -6
- package/CLAUDE.md +16 -6
- package/README.md +18 -5
- package/changelog/0.5.x/0.5.2.md +19 -0
- package/changelog/0.6.x/0.6.0.md +19 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/resources/definitions/noaa-marine-station.resource.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/noaa-marine-station.resource.js +8 -3
- package/dist/mcp-server/resources/definitions/noaa-marine-station.resource.js.map +1 -1
- package/dist/mcp-server/tools/definitions/noaa-marine-find-stations.tool.d.ts +1 -0
- package/dist/mcp-server/tools/definitions/noaa-marine-find-stations.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/noaa-marine-find-stations.tool.js +37 -120
- package/dist/mcp-server/tools/definitions/noaa-marine-find-stations.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/noaa-marine-get-conditions.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/noaa-marine-get-conditions.tool.js +1 -2
- package/dist/mcp-server/tools/definitions/noaa-marine-get-conditions.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/noaa-marine-get-current-profile.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/noaa-marine-get-current-profile.tool.js +1 -2
- package/dist/mcp-server/tools/definitions/noaa-marine-get-current-profile.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/noaa-marine-get-currents.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/noaa-marine-get-currents.tool.js +3 -6
- package/dist/mcp-server/tools/definitions/noaa-marine-get-currents.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/noaa-marine-get-monthly-means.tool.d.ts +102 -0
- package/dist/mcp-server/tools/definitions/noaa-marine-get-monthly-means.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/noaa-marine-get-monthly-means.tool.js +321 -0
- package/dist/mcp-server/tools/definitions/noaa-marine-get-monthly-means.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/noaa-marine-get-ocean-observations.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/noaa-marine-get-ocean-observations.tool.js +1 -2
- package/dist/mcp-server/tools/definitions/noaa-marine-get-ocean-observations.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/noaa-marine-get-tide-predictions.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/noaa-marine-get-tide-predictions.tool.js +2 -4
- package/dist/mcp-server/tools/definitions/noaa-marine-get-tide-predictions.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/noaa-marine-get-water-level.tool.d.ts +4 -4
- package/dist/mcp-server/tools/definitions/noaa-marine-get-water-level.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/noaa-marine-get-water-level.tool.js +19 -12
- package/dist/mcp-server/tools/definitions/noaa-marine-get-water-level.tool.js.map +1 -1
- package/dist/services/coops/coops-service.d.ts +27 -1
- package/dist/services/coops/coops-service.d.ts.map +1 -1
- package/dist/services/coops/coops-service.js +54 -0
- package/dist/services/coops/coops-service.js.map +1 -1
- package/dist/services/coops/date-range.d.ts +14 -2
- package/dist/services/coops/date-range.d.ts.map +1 -1
- package/dist/services/coops/date-range.js +17 -2
- package/dist/services/coops/date-range.js.map +1 -1
- package/dist/services/coops/row-page.d.ts +1 -1
- package/dist/services/coops/row-page.js +1 -1
- package/dist/services/coops/station-state.d.ts +44 -0
- package/dist/services/coops/station-state.d.ts.map +1 -0
- package/dist/services/coops/station-state.js +131 -0
- package/dist/services/coops/station-state.js.map +1 -0
- package/dist/services/coops/types.d.ts +35 -1
- package/dist/services/coops/types.d.ts.map +1 -1
- package/dist/services/geo.d.ts +9 -0
- package/dist/services/geo.d.ts.map +1 -0
- package/dist/services/geo.js +15 -0
- package/dist/services/geo.js.map +1 -0
- package/package.json +1 -1
- package/server.json +3 -3
package/AGENTS.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** noaa-marine-mcp-server
|
|
4
|
-
**Version:** 0.
|
|
4
|
+
**Version:** 0.6.0
|
|
5
5
|
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`
|
|
6
6
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revisions 2026-07-28 and 2025-*)
|
|
@@ -111,8 +111,15 @@ export const noaaMarineStationResource = resource('noaa-marine://station/{statio
|
|
|
111
111
|
}),
|
|
112
112
|
async handler(params, ctx) {
|
|
113
113
|
const coopsSvc = getCoopsService();
|
|
114
|
-
const
|
|
115
|
-
|
|
114
|
+
const id = params.station_id.toUpperCase();
|
|
115
|
+
// Station lookups read the cached catalogs — there is no per-station fetch.
|
|
116
|
+
const [tide, current, waterLevel, ndbc] = await Promise.all([
|
|
117
|
+
coopsSvc.getStations('tidepredictions', ctx),
|
|
118
|
+
coopsSvc.getStations('currentpredictions', ctx),
|
|
119
|
+
coopsSvc.getStations('waterlevels', ctx),
|
|
120
|
+
getNdbcService().getActiveStations(ctx),
|
|
121
|
+
]);
|
|
122
|
+
const station = [...tide, ...current, ...waterLevel, ...ndbc].find((s) => s.id.toUpperCase() === id);
|
|
116
123
|
if (!station) throw notFound(`Station ${params.station_id} not found`);
|
|
117
124
|
return station;
|
|
118
125
|
},
|
|
@@ -227,11 +234,13 @@ src/
|
|
|
227
234
|
server-config.ts # NOAA_APPLICATION_ID env var (optional, defaults to server name)
|
|
228
235
|
services/
|
|
229
236
|
coops/
|
|
230
|
-
coops-service.ts # CO-OPS Tides & Currents API (station cache, data fetch, message classifier, 403 throttle mapping)
|
|
231
|
-
date-range.ts # YYYYMMDD / YYYY-MM-DD calendar validation, compaction, and span, shared by the date-ranged tools
|
|
237
|
+
coops-service.ts # CO-OPS Tides & Currents API (station cache, resolved-state cache, data fetch, message classifier, 403 throttle mapping)
|
|
238
|
+
date-range.ts # YYYYMMDD / YYYY-MM-DD calendar validation, compaction, and span, shared by the date-ranged tools; the verification-lag window rule
|
|
232
239
|
prediction-class.ts # Decodes the catalog `type` letter into a prediction class
|
|
233
240
|
row-page.ts # Byte-bounded row paging shared by the time-series tools
|
|
241
|
+
station-state.ts # State codes; resolves each station's state (published, else nearest state-bearing row within 25 km)
|
|
234
242
|
types.ts # CO-OPS domain types
|
|
243
|
+
geo.ts # Haversine distance, shared by proximity search and state resolution
|
|
235
244
|
ndbc/
|
|
236
245
|
ndbc-service.ts # NDBC buoy service (active stations XML, realtime text)
|
|
237
246
|
types.ts # NDBC domain types
|
|
@@ -240,6 +249,7 @@ src/
|
|
|
240
249
|
noaa-marine-find-stations.tool.ts # noaa_marine_find_stations
|
|
241
250
|
noaa-marine-get-tide-predictions.tool.ts # noaa_marine_get_tide_predictions
|
|
242
251
|
noaa-marine-get-water-level.tool.ts # noaa_marine_get_water_level
|
|
252
|
+
noaa-marine-get-monthly-means.tool.ts # noaa_marine_get_monthly_means
|
|
243
253
|
noaa-marine-get-currents.tool.ts # noaa_marine_get_currents
|
|
244
254
|
noaa-marine-get-conditions.tool.ts # noaa_marine_get_conditions
|
|
245
255
|
noaa-marine-get-current-profile.tool.ts # noaa_marine_get_current_profile
|
|
@@ -285,7 +295,7 @@ Available skills:
|
|
|
285
295
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
286
296
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
287
297
|
| `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern, release commit on top. No tag, no push to main; opens the release PR when the project declares release PR mode |
|
|
288
|
-
| `release-pr-review` | Review pass on an open release PR — simplifier + correctness review,
|
|
298
|
+
| `release-pr-review` | Review pass on an open release PR — simplifier + correctness review, fixes as ordinary commits on top of the stack, PR body kept in sync. Release PR mode only |
|
|
289
299
|
| `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
290
300
|
| `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
|
|
291
301
|
| `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
|
package/CLAUDE.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** noaa-marine-mcp-server
|
|
4
|
-
**Version:** 0.
|
|
4
|
+
**Version:** 0.6.0
|
|
5
5
|
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`
|
|
6
6
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revisions 2026-07-28 and 2025-*)
|
|
@@ -111,8 +111,15 @@ export const noaaMarineStationResource = resource('noaa-marine://station/{statio
|
|
|
111
111
|
}),
|
|
112
112
|
async handler(params, ctx) {
|
|
113
113
|
const coopsSvc = getCoopsService();
|
|
114
|
-
const
|
|
115
|
-
|
|
114
|
+
const id = params.station_id.toUpperCase();
|
|
115
|
+
// Station lookups read the cached catalogs — there is no per-station fetch.
|
|
116
|
+
const [tide, current, waterLevel, ndbc] = await Promise.all([
|
|
117
|
+
coopsSvc.getStations('tidepredictions', ctx),
|
|
118
|
+
coopsSvc.getStations('currentpredictions', ctx),
|
|
119
|
+
coopsSvc.getStations('waterlevels', ctx),
|
|
120
|
+
getNdbcService().getActiveStations(ctx),
|
|
121
|
+
]);
|
|
122
|
+
const station = [...tide, ...current, ...waterLevel, ...ndbc].find((s) => s.id.toUpperCase() === id);
|
|
116
123
|
if (!station) throw notFound(`Station ${params.station_id} not found`);
|
|
117
124
|
return station;
|
|
118
125
|
},
|
|
@@ -227,11 +234,13 @@ src/
|
|
|
227
234
|
server-config.ts # NOAA_APPLICATION_ID env var (optional, defaults to server name)
|
|
228
235
|
services/
|
|
229
236
|
coops/
|
|
230
|
-
coops-service.ts # CO-OPS Tides & Currents API (station cache, data fetch, message classifier, 403 throttle mapping)
|
|
231
|
-
date-range.ts # YYYYMMDD / YYYY-MM-DD calendar validation, compaction, and span, shared by the date-ranged tools
|
|
237
|
+
coops-service.ts # CO-OPS Tides & Currents API (station cache, resolved-state cache, data fetch, message classifier, 403 throttle mapping)
|
|
238
|
+
date-range.ts # YYYYMMDD / YYYY-MM-DD calendar validation, compaction, and span, shared by the date-ranged tools; the verification-lag window rule
|
|
232
239
|
prediction-class.ts # Decodes the catalog `type` letter into a prediction class
|
|
233
240
|
row-page.ts # Byte-bounded row paging shared by the time-series tools
|
|
241
|
+
station-state.ts # State codes; resolves each station's state (published, else nearest state-bearing row within 25 km)
|
|
234
242
|
types.ts # CO-OPS domain types
|
|
243
|
+
geo.ts # Haversine distance, shared by proximity search and state resolution
|
|
235
244
|
ndbc/
|
|
236
245
|
ndbc-service.ts # NDBC buoy service (active stations XML, realtime text)
|
|
237
246
|
types.ts # NDBC domain types
|
|
@@ -240,6 +249,7 @@ src/
|
|
|
240
249
|
noaa-marine-find-stations.tool.ts # noaa_marine_find_stations
|
|
241
250
|
noaa-marine-get-tide-predictions.tool.ts # noaa_marine_get_tide_predictions
|
|
242
251
|
noaa-marine-get-water-level.tool.ts # noaa_marine_get_water_level
|
|
252
|
+
noaa-marine-get-monthly-means.tool.ts # noaa_marine_get_monthly_means
|
|
243
253
|
noaa-marine-get-currents.tool.ts # noaa_marine_get_currents
|
|
244
254
|
noaa-marine-get-conditions.tool.ts # noaa_marine_get_conditions
|
|
245
255
|
noaa-marine-get-current-profile.tool.ts # noaa_marine_get_current_profile
|
|
@@ -285,7 +295,7 @@ Available skills:
|
|
|
285
295
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
286
296
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
287
297
|
| `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern, release commit on top. No tag, no push to main; opens the release PR when the project declares release PR mode |
|
|
288
|
-
| `release-pr-review` | Review pass on an open release PR — simplifier + correctness review,
|
|
298
|
+
| `release-pr-review` | Review pass on an open release PR — simplifier + correctness review, fixes as ordinary commits on top of the stack, PR body kept in sync. Release PR mode only |
|
|
289
299
|
| `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
290
300
|
| `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
|
|
291
301
|
| `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
|
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<h1>@cyanheads/noaa-marine-mcp-server</h1>
|
|
3
3
|
<p><b>Find NOAA tide stations and NDBC buoys, fetch tide predictions, water levels, tidal currents, and live buoy conditions via MCP. STDIO or Streamable HTTP.</b>
|
|
4
|
-
<div>
|
|
4
|
+
<div>8 Tools • 1 Resource</div>
|
|
5
5
|
</p>
|
|
6
6
|
</div>
|
|
7
7
|
|
|
8
8
|
<div align="center">
|
|
9
9
|
|
|
10
|
-
[](./CHANGELOG.md) [](./LICENSE) [](https://github.com/users/cyanheads/packages/container/package/noaa-marine-mcp-server) [](https://modelcontextprotocol.io/) [](https://www.npmjs.com/package/@cyanheads/noaa-marine-mcp-server) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
@@ -38,6 +38,7 @@ US tide, current, and buoy data from NOAA CO-OPS and NDBC. Find tide, water-leve
|
|
|
38
38
|
| `noaa_marine_find_stations` | Find CO-OPS tide/water-level/current stations and NDBC buoys by location, name, state, or data capability. |
|
|
39
39
|
| `noaa_marine_get_tide_predictions` | High/low tide predictions or a 6-minute curve for a CO-OPS tide station. |
|
|
40
40
|
| `noaa_marine_get_water_level` | Observed water level at a 6-minute, hourly, high/low, or daily-mean cadence, paired with predictions and a storm-surge residual summary. |
|
|
41
|
+
| `noaa_marine_get_monthly_means` | Verified monthly tidal datums and extremes for a CO-OPS water-level station — the record for sea-level and tidal-range trends. |
|
|
41
42
|
| `noaa_marine_get_currents` | CO-OPS tidal current predictions — max flood/ebb/slack events or a 6-minute curve. |
|
|
42
43
|
| `noaa_marine_get_conditions` | Live NDBC buoy conditions: waves, wind, sea-surface and air temperature, pressure. |
|
|
43
44
|
| `noaa_marine_get_current_profile` | Observed ocean-current depth profile from an NDBC ADCP buoy. |
|
|
@@ -56,6 +57,7 @@ All resource data is also reachable via tools — use `noaa_marine_find_stations
|
|
|
56
57
|
### `noaa_marine_find_stations` <sub>tool</sub>
|
|
57
58
|
|
|
58
59
|
- Filter by proximity (`latitude`/`longitude` + `radius_km`, default 100 km, max 1000 km), name/ID substring (matched against both sources; an exact ID match sorts first), US state/territory (CO-OPS only), source (`coops`/`ndbc`/`all`), or `types`: data capabilities (`tide`, `current`, `water_level`, `met`, `current_profile`, `water_quality`) or NDBC platform class (`buoy`)
|
|
60
|
+
- A CO-OPS station whose catalog rows carry no state code — every current station, and some tide and water-level stations — reports and filters on the state of the nearest state-bearing tide or water-level station within 25 km, marked `state_derived: true`; that state can be wrong on waters shared across a state or national border. A station with no such neighbor shows its own non-code catalog value (e.g. `FM`) if it publishes one, which no state filter matches
|
|
59
61
|
- Returns up to `limit` (default 20, max 200) unified stations with source, coordinates, distance, data capabilities, and — for NDBC — physical platform class (buoy, fixed, oilrig, dart, tao, usv, other)
|
|
60
62
|
- CO-OPS prediction stations also carry `prediction_class`, a third axis beside capability and platform: a tide station is `reference` (serving `hilo` and the 6-minute curve) or `subordinate` (`hilo` only, with `reference_id` naming where its offsets come from), while a current station reports its class per depth bin in `bins[]` alongside each bin's number and catalog depth in feet — the bin numbers `noaa_marine_get_currents` takes as `bin`
|
|
61
63
|
- `total_found` and `truncated` report the full match count before the limit is applied
|
|
@@ -90,7 +92,18 @@ All resource data is also reachable via tools — use `noaa_marine_find_stations
|
|
|
90
92
|
- `residual_summary` (max surge, max drawdown) only when both series are present, computed from the finite observed/predicted pairs across the whole matched series rather than the returned page. Each side clamps at zero, so a window that stayed above prediction reports `max_drawdown: 0` and one that stayed below reports `max_surge: 0`. Reported on `6min` and `hourly` only — observed high and low waters do not occur at the predicted extreme times, so a `high_low` join would rest on a small fraction of the events, and `daily_mean` has no paired series at all; the notice says which applies
|
|
91
93
|
- A range whose rows fit the response budget returns whole; a longer one returns the leading rows as a page, with `rows_matched`, `rows_returned`, `page_offset`, and `next_offset` on both consumption surfaces. Observations carry the `offset` and the paired predictions follow by time window, so a page's two series always describe one span even after gap rows shorten the observed one
|
|
92
94
|
- `daily_mean` is requested in local standard time whatever `time_zone` was passed — CO-OPS serves that product in LST only and silently shifts any other zone by a day
|
|
93
|
-
- Typed `date_range_exceeded`, `invalid_date_range`, `station_not_found`, `no_data`, `datum_unavailable`, `great_lakes_only` (`daily_mean` at a coastal station), `verified_data_lag`, and `upstream_throttled` errors — `verified_data_lag` for
|
|
95
|
+
- Typed `date_range_exceeded`, `invalid_date_range`, `station_not_found`, `no_data`, `datum_unavailable`, `great_lakes_only` (`daily_mean` at a coastal station), `verified_data_lag`, and `upstream_throttled` errors — `verified_data_lag` for an `hourly`, `high_low`, or `daily_mean` window ending on or after the first day of the prior month, which CO-OPS may not have verified yet since it verifies the coarser products monthly for the prior month (an earlier window it answers the same way is `no_data`, since waiting cannot help it), and `upstream_throttled` for the HTTP 403 CO-OPS answers a burst of requests with (a 403 on the paired prediction fetch alone leaves `predictions_status: "unavailable"` instead, with a notice naming the wait)
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
### `noaa_marine_get_monthly_means` <sub>tool</sub>
|
|
100
|
+
|
|
101
|
+
- One row per station-month of the CO-OPS `monthly_mean` product: the month's `highest` and `lowest` water, its tidal datums (`mhhw`, `mhw`, `msl`, `mtl`, `mlw`, `mllw`, `dtl`), ranges (`gt`, `mn`, `dhq`, `dlq`), lunitidal intervals in hours (`hwi`, `lwi`), and CO-OPS's `inferred` code passed through verbatim — a code (`0`, `1`, `11` observed), not a boolean
|
|
102
|
+
- Heights are relative to the requested datum — eleven reference planes: MLLW (default), MHHW, MHW, MTL, MSL, MLW, NAVD, STND, IGLD and LWD (Great Lakes only), CRD (Columbia River only) — in `english` feet or `metric` meters. The ranges (`gt`, `mn`, `dhq`, `dlq`) are differences between two planes, so they read the same under every datum. A value CO-OPS publishes as an empty string is omitted, never zeroed — a Great Lakes station at `IGLD` carries only `highest`, `msl`, and `lowest`
|
|
103
|
+
- `begin_date`/`end_date` as `YYYYMMDD` or `YYYY-MM-DD`, spanning up to 73,000 days (the ceiling CO-OPS names), rejected locally before the call. The months the two dates fall in are the first and last returned; CO-OPS answers one month past `end_date`, and that month is dropped
|
|
104
|
+
- A range whose months fit the response budget returns whole; a longer one — decades of months — returns the leading months as a page with `rows_matched`, `rows_returned`, `page_offset`, and `next_offset`. Walk it with `offset`; an `offset` past the last month is an empty page rather than an error
|
|
105
|
+
- Typed `invalid_date_range`, `date_range_exceeded`, `station_not_found`, `datum_unavailable`, `verified_data_lag`, `no_data`, and `upstream_throttled` errors. CO-OPS answers an unpublished window with one sentence whatever the cause, so a window ending on or after the first day of the prior month is `verified_data_lag` and any earlier one is `no_data`
|
|
106
|
+
- Returns the monthly record, not a trend — fitting one is left to the caller
|
|
94
107
|
|
|
95
108
|
---
|
|
96
109
|
|
|
@@ -137,7 +150,7 @@ All resource data is also reachable via tools — use `noaa_marine_find_stations
|
|
|
137
150
|
|
|
138
151
|
### `noaa-marine://station/{station_id}` <sub>resource</sub>
|
|
139
152
|
|
|
140
|
-
- Station record as `application/json` — name, coordinates, source, capabilities, state, the CO-OPS `prediction_class` (with `reference_id` or per-bin `bins[]`, exactly as on `noaa_marine_find_stations`), and (NDBC) platform class
|
|
153
|
+
- Station record as `application/json` — name, coordinates, source, capabilities, state (with `state_derived`, resolved as on `noaa_marine_find_stations`), the CO-OPS `prediction_class` (with `reference_id` or per-bin `bins[]`, exactly as on `noaa_marine_find_stations`), and (NDBC) platform class
|
|
141
154
|
- `station_id` comes from `noaa_marine_find_stations`
|
|
142
155
|
- Typed `station_not_found` when both catalogs were read and neither carries the ID, and `source_unavailable` when a catalog could not be read — a station only the unread catalog carries is never reported as nonexistent. When the unread CO-OPS catalog was throttled (HTTP 403), the recovery names the couple-of-minutes wait
|
|
143
156
|
- Cached with a 6-hour TTL (`cacheHint`)
|
|
@@ -329,7 +342,7 @@ The Dockerfile defaults to HTTP transport, stateless session mode, and logs to `
|
|
|
329
342
|
| `src/config/` | `NOAA_APPLICATION_ID` env var parsing with Zod. |
|
|
330
343
|
| `src/services/coops/` | CO-OPS Tides & Currents API client: station list cache, data fetch, error detection. |
|
|
331
344
|
| `src/services/ndbc/` | NDBC buoy service: active stations XML parser, realtime text parser. |
|
|
332
|
-
| `src/mcp-server/tools/` |
|
|
345
|
+
| `src/mcp-server/tools/` | Eight tool definitions (`*.tool.ts`). |
|
|
333
346
|
| `src/mcp-server/resources/` | Station metadata resource (`noaa-marine-station.resource.ts`). |
|
|
334
347
|
| `tests/` | Vitest tests mirroring `src/`. |
|
|
335
348
|
| `docs/` | Design doc and directory tree. |
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Derived state for stateless CO-OPS stations, truncation notice fix"
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.5.2 — 2026-09-22
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`state_derived`** on `noaa_marine_find_stations` and the station resource: a CO-OPS station whose own catalog rows carry no state code — every current station, plus some tide and water-level stations — now resolves to the state of the nearest state-bearing tide or water-level row within 25 km, and the `state` filter matches that resolved value ([#36](https://github.com/cyanheads/noaa-marine-mcp-server/issues/36))
|
|
12
|
+
|
|
13
|
+
## Changed
|
|
14
|
+
|
|
15
|
+
- Haversine distance moved to `src/services/geo.ts` and the state-code list to `src/services/coops/station-state.ts`, shared by the proximity search and state resolution; `CLAUDE.md`/`AGENTS.md` resource example and structure tree synced with the code
|
|
16
|
+
|
|
17
|
+
## Fixed
|
|
18
|
+
|
|
19
|
+
- **`noaa_marine_find_stations` truncation notice** — a capped search disclosed `shown`/`cap`/`truncationCeiling` through `ctx.enrich.truncated()`, none of which the `enrichment` block declared, so the framework stripped them before they reached the caller; the guidance now composes into the already-declared `notice`, and the `content[]` trailer no longer prints a stray `**truncated:**` line ([#38](https://github.com/cyanheads/noaa-marine-mcp-server/issues/38))
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "New noaa_marine_get_monthly_means tool, window-based verified_data_lag fix"
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.6.0 — 2026-09-22
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`noaa_marine_get_monthly_means`** — verified monthly tidal datums and extremes for a CO-OPS water-level station, one row per station-month: highest/lowest water, tidal datums (MHHW, MHW, MSL, MTL, MLW, MLLW, DTL), ranges (GT, MN, DHQ, DLQ), lunitidal intervals (HWI, LWI), and CO-OPS's `inferred` code verbatim. Wraps the `monthly_mean` product, sent as `time_zone=lst` and trimmed of the resulting overrun month; empty-string upstream values are omitted rather than parsed to 0. Ranges up to 73,000 days, paged by `offset` ([#33](https://github.com/cyanheads/noaa-marine-mcp-server/issues/33))
|
|
12
|
+
|
|
13
|
+
## Changed
|
|
14
|
+
|
|
15
|
+
- **`noaa_marine_find_stations`** — the `types` value `water_level` now names `noaa_marine_get_monthly_means` alongside `noaa_marine_get_water_level` as the tool that reads it ([#33](https://github.com/cyanheads/noaa-marine-mcp-server/issues/33))
|
|
16
|
+
|
|
17
|
+
## Fixed
|
|
18
|
+
|
|
19
|
+
- **`noaa_marine_get_water_level`** — an `hourly`, `high_low`, or `daily_mean` window CO-OPS answers with "may not be offered at this station at the requested time" now resolves by window instead of always reading as `verified_data_lag`: a window ending on or after the first day of the prior month is `verified_data_lag`, an earlier one is `no_data`, since waiting cannot help a window before the station's record ([#41](https://github.com/cyanheads/noaa-marine-mcp-server/issues/41))
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { noaaMarineFindStations } from './mcp-server/tools/definitions/noaa-mari
|
|
|
10
10
|
import { noaaMarineGetConditions } from './mcp-server/tools/definitions/noaa-marine-get-conditions.tool.js';
|
|
11
11
|
import { noaaMarineGetCurrentProfile } from './mcp-server/tools/definitions/noaa-marine-get-current-profile.tool.js';
|
|
12
12
|
import { noaaMarineGetCurrents } from './mcp-server/tools/definitions/noaa-marine-get-currents.tool.js';
|
|
13
|
+
import { noaaMarineGetMonthlyMeans } from './mcp-server/tools/definitions/noaa-marine-get-monthly-means.tool.js';
|
|
13
14
|
import { noaaMarineGetOceanObservations } from './mcp-server/tools/definitions/noaa-marine-get-ocean-observations.tool.js';
|
|
14
15
|
import { noaaMarineGetTidePredictions } from './mcp-server/tools/definitions/noaa-marine-get-tide-predictions.tool.js';
|
|
15
16
|
import { noaaMarineGetWaterLevel } from './mcp-server/tools/definitions/noaa-marine-get-water-level.tool.js';
|
|
@@ -23,6 +24,7 @@ await createApp({
|
|
|
23
24
|
noaaMarineFindStations,
|
|
24
25
|
noaaMarineGetTidePredictions,
|
|
25
26
|
noaaMarineGetWaterLevel,
|
|
27
|
+
noaaMarineGetMonthlyMeans,
|
|
26
28
|
noaaMarineGetCurrents,
|
|
27
29
|
noaaMarineGetConditions,
|
|
28
30
|
noaaMarineGetCurrentProfile,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oEAAoE,CAAC;AAC/G,OAAO,EAAE,sBAAsB,EAAE,MAAM,kEAAkE,CAAC;AAC1G,OAAO,EAAE,uBAAuB,EAAE,MAAM,mEAAmE,CAAC;AAC5G,OAAO,EAAE,2BAA2B,EAAE,MAAM,wEAAwE,CAAC;AACrH,OAAO,EAAE,qBAAqB,EAAE,MAAM,iEAAiE,CAAC;AACxG,OAAO,EAAE,8BAA8B,EAAE,MAAM,2EAA2E,CAAC;AAC3H,OAAO,EAAE,4BAA4B,EAAE,MAAM,yEAAyE,CAAC;AACvH,OAAO,EAAE,uBAAuB,EAAE,MAAM,oEAAoE,CAAC;AAC7G,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAElE,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,wBAAwB;IAC9B,KAAK,EAAE,wBAAwB;IAC/B,WAAW,EAAE,WAAW;IACxB,KAAK,EAAE;QACL,sBAAsB;QACtB,4BAA4B;QAC5B,uBAAuB;QACvB,qBAAqB;QACrB,uBAAuB;QACvB,2BAA2B;QAC3B,8BAA8B;KAC/B;IACD,SAAS,EAAE,CAAC,yBAAyB,CAAC;IACtC,OAAO,EAAE,EAAE;IACX,UAAU,EAAE;QACV,YAAY,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;QACzD,gBAAgB,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC7D,0BAA0B,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;KACxE;IACD,YAAY,EAAE,itBAAitB;IAE/tB,KAAK,CAAC,IAAI;QACR,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;QACvC,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QAC1D,eAAe,EAAE,CAAC;IACpB,CAAC;CACF,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oEAAoE,CAAC;AAC/G,OAAO,EAAE,sBAAsB,EAAE,MAAM,kEAAkE,CAAC;AAC1G,OAAO,EAAE,uBAAuB,EAAE,MAAM,mEAAmE,CAAC;AAC5G,OAAO,EAAE,2BAA2B,EAAE,MAAM,wEAAwE,CAAC;AACrH,OAAO,EAAE,qBAAqB,EAAE,MAAM,iEAAiE,CAAC;AACxG,OAAO,EAAE,yBAAyB,EAAE,MAAM,sEAAsE,CAAC;AACjH,OAAO,EAAE,8BAA8B,EAAE,MAAM,2EAA2E,CAAC;AAC3H,OAAO,EAAE,4BAA4B,EAAE,MAAM,yEAAyE,CAAC;AACvH,OAAO,EAAE,uBAAuB,EAAE,MAAM,oEAAoE,CAAC;AAC7G,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAElE,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,wBAAwB;IAC9B,KAAK,EAAE,wBAAwB;IAC/B,WAAW,EAAE,WAAW;IACxB,KAAK,EAAE;QACL,sBAAsB;QACtB,4BAA4B;QAC5B,uBAAuB;QACvB,yBAAyB;QACzB,qBAAqB;QACrB,uBAAuB;QACvB,2BAA2B;QAC3B,8BAA8B;KAC/B;IACD,SAAS,EAAE,CAAC,yBAAyB,CAAC;IACtC,OAAO,EAAE,EAAE;IACX,UAAU,EAAE;QACV,YAAY,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;QACzD,gBAAgB,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC7D,0BAA0B,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;KACxE;IACD,YAAY,EAAE,itBAAitB;IAE/tB,KAAK,CAAC,IAAI;QACR,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;QACvC,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QAC1D,eAAe,EAAE,CAAC;IACpB,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"noaa-marine-station.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/noaa-marine-station.resource.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAKjE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"noaa-marine-station.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/noaa-marine-station.resource.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAKjE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;GA0KpC,CAAC"}
|
|
@@ -9,7 +9,7 @@ import { currentPredictionClass, tidePredictionClass } from '../../../services/c
|
|
|
9
9
|
import { getNdbcService } from '../../../services/ndbc/ndbc-service.js';
|
|
10
10
|
export const noaaMarineStationResource = resource('noaa-marine://station/{station_id}', {
|
|
11
11
|
name: 'noaa_marine_station',
|
|
12
|
-
description: `Metadata for a CO-OPS or NDBC station by ID: name, coordinates, source, data capabilities, the CO-OPS prediction class, and — for NDBC — the physical platform class. The type field is the primary data capability, meaning the same thing here as in noaa_marine_find_stations, and is omitted when the station reports no data capability; platform is the NDBC platform class (buoy, fixed, oilrig, dart, tao, usv, other), a separate axis that CO-OPS stations do not carry. prediction_class is a third axis, again identical to the field of that name on noaa_marine_find_stations: a tide station is reference (serving both hilo and the 6-minute curve) or subordinate (hilo only, with reference_id naming the station its offsets come from), while a current station carries its class per depth bin in bins[], whose bin numbers are what noaa_marine_get_currents takes as bin. CO-OPS station IDs are numeric for tide and water-level stations and alphanumeric for current stations, while NDBC station IDs are 5-character alphanumeric codes — use noaa_marine_find_stations to discover them. A station ID that neither catalog carries is a station_not_found error, distinct from source_unavailable, which says a catalog could not be read and so was never searched.`,
|
|
12
|
+
description: `Metadata for a CO-OPS or NDBC station by ID: name, coordinates, source, data capabilities, the CO-OPS prediction class, and — for NDBC — the physical platform class. The type field is the primary data capability, meaning the same thing here as in noaa_marine_find_stations, and is omitted when the station reports no data capability; platform is the NDBC platform class (buoy, fixed, oilrig, dart, tao, usv, other), a separate axis that CO-OPS stations do not carry. prediction_class is a third axis, again identical to the field of that name on noaa_marine_find_stations: a tide station is reference (serving both hilo and the 6-minute curve) or subordinate (hilo only, with reference_id naming the station its offsets come from), while a current station carries its class per depth bin in bins[], whose bin numbers are what noaa_marine_get_currents takes as bin. state is a CO-OPS station's US state or territory code, resolved as on noaa_marine_find_stations: its own catalog code, or — when its catalog rows carry none, as with every current station — the state of the nearest state-bearing tide or water-level station within 25 km, marked state_derived: true and possibly wrong on waters shared across a state or national border; failing both, the station's own non-code catalog value (e.g. FM) as published, and otherwise omitted. CO-OPS station IDs are numeric for tide and water-level stations and alphanumeric for current stations, while NDBC station IDs are 5-character alphanumeric codes — use noaa_marine_find_stations to discover them. A station ID that neither catalog carries is a station_not_found error, distinct from source_unavailable, which says a catalog could not be read and so was never searched.`,
|
|
13
13
|
mimeType: 'application/json',
|
|
14
14
|
cacheHint: { ttlMs: 21_600_000, cacheScope: 'public' },
|
|
15
15
|
params: z.object({
|
|
@@ -78,8 +78,13 @@ export const noaaMarineStationResource = resource('noaa-marine://station/{statio
|
|
|
78
78
|
// whose bins can mix classes. CO-OPS has no platform class.
|
|
79
79
|
if (caps[0])
|
|
80
80
|
result.type = caps[0];
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
// The same resolved state noaa_marine_find_stations reports and filters on.
|
|
82
|
+
const resolved = coopsSvc.stationStates({ tide, current, waterLevel }).get(match.id);
|
|
83
|
+
if (resolved) {
|
|
84
|
+
result.state = resolved.state;
|
|
85
|
+
if (resolved.derived)
|
|
86
|
+
result.state_derived = true;
|
|
87
|
+
}
|
|
83
88
|
const predictionClass = tidePredictionClass(tideRow?.type);
|
|
84
89
|
if (predictionClass)
|
|
85
90
|
result.prediction_class = predictionClass;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"noaa-marine-station.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/noaa-marine-station.resource.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjE,MAAM,CAAC,MAAM,yBAAyB,GAAG,QAAQ,CAAC,oCAAoC,EAAE;IACtF,IAAI,EAAE,qBAAqB;IAC3B,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"noaa-marine-station.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/noaa-marine-station.resource.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjE,MAAM,CAAC,MAAM,yBAAyB,GAAG,QAAQ,CAAC,oCAAoC,EAAE;IACtF,IAAI,EAAE,qBAAqB;IAC3B,WAAW,EAAE,0rDAA0rD;IACvsD,QAAQ,EAAE,kBAAkB;IAC5B,SAAS,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;IACtD,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,UAAU,EAAE,CAAC;aACV,MAAM,EAAE;aACR,KAAK,CAAC,uBAAuB,CAAC;aAC9B,QAAQ,CACP,sGAAsG,CACvG;KACJ,CAAC;IAEF,MAAM,EAAE;QACN;YACE,MAAM,EAAE,mBAAmB;YAC3B,IAAI,EAAE,gBAAgB,CAAC,QAAQ;YAC/B,IAAI,EAAE,8DAA8D;YACpE,QAAQ,EACN,wGAAwG;SAC3G;QACD;YACE,MAAM,EAAE,oBAAoB;YAC5B,IAAI,EAAE,gBAAgB,CAAC,kBAAkB;YACzC,IAAI,EAAE,oFAAoF;YAC1F,QAAQ,EACN,+FAA+F;SAClG;KACF;IAED,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG;QACvB,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;QACjC,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;QAE3C,2CAA2C;QAC3C,MAAM,CAAC,WAAW,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;YACzD,OAAO,CAAC,GAAG,CAAC;gBACV,QAAQ,CAAC,WAAW,CAAC,iBAAiB,EAAE,GAAG,CAAC;gBAC5C,QAAQ,CAAC,WAAW,CAAC,oBAAoB,EAAE,GAAG,CAAC;gBAC/C,QAAQ,CAAC,WAAW,CAAC,aAAa,EAAE,GAAG,CAAC;aACzC,CAAC;YACF,OAAO,CAAC,iBAAiB,CAAC,GAAG,CAAC;SAC/B,CAAC,CAAC;QAEH,eAAe;QACf,IAAI,WAAW,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YACvC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC;YACtD,mFAAmF;YACnF,sFAAsF;YACtF,+EAA+E;YAC/E,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5D,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;YACrE,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;YACxE,MAAM,KAAK,GAAG,OAAO,IAAI,WAAW,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC;YAEzD,IAAI,KAAK,EAAE,CAAC;gBACV,0FAA0F;gBAC1F,MAAM,IAAI,GAAa,EAAE,CAAC;gBAC1B,IAAI,OAAO;oBAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC/B,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC;oBAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACjD,IAAI,aAAa;oBAAE,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;gBAE5C,MAAM,MAAM,GAA4B;oBACtC,UAAU,EAAE,KAAK,CAAC,EAAE;oBACpB,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,MAAM,EAAE,OAAO;oBACf,QAAQ,EAAE,KAAK,CAAC,GAAG;oBACnB,SAAS,EAAE,KAAK,CAAC,GAAG;oBACpB,YAAY,EAAE,IAAI;iBACnB,CAAC;gBACF,0FAA0F;gBAC1F,0FAA0F;gBAC1F,yFAAyF;gBACzF,4DAA4D;gBAC5D,IAAI,IAAI,CAAC,CAAC,CAAC;oBAAE,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;gBACnC,4EAA4E;gBAC5E,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACrF,IAAI,QAAQ,EAAE,CAAC;oBACb,MAAM,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;oBAC9B,IAAI,QAAQ,CAAC,OAAO;wBAAE,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC;gBACpD,CAAC;gBACD,MAAM,eAAe,GAAG,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;gBAC3D,IAAI,eAAe;oBAAE,MAAM,CAAC,gBAAgB,GAAG,eAAe,CAAC;gBAC/D,uEAAuE;gBACvE,IAAI,OAAO,EAAE,YAAY;oBAAE,MAAM,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;gBACtE,MAAM,IAAI,GAAG,WAAW;qBACrB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC;qBACtC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;oBACT,MAAM,GAAG,GAA4B,EAAE,GAAG,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;oBAChF,IAAI,CAAC,CAAC,SAAS;wBAAE,GAAG,CAAC,UAAU,GAAG,CAAC,CAAC,SAAS,CAAC;oBAC9C,MAAM,QAAQ,GAAG,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;oBAChD,IAAI,QAAQ;wBAAE,GAAG,CAAC,gBAAgB,GAAG,QAAQ,CAAC;oBAC9C,OAAO,GAAG,CAAC;gBACb,CAAC,CAAC,CAAC;gBACL,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;oBAAE,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;gBACxC,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;QAED,aAAa;QACb,IAAI,UAAU,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YACtC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;YACtE,IAAI,KAAK,EAAE,CAAC;gBACV,+FAA+F;gBAC/F,yFAAyF;gBACzF,0FAA0F;gBAC1F,yDAAyD;gBACzD,MAAM,IAAI,GAAa,EAAE,CAAC;gBAC1B,IAAI,KAAK,CAAC,MAAM;oBAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACnC,IAAI,KAAK,CAAC,WAAW;oBAAE,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;gBACpD,IAAI,KAAK,CAAC,eAAe;oBAAE,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;gBACtD,MAAM,MAAM,GAA4B;oBACtC,UAAU,EAAE,KAAK,CAAC,EAAE;oBACpB,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,MAAM,EAAE,MAAM;oBACd,QAAQ,EAAE,KAAK,CAAC,GAAG;oBACnB,SAAS,EAAE,KAAK,CAAC,GAAG;oBACpB,YAAY,EAAE,IAAI;iBACnB,CAAC;gBACF,uFAAuF;gBACvF,yFAAyF;gBACzF,IAAI,IAAI,CAAC,CAAC,CAAC;oBAAE,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;gBACnC,IAAI,KAAK,CAAC,IAAI;oBAAE,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC;gBAC7C,IAAI,KAAK,CAAC,KAAK;oBAAE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;gBAC5C,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;QAED,4FAA4F;QAC5F,6FAA6F;QAC7F,8EAA8E;QAC9E,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,CAAC,WAAW,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtE,CAAC,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAEpE,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,QAAQ,GACZ,QAAQ,CAAC,MAAM,GAAG,CAAC;gBACjB,CAAC,CAAC,oBAAoB,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU;gBACrD,CAAC,CAAC,8BAA8B,CAAC;YACrC,uFAAuF;YACvF,wFAAwF;YACxF,MAAM,cAAc,GAClB,WAAW,CAAC,MAAM,KAAK,UAAU,IAAI,gBAAgB,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAC5E,MAAM,GAAG,CAAC,IAAI,CACZ,oBAAoB,EACpB,WAAW,MAAM,CAAC,UAAU,+BAA+B,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,2BAA2B,QAAQ,GAAG,EACnK;gBACE,GAAG,CAAC,cAAc;oBAChB,CAAC,CAAC;wBACE,QAAQ,EAAE;4BACR,IAAI,EAAE,+MAA+M;yBACtN;qBACF;oBACH,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,oBAAoB,CAAC,CAAC;gBAC1C,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,cAAc,EAAE,MAAM;aACvB,CACF,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,CAAC,IAAI,CACZ,mBAAmB,EACnB,WAAW,MAAM,CAAC,UAAU,oFAAoF,EAChH,EAAE,GAAG,GAAG,CAAC,WAAW,CAAC,mBAAmB,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAC3E,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -96,6 +96,7 @@ export declare const noaaMarineFindStations: import("@cyanheads/mcp-ts-core").To
|
|
|
96
96
|
longitude: z.ZodNumber;
|
|
97
97
|
distance_km: z.ZodOptional<z.ZodNumber>;
|
|
98
98
|
state: z.ZodOptional<z.ZodString>;
|
|
99
|
+
state_derived: z.ZodOptional<z.ZodBoolean>;
|
|
99
100
|
capabilities: z.ZodArray<z.ZodString>;
|
|
100
101
|
prediction_class: z.ZodOptional<z.ZodString>;
|
|
101
102
|
reference_id: z.ZodOptional<z.ZodString>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"noaa-marine-find-stations.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/noaa-marine-find-stations.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"noaa-marine-find-stations.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/noaa-marine-find-stations.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAiLjE,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAikBjC,CAAC"}
|
|
@@ -6,74 +6,9 @@ import { tool, z } from '@cyanheads/mcp-ts-core';
|
|
|
6
6
|
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
7
7
|
import { getCoopsService, isCoopsThrottled } from '../../../services/coops/coops-service.js';
|
|
8
8
|
import { currentPredictionClass, tidePredictionClass } from '../../../services/coops/prediction-class.js';
|
|
9
|
+
import { STATE_CODES } from '../../../services/coops/station-state.js';
|
|
10
|
+
import { haversineKm } from '../../../services/geo.js';
|
|
9
11
|
import { getNdbcService } from '../../../services/ndbc/ndbc-service.js';
|
|
10
|
-
/** Haversine distance in km between two lat/lon pairs. */
|
|
11
|
-
function haversineKm(lat1, lon1, lat2, lon2) {
|
|
12
|
-
const R = 6371;
|
|
13
|
-
const dLat = ((lat2 - lat1) * Math.PI) / 180;
|
|
14
|
-
const dLon = ((lon2 - lon1) * Math.PI) / 180;
|
|
15
|
-
const a = Math.sin(dLat / 2) ** 2 +
|
|
16
|
-
Math.cos((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) * Math.sin(dLon / 2) ** 2;
|
|
17
|
-
return R * 2 * Math.asin(Math.sqrt(a));
|
|
18
|
-
}
|
|
19
|
-
const STATE_CODES = [
|
|
20
|
-
'AL',
|
|
21
|
-
'AK',
|
|
22
|
-
'AZ',
|
|
23
|
-
'AR',
|
|
24
|
-
'CA',
|
|
25
|
-
'CO',
|
|
26
|
-
'CT',
|
|
27
|
-
'DE',
|
|
28
|
-
'FL',
|
|
29
|
-
'GA',
|
|
30
|
-
'HI',
|
|
31
|
-
'ID',
|
|
32
|
-
'IL',
|
|
33
|
-
'IN',
|
|
34
|
-
'IA',
|
|
35
|
-
'KS',
|
|
36
|
-
'KY',
|
|
37
|
-
'LA',
|
|
38
|
-
'ME',
|
|
39
|
-
'MD',
|
|
40
|
-
'MA',
|
|
41
|
-
'MI',
|
|
42
|
-
'MN',
|
|
43
|
-
'MS',
|
|
44
|
-
'MO',
|
|
45
|
-
'MT',
|
|
46
|
-
'NE',
|
|
47
|
-
'NV',
|
|
48
|
-
'NH',
|
|
49
|
-
'NJ',
|
|
50
|
-
'NM',
|
|
51
|
-
'NY',
|
|
52
|
-
'NC',
|
|
53
|
-
'ND',
|
|
54
|
-
'OH',
|
|
55
|
-
'OK',
|
|
56
|
-
'OR',
|
|
57
|
-
'PA',
|
|
58
|
-
'RI',
|
|
59
|
-
'SC',
|
|
60
|
-
'SD',
|
|
61
|
-
'TN',
|
|
62
|
-
'TX',
|
|
63
|
-
'UT',
|
|
64
|
-
'VT',
|
|
65
|
-
'VA',
|
|
66
|
-
'WA',
|
|
67
|
-
'WV',
|
|
68
|
-
'WI',
|
|
69
|
-
'WY',
|
|
70
|
-
'DC',
|
|
71
|
-
'PR',
|
|
72
|
-
'VI',
|
|
73
|
-
'GU',
|
|
74
|
-
'AS',
|
|
75
|
-
'MP',
|
|
76
|
-
];
|
|
77
12
|
/** Filter values only a CO-OPS row can carry. */
|
|
78
13
|
const COOPS_FILTER_VALUES = ['tide', 'current', 'water_level'];
|
|
79
14
|
/** Filter values only an NDBC row can carry — three data capabilities plus the buoy platform token. */
|
|
@@ -129,7 +64,7 @@ const AppliedSearchSchema = z.object({
|
|
|
129
64
|
state: z
|
|
130
65
|
.string()
|
|
131
66
|
.optional()
|
|
132
|
-
.describe(
|
|
67
|
+
.describe("The state filter applied, matched against each station's resolved state: its own catalog code, or for a station publishing none the state of the nearest state-bearing CO-OPS tide or water-level station within 25 km. Restricts results to CO-OPS and excludes NDBC."),
|
|
133
68
|
types: z
|
|
134
69
|
.array(z.string().describe('A requested capability or platform filter value.'))
|
|
135
70
|
.optional()
|
|
@@ -214,15 +149,13 @@ export const noaaMarineFindStations = tool('noaa_marine_find_stations', {
|
|
|
214
149
|
.min(-90)
|
|
215
150
|
.max(90)
|
|
216
151
|
.optional()
|
|
217
|
-
.describe('Center latitude in decimal degrees for proximity search. Required together with longitude — '
|
|
218
|
-
'supplying only one is rejected rather than silently ignored. Optionally pair with radius_km.'),
|
|
152
|
+
.describe('Center latitude in decimal degrees for proximity search. Required together with longitude — supplying only one is rejected rather than silently ignored. Optionally pair with radius_km.'),
|
|
219
153
|
longitude: z
|
|
220
154
|
.number()
|
|
221
155
|
.min(-180)
|
|
222
156
|
.max(180)
|
|
223
157
|
.optional()
|
|
224
|
-
.describe('Center longitude in decimal degrees for proximity search. Required together with latitude — '
|
|
225
|
-
'supplying only one is rejected rather than silently ignored. Optionally pair with radius_km.'),
|
|
158
|
+
.describe('Center longitude in decimal degrees for proximity search. Required together with latitude — supplying only one is rejected rather than silently ignored. Optionally pair with radius_km.'),
|
|
226
159
|
radius_km: z
|
|
227
160
|
.number()
|
|
228
161
|
.min(1)
|
|
@@ -232,16 +165,11 @@ export const noaaMarineFindStations = tool('noaa_marine_find_stations', {
|
|
|
232
165
|
query: z
|
|
233
166
|
.string()
|
|
234
167
|
.optional()
|
|
235
|
-
.describe('Station name or station ID substring to match, case-insensitive, on both sources. '
|
|
236
|
-
'E.g. "seattle", "puget sound", "9447130", "46041". A station whose ID matches exactly is ' +
|
|
237
|
-
'returned ahead of name matches, unless latitude/longitude were supplied — a proximity ' +
|
|
238
|
-
'search orders by distance instead. Blank or whitespace-only values are treated as omitted.'),
|
|
168
|
+
.describe('Station name or station ID substring to match, case-insensitive, on both sources. E.g. "seattle", "puget sound", "9447130", "46041". A station whose ID matches exactly is returned ahead of name matches, unless latitude/longitude were supplied — a proximity search orders by distance instead. Blank or whitespace-only values are treated as omitted.'),
|
|
239
169
|
state: z
|
|
240
170
|
.enum(STATE_CODES)
|
|
241
171
|
.optional()
|
|
242
|
-
.describe('Filter by 2-letter US state or territory code. Applies to CO-OPS stations only — '
|
|
243
|
-
'providing it restricts results to CO-OPS and excludes NDBC buoys (which carry no state). ' +
|
|
244
|
-
'E.g. "WA", "CA", "PR".'),
|
|
172
|
+
.describe('Filter by 2-letter US state or territory code. Applies to CO-OPS stations only — providing it restricts results to CO-OPS and excludes NDBC buoys (which carry no state). A station matches on its own catalog code, or — when its catalog rows carry none, as with every current station — on the state of the nearest state-bearing CO-OPS tide or water-level station within 25 km, which can be wrong on waters shared across a state or national border. E.g. "WA", "CA", "PR".'),
|
|
245
173
|
source: z
|
|
246
174
|
.enum(['coops', 'ndbc', 'all'])
|
|
247
175
|
.default('all')
|
|
@@ -257,20 +185,9 @@ export const noaaMarineFindStations = tool('noaa_marine_find_stations', {
|
|
|
257
185
|
'water_quality',
|
|
258
186
|
'buoy',
|
|
259
187
|
])
|
|
260
|
-
.describe("Filter value. Six are data capabilities, matched against a station's capabilities list: "
|
|
261
|
-
'tide (CO-OPS tide predictions → noaa_marine_get_tide_predictions), ' +
|
|
262
|
-
'current (CO-OPS tidal-current predictions → noaa_marine_get_currents), ' +
|
|
263
|
-
'water_level (CO-OPS observed water levels → noaa_marine_get_water_level), ' +
|
|
264
|
-
'met (NDBC meteorological → noaa_marine_get_conditions), ' +
|
|
265
|
-
'current_profile (NDBC observed ocean-current depth profile → noaa_marine_get_current_profile; ' +
|
|
266
|
-
'note this is a different data product and source than CO-OPS `current`), ' +
|
|
267
|
-
'water_quality (NDBC sub-surface water-column sensors → noaa_marine_get_ocean_observations). ' +
|
|
268
|
-
'The seventh, buoy, is a physical-platform filter (NDBC platform class equals buoy), not a data ' +
|
|
269
|
-
'capability — use it to select buoy-class platforms regardless of what data they serve.'))
|
|
188
|
+
.describe("Filter value. Six are data capabilities, matched against a station's capabilities list: tide (CO-OPS tide predictions → noaa_marine_get_tide_predictions), current (CO-OPS tidal-current predictions → noaa_marine_get_currents), water_level (CO-OPS observed water levels → noaa_marine_get_water_level, and their verified monthly means → noaa_marine_get_monthly_means), met (NDBC meteorological → noaa_marine_get_conditions), current_profile (NDBC observed ocean-current depth profile → noaa_marine_get_current_profile; note this is a different data product and source than CO-OPS `current`), water_quality (NDBC sub-surface water-column sensors → noaa_marine_get_ocean_observations). The seventh, buoy, is a physical-platform filter (NDBC platform class equals buoy), not a data capability — use it to select buoy-class platforms regardless of what data they serve."))
|
|
270
189
|
.optional()
|
|
271
|
-
.describe('Filter by data capability or NDBC platform class. Every returned station matches at least one requested '
|
|
272
|
-
'value — a capability value against its capabilities list, or buoy against its platform class. ' +
|
|
273
|
-
'Omit to return all stations.'),
|
|
190
|
+
.describe('Filter by data capability or NDBC platform class. Every returned station matches at least one requested value — a capability value against its capabilities list, or buoy against its platform class. Omit to return all stations.'),
|
|
274
191
|
limit: z
|
|
275
192
|
.number()
|
|
276
193
|
.int()
|
|
@@ -291,17 +208,11 @@ export const noaaMarineFindStations = tool('noaa_marine_find_stations', {
|
|
|
291
208
|
type: z
|
|
292
209
|
.string()
|
|
293
210
|
.optional()
|
|
294
|
-
.describe('The data capability this row leads with — always one of the values in capabilities. When a types '
|
|
295
|
-
'filter of capability values is set this is the first requested capability the station has, so it ' +
|
|
296
|
-
'never contradicts the filter; otherwise it is the first capability. Omitted when the station has ' +
|
|
297
|
-
'no data capability (e.g. a bare buoy/fixed platform matched only by a platform filter) — read ' +
|
|
298
|
-
'platform for its identity. This is a data-product axis, never the physical platform class.'),
|
|
211
|
+
.describe('The data capability this row leads with — always one of the values in capabilities. When a types filter of capability values is set this is the first requested capability the station has, so it never contradicts the filter; otherwise it is the first capability. Omitted when the station has no data capability (e.g. a bare buoy/fixed platform matched only by a platform filter) — read platform for its identity. This is a data-product axis, never the physical platform class.'),
|
|
299
212
|
platform: z
|
|
300
213
|
.string()
|
|
301
214
|
.optional()
|
|
302
|
-
.describe('NDBC physical platform class: buoy, fixed, oilrig, dart, tao, usv, or other. A different axis than '
|
|
303
|
-
'type/capabilities (which describe data products). Omitted for CO-OPS stations — CO-OPS publishes ' +
|
|
304
|
-
'no platform taxonomy.'),
|
|
215
|
+
.describe('NDBC physical platform class: buoy, fixed, oilrig, dart, tao, usv, or other. A different axis than type/capabilities (which describe data products). Omitted for CO-OPS stations — CO-OPS publishes no platform taxonomy.'),
|
|
305
216
|
latitude: z.number().describe('Station latitude in decimal degrees.'),
|
|
306
217
|
longitude: z.number().describe('Station longitude in decimal degrees.'),
|
|
307
218
|
distance_km: z
|
|
@@ -311,12 +222,14 @@ export const noaaMarineFindStations = tool('noaa_marine_find_stations', {
|
|
|
311
222
|
state: z
|
|
312
223
|
.string()
|
|
313
224
|
.optional()
|
|
314
|
-
.describe(
|
|
225
|
+
.describe("US state or territory code (CO-OPS stations only): the station's own catalog code, or — when its catalog rows carry none, as with every current station — the state of the nearest state-bearing CO-OPS tide or water-level station within 25 km, marked by state_derived. A derived state can be wrong on waters shared across a state or national border. When neither applies, the station's own non-code catalog value (e.g. FM) is shown as published and unmarked — no state filter returns such a station, since the filter takes codes only. Omitted when the station publishes nothing at all and no state-bearing station is that close."),
|
|
226
|
+
state_derived: z
|
|
227
|
+
.boolean()
|
|
228
|
+
.optional()
|
|
229
|
+
.describe("True when state was derived from the nearest state-bearing CO-OPS tide or water-level station within 25 km, because the station's own catalog rows carry no state code. Omitted when state is the station's own."),
|
|
315
230
|
capabilities: z
|
|
316
231
|
.array(z.string().describe('Capability identifier, e.g. "tide", "water_level".'))
|
|
317
|
-
.describe('Data products available at this station: any of tide, current, water_level (CO-OPS) or met, '
|
|
318
|
-
'current_profile, water_quality (NDBC). Empty when the station reports no data capability — ' +
|
|
319
|
-
'platform still identifies it.'),
|
|
232
|
+
.describe('Data products available at this station: any of tide, current, water_level (CO-OPS) or met, current_profile, water_quality (NDBC). Empty when the station reports no data capability — platform still identifies it.'),
|
|
320
233
|
prediction_class: z
|
|
321
234
|
.string()
|
|
322
235
|
.optional()
|
|
@@ -476,6 +389,11 @@ export const noaaMarineFindStations = tool('noaa_marine_find_stations', {
|
|
|
476
389
|
// Process CO-OPS stations
|
|
477
390
|
if (coopsResults.status === 'fulfilled' && coopsResults.value) {
|
|
478
391
|
const [tideStations, currentStations, waterLevelStations] = coopsResults.value;
|
|
392
|
+
const states = coopsSvc.stationStates({
|
|
393
|
+
tide: tideStations,
|
|
394
|
+
current: currentStations,
|
|
395
|
+
waterLevel: waterLevelStations,
|
|
396
|
+
});
|
|
479
397
|
// Build unique station map — a station can appear in multiple lists, and each list
|
|
480
398
|
// it appears in IS one of its capabilities, so membership needs no separate index.
|
|
481
399
|
const allCoops = new Map();
|
|
@@ -515,7 +433,8 @@ export const noaaMarineFindStations = tool('noaa_marine_find_stations', {
|
|
|
515
433
|
for (const s of allCoops.values()) {
|
|
516
434
|
if (!matchesTypeFilter(s.capabilities))
|
|
517
435
|
continue;
|
|
518
|
-
|
|
436
|
+
const resolved = states.get(s.id);
|
|
437
|
+
if (input.state && resolved?.state !== input.state)
|
|
519
438
|
continue;
|
|
520
439
|
if (!matchesQuery(s.name, s.id))
|
|
521
440
|
continue;
|
|
@@ -530,8 +449,11 @@ export const noaaMarineFindStations = tool('noaa_marine_find_stations', {
|
|
|
530
449
|
const coopsType = primaryTypeFor(s.capabilities);
|
|
531
450
|
if (coopsType)
|
|
532
451
|
entry.type = coopsType;
|
|
533
|
-
if (
|
|
534
|
-
entry.state =
|
|
452
|
+
if (resolved) {
|
|
453
|
+
entry.state = resolved.state;
|
|
454
|
+
if (resolved.derived)
|
|
455
|
+
entry.state_derived = true;
|
|
456
|
+
}
|
|
535
457
|
const tideRow = tideRowById.get(s.id);
|
|
536
458
|
const predictionClass = tidePredictionClass(tideRow?.type);
|
|
537
459
|
if (predictionClass)
|
|
@@ -616,7 +538,7 @@ export const noaaMarineFindStations = tool('noaa_marine_find_stations', {
|
|
|
616
538
|
const stations = results.slice(0, input.limit);
|
|
617
539
|
const truncated = stations.length < total_found;
|
|
618
540
|
// One notice reaches the caller, so every source of guidance composes into it —
|
|
619
|
-
// ctx.enrich.
|
|
541
|
+
// ctx.enrich.notice() writes `notice` last-wins and would otherwise erase the rest.
|
|
620
542
|
const notices = [];
|
|
621
543
|
if (failed.length > 0) {
|
|
622
544
|
const report = { answered, attempted, failed };
|
|
@@ -644,20 +566,13 @@ export const noaaMarineFindStations = tool('noaa_marine_find_stations', {
|
|
|
644
566
|
ctx.enrich({ applied_search: applied });
|
|
645
567
|
notices.push(describeEmptySearch(applied));
|
|
646
568
|
}
|
|
569
|
+
// The cap itself is on output as `truncated`, beside `stations.length` and `total_found`;
|
|
570
|
+
// the notice carries the guidance for reaching the rest.
|
|
647
571
|
if (truncated) {
|
|
648
|
-
|
|
649
|
-
shown: stations.length,
|
|
650
|
-
cap: input.limit,
|
|
651
|
-
ceiling: total_found,
|
|
652
|
-
guidance: [
|
|
653
|
-
`Showing ${stations.length} of ${total_found} matches — raise limit (max 200) or narrow the filters to reach the rest.`,
|
|
654
|
-
...notices,
|
|
655
|
-
].join(' '),
|
|
656
|
-
});
|
|
572
|
+
notices.unshift(`Showing ${stations.length} of ${total_found} matches — raise limit (max 200) or narrow the filters to reach the rest.`);
|
|
657
573
|
}
|
|
658
|
-
|
|
574
|
+
if (notices.length > 0)
|
|
659
575
|
ctx.enrich.notice(notices.join(' '));
|
|
660
|
-
}
|
|
661
576
|
ctx.log.info('Station search complete', {
|
|
662
577
|
total_found,
|
|
663
578
|
returned: stations.length,
|
|
@@ -677,7 +592,9 @@ export const noaaMarineFindStations = tool('noaa_marine_find_stations', {
|
|
|
677
592
|
const lines = [header];
|
|
678
593
|
for (const s of result.stations) {
|
|
679
594
|
const dist = s.distance_km !== undefined ? ` · ${s.distance_km} km` : '';
|
|
680
|
-
const state = s.state
|
|
595
|
+
const state = s.state
|
|
596
|
+
? ` · ${s.state}${s.state_derived ? ' (derived from the nearest state-bearing station)' : ''}`
|
|
597
|
+
: '';
|
|
681
598
|
const typeStr = s.type ? ` · **Type:** ${s.type}` : '';
|
|
682
599
|
const platformStr = s.platform ? ` · **Platform:** ${s.platform}` : '';
|
|
683
600
|
const classStr = s.prediction_class ? ` · **Prediction class:** ${s.prediction_class}` : '';
|