usgs-mcp 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,45 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ *.egg
7
+ dist/
8
+ build/
9
+ *.whl
10
+
11
+ # Virtual environments
12
+ .venv/
13
+ venv/
14
+ env/
15
+
16
+ # Testing
17
+ .pytest_cache/
18
+ .coverage
19
+ htmlcov/
20
+ .tox/
21
+
22
+ # IDE
23
+ .idea/
24
+ .vscode/
25
+ *.swp
26
+ *.swo
27
+ *~
28
+
29
+ # OS
30
+ .DS_Store
31
+ Thumbs.db
32
+
33
+ # Linting
34
+ .ruff_cache/
35
+ .mypy_cache/
36
+
37
+ # Lock files (each server manages its own)
38
+ uv.lock
39
+
40
+ # Environment
41
+ .env
42
+ .env.local
43
+
44
+ # Registry tokens
45
+ .mcpregistry_*
usgs_mcp-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Mansur Ali Jisan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.4
2
+ Name: usgs-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server providing access to USGS Water Services for streamflow, flood stages, and peak events
5
+ Project-URL: Homepage, https://github.com/mansurjisan/ocean-mcp
6
+ Project-URL: Repository, https://github.com/mansurjisan/ocean-mcp
7
+ Project-URL: Bug Tracker, https://github.com/mansurjisan/ocean-mcp/issues
8
+ Project-URL: Documentation, https://github.com/mansurjisan/ocean-mcp/tree/main/servers/usgs-mcp
9
+ Project-URL: Changelog, https://github.com/mansurjisan/ocean-mcp/releases
10
+ Author-email: Mansur Ali Jisan <mansur.jisan@noaa.gov>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: discharge,flood,gage-height,hydrology,mcp,model-context-protocol,river,streamflow,usgs,water-services
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
23
+ Classifier: Topic :: Scientific/Engineering :: GIS
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Requires-Python: >=3.11
26
+ Requires-Dist: httpx>=0.27.0
27
+ Requires-Dist: mcp[cli]>=1.0.0
28
+ Requires-Dist: pydantic>=2.0.0
29
+ Description-Content-Type: text/markdown
30
+
31
+ # usgs-mcp
32
+
33
+ <!-- mcp-name: io.github.mansurjisan/usgs-mcp -->
34
+
35
+ MCP server providing access to **USGS Water Services** for real-time streamflow, flood stages, peak events, and historical statistics.
36
+
37
+ This server fills a critical gap in the ocean-mcp ecosystem by providing **inland flooding and river data** — often the deadliest aspect of hurricanes and storms.
38
+
39
+ ## Data Sources
40
+
41
+ - **USGS Water Services** (`waterservices.usgs.gov`) — real-time and historical streamflow data
42
+ - **USGS NWIS Peak** (`nwis.waterdata.usgs.gov`) — annual peak streamflow records
43
+
44
+ No API key required. All data is public domain.
45
+
46
+ ## Tools (10)
47
+
48
+ ### Site Discovery
49
+ | Tool | Description |
50
+ |------|-------------|
51
+ | `usgs_find_sites` | Find gauge stations by state or bounding box |
52
+ | `usgs_get_site_info` | Get detailed metadata for a specific site |
53
+ | `usgs_find_nearest_sites` | Find sites near a lat/lon point |
54
+
55
+ ### Streamflow Data
56
+ | Tool | Description |
57
+ |------|-------------|
58
+ | `usgs_get_instantaneous_values` | Real-time ~15-minute interval data (up to 120 days) |
59
+ | `usgs_get_daily_values` | Daily mean/min/max values (decades of history) |
60
+ | `usgs_get_hydrograph` | Summary with trend and historical median comparison |
61
+
62
+ ### Flood Analysis
63
+ | Tool | Description |
64
+ |------|-------------|
65
+ | `usgs_get_peak_streamflow` | Annual peak flow records (50+ years at many sites) |
66
+ | `usgs_get_flood_status` | Current conditions vs. historical context |
67
+
68
+ ### Statistics
69
+ | Tool | Description |
70
+ |------|-------------|
71
+ | `usgs_get_monthly_stats` | Monthly mean/min/max/percentiles |
72
+ | `usgs_get_daily_stats` | Daily percentiles (flow duration) |
73
+
74
+ ## Quick Start
75
+
76
+ ### Install from PyPI
77
+ ```bash
78
+ uvx usgs-mcp
79
+ ```
80
+
81
+ ### Install from source
82
+ ```bash
83
+ cd servers/usgs-mcp
84
+ uv sync
85
+ uv run usgs-mcp
86
+ ```
87
+
88
+ ### Claude Desktop Configuration
89
+ ```json
90
+ {
91
+ "mcpServers": {
92
+ "usgs": {
93
+ "command": "uvx",
94
+ "args": ["usgs-mcp"]
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
100
+ ## Example Queries
101
+
102
+ - "What is the current streamflow on the Potomac River?" → `usgs_get_instantaneous_values(site_number="01646500")`
103
+ - "Find USGS gauges in Texas" → `usgs_find_sites(state_code="TX")`
104
+ - "Is the Mississippi at St. Louis flooding?" → `usgs_get_flood_status(site_number="07010000")`
105
+ - "What were the biggest floods on the Potomac?" → `usgs_get_peak_streamflow(site_number="01646500")`
106
+
107
+ ## Development
108
+
109
+ ```bash
110
+ cd servers/usgs-mcp
111
+ uv sync --group dev
112
+
113
+ # Unit tests (no network)
114
+ uv run pytest tests/ --ignore=tests/test_live.py --ignore=tests/test_mcp_protocol.py -v
115
+
116
+ # Integration tests (needs network)
117
+ uv run pytest tests/test_live.py -v
118
+
119
+ # MCP protocol test
120
+ uv run pytest tests/test_mcp_protocol.py -v
121
+ ```
122
+
123
+ ## License
124
+
125
+ MIT
@@ -0,0 +1,95 @@
1
+ # usgs-mcp
2
+
3
+ <!-- mcp-name: io.github.mansurjisan/usgs-mcp -->
4
+
5
+ MCP server providing access to **USGS Water Services** for real-time streamflow, flood stages, peak events, and historical statistics.
6
+
7
+ This server fills a critical gap in the ocean-mcp ecosystem by providing **inland flooding and river data** — often the deadliest aspect of hurricanes and storms.
8
+
9
+ ## Data Sources
10
+
11
+ - **USGS Water Services** (`waterservices.usgs.gov`) — real-time and historical streamflow data
12
+ - **USGS NWIS Peak** (`nwis.waterdata.usgs.gov`) — annual peak streamflow records
13
+
14
+ No API key required. All data is public domain.
15
+
16
+ ## Tools (10)
17
+
18
+ ### Site Discovery
19
+ | Tool | Description |
20
+ |------|-------------|
21
+ | `usgs_find_sites` | Find gauge stations by state or bounding box |
22
+ | `usgs_get_site_info` | Get detailed metadata for a specific site |
23
+ | `usgs_find_nearest_sites` | Find sites near a lat/lon point |
24
+
25
+ ### Streamflow Data
26
+ | Tool | Description |
27
+ |------|-------------|
28
+ | `usgs_get_instantaneous_values` | Real-time ~15-minute interval data (up to 120 days) |
29
+ | `usgs_get_daily_values` | Daily mean/min/max values (decades of history) |
30
+ | `usgs_get_hydrograph` | Summary with trend and historical median comparison |
31
+
32
+ ### Flood Analysis
33
+ | Tool | Description |
34
+ |------|-------------|
35
+ | `usgs_get_peak_streamflow` | Annual peak flow records (50+ years at many sites) |
36
+ | `usgs_get_flood_status` | Current conditions vs. historical context |
37
+
38
+ ### Statistics
39
+ | Tool | Description |
40
+ |------|-------------|
41
+ | `usgs_get_monthly_stats` | Monthly mean/min/max/percentiles |
42
+ | `usgs_get_daily_stats` | Daily percentiles (flow duration) |
43
+
44
+ ## Quick Start
45
+
46
+ ### Install from PyPI
47
+ ```bash
48
+ uvx usgs-mcp
49
+ ```
50
+
51
+ ### Install from source
52
+ ```bash
53
+ cd servers/usgs-mcp
54
+ uv sync
55
+ uv run usgs-mcp
56
+ ```
57
+
58
+ ### Claude Desktop Configuration
59
+ ```json
60
+ {
61
+ "mcpServers": {
62
+ "usgs": {
63
+ "command": "uvx",
64
+ "args": ["usgs-mcp"]
65
+ }
66
+ }
67
+ }
68
+ ```
69
+
70
+ ## Example Queries
71
+
72
+ - "What is the current streamflow on the Potomac River?" → `usgs_get_instantaneous_values(site_number="01646500")`
73
+ - "Find USGS gauges in Texas" → `usgs_find_sites(state_code="TX")`
74
+ - "Is the Mississippi at St. Louis flooding?" → `usgs_get_flood_status(site_number="07010000")`
75
+ - "What were the biggest floods on the Potomac?" → `usgs_get_peak_streamflow(site_number="01646500")`
76
+
77
+ ## Development
78
+
79
+ ```bash
80
+ cd servers/usgs-mcp
81
+ uv sync --group dev
82
+
83
+ # Unit tests (no network)
84
+ uv run pytest tests/ --ignore=tests/test_live.py --ignore=tests/test_mcp_protocol.py -v
85
+
86
+ # Integration tests (needs network)
87
+ uv run pytest tests/test_live.py -v
88
+
89
+ # MCP protocol test
90
+ uv run pytest tests/test_mcp_protocol.py -v
91
+ ```
92
+
93
+ ## License
94
+
95
+ MIT
@@ -0,0 +1,72 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "usgs-mcp"
7
+ version = "0.1.0"
8
+ description = "MCP server providing access to USGS Water Services for streamflow, flood stages, and peak events"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.11"
13
+ authors = [
14
+ {name = "Mansur Ali Jisan", email = "mansur.jisan@noaa.gov"},
15
+ ]
16
+ keywords = [
17
+ "mcp",
18
+ "model-context-protocol",
19
+ "usgs",
20
+ "streamflow",
21
+ "flood",
22
+ "water-services",
23
+ "hydrology",
24
+ "river",
25
+ "discharge",
26
+ "gage-height",
27
+ ]
28
+ classifiers = [
29
+ "Development Status :: 4 - Beta",
30
+ "Intended Audience :: Science/Research",
31
+ "Intended Audience :: Developers",
32
+ "License :: OSI Approved :: MIT License",
33
+ "Programming Language :: Python :: 3",
34
+ "Programming Language :: Python :: 3.11",
35
+ "Programming Language :: Python :: 3.12",
36
+ "Programming Language :: Python :: 3.13",
37
+ "Topic :: Scientific/Engineering :: Atmospheric Science",
38
+ "Topic :: Scientific/Engineering :: GIS",
39
+ "Topic :: Software Development :: Libraries :: Python Modules",
40
+ ]
41
+ dependencies = [
42
+ "mcp[cli]>=1.0.0",
43
+ "httpx>=0.27.0",
44
+ "pydantic>=2.0.0",
45
+ ]
46
+
47
+ [project.urls]
48
+ Homepage = "https://github.com/mansurjisan/ocean-mcp"
49
+ Repository = "https://github.com/mansurjisan/ocean-mcp"
50
+ "Bug Tracker" = "https://github.com/mansurjisan/ocean-mcp/issues"
51
+ Documentation = "https://github.com/mansurjisan/ocean-mcp/tree/main/servers/usgs-mcp"
52
+ Changelog = "https://github.com/mansurjisan/ocean-mcp/releases"
53
+
54
+ [project.scripts]
55
+ usgs-mcp = "usgs_mcp.server:main"
56
+
57
+ [tool.hatch.build.targets.wheel]
58
+ packages = ["src/usgs_mcp"]
59
+
60
+ [tool.pytest.ini_options]
61
+ testpaths = ["tests"]
62
+ asyncio_mode = "auto"
63
+ markers = [
64
+ "integration: tests that hit live USGS APIs (deselect with '-m \"not integration\"')",
65
+ ]
66
+
67
+ [dependency-groups]
68
+ dev = [
69
+ "pytest>=8.0.0",
70
+ "pytest-asyncio>=0.24.0",
71
+ "respx>=0.22.0",
72
+ ]
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.mansurjisan/usgs-mcp",
4
+ "title": "USGS Water Services MCP Server",
5
+ "description": "USGS Water Services streamflow, flood stages, and peak events via waterservices.usgs.gov",
6
+ "version": "0.1.0",
7
+ "repository": {
8
+ "url": "https://github.com/mansurjisan/ocean-mcp",
9
+ "source": "github"
10
+ },
11
+ "packages": [
12
+ {
13
+ "registryType": "pypi",
14
+ "identifier": "usgs-mcp",
15
+ "version": "0.1.0",
16
+ "runtimeHint": "uvx",
17
+ "transport": {
18
+ "type": "stdio"
19
+ }
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,9 @@
1
+ # Smithery configuration — https://smithery.ai/docs/config
2
+ startCommand:
3
+ type: stdio
4
+ configSchema:
5
+ type: object
6
+ required: []
7
+ properties: {}
8
+ commandFunction: |-
9
+ (config) => ({ command: 'uvx', args: ['usgs-mcp'] })
File without changes
@@ -0,0 +1,3 @@
1
+ from .server import main
2
+
3
+ main()
@@ -0,0 +1,78 @@
1
+ """Async HTTP client for USGS Water Services API."""
2
+
3
+ from typing import Any
4
+
5
+ import httpx
6
+
7
+ from .models import USGS_BASE_URL, USGS_PEAK_URL, USER_AGENT
8
+
9
+
10
+ class USGSAPIError(Exception):
11
+ """Custom exception for USGS API errors."""
12
+
13
+ pass
14
+
15
+
16
+ def parse_rdb(text: str) -> list[dict[str, str]]:
17
+ """Parse USGS RDB tab-delimited format to list of dicts.
18
+
19
+ RDB format: # comment lines, then header row, then data type row
20
+ (5s, 15n, etc.), then data rows.
21
+ """
22
+ lines = [line for line in text.strip().split("\n") if not line.startswith("#")]
23
+ if len(lines) < 2:
24
+ return []
25
+ headers = lines[0].split("\t")
26
+ # Skip lines[1] — data type definitions (e.g., "5s\t15s\t20d")
27
+ rows = []
28
+ for line in lines[2:]:
29
+ if not line.strip():
30
+ continue
31
+ vals = line.split("\t")
32
+ rows.append(dict(zip(headers, vals)))
33
+ return rows
34
+
35
+
36
+ class USGSClient:
37
+ """Async client for USGS Water Services API."""
38
+
39
+ def __init__(self) -> None:
40
+ self._client: httpx.AsyncClient | None = None
41
+
42
+ async def _get_client(self) -> httpx.AsyncClient:
43
+ if self._client is None or self._client.is_closed:
44
+ self._client = httpx.AsyncClient(
45
+ timeout=30.0,
46
+ headers={"User-Agent": USER_AGENT},
47
+ )
48
+ return self._client
49
+
50
+ async def get_json(self, endpoint: str, params: dict[str, Any]) -> dict:
51
+ """Fetch JSON from IV/DV endpoints."""
52
+ params["format"] = "json"
53
+ client = await self._get_client()
54
+ response = await client.get(f"{USGS_BASE_URL}/{endpoint}/", params=params)
55
+ response.raise_for_status()
56
+ return response.json()
57
+
58
+ async def get_rdb(
59
+ self, endpoint: str, params: dict[str, Any]
60
+ ) -> list[dict[str, str]]:
61
+ """Fetch RDB (tab-delimited) from site/stat endpoints."""
62
+ params["format"] = "rdb"
63
+ client = await self._get_client()
64
+ response = await client.get(f"{USGS_BASE_URL}/{endpoint}/", params=params)
65
+ response.raise_for_status()
66
+ return parse_rdb(response.text)
67
+
68
+ async def get_peak(self, params: dict[str, Any]) -> list[dict[str, str]]:
69
+ """Fetch peak streamflow RDB from nwis.waterdata.usgs.gov."""
70
+ params["format"] = "rdb"
71
+ client = await self._get_client()
72
+ response = await client.get(USGS_PEAK_URL, params=params)
73
+ response.raise_for_status()
74
+ return parse_rdb(response.text)
75
+
76
+ async def close(self) -> None:
77
+ if self._client and not self._client.is_closed:
78
+ await self._client.aclose()
@@ -0,0 +1,141 @@
1
+ """Constants and helpers for usgs-mcp."""
2
+
3
+ USGS_BASE_URL = "https://waterservices.usgs.gov/nwis"
4
+ USGS_PEAK_URL = "https://nwis.waterdata.usgs.gov/nwis/peak"
5
+
6
+ USER_AGENT = "usgs-mcp/0.1.0 (https://github.com/mansurjisan/ocean-mcp)"
7
+
8
+ # Well-known USGS parameter codes
9
+ PARAMETER_CODES: dict[str, dict] = {
10
+ "00060": {
11
+ "name": "Discharge",
12
+ "units": "ft³/s (cfs)",
13
+ "description": "Streamflow discharge",
14
+ },
15
+ "00065": {
16
+ "name": "Gage height",
17
+ "units": "ft",
18
+ "description": "Water surface elevation above datum",
19
+ },
20
+ "00010": {"name": "Temperature", "units": "°C", "description": "Water temperature"},
21
+ "00045": {
22
+ "name": "Precipitation",
23
+ "units": "in",
24
+ "description": "Accumulated precipitation",
25
+ },
26
+ "00095": {
27
+ "name": "Specific conductance",
28
+ "units": "µS/cm",
29
+ "description": "Specific conductance at 25°C",
30
+ },
31
+ "00300": {
32
+ "name": "Dissolved oxygen",
33
+ "units": "mg/L",
34
+ "description": "Dissolved oxygen",
35
+ },
36
+ "00400": {"name": "pH", "units": "standard units", "description": "pH of water"},
37
+ "63680": {"name": "Turbidity", "units": "FNU", "description": "Turbidity"},
38
+ }
39
+
40
+ # Well-known reference sites for testing and examples
41
+ REFERENCE_SITES: dict[str, dict] = {
42
+ "01646500": {"name": "Potomac River at Little Falls, MD", "state": "MD"},
43
+ "02037500": {"name": "James River at Richmond, VA", "state": "VA"},
44
+ "07010000": {"name": "Mississippi River at St. Louis, MO", "state": "MO"},
45
+ "08066500": {"name": "Trinity River at Romayor, TX", "state": "TX"},
46
+ "02146000": {"name": "Catawba River near Pleasant Garden, NC", "state": "NC"},
47
+ }
48
+
49
+ # US state codes for site queries
50
+ US_STATE_CODES: dict[str, str] = {
51
+ "AL": "Alabama",
52
+ "AK": "Alaska",
53
+ "AZ": "Arizona",
54
+ "AR": "Arkansas",
55
+ "CA": "California",
56
+ "CO": "Colorado",
57
+ "CT": "Connecticut",
58
+ "DE": "Delaware",
59
+ "FL": "Florida",
60
+ "GA": "Georgia",
61
+ "HI": "Hawaii",
62
+ "ID": "Idaho",
63
+ "IL": "Illinois",
64
+ "IN": "Indiana",
65
+ "IA": "Iowa",
66
+ "KS": "Kansas",
67
+ "KY": "Kentucky",
68
+ "LA": "Louisiana",
69
+ "ME": "Maine",
70
+ "MD": "Maryland",
71
+ "MA": "Massachusetts",
72
+ "MI": "Michigan",
73
+ "MN": "Minnesota",
74
+ "MS": "Mississippi",
75
+ "MO": "Missouri",
76
+ "MT": "Montana",
77
+ "NE": "Nebraska",
78
+ "NV": "Nevada",
79
+ "NH": "New Hampshire",
80
+ "NJ": "New Jersey",
81
+ "NM": "New Mexico",
82
+ "NY": "New York",
83
+ "NC": "North Carolina",
84
+ "ND": "North Dakota",
85
+ "OH": "Ohio",
86
+ "OK": "Oklahoma",
87
+ "OR": "Oregon",
88
+ "PA": "Pennsylvania",
89
+ "RI": "Rhode Island",
90
+ "SC": "South Carolina",
91
+ "SD": "South Dakota",
92
+ "TN": "Tennessee",
93
+ "TX": "Texas",
94
+ "UT": "Utah",
95
+ "VT": "Vermont",
96
+ "VA": "Virginia",
97
+ "WA": "Washington",
98
+ "WV": "West Virginia",
99
+ "WI": "Wisconsin",
100
+ "WY": "Wyoming",
101
+ "DC": "District of Columbia",
102
+ "PR": "Puerto Rico",
103
+ "VI": "Virgin Islands",
104
+ "GU": "Guam",
105
+ "AS": "American Samoa",
106
+ "MP": "Northern Mariana Islands",
107
+ }
108
+
109
+ # NWS flood stage categories
110
+ FLOOD_CATEGORIES: dict[str, str] = {
111
+ "action": "Water is approaching bankfull; begin monitoring",
112
+ "flood": "Minor flooding is occurring or imminent",
113
+ "moderate": "Moderate flooding; some inundation of structures/roads",
114
+ "major": "Major flooding; extensive inundation, significant threat to life/property",
115
+ }
116
+
117
+ # Stat report types
118
+ STAT_REPORT_TYPES: dict[str, str] = {
119
+ "daily": "Daily statistics",
120
+ "monthly": "Monthly statistics",
121
+ "annual": "Annual statistics",
122
+ }
123
+
124
+ # USGS data qualification codes
125
+ QUALIFICATION_CODES: dict[str, str] = {
126
+ "A": "Approved for publication",
127
+ "P": "Provisional, subject to revision",
128
+ "e": "Estimated",
129
+ "Eqp": "Equipment malfunction",
130
+ "Mnt": "Maintenance",
131
+ "Dry": "Dry conditions",
132
+ "Ssn": "Parameter monitored seasonally",
133
+ }
134
+
135
+
136
+ def format_parameter(code: str) -> str:
137
+ """Return a human-readable name for a USGS parameter code."""
138
+ info = PARAMETER_CODES.get(code)
139
+ if info:
140
+ return f"{info['name']} ({info['units']})"
141
+ return f"Parameter {code}"
@@ -0,0 +1,32 @@
1
+ """FastMCP server entry point for USGS Water Services."""
2
+
3
+ from collections.abc import AsyncIterator
4
+ from contextlib import asynccontextmanager
5
+
6
+ from mcp.server.fastmcp import FastMCP
7
+
8
+ from .client import USGSClient
9
+
10
+
11
+ @asynccontextmanager
12
+ async def app_lifespan(server: FastMCP) -> AsyncIterator[dict]:
13
+ """Manage the shared USGSClient lifecycle."""
14
+ client = USGSClient()
15
+ try:
16
+ yield {"usgs_client": client}
17
+ finally:
18
+ await client.close()
19
+
20
+
21
+ mcp = FastMCP("usgs_mcp", lifespan=app_lifespan)
22
+
23
+ # Import tool modules to register them with the server
24
+ from .tools import flood, sites, statistics, streamflow # noqa: E402, F401
25
+
26
+
27
+ def main() -> None:
28
+ mcp.run()
29
+
30
+
31
+ if __name__ == "__main__":
32
+ main()
File without changes