Skip to main content
Glama
mcp-server-usgs-nationalmap

mcp-server-usgs-nationalmap

by GSA-TTS

mcp-server-usgs-nationalmap

An MCP (Model Context Protocol) server that wraps the USGS NHDPlus High Resolution (NHDPlus HR) ArcGIS REST MapServer, letting an LLM query official U.S. hydrography — streams, rivers, lakes, stream gages, water features, and watershed boundaries — for a point or arbitrary GeoJSON geometry.

  • Service: https://hydro.nationalmap.gov/arcgis/rest/services/NHDPlus_HR/MapServer

  • Source: USGS — National Hydrography Dataset Plus High Resolution

  • Auth: none (public service)

  • Package: hydro_mcp

What the dataset is

NHDPlus HR is a nationally seamless, routed hydrography network built from the high-resolution NHD, the Watershed Boundary Dataset (WBD), and 3DEP elevation. The MapServer exposes 13 layers; this server wraps the ones most useful for site/hydrology analysis.

Related MCP server: geocontext

Tools

All geometry tools accept either a lat + lon pair (convenience) or a GeoJSON geometry string. Supported GeoJSON types: Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, plus a BoundingBox shorthand {"type": "BoundingBox", "bbox": [minLon, minLat, maxLon, maxLat]}. All coordinates are WGS84 decimal degrees. Feature geometry is not returned (payloads stay small); results include provenance.

Tool

Layers

Purpose

hydro_find_waterways

NetworkNHDFlowline (3), optional NonNetworkNHDFlowline (4)

Streams/rivers/canals with stream order, drainage area, slope, elevation, and EROM modeled flow (qama, cfs) / velocity (vama, fps). verbose=True returns all ~80 attributes; include_non_network=True adds isolated flowlines.

hydro_find_waterbodies

NHDWaterbody (9), NHDArea (8)

Lakes, ponds, reservoirs, swamps, and areal water features (wide rivers, bays, rapids, dams). feature_class = waterbody | area | both.

hydro_find_gages

NHDPlusGage (0)

Stream gages with NWIS linkage (sourceid/sourceagency) and drainage area (dasqmi, square miles).

hydro_find_water_features

NHDPoint (2), NHDLine (7)

Springs, waterfalls, dams/weirs, gates, levees, wells, etc. feature_class = point | line | both.

hydro_identify_watershed

WBDHU12 (12)

HUC12 watershed name, 12-digit code, downstream HUC (tohuc), type, area, states.

hydro_count_features

any of the above

Count-only guardrail; check result size before a find_*. layer = waterways | waterbodies | areas | gages | points | lines | watershed.

hydro_list_layers

service root

Discover all 13 layers (id, name, geometry type).

Conventions & gotchas

  • Coordinates: WGS84 lat/lon. Convert addresses/place names to coordinates first.

  • Point/line layers (gages, water features): pass a Polygon or BoundingBox to capture nearby features — a bare point rarely coincides exactly with a point/line feature.

  • Feature codes: ftype/fcode are integers; results add ftype_label / fcode_label (e.g. 46006 → "Stream/River: Perennial"). hutype and purpcode are similarly labeled.

  • Drainage-area units differ: gages report dasqmi in square miles; flowlines/watersheds use AreaSqKm / TotalDrainageAreaSqKm.

  • Pagination: each layer caps at 2000 records/request; results paginate automatically and set truncated: true if a cap is hit.

  • Field names on the service are lowercase; the CamelCase forms are aliases.

Setup

uv sync

Run

# stdio (Claude Desktop, Claude Code, local MCP clients)
uv run python -m hydro_mcp.app

# HTTP (set PORT or DATABRICKS_APP_PORT); served at /mcp, health at /health
PORT=8000 uv run python -m hydro_mcp.app

Project structure

src/hydro_mcp/
├── app.py            # FastMCP init, instructions, transport wiring
├── routes.py         # /health endpoint
├── models.py         # constants, layer IDs, curated field lists, code label maps, dataclasses
├── utils.py          # ArcGIS HTTP client (stdlib urllib), GeoJSON->Esri, code translation
└── tools/            # one file per tool
    ├── __init__.py           # register_tools(mcp)
    ├── find_waterways.py
    ├── find_waterbodies.py
    ├── find_gages.py
    ├── find_water_features.py
    ├── identify_watershed.py
    ├── count_features.py
    └── list_layers.py

Possible future add-ons

