Search EPA compliance, TRI, Superfund, drinking-water, EJScreen, and AirNow data.
Search and retrieve EPA environmental data: facility compliance (ECHO), toxic releases (TRI), Superfund sites, drinking water systems, environmental-justice screening (EJScreen), and real-time air quality (AirNow). STDIO or Streamable HTTP.
EPA environmental data across five federal programs — facility compliance (ECHO), toxic chemical releases (TRI), Superfund cleanup sites, drinking water systems (SDWIS), and environmental-justice screening (EJScreen) — plus real-time air quality via AirNow. Search facilities by location or compliance status, pull inspection and enforcement history, track toxic releases across a region, and screen a point for environmental-justice risk from any MCP client. Runs as a stdio process or a local Streamable HTTP server.
| Tool | Description |
|---|---|
epa_search_facilities | Search EPA-regulated facilities by location, industry program, or compliance status across CAA, CWA, RCRA, TRI, and SDWA |
epa_get_facility | Full compliance profile for one facility by EPA Registry ID — inspections, enforcement actions, and penalties |
epa_search_violations | Search EPA civil and criminal enforcement cases by state, program, or date range |
epa_get_air_quality | Current AQI observations or next-day forecasts from AirNow |
epa_get_tri_releases | Per-chemical Toxic Release Inventory data for a single facility |
epa_search_tri_releases | Toxic Release Inventory records across facilities in a state or county |
epa_search_superfund | Search Superfund (CERCLA/SEMS) sites by location or NPL listing status |
epa_search_water_systems | Search drinking water systems (SDWIS) by state or ZIP code |
epa_get_ejscreen | EJScreen environmental-justice indicators for a point and buffer |
| Resource | Description |
|---|---|
epa://facility/{registry_id} | Full compliance profile for a facility by EPA Registry ID (same data as epa_get_facility) |
epa://superfund/{site_id} | Superfund site record by SEMS site ID |
All resource data is also reachable via tools — use epa_get_facility and epa_search_superfund for programmatic access in tool-only MCP clients.
epa_search_facilities toolprograms filter narrows to CAA, CWA, RCRA, TRI, or SDWA registrants; has_violation surfaces only non-compliant facilitiesregistryId for epa_get_facility, plus fipsCode when available for Census chainingepa_get_facility toolregistry_id, obtained from epa_search_facilitiesPromise.allSettled — partial data is returned even when one upstream endpoint failsairCompliance / waterCompliance are present only when the facility is registered under that programfacility_not_found when ECHO has no record for the Registry IDepa_search_violations toolstate or zip_code is requiredprogram filter covers CAA, CWA, RCRA, SDWA, CERCLA, FIFRA, or TSCA; case_type is civil, criminal, or all (default all)date_filed_start / date_filed_end)facilityName and registryId are not populated by ECHO's enforcement-case endpoint — chain into epa_get_facility for facility detailepa_get_air_quality toolzip_code or both latitude and longitudemode: current (default) or forecast (requires forecast_date, ISO 8601)categoryNumber (1 Good – 6 Hazardous) and categoryNamedistance_miles sets the reporting-station search radius (default 25, max 300)AIRNOW_API_KEY is setepa_get_tri_releases toolfacility_id is the TRI facilityId from epa_search_tri_releases; optional year (1987–2030, defaults to all available years) and chemical_name (partial match)epa_search_tri_releases toolstate is required (2-letter); optional county (partial match), year, and chemical_nameepa_get_tri_releases — use this for area discovery, then drill into a specific facilityepa_search_superfund toolstate/city/zip_code, or latitude+longitude+radius_miles (0.1–500 miles) — one is requirednpl_status filter: listed, not-listed, proposed, or all (default all)epa_search_water_systems toolstate or zip_code is requiredhas_violation surfaces only systems with active violations; pws_type filters to community, non-transient, or transient (output type reports the SDWIS codes CWS / NTNCWS / TNCWS)epa_get_ejscreen toollatitude, longitude, distance (default 1), and unit (miles or kilometers, default miles); kilometers are converted to miles before the request, and the buffer is capped at 15 milescoverage.valid: false with a note instead of fabricated indicatorsepa://facility/{registry_id} resourceepa_get_facility; registry_id comes from epa_search_facilitiesepa://superfund/{site_id} resourceepa_search_superfund records; site_id comes from epa_search_superfundBuilt on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
EPA-specific:
epa_ tool surface: ECHO (facility compliance), Envirofacts DMAP (TRI, Superfund, SDWIS), AirNow (real-time air quality), and the community-maintained EJAM API rehosting EJScreen data (v2.2, 2022)epa_get_facility — 3–5 upstream calls resolved concurrently with Promise.allSettledAgent-friendly output:
registryId for compliance lookups and fipsCode when available for Census queries; TRI search supplies facilityId for release detailsepa_get_facility returns available program data even when one DFR endpoint is unavailable, with airCompliance/waterCompliance present only when that program appliesmessage field that echoes the applied filters and suggests how to broaden the searchAdd the following to your MCP client configuration file. An AirNow API key is optional — set AIRNOW_API_KEY to enable epa_get_air_quality (register free at docs.airnowapi.org); without it the server starts with the other 8 tools. ECHO, DMAP, and EJScreen tools work without authentication.
{
"mcpServers": {
"epa-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/epa-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"AIRNOW_API_KEY": "your-airnow-key"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"epa-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/epa-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"AIRNOW_API_KEY": "your-airnow-key"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"epa-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"-e", "AIRNOW_API_KEY=your-airnow-key",
"ghcr.io/cyanheads/epa-mcp-server:latest"
]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 AIRNOW_API_KEY=... bun run start:http
# Server listens at http://localhost:3010/mcp
epa_get_air_quality — register free at docs.airnowapi.org/account/request. Without it the server runs the other 8 tools. ECHO, DMAP, and EJScreen tools require no API key.git clone https://github.com/cyanheads/epa-mcp-server.git
cd epa-mcp-server
bun install
cp .env.example .env
# optionally set AIRNOW_API_KEY to enable the air quality tool
All configuration is validated at startup via Zod schemas in src/config/. Key environment variables:
| Variable | Description | Default |
|---|---|---|
AIRNOW_API_KEY | Optional. Enables epa_get_air_quality when set; the server runs the other 8 tools without it. Free registration at docs.airnowapi.org. | — |
EPA_ECHO_BASE_URL | ECHO API base URL | https://echodata.epa.gov/echo |
EPA_DMAP_BASE_URL | Envirofacts DMAP API base URL | https://data.epa.gov/dmapservice |
EPA_AIRNOW_BASE_URL | AirNow API base URL | https://www.airnowapi.org/aq |
EJSCREEN_API_BASE_URL | Optional. EJScreen (EJAM) API base URL used by epa_get_ejscreen. | https://api.ejanalysis.com |
MCP_TRANSPORT_TYPE | Transport: stdio or http | stdio |
MCP_HTTP_PORT | HTTP server port | 3010 |
MCP_HTTP_ENDPOINT_PATH | HTTP endpoint path | /mcp |
MCP_SESSION_MODE | HTTP sessions: auto, stateful, or stateless. Explicit environment values override the server default; auto resolves to stateful. Tenant-scoped caching is independent of sessions. | stateless |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth | none |
MCP_LOG_LEVEL | Log level (RFC 5424) | info |
LOGS_DIR | Directory for log files (Node.js only) | <project-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1 | in-memory |
OTEL_ENABLED | Enable OpenTelemetry tracing and metrics | false |
See .env.example for the full list of optional overrides.
Build and run:
# One-time build
bun run rebuild
# Run the built server
bun run start:stdio
# or
bun run start:http
Run checks and tests:
bun run devcheck # Lint, format, typecheck, security
bun run test # Vitest test suite
bun run lint:mcp # Validate MCP definitions against spec
docker build -t epa-mcp-server .
docker run --rm -e AIRNOW_API_KEY=your-key -p 3010:3010 epa-mcp-server
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.
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools/resources and inits services. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). Nine tools across ECHO, DMAP, EJAM, and AirNow. |
src/mcp-server/resources | Resource definitions (*.resource.ts). Facility and Superfund URI handlers. |
src/services/echo | ECHO REST API service layer — facility search, facility detail, enforcement cases. |
src/services/dmap | Envirofacts DMAP service layer — TRI releases, Superfund sites, drinking water systems. |
src/services/airnow | AirNow service layer — current and forecast AQI observations. |
src/services/ejscreen | EJScreen (EJAM) service layer — environmental-justice indicators for a point + buffer. |
tests/ | Unit and integration tests mirroring src/. |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for request-scoped logging, ctx.state for tenant-scoped storagesrc/mcp-server/*/definitions/index.tsIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.
Source-derived launch command. Check the maintainer’s required arguments and credentials before running:
npx -y @cyanheads/epa-mcp-serverMerge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.
{
"mcpServers": {
"io-github-cyanheads-epa-mcp-server": {
"command": "npx",
"args": [
"-y",
"@cyanheads/epa-mcp-server"
]
}
}
}Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.
Claude Desktop setup referenceio.github.cyanheads/epa-mcp-server works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.
~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.~/.cursor/mcp.jsonRestart Cursor for changes to take effect..vscode/mcp.jsonReload VS Code window for changes to take effect.~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect..mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.