cwms-tools 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.
Files changed (48) hide show
  1. cwms_tools-0.1.0/LICENSE +21 -0
  2. cwms_tools-0.1.0/PKG-INFO +266 -0
  3. cwms_tools-0.1.0/README.md +231 -0
  4. cwms_tools-0.1.0/pyproject.toml +146 -0
  5. cwms_tools-0.1.0/src/cwms_tools/__init__.py +12 -0
  6. cwms_tools-0.1.0/src/cwms_tools/cli/__init__.py +1 -0
  7. cwms_tools-0.1.0/src/cwms_tools/cli/app.py +127 -0
  8. cwms_tools-0.1.0/src/cwms_tools/cli/commands/__init__.py +1 -0
  9. cwms_tools-0.1.0/src/cwms_tools/cli/commands/config.py +73 -0
  10. cwms_tools-0.1.0/src/cwms_tools/cli/commands/env.py +62 -0
  11. cwms_tools-0.1.0/src/cwms_tools/cli/commands/fingerprint.py +35 -0
  12. cwms_tools-0.1.0/src/cwms_tools/cli/commands/mcp.py +129 -0
  13. cwms_tools-0.1.0/src/cwms_tools/cli/commands/place.py +232 -0
  14. cwms_tools-0.1.0/src/cwms_tools/cli/commands/publisher.py +52 -0
  15. cwms_tools-0.1.0/src/cwms_tools/cli/commands/region.py +100 -0
  16. cwms_tools-0.1.0/src/cwms_tools/cli/commands/schema.py +157 -0
  17. cwms_tools-0.1.0/src/cwms_tools/cli/commands/value.py +228 -0
  18. cwms_tools-0.1.0/src/cwms_tools/cli/commands/whoami.py +30 -0
  19. cwms_tools-0.1.0/src/cwms_tools/cli/exit_codes.py +45 -0
  20. cwms_tools-0.1.0/src/cwms_tools/cli/render.py +97 -0
  21. cwms_tools-0.1.0/src/cwms_tools/core/__init__.py +1 -0
  22. cwms_tools-0.1.0/src/cwms_tools/core/_workarounds.py +29 -0
  23. cwms_tools-0.1.0/src/cwms_tools/core/cache.py +188 -0
  24. cwms_tools-0.1.0/src/cwms_tools/core/catalog.py +448 -0
  25. cwms_tools-0.1.0/src/cwms_tools/core/concurrency.py +85 -0
  26. cwms_tools-0.1.0/src/cwms_tools/core/errors.py +193 -0
  27. cwms_tools-0.1.0/src/cwms_tools/core/fingerprint.py +84 -0
  28. cwms_tools-0.1.0/src/cwms_tools/core/geo.py +80 -0
  29. cwms_tools-0.1.0/src/cwms_tools/core/levels.py +338 -0
  30. cwms_tools-0.1.0/src/cwms_tools/core/locations.py +108 -0
  31. cwms_tools-0.1.0/src/cwms_tools/core/models.py +414 -0
  32. cwms_tools-0.1.0/src/cwms_tools/core/offices.py +119 -0
  33. cwms_tools-0.1.0/src/cwms_tools/core/overview.py +178 -0
  34. cwms_tools-0.1.0/src/cwms_tools/core/places.py +526 -0
  35. cwms_tools-0.1.0/src/cwms_tools/core/projects.py +182 -0
  36. cwms_tools-0.1.0/src/cwms_tools/core/publishers.py +190 -0
  37. cwms_tools-0.1.0/src/cwms_tools/core/publishers_index.py +198 -0
  38. cwms_tools-0.1.0/src/cwms_tools/core/session.py +195 -0
  39. cwms_tools-0.1.0/src/cwms_tools/core/timeseries.py +212 -0
  40. cwms_tools-0.1.0/src/cwms_tools/core/values.py +243 -0
  41. cwms_tools-0.1.0/src/cwms_tools/data/__init__.py +1 -0
  42. cwms_tools-0.1.0/src/cwms_tools/data/cwms-overview.md +925 -0
  43. cwms_tools-0.1.0/src/cwms_tools/mcp/__init__.py +1 -0
  44. cwms_tools-0.1.0/src/cwms_tools/mcp/fastmcp_capabilities.py +60 -0
  45. cwms_tools-0.1.0/src/cwms_tools/mcp/resources.py +200 -0
  46. cwms_tools-0.1.0/src/cwms_tools/mcp/server.py +249 -0
  47. cwms_tools-0.1.0/src/cwms_tools/mcp/tools.py +489 -0
  48. cwms_tools-0.1.0/src/cwms_tools/py.typed +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Brian Connelly
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,266 @@
1
+ Metadata-Version: 2.4
2
+ Name: cwms-tools
3
+ Version: 0.1.0
4
+ Summary: Agent-friendly tools (MCP server and CLI) for the USACE Corps Water Management System (CWMS) Data API.
5
+ Keywords: cwms,usace,mcp,cli,water,hydrology,agent
6
+ Author: Brian Connelly
7
+ Author-email: Brian Connelly <bdc@bconnelly.net>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Hydrology
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Dist: cwms-python>=1.0.7
22
+ Requires-Dist: fastmcp>=3
23
+ Requires-Dist: typer>=0.15
24
+ Requires-Dist: pydantic>=2
25
+ Requires-Dist: platformdirs>=4
26
+ Requires-Dist: diskcache>=5
27
+ Requires-Dist: anyio>=4
28
+ Requires-Dist: python-dateutil>=2.9
29
+ Requires-Python: >=3.10
30
+ Project-URL: Homepage, https://github.com/briandconnelly/cwms-tools
31
+ Project-URL: Repository, https://github.com/briandconnelly/cwms-tools
32
+ Project-URL: Issues, https://github.com/briandconnelly/cwms-tools/issues
33
+ Project-URL: Changelog, https://github.com/briandconnelly/cwms-tools/blob/main/CHANGELOG.md
34
+ Description-Content-Type: text/markdown
35
+
36
+ # cwms-tools
37
+
38
+ [![CI](https://github.com/briandconnelly/cwms-tools/actions/workflows/ci.yml/badge.svg)](https://github.com/briandconnelly/cwms-tools/actions/workflows/ci.yml)
39
+ [![PyPI](https://img.shields.io/pypi/v/cwms-tools.svg)](https://pypi.org/project/cwms-tools/)
40
+ [![Python](https://img.shields.io/pypi/pyversions/cwms-tools.svg)](https://pypi.org/project/cwms-tools/)
41
+
42
+ Read-only, agent-friendly tools for the U.S. Army Corps of Engineers'
43
+ [CWMS Data API](https://cwms-data.usace.army.mil/cwms-data/). `cwms-tools` wraps the official
44
+ [`cwms-python`](https://github.com/HydrologicEngineeringCenter/cwms-python) client with two surfaces that share one behavioral
45
+ core:
46
+
47
+ - an [MCP](https://modelcontextprotocol.io/) server for agent runtimes such as Claude Code, Codex, and custom
48
+ MCP clients
49
+ - a non-interactive CLI with compact JSON output, stable exit codes, and a
50
+ machine-readable schema
51
+
52
+ The goal is simple: let agents answer common hydrologic questions with one task
53
+ call instead of a brittle chain of raw API lookups.
54
+
55
+ ## What It Does
56
+
57
+ - Resolves natural place names to canonical CWMS locations, with ghost-location
58
+ filtering and co-located sensor hints.
59
+ - Describes a place in one call: location record, project metadata when
60
+ available, published parameters, publishers, and latest data timestamp.
61
+ - Reads the latest value or bounded history for CWMS time series parameters.
62
+ - Optionally classifies the latest value against CWMS Location Levels when
63
+ callers opt in with `--with-status` or `with_status=true`.
64
+ - Browses one office's catalog by state or bounding box.
65
+ - Finds publishers for a parameter across cached or explicitly requested
66
+ offices.
67
+ - Serves a bundled CWMS orientation document as MCP resources so agents can load
68
+ background material selectively.
69
+
70
+ ## Install
71
+
72
+ ```bash
73
+ uv add cwms-tools
74
+ # or: pipx install cwms-tools
75
+ ```
76
+
77
+ To run from a checkout instead:
78
+
79
+ ```bash
80
+ git clone https://github.com/briandconnelly/cwms-tools.git
81
+ cd cwms-tools
82
+ uv sync
83
+ uv run cwms-tools --help
84
+ ```
85
+
86
+ ## CLI Quick Start
87
+
88
+ The CLI is designed for non-interactive callers. When stdout is not a TTY,
89
+ machine mode is enabled automatically: compact JSON on stdout, diagnostics on
90
+ stderr, no color, no prompts, and no progress UI. You can force that profile
91
+ with `--machine` or `--json`.
92
+
93
+ ```bash
94
+ # Inspect the resolved session and upstream API root.
95
+ uv run cwms-tools whoami
96
+
97
+ # Print the command tree, output classes, exit codes, environment inputs,
98
+ # MCP tools, and MCP resources as a stable machine-readable contract.
99
+ uv run cwms-tools schema
100
+
101
+ # Print a SHA-256 fingerprint over the agent-visible capability surface.
102
+ uv run cwms-tools fingerprint
103
+
104
+ # Resolve a place name to ranked CWMS locations.
105
+ uv run cwms-tools place search "Fort Peck" --office NWDM
106
+
107
+ # Describe one place: location, project metadata, parameters, publishers,
108
+ # freshness, and partial-result flags.
109
+ uv run cwms-tools place describe NWDM/FTPK
110
+
111
+ # List parameters published at a place.
112
+ uv run cwms-tools place parameters NWDM/FTPK
113
+
114
+ # Read the latest elevation value. Status lookup is skipped by default because
115
+ # CWMS Location Levels calls can be slow.
116
+ uv run cwms-tools value get NWDM/FTPK/Elev
117
+
118
+ # Opt in to threshold classification when status context matters.
119
+ uv run cwms-tools value get NWDM/FTPK/Elev --with-status
120
+
121
+ # Read a bounded history window.
122
+ uv run cwms-tools value history NWDM/FTPK/Elev \
123
+ --begin 2026-05-16T00:00:00Z \
124
+ --end 2026-05-17T00:00:00Z
125
+
126
+ # Browse an office catalog by state.
127
+ uv run cwms-tools region browse --office SWT --state OK
128
+
129
+ # Find publishers reporting a parameter in selected offices.
130
+ uv run cwms-tools publisher for-parameter Elev --office NWDM --office SWT
131
+ ```
132
+
133
+ Useful global flags:
134
+
135
+ - `--machine` / `--json`: compact structured output for agents and scripts.
136
+ - `--no-cache`: bypass the on-disk catalog cache for one invocation.
137
+ - `--isolated`: bypass cache and ignore `CWMS_TOOLS_*` environment variables.
138
+ - `--version`: print the installed `cwms-tools` version.
139
+
140
+ Exit codes are part of the CLI contract:
141
+
142
+ | Exit | Meaning |
143
+ | ---: | --- |
144
+ | `0` | success |
145
+ | `2` | usage or invalid field |
146
+ | `3` | not found or publisher unavailable |
147
+ | `4` | session unconfigured |
148
+ | `6` | rate limited |
149
+ | `7` | timeout |
150
+ | `9` | upstream error or catalog cursor invalidation |
151
+ | `11` | wrapper bug |
152
+ | `12` | ghost location or ghost office |
153
+
154
+ ## MCP Quick Start
155
+
156
+ Use stdio for local agent runtimes:
157
+
158
+ ```bash
159
+ uv run cwms-tools mcp serve --transport stdio
160
+ ```
161
+
162
+ Use streamable HTTP for shared or remote deployment:
163
+
164
+ ```bash
165
+ uv run cwms-tools mcp serve --transport streamable-http --host 127.0.0.1 --port 8765
166
+ ```
167
+
168
+ Claude Code config example:
169
+
170
+ ```jsonc
171
+ {
172
+ "mcpServers": {
173
+ "cwms-tools": {
174
+ "command": "uv",
175
+ "args": ["run", "cwms-tools", "mcp", "serve", "--transport", "stdio"]
176
+ }
177
+ }
178
+ }
179
+ ```
180
+
181
+ The MCP server exposes task-level tools rather than raw endpoint mirrors:
182
+
183
+ | Tool | Purpose |
184
+ | --- | --- |
185
+ | `cwms_search_places` | Resolve an ambiguous place name to ranked locations. |
186
+ | `cwms_describe_place` | Read location, project, parameter, publisher, and freshness data in one call. |
187
+ | `cwms_list_parameters` | List parameters published at a location, grouped by publisher. |
188
+ | `cwms_get_value` | Read the latest observation, optionally with threshold status. |
189
+ | `cwms_get_history` | Read raw observations across a bounded time window. |
190
+ | `cwms_browse_region` | Browse one office's locations, optionally by state or bounding box. |
191
+ | `cwms_publishers_for_parameter` | List publishers reporting a parameter across selected offices. |
192
+ | `cwms_get_overview_section` | Read bundled CWMS orientation content. |
193
+
194
+ Resources:
195
+
196
+ - `cwms://capabilities`: server version, fingerprint, tools, and resources.
197
+ - `cwms://overview`: index of bundled CWMS overview sections.
198
+ - `cwms://overview/{section_id}{?detail}`: summary or full section body.
199
+ - `cwms://overview/{section_id}/chunk/{chunk_id}`: one large-section chunk.
200
+
201
+ ## Configuration
202
+
203
+ `cwms-tools` works anonymously by default. Environment variables are optional:
204
+
205
+ | Variable | Purpose |
206
+ | --- | --- |
207
+ | `CWMS_TOOLS_API_ROOT` | Override the CWMS Data API root. |
208
+ | `CWMS_TOOLS_CACHE_DIR` | Override the disk cache location. |
209
+ | `CWMS_TOOLS_WORKERS` | Set bounded worker concurrency. |
210
+ | `CWMS_TOOLS_REPO_URL` | Override the repository URL advertised in the user agent. |
211
+ | `CWMS_TOOLS_USER_AGENT_EXTRA` | Append extra text to the user agent. |
212
+ | `CWMS_TOOLS_OPERATOR_EMAIL` | Send a contact email via the `From` header. |
213
+ | `CWMS_TOOLS_MAX_RPS` | Declared for rate-limit policy; not enforced in v0.1.0. |
214
+ | `CWMS_API_KEY` | Reserved secret input for authenticated CDA deployments. |
215
+ | `CWMS_TOKEN` | Reserved secret input for authenticated CDA deployments. |
216
+
217
+ Inspect the resolved configuration without making a data call:
218
+
219
+ ```bash
220
+ uv run cwms-tools env
221
+ uv run cwms-tools config show --resolved
222
+ ```
223
+
224
+ ## CWMS Notes
225
+
226
+ CWMS is USACE's Corps Water Management System: the operational data platform for
227
+ federal reservoirs, flood-control dams, navigation locks, hydropower projects,
228
+ and environmental monitoring stations.
229
+
230
+ Two upstream data-shape issues come up often:
231
+
232
+ - **Ghost records.** Some catalog locations do not publish time-series data.
233
+ Search and browse responses expose `parameter_count`, `parameters`, and
234
+ `data_at` repair hints so agents can move to the data-bearing sibling.
235
+ - **Northwestern Division stubs.** `NWO`, `NWK`, `NWS`, `NWP`, and `NWW` are
236
+ near-empty CDA stubs. Use `NWDM` for Missouri River data and `NWDP` for
237
+ Pacific Northwest data. Error envelopes include repair hints when a stub is
238
+ targeted.
239
+
240
+ ## Upstream Etiquette
241
+
242
+ The CWMS Data API is a shared public service. `cwms-tools` identifies itself
243
+ with a descriptive `User-Agent`, caps concurrent requests, honors
244
+ `Retry-After`, avoids background scans, and does not cache live time-series
245
+ values. If you operate CDA and see problematic traffic from this client, please
246
+ open an issue at <https://github.com/briandconnelly/cwms-tools/issues>.
247
+
248
+ ## Development
249
+
250
+ ```bash
251
+ uv sync
252
+ uv run prek run --all-files
253
+ uv run pytest --cov=cwms_tools
254
+ uv run ty check
255
+ ```
256
+
257
+ The test suite uses unit tests and mocked CDA responses. Live CDA integration
258
+ tests are marked `integration` and are skipped unless explicitly selected.
259
+
260
+ Before opening a substantial PR, please open an issue to discuss the intended
261
+ change. The package is still pre-release, and the CLI/MCP schema contract is
262
+ the main compatibility surface.
263
+
264
+ ## License
265
+
266
+ [MIT](LICENSE)
@@ -0,0 +1,231 @@
1
+ # cwms-tools
2
+
3
+ [![CI](https://github.com/briandconnelly/cwms-tools/actions/workflows/ci.yml/badge.svg)](https://github.com/briandconnelly/cwms-tools/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/cwms-tools.svg)](https://pypi.org/project/cwms-tools/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/cwms-tools.svg)](https://pypi.org/project/cwms-tools/)
6
+
7
+ Read-only, agent-friendly tools for the U.S. Army Corps of Engineers'
8
+ [CWMS Data API](https://cwms-data.usace.army.mil/cwms-data/). `cwms-tools` wraps the official
9
+ [`cwms-python`](https://github.com/HydrologicEngineeringCenter/cwms-python) client with two surfaces that share one behavioral
10
+ core:
11
+
12
+ - an [MCP](https://modelcontextprotocol.io/) server for agent runtimes such as Claude Code, Codex, and custom
13
+ MCP clients
14
+ - a non-interactive CLI with compact JSON output, stable exit codes, and a
15
+ machine-readable schema
16
+
17
+ The goal is simple: let agents answer common hydrologic questions with one task
18
+ call instead of a brittle chain of raw API lookups.
19
+
20
+ ## What It Does
21
+
22
+ - Resolves natural place names to canonical CWMS locations, with ghost-location
23
+ filtering and co-located sensor hints.
24
+ - Describes a place in one call: location record, project metadata when
25
+ available, published parameters, publishers, and latest data timestamp.
26
+ - Reads the latest value or bounded history for CWMS time series parameters.
27
+ - Optionally classifies the latest value against CWMS Location Levels when
28
+ callers opt in with `--with-status` or `with_status=true`.
29
+ - Browses one office's catalog by state or bounding box.
30
+ - Finds publishers for a parameter across cached or explicitly requested
31
+ offices.
32
+ - Serves a bundled CWMS orientation document as MCP resources so agents can load
33
+ background material selectively.
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ uv add cwms-tools
39
+ # or: pipx install cwms-tools
40
+ ```
41
+
42
+ To run from a checkout instead:
43
+
44
+ ```bash
45
+ git clone https://github.com/briandconnelly/cwms-tools.git
46
+ cd cwms-tools
47
+ uv sync
48
+ uv run cwms-tools --help
49
+ ```
50
+
51
+ ## CLI Quick Start
52
+
53
+ The CLI is designed for non-interactive callers. When stdout is not a TTY,
54
+ machine mode is enabled automatically: compact JSON on stdout, diagnostics on
55
+ stderr, no color, no prompts, and no progress UI. You can force that profile
56
+ with `--machine` or `--json`.
57
+
58
+ ```bash
59
+ # Inspect the resolved session and upstream API root.
60
+ uv run cwms-tools whoami
61
+
62
+ # Print the command tree, output classes, exit codes, environment inputs,
63
+ # MCP tools, and MCP resources as a stable machine-readable contract.
64
+ uv run cwms-tools schema
65
+
66
+ # Print a SHA-256 fingerprint over the agent-visible capability surface.
67
+ uv run cwms-tools fingerprint
68
+
69
+ # Resolve a place name to ranked CWMS locations.
70
+ uv run cwms-tools place search "Fort Peck" --office NWDM
71
+
72
+ # Describe one place: location, project metadata, parameters, publishers,
73
+ # freshness, and partial-result flags.
74
+ uv run cwms-tools place describe NWDM/FTPK
75
+
76
+ # List parameters published at a place.
77
+ uv run cwms-tools place parameters NWDM/FTPK
78
+
79
+ # Read the latest elevation value. Status lookup is skipped by default because
80
+ # CWMS Location Levels calls can be slow.
81
+ uv run cwms-tools value get NWDM/FTPK/Elev
82
+
83
+ # Opt in to threshold classification when status context matters.
84
+ uv run cwms-tools value get NWDM/FTPK/Elev --with-status
85
+
86
+ # Read a bounded history window.
87
+ uv run cwms-tools value history NWDM/FTPK/Elev \
88
+ --begin 2026-05-16T00:00:00Z \
89
+ --end 2026-05-17T00:00:00Z
90
+
91
+ # Browse an office catalog by state.
92
+ uv run cwms-tools region browse --office SWT --state OK
93
+
94
+ # Find publishers reporting a parameter in selected offices.
95
+ uv run cwms-tools publisher for-parameter Elev --office NWDM --office SWT
96
+ ```
97
+
98
+ Useful global flags:
99
+
100
+ - `--machine` / `--json`: compact structured output for agents and scripts.
101
+ - `--no-cache`: bypass the on-disk catalog cache for one invocation.
102
+ - `--isolated`: bypass cache and ignore `CWMS_TOOLS_*` environment variables.
103
+ - `--version`: print the installed `cwms-tools` version.
104
+
105
+ Exit codes are part of the CLI contract:
106
+
107
+ | Exit | Meaning |
108
+ | ---: | --- |
109
+ | `0` | success |
110
+ | `2` | usage or invalid field |
111
+ | `3` | not found or publisher unavailable |
112
+ | `4` | session unconfigured |
113
+ | `6` | rate limited |
114
+ | `7` | timeout |
115
+ | `9` | upstream error or catalog cursor invalidation |
116
+ | `11` | wrapper bug |
117
+ | `12` | ghost location or ghost office |
118
+
119
+ ## MCP Quick Start
120
+
121
+ Use stdio for local agent runtimes:
122
+
123
+ ```bash
124
+ uv run cwms-tools mcp serve --transport stdio
125
+ ```
126
+
127
+ Use streamable HTTP for shared or remote deployment:
128
+
129
+ ```bash
130
+ uv run cwms-tools mcp serve --transport streamable-http --host 127.0.0.1 --port 8765
131
+ ```
132
+
133
+ Claude Code config example:
134
+
135
+ ```jsonc
136
+ {
137
+ "mcpServers": {
138
+ "cwms-tools": {
139
+ "command": "uv",
140
+ "args": ["run", "cwms-tools", "mcp", "serve", "--transport", "stdio"]
141
+ }
142
+ }
143
+ }
144
+ ```
145
+
146
+ The MCP server exposes task-level tools rather than raw endpoint mirrors:
147
+
148
+ | Tool | Purpose |
149
+ | --- | --- |
150
+ | `cwms_search_places` | Resolve an ambiguous place name to ranked locations. |
151
+ | `cwms_describe_place` | Read location, project, parameter, publisher, and freshness data in one call. |
152
+ | `cwms_list_parameters` | List parameters published at a location, grouped by publisher. |
153
+ | `cwms_get_value` | Read the latest observation, optionally with threshold status. |
154
+ | `cwms_get_history` | Read raw observations across a bounded time window. |
155
+ | `cwms_browse_region` | Browse one office's locations, optionally by state or bounding box. |
156
+ | `cwms_publishers_for_parameter` | List publishers reporting a parameter across selected offices. |
157
+ | `cwms_get_overview_section` | Read bundled CWMS orientation content. |
158
+
159
+ Resources:
160
+
161
+ - `cwms://capabilities`: server version, fingerprint, tools, and resources.
162
+ - `cwms://overview`: index of bundled CWMS overview sections.
163
+ - `cwms://overview/{section_id}{?detail}`: summary or full section body.
164
+ - `cwms://overview/{section_id}/chunk/{chunk_id}`: one large-section chunk.
165
+
166
+ ## Configuration
167
+
168
+ `cwms-tools` works anonymously by default. Environment variables are optional:
169
+
170
+ | Variable | Purpose |
171
+ | --- | --- |
172
+ | `CWMS_TOOLS_API_ROOT` | Override the CWMS Data API root. |
173
+ | `CWMS_TOOLS_CACHE_DIR` | Override the disk cache location. |
174
+ | `CWMS_TOOLS_WORKERS` | Set bounded worker concurrency. |
175
+ | `CWMS_TOOLS_REPO_URL` | Override the repository URL advertised in the user agent. |
176
+ | `CWMS_TOOLS_USER_AGENT_EXTRA` | Append extra text to the user agent. |
177
+ | `CWMS_TOOLS_OPERATOR_EMAIL` | Send a contact email via the `From` header. |
178
+ | `CWMS_TOOLS_MAX_RPS` | Declared for rate-limit policy; not enforced in v0.1.0. |
179
+ | `CWMS_API_KEY` | Reserved secret input for authenticated CDA deployments. |
180
+ | `CWMS_TOKEN` | Reserved secret input for authenticated CDA deployments. |
181
+
182
+ Inspect the resolved configuration without making a data call:
183
+
184
+ ```bash
185
+ uv run cwms-tools env
186
+ uv run cwms-tools config show --resolved
187
+ ```
188
+
189
+ ## CWMS Notes
190
+
191
+ CWMS is USACE's Corps Water Management System: the operational data platform for
192
+ federal reservoirs, flood-control dams, navigation locks, hydropower projects,
193
+ and environmental monitoring stations.
194
+
195
+ Two upstream data-shape issues come up often:
196
+
197
+ - **Ghost records.** Some catalog locations do not publish time-series data.
198
+ Search and browse responses expose `parameter_count`, `parameters`, and
199
+ `data_at` repair hints so agents can move to the data-bearing sibling.
200
+ - **Northwestern Division stubs.** `NWO`, `NWK`, `NWS`, `NWP`, and `NWW` are
201
+ near-empty CDA stubs. Use `NWDM` for Missouri River data and `NWDP` for
202
+ Pacific Northwest data. Error envelopes include repair hints when a stub is
203
+ targeted.
204
+
205
+ ## Upstream Etiquette
206
+
207
+ The CWMS Data API is a shared public service. `cwms-tools` identifies itself
208
+ with a descriptive `User-Agent`, caps concurrent requests, honors
209
+ `Retry-After`, avoids background scans, and does not cache live time-series
210
+ values. If you operate CDA and see problematic traffic from this client, please
211
+ open an issue at <https://github.com/briandconnelly/cwms-tools/issues>.
212
+
213
+ ## Development
214
+
215
+ ```bash
216
+ uv sync
217
+ uv run prek run --all-files
218
+ uv run pytest --cov=cwms_tools
219
+ uv run ty check
220
+ ```
221
+
222
+ The test suite uses unit tests and mocked CDA responses. Live CDA integration
223
+ tests are marked `integration` and are skipped unless explicitly selected.
224
+
225
+ Before opening a substantial PR, please open an issue to discuss the intended
226
+ change. The package is still pre-release, and the CLI/MCP schema contract is
227
+ the main compatibility surface.
228
+
229
+ ## License
230
+
231
+ [MIT](LICENSE)
@@ -0,0 +1,146 @@
1
+ [project]
2
+ name = "cwms-tools"
3
+ version = "0.1.0"
4
+ description = "Agent-friendly tools (MCP server and CLI) for the USACE Corps Water Management System (CWMS) Data API."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [
10
+ { name = "Brian Connelly", email = "bdc@bconnelly.net" },
11
+ ]
12
+ keywords = ["cwms", "usace", "mcp", "cli", "water", "hydrology", "agent"]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Intended Audience :: Developers",
16
+ "Intended Audience :: Science/Research",
17
+ "Operating System :: OS Independent",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Topic :: Scientific/Engineering :: Hydrology",
24
+ "Topic :: Software Development :: Libraries :: Python Modules",
25
+ ]
26
+ dependencies = [
27
+ "cwms-python>=1.0.7",
28
+ "fastmcp>=3",
29
+ "typer>=0.15",
30
+ "pydantic>=2",
31
+ "platformdirs>=4",
32
+ "diskcache>=5",
33
+ "anyio>=4",
34
+ "python-dateutil>=2.9",
35
+ ]
36
+
37
+ [project.urls]
38
+ Homepage = "https://github.com/briandconnelly/cwms-tools"
39
+ Repository = "https://github.com/briandconnelly/cwms-tools"
40
+ Issues = "https://github.com/briandconnelly/cwms-tools/issues"
41
+ Changelog = "https://github.com/briandconnelly/cwms-tools/blob/main/CHANGELOG.md"
42
+
43
+ [project.scripts]
44
+ cwms-tools = "cwms_tools.cli.app:app"
45
+
46
+ [dependency-groups]
47
+ dev = [
48
+ "pytest>=8",
49
+ "pytest-cov>=5",
50
+ "pytest-asyncio>=0.24",
51
+ "responses>=0.25",
52
+ "vcrpy>=6",
53
+ "ruff>=0.15",
54
+ "ty>=0.0.1a1",
55
+ "prek>=0.2",
56
+ ]
57
+
58
+ [build-system]
59
+ requires = ["uv_build>=0.11.14,<0.12.0"]
60
+ build-backend = "uv_build"
61
+
62
+ [tool.uv]
63
+ package = true
64
+
65
+ # --------------------------------------------------------------------------
66
+ # Ruff (lint + format)
67
+ # --------------------------------------------------------------------------
68
+ [tool.ruff]
69
+ line-length = 100
70
+ target-version = "py310"
71
+ src = ["src", "tests"]
72
+
73
+ [tool.ruff.lint]
74
+ select = [
75
+ "E", "F", "W", # pycodestyle + pyflakes
76
+ "I", # isort
77
+ "B", # bugbear
78
+ "UP", # pyupgrade
79
+ "SIM", # simplify
80
+ "PIE", # misc
81
+ "RUF", # ruff-specific
82
+ "PL", # pylint subset
83
+ "PT", # pytest style
84
+ "TID", # tidy imports
85
+ "TCH", # type-checking imports
86
+ "ARG", # unused arguments
87
+ "PTH", # use pathlib
88
+ ]
89
+ ignore = [
90
+ "PLR0913", # too many arguments — common in API wrappers
91
+ "PLR2004", # magic value used in comparison — noisy in tests
92
+ ]
93
+
94
+ [tool.ruff.lint.per-file-ignores]
95
+ # Tests get a permissive lint policy: pytest idioms regularly trip checks meant
96
+ # for production code (TID -> inline imports for monkeypatching, PT018 -> compound
97
+ # asserts, PLC0415 -> per-test imports), and ruff's en-dash heuristic fires on
98
+ # normal English in docstrings.
99
+ "tests/**" = ["ARG", "PLR2004", "S101", "TC002", "TC003", "PT018", "PLC0415", "RUF002", "RUF003"]
100
+
101
+ [tool.ruff.format]
102
+ quote-style = "double"
103
+
104
+ # --------------------------------------------------------------------------
105
+ # ty (type checker)
106
+ # --------------------------------------------------------------------------
107
+ [tool.ty.src]
108
+ include = ["src", "tests"]
109
+
110
+ [tool.ty.rules]
111
+ # Strict on our own code; permissive on third-party shapes
112
+ unresolved-import = "warn"
113
+
114
+ # --------------------------------------------------------------------------
115
+ # pytest
116
+ # --------------------------------------------------------------------------
117
+ [tool.pytest.ini_options]
118
+ minversion = "8.0"
119
+ testpaths = ["tests"]
120
+ addopts = [
121
+ "-ra",
122
+ "--strict-markers",
123
+ "--strict-config",
124
+ ]
125
+ asyncio_mode = "auto"
126
+ markers = [
127
+ "integration: live-CDA tests (skipped by default)",
128
+ "slow: slow tests",
129
+ ]
130
+
131
+ # --------------------------------------------------------------------------
132
+ # coverage
133
+ # --------------------------------------------------------------------------
134
+ [tool.coverage.run]
135
+ source = ["src/cwms_tools"]
136
+ branch = true
137
+
138
+ [tool.coverage.report]
139
+ exclude_lines = [
140
+ "pragma: no cover",
141
+ "raise NotImplementedError",
142
+ "if TYPE_CHECKING:",
143
+ "if __name__ == .__main__.:",
144
+ ]
145
+ fail_under = 80 # v0.1.0 floor; raise toward 95 as wrapper-error paths get hit
146
+ show_missing = true
@@ -0,0 +1,12 @@
1
+ """cwms-tools — agent-friendly tools for the USACE CWMS Data API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib.metadata import PackageNotFoundError, version
6
+
7
+ try:
8
+ __version__ = version("cwms-tools")
9
+ except PackageNotFoundError: # pragma: no cover
10
+ __version__ = "0.0.0+unknown"
11
+
12
+ __all__ = ["__version__"]
@@ -0,0 +1 @@
1
+ """Typer CLI adapter over the core."""