The current tools are geometry-intersection queries. NHDPlus HR is a fully routed network, which opens up several higher-value tools not yet implemented:

  • Upstream/downstream network tracing (hydro_trace_upstream / hydro_trace_downstream): walk the flowline network from a starting reach using hydroseq, levelpathi, uphydroseq/dnhydroseq, and fromnode/tonode. Enables "what's upstream of this point?" and pollutant/ flow-path questions.

  • Watershed routing traversal: follow tohuc from a starting HUC12 down the drainage chain (or accumulate the upstream set) to build drainage lineages.

  • Catchment lookup (NHDPlusCatchment, layer 10): return the elevation-based incremental catchment polygon(s) for a location, and join to flowline VAAs via nhdplusid.

  • Reach-code / NHDPlusID lookup tools: fetch a specific reach or feature by reachcode / permanent_identifier / nhdplusid rather than by geometry.

  • Gage cross-walk to NWIS: resolve sourceid/gageidma to live USGS NWIS streamflow observations.

  • Attribute filtering: expose a where filter (e.g. minimum stream order, perennial-only) on the find_* tools for large-area queries.

Data source & attribution

USGS The National Map — National Hydrography Dataset Plus High Resolution (NHDPlus HR). Public domain. See https://www.usgs.gov/national-hydrography/national-hydrography-dataset.

Available Tools

7 tools
hydro_count_featuresCount NHDPlus HR Features at GeometryA
Read-onlyIdempotent

Return ONLY how many features of a layer intersect a point or geometry (size check).

Use before the find_* tools to gauge result size without fetching full attributes -- useful for large polygons or densely mapped areas. A count of 0 means the geometry is outside coverage or has no features of that type.

Returns: { "count": int, "layer": str, "query_geometry": {"type": str, "crs_epsg": int}, "provenance": {...} }

Error responses:

  • "Error: supply either lat+lon or geometry, not both (or neither)"

  • "Error: unsupported geometry type ..."

  • "Error: NHDPlus HR request failed: ..."

Inputs: layer (required), lat, lon, geometry. Returns: object

hydro_find_gagesFind NHDPlus Stream GagesA
Read-onlyIdempotent

Find NHDPlus stream gages intersecting a geometry (NHDPlusGage, layer 0).

Each gage links to NWIS via sourceid + sourceagency and reports the monitored drainage area (dasqmi, in SQUARE MILES) and reach location.

Use when: "What stream gages are in this watershed / bounding box?", "Which gage monitors this river reach?"

Because gages are point features, supply a Polygon or BoundingBox to find gages within an area (a bare point rarely coincides exactly with a gage).

