Find NOAA tide stations and NDBC buoys, fetch tide predictions, currents, and live conditions.
One address for any MCP client. No sign-in needed on this server.
8 read
Grouped by tool name. At most: only reads or looks up information.
Odel charges nothing per call. The publisher may charge for its own service.
Find NOAA tide stations and NDBC buoys, fetch tide predictions, water levels, tidal currents, and live buoy conditions via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://noaa-marine.caseyjhand.com/mcp
US tides, currents, water levels, and buoy observations from NOAA CO-OPS and NDBC. Find coastal stations and buoys, then fetch tide and tidal-current predictions, observed and monthly-mean water levels, and live buoy conditions, current profiles, and water-column readings.
| Tool | Description |
|---|---|
noaa_marine_find_stations | Find CO-OPS tide, water-level, and current stations and NDBC buoys by location, name, ID, state, or capability |
noaa_marine_get_tide_predictions | High/low tide predictions or a 6-minute curve for a CO-OPS tide station |
noaa_marine_get_water_level | Observed water level at four cadences, paired with predictions and a storm-surge residual |
noaa_marine_get_monthly_means | Verified monthly tidal datums and extremes for a CO-OPS water-level station |
noaa_marine_get_currents | CO-OPS tidal-current predictions: max flood, max ebb, and slack events, or a 6-minute curve |
noaa_marine_get_conditions | Live NDBC buoy conditions: waves, wind, sea-surface and air temperature, pressure |
noaa_marine_get_current_profile | Observed ocean-current depth profile from an NDBC ADCP buoy |
noaa_marine_get_ocean_observations | Sub-surface water-column readings (temperature, salinity, oxygen, and more) from an NDBC station |
| Resource | Description |
|---|---|
noaa-marine://station/{station_id} | Metadata for a CO-OPS or NDBC station: name, coordinates, source, capabilities, prediction class, platform |
Everything in the resource except NDBC owner is also on noaa_marine_find_stations rows, for clients that only call tools.
noaa_marine_find_stations toollatitude + longitude with radius_km (default 100, max 1000), query (name or ID substring on both sources; an exact ID leads unless sorting by distance), state (CO-OPS only), source (coops / ndbc / all), and types (tide, current, water_level, met, current_profile, water_quality, or the platform class buoy); limit defaults to 20, max 200source, capabilities, type, and for NDBC a platform; CO-OPS rows add state (state_derived: true when borrowed from the nearest state-bearing station within 25 km), a tide prediction_class (reference, or subordinate with reference_id), and current bins[] giving each bin's number, depth in feet, and classtotal_found and truncated report the count before limit; zero matches is a success with applied_search, a partial catalog read is flagged in sources, and total catalog failure is sources_unavailable; only one of latitude/longitude is incomplete_coordinatesnoaa_marine_get_tide_predictions toolinterval hilo (default) or 6min, up to 365 days; ten datums (MLLW, MHHW, MHW, MTL, MSL, MLW, DTL, NAVD, STND, CRD), time_zone (lst_ldt, gmt, lst), and units (english feet, metric meters)6min works at reference stations only: a subordinate station fails as subordinate_no_6min naming its reference station, and a Great Lakes station is no_predictions, pointing to noaa_marine_get_water_levelinvalid_date_range, date_range_exceeded, station_not_found, datum_unavailable (naming the planes the station carries), and upstream_throttled, a retryable CO-OPS HTTP 403 that clears in a couple of minutesnoaa_marine_get_water_level toolinterval 6min (default, 31 days), hourly or high_low (365 days), or daily_mean (3,655 days, Great Lakes only, always in LST; a coastal station is great_lakes_only); thirteen datums (MLLW, MHHW, MHW, MTL, MSL, MLW, NAVD, STND, IGLD, LWD, CRD, LWI, HWI)quality (p / v, 6min only), sigma (6min and hourly), and type (H / HH / L / LL, high_low only); slots with no reading are dropped and counted in gaps_droppeddaily_mean), with predictions_status (empty / unavailable) explaining an empty series; residual_summary (max_surge, max_drawdown) covers the whole matched range, on 6min and hourly only. Failures: invalid_date_range, date_range_exceeded, station_not_found, datum_unavailable, no_data, verified_data_lag (a coarser-cadence window ending on or after the first day of the prior month, which CO-OPS may not have verified yet), and upstream_throttlednoaa_marine_get_monthly_means toolenglish feet or metric meters; the first and last months returned are those begin_date and end_date fall inhighest, lowest, the tidal datums (mhhw, mhw, msl, mtl, mlw, mllw, dtl), ranges (gt, mn, dhq, dlq), lunitidal intervals in hours (hwi, lwi), and the inferred code verbatim; values CO-OPS leaves blank are omitted, so a Great Lakes station carries only highest, msl, and lowestinvalid_date_range, date_range_exceeded, station_not_found, datum_unavailable, upstream_throttled, and for an empty window verified_data_lag when it ends on or after the first day of the prior month, no_data otherwisenoaa_marine_get_currents toolinterval MAX_SLACK (default: max flood, max ebb, and slack events) or 6min, up to 365 days; bin picks a depth bin from bins[] (default the shallowest); units english (knots, feet) or metric (cm/s, meters)bin and depth; rows carry a flood / ebb / slack type, a non-negative speed, and as direction the station's mean bearing for that flowbin_unavailable listing the ones it has; a station CO-OPS won't predict as events returns an empty list with its statement in notice, and one with no published predictions is predictions_unavailable; also invalid_date_range, date_range_exceeded, station_not_found, no_predictions, and upstream_throttlednoaa_marine_get_conditions toolstation_id; returns wind, waves, sea-surface and air temperature, dew point, and pressure in SI units, except tide_ft and visibility_nmiwaves_observed_at and any other older block named in notice. Failures: buoy_not_found, no_sensor_datanoaa_marine_get_current_profile toolstation_id, found with types: ["current_profile"]; bins[] runs shallowest first with depth_m, direction_deg (true, flow-toward), and speed_cm_s, the last two null when unreportednoaa_marine_get_currents returns; failures are profile_not_found (no ADCP file) and no_current_datanoaa_marine_get_ocean_observations toolstation_id, found with types: ["water_quality"], a catalog flag rather than a guarantee; readings[] holds one row per depth with temperature, conductivity, salinity, dissolved oxygen (% and ppm), chlorophyll, turbidity, pH, and redox potentialnull; failures are observations_not_found (no .ocean file) and no_ocean_datanoaa-marine://station/{station_id} resourcestation_id from noaa_marine_find_stations; returns that tool's station record as application/json, plus owner for NDBC stations, with a 6-hour cacheHintstation_not_found only when both catalogs were read; source_unavailable when one could not beBuilt on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
CO-OPS / NDBC-specific:
realtime2 feeds (.txt, .adcp, .ocean), with station catalogs cached in memory for 6 hours; CO-OPS requests carry application= from NOAA_APPLICATION_ID9447130), CO-OPS current IDs alphanumeric (ACT4176), NDBC IDs 5-character codes (46041)begin_date / end_date as YYYYMMDD or YYYY-MM-DD and check the calendar and each tool's range ceiling before calling (invalid_date_range, date_range_exceeded); heights default to MLLW, and a datum the station lacks is datum_unavailable naming ones it carrieshourly, high_low, daily_mean, and monthly means once a month, for the prior month: an empty window ending on or after the first of the prior month is verified_data_lag, an earlier one no_dataupstream_throttled whose recovery names the waitAgent-friendly output:
rows_matched, rows_returned, page_offset, and next_offset, walked with offset (limit shrinks a page)null or omitted, never fabricated: NDBC MM markers become null, blank CO-OPS values are dropped, and quality is left out rather than defaultedsource (coops | ndbc), capability type, NDBC platform, and prediction_class, with datum and units echoed on every CO-OPS height response, so callers branch on data, not string parsingA public instance is available at https://noaa-marine.caseyjhand.com/mcp, with no installation or API key. It serves Streamable HTTP; each client takes its own config shape.
For Claude Code:
claude mcp add --transport http noaa-marine-mcp-server https://noaa-marine.caseyjhand.com/mcp
For Cline (cline_mcp_settings.json):
{
"mcpServers": {
"noaa-marine-mcp-server": {
"type": "streamableHttp",
"url": "https://noaa-marine.caseyjhand.com/mcp"
}
}
}
For Cursor (mcp.json):
{
"mcpServers": {
"noaa-marine-mcp-server": {
"url": "https://noaa-marine.caseyjhand.com/mcp"
}
}
}
Claude Desktop adds remote servers under Settings → Connectors.
Add the following to your MCP client configuration file:
{
"mcpServers": {
"noaa-marine-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/noaa-marine-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"noaa-marine-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/noaa-marine-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"noaa-marine-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"ghcr.io/cyanheads/noaa-marine-mcp-server:latest"
]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
git clone https://github.com/cyanheads/noaa-marine-mcp-server.git
cd noaa-marine-mcp-server
bun install
cp .env.example .env
# edit .env if needed (all vars optional)
| Variable | Description | Default |
|---|---|---|
NOAA_APPLICATION_ID | Courtesy identifier sent as application= on CO-OPS requests. | noaa-marine-mcp-server |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_HTTP_HOST | HTTP bind address. | 127.0.0.1 |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto. | stateless |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
LOGS_DIR | Directory for log files (Node.js only). | <project-root>/logs |
LOG_TOOL_FAILURE_PAYLOADS | Log each failed tool call's arguments and result (redacted by key name only), capped by LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES. | false |
OTEL_ENABLED | Enable OpenTelemetry. | false |
OTEL_EXPORTER_OTLP_ENDPOINT | OTLP base URL; traces and metrics export to /v1/traces and /v1/metrics under it. | — |
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT | Opt-in OTLP log export endpoint; the base URL never enables it. | — |
See .env.example for the full list of optional overrides.
Build and run:
bun run rebuild
bun run start:stdio
# or
bun run start:http
Run checks and tests:
bun run devcheck # Lint, format, typecheck, security
bun run test # Vitest test suite
bun run lint:mcp # Validate MCP definitions against spec
docker build -t noaa-marine-mcp-server .
docker run --rm -p 3010:3010 noaa-marine-mcp-server
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/noaa-marine-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
| Path | Purpose |
|---|---|
src/index.ts | createApp() entry point: registers the tools and resource, initializes services. |
src/config/ | NOAA_APPLICATION_ID env var parsing with Zod. |
src/services/coops/ | CO-OPS client: station catalog cache, data fetch, error classification, date-range validation, row paging, state resolution. |
src/services/ndbc/ | NDBC service: active-stations XML and realtime2 feed parsers. |
src/services/geo.ts | Haversine distance for proximity search and state resolution. |
src/mcp-server/tools/definitions/ | Eight tool definitions (*.tool.ts). |
src/mcp-server/resources/definitions/ | Station metadata resource (noaa-marine-station.resource.ts). |
tests/ | Vitest tests mirroring src/. |
docs/ | Design doc and directory tree. |
See CLAUDE.md/AGENTS.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for request-scoped loggingsrc/index.ts directly (no barrels for this server)MM values must normalize to null, not be passed through as stringsIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.