<div align="center"> <h1>@cyanheads/noaa-marine-mcp-server</h1> <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> <div>7 Tools • 1 Resource</div> </p> </div>
<div align="center">
      
</div>
<div align="center">
  

</div>
<div align="center">
Public Hosted Server: https://noaa-marine.caseyjhand.com/mcp
</div>
---
Tools
Seven tools covering the full US marine operational workflow — station discovery, tide predictions, observed water levels, tidal current predictions and profiles, and live offshore buoy conditions:
| Tool | Description | |:-----|:------------| | noaa_marine_find_stations | Find CO-OPS tide/water-level/current stations and NDBC buoys near a location or by name/state. Required first step to resolve place names or coordinates to station IDs. | | noaa_marine_get_tide_predictions | High/low tide predictions for a CO-OPS tide station over a date range. Supports 6-minute interval output and multiple datums (defaults to MLLW — US nautical chart standard). | | noaa_marine_get_water_level | Observed water level (real-time or historical) for a CO-OPS station, paired with predictions to compute storm surge or anomalous drawdown. | | noaa_marine_get_currents | Tidal current predictions for a CO-OPS current station: max flood/ebb speeds, slack times, and directions. Defaults to MAX_SLACK (practical passage-planning view). | | noaa_marine_get_conditions | Live marine conditions from an NDBC buoy: wave height/period/direction, wind, sea-surface temp, air temp, and barometric pressure. | | noaa_marine_get_current_profile | Observed ocean-current depth profile from an NDBC ADCP buoy: speed and direction at each depth bin. Distinct from noaa_marine_get_currents, which is a CO-OPS tidal-current forecast. | | noaa_marine_get_ocean_observations | Sub-surface water-column observations from an NDBC station: water temperature, salinity, dissolved oxygen, chlorophyll, turbidity, and pH at each reported depth. The water-column counterpart to noaa_marine_get_conditions (surface weather and sea state). |
noaa_marine_find_stations
Unified station discovery across CO-OPS (3,450+ tide/water-level stations, 4,430+ current stations) and NDBC (1,354+ active buoys worldwide).
- Filter by proximity (latitude/longitude + radius), name substring, state/territory, source (CO-OPS vs NDBC), or
types: data capabilities (tide, current, water_level, met, current_profile) or NDBC platform class (buoy) - Returns a unified station list with source, coordinates, distance, data capabilities, and — for NDBC — the physical platform class (buoy, fixed, oilrig, dart, tao, usv, other)
- Station lists are cached in-memory (6-hour TTL) — first call after startup may be slightly slower
- Required first step: CO-OPS and NDBC use non-overlapping ID systems; guessing a station ID reliably fails
---
noaa_marine_get_tide_predictions
CO-OPS MLLW tide predictions for planning tidal windows.
- High/low events (default) or 6-minute continuous curve
- Eight datums: MLLW (default, US nautical chart), MHHW, MSL, MTL, MHW, MLW, CD, STND
- Time zone options: local standard/daylight (default), GMT, local standard only
- Units: English (feet, default) or metric (meters)
- Maximum date range: 1 year per request (typed error
date_range_exceededfor longer ranges)
---
noaa_marine_get_water_level
Observed water level vs. predicted — the storm surge view.
- 6-minute observed water level readings with quality flags
- Paired tide predictions fetched in parallel (failure degrades gracefully — observed levels still returned)
- Optional residual summary: max surge and max drawdown when both series are present
- Maximum date range: 31 days per request for 6-minute data
---
noaa_marine_get_currents
CO-OPS tidal current predictions for passage planning.
- MAX_SLACK interval (default): max flood, max ebb, and slack events only — the actionable view for transiting inlets and channels
- 6-minute interval: full continuous current curve for charting or integration
- Current station IDs use alphanumeric format (e.g.,
ACT4176), distinct from numeric tide station IDs — usefind_stationswithtypes: ["current"]to discover them
---
noaa_marine_get_conditions
Live NDBC buoy observations (most recent ~45 days, updated every 10 minutes).
- Wave height (m), dominant and average period (sec), mean wave direction
- Wind speed and gust (m/s), wind direction
- Sea-surface temperature, air temperature, dew point (°C)
- Barometric pressure (hPa)
- All sensor fields nullable (
nullwhen buoy sensor did not report — normal for offshore buoys) - All values in SI units except
TIDE(feet) andVIS(nautical miles), which are rarely populated at offshore buoys
---
noaa_marine_get_current_profile
Observed ocean-current depth profile from an NDBC ADCP buoy — the most recent measurement at each depth bin.
- Depth (m), direction (degrees true, the direction the current flows toward), and speed (cm/s) per bin
- Distinct from
noaa_marine_get_currents: this is an NDBC observed acoustic-Doppler measurement, not a CO-OPS tidal-current prediction - Most NDBC stations serve no ADCP profile — use
find_stationswithsource="ndbc"andtypes: ["current_profile"]to discover the ones that do - Direction or speed is
nullfor a bin when NDBC did not report that component
noaa_marine_get_ocean_observations
Sub-surface water-column observations from an NDBC station — the most recent reading at each reported depth.
- Water temperature (°C), conductivity (mS/cm), salinity (psu), dissolved oxygen (% saturation and ppm), chlorophyll (µg/l), turbidity (FTU), pH, and redox potential (mV) per depth
- The water-column counterpart to
noaa_marine_get_conditions: this reports what the water is doing below the surface, that one reports surface weather and sea state - Sensor coverage is sparse — most stations report only temperature and salinity; any value the station did not report comes back
null, never a fabricated zero - Sub-surface sensors are on only a subset of NDBC stations and carry no station-catalog flag, so there is no capability filter — call it on candidate
source="ndbc"station IDs and expect theobservations_not_founderror on the many stations that serve no ocean file
Resources and prompts
| Type | Name | Description | |:-----|:-----|:------------| | Resource | noaa-marine://station/{station_id} | Metadata for a CO-OPS or NDBC station by ID: name, coordinates, source, data capabilities, state, and — for NDBC — physical platform class. |
All resource data is also reachable via tools. Use noaa_marine_find_stations to discover station IDs before accessing the resource.
Features
Built on @cyanheads/mcp-ts-core:
- Declarative tool and resource definitions — single file per primitive, framework handles registration and validation
- Unified error handling — handlers throw, framework catches, classifies, and formats with typed error contracts
- Structured logging with optional OpenTelemetry tracing
- STDIO and Streamable HTTP transports
- Pluggable auth:
none,jwt,oauth
NOAA-specific:
- In-memory station cache (6-hour TTL) for CO-OPS and NDBC station lists — discovery is fast after first startup
- CO-OPS and NDBC integrated in a unified station model —
find_stationsfans out across both sources in parallel - NDBC fixed-width text parser:
MM(missing sensor data) normalized tonull, not passed through as strings - Paired water-level + prediction fetches for storm surge residual computation
- CO-OPS
application=courtesy parameter sent on every request (configurable viaNOAA_APPLICATION_ID)
Agent-friendly output:
- Datum echoed on every tide/water-level response — agents can state units and reference correctly without assumptions
total_foundonfind_stationsshows count beforelimitslice so agents know whether to re-query- All NDBC sensor fields explicitly nullable with per-field unit documentation — agents don't fabricate missing readings
- Typed station source (
coops|ndbc) on every station record, plus a data-capabilitytypeand (NDBC only) aplatformclass where applicable — agents can branch on data, not string parsing
Getting started
No API key required. Both NOAA CO-OPS and NDBC are open, keyless data sources.
Public Hosted Instance
Connect directly via Streamable HTTP — no install, no API key:
{
"mcpServers": {
"noaa-marine": {
"type": "streamable-http",
"url": "https://noaa-marine.caseyjhand.com/mcp"
}
}
}
Self-hosted / local
Add the following to your MCP client configuration file:
{
"mcpServers": {
"noaa-marine": {
"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": {
"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": {
"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
Prerequisites
- Bun v1.3.14 or higher (or Node.js v24+).
- No external API keys needed — NOAA CO-OPS and NDBC are fully open.
Installation
- Clone the repository:
git clone https://github.com/cyanheads/noaa-marine-mcp-server.git
- Navigate into the directory:
cd noaa-marine-mcp-server
- Install dependencies:
bun install
- Configure environment:
cp .env.example .env
# edit .env if needed (all vars optional)
Configuration
| 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 | Port for HTTP server. | 3010 | | MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none | | MCP_LOG_LEVEL | Log level (RFC 5424). | info | | LOGS_DIR | Directory for log files (Node.js only). | <project-root>/logs | | OTEL_ENABLED | Enable OpenTelemetry instrumentation. | false |
See .env.example for the full list of optional overrides.
Running the server
Local development
- 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
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.
Project structure
| Path | Purpose | |:-----|:--------| | src/index.ts | createApp() entry point — registers tools, resource, and initializes services. | | src/config/ | NOAA_APPLICATION_ID env var parsing with Zod. | | src/services/coops/ | CO-OPS Tides & Currents API client: station list cache, data fetch, error detection. | | src/services/ndbc/ | NDBC buoy service: active stations XML parser, realtime text parser. | | src/mcp-server/tools/ | Five tool definitions (*.tool.ts). | | src/mcp-server/resources/ | Station metadata resource (noaa-marine-station.resource.ts). | | tests/ | Vitest tests mirroring src/. | | docs/ | 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 logic - Use
ctx.logfor request-scoped logging - Register 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 and pull requests are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
License
Apache-2.0 — see LICENSE for details.











