@ryancardin/noaa-tides-currents-mcp-server 1.0.0 → 2.0.1

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.
Files changed (88) hide show
  1. package/README.md +164 -199
  2. package/dist/client/cache.d.ts +15 -0
  3. package/dist/client/cache.js +44 -0
  4. package/dist/client/http.d.ts +28 -0
  5. package/dist/client/http.js +178 -0
  6. package/dist/constants.d.ts +28 -0
  7. package/dist/constants.js +28 -0
  8. package/dist/format/respond.d.ts +25 -0
  9. package/dist/format/respond.js +40 -0
  10. package/dist/format/series.d.ts +16 -0
  11. package/dist/format/series.js +42 -0
  12. package/dist/format/units.d.ts +16 -0
  13. package/dist/format/units.js +37 -0
  14. package/dist/index.d.ts +14 -0
  15. package/dist/index.js +78 -8
  16. package/dist/interfaces/moon.d.ts +4 -4
  17. package/dist/interfaces/moon.js +58 -17
  18. package/dist/interfaces/sun.d.ts +4 -4
  19. package/dist/interfaces/sun.js +94 -25
  20. package/dist/prompts/index.d.ts +6 -0
  21. package/dist/prompts/index.js +89 -0
  22. package/dist/reference/content.d.ts +10 -0
  23. package/dist/reference/content.js +202 -0
  24. package/dist/resources/index.d.ts +7 -0
  25. package/dist/resources/index.js +73 -0
  26. package/dist/schemas/common.d.ts +38 -14
  27. package/dist/schemas/common.js +82 -18
  28. package/dist/services/data-api.d.ts +81 -0
  29. package/dist/services/data-api.js +117 -0
  30. package/dist/services/dpapi.d.ts +55 -0
  31. package/dist/services/dpapi.js +60 -0
  32. package/dist/services/metadata-api.d.ts +62 -0
  33. package/dist/services/metadata-api.js +105 -0
  34. package/dist/services/moon-phase-service.d.ts +2 -2
  35. package/dist/services/moon-phase-service.js +23 -25
  36. package/dist/services/sun-service.d.ts +2 -2
  37. package/dist/services/sun-service.js +53 -31
  38. package/dist/tools/astronomy.d.ts +7 -0
  39. package/dist/tools/astronomy.js +270 -0
  40. package/dist/tools/currents.d.ts +5 -0
  41. package/dist/tools/currents.js +168 -0
  42. package/dist/tools/derived.d.ts +6 -0
  43. package/dist/tools/derived.js +296 -0
  44. package/dist/tools/index.d.ts +3 -14
  45. package/dist/tools/index.js +17 -32
  46. package/dist/tools/met.d.ts +5 -0
  47. package/dist/tools/met.js +110 -0
  48. package/dist/tools/reference.d.ts +6 -0
  49. package/dist/tools/reference.js +33 -0
  50. package/dist/tools/station-metadata.d.ts +6 -0
  51. package/dist/tools/station-metadata.js +208 -0
  52. package/dist/tools/stations.d.ts +5 -0
  53. package/dist/tools/stations.js +265 -0
  54. package/dist/tools/water.d.ts +5 -0
  55. package/dist/tools/water.js +270 -0
  56. package/dist/validation/dates.d.ts +50 -0
  57. package/dist/validation/dates.js +139 -0
  58. package/package.json +27 -14
  59. package/.claude/settings.local.json +0 -29
  60. package/CLAUDE.md +0 -71
  61. package/Dockerfile +0 -14
  62. package/smithery.yaml +0 -16
  63. package/src/index.ts +0 -13
  64. package/src/interfaces/moon.ts +0 -44
  65. package/src/interfaces/noaa.ts +0 -130
  66. package/src/interfaces/parameters.ts +0 -20
  67. package/src/interfaces/sun.ts +0 -57
  68. package/src/schemas/common.ts +0 -23
  69. package/src/schemas/dpapi.ts +0 -99
  70. package/src/server/config.ts +0 -43
  71. package/src/server/mcp-server.ts +0 -135
  72. package/src/services/dpapi-service.ts +0 -187
  73. package/src/services/moon-phase-service.ts +0 -167
  74. package/src/services/noaa-parameters-service.ts +0 -139
  75. package/src/services/noaa-service.ts +0 -171
  76. package/src/services/sun-service.ts +0 -275
  77. package/src/tools/derived-product-tools.ts +0 -180
  78. package/src/tools/index.ts +0 -40
  79. package/src/tools/moon-tools.ts +0 -79
  80. package/src/tools/parameter-tools.ts +0 -82
  81. package/src/tools/station-tools.ts +0 -57
  82. package/src/tools/sun-tools.ts +0 -120
  83. package/src/tools/water-tools.ts +0 -166
  84. package/src/types/moon.ts +0 -27
  85. package/src/types/sun.ts +0 -51
  86. package/src/types/suncalc.d.ts +0 -110
  87. package/test-dpapi.js +0 -0
  88. package/tsconfig.json +0 -15
