Skip to main content
Glama
nes-lter-mcp

nes-lter-mcp

by blongworth

nes-lter-mcp

An MCP server for the NES-LTER API. It exposes efficient discovery, batch CTD queries, cruise datasets, schema discovery, and provenance-rich results.

Example prompt to an MCP client:

show me the profile for oxygen for station L8 for winter 2025

The server can satisfy this with query_ctd(station="L8", variable="oxygen", season="winter", year=2025).

Install / run

uv sync
uv run nes-lter-mcp

For MCP clients, configure the command as:

uv --directory /path/to/nes-lter-mcp run nes-lter-mcp

Related MCP server: erddap-mcp-demo

Tools

  • list_stations() — station catalog with coordinates, depth, and provenance.

  • find_cruises(year?, season?, cruise_type?) — full CTD cruise catalog when unfiltered, or cruises filtered by year/season/type. Results preserve the API fields from /api/ctd/cruises/all.

  • list_casts(cruise_name, station?, max_distance_km?, offset?, limit?) — bounded cast discovery.

  • get_cast_profile(cruise_name, cast_number, variable, include_points?, depth_min?, depth_max?) — one profile.

  • query_ctd(...) — batch CTD query with surface_definition (shallowest_valid, mean_0_5m, mean_0_10m, or mean_0_20m) and optional depth_bin_m.

  • get_station_profile(...) — compatibility wrapper around query_ctd.

  • plot_ctd_profiles(...) — plotting-ready CTD records plus a Vega-Lite vertical-profile specification. Supports one or multiple variables, and aggregation=none, cruise_mean, or period_mean.

  • list_dataset_rows(dataset, cruise_name, ...) — underway, bottles, bottle summaries, nutrients, HPLC, chlorophyll, events, and event history.

  • query_underway(variable, cruise_name?, year?, season?, cruise_type?, aggregation?, start_time?, end_time?, ...) — vessel-aware underway query (wind, temperature, salinity, position, speed, etc.) across Endeavor, Neil Armstrong, Atlantis, Hugh R. Sharp, and Atlantic Explorer's differing column conventions, with aggregation=cruise_mean (default), period_mean, or none. Directional variables (wind_direction, heading, course_over_ground) use a circular mean.

  • get_cruise_track(cruise_name) — track URL and provenance.

  • get_cruise_track_data(cruise_name) — GeoJSON cruise track built from cast positions.

  • make_station_map(stations?, cruises?, include_tracks?) — station GeoJSON and a Vega-Lite map specification.

  • get_dataset_schema(dataset, cruise_name) — available columns or API schema.

  • resolve_variable(name, cruise_name?, dataset?) — friendly-name resolution for CTD or underway variables (underway resolution is vessel-aware when cruise_name is given).

The nes-lter://stations and nes-lter://variable-aliases MCP resources provide stable reference context.

Plot viewer

web/nes-lter-vega-plot.js defines a reusable <nes-lter-vega-plot> browser component. The component accepts the vega_lite object returned by plot_ctd_profiles:

<script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
<script type="module" src="./nes-lter-vega-plot.js"></script>
<nes-lter-vega-plot id="plot"></nes-lter-vega-plot>
<script>
  document.querySelector("#plot").spec = plot_result.vega_lite;
</script>

To view the included Spring 2025 L8 oxygen demonstration:

cd web
python3 -m http.server 8080

Open http://localhost:8080/demo.html. The nes-lter://vega-lite-viewer MCP resource exposes the component source to compatible clients.

Common variables include oxygen, temperature, salinity, density, fluorescence, and par. Raw CTD column names like sbox0mm_kg are also accepted.

By default, winter 2025 is interpreted meteorologically as Dec 2024 through Feb 2025.

Configuration

Set NES_LTER_API_BASE_URL to override the API base URL. Defaults to https://nes-lter-api.whoi.edu.

NES_LTER_CACHE_TTL controls the in-memory cache duration in seconds (default 900), and NES_LTER_MAX_CONCURRENCY controls concurrent API requests (default 8).

For efficient interaction, use query_ctd(..., include_points=false) first, then request full points for selected casts. For plots, prefer plot_ctd_profiles because it returns compact plotting data and a Vega-Lite spec. For maps, use GeoJSON from make_station_map or get_cruise_track_data. All data responses include source_url and retrieved_at.

Query benchmarks

benchmarks/bench_queries.py compares MCP tool latency with an equivalent direct NES-LTER API workflow. It records elapsed time, response size, row count, errors, median, and p95 summaries as JSON Lines:

