Search EPA compliance, TRI, Superfund, drinking-water, EJScreen, and AirNow data.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Get current AQI observations or the issued AQI forecast for a US location from the AirNow API. Provide either zip_code or both latitude and longitude. Current mode returns the latest hourly NowCast AQI for each pollutant (PM2.5, ozone, PM10) from the closest monitor within 50 miles, with the monitor site name, site ID, and reporting agency; pollutants in one reporting area can come from different monitors. Forecast mode returns every day the reporting area's agency has issued (typically one to five), each with the AQI category, an action-day flag, and the issue date, plus the agency's forecast discussion when one exists; some agencies forecast a category without an AQI number. In forecast mode, set forecast_date to keep only the forecast valid on that date; current mode rejects it. Data are preliminary — suitable for awareness, not regulatory, trend, or enforcement decisions. Responses are cached for ~1 hour.
Data mode: "current" for the latest hourly observations, "forecast" for every forecast day the reporting area has issued
Latitude in decimal degrees (use with longitude instead of zip_code)
5-digit US ZIP code for the location
Longitude in decimal degrees (use with latitude instead of zip_code)
Forecast mode only (rejected in current mode): keep just the forecast valid on this date (YYYY-MM-DD). Omit to get every issued forecast day. When no issued day matches, the result is empty and its message lists the issued dates.
Get current AQI observations or the issued AQI forecast for a US location from the AirNow API. Provide either zip_code or both latitude and longitude. Current mode returns the latest hourly NowCast AQI for each pollutant (PM2.5, ozone, PM10) from the closest monitor within 50 miles, with the monitor site name, site ID, and reporting agency; pollutants in one reporting area can come from different monitors. Forecast mode returns every day the reporting area's agency has issued (typically one to five), each with the AQI category, an action-day flag, and the issue date, plus the agency's forecast discussion when one exists; some agencies forecast a category without an AQI number. In forecast mode, set forecast_date to keep only the forecast valid on that date; current mode rejects it. Data are preliminary — suitable for awareness, not regulatory, trend, or enforcement decisions. Responses are cached for ~1 hour.
| Argument | Type | Description |
|---|---|---|
| mode | string | Data mode: "current" for the latest hourly observations, "forecast" for every forecast day the reporting area has issued |
| latitude | number | Latitude in decimal degrees (use with longitude instead of zip_code) |
| zip_code | string | 5-digit US ZIP code for the location |
| longitude | number | Longitude in decimal degrees (use with latitude instead of zip_code) |
| forecast_date | string | Forecast mode only (rejected in current mode): keep just the forecast valid on this date (YYYY-MM-DD). Omit to get every issued forecast day. When no issued day matches, the result is empty and its message lists the issued dates. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"mode": {
"enum": [
"current",
"forecast"
],
"type": "string",
"default": "current",
"description": "Data mode: \"current\" for the latest hourly observations, \"forecast\" for every forecast day the reporting area has issued"
},
"latitude": {
"type": "number",
"description": "Latitude in decimal degrees (use with longitude instead of zip_code)"
},
"zip_code": {
"type": "string",
"description": "5-digit US ZIP code for the location"
},
"longitude": {
"type": "number",
"description": "Longitude in decimal degrees (use with latitude instead of zip_code)"
},
"forecast_date": {
"type": "string",
"description": "Forecast mode only (rejected in current mode): keep just the forecast valid on this date (YYYY-MM-DD). Omit to get every issued forecast day. When no issued day matches, the result is empty and its message lists the issued dates."
}
},
"additionalProperties": false
}Get EJScreen environmental-justice indicators for a point and surrounding buffer. Returns the 13 EJScreen environmental indicators (PM2.5, ozone, diesel particulate, NO2, lead paint, traffic proximity, Superfund/RMP/hazardous-waste/wastewater proximity, underground storage tanks, drinking-water non-compliance, and RSEI toxic air releases) and 6 demographic indicators (people of color, low income, limited English, less than high school, under 5, over 64), each with national and state percentiles plus EJ Index values, along with the Demographic Index and Supplemental Demographic Index. Data is EJScreen v2.2 (2022) served via the community-maintained EJAM API (api.ejanalysis.com, Public Environmental Data Partners), which rehosts EJScreen after EPA discontinued public access in 2025 — not a live EPA endpoint. Provide latitude, longitude, and a buffer distance (default 1 mile, max 15 miles). Points outside US coverage return a coverage note instead of indicators.
| Argument | Type | Description |
|---|---|---|
| unit | string | Unit for distance. Kilometers are converted to miles (× 0.621371) before the request. |
| distance | number | Buffer radius around the point, in the given unit (default 1). The miles-equivalent must be 15 or less. |
| latitude* | number | Latitude in decimal degrees. |
| longitude* | number | Longitude in decimal degrees. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"latitude",
"longitude"
],
"properties": {
"unit": {
"enum": [
"miles",
"kilometers"
],
"type": "string",
"default": "miles",
"description": "Unit for distance. Kilometers are converted to miles (× 0.621371) before the request."
},
"distance": {
"type": "number",
"default": 1,
"description": "Buffer radius around the point, in the given unit (default 1). The miles-equivalent must be 15 or less.",
"exclusiveMinimum": 0
},
"latitude": {
"type": "number",
"maximum": 90,
"minimum": -90,
"description": "Latitude in decimal degrees."
},
"longitude": {
"type": "number",
"maximum": 180,
"minimum": -180,
"description": "Longitude in decimal degrees."
}
},
"additionalProperties": false
}Retrieve a full compliance profile for a single EPA-regulated facility by EPA Registry ID. Returns facility metadata, ECHO's facility-level compliance status, a per-statute (CAA, CWA, RCRA, SDWA, and others ECHO tracks) compliance summary, the newest 100 inspections and other compliance monitoring records from the last five years, five years of formal enforcement actions with penalties, the latest TRI release total, and the facility's TRI facility IDs for epa_get_tri_releases. Use epa_search_facilities to discover Registry IDs. For area-level enforcement discovery use epa_search_violations instead.
| Argument | Type | Description |
|---|---|---|
| registry_id* | string | EPA FRS Registry ID for the facility (obtained from epa_search_facilities) |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"registry_id"
],
"properties": {
"registry_id": {
"type": "string",
"minLength": 1,
"description": "EPA FRS Registry ID for the facility (obtained from epa_search_facilities)"
}
},
"additionalProperties": false
}Query Toxic Release Inventory annual chemical release data for a specific facility. Returns per-chemical release records with chemical name, reporting year, routine on-site releases by medium (air, water, land, underground injection), and the one-time / non-routine release quantity. Quantities are in pounds; dioxin and dioxin-like compounds, which TRI has facilities report in grams, are converted and marked with reportedUnit. TRI data lags ~18 months — the most recent available year is typically 2 years prior to the current calendar year. facility_id is the 15-character TRI facility ID: take it from the facilityId field of epa_search_tri_releases, which also identifies top emitters across a region, or from the triFacilityIds of epa_get_facility for a facility you already hold a Registry ID for. A 12-digit EPA Registry ID is a different identifier and matches no TRI records.
| Argument | Type | Description |
|---|---|---|
| year | integer | Reporting year to retrieve. Omit for all available years, newest first. At most 500 filings are returned; narrow with year or chemical_name when capped. TRI data is typically available through ~2 years prior to the current year. |
| facility_id* | string | 15-character TRI facility ID (e.g. "9867WSNRWD1981S") — the facilityId field returned by epa_search_tri_releases, or an entry of epa_get_facility triFacilityIds. A 12-digit EPA Registry ID matches no TRI records. |
| chemical_name | string | Optional filter to restrict results to a specific chemical (partial match, case-insensitive). Commas, apostrophes, brackets, and Greek letters are matched as written, e.g. "4,4'-Methylenedianiline". |
Search EPA-regulated facilities by location, environmental program, or compliance status across CAA, CWA, RCRA, TRI, and SDWA. Returns facility name, EPA Registry ID, coordinates, county FIPS code, per-program registration flags, five-year inspection and penalty totals, and TRI release totals. Registry IDs returned here feed epa_get_facility. Supports proximity search via latitude + longitude + radius_miles. At least one geographic filter (zip_code, state, city, or a complete latitude+longitude+radius_miles triple) is required — unscoped searches time out.
| Argument | Type | Description |
|---|---|---|
| city | string | City name, case-insensitive; matches cities that begin with it ("TACOMA", or "SEAT" for SEATTLE and SEATAC). Pair with state. |
| limit | integer | Maximum number of facilities to return (1–100) |
| state | string | 2-letter US state abbreviation (e.g. "WA", "CA") |
| latitude | number | Latitude in decimal degrees. Pair with longitude and radius_miles for proximity search. |
| programs | array | Restrict results to facilities registered in one environmental program — ECHO filters on a single program per search, so pass one value. CAA=air, CWA=water, RCRA=hazardous waste, TRI=toxic releases, SDWA=drinking water. |
| zip_code | string | 5-digit ZIP code to search within |
Search Superfund (CERCLA/SEMS) sites by location or NPL listing status. Returns site name, EPA site ID, NPL status (listed/not listed/proposed), cleanup status, coordinates, and county FIPS code. Accepts either state/city/ZIP or latitude+longitude+radius for proximity searches. Use for "are there Superfund sites near X" questions.
| Argument | Type | Description |
|---|---|---|
| city | string | City name (partial match within state, case-insensitive). Punctuation is matched as written ("St. Louis", "Coeur d'Alene"). |
| limit | integer | Maximum number of sites to return (1–200) |
| state | string | 2-letter US state abbreviation to filter sites |
| latitude | number | Latitude for proximity search (use with longitude and radius_miles) |
| zip_code | string | 5-digit ZIP code to filter sites |
| longitude | number | Longitude for proximity search (use with latitude and radius_miles) |
| npl_status | string | Filter by NPL (National Priorities List) status: listed=final NPL, not-listed=not on NPL, proposed=proposed for listing, all=include all |
| radius_miles |
Search Toxic Release Inventory data across facilities in a state or county, for one reporting year or, without year, every available year newest first. Returns facility name and county, TRI facility ID, chemical name, and the one-time / non-routine release quantity; set include_release_breakdown to add routine on-site releases by medium (air, water, land, and underground-injection), at the cost of a slower call. Quantities are in pounds; dioxin and dioxin-like compounds, which TRI has facilities report in grams, are converted and marked with reportedUnit. Use to identify top polluters in an area or build an environmental exposure profile. Use epa_get_tri_releases for detailed release records for a single facility. TRI data lags ~18 months from the current calendar year.
| Argument | Type | Description |
|---|---|---|
| year | integer | Reporting year. Omit to search every available year in the state, newest reporting year first, so limit keeps the most recent filings. TRI data lags ~18 months. |
| limit | integer | Maximum number of release records to return (1–200) |
| state* | string | 2-letter US state abbreviation (required, e.g. "WA", "TX") |
| county | string | County name to narrow results within the state, e.g. "King". Case-insensitive partial match on the bare name, so a short name can also match a longer one (LAKE matches LAKE OF THE WOODS; check countyName on each record); a trailing "County", "Parish", or "Borough" is dropped before matching. Punctuation is matched as written ("Prince George's"). |
Search EPA civil and criminal enforcement cases by state, ZIP code, regulatory program, case type, or filing-date range. Returns case identifier, programs involved, penalty assessed, settlement and filing dates, and case type. Designed for area-level violation discovery — for a single known facility's enforcement history use epa_get_facility instead. At least one of state or zip_code is required.
| Argument | Type | Description |
|---|---|---|
| limit | integer | Maximum number of cases to return (1–100) |
| state | string | 2-letter US state abbreviation to filter cases (e.g. "TX", "CA") |
| program | string | Filter by regulatory program: CAA=Clean Air Act, CWA=Clean Water Act, RCRA=hazardous waste, SDWA=drinking water, CERCLA=Superfund, FIFRA=pesticides, TSCA=toxic substances. Matches any law a case cites, so a returned case's programsViolated (its primary law) can differ. |
| zip_code | string | 5-digit ZIP code of the case's facility location |
| case_type | string | Filter by case type: civil (regulatory violations), criminal (willful violations), or all. A filing-date range covers civil cases only: with a date, all returns civil cases and criminal is rejected. |
| date_filed_end | any | Latest filing date, inclusive (YYYY-MM-DD). Civil cases only. Omit, or pass "", for no upper bound. |
Search drinking water systems (SDWIS) by state or ZIP code. Returns system name, PWSID, population served, primary water source, and activity status. With has_violation, returns only systems with an open violation — one SDWIS has not marked returned to compliance or closed out when the system was deactivated. At least one of state or zip_code is required.
| Argument | Type | Description |
|---|---|---|
| limit | integer | Maximum number of systems to return (1–200) |
| state | string | 2-letter US state abbreviation (e.g. "WA", "CA") |
| pws_type | string | Filter by public water system type: community=permanent residences, non-transient=regular non-residents (schools, businesses), transient=occasional users (campgrounds) |
| zip_code | string | 5-digit ZIP code of the water system's address of record — often its owner's or operator's office, which can be far from the area the system serves |
| has_violation | boolean | When true, return only systems with an open SDWIS violation (not yet returned to compliance). Works with state, zip_code, or both. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"limit": {
"type": "integer",
"default": 50,
"maximum": 200,
"minimum": 1,
"description": "Maximum number of systems to return (1–200)"
},
"state": {
"type": "string",
"description": "2-letter US state abbreviation (e.g. \"WA\", \"CA\")"
},
"pws_type": {
"enum": [
"community",
"non-transient",
"transient"
],
"type": "string",
"description": "Filter by public water system type: community=permanent residences, non-transient=regular non-residents (schools, businesses), transient=occasional users (campgrounds)"
},
"zip_code": {
"type": "string",
"description": "5-digit ZIP code of the water system's address of record — often its owner's or operator's office, which can be far from the area the system serves"
},
"has_violation": {
"type": "boolean",
"description": "When true, return only systems with an open SDWIS violation (not yet returned to compliance). Works with state, zip_code, or both."
}
},
"additionalProperties": false
}{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"facility_id"
],
"properties": {
"year": {
"type": "integer",
"maximum": 2030,
"minimum": 1987,
"description": "Reporting year to retrieve. Omit for all available years, newest first. At most 500 filings are returned; narrow with year or chemical_name when capped. TRI data is typically available through ~2 years prior to the current year."
},
"facility_id": {
"type": "string",
"minLength": 1,
"description": "15-character TRI facility ID (e.g. \"9867WSNRWD1981S\") — the facilityId field returned by epa_search_tri_releases, or an entry of epa_get_facility triFacilityIds. A 12-digit EPA Registry ID matches no TRI records."
},
"chemical_name": {
"type": "string",
"maxLength": 4094,
"description": "Optional filter to restrict results to a specific chemical (partial match, case-insensitive). Commas, apostrophes, brackets, and Greek letters are matched as written, e.g. \"4,4'-Methylenedianiline\"."
}
},
"additionalProperties": false
}| longitude | number | Longitude in decimal degrees. Pair with latitude and radius_miles for proximity search. |
| active_only | boolean | When true, return only facilities with an active permit |
| radius_miles | number | Search radius in miles around the given latitude/longitude (max 100). Requires latitude and longitude. |
| has_violation | boolean | When true, return only facilities with at least one program in significant noncompliance (complianceStatus "Significant Violation") |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"city": {
"type": "string",
"description": "City name, case-insensitive; matches cities that begin with it (\"TACOMA\", or \"SEAT\" for SEATTLE and SEATAC). Pair with state."
},
"limit": {
"type": "integer",
"default": 50,
"maximum": 100,
"minimum": 1,
"description": "Maximum number of facilities to return (1–100)"
},
"state": {
"type": "string",
"description": "2-letter US state abbreviation (e.g. \"WA\", \"CA\")"
},
"latitude": {
"type": "number",
"maximum": 90,
"minimum": -90,
"description": "Latitude in decimal degrees. Pair with longitude and radius_miles for proximity search."
},
"programs": {
"type": "array",
"items": {
"enum": [
"CAA",
"CWA",
"RCRA",
"TRI",
"SDWA"
],
"type": "string"
},
"maxItems": 1,
"description": "Restrict results to facilities registered in one environmental program — ECHO filters on a single program per search, so pass one value. CAA=air, CWA=water, RCRA=hazardous waste, TRI=toxic releases, SDWA=drinking water."
},
"zip_code": {
"type": "string",
"description": "5-digit ZIP code to search within"
},
"longitude": {
"type": "number",
"maximum": 180,
"minimum": -180,
"description": "Longitude in decimal degrees. Pair with latitude and radius_miles for proximity search."
},
"active_only": {
"type": "boolean",
"description": "When true, return only facilities with an active permit"
},
"radius_miles": {
"type": "number",
"maximum": 100,
"description": "Search radius in miles around the given latitude/longitude (max 100). Requires latitude and longitude.",
"exclusiveMinimum": 0
},
"has_violation": {
"type": "boolean",
"description": "When true, return only facilities with at least one program in significant noncompliance (complianceStatus \"Significant Violation\")"
}
},
"additionalProperties": false
}| number |
| Search radius in miles from the given coordinates. Required when using latitude+longitude. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"city": {
"type": "string",
"maxLength": 4094,
"description": "City name (partial match within state, case-insensitive). Punctuation is matched as written (\"St. Louis\", \"Coeur d'Alene\")."
},
"limit": {
"type": "integer",
"default": 50,
"maximum": 200,
"minimum": 1,
"description": "Maximum number of sites to return (1–200)"
},
"state": {
"type": "string",
"description": "2-letter US state abbreviation to filter sites"
},
"latitude": {
"type": "number",
"description": "Latitude for proximity search (use with longitude and radius_miles)"
},
"zip_code": {
"type": "string",
"description": "5-digit ZIP code to filter sites"
},
"longitude": {
"type": "number",
"description": "Longitude for proximity search (use with latitude and radius_miles)"
},
"npl_status": {
"enum": [
"listed",
"not-listed",
"proposed",
"all"
],
"type": "string",
"default": "all",
"description": "Filter by NPL (National Priorities List) status: listed=final NPL, not-listed=not on NPL, proposed=proposed for listing, all=include all"
},
"radius_miles": {
"type": "number",
"maximum": 500,
"minimum": 0.1,
"description": "Search radius in miles from the given coordinates. Required when using latitude+longitude."
}
},
"additionalProperties": false
}| chemical_name |
| string |
| Optional filter to restrict results to a specific chemical (partial match, case-insensitive). Commas, apostrophes, brackets, and Greek letters are matched as written, e.g. "4,4'-Methylenedianiline". |
| include_release_breakdown | boolean | Add routine on-site releases by medium (air, water, land, underground injection) to each record. Costs one extra upstream request, which can add about 20 seconds when EPA has not cached it. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"state"
],
"properties": {
"year": {
"type": "integer",
"maximum": 2030,
"minimum": 1987,
"description": "Reporting year. Omit to search every available year in the state, newest reporting year first, so limit keeps the most recent filings. TRI data lags ~18 months."
},
"limit": {
"type": "integer",
"default": 50,
"maximum": 200,
"minimum": 1,
"description": "Maximum number of release records to return (1–200)"
},
"state": {
"type": "string",
"maxLength": 2,
"minLength": 2,
"description": "2-letter US state abbreviation (required, e.g. \"WA\", \"TX\")"
},
"county": {
"type": "string",
"maxLength": 4094,
"description": "County name to narrow results within the state, e.g. \"King\". Case-insensitive partial match on the bare name, so a short name can also match a longer one (LAKE matches LAKE OF THE WOODS; check countyName on each record); a trailing \"County\", \"Parish\", or \"Borough\" is dropped before matching. Punctuation is matched as written (\"Prince George's\")."
},
"chemical_name": {
"type": "string",
"maxLength": 4094,
"description": "Optional filter to restrict results to a specific chemical (partial match, case-insensitive). Commas, apostrophes, brackets, and Greek letters are matched as written, e.g. \"4,4'-Methylenedianiline\"."
},
"include_release_breakdown": {
"type": "boolean",
"default": false,
"description": "Add routine on-site releases by medium (air, water, land, underground injection) to each record. Costs one extra upstream request, which can add about 20 seconds when EPA has not cached it."
}
},
"additionalProperties": false
}| date_filed_start | any | Earliest filing date, inclusive (YYYY-MM-DD). Civil cases only. Omit, or pass "", for no lower bound. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"limit": {
"type": "integer",
"default": 50,
"maximum": 100,
"minimum": 1,
"description": "Maximum number of cases to return (1–100)"
},
"state": {
"type": "string",
"description": "2-letter US state abbreviation to filter cases (e.g. \"TX\", \"CA\")"
},
"program": {
"enum": [
"CAA",
"CWA",
"RCRA",
"SDWA",
"CERCLA",
"FIFRA",
"TSCA"
],
"type": "string",
"description": "Filter by regulatory program: CAA=Clean Air Act, CWA=Clean Water Act, RCRA=hazardous waste, SDWA=drinking water, CERCLA=Superfund, FIFRA=pesticides, TSCA=toxic substances. Matches any law a case cites, so a returned case's programsViolated (its primary law) can differ."
},
"zip_code": {
"type": "string",
"description": "5-digit ZIP code of the case's facility location"
},
"case_type": {
"enum": [
"civil",
"criminal",
"all"
],
"type": "string",
"default": "all",
"description": "Filter by case type: civil (regulatory violations), criminal (willful violations), or all. A filing-date range covers civil cases only: with a date, all returns civil cases and criminal is rejected."
},
"date_filed_end": {
"anyOf": [
{
"type": "string",
"maxLength": 0,
"minLength": 0,
"description": "No bound"
},
{
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "Date in YYYY-MM-DD form"
}
],
"description": "Latest filing date, inclusive (YYYY-MM-DD). Civil cases only. Omit, or pass \"\", for no upper bound."
},
"date_filed_start": {
"anyOf": [
{
"type": "string",
"maxLength": 0,
"minLength": 0,
"description": "No bound"
},
{
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "Date in YYYY-MM-DD form"
}
],
"description": "Earliest filing date, inclusive (YYYY-MM-DD). Civil cases only. Omit, or pass \"\", for no lower bound."
}
},
"additionalProperties": false
}