mcp-server-usgs-nationalmap
Provides tools for querying USGS NHDPlus HR hydrography data via ArcGIS REST MapServer, including streams, rivers, lakes, gages, water features, and watershed boundaries.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-server-usgs-nationalmap Find the watershed for lat 40.7128, lon -74.0060"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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/MapServerSource: 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 |
| NetworkNHDFlowline (3), optional NonNetworkNHDFlowline (4) | Streams/rivers/canals with stream order, drainage area, slope, elevation, and EROM modeled flow ( |
| NHDWaterbody (9), NHDArea (8) | Lakes, ponds, reservoirs, swamps, and areal water features (wide rivers, bays, rapids, dams). |
| NHDPlusGage (0) | Stream gages with NWIS linkage ( |
| NHDPoint (2), NHDLine (7) | Springs, waterfalls, dams/weirs, gates, levees, wells, etc. |
| WBDHU12 (12) | HUC12 watershed name, 12-digit code, downstream HUC ( |
| any of the above | Count-only guardrail; check result size before a |
| 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
PolygonorBoundingBoxto capture nearby features — a bare point rarely coincides exactly with a point/line feature.Feature codes:
ftype/fcodeare integers; results addftype_label/fcode_label(e.g.46006 → "Stream/River: Perennial").hutypeandpurpcodeare similarly labeled.Drainage-area units differ: gages report
dasqmiin square miles; flowlines/watersheds useAreaSqKm/TotalDrainageAreaSqKm.Pagination: each layer caps at 2000 records/request; results paginate automatically and set
truncated: trueif a cap is hit.Field names on the service are lowercase; the CamelCase forms are aliases.
Setup
uv syncRun
# 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.appProject 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.pyPossible 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 usinghydroseq,levelpathi,uphydroseq/dnhydroseq, andfromnode/tonode. Enables "what's upstream of this point?" and pollutant/ flow-path questions.Watershed routing traversal: follow
tohucfrom 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/nhdplusidrather than by geometry.Gage cross-walk to NWIS: resolve
sourceid/gageidmato live USGS NWIS streamflow observations.Attribute filtering: expose a
wherefilter (e.g. minimum stream order, perennial-only) on thefind_*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 toolshydro_count_featuresCount NHDPlus HR Features at GeometryARead-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 GagesARead-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 FeaturesARead-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 FeaturesARead-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)ARead-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 WatershedARead-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 LayersARead-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
Scored across 7 tools
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.
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.
Seven tools is well-scoped for a hydrography-focused server, covering query, discovery, and utility operations without redundancy. Each tool earns its place.
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
Related MCP Connectors
Geospatial AI MCP server — satellite imagery, embeddings, weather, GNS governance
Hosted MCP server for live public-data APIs and Skills for AI agents.
MCP server for OpenAI API (chat completions, image generation, embeddings) via AceDataCloud
Related MCP Servers
- AlicenseAqualityDmaintenanceAn MCP server that gives LLMs access to geographic data conversion tools, enabling transformations between different formats like WKT, GeoJSON, CSV, TopoJSON, and KML, as well as performing reverse geocoding.940 npm18MIT
- AlicenseNot gradedqualityFmaintenanceAn 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 npm4MIT
- AlicenseAqualityDmaintenanceAn MCP server that gives AI agents clean, token-efficient access to US civic & property data — geocoding, census tracts, Opportunity Zones, ACS demographics, and FEMA flood zones — sourced entirely from free federal open data.578 npm1MIT
- AlicenseAqualityDmaintenanceAn 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.64 npmMIT