uv run python benchmarks/bench_queries.py --warmups 2 --repetitions 10 --mode both --output benchmark-results.jsonl

Use --mode mcp or --mode direct to run one side only. Add --mode all for the optional bare-LLM comparison:

export LLM_API_KEY=...
export LLM_MODEL=gpt-4o-mini
export LLM_INPUT_USD_PER_1M=0
export LLM_OUTPUT_USD_PER_1M=0
uv run python benchmarks/bench_queries.py --mode all --repetitions 10 --output benchmark-results.jsonl

The bare-LLM condition receives only the API base URL and a few endpoint paths. The model produces an API plan without MCP tools, then the harness executes the allowlisted direct API workflow and returns the same profile rows. Its elapsed time therefore includes model planning plus API retrieval. LLM rows include total latency, separate model/API timings, token usage, and estimated cost when both pricing environment variables are set.

--mode all also includes a codex condition. It launches codex exec --json in an isolated temporary directory with user configuration, rules, and MCP settings excluded. Codex must retrieve the same structured data using read-only API requests. Use --codex-sandbox read-only by default; the Codex CLI must be installed and authenticated. Offline checks are in tests/test_bench_queries.py.

To plot multiple variables together, pass variables=["sbeox0ml_l", "t090c"]; the Vega-Lite result overlays them on one chart with a shared depth axis and independent x-scales, which avoids conflating their units. Use width and height to control the returned chart dimensions.

Available Tools

10 tools
find_cruisesB

Find cruises by year, meteorological season, and type.

Inputs: year (required), season, refresh, cruise_type. Returns: object

get_cast_profileC

Get one CTD profile, optionally compact and depth-filtered.

Inputs: cast_number (required), cruise_name (required), variable, depth_max, +2 more. Returns: object

get_cruise_trackC

Return the API's cruise track resource URL for client-side map use.

Inputs: cruise_name (required). Returns: object

get_dataset_schemaC

Infer available columns and example values for a cruise dataset.

Inputs: dataset (required), cruise_name (required). Returns: object

get_station_profileC

Compatibility wrapper for station profile lookup; use query_ctd for compact results.

Inputs: station (required), year, limit, season, +5 more. Returns: object

list_castsC

List casts, optionally restricted to the nearest standard station.

Inputs: cruise_name (required), limit, offset, refresh, +2 more. Returns: object

list_dataset_rowsC

Fetch and filter underway, bottles, nutrients, HPLC, chlorophyll, or event rows.

Inputs: dataset (required), cruise_name (required), limit, offset, +5 more. Returns: object

list_stationsB

List standard NES-LTER stations with coordinates and station metadata.

Inputs: refresh. Returns: object

query_ctdC

Batch CTD query. Returns compact cast summaries unless include_points is true.

Inputs: year, limit, season, refresh, +8 more. Returns: object

resolve_variableC

Resolve a friendly variable name to candidate API columns.

Inputs: name (required), dataset, cruise_name. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3/5.0

    Scored across 10 tools

    Disambiguation3/5

    Most tools are distinct (stations, cruises, casts, CTD, track, schema, variable resolution). However, get_cast_profile vs query_ctd overlap since both return CTD profiles, and get_station_profile is explicitly a compatibility wrapper that duplicates query_ctd purpose—creating real misselection risk.

    Naming Consistency4/5

    Most tools follow a consistent verb_noun pattern (list_, find_, get_, query_). Minor deviations exist with get_cruise_track (returns a URL rather than track data) and resolve_variable (verb_object but semantically different), but overall patterns are predictable.

    Tool Count4/5

    Ten tools is a reasonable count for an oceanographic data server covering stations, cruises, CTD casts, datasets, schema, and variable resolution. Slightly heavy but each tool addresses a distinct lookup/query need within the domain.

    Completeness5/5

    The surface covers the full NES-LTER data lifecycle: station discovery, cruise search, cast listing, profile retrieval, batch queries, dataset row fetching, schema introspection, and variable resolution. The get_station_profile wrapper suggests legacy coverage, and no obvious dead ends exist for a data-retrieval server.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      D
      maintenance
      Access oceanographic and environmental data from 63+ ERDDAP servers worldwide through natural language queries. Search datasets, retrieve metadata, and download scientific data for climate research, marine biology, and coastal management.
      18
      -
    • A
      license
      A
      quality
      D
      maintenance
      Enables natural language interaction with rasdaman databases, allowing users to list coverages, describe datacubes, and execute WCPS queries.
      5
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables querying NYC Open Data datasets using natural language, with tools to search datasets, run SoQL queries, and retrieve metadata, all without an API key.
      200 npm
      MIT