Returns: { "finding": str, "count": int, "truncated": bool, "features": [...], # station_nm, sourceid, sourceagency, dasqmi (SQ MILES), ... "provenance": {...}, "query_geometry": {"type": str, "crs_epsg": int} }

NOTE: dasqmi is drainage area in SQUARE MILES (not sq km).

Error responses:

  • "Error: supply either lat+lon or geometry, not both (or neither)"

  • "Error: unsupported geometry type ..."

  • "Error: NHDPlus HR request failed: ..."

Inputs: lat, lon, geometry. Returns: object

hydro_find_waterbodiesFind NHD Waterbodies & Areal Water FeaturesA
Read-onlyIdempotent

Find NHD waterbodies (lakes, ponds, reservoirs, swamps) and areal water features.

Queries NHDWaterbody (layer 9) and/or NHDArea (layer 8) polygons intersecting the geometry. Both carry gnis_name, ftype/fcode, areasqkm, and elevation.

Use when: "Is there a lake within this parcel?", "What reservoirs intersect this bounding box?", "Name the waterbody at this point."

Returns: { "finding": str, "waterbody": {count, truncated, features, provenance}, # if requested "area": {count, truncated, features, provenance}, # if requested "query_geometry": {"type": str, "crs_epsg": int} }

ftype/fcode are integer codes; ftype_label/fcode_label give readable names.

Error responses:

  • "Error: supply either lat+lon or geometry, not both (or neither)"

  • "Error: unsupported geometry type ..."

  • "Error: NHDPlus HR request failed: ..."

Inputs: lat, lon, geometry, feature_class. Returns: object

hydro_find_water_featuresFind NHD Point & Line Water FeaturesA
Read-onlyIdempotent

Find NHD point & line water features (springs, waterfalls, dams, gates, levees, ...).

Queries NHDPoint (layer 2) and/or NHDLine (layer 7) intersecting the geometry. Both carry gnis_name and ftype/fcode; point features also carry reachcode.

Use when: "Are there any waterfalls or springs near this point?", "What dams/weirs cross this river within the bounding box?"

Because these are point/line features, supply a Polygon or BoundingBox to find features within an area.

Returns: { "finding": str, "point": {count, truncated, features, provenance}, # if requested "line": {count, truncated, features, provenance}, # if requested "query_geometry": {"type": str, "crs_epsg": int} }

ftype/fcode are integer codes; ftype_label/fcode_label give readable names.

Error responses:

  • "Error: supply either lat+lon or geometry, not both (or neither)"

  • "Error: unsupported geometry type ..."

  • "Error: NHDPlus HR request failed: ..."

Inputs: lat, lon, geometry, feature_class. Returns: object

hydro_find_waterwaysFind NHD Waterways (Flowlines)A
Read-onlyIdempotent

Find NHD flowlines (streams, rivers, canals, artificial paths) intersecting a geometry.

Uses NHDPlus HR NetworkNHDFlowline (layer 3), which carries stream order, total drainage area (totdasqkm), slope, smoothed elevation, and EROM mean-annual flow (qama, cfs) / velocity (vama, fps) estimates.

Use lat+lon when: "What river runs through 45.5, -122.6?" Use geometry when: "What streams cross this project polygon / pipeline corridor?" Set include_non_network=True to also capture isolated (non-routed) flowlines. Set verbose=True for the full attribute set (network IDs + full EROM suite).

Returns: { "finding": str, # plain-language summary "count": int, "truncated": bool, # true if capped at max_records "features": [...], # flowline attributes (+ ftype_label / fcode_label) "non_network": {...}, # present only when include_non_network=True "provenance": {...}, "query_geometry": {"type": str, "crs_epsg": int} }

ftype/fcode are integer codes; ftype_label/fcode_label give readable names.

Error responses:

  • "Error: supply either lat+lon or geometry, not both (or neither)"

  • "Error: unsupported geometry type ..."

  • "Error: NHDPlus HR request failed: ..."

Inputs: lat, lon, verbose, geometry, +1 more. Returns: object

hydro_identify_watershedIdentify HUC12 WatershedA
Read-onlyIdempotent

Identify the HUC12 watershed(s) containing/intersecting a geometry (WBDHU12, layer 12).

Returns the watershed name, 12-digit HUC code, downstream HUC (tohuc, for routing), hydrologic-unit type (hutype + label), area, and states.

Use when: "What watershed is this point in?", "Which HUC12 does this river reach drain to?", "List the sub-watersheds this project boundary spans."

A point returns the single containing HUC12; a polygon/bbox may intersect several.

Returns: { "finding": str, "count": int, "truncated": bool, "features": [...], # huc12, name, tohuc, hutype (+ hutype_label), areasqkm, states, ... "provenance": {...}, "query_geometry": {"type": str, "crs_epsg": int} }

Error responses:

  • "Error: supply either lat+lon or geometry, not both (or neither)"

  • "Error: unsupported geometry type ..."

  • "Error: NHDPlus HR request failed: ..."

Inputs: lat, lon, geometry. Returns: object

hydro_list_layersList NHDPlus HR Service LayersA
Read-onlyIdempotent

List the layers available in the NHDPlus HR service (id, name, geometry type).

Use when: You need to discover available layers beyond those wrapped by the find_* tools (e.g. NHDPlusCatchment, NHDPlusSink, FlowDirection, NHDPlusBoundaryUnit) or to confirm current layer IDs.

Returns: { "layers": [{"id": int, "name": str, "geometry_type": str}, ...], "provenance": {...} }

Error responses:

  • "Error: NHDPlus HR request failed: ..." -- network or service error

Inputs: no named parameters. Returns: object

Tool Schema Changelog

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

Loading tool schema changelog…

    TDQS

    A4.6/5.0

    Scored across 7 tools

    Disambiguation5/5

    Each tool targets a distinct NHD feature type or utility: flowlines, waterbodies/areas, gages, point/line features, watersheds, counting, and layer listing. Descriptions clearly separate geometry types and layer IDs, so no two tools overlap in purpose.

    Naming Consistency5/5

    All tools follow a consistent hydro_<verb>_<noun> pattern in snake_case. The verbs (find, identify, count, list) appropriately reflect each action, and the noun components are unique and descriptive.

    Tool Count5/5

    Seven tools is well-scoped for a hydrography-focused server, covering query, discovery, and utility operations without redundancy. Each tool earns its place.

    Completeness4/5

    The core NHD feature layers are covered (flowlines, waterbodies, gages, point/line features, HUC12), and count/list tools aid discovery. However, hydro_list_layers exposes additional layers like catchments and sinks that cannot be queried directly, leaving a minor gap.

    Maintenance

    ActivityInactive
    ResponsivenessLimited data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      F
      maintenance
      An experimental MCP server providing spatial context for LLMs by interfacing with French Geoplateforme services. It enables tasks such as geocoding, altitude lookups, and querying administrative, cadastral, or urban planning data.
      10 npm
      4
      MIT
    • A
      license
      A
      quality
      D
      maintenance
      An MCP server that gives AI assistants direct access to the FEMA National Flood Hazard Layer (NFHL) for flood zone lookups, FIRM panel information, and flood map data in the United States.
      6
      4 npm
      MIT