Skip to main content
Glama
zachegner

EPA Envirofacts MCP Server

by zachegner

EPA Envirofacts MCP Server

MCP Python 3.11+ License: MIT

A Model Context Protocol (MCP) server that provides AI agents with structured access to U.S. EPA environmental data through the Envirofacts API.

Features • Installation • Usage • Configuration • Contributing


Features

  • Environmental Summary by Location: Get comprehensive environmental data for any U.S. location including:

    • Nearby regulated facilities (TRI, RCRA, SDWIS, FRS)

    • Chemical release data from Toxics Release Inventory

    • Safe Drinking Water Act violations

    • Hazardous waste sites

    • Distance-based ranking and filtering

  • Search Facilities: Search for EPA-regulated facilities by name, NAICS code, state, ZIP code, or city

  • Chemical Release Data: Query TRI chemical releases by chemical name, CAS number, state, county, or year with comprehensive aggregations and optional year-over-year trends

  • Geocoding Support: Convert addresses, cities, and ZIP codes to coordinates

  • Robust Error Handling: Retry logic, timeout handling, and graceful degradation

  • Modular Architecture: Easy to extend with additional EPA data tools

  • Comprehensive Testing: Unit tests with mocks and integration tests with live API

Related MCP server: Jana MCP Server

Installation

Install using uv (fastest method):

# Install uv if you haven't already
curl -LsSf https://astral.sh/uv/install.sh | sh

# Clone and install
git clone https://github.com/zachegner/envirofacts-mcp
cd envirofacts-mcp
uv sync

Docker Installation (No Local Setup Required)

Prerequisites:

  • Docker and Docker Compose

Quick Start:

# Clone the repository
git clone https://github.com/zachegner/envirofacts-mcp
cd envirofacts-mcp

# Build and run with Docker
docker build -t epa-mcp .
docker run -i --rm epa-mcp

Using Docker Compose (Recommended):

# Clone the repository
git clone https://github.com/zachegner/envirofacts-mcp
cd envirofacts-mcp

# Copy environment configuration
cp .env.example .env
# Edit .env with your preferred settings (optional)

# Start the server
docker-compose up mcp-server

# Or run in background
docker-compose up -d mcp-server

Configuration:

Create a .env file from the template:

cp .env.example .env
# Edit .env with your preferred settings

Running Tests with Docker:

# Run unit tests
docker-compose run test

# Run integration tests (requires internet)
docker-compose run test pytest tests/ -v -m integration

Development with Docker:

The docker-compose.yml includes volume mounts for live development:

  • Source code changes are reflected immediately

  • No need to rebuild the image for code changes

  • Use docker-compose up mcp-server for development

Alternative: Traditional Installation

Prerequisites:

  • Python 3.11 or higher

  • pip (Python package manager)

Steps:

# Clone the repository
git clone https://github.com/zachegner/envirofacts-mcp
cd envirofacts-mcp

# Create virtual environment
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install dependencies
pip install -e .

Using with Claude Desktop or Other MCP Clients

Add this configuration to your MCP client settings (e.g., claude_desktop_config.json):

With Docker (Recommended for easy setup):

{
  "mcpServers": {
    "epa-envirofacts": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "epa-mcp"
      ]
    }
  }
}

With Docker Compose:

{
  "mcpServers": {
    "epa-envirofacts": {
      "command": "docker-compose",
      "args": [
        "run",
        "--rm",
        "mcp-server"
      ]
    }
  }
}

With uv (requires local Python setup):

{
  "mcpServers": {
    "epa-envirofacts": {
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/to/envirofacts-mcp",
        "run",
        "python",
        "server.py"
      ]
    }
  }
}

With traditional installation:

{
  "mcpServers": {
    "epa-envirofacts": {
      "command": "python",
      "args": ["/absolute/path/to/envirofacts-mcp/server.py"]
    }
  }
}

Configuration (Optional)

Create a .env file for custom settings:

cp .env.example .env
# Edit .env with your preferred settings

Configuration

The server can be configured through environment variables or a .env file:

# EPA API Configuration
EPA_API_BASE_URL=https://data.epa.gov/efservice/
REQUEST_TIMEOUT=300
RETRY_ATTEMPTS=3
MAX_RESULTS_PER_QUERY=1000

# Geocoding Configuration
GEOCODING_SERVICE=nominatim
GEOCODING_USER_AGENT=epa-envirofacts-mcp/1.0
GEOCODING_API_KEY=

# Logging Configuration
LOG_LEVEL=INFO

