@cyanheads/epa-mcp-server 0.1.1
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.
- package/CLAUDE.md +405 -0
- package/Dockerfile +98 -0
- package/README.md +322 -0
- package/changelog/0.1.x/0.1.0.md +22 -0
- package/changelog/0.1.x/0.1.1.md +18 -0
- package/changelog/template.md +119 -0
- package/dist/config/server-config.d.ts +15 -0
- package/dist/config/server-config.d.ts.map +1 -0
- package/dist/config/server-config.js +38 -0
- package/dist/config/server-config.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server/resources/definitions/facility.resource.d.ts +9 -0
- package/dist/mcp-server/resources/definitions/facility.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/facility.resource.js +24 -0
- package/dist/mcp-server/resources/definitions/facility.resource.js.map +1 -0
- package/dist/mcp-server/resources/definitions/index.d.ts +12 -0
- package/dist/mcp-server/resources/definitions/index.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/index.js +10 -0
- package/dist/mcp-server/resources/definitions/index.js.map +1 -0
- package/dist/mcp-server/resources/definitions/superfund-site.resource.d.ts +9 -0
- package/dist/mcp-server/resources/definitions/superfund-site.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/superfund-site.resource.js +26 -0
- package/dist/mcp-server/resources/definitions/superfund-site.resource.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-air-quality.tool.d.ts +46 -0
- package/dist/mcp-server/tools/definitions/get-air-quality.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-air-quality.tool.js +193 -0
- package/dist/mcp-server/tools/definitions/get-air-quality.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-facility.tool.d.ts +61 -0
- package/dist/mcp-server/tools/definitions/get-facility.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-facility.tool.js +193 -0
- package/dist/mcp-server/tools/definitions/get-facility.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-tri-releases.tool.d.ts +26 -0
- package/dist/mcp-server/tools/definitions/get-tri-releases.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-tri-releases.tool.js +92 -0
- package/dist/mcp-server/tools/definitions/get-tri-releases.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/index.d.ts +305 -0
- package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/index.js +31 -0
- package/dist/mcp-server/tools/definitions/index.js.map +1 -0
- package/dist/mcp-server/tools/definitions/search-facilities.tool.d.ts +58 -0
- package/dist/mcp-server/tools/definitions/search-facilities.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/search-facilities.tool.js +182 -0
- package/dist/mcp-server/tools/definitions/search-facilities.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/search-superfund.tool.d.ts +49 -0
- package/dist/mcp-server/tools/definitions/search-superfund.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/search-superfund.tool.js +155 -0
- package/dist/mcp-server/tools/definitions/search-superfund.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/search-tri-releases.tool.d.ts +29 -0
- package/dist/mcp-server/tools/definitions/search-tri-releases.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/search-tri-releases.tool.js +113 -0
- package/dist/mcp-server/tools/definitions/search-tri-releases.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/search-violations.tool.d.ts +48 -0
- package/dist/mcp-server/tools/definitions/search-violations.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/search-violations.tool.js +156 -0
- package/dist/mcp-server/tools/definitions/search-violations.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/search-water-systems.tool.d.ts +38 -0
- package/dist/mcp-server/tools/definitions/search-water-systems.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/search-water-systems.tool.js +147 -0
- package/dist/mcp-server/tools/definitions/search-water-systems.tool.js.map +1 -0
- package/dist/services/airnow/airnow-service.d.ts +45 -0
- package/dist/services/airnow/airnow-service.d.ts.map +1 -0
- package/dist/services/airnow/airnow-service.js +160 -0
- package/dist/services/airnow/airnow-service.js.map +1 -0
- package/dist/services/airnow/types.d.ts +39 -0
- package/dist/services/airnow/types.d.ts.map +1 -0
- package/dist/services/airnow/types.js +6 -0
- package/dist/services/airnow/types.js.map +1 -0
- package/dist/services/dmap/dmap-service.d.ts +60 -0
- package/dist/services/dmap/dmap-service.d.ts.map +1 -0
- package/dist/services/dmap/dmap-service.js +320 -0
- package/dist/services/dmap/dmap-service.js.map +1 -0
- package/dist/services/dmap/types.d.ts +108 -0
- package/dist/services/dmap/types.d.ts.map +1 -0
- package/dist/services/dmap/types.js +6 -0
- package/dist/services/dmap/types.js.map +1 -0
- package/dist/services/echo/echo-service.d.ts +57 -0
- package/dist/services/echo/echo-service.d.ts.map +1 -0
- package/dist/services/echo/echo-service.js +295 -0
- package/dist/services/echo/echo-service.js.map +1 -0
- package/dist/services/echo/types.d.ts +198 -0
- package/dist/services/echo/types.d.ts.map +1 -0
- package/dist/services/echo/types.js +6 -0
- package/dist/services/echo/types.js.map +1 -0
- package/package.json +80 -0
- package/server.json +111 -0
package/README.md
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>@cyanheads/epa-mcp-server</h1>
|
|
3
|
+
<p><b>Access EPA environmental data — facility compliance (ECHO), toxic releases (TRI), Superfund sites, drinking water systems, and real-time air quality (AirNow) via MCP. STDIO or Streamable HTTP.</b>
|
|
4
|
+
<div>8 Tools • 2 Resources</div>
|
|
5
|
+
</p>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div align="center">
|
|
9
|
+
|
|
10
|
+
[](./CHANGELOG.md) [](./LICENSE) [](https://modelcontextprotocol.io/) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
|
+
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div align="center">
|
|
15
|
+
|
|
16
|
+
[](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
|
|
17
|
+
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Tools
|
|
23
|
+
|
|
24
|
+
8 tools spanning EPA facility compliance, toxic chemical releases, Superfund cleanup sites, drinking water safety, and real-time air quality:
|
|
25
|
+
|
|
26
|
+
| Tool | Description |
|
|
27
|
+
|:---|:---|
|
|
28
|
+
| `epa_search_facilities` | Search EPA-regulated facilities by location, industry, or compliance status across all environmental programs (CAA, CWA, RCRA, TRI, SDWA). Returns facility name, EPA Registry ID, coordinates, county FIPS, per-program compliance flags, inspection counts, penalty totals, and TRI release totals. |
|
|
29
|
+
| `epa_get_facility` | Retrieve a full compliance profile for a single EPA-regulated facility: compliance status per program, inspection dates, formal enforcement actions, penalty amounts, and TRI annual release totals. Aggregates multiple ECHO DFR endpoints in parallel. |
|
|
30
|
+
| `epa_search_violations` | Search EPA civil and criminal enforcement cases by state, regulatory program, or date range. Returns case identifier, facility name and Registry ID, programs involved, penalty assessed, settlement date, and case type. |
|
|
31
|
+
| `epa_get_air_quality` | Get AQI observations or forecasts for a location. Returns per-pollutant AQI values (PM2.5, ozone, CO, SO2, NO2), AQI category (Good through Hazardous), reporting area name, and observation timestamp. |
|
|
32
|
+
| `epa_get_tri_releases` | Query Toxic Release Inventory annual chemical release data for a specific facility by medium (air, water, land, underground injection) and reporting year. |
|
|
33
|
+
| `epa_search_tri_releases` | Search Toxic Release Inventory data across facilities in a state or county for a given year. Returns facility name, TRI ID, chemical name, total releases by medium, and facility coordinates. |
|
|
34
|
+
| `epa_search_superfund` | Search Superfund (CERCLA/SEMS) sites by location or NPL listing status. Accepts state/city/ZIP or lat/lng + radius for proximity searches. Returns site name, EPA ID, NPL status, cleanup status, and coordinates. |
|
|
35
|
+
| `epa_search_water_systems` | Search drinking water systems (SDWIS) by state or ZIP code. Returns system name, PWSID, population served, primary water source, and active violation status. |
|
|
36
|
+
|
|
37
|
+
### `epa_search_facilities`
|
|
38
|
+
|
|
39
|
+
Search for EPA-regulated facilities with cross-program compliance data.
|
|
40
|
+
|
|
41
|
+
- Geographic filters: ZIP code, state, city (city requires state)
|
|
42
|
+
- Program filter: narrow to CAA, CWA, RCRA, TRI, or SDWA registrants
|
|
43
|
+
- Compliance filter: `has_violation` flag to surface only non-compliant facilities
|
|
44
|
+
- Returns `RegistryID` (key for `epa_get_facility`), `FacFIPSCode` (county FIPS for Census chaining), and coordinates
|
|
45
|
+
- Results cap enforced — unscoped searches are prohibited at the input validation layer
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
### `epa_get_facility`
|
|
50
|
+
|
|
51
|
+
Retrieve a comprehensive compliance profile by EPA Registry ID.
|
|
52
|
+
|
|
53
|
+
- Aggregates 3–5 ECHO DFR endpoints in parallel: program flags and TRI totals, compliance summary, inspection/enforcement history, CAA details (if registered), CWA/NPDES permit details (if registered)
|
|
54
|
+
- Uses `Promise.allSettled` — partial data returned even if one upstream endpoint fails
|
|
55
|
+
- Includes formal enforcement actions, penalty amounts, and inspection dates across all programs
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
### `epa_search_violations`
|
|
60
|
+
|
|
61
|
+
Search area-level EPA enforcement cases — distinct from per-facility history in `epa_get_facility`.
|
|
62
|
+
|
|
63
|
+
- Program filter: CAA, CWA, RCRA, SDWA, CERCLA, FIFRA, or TSCA
|
|
64
|
+
- Case type: civil, criminal, or all
|
|
65
|
+
- Date range filtering by filing date (ISO 8601)
|
|
66
|
+
- Returns case identifier, affected facility name and Registry ID for downstream `epa_get_facility` lookup
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
### `epa_get_air_quality`
|
|
71
|
+
|
|
72
|
+
Get current AQI observations or daily forecasts from AirNow.
|
|
73
|
+
|
|
74
|
+
- Accepts ZIP code or latitude/longitude coordinates
|
|
75
|
+
- `mode: current` returns the latest observed AQI per pollutant; `mode: forecast` returns daily AQI forecasts (requires `forecast_date`)
|
|
76
|
+
- AQI categories: Good (1) through Hazardous (6) with numeric and text category
|
|
77
|
+
- Data is preliminary — suitable for awareness, not regulatory or enforcement decisions
|
|
78
|
+
- AirNow responses cached at ~1 hour TTL to respect rate limits
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### `epa_get_tri_releases`
|
|
83
|
+
|
|
84
|
+
Query per-chemical release breakdown for a single TRI facility.
|
|
85
|
+
|
|
86
|
+
- Accepts TRI facility ID from `epa_search_facilities` results
|
|
87
|
+
- Returns release quantities by medium: air, water, land, underground injection
|
|
88
|
+
- Optional year filter (defaults to most recent available); optional chemical name filter
|
|
89
|
+
- TRI data lags ~18 months — most recent available year is typically 2 years prior to current
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
### `epa_search_tri_releases`
|
|
94
|
+
|
|
95
|
+
Identify top polluters in a region via TRI data.
|
|
96
|
+
|
|
97
|
+
- State and optional county scope; required year parameter
|
|
98
|
+
- Optional chemical name filter to focus on a specific substance
|
|
99
|
+
- Returns facility coordinates for downstream map or proximity analysis
|
|
100
|
+
- Complement to `epa_get_tri_releases` — use this for area discovery, then drill into a specific facility
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
### `epa_search_superfund`
|
|
105
|
+
|
|
106
|
+
Search Superfund (CERCLA/SEMS) sites by location or proximity.
|
|
107
|
+
|
|
108
|
+
- Two input shapes: state/city/ZIP for administrative filters, or lat/lng + radius for proximity
|
|
109
|
+
- NPL status filter: listed, not-listed, proposed, or all
|
|
110
|
+
- Returns site cleanup status and coordinates for downstream spatial analysis
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
### `epa_search_water_systems`
|
|
115
|
+
|
|
116
|
+
Identify drinking water systems with active or recent violations.
|
|
117
|
+
|
|
118
|
+
- State and optional ZIP code scope
|
|
119
|
+
- `has_violation` flag surfaces only systems with current violations
|
|
120
|
+
- PWS type filter: community (`CWS`), non-transient non-community (`NTNCWS`), or transient non-community (`TNCWS`)
|
|
121
|
+
|
|
122
|
+
## Resources and prompts
|
|
123
|
+
|
|
124
|
+
| Type | Name | Description |
|
|
125
|
+
|:---|:---|:---|
|
|
126
|
+
| Resource | `epa://facility/{registry_id}` | Full compliance profile for a facility by EPA Registry ID (same data as `epa_get_facility`) |
|
|
127
|
+
| Resource | `epa://superfund/{site_id}` | Superfund site record by SEMS site ID |
|
|
128
|
+
|
|
129
|
+
All resource data is also reachable via tools. Use `epa_get_facility` and `epa_search_superfund` for programmatic access in tool-only MCP clients.
|
|
130
|
+
|
|
131
|
+
## Features
|
|
132
|
+
|
|
133
|
+
Built on [`@cyanheads/mcp-ts-core`](https://www.npmjs.com/package/@cyanheads/mcp-ts-core):
|
|
134
|
+
|
|
135
|
+
- Declarative tool and resource definitions — single file per primitive, framework handles registration and validation
|
|
136
|
+
- Unified error handling — handlers throw, framework catches, classifies, and formats
|
|
137
|
+
- Pluggable auth: `none`, `jwt`, `oauth`
|
|
138
|
+
- Swappable storage backends: `in-memory`, `filesystem`, `Supabase`, `Cloudflare KV/R2/D1`
|
|
139
|
+
- Structured logging with optional OpenTelemetry tracing
|
|
140
|
+
- STDIO and Streamable HTTP transports
|
|
141
|
+
|
|
142
|
+
EPA-specific:
|
|
143
|
+
|
|
144
|
+
- Three complementary EPA APIs unified behind a single `epa_` tool surface: ECHO (facility compliance), Envirofacts DMAP (TRI, Superfund, SDWIS), and AirNow (real-time air quality)
|
|
145
|
+
- Parallel ECHO DFR aggregation in `epa_get_facility` — 3–5 upstream calls resolved concurrently with `Promise.allSettled`
|
|
146
|
+
- AirNow response caching (~1 hour TTL) to stay within per-key rate limits
|
|
147
|
+
- DMAP coordinate normalization — `tri.tri_facility` DDMMSS integers converted to decimal degrees
|
|
148
|
+
|
|
149
|
+
Agent-friendly output:
|
|
150
|
+
|
|
151
|
+
- Cross-tool join keys surfaced on every response — `RegistryID` and `FacFIPSCode` from facility search feed directly into compliance, TRI, and Census API workflows
|
|
152
|
+
- Typed enforcement and compliance status fields — agents branch on data values, not string parsing
|
|
153
|
+
- Structured partial failure — `epa_get_facility` returns available program data even when one DFR endpoint is unavailable, with per-section status
|
|
154
|
+
|
|
155
|
+
## Getting started
|
|
156
|
+
|
|
157
|
+
Add the following to your MCP client configuration file. An AirNow API key is required for `epa_get_air_quality` — register free at [docs.airnowapi.org](https://docs.airnowapi.org/account/request/). ECHO and DMAP tools work without authentication.
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"mcpServers": {
|
|
162
|
+
"epa": {
|
|
163
|
+
"type": "stdio",
|
|
164
|
+
"command": "bunx",
|
|
165
|
+
"args": ["@cyanheads/epa-mcp-server@latest"],
|
|
166
|
+
"env": {
|
|
167
|
+
"MCP_TRANSPORT_TYPE": "stdio",
|
|
168
|
+
"MCP_LOG_LEVEL": "info",
|
|
169
|
+
"AIRNOW_API_KEY": "your-airnow-key"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Or with npx (no Bun required):
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"mcpServers": {
|
|
181
|
+
"epa": {
|
|
182
|
+
"type": "stdio",
|
|
183
|
+
"command": "npx",
|
|
184
|
+
"args": ["-y", "@cyanheads/epa-mcp-server@latest"],
|
|
185
|
+
"env": {
|
|
186
|
+
"MCP_TRANSPORT_TYPE": "stdio",
|
|
187
|
+
"MCP_LOG_LEVEL": "info",
|
|
188
|
+
"AIRNOW_API_KEY": "your-airnow-key"
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
For Streamable HTTP, set the transport and start the server:
|
|
196
|
+
|
|
197
|
+
```sh
|
|
198
|
+
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 AIRNOW_API_KEY=... bun run start:http
|
|
199
|
+
# Server listens at http://localhost:3010/mcp
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Prerequisites
|
|
203
|
+
|
|
204
|
+
- [Bun v1.3.0](https://bun.sh/) or higher (or Node.js v24+).
|
|
205
|
+
- An AirNow API key for `epa_get_air_quality` — register free at [docs.airnowapi.org/account/request](https://docs.airnowapi.org/account/request/). ECHO and DMAP tools require no API key.
|
|
206
|
+
|
|
207
|
+
### Installation
|
|
208
|
+
|
|
209
|
+
1. **Clone the repository:**
|
|
210
|
+
|
|
211
|
+
```sh
|
|
212
|
+
git clone https://github.com/cyanheads/epa-mcp-server.git
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
2. **Navigate into the directory:**
|
|
216
|
+
|
|
217
|
+
```sh
|
|
218
|
+
cd epa-mcp-server
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
3. **Install dependencies:**
|
|
222
|
+
|
|
223
|
+
```sh
|
|
224
|
+
bun install
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
4. **Configure environment:**
|
|
228
|
+
|
|
229
|
+
```sh
|
|
230
|
+
cp .env.example .env
|
|
231
|
+
# edit .env and set AIRNOW_API_KEY
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Configuration
|
|
235
|
+
|
|
236
|
+
All configuration is validated at startup via Zod schemas in `src/config/`. Key environment variables:
|
|
237
|
+
|
|
238
|
+
| Variable | Description | Default |
|
|
239
|
+
|:---|:---|:---|
|
|
240
|
+
| `AIRNOW_API_KEY` | **Required for `epa_get_air_quality`.** Free registration at [docs.airnowapi.org](https://docs.airnowapi.org/account/request/). ECHO and DMAP tools work without it. | — |
|
|
241
|
+
| `EPA_ECHO_BASE_URL` | ECHO API base URL | `https://echodata.epa.gov/echo` |
|
|
242
|
+
| `EPA_DMAP_BASE_URL` | Envirofacts DMAP API base URL | `https://data.epa.gov/dmapservice` |
|
|
243
|
+
| `EPA_AIRNOW_BASE_URL` | AirNow API base URL | `https://www.airnowapi.org/aq` |
|
|
244
|
+
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http` | `stdio` |
|
|
245
|
+
| `MCP_HTTP_PORT` | HTTP server port | `3010` |
|
|
246
|
+
| `MCP_HTTP_ENDPOINT_PATH` | HTTP endpoint path | `/mcp` |
|
|
247
|
+
| `MCP_AUTH_MODE` | Auth mode: `none`, `jwt`, or `oauth` | `none` |
|
|
248
|
+
| `MCP_LOG_LEVEL` | Log level (RFC 5424) | `info` |
|
|
249
|
+
| `LOGS_DIR` | Directory for log files (Node.js only) | `<project-root>/logs` |
|
|
250
|
+
| `STORAGE_PROVIDER_TYPE` | Storage backend: `in-memory`, `filesystem`, `supabase`, `cloudflare-kv/r2/d1` | `in-memory` |
|
|
251
|
+
| `OTEL_ENABLED` | Enable [OpenTelemetry](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry) tracing and metrics | `false` |
|
|
252
|
+
|
|
253
|
+
See [`.env.example`](./.env.example) for the full list of optional overrides.
|
|
254
|
+
|
|
255
|
+
## Running the server
|
|
256
|
+
|
|
257
|
+
### Local development
|
|
258
|
+
|
|
259
|
+
- **Build and run:**
|
|
260
|
+
|
|
261
|
+
```sh
|
|
262
|
+
# One-time build
|
|
263
|
+
bun run rebuild
|
|
264
|
+
|
|
265
|
+
# Run the built server
|
|
266
|
+
bun run start:stdio
|
|
267
|
+
# or
|
|
268
|
+
bun run start:http
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
- **Run checks and tests:**
|
|
272
|
+
|
|
273
|
+
```sh
|
|
274
|
+
bun run devcheck # Lint, format, typecheck, security
|
|
275
|
+
bun run test # Vitest test suite
|
|
276
|
+
bun run lint:mcp # Validate MCP definitions against spec
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Docker
|
|
280
|
+
|
|
281
|
+
```sh
|
|
282
|
+
docker build -t epa-mcp-server .
|
|
283
|
+
docker run --rm -e AIRNOW_API_KEY=your-key -p 3010:3010 epa-mcp-server
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to `/var/log/epa-mcp-server`. OpenTelemetry peer dependencies are installed by default — build with `--build-arg OTEL_ENABLED=false` to omit them.
|
|
287
|
+
|
|
288
|
+
## Project structure
|
|
289
|
+
|
|
290
|
+
| Directory | Purpose |
|
|
291
|
+
|:---|:---|
|
|
292
|
+
| `src/index.ts` | `createApp()` entry point — registers tools/resources and inits services. |
|
|
293
|
+
| `src/config` | Server-specific environment variable parsing and validation with Zod. |
|
|
294
|
+
| `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). Eight tools across ECHO, DMAP, and AirNow. |
|
|
295
|
+
| `src/mcp-server/resources` | Resource definitions (`*.resource.ts`). Facility and Superfund URI handlers. |
|
|
296
|
+
| `src/services/echo` | ECHO REST API service layer — facility search, facility detail, enforcement cases. |
|
|
297
|
+
| `src/services/dmap` | Envirofacts DMAP service layer — TRI releases, Superfund sites, drinking water systems. |
|
|
298
|
+
| `src/services/airnow` | AirNow service layer — current and forecast AQI observations. |
|
|
299
|
+
| `tests/` | Unit and integration tests mirroring `src/`. |
|
|
300
|
+
|
|
301
|
+
## Development guide
|
|
302
|
+
|
|
303
|
+
See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version:
|
|
304
|
+
|
|
305
|
+
- Handlers throw, framework catches — no `try/catch` in tool logic
|
|
306
|
+
- Use `ctx.log` for request-scoped logging, `ctx.state` for tenant-scoped storage
|
|
307
|
+
- Register new tools and resources via the barrels in `src/mcp-server/*/definitions/index.ts`
|
|
308
|
+
- Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
|
|
309
|
+
- ECHO searches must enforce at least one geographic parameter — unscoped queries time out against the live API
|
|
310
|
+
|
|
311
|
+
## Contributing
|
|
312
|
+
|
|
313
|
+
Issues and pull requests are welcome. Run checks and tests before submitting:
|
|
314
|
+
|
|
315
|
+
```sh
|
|
316
|
+
bun run devcheck
|
|
317
|
+
bun run test
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## License
|
|
321
|
+
|
|
322
|
+
Apache-2.0 — see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Initial release — EPA environmental data server (ECHO, TRI, Superfund, SDWIS, AirNow)"
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.1.0 — 2026-05-25
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`epa_search_facilities`** — search EPA-regulated facilities by location, program, and compliance status via ECHO `get_facility_info`; returns `RegistryID`, county FIPS, coordinates, per-program flags, and TRI totals
|
|
12
|
+
- **`epa_get_facility`** — full compliance profile aggregated from 3–5 parallel ECHO DFR endpoints; uses `Promise.allSettled` for partial-success resilience
|
|
13
|
+
- **`epa_search_violations`** — area-level civil and criminal enforcement case search via ECHO `case_rest_services`
|
|
14
|
+
- **`epa_get_air_quality`** — current and forecast AQI by ZIP or lat/lng via AirNow; ~1 hour response cache
|
|
15
|
+
- **`epa_get_tri_releases`** — per-chemical release breakdown by medium for a single TRI facility via DMAP `tri.tri_reporting_form`
|
|
16
|
+
- **`epa_search_tri_releases`** — area TRI emitter search by state/county and year
|
|
17
|
+
- **`epa_search_superfund`** — Superfund site search by location or lat/lng + radius via DMAP `sems.envirofacts_site`
|
|
18
|
+
- **`epa_search_water_systems`** — drinking water system search by state/ZIP via DMAP `sdwis.water_system`
|
|
19
|
+
- **`epa://facility/{registry_id}`** resource — compliance profile by EPA Registry ID
|
|
20
|
+
- **`epa://superfund/{site_id}`** resource — Superfund site record by SEMS site ID
|
|
21
|
+
- `EchoService`, `DmapService`, `AirNowService` — service layers for ECHO, Envirofacts DMAP, and AirNow APIs
|
|
22
|
+
- `AIRNOW_API_KEY` env var (required for air quality tool); ECHO and DMAP are unauthenticated
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Bug fixes from field testing — TRI field mapping, Superfund site lookup, SDWIS query, AirNow cache keys, ECHO headers, and server.json env var config"
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.1.1 — 2026-05-25
|
|
8
|
+
|
|
9
|
+
## Fixed
|
|
10
|
+
|
|
11
|
+
- **`epa_get_tri_releases` / `epa_search_tri_releases`** — corrected field name mappings from `tri_reporting_form`; chemical names and release quantities (air, water, land, underground injection) were always empty due to wrong column names ([#3](https://github.com/cyanheads/epa-mcp-server/issues/3))
|
|
12
|
+
- **`epa_search_superfund`** — `site_name` was always empty; fixed wrong field name in `normalizeSemsSite()` ([#4](https://github.com/cyanheads/epa-mcp-server/issues/4))
|
|
13
|
+
- **`epa://superfund/{site_id}` resource** — site lookup heuristic was wrong; resource never found sites by ID; fixed ID matching logic ([#6](https://github.com/cyanheads/epa-mcp-server/issues/6))
|
|
14
|
+
- **`epa_search_water_systems`** — `has_violation` filter crashed because `violation_flag` column does not exist in SDWIS; switched to correct column; city field was always missing and is now populated ([#5](https://github.com/cyanheads/epa-mcp-server/issues/5))
|
|
15
|
+
- **`epa_get_air_quality`** — cache key generation produced invalid characters (slashes from lat/lng), crashing every call; replaced with a safe `key-value` join ([#1](https://github.com/cyanheads/epa-mcp-server/issues/1))
|
|
16
|
+
- **`epa_search_facilities` / `epa_search_violations`** — ECHO API bot-blocking returned empty results silently; added `User-Agent` header to all ECHO requests ([#2](https://github.com/cyanheads/epa-mcp-server/issues/2))
|
|
17
|
+
- **`AIRNOW_API_KEY`** — env var was missing from `server.json` `environmentVariables` and `manifest.json` `user_config`; added to both ([#7](https://github.com/cyanheads/epa-mcp-server/issues/7))
|
|
18
|
+
- **`epa_get_facility`** — tool description no longer leaks internal "ECHO DFR endpoints in parallel" implementation detail ([#8](https://github.com/cyanheads/epa-mcp-server/issues/8))
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
# FORMAT REFERENCE — do not edit. Copy this file to
|
|
3
|
+
# `changelog/<major.minor>.x/<version>.md` (e.g. `changelog/0.8.x/0.8.6.md`)
|
|
4
|
+
# to author a new release. Set that file's H1 to `# <version> — YYYY-MM-DD`
|
|
5
|
+
# with a concrete date.
|
|
6
|
+
|
|
7
|
+
# Required. One-line GitHub Release-style headline. 350 character cap.
|
|
8
|
+
# Default short and scannable. Don't pad, don't stitch unrelated changes with
|
|
9
|
+
# semicolons — pick the headline. Quotes required: unquoted YAML treats `: `
|
|
10
|
+
# inside the value as a key separator and fails GitHub's strict parser.
|
|
11
|
+
summary: ""
|
|
12
|
+
|
|
13
|
+
# Set `true` when consumers must change code to upgrade: API removals,
|
|
14
|
+
# signature changes, config renames, behavior changes that break existing
|
|
15
|
+
# usage. Flagged as `Breaking` in the rollup.
|
|
16
|
+
breaking: false
|
|
17
|
+
|
|
18
|
+
# Set `true` if this release contains any security fix. Pairs with the
|
|
19
|
+
# `## Security` section below. Flagged as `Security` in the rollup so
|
|
20
|
+
# users can triage upgrade urgency at a glance.
|
|
21
|
+
security: false
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# <version> — YYYY-MM-DD
|
|
25
|
+
|
|
26
|
+
<!--
|
|
27
|
+
AUTHORING GUIDE — applies to the new per-version file you create from this
|
|
28
|
+
template.
|
|
29
|
+
|
|
30
|
+
Audience: someone scanning release notes to decide what affects them. Lead
|
|
31
|
+
each bullet with the symbol or concept name in **bold** so they can skip
|
|
32
|
+
what's irrelevant and zoom in on what's not.
|
|
33
|
+
|
|
34
|
+
Tone: terse, fact-dense, not verbose. Default to one sentence per bullet —
|
|
35
|
+
name the symbol, state what changed, stop. Use a second sentence only when
|
|
36
|
+
it carries weight. If a bullet feels long, it is.
|
|
37
|
+
|
|
38
|
+
Cut: mechanism walkthroughs (those belong in JSDoc, AGENTS.md, or the
|
|
39
|
+
relevant skill), ceremonial framings ("This release introduces…",
|
|
40
|
+
backwards-compat paragraphs), file-by-file test enumerations, internal
|
|
41
|
+
implementation notes. Prefer code/symbol names over English re-explanations.
|
|
42
|
+
|
|
43
|
+
Narrative intro: skip by default. Add one short sentence only when the
|
|
44
|
+
release theme genuinely needs framing the bullets can't carry.
|
|
45
|
+
|
|
46
|
+
Sections: Keep a Changelog order — Added, Changed, Deprecated, Removed,
|
|
47
|
+
Fixed, Security. Include only sections with entries; delete the rest
|
|
48
|
+
(including the commented-out scaffolding below). Don't ship empty headers.
|
|
49
|
+
|
|
50
|
+
Include: every distinct fact a reader needs to adopt or audit the release —
|
|
51
|
+
new exports, signatures, lint rule IDs, env vars, breaking changes, version
|
|
52
|
+
bumps on shipped skills. Nothing more.
|
|
53
|
+
|
|
54
|
+
Links: link issues, PRs, docs, or skills where they help a reader jump to
|
|
55
|
+
context. Once per item per entry — don't re-link the same issue in summary,
|
|
56
|
+
narrative, and bullet. Skip links for inline symbol names; code spans speak
|
|
57
|
+
for themselves.
|
|
58
|
+
|
|
59
|
+
Issue/PR URLs: use full URLs. GitHub's bare `#NN` auto-link only resolves
|
|
60
|
+
inside its own UI, not in npm reads or local editors.
|
|
61
|
+
|
|
62
|
+
[#38](https://github.com/cyanheads/mcp-ts-core/issues/38) ← issue
|
|
63
|
+
[#42](https://github.com/cyanheads/mcp-ts-core/pull/42) ← PR
|
|
64
|
+
|
|
65
|
+
Verify numbers exist before linking (`gh issue view NN`, `gh pr view NN`).
|
|
66
|
+
Never speculate on a future number — `#42` for an upcoming PR silently
|
|
67
|
+
resolves to whatever real item already owns 42, and timeline previews pull
|
|
68
|
+
in that unrelated item's metadata.
|
|
69
|
+
|
|
70
|
+
TAG ANNOTATIONS — the annotated tag body renders as the GitHub Release body
|
|
71
|
+
via `gh release create --notes-from-tag`. The tag is a derivative of this
|
|
72
|
+
changelog entry — a condensed, scannable version, not a copy. Format:
|
|
73
|
+
|
|
74
|
+
<theme — omit version number, GitHub prepends it>
|
|
75
|
+
← blank line
|
|
76
|
+
<1-2 sentence context: what this release does>
|
|
77
|
+
← blank line
|
|
78
|
+
Dependency bumps: ← section header
|
|
79
|
+
← blank line
|
|
80
|
+
- `@cyanheads/mcp-ts-core` ^0.9.1 → ^0.9.6 ← bullet
|
|
81
|
+
← blank line
|
|
82
|
+
Changed: ← only sections with entries
|
|
83
|
+
← blank line
|
|
84
|
+
- `format()` output includes `query` in text mode
|
|
85
|
+
← blank line
|
|
86
|
+
Added:
|
|
87
|
+
← blank line
|
|
88
|
+
- `manifest.json` scaffolded for MCPB bundle support
|
|
89
|
+
- Install badges (Claude Desktop, Cursor, VS Code)
|
|
90
|
+
← blank line
|
|
91
|
+
<N> tests pass; `bun run devcheck` clean. ← footer
|
|
92
|
+
|
|
93
|
+
Never a flat comma-separated string. Always structured markdown with
|
|
94
|
+
sections. The tag must scan well as a rendered GitHub Release page.
|
|
95
|
+
-->
|
|
96
|
+
|
|
97
|
+
## Added
|
|
98
|
+
|
|
99
|
+
-
|
|
100
|
+
|
|
101
|
+
## Changed
|
|
102
|
+
|
|
103
|
+
-
|
|
104
|
+
|
|
105
|
+
<!-- ## Deprecated
|
|
106
|
+
|
|
107
|
+
- -->
|
|
108
|
+
|
|
109
|
+
<!-- ## Removed
|
|
110
|
+
|
|
111
|
+
- -->
|
|
112
|
+
|
|
113
|
+
## Fixed
|
|
114
|
+
|
|
115
|
+
-
|
|
116
|
+
|
|
117
|
+
<!-- ## Security
|
|
118
|
+
|
|
119
|
+
- -->
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Server-specific environment variable configuration for epa-mcp-server.
|
|
3
|
+
* @module config/server-config
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
declare const ServerConfigSchema: z.ZodObject<{
|
|
7
|
+
airNowApiKey: z.ZodString;
|
|
8
|
+
echoBaseUrl: z.ZodDefault<z.ZodString>;
|
|
9
|
+
dmapBaseUrl: z.ZodDefault<z.ZodString>;
|
|
10
|
+
airNowBaseUrl: z.ZodDefault<z.ZodString>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
export type ServerConfig = z.infer<typeof ServerConfigSchema>;
|
|
13
|
+
export declare function getServerConfig(): ServerConfig;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=server-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C,QAAA,MAAM,kBAAkB;;;;;iBAoBtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CAQ9C"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Server-specific environment variable configuration for epa-mcp-server.
|
|
3
|
+
* @module config/server-config
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { parseEnvConfig } from '@cyanheads/mcp-ts-core/config';
|
|
7
|
+
const ServerConfigSchema = z.object({
|
|
8
|
+
airNowApiKey: z
|
|
9
|
+
.string()
|
|
10
|
+
.min(1)
|
|
11
|
+
.describe('AirNow API key (free registration at docs.airnowapi.org)'),
|
|
12
|
+
echoBaseUrl: z
|
|
13
|
+
.string()
|
|
14
|
+
.url()
|
|
15
|
+
.default('https://echodata.epa.gov/echo')
|
|
16
|
+
.describe('ECHO API base URL'),
|
|
17
|
+
dmapBaseUrl: z
|
|
18
|
+
.string()
|
|
19
|
+
.url()
|
|
20
|
+
.default('https://data.epa.gov/dmapservice')
|
|
21
|
+
.describe('DMAP API base URL'),
|
|
22
|
+
airNowBaseUrl: z
|
|
23
|
+
.string()
|
|
24
|
+
.url()
|
|
25
|
+
.default('https://www.airnowapi.org/aq')
|
|
26
|
+
.describe('AirNow API base URL'),
|
|
27
|
+
});
|
|
28
|
+
let _config;
|
|
29
|
+
export function getServerConfig() {
|
|
30
|
+
_config ??= parseEnvConfig(ServerConfigSchema, {
|
|
31
|
+
airNowApiKey: 'AIRNOW_API_KEY',
|
|
32
|
+
echoBaseUrl: 'EPA_ECHO_BASE_URL',
|
|
33
|
+
dmapBaseUrl: 'EPA_DMAP_BASE_URL',
|
|
34
|
+
airNowBaseUrl: 'EPA_AIRNOW_BASE_URL',
|
|
35
|
+
});
|
|
36
|
+
return _config;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=server-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-config.js","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAE/D,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,YAAY,EAAE,CAAC;SACZ,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,0DAA0D,CAAC;IACvE,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,GAAG,EAAE;SACL,OAAO,CAAC,+BAA+B,CAAC;SACxC,QAAQ,CAAC,mBAAmB,CAAC;IAChC,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,GAAG,EAAE;SACL,OAAO,CAAC,kCAAkC,CAAC;SAC3C,QAAQ,CAAC,mBAAmB,CAAC;IAChC,aAAa,EAAE,CAAC;SACb,MAAM,EAAE;SACR,GAAG,EAAE;SACL,OAAO,CAAC,8BAA8B,CAAC;SACvC,QAAQ,CAAC,qBAAqB,CAAC;CACnC,CAAC,CAAC;AAIH,IAAI,OAAiC,CAAC;AAEtC,MAAM,UAAU,eAAe;IAC7B,OAAO,KAAK,cAAc,CAAC,kBAAkB,EAAE;QAC7C,YAAY,EAAE,gBAAgB;QAC9B,WAAW,EAAE,mBAAmB;QAChC,WAAW,EAAE,mBAAmB;QAChC,aAAa,EAAE,qBAAqB;KACrC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview epa-mcp-server MCP server entry point.
|
|
4
|
+
* @module index
|
|
5
|
+
*/
|
|
6
|
+
import { createApp } from '@cyanheads/mcp-ts-core';
|
|
7
|
+
import { allResourceDefinitions } from './mcp-server/resources/definitions/index.js';
|
|
8
|
+
import { allToolDefinitions } from './mcp-server/tools/definitions/index.js';
|
|
9
|
+
import { initAirNowService } from './services/airnow/airnow-service.js';
|
|
10
|
+
import { initDmapService } from './services/dmap/dmap-service.js';
|
|
11
|
+
import { initEchoService } from './services/echo/echo-service.js';
|
|
12
|
+
await createApp({
|
|
13
|
+
tools: [...allToolDefinitions],
|
|
14
|
+
resources: [...allResourceDefinitions],
|
|
15
|
+
prompts: [],
|
|
16
|
+
instructions: 'EPA environmental data server covering facility compliance (ECHO), toxic releases (TRI), Superfund sites, drinking water systems (SDWIS), and real-time air quality (AirNow).\n' +
|
|
17
|
+
'- All tools are read-only. All APIs are US federal government public domain data.\n' +
|
|
18
|
+
'- ECHO tools require at least one geographic filter (zip_code, state, or city) — unscoped searches time out.\n' +
|
|
19
|
+
'- TRI data lags ~18 months — most recent available year is typically 2 years prior to current.\n' +
|
|
20
|
+
'- AirNow data is preliminary and not valid for regulatory, trend, or enforcement purposes.\n' +
|
|
21
|
+
'- Typical workflows: epa_search_facilities → epa_get_facility → epa_search_violations (compliance audit); epa_search_superfund (proximity); epa_search_water_systems + has_violation=true (drinking water safety).',
|
|
22
|
+
setup(core) {
|
|
23
|
+
initEchoService(core.config, core.storage);
|
|
24
|
+
initDmapService(core.config, core.storage);
|
|
25
|
+
initAirNowService(core.config, core.storage);
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAElE,MAAM,SAAS,CAAC;IACd,KAAK,EAAE,CAAC,GAAG,kBAAkB,CAAC;IAC9B,SAAS,EAAE,CAAC,GAAG,sBAAsB,CAAC;IACtC,OAAO,EAAE,EAAE;IACX,YAAY,EACV,iLAAiL;QACjL,qFAAqF;QACrF,gHAAgH;QAChH,kGAAkG;QAClG,8FAA8F;QAC9F,oNAAoN;IACtN,KAAK,CAAC,IAAI;QACR,eAAe,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3C,eAAe,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3C,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Resource for fetching a full EPA facility compliance profile by Registry ID.
|
|
3
|
+
* @module mcp-server/resources/definitions/facility.resource
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
export declare const facilityResource: import("@cyanheads/mcp-ts-core").ResourceDefinition<z.ZodObject<{
|
|
7
|
+
registry_id: z.ZodString;
|
|
8
|
+
}, z.core.$strip>, undefined, undefined>;
|
|
9
|
+
//# sourceMappingURL=facility.resource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facility.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/facility.resource.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAIrD,eAAO,MAAM,gBAAgB;;wCAuB3B,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Resource for fetching a full EPA facility compliance profile by Registry ID.
|
|
3
|
+
* @module mcp-server/resources/definitions/facility.resource
|
|
4
|
+
*/
|
|
5
|
+
import { resource, z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { notFound } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
import { getEchoService } from '../../../services/echo/echo-service.js';
|
|
8
|
+
export const facilityResource = resource('epa://facility/{registry_id}', {
|
|
9
|
+
name: 'epa-facility',
|
|
10
|
+
description: 'Full compliance profile for an EPA-regulated facility by EPA FRS Registry ID. Returns the same data as epa_get_facility.',
|
|
11
|
+
mimeType: 'application/json',
|
|
12
|
+
params: z.object({
|
|
13
|
+
registry_id: z.string().describe('EPA FRS Registry ID for the facility'),
|
|
14
|
+
}),
|
|
15
|
+
async handler(params, ctx) {
|
|
16
|
+
ctx.log.info('epa://facility resource', { registryId: params.registry_id });
|
|
17
|
+
const profile = await getEchoService().getFacility(params.registry_id, ctx);
|
|
18
|
+
if (!profile.registryId) {
|
|
19
|
+
throw notFound(`No facility found for Registry ID "${params.registry_id}". Use epa_search_facilities to discover valid Registry IDs.`, { registryId: params.registry_id });
|
|
20
|
+
}
|
|
21
|
+
return profile;
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
//# sourceMappingURL=facility.resource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facility.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/facility.resource.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjE,MAAM,CAAC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,8BAA8B,EAAE;IACvE,IAAI,EAAE,cAAc;IACpB,WAAW,EACT,0HAA0H;IAC5H,QAAQ,EAAE,kBAAkB;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;KACzE,CAAC;IAEF,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG;QACvB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAyB,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QAE5E,MAAM,OAAO,GAAG,MAAM,cAAc,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;QAE5E,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;YACxB,MAAM,QAAQ,CACZ,sCAAsC,MAAM,CAAC,WAAW,8DAA8D,EACtH,EAAE,UAAU,EAAE,MAAM,CAAC,WAAW,EAAE,CACnC,CAAC;QACJ,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Barrel export for all EPA MCP server resource definitions.
|
|
3
|
+
* @module mcp-server/resources/definitions/index
|
|
4
|
+
*/
|
|
5
|
+
export { facilityResource } from './facility.resource.js';
|
|
6
|
+
export { superfundSiteResource } from './superfund-site.resource.js';
|
|
7
|
+
export declare const allResourceDefinitions: readonly [import("@cyanheads/mcp-ts-core").ResourceDefinition<import("zod").ZodObject<{
|
|
8
|
+
registry_id: import("zod").ZodString;
|
|
9
|
+
}, import("zod/v4/core").$strip>, undefined, undefined>, import("@cyanheads/mcp-ts-core").ResourceDefinition<import("zod").ZodObject<{
|
|
10
|
+
site_id: import("zod").ZodString;
|
|
11
|
+
}, import("zod/v4/core").$strip>, undefined, undefined>];
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAKrE,eAAO,MAAM,sBAAsB;;;;wDAAqD,CAAC"}
|