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 releases (TRI), Superfund sites, drinking water systems (SDWIS), and environmental-justice screening (EJScreen) — plus real-time AirNow air quality. Pull a facility's inspection and enforcement history, track toxic releases across a region, or screen any point for environmental-justice risk.
Tools
Tool | Description |
| Search EPA-regulated facilities by location, environmental program, or compliance status across CAA, CWA, RCRA, TRI, and SDWA |
| Full compliance profile for one facility by EPA Registry ID — compliance status, inspections, enforcement actions, penalties, and TRI IDs |
| 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 (prefix match, preferably with state), or latitude + longitude + radius_miles (max 100 miles); up to 100 results (default 50)
Returns
registryIdforepa_get_facility, county andfipsCode, coordinates, per-program flags, and five-year inspection and penalty totalsprogramstakes one of CAA, CWA, RCRA, TRI, or SDWA;has_violationkeeps facilities in significant noncompliance;active_onlykeeps facilities with an active permit
epa_get_facility tool
registry_idcomes fromepa_search_facilitiesReturns
complianceStatus, a per-statuteprogramCompliancesummary, the newest 100 inspections from the last five years (inspectionsTotalcounts them all), five years of enforcement actions with penalties, the latest TRI total withtriReleasesTransfersYear, andtriFacilityIdsforepa_get_tri_releasesIf the status lookup fails, the profile returns without
complianceStatusand anoticesays so; an unknown ID fails asfacility_not_found
epa_search_violations tool
Requires
stateorzip_code; up to 100 cases (default 50)programfilters CAA, CWA, RCRA, SDWA, CERCLA, FIFRA, or TSCA, matching any law a case cites;case_typeis civil, criminal, or all (default)date_filed_start/date_filed_end(YYYY-MM-DD, inclusive) bound the filing date. Only civil cases carry filing dates, so with a dateallreturns civil cases with anotice, andcriminalis rejected
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; forecast mode returns each issued day with
dateValid,actionDay, anddiscussion, andforecast_date(YYYY-MM-DD) keeps one dayReadings carry
categoryNameandcategoryNumber(1 Good–6 Hazardous);attributionmarks the data preliminary and unsuitable for regulatory 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_releasesorepa_get_facilitytriFacilityIds; optionalyear(1987–2030) and partialchemical_nameReturns per-chemical air, water, land, and underground-injection releases plus a one-time/non-routine total, newest reporting year first when
yearis omitted. At 500 filings, both response surfaces warn that more may exist; narrow withyearorchemical_nameQuantities are in pounds; dioxin records reported in grams are converted and carry
reportedUnit: "grams"
epa_search_tri_releases tool
Requires 2-letter
state; optionalcounty(partial match, case-insensitive), partialchemical_name, andyear; up to 200 records (default 50), newest reporting year first whenyearis omittedReturns
facilityIdforepa_get_tri_releasesand 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)Dioxin records reported 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)Proximity searches check up to 500 candidate sites, then return those within the requested radius. A
noticewarns when the candidate cap may have omitted nearby sites, even if none were returned; sites without coordinates cannot be located
epa_search_water_systems tool
Requires
stateorzip_code; up to 200 systems (default 50).zip_codematches each system's address of record, not its service areahas_violationkeeps systems with an open SDWIS violation;pws_typeis community, non-transient, or transient, returned as CWS / NTNCWS / TNCWS intypehas_violationreads at most 1,000 records; when that fills, anoticesays 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:
One
epa_tool surface over ECHO (facility compliance), Envirofacts DMAP (TRI, Superfund, SDWIS), AirNow (air quality), and the community-maintained EJAM API (EJScreen v2.2, 2022)AirNow responses cache for about one hour to stay within per-key rate limits
TRI reporting lags about 18 months; the latest reporting year is typically two years behind
Agent-friendly output:
Search results carry the IDs the next tool needs:
registryId,fipsCode,facilityId, andtriFacilityIdsepa_get_facilityreturns a partial profile with anoticewhen the status lookup failsEmpty searches return a
messagethat echoes the applied filters and suggests how to broaden them
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.3.
(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 |
|
| EJScreen (EJAM) API base URL |
|
| 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. |
|
| Byte cap per logged payload |
|
| Directory for log files (Node.js only) |
|
| Storage backend: |
|
| Enable OpenTelemetry tracing and metrics |
|
| OTLP base URL | — |
| Per-signal endpoint overrides | — |
| Enables OTLP log export | — |
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 AirNow MCP — official US current + forecast AQI, keyless, from AirNow's public file products…
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.10 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.11 npmMIT