Configuration Options

  • EPA_API_BASE_URL: Base URL for EPA Envirofacts API (default: https://data.epa.gov/efservice/)

  • REQUEST_TIMEOUT: Request timeout in seconds (default: 300)

  • RETRY_ATTEMPTS: Number of retry attempts for failed requests (default: 3)

  • MAX_RESULTS_PER_QUERY: Maximum results per API query (default: 1000)

  • GEOCODING_SERVICE: Geocoding service to use (default: nominatim)

  • GEOCODING_USER_AGENT: User agent string for geocoding requests

  • LOG_LEVEL: Logging level (DEBUG, INFO, WARNING, ERROR)

Usage

Running the Server

# Build and run
docker build -t epa-mcp .
docker run -i --rm epa-mcp

# Or with Docker Compose
docker-compose up mcp-server

With uv (requires local Python setup)

uv run python server.py

Traditional Method

# Make sure your virtual environment is activated
source venv/bin/activate  # On Windows: venv\Scripts\activate
python server.py

The server will start and register the available tools. You can then connect to it using an MCP client (like Claude Desktop).

Connecting to Claude Desktop

  1. Open your Claude Desktop configuration file:

    • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

    • Windows: %APPDATA%\Claude\claude_desktop_config.json

  2. Add the EPA Envirofacts MCP server:

{
  "mcpServers": {
    "epa-envirofacts": {
      "command": "uv",
      "args": [
        "--directory",
        "/Users/yourusername/path/to/envirofacts-mcp",
        "run",
        "python",
        "server.py"
      ]
    }
  }
}
  1. Restart Claude Desktop

  2. Look for the šŸ”Œ icon to confirm the server is connected

Available Tools

1. Environmental Summary by Location

Get comprehensive environmental data for any U.S. location.

Parameters:

  • location (string): Address, city, or ZIP code (e.g., "New York, NY", "10001", "Los Angeles, CA")

  • radius_miles (float, optional): Search radius in miles (default: 5.0, max: 100.0)

Returns:

  • Location coordinates and search parameters

  • Count of facilities by type (TRI, RCRA, SDWIS, FRS)

  • Top facilities ranked by distance

  • Water systems and active violations

  • Chemical release summary with top chemicals

  • Hazardous waste sites

  • Summary statistics

Example Usage:

# Get environmental summary for NYC
summary = await get_environmental_summary_by_location("10001", radius_miles=3.0)

print(f"Found {summary.total_facilities} facilities")
print(f"Active water violations: {summary.total_violations}")
print(f"Chemical releases: {summary.chemical_releases.total_releases} pounds")

2. Search Facilities

Search for EPA-regulated facilities using various filters.

Parameters:

  • facility_name (string, optional): Partial or full facility name (uses contains matching)

  • naics_code (string, optional): NAICS industry code

  • state (string, optional): Two-letter state code (e.g., 'NY', 'CA')

  • zip_code (string, optional): 5-digit ZIP code

  • city (string, optional): City name

  • limit (int, optional): Maximum results to return (default: 100, max: 1000)

Returns:

  • List of facilities with:

    • Registry ID and facility name

    • Address and location information

    • Active EPA programs (TRI, RCRA, etc.)

    • Industry codes and descriptions

    • Facility status

Example Usage:

# Search by facility name
facilities = await search_facilities(facility_name="Chemical")

# Search by state and city
facilities = await search_facilities(state="CA", city="Los Angeles")

# Search by NAICS code
facilities = await search_facilities(naics_code="325199")

# Search with multiple parameters
facilities = await search_facilities(
    facility_name="Manufacturing",
    state="TX",
    limit=50
)

print(f"Found {len(facilities)} facilities")
for facility in facilities[:3]:
    print(f"{facility.name} - {facility.city}, {facility.state}")

3. Chemical Release Data

Query TRI chemical releases with flexible search parameters and comprehensive aggregations.

Parameters:

  • chemical_name (string, optional): Chemical name (partial match, e.g., 'benzene')

  • cas_number (string, optional): CAS Registry Number (exact match, e.g., '71-43-2')

  • state (string, optional): Two-letter state code (e.g., 'NY', 'CA')

  • county (string, optional): County name (filtered client-side)

  • year (int, optional): Reporting year (None for most recent available)

  • limit (int, optional): Maximum results to return (default: 100, max: 1000)

  • include_trends (bool, optional): Whether to calculate year-over-year trends (default: False)

  • trend_years (list, optional): Specific years for trend analysis

Returns:

  • Search parameters used

  • Summary statistics (total facilities, chemicals, releases)

  • Releases by medium (air, water, land, underground injection)

  • Facilities grouped by facility with all their chemical releases

  • Chemicals grouped by chemical with all facilities releasing them

  • Top facilities and chemicals by total releases

  • Optional year-over-year trends with percentage changes

Example Usage:

# Search by chemical name
data = await get_chemical_release_data(chemical_name="benzene", state="CA")

print(f"Found {data.total_facilities} facilities releasing benzene")
print(f"Total releases: {data.total_releases} pounds")
print(f"Air releases: {data.air_releases} pounds")

# Search by CAS number
data = await get_chemical_release_data(cas_number="71-43-2", year=2022)

# Search with trends
data = await get_chemical_release_data(
    chemical_name="lead", 
    include_trends=True,
    trend_years=[2020, 2021, 2022]
)

if data.trends:
    trend = data.trends[0]
    print(f"Trend direction: {trend.trend_direction}")
    print(f"Percentage change: {trend.percentage_change}%")

4. Facility Compliance History

Get compliance and enforcement history for EPA-regulated facilities.

Parameters:

  • registry_id (string): FRS Registry ID or program-specific ID (RCRA Handler ID, TRI Facility ID)

  • program (string, optional): Filter by program ('TRI' or 'RCRA')

  • years (int, optional): Historical years to include (default: 5, max: 20)

Returns:

  • Facility information

  • Compliance records by program

  • Violations with dates and status

  • Overall compliance status

  • Summary statistics

Example Usage:

# By FRS registry ID
compliance = await get_facility_compliance_history("110000012345")

# By program-specific ID with filter
compliance = await get_facility_compliance_history("VAD000012345", program="RCRA")

# With custom timeframe
compliance = await get_facility_compliance_history("110000012345", years=10)

print(f"Overall status: {compliance.overall_status}")
print(f"Total violations: {compliance.total_violations}")
for record in compliance.compliance_records:
    print(f"{record.program}: {record.status}")

5. Health Check

Check system health and EPA API connectivity.

Returns:

  • Server status and version

  • EPA API connectivity status

  • Configuration information

Example Queries

Environmental Summary Queries

# Major cities
await get_environmental_summary_by_location("New York, NY", 5.0)
await get_environmental_summary_by_location("Los Angeles, CA", 5.0)
await get_environmental_summary_by_location("Chicago, IL", 5.0)

# ZIP codes
await get_environmental_summary_by_location("10001", 3.0)  # NYC
await get_environmental_summary_by_location("90001", 3.0)  # LA
await get_environmental_summary_by_location("48502", 2.0)  # Flint, MI

# Full addresses
await get_environmental_summary_by_location("1600 Pennsylvania Avenue NW, Washington, DC", 2.0)

# Different radius sizes
await get_environmental_summary_by_location("Houston, TX", 1.0)   # Small radius
await get_environmental_summary_by_location("Houston, TX", 20.0)  # Large radius

Facility Search Queries

# Search by facility name
await search_facilities(facility_name="Chemical")
await search_facilities(facility_name="Manufacturing")
await search_facilities(facility_name="Power Plant")

# Search by state
await search_facilities(state="CA")
await search_facilities(state="NY")
await search_facilities(state="TX")

# Search by city
await search_facilities(city="Los Angeles")
await search_facilities(city="Houston")
await search_facilities(city="Chicago")

# Search by ZIP code
await search_facilities(zip_code="10001")  # NYC
await search_facilities(zip_code="90210")  # Beverly Hills
await search_facilities(zip_code="60601")  # Chicago

# Search by NAICS code
await search_facilities(naics_code="325199")  # Chemical manufacturing
await search_facilities(naics_code="221112")  # Electric power generation
await search_facilities(naics_code="324110")  # Petroleum refining

# Combined searches
await search_facilities(facility_name="Chemical", state="CA")
await search_facilities(city="Houston", state="TX")
await search_facilities(facility_name="Power", naics_code="221112")

Chemical Release Queries

# Search by chemical name
await get_chemical_release_data(chemical_name="benzene")
await get_chemical_release_data(chemical_name="lead")
await get_chemical_release_data(chemical_name="mercury")

# Search by CAS number
await get_chemical_release_data(cas_number="71-43-2")  # Benzene
await get_chemical_release_data(cas_number="7439-92-1")  # Lead
await get_chemical_release_data(cas_number="7439-97-6")  # Mercury

# Search by state
await get_chemical_release_data(state="CA")
await get_chemical_release_data(state="TX")
await get_chemical_release_data(state="NY")

# Search by chemical and state
await get_chemical_release_data(chemical_name="benzene", state="CA")
await get_chemical_release_data(chemical_name="lead", state="TX")
await get_chemical_release_data(cas_number="71-43-2", state="NY")

# Search by year
await get_chemical_release_data(chemical_name="benzene", year=2022)
await get_chemical_release_data(state="CA", year=2021)

# Search with trends
await get_chemical_release_data(
    chemical_name="benzene",
    include_trends=True,
    trend_years=[2020, 2021, 2022]
)
await get_chemical_release_data(
    state="CA",
    include_trends=True
)

Testing

Unit Tests

Run unit tests with mocked responses:

# With Docker (recommended)
docker-compose run test

# With uv
uv run pytest tests/ -v

# Traditional method
pytest tests/ -v

Integration Tests

Run integration tests with live EPA API calls (slower):

# With Docker
docker-compose run test pytest tests/ -v -m integration

# With uv
uv run pytest tests/ -v -m integration

# Traditional method
pytest tests/ -v -m integration

Test Coverage

Generate test coverage report:

# With Docker
docker-compose run test pytest tests/ --cov=src --cov-report=html

# With uv
uv run pytest tests/ --cov=src --cov-report=html

# Traditional method
pytest tests/ --cov=src --cov-report=html

Test Categories

  • Unit Tests: Fast tests with mocked dependencies

  • Integration Tests: Slower tests with live EPA API calls (marked with @pytest.mark.integration)

  • Slow Tests: Tests that may take longer (marked with @pytest.mark.slow)

Project Structure

envirofacts-mcp/
ā”œā”€ā”€ server.py                    # FastMCP server entry point
ā”œā”€ā”€ config.py                    # Configuration settings
ā”œā”€ā”€ requirements.txt            # Python dependencies
ā”œā”€ā”€ .env.example                 # Example environment variables
ā”œā”€ā”€ .gitignore                   # Git ignore file
ā”œā”€ā”€ README.md                    # This file
ā”œā”€ā”€ src/
│   ā”œā”€ā”€ __init__.py
│   ā”œā”€ā”€ client/                  # EPA API clients
│   │   ā”œā”€ā”€ __init__.py
│   │   ā”œā”€ā”€ base.py             # Base client with retry logic
│   │   ā”œā”€ā”€ frs.py              # FRS (Facility Registry) queries
│   │   ā”œā”€ā”€ tri.py              # TRI (Toxics Release) queries
│   │   ā”œā”€ā”€ sdwis.py            # SDWIS (Safe Drinking Water) queries
│   │   ā”œā”€ā”€ rcra.py             # RCRA (Hazardous Waste) queries
│   │   └── compliance.py        # Compliance history queries
│   ā”œā”€ā”€ models/                 # Pydantic data models
│   │   ā”œā”€ā”€ __init__.py
│   │   ā”œā”€ā”€ common.py           # Common models (LocationParams, Coordinates)
│   │   ā”œā”€ā”€ facility.py         # Facility-related models
│   │   ā”œā”€ā”€ releases.py         # Chemical release models
│   │   ā”œā”€ā”€ water.py            # Water violation models
│   │   ā”œā”€ā”€ summary.py          # EnvironmentalSummary response model
│   │   └── compliance.py       # Compliance history models
│   ā”œā”€ā”€ tools/                  # MCP tools
│   │   ā”œā”€ā”€ __init__.py
│   │   ā”œā”€ā”€ location_summary.py # Tool 1: Environmental summary
│   │   ā”œā”€ā”€ search_facilities.py # Tool 2: Search facilities
│   │   └── compliance_history.py # Tool 3: Compliance history
│   └── utils/                  # Utility functions
│       ā”œā”€ā”€ __init__.py
│       ā”œā”€ā”€ geocoding.py        # Geocoding functions
│       ā”œā”€ā”€ distance.py         # Distance calculations
│       └── aggregation.py      # Data aggregation helpers
ā”œā”€ā”€ tests/                      # Test suite
│   ā”œā”€ā”€ __init__.py
│   ā”œā”€ā”€ conftest.py            # Pytest fixtures and mocks
│   ā”œā”€ā”€ client/                # Client tests
│   │   ā”œā”€ā”€ test_base.py
│   │   ā”œā”€ā”€ test_frs.py
│   │   ā”œā”€ā”€ test_tri.py
│   │   ā”œā”€ā”€ test_sdwis.py
│   │   └── test_rcra.py
│   ā”œā”€ā”€ tools/                 # Tool tests
│   │   ā”œā”€ā”€ test_location_summary.py
│   │   ā”œā”€ā”€ test_location_summary_integration.py
│   │   ā”œā”€ā”€ test_search_facilities.py
│   │   ā”œā”€ā”€ test_search_facilities_integration.py
│   │   ā”œā”€ā”€ test_compliance_history.py
│   │   └── test_compliance_history_integration.py
│   └── utils/                 # Utility tests
│       ā”œā”€ā”€ test_geocoding.py
│       ā”œā”€ā”€ test_distance.py
│       └── test_aggregation.py
└── epa-mcp-requirements.md    # Requirements document

EPA Data Sources

The server integrates with multiple EPA data systems:

  • FRS (Facility Registry Service): Master facility database

  • TRI (Toxics Release Inventory): Chemical release data

  • SDWIS (Safe Drinking Water Information System): Water quality violations

  • RCRA (Resource Conservation and Recovery Act): Hazardous waste sites

Error Handling

The server includes comprehensive error handling:

  • Network Errors: Automatic retry with exponential backoff

  • API Timeouts: Graceful handling of EPA API 15-minute timeout

  • Geocoding Failures: Clear error messages with suggestions

  • Empty Results: Informative messages instead of errors

  • Partial Failures: Continue with available data, log warnings

Performance

  • Parallel API Calls: Uses asyncio.gather() for concurrent EPA API requests

  • Geocoding Cache: In-memory cache to avoid repeated geocoding requests

  • Rate Limiting: Respects Nominatim's 1 request/second rate limit

  • Pagination: Limits results to prevent overwhelming responses

  • Distance Filtering: Efficiently filters facilities by distance

Development

Setting Up Development Environment

# Clone the repository
git clone <repository-url>
cd envirofacts-mcp

# Install with development dependencies
uv sync --all-extras

# Or with pip
pip install -e ".[dev]"

Running Development Server

# With uv
uv run python server.py

# With auto-reload for development
uv run watchfiles "uv run python server.py" src/

Contributing

  1. Fork the repository

  2. Create a feature branch: git checkout -b feature-name

  3. Make your changes and add tests

  4. Run the test suite: uv run pytest tests/ -v

  5. Commit your changes: git commit -am 'Add feature'

  6. Push to the branch: git push origin feature-name

  7. Submit a pull request

Adding New Tools

To add a new EPA data tool:

  1. Create a new tool file in src/tools/

  2. Implement the tool function with proper error handling

  3. Add unit tests in tests/tools/

  4. Add integration tests if needed

  5. Register the tool in server.py

  6. Update documentation

Code Style

  • Follow PEP 8 style guidelines

  • Use type hints for all function parameters and return values

  • Include comprehensive docstrings

  • Write tests for all new functionality

  • Use meaningful variable and function names

Troubleshooting

Common Issues

Installation Issues:

  • Make sure you have Python 3.11 or higher: python --version

  • If using uv, ensure it's up to date: uv self update

  • Try clearing uv cache: uv cache clean

Geocoding Failures:

  • Check internet connectivity

  • Verify location string format

  • Try a different location format (ZIP code vs. city name)

  • Rate limit: Nominatim allows 1 request/second

API Timeouts:

Empty Results:

  • Try a larger search radius

  • Verify location is in the United States

  • Check if area has EPA-regulated facilities

MCP Connection Issues:

  • Verify the absolute path in your MCP client configuration

  • Check that the server starts without errors: uv run python server.py

  • Restart your MCP client (e.g., Claude Desktop)

  • Check client logs for connection errors

Debug Mode

Enable debug logging:

# With uv
LOG_LEVEL=DEBUG uv run python server.py

# Traditional method
export LOG_LEVEL=DEBUG
python server.py

Logs Location

  • Server logs: Check console output

  • Claude Desktop logs:

    • macOS: ~/Library/Logs/Claude/mcp*.log

    • Windows: %APPDATA%\Claude\logs\mcp*.log

What is MCP?

The Model Context Protocol (MCP) is an open protocol that enables AI assistants like Claude to securely connect to external data sources and tools. This server implements MCP to provide access to EPA environmental data.

Learn more: modelcontextprotocol.io

Available Data Sources

This server provides access to:

  • FRS (Facility Registry Service): Master facility database with 800,000+ facilities

  • TRI (Toxics Release Inventory): Chemical release data from industrial facilities

  • SDWIS (Safe Drinking Water Information System): Water quality and violations data

  • RCRA (Resource Conservation and Recovery Act): Hazardous waste sites and handlers

All data is sourced from the U.S. Environmental Protection Agency's public Envirofacts API.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • U.S. Environmental Protection Agency for providing the Envirofacts API

  • Anthropic for the Model Context Protocol

  • FastMCP framework for MCP server implementation

  • Geopy library for geocoding functionality

  • Astral for the uv package manager

Support

For questions, issues, or contributions:

  1. Check the troubleshooting section above

  2. Search existing issues in the repository

  3. Create a new issue with detailed information

  4. Include error messages, configuration, and steps to reproduce


Note: This server provides access to public EPA data. All data is publicly available through the EPA Envirofacts API. No API key is required for basic usage.

Disclaimer: This is an unofficial third-party implementation and is not affiliated with or endorsed by the U.S. Environmental Protection Agency.

Available Tools

5 tools
environmental_summary_by_locationA

Get comprehensive environmental data for a location.

Provides environmental summary including nearby regulated facilities, chemical releases, water quality violations, and hazardous waste sites within a specified radius.

Args: location: Address, city, or ZIP code radius_miles: Search radius in miles (default: 5.0)

Returns: Comprehensive environmental summary

ParametersJSON Schema
NameRequiredDescriptionDefault
locationYes
radius_milesNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
locationYesOriginal location query
coordinatesNoResolved coordinates
data_sourcesNoEPA data sources queried
radius_milesYesSearch radius used
summary_statsNoAdditional summary statistics
water_systemsNoWater systems in area
top_facilitiesNoTop facilities within radius
facility_countsNoCount of facilities by type
hazardous_sitesNoRCRA hazardous waste sites
query_timestampNoWhen the query was executed
total_facilitiesNoTotal facilities found
total_violationsNoTotal active violations
water_violationsNoActive water violations
chemical_releasesNoChemical release summary
total_hazardous_sitesNoTotal hazardous waste sites

TDQS

A3.6/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions the tool 'Provides environmental summary' but doesn't specify whether this is a read-only operation, potential rate limits, data freshness, or error conditions. The description is functional but lacks critical behavioral context for a tool with no annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded with the core purpose first, followed by details. The Args and Returns sections are well-structured. Minor improvement could be made by integrating the parameter explanations more seamlessly rather than as separate sections.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has an output schema, the description doesn't need to detail return values. However, for a tool with no annotations and 2 parameters, the description provides adequate purpose and parameter context but lacks behavioral transparency about how the tool operates, which is a significant gap for a data retrieval tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description compensates well by explaining both parameters in the Args section: 'location: Address, city, or ZIP code' clarifies the format beyond just 'string', and 'radius_miles: Search radius in miles (default: 5.0)' provides units and default value. This adds meaningful context that the bare schema lacks.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Get comprehensive environmental data') and resource ('for a location'), distinguishing it from siblings by specifying it provides a multi-faceted environmental summary rather than focused data on chemical releases, compliance history, or facility searches.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage context by listing the types of environmental data included, but doesn't explicitly state when to use this tool versus alternatives like get_chemical_release_data or get_facility_compliance_history_tool. It provides some guidance through the data scope but lacks explicit comparisons or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_chemical_release_dataA

Query TRI chemical releases with flexible search parameters.

Provides comprehensive chemical release data from the Toxics Release Inventory (TRI), allowing searches by chemical name, CAS number, state, county, and year.

Args: chemical_name: Chemical name (partial match) cas_number: CAS Registry Number (exact match) state: Two-letter state code county: County name (filtered client-side) year: Reporting year (None for most recent available) limit: Maximum results to return (default: 100)

Returns: Comprehensive chemical release data with aggregations

ParametersJSON Schema
NameRequiredDescriptionDefault
chemical_nameNo
cas_numberNo
stateNo
countyNo
yearNo
limitNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
chemicalsNoChemicals grouped by chemical
facilitiesNoFacilities grouped by facility
air_releasesNoTotal air releases (pounds)
data_sourcesNoData sources used
land_releasesNoTotal land releases (pounds)
search_paramsYesSearch parameters used
top_chemicalsNoTop chemicals by total releases
reporting_yearNoYear of data (if single year)
top_facilitiesNoTop facilities by total releases
total_releasesYesTotal releases across all media (pounds)
water_releasesNoTotal water releases (pounds)
query_timestampYesTimestamp of query
total_chemicalsYesTotal unique chemicals released
total_facilitiesYesTotal facilities with releases
underground_injectionsNoTotal underground injections (pounds)

TDQS

A3.6/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It mentions the tool 'allows searches' and provides parameter details, but doesn't disclose important behavioral traits like whether this is a read-only operation, potential rate limits, authentication requirements, data freshness, or what happens when no parameters are provided. The mention of 'county: County name (filtered client-side)' is useful context but insufficient for comprehensive transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with a clear purpose statement followed by detailed parameter explanations. Every sentence adds value, though the 'Returns' section is somewhat redundant given the existence of an output schema. The text is appropriately sized for a 6-parameter tool with no schema descriptions.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (6 parameters, 0% schema coverage) and the existence of an output schema, the description is reasonably complete. It thoroughly documents all parameters and their semantics. The main gaps are the lack of behavioral context (no annotations) and no guidance on when to use versus siblings, but the parameter documentation is comprehensive.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage and 6 parameters, the description provides excellent parameter semantics. It clearly explains each parameter's purpose, match behavior (partial vs exact), filtering approach, and default values. The description adds substantial meaning beyond the bare schema, fully compensating for the lack of schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool 'Query TRI chemical releases with flexible search parameters' and specifies it provides 'comprehensive chemical release data from the Toxics Release Inventory (TRI)'. It uses specific verbs ('Query', 'search') and identifies the exact resource (TRI chemical releases), distinguishing it from sibling tools that focus on facilities, compliance, or summaries.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus its siblings. While it mentions 'flexible search parameters', it doesn't explain when this tool is preferred over alternatives like 'search_facilities_tool' or 'environmental_summary_by_location'. There's no mention of use cases, prerequisites, or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_facility_compliance_history_toolA

Get compliance and enforcement history for an EPA-regulated facility.

Retrieves compliance status, violations, and enforcement history for EPA-regulated facilities across RCRA and TRI programs. Supports both FRS registry IDs and program-specific IDs with intelligent fallback logic.

Args: registry_id: FRS Registry ID or program-specific ID (RCRA Handler ID, TRI Facility ID) program: Optional program filter ('TRI' or 'RCRA') years: Historical years to include (default: 5)

Returns: Complete compliance history with facility information, compliance records, violations, and summary statistics

ParametersJSON Schema
NameRequiredDescriptionDefault
registry_idYes
programNo
yearsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
last_updatedNoDate of last compliance update
facility_infoYesFacility information
overall_statusYesOverall facility compliance status
years_analyzedYesNumber of years analyzed
total_penaltiesNoTotal penalties across all programs
total_violationsNoTotal violations across all programs
compliance_recordsNoCompliance records by program

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It adds useful context such as 'intelligent fallback logic' for IDs and default behavior for years, but it does not cover critical aspects like rate limits, authentication needs, or error handling, leaving gaps for a tool with no annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded, starting with the core purpose followed by details on retrieval scope, parameters, and returns. Every sentence adds value, but the Args and Returns sections could be integrated more seamlessly into the flow for slightly better structure.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (3 parameters, no annotations, but with output schema), the description is fairly complete. It covers purpose, parameter semantics, and return values, and the output schema reduces the need to detail return formats. However, it lacks behavioral details like rate limits or error cases, which would enhance completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It adds meaningful semantics beyond the schema by explaining registry_id accepts FRS or program-specific IDs, program filters as 'TRI' or 'RCRA', and years defaults to 5 with historical context. This compensates well for the lack of schema descriptions, though not exhaustively.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verbs ('Get', 'Retrieves') and resources ('compliance and enforcement history for an EPA-regulated facility'), distinguishing it from siblings like environmental_summary_by_location or search_facilities_tool by focusing on historical compliance data rather than summaries or searches.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage by specifying it retrieves history for EPA-regulated facilities across RCRA and TRI programs, but it does not explicitly state when to use this tool versus alternatives like get_chemical_release_data or provide exclusions. The context is clear but lacks explicit guidance on tool selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

health_checkB

System health and API connectivity check.

Returns: Health status information including EPA API connectivity

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions the tool returns 'Health status information including EPA API connectivity,' which implies a read-only, non-destructive operation. However, it lacks details on response format, error handling, rate limits, or authentication needs—leaving behavioral gaps for a tool with no annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose in the first sentence and follows with output details. It's efficient with two sentences and minimal waste, though the second sentence could be integrated more smoothly (e.g., 'Returns health status information, including EPA API connectivity').

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (0 parameters, no annotations, but an output schema exists), the description is reasonably complete. It explains what the tool does and what it returns, and since an output schema is present, it needn't detail return values. However, it could better address behavioral aspects like error cases or typical use contexts.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 0 parameters with 100% coverage, so no parameter documentation is needed. The description appropriately avoids discussing parameters, focusing instead on the tool's purpose and output. This meets the baseline for tools with no parameters, as it doesn't add unnecessary details.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose as a 'System health and API connectivity check' with specific verbs ('check') and resources ('System health', 'EPA API connectivity'). It distinguishes itself from sibling tools focused on environmental data retrieval, but doesn't explicitly contrast with hypothetical alternative health-check tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. While it's implicitly for monitoring system/API status, there's no explicit context about prerequisites, timing (e.g., during troubleshooting), or comparisons to other diagnostic tools. It merely states what it does without usage instructions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_facilities_toolA

Search for EPA-regulated facilities using various filters.

Search for facilities in the EPA's Facility Registry Service using facility name, NAICS code, state, ZIP code, or city. At least one search parameter must be provided.

Args: facility_name: Partial or full facility name (uses contains matching) naics_code: NAICS industry code state: Two-letter state code (e.g., 'NY', 'CA') zip_code: 5-digit ZIP code city: City name limit: Maximum results to return (default: 100)

Returns: List of facilities with registry ID, name, address, coordinates, active programs, industry codes, and status information

ParametersJSON Schema
NameRequiredDescriptionDefault
facility_nameNo
naics_codeNo
stateNo
zip_codeNo
cityNo
limitNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It adds useful context about the search functionality (e.g., 'uses contains matching' for facility_name) and the data source, but doesn't mention rate limits, authentication needs, or potential errors. The description doesn't contradict annotations (none exist), but could be more comprehensive for a search tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured and front-loaded with the core purpose, followed by clear sections for arguments and returns. Every sentence adds value: the first states what the tool does, the second specifies requirements, and the parameter/return details are essential given the lack of schema descriptions. No wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 6 parameters with 0% schema coverage and no annotations, the description does a good job explaining inputs and outputs. The presence of an output schema means the description doesn't need to detail return values, which it appropriately summarizes. However, for a search tool with multiple filters, more behavioral context (e.g., performance expectations) would enhance completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must fully compensate. It successfully adds meaning for all 6 parameters: it explains each filter's purpose (e.g., 'Partial or full facility name'), provides format examples (e.g., 'Two-letter state code'), and notes constraints like '5-digit ZIP code' and the default limit. This goes well beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool searches for EPA-regulated facilities using various filters, specifying the data source (EPA's Facility Registry Service) and the type of resource. It distinguishes from siblings like 'get_facility_compliance_history_tool' by focusing on search rather than compliance history, though it doesn't explicitly name alternatives.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides some usage context by stating 'At least one search parameter must be provided,' which helps avoid empty queries. However, it doesn't specify when to use this tool versus alternatives like 'environmental_summary_by_location' or 'get_facility_compliance_history_tool,' leaving the agent to infer based on tool names alone.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updatesv1.0.0
    • Addedget_chemical_release_data
    • Addedget_facility_compliance_history_tool
    • Addedsearch_facilities_tool
  2. 2 tool updates
    • First observedenvironmental_summary_by_location
    • First observedhealth_check

TDQS

A3.8/5.0

Scored across 5 tools

Disambiguation5/5

Each tool has a clearly distinct purpose with no overlap. The environmental_summary_by_location provides a comprehensive overview, get_chemical_release_data focuses on TRI chemical releases, get_facility_compliance_history_tool handles compliance history, search_facilities_tool searches for facilities, and health_check is a system utility. The descriptions clearly differentiate their scopes and use cases.

Naming Consistency3/5

The naming is mixed with some inconsistencies. environmental_summary_by_location and get_chemical_release_data follow a verb_noun pattern, but get_facility_compliance_history_tool and search_facilities_tool append '_tool' unnecessarily, and health_check is a simple compound. While readable, the deviation from a single convention reduces predictability.

Tool Count5/5

With 5 tools, this is well-scoped for an EPA data server. Each tool serves a distinct and essential function: summary, chemical data, compliance, facility search, and health check. The count is appropriate, avoiding both thin coverage and bloat, and aligns with the server's purpose of providing environmental data access.

Completeness4/5

The tool set covers core EPA data domains well, including summaries, chemical releases, facility compliance, and facility search, with no dead ends. A minor gap exists in lacking update or delete operations, but this is reasonable for a read-only data server. Agents can effectively query and analyze environmental data without significant workarounds.

Maintenance

ActivityInactive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    Not graded
    maintenance
    Provides natural language access to environmental data including air quality measurements, greenhouse gas emissions, and facility records. It enables users to perform geographic searches, trend analysis, and proximity-based queries using data from sources like OpenAQ and Climate TRACE.
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides AI agents with structured access to the U.S. EIA Open Data API for energy data including power plants, operations, fuel prices, projections, and state CO2 emissions.
    MIT