@cyanheads/noaa-marine-mcp-server
Public Hosted Server: https://noaa-marine.caseyjhand.com/mcp
Overview
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 the latest buoy conditions, current profiles, and water-column readings.
Tools
Tool | Description |
| Find CO-OPS tide, water-level, and current stations and NDBC buoys by location, name, ID, state, or capability |
| High/low tide predictions or a 6-minute curve for a CO-OPS tide station |
| Observed water level at four cadences, with extrema and storm-surge residuals where available |
| Verified monthly tidal datums and extremes for a CO-OPS water-level station |
| CO-OPS tidal-current predictions: max flood, max ebb, and slack events, or a 6-minute harmonic curve |
| Latest NDBC buoy conditions: waves, wind, sea-surface and air temperature, pressure |
| Observed ocean-current depth profile from an NDBC ADCP buoy |
| Sub-surface water-column readings (temperature, salinity, oxygen, and more) from an NDBC station |
Resources
Resource | Description |
| 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.
Agent skill
The published noaa-marine-guide skill carries the gates and conventions the tool descriptions don't: which station serves which tool, why a place-name search misses, the clock and direction differences between CO-OPS and NDBC, and what the server can't answer (forecasts, warnings, past buoy readings). MCP clients read it as skill://noaa-marine-guide/SKILL.md through the Skills extension. Plugin installs include it too.
Related MCP server: mcp-noaa-tides
Capability reference
noaa_marine_find_stations tool
Filters combine:
latitude+longitudewithradius_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), andtypes(tide,current,water_level,met,current_profile,water_quality, or the platform classbuoy);limitdefaults to 20, and a larger value is clamped to 200 with a notice (rejected whenNOAA_CLAMP_LIMITS=false);offsetpages past one pageEach station carries
source,capabilities,type, and for NDBC aplatform; CO-OPS rows addstate(state_derived: truewhen borrowed from the nearest state-bearing station within 25 km), a tideprediction_class(reference, orsubordinatewithreference_id), and currentbins[]giving each bin's number, depth in feet, and classtotal_foundcounts every match across pages;truncatedandnext_offsetappear while matches remain past the page; zero matches is a success withapplied_search, and the notice distinguishes a name/ID miss in the catalogs read from matches excluded by other filters.sourcesflags partial catalog reads. Total catalog failure issources_unavailable. Supplying justlatitudeorlongitudeisincomplete_coordinates
noaa_marine_get_tide_predictions tool
intervalhilo(default) or6min, up to 365 days; ten datums (MLLW, MHHW, MHW, MTL, MSL, MLW, DTL, NAVD, STND, CRD),time_zone(lst_ldt,gmt,lst), andunits(englishfeet,metricmeters)6minworks at reference stations only: a subordinate station fails assubordinate_no_6minnaming its reference station, and a Great Lakes station isno_predictions, pointing tonoaa_marine_get_water_levelOther failures:
invalid_date_range,date_range_exceeded,station_not_found,datum_unavailable(its hint names a datum to retry with, never the one just rejected), andupstream_throttled, a retryable CO-OPS HTTP 403 that clears in a couple of minutes
noaa_marine_get_water_level tool
interval6min(default, 31 days),hourlyorhigh_low(365 days), ordaily_mean(3,655 days, Great Lakes only, always in LST; a coastal station isgreat_lakes_only); eleven datums (MLLW, MHHW, MHW, MTL, MSL, MLW, NAVD, STND, IGLD, LWD, CRD)Observations carry
quality(p/v,6minonly),sigma(6minandhourly), andtype(H/HH/L/LL,high_lowonly); slots with no reading are dropped and counted ingaps_droppedPaired predictions at the matching cadence (none on
daily_mean), withpredictions_status(empty/unavailable) explaining an empty series;observed_extremesreportsmaxandminwith times across the whole matched range, including daily averages ondaily_mean. On6minandhourly,residual_summaryreportsmax_surgeandmax_drawdownacross all matched observed/predicted pairs, withmax_surge_atandmax_drawdown_atwhen the respective sign occurs. Failures:invalid_date_range,date_range_exceeded,station_not_found,datum_unavailable,no_gauge,future_window(begin_dateafter the station's current date in the requestedtime_zone),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), andupstream_throttled
noaa_marine_get_monthly_means tool
Up to 73,000 days per request; eleven datums (MLLW, MHHW, MHW, MTL, MSL, MLW, NAVD, STND, IGLD, LWD, CRD) in
englishfeet ormetricmeters; returned months fall within the inclusive local calendar months ofbegin_dateandend_dateOne row per month:
highest,lowest, the tidal datums (mhhw,mhw,msl,mtl,mlw,mllw,dtl), ranges (gt,mn,dhq,dlq), lunitidal intervals in hours (hwi,lwi), and theinferredcode verbatim; values CO-OPS leaves blank are omitted, so a Great Lakes station carries onlyhighest,msl, andlowestFailures:
invalid_date_range,date_range_exceeded,station_not_found,datum_unavailable,no_gauge,upstream_throttled, and for an empty windowverified_data_lagwhen it ends on or after the first day of the prior month,no_dataotherwise
noaa_marine_get_currents tool
intervalMAX_SLACK(default, up to 365 days) or6min(up to 31 days);binpicks a depth bin frombins[](CO-OPS chooses the default when omitted);unitsenglish(knots, feet) ormetric(cm/s, meters)For
6min, harmonic bins return the curve inpredictions[]. When CO-OPS answers with max flood, max ebb, and slack events, they appear inevents[]withpredictions: []and a notice. Every response echoesbinanddepth; rows carry aflood/ebb/slacktype, a non-negativespeed, and asdirectionthe station's mean bearing for that flowA bin the station lacks is
bin_unavailablelisting the ones it has; a station CO-OPS won't predict as events returns an empty list with its statement innotice, and one with no published predictions ispredictions_unavailable; alsoinvalid_date_range,date_range_exceeded,station_not_found,no_predictions, andupstream_throttled
noaa_marine_get_conditions tool
One NDBC
station_id; returns wind, waves, sea-surface and air temperature, dew point, and pressure in SI units, excepttide_ftandvisibility_nmiA 7-digit CO-OPS station ID also works when NDBC relays that gauge's met sensors under a station of its own:
station_idis then the NDBC station read, the requested ID comes back ascoops_station_id, andnoticenames bothEvery sensor field is nullable; each column block comes from the newest row within 90 minutes that carried it, waves reporting their own
waves_observed_atand any other older block named innotice. Failures:buoy_not_found(no observation file and no relay, a relay with no file, or several relays the met flag doesn't settle),no_sensor_data
noaa_marine_get_current_profile tool
One NDBC
station_id, found withtypes: ["current_profile"];bins[]runs shallowest first withdepth_m,direction_deg(true, flow-toward), andspeed_cm_s, the last two null when unreportedObserved ADCP measurements, not the CO-OPS predictions
noaa_marine_get_currentsreturns; failures areprofile_not_found(no ADCP file) andno_current_data
noaa_marine_get_ocean_observations tool
One NDBC
station_id, found with the catalog filtertypes: ["water_quality"];readings[]holds one row per depth with temperature, conductivity, salinity, dissolved oxygen (% and ppm), chlorophyll, turbidity, pH, and redox potentialMost stations report only temperature and salinity, and unreported values are
null; failures areobservations_not_found(no.oceanfile) andno_ocean_data
noaa-marine://station/{station_id} resource
station_idfromnoaa_marine_find_stations; returns that tool's station record asapplication/json, plusownerfor NDBC stations, with a 6-hourcacheHintstation_not_foundonly when both catalogs were read;source_unavailablewhen one could not be
Features
Built 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:
CO-OPS data and metadata APIs plus the NDBC active-stations catalog and
realtime2feeds (.txt,.adcp,.ocean), with station catalogs cached in memory for 6 hours; CO-OPS requests carryapplication=fromNOAA_APPLICATION_IDStation IDs belong to one source, with one exception: CO-OPS tide and water-level IDs are numeric (
9447130), CO-OPS current IDs alphanumeric (ACT4176), NDBC IDs mostly 5-character codes (46041) with some 7-digit numbers, and onlynoaa_marine_get_conditionsreads a CO-OPS ID, through its NDBC relayCO-OPS tools take
begin_date/end_dateasYYYYMMDDorYYYY-MM-DDand check the calendar and each tool's range ceiling before calling (invalid_date_range,date_range_exceeded); heights default to MLLW, a datum a station lacks isdatum_unavailablewith a hint naming a datum to retry with, never the rejected one, and a datum failure at a station that has never had a water-level gauge isno_gauge. When a water-level or monthly-mean request fails for a window entirely after a confirmed gauge retirement,no_datanames the removal date and points to nearby gaugesCO-OPS verifies
hourly,high_low,daily_mean, and monthly means once a month, for the prior month. When a recenthourly,daily_mean, or monthly series ends early,noticenames the last available observation or month and says later data may still await verificationCO-OPS answers a burst of requests with HTTP 403 for about two minutes; the CO-OPS data tools report it as a retryable
upstream_throttledwhose recovery names the wait
Agent-friendly output:
Count paging on the four CO-OPS series tools: a page is
limitrows fromoffset, defaulting to 50 (24 for monthly means) with a larger value clamped to 1,000 and a notice; a range of at mostlimitrows returns whole, and a longer one addsrows_matched,rows_returned,page_offset, andnext_offsetAll three NDBC data tools report
observed_atandobservation_age_minutes(signed elapsed minutes rounded down), and add a notice when the reading is over three hours old. NDBC capability filters identify candidate stations, and the selected feed may be unavailable. Missing meansnullor omitted, never fabricated: NDBCMMmarkers becomenull, blank CO-OPS values are dropped, andqualityis left out rather than defaultedDiscriminated fields: station
source(coops|ndbc), capabilitytype, NDBCplatform, andprediction_class, with datum and units echoed on every CO-OPS height response, so callers branch on data, not string parsing
Getting started
Connect to the hosted instance, no install:
claude mcp add --transport http noaa-marine-mcp-server https://noaa-marine.caseyjhand.com/mcpOr run it locally:
claude mcp add noaa-marine-mcp-server -- npx -y @cyanheads/noaa-marine-mcp-serverFor other clients, add this to the MCP config file:
{
"mcpServers": {
"noaa-marine-mcp-server": {
"command": "npx",
"args": ["-y", "@cyanheads/noaa-marine-mcp-server"]
}
}
}No API key: NOAA CO-OPS and NDBC are open data sources. For Docker or a source build, see Running the server.
Configuration
Variable | Description | Default |
| Courtesy identifier sent as |
|
| Clamp a |
|
| Transport: |
|
| HTTP server port. |
|
| HTTP bind address. |
|
| HTTP session mode: |
|
| Authentication: |
|
| Log level ( |
|
| Directory for log files (Node.js only). Relative paths resolve from the server package root. |
|
| Log each failed tool call's arguments and result (redacted by key name only), capped by |
|
| Enable OpenTelemetry. |
|
| OTLP base URL; traces and metrics export to | — |
| Opt-in OTLP log export endpoint; the base URL never enables it. | — |
See .env.example for the full list of optional overrides.
Running the server
Local development
Requires Bun v1.4.4 or higher (or Node.js v24+).
Clone and install:
git clone https://github.com/cyanheads/noaa-marine-mcp-server.git cd noaa-marine-mcp-server && bun install cp .env.example .env # all vars optionalBuild and run:
bun run rebuild bun run start:stdio # or bun run start:httpRun 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
docker run --rm -p 3010:3010 ghcr.io/cyanheads/noaa-marine-mcp-server:latestThe image defaults to HTTP transport, stateless session mode, and logs to /var/log/noaa-marine-mcp-server. To run it over stdio from an MCP client, use "command": "docker" with "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/noaa-marine-mcp-server:latest"]. To build locally, docker build -t noaa-marine-mcp-server .; OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
Path | Purpose |
|
|
|
|
| CO-OPS client: station catalog cache, data fetch, error classification, date-range validation, row paging, state resolution. |
| NDBC service: active-stations XML and |
| Haversine distance for proximity search and state resolution. |
| Eight tool definitions ( |
| Station metadata resource ( |
| Vitest tests mirroring |
| Design doc and directory tree. |
Development guide
See CLAUDE.md/AGENTS.md for development guidelines and architectural rules. The short version:
Handlers throw, framework catches — no
try/catchin tool logicUse
ctx.logfor request-scoped loggingRegister tools and resources in
src/index.tsdirectly (no barrels for this server)Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
NDBC
MMvalues must normalize tonull, not be passed through as strings
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run testLicense
Apache-2.0 — see LICENSE for details.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
NOAA Tides & Currents MCP.
Tides MCP — NOAA Tides and Currents data
Search NOAA climate stations and datasets, fetch historical weather observations.
Marine MCP — wraps marine-api.open-meteo.com (free, no auth)
Related MCP Servers
- AlicenseAqualityBmaintenanceThis is an MCP (Model Context Protocol) server that provides tools for interacting with the NOAA Tides and Currents API.2568 npm6MIT
- AlicenseNot gradedqualityCmaintenanceMCP server that provides access to NOAA Tides & Currents data including tide predictions, water levels, currents, and meteorological observations.313 npmMIT
- FlicenseNot gradedqualityDmaintenanceMinimal MCP server exposing tools for kitesurfing forecasts (wind and waves via Open-Meteo) and tide predictions (via NOAA CO-OPS).-
- FlicenseNot gradedqualityCmaintenanceMCP server that provides tide predictions, station lookup, and tidal event alerts for US coastal locations using live NOAA data.-