@@ -0,0 +1,89 @@
1
+ /**
2
+ * MCP prompts: reusable workflow templates that orchestrate the server's
3
+ * tools for the most common NOAA questions.
4
+ */
5
+ import { z } from "zod";
6
+ function userPrompt(text) {
7
+ return {
8
+ messages: [
9
+ {
10
+ role: "user",
11
+ content: { type: "text", text },
12
+ },
13
+ ],
14
+ };
15
+ }
16
+ export function registerPrompts(server) {
17
+ server.registerPrompt("tide_report", {
18
+ title: "Tide Report",
19
+ description: "Produce a tide report (high/low times, current level, context) for a place or station and date.",
20
+ argsSchema: {
21
+ location: z
22
+ .string()
23
+ .describe("Place name, coordinates, or a 7-digit NOAA station ID."),
24
+ date: z
25
+ .string()
26
+ .optional()
27
+ .describe("Date (YYYY-MM-DD). Defaults to today."),
28
+ },
29
+ }, ({ location, date }) => userPrompt(`Create a tide report for ${location}${date ? ` on ${date}` : " for today"}.
30
+
31
+ Steps:
32
+ 1. If "${location}" is not already a NOAA station ID, resolve it to coordinates and use noaa_find_nearest_stations (type "tidepredictions") to pick the closest station; state which station you chose and how far away it is.
33
+ 2. Get high/low tide times with noaa_get_tide_predictions (interval "hilo"${date ? `, begin_date "${date}", end_date "${date}"` : ', date "today"'}).
34
+ 3. If the station observes water levels, compare the current observed level (noaa_get_water_levels, date "latest") against the predictions and note any surge (observed minus predicted).
35
+ 4. Report: station used, each high/low with time and height (state the datum — MLLW — and units), current conditions if available, and the moon phase (astro_get_moon_phase) with a note on spring/neap tides.`));
36
+ server.registerPrompt("boating_conditions", {
37
+ title: "Boating & On-the-Water Conditions",
38
+ description: "Assemble tides, currents, wind, weather, and daylight for a location and date — a pre-departure briefing.",
39
+ argsSchema: {
40
+ location: z
41
+ .string()
42
+ .describe("Place name or coordinates of the launch/operating area."),
43
+ date: z
44
+ .string()
45
+ .optional()
46
+ .describe("Date (YYYY-MM-DD). Defaults to today."),
47
+ },
48
+ }, ({ location, date }) => userPrompt(`Prepare an on-the-water conditions briefing for ${location}${date ? ` on ${date}` : " today"}.
49
+
50
+ Gather (resolve ${location} to coordinates first):
51
+ 1. Tides: nearest tide-prediction station (noaa_find_nearest_stations type "tidepredictions") → noaa_get_tide_predictions interval "hilo".
52
+ 2. Currents: nearest current-prediction station (type "currentpredictions") → noaa_get_current_predictions interval "max_slack" — report max flood/ebb speeds (note units!) and slack times, best transit windows.
53
+ 3. Weather: nearest met station (type "met") → noaa_get_meteorological_data for wind (latest and/or recent trend) and air/water temperature.
54
+ 4. Daylight: astro_get_sun_times for sunrise/sunset/twilight.
55
+
56
+ Present as a briefing: daylight window, tide events, slack-water windows, current strength warnings, wind conditions, and water temperature. Flag any station that is far (>25 km) from the requested location.`));
57
+ server.registerPrompt("station_flood_risk", {
58
+ title: "Station Flood Risk Profile",
59
+ description: "Synthesize high-tide-flooding history, extremes, sea level trend, and projections into a flood risk profile for a station.",
60
+ argsSchema: {
61
+ station: z
62
+ .string()
63
+ .describe('7-digit NOAA water-level station ID (e.g. "8454000").'),
64
+ },
65
+ }, ({ station }) => userPrompt(`Build a flood risk profile for NOAA station ${station}.
66
+
67
+ Gather:
68
+ 1. noaa_get_station_info (expand ["floodlevels"]) — station identity and NOS/NWS flood thresholds.
69
+ 2. noaa_get_high_tide_flooding report "annual" with range 20 — historical flood-day trend.
70
+ 3. noaa_get_high_tide_flooding report "annual_outlook" — next-year outlook, and report "projections" — decadal projections.
71
+ 4. noaa_get_sea_level_trends — long-term relative sea level trend.
72
+ 5. noaa_get_top_ten_water_levels — worst historical events.
73
+ 6. noaa_get_extreme_water_levels — exceedance probability levels.
74
+
75
+ Synthesize: how often the station floods now vs 20 years ago, the driving sea-level trend, projected flooding by 2050, historical worst cases, and the water levels associated with 1%-annual-chance events. State datums and units explicitly.`));
76
+ server.registerPrompt("station_overview", {
77
+ title: "Station Capabilities Overview",
78
+ description: "Summarize everything a NOAA station offers: sensors, datums, products, data availability.",
79
+ argsSchema: {
80
+ station: z
81
+ .string()
82
+ .describe("NOAA station ID (7-digit or alphanumeric current station)."),
83
+ },
84
+ }, ({ station }) => userPrompt(`Summarize NOAA station ${station}: what it is, where it is, and what data it offers.
85
+
86
+ Use noaa_get_station_info (expand ["details", "sensors", "products"]), noaa_get_station_datums, and if it is a prediction station check whether it is reference (R) or subordinate (S) via noaa_search_stations / noaa_get_prediction_offsets.
87
+
88
+ Report: location/state/established date, tide type, installed sensors, supported datums (with MLLW–MSL offset), available data products and the right tool for each, and any caveats (Great Lakes, subordinate hilo-only predictions, historic-only status).`));
89
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Curated NOAA CO-OPS reference content, compiled from the official API
3
+ * documentation (api.tidesandcurrents.noaa.gov). Served both by the
4
+ * noaa_get_reference_guide tool and as noaa://reference/{topic} resources.
5
+ */
6
+ export declare const REFERENCE_TOPICS: readonly ["products", "datums", "units", "time_zones", "intervals", "station_types", "data_limits", "quality_flags", "date_formats"];
7
+ export type ReferenceTopic = (typeof REFERENCE_TOPICS)[number];
8
+ export declare const REFERENCE_CONTENT: Record<ReferenceTopic, string>;
9
+ /** One-line summaries used in resource listings and the guide index. */
10
+ export declare const REFERENCE_SUMMARIES: Record<ReferenceTopic, string>;
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Curated NOAA CO-OPS reference content, compiled from the official API
3
+ * documentation (api.tidesandcurrents.noaa.gov). Served both by the
4
+ * noaa_get_reference_guide tool and as noaa://reference/{topic} resources.
5
+ */
6
+ export const REFERENCE_TOPICS = [
7
+ "products",
8
+ "datums",
9
+ "units",
10
+ "time_zones",
11
+ "intervals",
12
+ "station_types",
13
+ "data_limits",
14
+ "quality_flags",
15
+ "date_formats",
16
+ ];
17
+ export const REFERENCE_CONTENT = {
18
+ products: `# NOAA CO-OPS Data Products
19
+
20
+ ## Water level / tide products (all require a datum)
21
+ | Product | Description | Tool |
22
+ |---|---|---|
23
+ | water_level | 6-minute preliminary or verified observations | noaa_get_water_levels (interval "6") |
24
+ | one_minute_water_level | 1-minute preliminary observations | noaa_get_water_levels (interval "1") |
25
+ | hourly_height | Verified hourly heights | noaa_get_water_levels (interval "hourly") |
26
+ | high_low | Verified daily highs/lows (HH/H/L/LL) | noaa_get_water_level_summaries |
27
+ | daily_mean | Verified daily means — GREAT LAKES ONLY, requires lst time zone | noaa_get_water_level_summaries |
28
+ | daily_max_min | Daily maxima/minima with completeness % | noaa_get_water_level_summaries |
29
+ | monthly_mean | Verified monthly datum means (MHHW, MSL, ... columns) | noaa_get_water_level_summaries |
30
+ | predictions | Harmonic tide predictions (heights or high/low events) | noaa_get_tide_predictions |
31
+ | air_gap | Bridge clearance (structure to water surface) | noaa_get_meteorological_data |
32
+
33
+ ## Meteorological products (no datum)
34
+ air_temperature, water_temperature, wind (speed/gust/direction), air_pressure,
35
+ conductivity, visibility, humidity, salinity — via noaa_get_meteorological_data.
36
+
37
+ ## Currents
38
+ | Product | Description | Tool |
39
+ |---|---|---|
40
+ | currents | Observed current speed/direction by depth bin | noaa_get_currents |
41
+ | currents_predictions | Predicted currents incl. max flood/ebb and slack | noaa_get_current_predictions |
42
+
43
+ ## Derived products (DPAPI)
44
+ Sea level trends, sea level rise projections, extreme water levels,
45
+ top-ten/peak water levels, high tide flooding — via noaa_get_sea_level_trends,
46
+ noaa_get_extreme_water_levels, noaa_get_top_ten_water_levels, noaa_get_high_tide_flooding.
47
+
48
+ Notes:
49
+ - Great Lakes stations have NO tide predictions.
50
+ - Subordinate ("S" type) prediction stations only support interval=hilo.`,
51
+ datums: `# Vertical Datums
52
+
53
+ A datum is the zero reference for water heights. The same water level reads
54
+ differently against different datums — always report which datum was used.
55
+
56
+ | Datum | Meaning | Applies to |
57
+ |---|---|---|
58
+ | MLLW | Mean Lower Low Water — standard US nautical chart datum | Coastal/tidal stations (default choice) |
59
+ | MLW | Mean Low Water | Coastal |
60
+ | MSL | Mean Sea Level | Coastal |
61
+ | MTL | Mean Tide Level | Coastal |
62
+ | MHW | Mean High Water | Coastal |
63
+ | MHHW | Mean Higher High Water (flood analyses often use this) | Coastal |
64
+ | STND | Station datum (arbitrary fixed zero, always available) | All stations |
65
+ | NAVD | North American Vertical Datum 1988 | Only stations where computed |
66
+ | CRD | Columbia River Datum | Columbia River stations only |
67
+ | IGLD | International Great Lakes Datum 1985 | Great Lakes ONLY |
68
+ | LWD | Low Water Datum (Great Lakes chart datum) | Great Lakes ONLY |
69
+
70
+ - Datum values come from the current National Tidal Datum Epoch (1983–2001).
71
+ - Requesting a datum a station does not support returns an error; check
72
+ supported datums first with noaa_get_station_datums.
73
+ - Datum applies to water-level products and tide predictions; it does not
74
+ apply to meteorological or current data.`,
75
+ units: `# Unit Systems
76
+
77
+ The \`units\` parameter is english (default) or metric. Units differ per
78
+ measurement — note the two asymmetric cases (wind vs currents in metric):
79
+
80
+ | Measurement | english | metric |
81
+ |---|---|---|
82
+ | Water level / air gap | feet | meters |
83
+ | Air & water temperature | °F | °C |
84
+ | Wind speed & gust | knots | m/s |
85
+ | **Current speed** | **knots** | **cm/s (NOT m/s)** |
86
+ | Visibility | nautical miles | kilometers |
87
+ | Air pressure | millibars | millibars (unchanged) |
88
+ | Salinity | PSU | PSU (unchanged) |
89
+ | Conductivity | mS/cm | mS/cm (unchanged) |`,
90
+ time_zones: `# Time Zones
91
+
92
+ | Value | Meaning |
93
+ |---|---|
94
+ | gmt | Greenwich Mean Time (UTC) |
95
+ | lst | Station's Local Standard Time — never shifts for DST |
96
+ | lst_ldt | Station's local time honoring daylight saving |
97
+
98
+ - daily_mean data REQUIRES lst (the server enforces this automatically).
99
+ - Predictions and observations may be requested in any of the three.
100
+ - When comparing NOAA timestamps with other data sources, prefer gmt.`,
101
+ intervals: `# Interval Parameter
102
+
103
+ | Tool / product | Valid intervals |
104
+ |---|---|
105
+ | Tide predictions | hilo (high/low events), h (hourly), 1, 5, 6, 10, 15, 30, 60 (minutes) |
106
+ | Water levels | chosen via interval param: "1" (1-min), "6" (6-min, standard), "hourly" |
107
+ | Meteorological | 6 (default) or h (hourly) |
108
+ | Observed currents | 6-minute (default) or h |
109
+ | Current predictions | max_slack (max flood/ebb + slack events), h, 1, 6, 10, 30, 60 |
110
+
111
+ - interval=hilo returns the four daily tide events instead of a time series —
112
+ this is what you want for "when is high tide?".
113
+ - interval=max_slack is the currents analog: max flood, max ebb, slack times.`,
114
+ station_types: `# Station Types (noaa_search_stations \`type\` values)
115
+
116
+ | Type | Stations that... |
117
+ |---|---|
118
+ | waterlevels | actively observe water levels |
119
+ | historicwl | historically observed water levels |
120
+ | met | have meteorological sensors |
121
+ | waterlevelsandmet | have both |
122
+ | tidepredictions | have tide predictions (R=reference, S=subordinate) |
123
+ | currents | actively observe currents (alphanumeric IDs like cb0102) |
124
+ | historiccurrents / surveycurrents | historical/survey current observations |
125
+ | currentpredictions | have current predictions |
126
+ | harcon | have harmonic constituents |
127
+ | datums / supersededdatums | have (superseded) tidal datums |
128
+ | benchmarks / supersededbenchmarks | have leveling benchmarks |
129
+ | cond / watertemp / airgap / visibility | conductivity / water temp / air gap / visibility sensors |
130
+ | physocean | physical oceanography (PORTS) |
131
+ | tcoon | Texas Coastal Ocean Observation Network |
132
+ | 1minute | report 1-minute water levels |
133
+ | highwater / lowwater | high/low water marks |
134
+
135
+ Station ID formats: 7-digit numeric for water-level/met (e.g. 9414290);
136
+ alphanumeric for current stations (e.g. cb0102). Tide-prediction subordinate
137
+ (S) stations derive predictions from a reference (R) station via offsets.`,
138
+ data_limits: `# Maximum Request Spans (enforced before calling NOAA)
139
+
140
+ | Product | Max span per request |
141
+ |---|---|
142
+ | 1-minute water levels | 4 days |
143
+ | 6-minute water levels | 31 days |
144
+ | Hourly heights | 1 year |
145
+ | High/low observations | 1 year |
146
+ | Daily means / daily max-min | 10 years |
147
+ | Monthly means | 200 years |
148
+ | Tide predictions, interval=hilo | 10 years |
149
+ | Tide predictions, other intervals | 1 year |
150
+ | Observed currents (any bin, incl. bin=0) | 7 days |
151
+ | Current predictions, interval=max_slack | 1 year |
152
+ | Current predictions, other intervals | 31 days |
153
+ | Meteorological products | 31 days |
154
+
155
+ For longer periods, make multiple chunked requests. NOAA throttles heavy
156
+ query volume (no published numeric limit) — request only what you need.`,
157
+ quality_flags: `# Data Quality Fields & Flags
158
+
159
+ Water level responses include:
160
+ - v — value; s — sigma (standard deviation of the 1-second samples)
161
+ - q — quality: p = preliminary (recent, unverified), v = verified
162
+ - f — comma-separated flag counts/indicators:
163
+ - Preliminary water levels: O=outliers beyond 3-sigma, F=flat tolerance
164
+ exceeded, R=rate-of-change exceeded, L=max/min limit exceeded
165
+ - Verified water levels: I=inferred, F=flat, R=rate-of-change, T=max/min
166
+ - Hourly heights & high_low: I=inferred, L=limit exceeded
167
+ - Met products: X=max exceeded, N=min exceeded, R=rate-of-change
168
+ - Wind: X=max speed exceeded, R=rate-of-change
169
+ - Air gap: O, F, R, A=max/min limit
170
+
171
+ high_low \`ty\` values: HH=higher high, H=high, L=low, LL=lower low
172
+ (mixed-tide coasts have two unequal highs and lows per day).
173
+
174
+ Verified data replaces preliminary data after NOAA QC (typically days to
175
+ weeks later); recent observations are almost always preliminary.`,
176
+ date_formats: `# Date Parameters
177
+
178
+ Formats accepted: yyyyMMdd, "yyyyMMdd HH:mm", MM/dd/yyyy, "MM/dd/yyyy HH:mm",
179
+ and ISO yyyy-MM-dd or yyyy-MM-ddTHH:mm (normalized automatically).
180
+
181
+ Exactly ONE of these combinations per request:
182
+ 1. begin_date + end_date — explicit window
183
+ 2. begin_date + range — N hours forward from begin
184
+ 3. end_date + range — N hours back from end
185
+ 4. date=today | latest | recent — today (midnight→now), latest (single most
186
+ recent reading, ~18 min window), recent (last 72 hours)
187
+ 5. range alone — N hours back from now
188
+
189
+ Timestamps in responses are in the requested time_zone (gmt | lst | lst_ldt).`,
190
+ };
191
+ /** One-line summaries used in resource listings and the guide index. */
192
+ export const REFERENCE_SUMMARIES = {
193
+ products: "Every NOAA CO-OPS data product and which tool serves it",
194
+ datums: "Vertical datums (MLLW, MSL, IGLD...) and station applicability",
195
+ units: "What english vs metric means per measurement (incl. cm/s currents)",
196
+ time_zones: "gmt / lst / lst_ldt semantics and restrictions",
197
+ intervals: "Valid interval values per product family",
198
+ station_types: "Station type filters and station ID formats",
199
+ data_limits: "Maximum date-range span per product",
200
+ quality_flags: "Data quality fields (v/s/f/q), flag letters, HH/H/L/LL",
201
+ date_formats: "Accepted date formats and parameter combinations",
202
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * MCP resources: the reference guide topics exposed as noaa://reference/{topic}
3
+ * plus a getting-started guide. Clients that support resources can pin these
4
+ * into context without a tool round-trip.
5
+ */
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ export declare function registerResources(server: McpServer): void;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * MCP resources: the reference guide topics exposed as noaa://reference/{topic}
3
+ * plus a getting-started guide. Clients that support resources can pin these
4
+ * into context without a tool round-trip.
5
+ */
6
+ import { ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { REFERENCE_CONTENT, REFERENCE_SUMMARIES, REFERENCE_TOPICS, } from "../reference/content.js";
8
+ const GETTING_STARTED = `# NOAA Tides & Currents MCP Server — Getting Started
9
+
10
+ Typical workflows:
11
+
12
+ ## "When is high tide near <place>?"
13
+ 1. Geocode the place to lat/lon (outside this server).
14
+ 2. noaa_find_nearest_stations (type "tidepredictions") → pick a station ID.
15
+ 3. noaa_get_tide_predictions with interval "hilo" and a date window.
16
+
17
+ ## "What are conditions right now at <station>?"
18
+ - noaa_get_water_levels (date "latest") for water level
19
+ - noaa_get_meteorological_data (product "wind"/"air_temperature", date "latest")
20
+ - noaa_get_currents (date "latest") at a current station (different ID scheme!)
21
+
22
+ ## "How often does <station> flood, and what's projected?"
23
+ - noaa_get_high_tide_flooding (report "annual", range 15)
24
+ - noaa_get_sea_level_trends, noaa_get_sea_level_rise_projections
25
+ - noaa_get_extreme_water_levels for exceedance probabilities
26
+
27
+ ## Things that trip people up
28
+ - Datum matters: heights above MLLW differ from MSL by several feet at many
29
+ stations. MLLW is the chart default. See noaa://reference/datums.
30
+ - Units: metric current speed is cm/s, not m/s. See noaa://reference/units.
31
+ - Water-level and current stations have DIFFERENT ID schemes (7-digit vs
32
+ alphanumeric like "cb0102").
33
+ - Predictions are astronomical — storms are not included.
34
+ - Per-product maximum date spans apply. See noaa://reference/data_limits.
35
+ - Great Lakes stations: no tide predictions; datums IGLD/LWD; daily_mean.
36
+ `;
37
+ export function registerResources(server) {
38
+ server.registerResource("getting-started", "noaa://guide/getting-started", {
39
+ title: "Getting Started with NOAA Tides & Currents",
40
+ description: "Workflow recipes and common pitfalls for this server.",
41
+ mimeType: "text/markdown",
42
+ }, async (uri) => ({
43
+ contents: [
44
+ { uri: uri.href, mimeType: "text/markdown", text: GETTING_STARTED },
45
+ ],
46
+ }));
47
+ server.registerResource("reference", new ResourceTemplate("noaa://reference/{topic}", {
48
+ list: async () => ({
49
+ resources: REFERENCE_TOPICS.map((topic) => ({
50
+ uri: `noaa://reference/${topic}`,
51
+ name: `NOAA reference: ${topic}`,
52
+ description: REFERENCE_SUMMARIES[topic],
53
+ mimeType: "text/markdown",
54
+ })),
55
+ }),
56
+ complete: {
57
+ topic: async (value) => REFERENCE_TOPICS.filter((t) => t.startsWith(value ?? "")).map(String),
58
+ },
59
+ }), {
60
+ title: "NOAA CO-OPS Reference",
61
+ description: "Curated NOAA API reference topics (datums, units, limits, flags...).",
62
+ mimeType: "text/markdown",
63
+ }, async (uri, variables) => {
64
+ const topic = String(variables.topic);
65
+ const content = REFERENCE_CONTENT[topic];
66
+ if (!content) {
67
+ throw new Error(`Unknown reference topic "${topic}". Valid topics: ${REFERENCE_TOPICS.join(", ")}.`);
68
+ }
69
+ return {
70
+ contents: [{ uri: uri.href, mimeType: "text/markdown", text: content }],
71
+ };
72
+ });
73
+ }
@@ -1,14 +1,38 @@
1
- import { z } from 'zod';
2
- export declare const StationSchema: z.ZodString;
3
- export declare const DateSchema: z.ZodOptional<z.ZodString>;
4
- export declare const BeginDateSchema: z.ZodOptional<z.ZodString>;
5
- export declare const EndDateSchema: z.ZodOptional<z.ZodString>;
6
- export declare const RangeSchema: z.ZodOptional<z.ZodNumber>;
7
- export declare const DatumSchema: z.ZodOptional<z.ZodString>;
8
- export declare const UnitsSchema: z.ZodOptional<z.ZodEnum<["english", "metric"]>>;
9
- export declare const TimeZoneSchema: z.ZodOptional<z.ZodEnum<["gmt", "lst", "lst_ldt"]>>;
10
- export declare const FormatSchema: z.ZodOptional<z.ZodEnum<["json", "xml", "csv"]>>;
11
- export declare const BinSchema: z.ZodOptional<z.ZodNumber>;
12
- export declare const IntervalSchema: z.ZodOptional<z.ZodString>;
13
- export declare const refineDateParams: (data: any) => any;
14
- export declare const dateRefinementMessage = "You must provide either 'date', 'begin_date' and 'end_date', 'begin_date' and 'range', 'end_date' and 'range', or just 'range'";
1
+ /**
2
+ * Shared Zod field schemas used across tool input schemas.
3
+ * Field descriptions matter: MCP clients show them to the model, so they
4
+ * carry the load-bearing NOAA nuances (units, datums, limits).
5
+ */
6
+ import { z } from "zod";
7
+ /** 7-digit numeric (water level/met) or alphanumeric current-station IDs like "cb0102". */
8
+ export declare const StationIdSchema: z.ZodString;
9
+ export declare const DateAliasSchema: z.ZodEnum<["today", "latest", "recent"]>;
10
+ export declare const BeginDateSchema: z.ZodString;
11
+ export declare const EndDateSchema: z.ZodString;
12
+ export declare const RangeSchema: z.ZodNumber;
13
+ export declare const DatumSchema: z.ZodEnum<["MHHW", "MHW", "MTL", "MSL", "MLW", "MLLW", "NAVD", "STND", "IGLD", "LWD", "CRD"]>;
14
+ export declare const UnitsSchema: z.ZodDefault<z.ZodEnum<["english", "metric"]>>;
15
+ export declare const TimeZoneSchema: z.ZodDefault<z.ZodEnum<["gmt", "lst", "lst_ldt"]>>;
16
+ export declare const ResponseFormatSchema: z.ZodDefault<z.ZodEnum<["markdown", "json"]>>;
17
+ /** Base date-selection fields shared by all Data API tools. */
18
+ export declare const dateFields: {
19
+ date: z.ZodOptional<z.ZodEnum<["today", "latest", "recent"]>>;
20
+ begin_date: z.ZodOptional<z.ZodString>;
21
+ end_date: z.ZodOptional<z.ZodString>;
22
+ range: z.ZodOptional<z.ZodNumber>;
23
+ };
24
+ export declare const READ_ONLY_ANNOTATIONS: {
25
+ readonly readOnlyHint: true;
26
+ readonly destructiveHint: false;
27
+ readonly idempotentHint: true;
28
+ readonly openWorldHint: true;
29
+ };
30
+ /** Astronomy tools compute locally — no external world interaction. */
31
+ export declare const LOCAL_COMPUTE_ANNOTATIONS: {
32
+ readonly readOnlyHint: true;
33
+ readonly destructiveHint: false;
34
+ readonly idempotentHint: true;
35
+ readonly openWorldHint: false;
36
+ };
37
+ export declare const LatitudeSchema: z.ZodNumber;
38
+ export declare const LongitudeSchema: z.ZodNumber;
@@ -1,18 +1,82 @@
1
- import { z } from 'zod';
2
- // Common parameter schemas
3
- export const StationSchema = z.string().min(1).describe('Station ID');
4
- export const DateSchema = z.string().optional().describe('Date to retrieve data for ("today", "latest", "recent", or specific date)');
5
- export const BeginDateSchema = z.string().optional().describe('Start date (YYYYMMDD or MM/DD/YYYY)');
6
- export const EndDateSchema = z.string().optional().describe('End date (YYYYMMDD or MM/DD/YYYY)');
7
- export const RangeSchema = z.number().optional().describe('Number of hours to retrieve data for');
8
- export const DatumSchema = z.string().optional().describe('Datum to use (MLLW, MSL, etc.)');
9
- export const UnitsSchema = z.enum(['english', 'metric']).optional().describe('Units to use ("english" or "metric")');
10
- export const TimeZoneSchema = z.enum(['gmt', 'lst', 'lst_ldt']).optional().describe('Time zone (gmt, lst, lst_ldt)');
11
- export const FormatSchema = z.enum(['json', 'xml', 'csv']).optional().describe('Output format (json, xml, csv)');
12
- export const BinSchema = z.number().optional().describe('Bin number');
13
- export const IntervalSchema = z.string().optional().describe('Interval (hilo, hl, h, or a number for minutes)');
14
- // Schema refinement function for date parameters
15
- export const refineDateParams = (data) => (data.date || (data.begin_date && data.end_date) ||
16
- (data.begin_date && data.range) || (data.end_date && data.range) ||
17
- data.range);
18
- export const dateRefinementMessage = "You must provide either 'date', 'begin_date' and 'end_date', 'begin_date' and 'range', 'end_date' and 'range', or just 'range'";
1
+ /**
2
+ * Shared Zod field schemas used across tool input schemas.
3
+ * Field descriptions matter: MCP clients show them to the model, so they
4
+ * carry the load-bearing NOAA nuances (units, datums, limits).
5
+ */
6
+ import { z } from "zod";
7
+ /** 7-digit numeric (water level/met) or alphanumeric current-station IDs like "cb0102". */
8
+ export const StationIdSchema = z
9
+ .string()
10
+ .regex(/^[A-Za-z0-9_]{4,10}$/, 'Station IDs are 7-digit numbers for water-level/met stations (e.g. "9414290") or alphanumeric codes for current stations (e.g. "cb0102").')
11
+ .describe('Station ID. Water-level/met stations use 7-digit numeric IDs (e.g. "9414290" San Francisco); current stations use alphanumeric IDs (e.g. "cb0102"). Find stations with noaa_search_stations or noaa_find_nearest_stations.');
12
+ export const DateAliasSchema = z
13
+ .enum(["today", "latest", "recent"])
14
+ .describe('Shortcut window: "today" = midnight to now, "latest" = single most recent reading, "recent" = last 72 hours. Mutually exclusive with begin_date/end_date/range.');
15
+ export const BeginDateSchema = z
16
+ .string()
17
+ .describe('Start date/time. Formats: yyyyMMdd, "yyyyMMdd HH:mm", MM/dd/yyyy, or ISO yyyy-MM-dd[THH:mm].');
18
+ export const EndDateSchema = z
19
+ .string()
20
+ .describe("End date/time. Same formats as begin_date.");
21
+ export const RangeSchema = z
22
+ .number()
23
+ .int()
24
+ .positive()
25
+ .describe("Number of hours. With begin_date: hours forward. With end_date: hours back. Alone: hours back from now.");
26
+ export const DatumSchema = z
27
+ .enum([
28
+ "MHHW",
29
+ "MHW",
30
+ "MTL",
31
+ "MSL",
32
+ "MLW",
33
+ "MLLW",
34
+ "NAVD",
35
+ "STND",
36
+ "IGLD",
37
+ "LWD",
38
+ "CRD",
39
+ ])
40
+ .describe("Vertical reference datum for heights. MLLW is the standard chart datum for coastal stations. IGLD and LWD apply to Great Lakes stations ONLY; NAVD/CRD exist only at stations where computed. Check a station's supported datums with noaa_get_station_datums.");
41
+ export const UnitsSchema = z
42
+ .enum(["english", "metric"])
43
+ .default("english")
44
+ .describe("Unit system. english: feet, °F, knots (wind AND currents), nautical miles. metric: meters, °C, m/s for wind but cm/s for currents, kilometers. Air pressure is millibars and salinity is PSU in BOTH systems.");
45
+ export const TimeZoneSchema = z
46
+ .enum(["gmt", "lst", "lst_ldt"])
47
+ .default("lst_ldt")
48
+ .describe("Time zone for timestamps: gmt = UTC, lst = station local standard time (no DST), lst_ldt = station local time with DST. Note: daily_mean data requires lst.");
49
+ export const ResponseFormatSchema = z
50
+ .enum(["markdown", "json"])
51
+ .default("markdown")
52
+ .describe('Output format: "markdown" for a readable summary table, "json" for the complete structured payload.');
53
+ /** Base date-selection fields shared by all Data API tools. */
54
+ export const dateFields = {
55
+ date: DateAliasSchema.optional(),
56
+ begin_date: BeginDateSchema.optional(),
57
+ end_date: EndDateSchema.optional(),
58
+ range: RangeSchema.optional(),
59
+ };
60
+ export const READ_ONLY_ANNOTATIONS = {
61
+ readOnlyHint: true,
62
+ destructiveHint: false,
63
+ idempotentHint: true,
64
+ openWorldHint: true,
65
+ };
66
+ /** Astronomy tools compute locally — no external world interaction. */
67
+ export const LOCAL_COMPUTE_ANNOTATIONS = {
68
+ readOnlyHint: true,
69
+ destructiveHint: false,
70
+ idempotentHint: true,
71
+ openWorldHint: false,
72
+ };
73
+ export const LatitudeSchema = z
74
+ .number()
75
+ .min(-90)
76
+ .max(90)
77
+ .describe("Latitude in decimal degrees (-90 to 90).");
78
+ export const LongitudeSchema = z
79
+ .number()
80
+ .min(-180)
81
+ .max(180)
82
+ .describe("Longitude in decimal degrees (-180 to 180).");
@@ -0,0 +1,81 @@
1
+ /**
2
+ * NOAA CO-OPS Data Retrieval API service.
3
+ *
4
+ * Encodes the per-product rules discovered from the official docs:
5
+ * - which products need a datum
6
+ * - per-product/interval maximum request spans (validated before calling)
7
+ * - product-specific restrictions (daily_mean is Great Lakes-only and
8
+ * requires time_zone=lst; bin=0 caps currents at 7 days; etc.)
9
+ */
10
+ import { DateParams } from "../validation/dates.js";
11
+ import type { UnitSystem } from "../format/units.js";
12
+ export type NoaaTimeZone = "gmt" | "lst" | "lst_ldt";
13
+ export interface DataApiBaseParams extends DateParams {
14
+ station: string;
15
+ units: UnitSystem;
16
+ time_zone: NoaaTimeZone;
17
+ }
18
+ interface DataObservation {
19
+ t: string;
20
+ v?: string;
21
+ s?: string;
22
+ f?: string;
23
+ q?: string;
24
+ ty?: string;
25
+ d?: string;
26
+ dr?: string;
27
+ g?: string;
28
+ b?: string;
29
+ [key: string]: string | undefined;
30
+ }
31
+ export interface DataApiResponse {
32
+ metadata?: {
33
+ id: string;
34
+ name: string;
35
+ lat: string;
36
+ lon: string;
37
+ };
38
+ data?: DataObservation[];
39
+ predictions?: DataObservation[];
40
+ current_predictions?: {
41
+ cp: DataObservation[];
42
+ } | DataObservation[];
43
+ [key: string]: unknown;
44
+ }
45
+ export type WaterLevelInterval = "1" | "6" | "hourly";
46
+ export declare function getWaterLevels(params: DataApiBaseParams & {
47
+ interval: WaterLevelInterval;
48
+ datum: string;
49
+ }): Promise<{
50
+ product: string;
51
+ response: DataApiResponse;
52
+ }>;
53
+ export type SummaryProduct = "high_low" | "daily_mean" | "daily_max_min" | "monthly_mean";
54
+ export declare function getWaterLevelSummaries(params: DataApiBaseParams & {
55
+ product: SummaryProduct;
56
+ datum: string;
57
+ }): Promise<{
58
+ response: DataApiResponse;
59
+ timeZoneForced: boolean;
60
+ }>;
61
+ export type PredictionInterval = "hilo" | "h" | "1" | "5" | "6" | "10" | "15" | "30" | "60";
62
+ export declare function getTidePredictions(params: DataApiBaseParams & {
63
+ interval: PredictionInterval;
64
+ datum: string;
65
+ }): Promise<DataApiResponse>;
66
+ export declare function getCurrents(params: DataApiBaseParams & {
67
+ bin?: number;
68
+ expand_detailed?: boolean;
69
+ }): Promise<DataApiResponse>;
70
+ export type CurrentPredictionInterval = "max_slack" | "h" | "1" | "6" | "10" | "30" | "60";
71
+ export declare function getCurrentPredictions(params: DataApiBaseParams & {
72
+ bin?: number;
73
+ interval: CurrentPredictionInterval;
74
+ vel_type?: "default" | "speed_dir";
75
+ }): Promise<DataApiResponse>;
76
+ export type MetProduct = "air_temperature" | "water_temperature" | "wind" | "air_pressure" | "air_gap" | "conductivity" | "visibility" | "humidity" | "salinity";
77
+ export declare function getMeteorologicalData(params: DataApiBaseParams & {
78
+ product: MetProduct;
79
+ interval?: "6" | "h";
80
+ }): Promise<DataApiResponse>;
81
+ export {};