EPA MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@EPA MCP ServerFind facilities in Houston, TX"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Public Hosted Server: https://epa.caseyjhand.com/mcp
Overview
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, a local Streamable HTTP server, or the public hosted endpoint above.
Tools
Tool | Description |
| Search EPA-regulated facilities by location, industry program, or compliance status across CAA, CWA, RCRA, TRI, and SDWA |
| Full compliance profile for one facility by EPA Registry ID — inspections, enforcement actions, and penalties |
| Search EPA civil and criminal enforcement cases by state, program, or date range |
| Current AQI observations or the issued AQI forecast from AirNow |
| Per-chemical Toxic Release Inventory data for a single facility |
| Toxic Release Inventory records across facilities in a state or county |
| Search Superfund (CERCLA/SEMS) sites by location or NPL listing status |
| Search drinking water systems (SDWIS) by state or ZIP code |
| EJScreen environmental-justice indicators for a point and buffer |
Resources
Resource | Description |
| Full compliance profile for a facility by EPA Registry ID (same data as |
| 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.
Related MCP server: mcp-epa-envirofacts
Capability reference
epa_search_facilities tool
Provide ZIP code, state, city (preferably with state), or latitude + longitude + radius_miles (max 100 miles); up to 100 results (default 50)
Returns
registryIdforepa_get_facility, plusfipsCodewhen available for Census queriesprogramsfilters CAA, CWA, RCRA, TRI, or SDWA;has_violationfilters significant violations
epa_get_facility tool
registry_idcomes fromepa_search_facilitiesReturns compliance, inspections, enforcement actions, penalties, and TRI totals; available program data survives partial upstream failures
airCompliance/waterComplianceappear only for registered programs;facility_not_foundidentifies a missing record
epa_search_violations tool
Requires
stateorzip_code; up to 100 cases (default 50)programfilters CAA, CWA, RCRA, SDWA, CERCLA, FIFRA, or TSCA;case_typeis civil, criminal, or all (default);date_filed_start/date_filed_endfilter ISO datesfacilityNameandregistryIdare unavailable from this endpoint; discover facilities withepa_search_facilities
epa_get_air_quality tool
Provide
zip_codeor bothlatitudeandlongitude;modeis current (default) or forecastCurrent mode returns each pollutant's latest hourly NowCast AQI from the closest monitor within 50 miles, with
siteName,siteID, andreportingAgencyForecast mode returns every day the reporting area's agency issued, with
dateValid,actionDay, and the agency'sdiscussion; optionalforecast_date(YYYY-MM-DD) keeps one day and is rejected in current mode. Category-only forecasts carry noaqiReadings carry
categoryNameandcategoryNumber(1 Good–6 Hazardous);attributioncredits the agencies and the U.S. EPA AirNow program and marks the data preliminary, unsuitable for regulatory, trend, or enforcement decisionsRegistered only when
AIRNOW_API_KEYis set; responses cache for about one hour
epa_get_tri_releases tool
facility_idis the TRIfacilityIdfromepa_search_tri_releases; optionalyear(1987–2030, all available by default) and partialchemical_nameReturns per-chemical air, water, land, and underground-injection releases, plus a separate one-time/non-routine total
Quantities are in pounds. TRI has facilities report dioxin and dioxin-like compounds in grams; those records are converted to pounds and carry
reportedUnit: "grams"
epa_search_tri_releases tool
Requires 2-letter
state; optionalcounty(bare name, case-insensitive partial match, soLakealso matches LAKE OF THE WOODS — each record'scountyNamesays which; a trailing County, Parish, or Borough is dropped), partialchemical_name, andyear; up to 200 records (default 50)One upstream request however sparse the filters or large the state — EPA joins facilities to their reporting forms server-side
Returns each record's one-time/non-routine total;
include_release_breakdown: trueadds air, water, land, and underground-injection releases at the cost of one more upstream request (up to ~20 seconds when EPA has not cached it)Returns
facilityIdforepa_get_tri_releases; enrichment marks results truncated when the limit is reachedDioxin and dioxin-like compounds, reported to TRI in grams, are converted to pounds and carry
reportedUnit: "grams"
epa_search_superfund tool
Provide
state/city/zip_code, orlatitude+longitude+radius_miles(0.1–500); up to 200 sites (default 50)Returns site identifiers, location, NPL, and cleanup status;
npl_statusfilters listed, not-listed, proposed, or all (default)
epa_search_water_systems tool
Requires
stateorzip_code; up to 200 systems (default 50).zip_codematches each system's address of record (often the owner's or operator's office), not its service areahas_violationkeeps systems with an open SDWIS violation; violations returned to compliance or closed out when the system was deactivated don't count.pws_typeis community, non-transient, or transient, returned as CWS / NTNCWS / TNCWS intypeWith
statealone,has_violationreads the state's first 1,000 open violation records; withzip_code, it reads up to 1,000 systems in the ZIP and checks them by PWSID. When either read fills, an enrichmentnoticesays the list may miss violating systems
epa_get_ejscreen tool
Requires
latitudeandlongitude;distancedefaults to 1,unitto miles (kilometers accepted); buffer capped at 15 milesReturns 13 environmental and 6 demographic indicators with national/state percentiles and demographic indices; outside coverage,
coverage.valid: falsecarries a noteEJScreen v2.2 (2022) comes from the community-maintained EJAM API (Public Environmental Data Partners)
epa://facility/{registry_id} resource
registry_idcomes fromepa_search_facilitiesReturns the
epa_get_facilitycompliance profile; errors when no facility resolves
epa://superfund/{site_id} resource
site_idcomes fromepa_search_superfundReturns the Superfund site record; errors when no SEMS record resolves
Features
Built 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:
Multiple environmental data sources unified behind a single
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)Parallel ECHO DFR aggregation in
epa_get_facility— 3–5 upstream calls resolved concurrently withPromise.allSettledAirNow response caching (~1 hour TTL) to stay within per-key rate limits
TRI reporting lags about 18 months; the latest available reporting year is typically two years behind
Agent-friendly output:
Facility search supplies
registryIdfor compliance lookups andfipsCodewhen available for Census queries; TRI search suppliesfacilityIdfor release detailsStructured partial failure —
epa_get_facilityreturns available program data even when one DFR endpoint is unavailable, withairCompliance/waterCompliancepresent only when that program appliesRecovery-hint messages on empty results — every search tool returns a
messagefield that echoes the applied filters and suggests how to broaden the search
Getting started
Public Hosted Instance
A public instance is available at https://epa.caseyjhand.com/mcp — no installation required. It runs without an AirNow key, so epa_get_air_quality is not available there; the other 8 tools are. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"epa-mcp-server": {
"type": "streamable-http",
"url": "https://epa.caseyjhand.com/mcp"
}
}
}Self-Hosted / Local
Add 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/mcpPrerequisites
Bun v1.4.0 or higher (or Node.js v24+); development and Docker use Bun v1.4.2.
(Optional) An AirNow API key to enable
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.
Installation
Clone the repository:
git clone https://github.com/cyanheads/epa-mcp-server.gitNavigate into the directory:
cd epa-mcp-serverInstall dependencies:
bun installConfigure environment:
cp .env.example .env
# optionally set AIRNOW_API_KEY to enable the air quality toolConfiguration
All configuration is validated at startup via Zod schemas in src/config/. Key environment variables:
Variable | Description | Default |
| Optional. Enables | — |
| ECHO API base URL |
|
| Envirofacts DMAP API base URL |
|
| AirNow API base URL |
|
| Optional. EJScreen (EJAM) API base URL used by |
|
| Transport: |
|
| HTTP server port |
|
| HTTP endpoint path |
|
| HTTP sessions: |
|
| Auth mode: |
|
| Log level (RFC 5424) |
|
| Log failed calls' arguments and results with key-name redaction. Secrets inside free-form values remain visible. |
|
| UTF-8 byte cap per logged payload. |
|
| Directory for log files (Node.js only) |
|
| Storage backend: |
|
| Enable OpenTelemetry tracing and metrics |
|
| OTLP base URL; traces use | — |
| Signal-specific endpoint overrides, used as-is. | — |
| Enables OTLP log export; the base endpoint alone does not enable it. | — |
See .env.example for the full list of optional overrides.
Running the server
Local development
Build and run:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:httpRun 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
docker build -t epa-mcp-server .
docker run --rm -e AIRNOW_API_KEY=your-key -p 3010:3010 epa-mcp-serverThe 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.
Project structure
Directory | Purpose |
|
|
| Server-specific environment variable parsing and validation with Zod. |
| Tool definitions ( |
| Resource definitions ( |
| ECHO REST API service layer — facility search, facility detail, enforcement cases. |
| Envirofacts DMAP service layer — TRI releases, Superfund sites, drinking water systems. |
| AirNow service layer — current and forecast AQI observations. |
| EJScreen (EJAM) service layer — environmental-justice indicators for a point + buffer. |
| Unit and integration tests mirroring |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
Handlers throw, framework catches — no
try/catchin tool logicUse
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storageRegister new tools and resources via the barrels in
src/mcp-server/*/definitions/index.tsWrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
ECHO searches must enforce at least one geographic parameter — unscoped queries time out against the live API
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run testLicense
Apache-2.0 — see LICENSE for details.
This server cannot be deployed
Maintenance
Related MCP Connectors
EPA ECHO MCP — wraps EPA ECHO Web Services (free, no auth)
EPA Emissions MCP — wraps EPA Envirofacts REST API (free, no auth)
EPA AQS MCP — the Air Quality System, EPA's regulatory-grade air monitoring archive
Related MCP Servers
- AlicenseCqualityAmaintenanceExposes EPA Computational Toxicology (CompTox) evidence federation through MCP, enabling chemical identity, hazard, exposure, and bioactivity data retrieval for AI agents.856Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables querying US EPA Envirofacts data using natural language, providing access to environmental information through the Pipeworx MCP gateway.20 npmMIT
- AlicenseAqualityCmaintenanceAn MCP server that exposes EPA ECHO water quality data as tools, enabling facility search, permit limits, discharge measurements, violations, and enforcement actions.6MIT
- AlicenseNot gradedqualityBmaintenanceWraps EPA ECHO Web Services (free, no auth) to enable querying environmental data via natural language.245 npmMIT