Skip to main content
Glama
skandru

Waterboards MCP Server

by skandru

Waterboards MCP Server

An MCP server that answers California drinking water questions — public water systems and their SAFER risk/violation status, source-water chemistry, and school lead results, by ZIP code or water system ID — from curated Databricks Gold views over State Water Board open data (data.ca.gov). Built with FastMCP and uv; deployed as a Databricks App.

Design decisions and the review of the original plan: req/mcp_design_review.md (local, git-ignored).

Architecture

data.ca.gov drinking water files (+ Census ZCTA centroids)
   └─ sql/drinking_water/  Bronze → Silver → Gold   (run in a Databricks SQL warehouse)
        └─ users.suresh_kandru.v_* views  (the contract in this README)
             └─ Databricks SQL warehouse
                  └─ this server (FastMCP, Databricks Apps, /mcp over HTTP)
                       └─ MCP client / agent

The server is read-only and queries only <catalog>.<schema>.v_* Gold views (plus information_schema for dataset discovery). Every caller-supplied value is bound as a named SQL parameter.

Related MCP server: SWATGenX MCP Server

Data pipeline

scripts/download_drinking_water_data.py downloads the source files. Upload them (plus the Census ZCTA gazetteer as zcta_centroids.txt) to /Volumes/users/suresh_kandru/raw/drinking_water/current/, then run in order:

Script

Layer

Output

sql/drinking_water/00_setup.sql

setup

volume, silver_clean_str helper

sql/drinking_water/01_bronze.sql

Bronze

raw all-string tables bronze_dw_*, bronze_zcta_centroids

sql/drinking_water/02_silver.sql

Silver

typed dw_systems, dw_safer, dw_lead_schools, dw_source_chemistry(_latest), dw_wells, and 50 km ZIP pair tables

sql/drinking_water/03_gold.sql

Gold

the v_* views below

sql/drinking_water/04_tap_water.sql

Bronze/Silver/Gold

DDW SDWIS tap-water results and ZIP-to-service-area lookup → v_tap_water_for_zip. Inputs tap_water_results.csv.gz and zip_service_areas.csv are built by scripts/build_tap_water_results.py and scripts/build_zip_service_areas.py

Silver turns NULL/NaN/N/A into SQL NULL, parses dates, keeps non-detects explicit (is_non_detect, result_numeric NULL, reporting_limit), and drops street addresses. Systems are located by SAFER coordinates, wells by their own coordinates, and schools by the ZIP parsed from their address.

Tools

Tool

Gold view

Purpose

waterboards_list_datasets

information_schema.tables

What datasets are available

waterboards_find_water_systems_near_zip

v_water_systems_near_zip

Water systems near a ZIP, optionally only failing/at-risk/violating

waterboards_get_water_system

v_water_systems

Profile, SAFER status, violations for one pws_id

waterboards_get_source_chemistry_near_zip

v_source_chemistry_near_zip

Latest well chemistry near a ZIP, filter by chemical

waterboards_get_system_source_chemistry

v_source_chemistry_latest

Latest well chemistry for one pws_id

waterboards_get_school_lead_near_zip

v_school_lead_near_zip

School lead sampling near a ZIP

waterboards_get_tap_water_for_zip

v_tap_water_for_zip

Latest tap/treated-water sample results for the systems serving a ZIP

All return {results, count, offset, has_more, next_offset, source, retrieved_at}, or {error, hint}. Empty results include an actionable hint.

Gold view contract

The views must exist with at least these columns (the server does SELECT * then filters/orders on them):

View

Required columns

v_water_systems

pws_id, system_name, safer_status, is_failing, has_primary_mcl_violation, has_ecoli_violation, has_treatment_technique_violation, data_as_of, source_url + profile/risk columns

v_water_systems_near_zip

zip, distance_km + all v_water_systems columns

v_source_chemistry_latest

pws_id, well_id, chemical_name, is_non_detect, result_numeric, units, reporting_limit, last_observed, source_url

v_source_chemistry_near_zip

zip, distance_km + all v_source_chemistry_latest columns

v_tap_water_for_zip

zip, pct_of_zip_covered, pws_id, system_name, water_stage, analyte_name, result_numeric, units, is_non_detect, mcl, exceeds_mcl, last_observed, source_url

v_school_lead_near_zip

zip, distance_km, school_name, action_level_exceeded, result_numeric, last_observed, source_url + school/system columns

zip + distance_km are precomputed (pairs up to 50 km), so the server filters distance_km <= :radius_km rather than doing geospatial math per request. Keep PII (street addresses, phone numbers) out of Gold.

Run locally

cp .env.example .env          # set DATABRICKS_SQL_WAREHOUSE_ID
databricks auth login --host https://<workspace>.cloud.databricks.com
uv sync --group dev
uv run python main.py         # stdio

Local auth uses the Databricks SDK's unified auth (CLI profile via DATABRICKS_CONFIG_PROFILE, or DATABRICKS_HOST + DATABRICKS_TOKEN). Connect a client with an entry like:

{
  "mcpServers": {
    "waterboards": {
      "command": "uv",
      "args": ["run", "waterboards-mcp"],
      "cwd": "/absolute/path/to/this/repo"
    }
  }
}

HTTP mode (what Databricks Apps runs): MCP_TRANSPORT=streamable-http uv run python main.py, then curl localhost:8000/health.

Verify

uv run pytest tests/ -v
uv run ruff check . && uv run ruff format --check .

Tests mock the Databricks SDK; no workspace is needed.

Add a tool

One tool per file in src/waterboards_mcp/tools/, exposing register(mcp); add it to register_tools in tools/__init__.py. Use ZipCode/RadiusKm/PaginationParams from models.py for validated inputs and call warehouse.query_gold(...) with static where/order_by SQL and Params for values. Document source, refresh cadence, and result shape in the docstring — the model reads it.

Deploy

See deploy/databricks/README.md.

Evaluate

See eval/README.md and the mcp-eval skill.

License

See LICENSE.

Available Tools

7 tools
waterboards_find_water_systems_near_zipFind public water systems near a ZIP codeA
Read-onlyIdempotent

Find drinking water systems serving the area near a ZIP code.

Source: Gold view v_water_systems_near_zip (California State Water Board SDWIS inventory + SAFER risk assessment). Refreshed when the pipeline reloads the open data; each result has data_as_of. Only systems with coordinates in the SAFER assessment can be located. Distance is from the ZIP centroid to the system's location, so a nearby system does not necessarily serve that ZIP. Ordered nearest first.

Set flagged_only to return only systems that are Failing, At-Risk, or have an MCL, E. coli, or treatment-technique violation.

Each result has pws_id, system_name, county, city, federal_type, total_population, distance_km, safer_status, is_failing, violation flags/analytes, risk levels, and source_url. Use pws_id with waterboards_get_water_system for the full profile. SAFER status is a regulatory screening, not a tap-water safety determination.

ParametersJSON Schema
NameRequiredDescriptionDefault
zip_codeYes5-digit US ZIP code, e.g. 94103.
radius_kmNoSearch radius in kilometers (1-50).
paginationNo
flagged_onlyNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already cover the safe-read profile (readOnly, idempotent, non-destructive, openWorld), so the description is free to add genuinely new context: the Gold-view source and refresh cadence, the `data_as_of` freshness field, the coverage limitation that only geocoded SAFER systems can be located, nearest-first ordering, and the disclaimer that SAFER status is regulatory screening rather than a tap-water safety determination. That is a rich disclosure set beyond structured fields.

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?

Front-loads purpose, then source/provenance, limitation, distance semantics, the flagged_only option, and result fields. Most sentences earn their place, though the parenthetical provenance line and the full field enumeration are somewhat heavy for a description whose output schema already exists.

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

Completeness5/5

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

For a geo-search tool with an output schema, an agent has everything needed: what it returns, what it cannot return (ungeocoded systems), how distance is computed, ordering, freshness via `data_as_of`, the special-case filter, and where to go next for a full profile. Nothing material is left to inference.

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 50% schema coverage, the description compensates for the undocumented `flagged_only` parameter by spelling out exactly which statuses/violations it selects. It also clarifies that `distance_km` is measured from ZIP centroid to system location, which the schema does not say. It adds no extra syntax for `radius_km` or the pagination object beyond what the schema provides.

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?

States a specific verb+resource ("Find drinking water systems serving the area near a ZIP code") and its positional scope, and names the downstream sibling by telling the agent to use `pws_id` with waterboards_get_water_system for the full profile. That cross-reference plus the ZIP/area framing separates it from get_tap_water_for_zip and the source-chemistry siblings without either schema being opened.

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

Usage Guidelines4/5

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

Gives an explicit condition for the `flagged_only` variant (Failing, At-Risk, MCL/E. coli/treatment-technique violation) and routes the agent onward via `pws_id`. It also flags a real usage trap ("a nearby system does not necessarily serve that ZIP"). It stops short of stating when to prefer this over get_tap_water_for_zip, so no explicit exclusion is given.

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

waterboards_get_school_lead_near_zipGet school drinking water lead results near a ZIP codeA
Read-onlyIdempotent

Get lead-in-drinking-water results at K-12 schools near a ZIP code.

Source: Gold view v_school_lead_near_zip (California State Water Board school lead sampling, 2020 onward; the data on file stops at the newest last_observed, so say so when results are old). Distance is between ZIP centroids. Ordered nearest first, then most recent.

exceedances_only returns only samples the state flagged as above the lead action level.

Each result has school_name, district, school_zip, sample_location, result_numeric and units (UG/L), reporting_limit, is_non_detect (result_numeric is null when true), action_level_exceeded, follow_up_action, last_observed (sample date), pws_id, and source_url. These are point-in-time samples, not a health determination.

ParametersJSON Schema
NameRequiredDescriptionDefault
zip_codeYes5-digit US ZIP code, e.g. 94103.
radius_kmNoSearch radius in kilometers (1-50).
paginationNo
exceedances_onlyNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior5/5

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

Beyond the annotations (readOnly/idempotent/openWorld), it discloses the underlying view, the 2020-onward coverage window with the 'data stops at newest last_observed' caveat, that distance is measured between ZIP centroids, the sort order, and that results are point-in-time samples and not a health determination. That is substantial behavioral context the structured fields do not carry.

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?

Front-loaded with purpose, then source, ordering, filter, fields, and caveat in a logical order, with no filler. The full field enumeration is partly redundant given an output schema exists, which keeps it short of a 5.

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

Completeness5/5

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

For a read-only geo-filtered list tool it covers source provenance, coverage limits, ordering, the filter flag, key field semantics (units, non-detect nulls), and the health-interpretation caveat. With an output schema present, return values need no further explanation.

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 coverage is 50%; the description supplies the missing semantics for exceedances_only ('only samples the state flagged as above the lead action level') and clarifies that distance is ZIP-centroid based. It adds real meaning beyond the schema, though it doesn't elaborate on radius_km or pagination behavior.

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?

States a specific verb and resource: 'Get lead-in-drinking-water results at K-12 schools near a ZIP code.' The school-lead-sampling scope is concrete enough that an agent can distinguish it from siblings like get_tap_water_for_zip and get_source_chemistry_near_zip without opening any schema.

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 gives clear context (California school lead sampling, 2020 onward, ZIP-centroid distance, nearest-first ordering) and guidance on the exceedances_only flag, but never states when to prefer this tool over the sibling chemistry/tap-water tools or any exclusions. Usage is implied rather than directed.

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

waterboards_get_source_chemistry_near_zipGet drinking water source chemistry near a ZIP codeA
Read-onlyIdempotent

Get latest source-water chemistry for wells near a ZIP code.

Source: Gold view v_source_chemistry_near_zip (DDW/GAMA drinking water source chemistry, 2020 to present). Refreshed when the pipeline reloads the open data; each result has last_observed. Ordered nearest well first, then most recent.

chemical is a case-insensitive name fragment (e.g. 'arsenic', 'nitrate', 'PFOA'); omit it to see every chemical, which is large. Set detected_only to skip non-detects.

Each result is the latest sample for one well and chemical: pws_id, system_name, well_id, chemical_name, result_numeric, units, reporting_limit, is_non_detect, last_observed (sample date), sample_count, and source_url. When is_non_detect is true, result_numeric is null and the chemical was not found above reporting_limit. This is raw source water before treatment, not tap water, and not a health determination; units vary by chemical (UG/L, NG/L, MG/L, ...) so never compare across units.

ParametersJSON Schema
NameRequiredDescriptionDefault
chemicalNo
zip_codeYes5-digit US ZIP code, e.g. 94103.
radius_kmNoSearch radius in kilometers (1-50).
paginationNo
detected_onlyNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior5/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive), and the description layers substantial extra context: the data source and refresh cadence, that results carry `last_observed`, ordering by nearest then most recent, non-detect semantics (result_numeric null), and strong caveats about units varying so results must not be compared across units. This goes well beyond what structured fields provide.

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?

Front-loads purpose, then data provenance, then filter semantics, then caveats. Dense but each paragraph earns its place; the caveat block is slightly long but carries real safety information (units, non-detects).

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?

An output schema exists so return fields need not be re-explained, yet the description usefully interprets them (last_observed, is_non_detect behavior). Combined with refresh cadence and unit caveats, coverage is strong; only radius and pagination handling are left implicit.

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 40%, so the description must compensate, and it does for the two most ambiguous params: `chemical` is a case-insensitive name fragment with examples and an omit-to-see-all option, and `detected_only` is explained. It leaves `radius_km` behavior and `pagination` unaddressed, so a minor gap remains.

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?

States a specific verb (get latest source-water chemistry) plus resource and scope (wells near a ZIP code), and immediately differentiates from siblings by noting this is raw source water, not tap water. An agent can distinguish it from get_system_source_chemistry (system-scoped) and get_tap_water_for_zip without opening a schema.

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

Usage Guidelines4/5

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

Gives practical selection guidance: omit `chemical` to see everything (with a warning it is large), set `detected_only` to skip non-detects, and clarifies the dataset is raw source water, not treated tap water. It does not, however, name sibling alternatives (e.g. get_tap_water_for_zip, get_system_source_chemistry) explicitly as substitutes, so routing is implied rather than stated.

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

waterboards_get_system_source_chemistryGet source chemistry for a water systemA
Read-onlyIdempotent

Get latest source-water chemistry for one water system's wells.

Source: Gold view v_source_chemistry_latest (DDW/GAMA drinking water source chemistry, 2020 to present). Refreshed when the pipeline reloads the open data; each result has last_observed. Ordered by chemical, then well. Get pws_id from waterboards_find_water_systems_near_zip.

chemical is a case-insensitive name fragment; detected_only skips non-detects.

Each result is the latest sample for one well and chemical: pws_id, system_name, well_id, chemical_name, result_numeric, units, reporting_limit, is_non_detect, last_observed (sample date), sample_count, and source_url. When is_non_detect is true, result_numeric is null and the chemical was not found above reporting_limit. This is raw source water before treatment, not tap water, and not a health determination; units vary by chemical (UG/L, NG/L, MG/L, ...) so never compare across units.

ParametersJSON Schema
NameRequiredDescriptionDefault
pws_idYesCalifornia public water system ID, e.g. CA0110005.
chemicalNo
paginationNo
detected_onlyNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive), and the description adds substantial context beyond them: the underlying 'Gold view v_source_chemistry_latest' provenance, refresh cadence, result ordering (chemical, then well), non-detect semantics (result_numeric null when is_non_detect is true), and a critical units caveat warning against cross-unit comparison.

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?

Front-loaded with purpose, then provenance, then parameters, then result semantics in clearly separated paragraphs. Dense but every sentence carries operational value (units warning, non-detect semantics, source tool); slightly long, but nothing is filler.

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

Completeness5/5

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

An output schema exists, so return values need not be enumerated, yet the description goes further and explains how to interpret the key fields. Combined with provenance, ordering, the non-detect rule, and the pre-treatment/units caveats, an agent has everything needed to call and correctly interpret this 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?

Schema description coverage is only 25%, but the description compensates for the two non-obvious parameters: 'chemical' is clarified as a case-insensitive name fragment, and 'detected_only' is defined as skipping non-detects. It also explains where pws_id comes from, though it says nothing about the pagination limits/offset beyond what the schema documents.

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?

Opens with a specific verb+resource+scope: 'Get latest source-water chemistry for one water system's wells.' That scope ('one water system') cleanly separates it from the sibling waterboards_get_source_chemistry_near_zip and from waterboards_get_tap_water_for_zip, which the description also explicitly contrasts against.

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

Usage Guidelines5/5

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

Explicitly routes the agent to waterboards_find_water_systems_near_zip to obtain the required pws_id, and states when the result is not appropriate ('not tap water, and not a health determination'), which tells the agent to reach for the tap-water sibling instead in those cases.

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

waterboards_get_tap_water_for_zipGet tap water sample results for a California ZIP codeA
Read-onlyIdempotent

Get the latest tap-water test results for a California ZIP code.

Source: Gold view v_tap_water_for_zip (DDW SDWIS sample results, 2023 to present, joined to water-system service-area boundaries). Refreshed when the pipeline reloads the open data; each result has last_observed. Systems are ordered by how much of the ZIP they serve (pct_of_zip_covered, highest first); a ZIP can be served by several systems, so use the largest coverage as the likely supplier.

stage is 'distribution' (samples from the pipes that reach customers, the closest to tap water; default) or 'treated' (leaving the treatment plant). chemical is a case-insensitive name fragment (e.g. 'lead', 'arsenic', 'pfas', 'nitrate'). detected_only (default true) hides non-detects; exceeds_mcl_only shows only results above the legal limit. min_coverage_pct (default 10) hides systems covering less of the ZIP than that; lower it to 0 to see every overlapping system.

Each result: zip, pct_of_zip_covered, pws_id, system_name, total_population, water_stage, analyte_name, result_numeric, units, is_non_detect, reporting_limit, mcl, exceeds_mcl, any_exceeds_mcl, max_result_numeric, sample_count, last_observed, sampling_point, method, source_url. When is_non_detect is true, result_numeric is null and the analyte was below reporting_limit.

These are system-level samples, not results for an individual address or home. A single sample above the MCL is not a violation by itself (many limits apply to running averages), and this is not a health determination. Units vary by analyte; never compare across units.

ParametersJSON Schema
NameRequiredDescriptionDefault
stageNodistribution
chemicalNo
zip_codeYes5-digit US ZIP code, e.g. 94103.
paginationNo
detected_onlyNo
exceeds_mcl_onlyNo
min_coverage_pctNoMinimum % of the ZIP a water system must cover.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

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

Goes well beyond the annotations: names the underlying view, refresh cadence and last_observed, ordering rationale, the non-detect convention (result_numeric null below reporting_limit), and important caveats that a single MCL exceedance is not a violation and this is not a health determination. Annotation coverage is strong but the description adds genuinely non-obvious behavioral context.

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?

Front-loaded with purpose, then data source, ordering, parameters, then caveats – a logical order. It is dense and long, and the full enumeration of return fields is partly redundant given an output schema exists, though it earns its place by explaining is_non_detect semantics.

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

Completeness5/5

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

For a 7-parameter read tool with an output schema and full annotations, the description supplies everything an agent needs: scope, source, ordering, per-filter behavior, and interpretation limits. Nothing required to call it correctly is missing.

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 coverage is only 29%, so the description must compensate, and it does: it defines stage values, gives chemical fragment examples ('lead', 'arsenic', 'pfas', 'nitrate'), and explains detected_only (hides non-detects), exceeds_mcl_only (above legal limit), and min_coverage_pct (default 10, lower to 0 for all overlapping systems). Only zip_code and pagination are left to the schema, which documents them.

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?

States a specific verb and resource ('Get the latest tap-water test results for a California ZIP code') and pins the domain to California and to system-level tap-water samples. The stage distinction (distribution = pipes reaching customers vs treated = leaving the plant) makes it distinguishable from source-chemistry and school-lead siblings.

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

Usage Guidelines4/5

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

It gives clear operating context: defaults for stage, detected_only, exceeds_mcl_only and min_coverage_pct, plus guidance to use the largest pct_of_zip_covered as the likely supplier. It does not explicitly name when to prefer a sibling tool (e.g. get_source_chemistry_near_zip) over this one, so routing is implied rather than spelled out.

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

waterboards_get_water_systemGet a public water system profileA
Read-onlyIdempotent

Get the profile and compliance/risk summary of one water system.

Source: Gold view v_water_systems (California State Water Board SDWIS inventory + SAFER risk assessment). Refreshed when the pipeline reloads the open data; the result carries data_as_of.

The single result has system name, county, city, type, owner, source type, population, service connections, last sanitary survey, safer_status, is_failing, economic_status (DAC/SDAC), violation flags with analytes (primary/secondary MCL, E. coli, treatment technique, monitoring), risk levels by category, and source_url. has_risk_assessment is false when the system is not in the SAFER file, in which case the violation/risk fields are null (unknown, not "none"). This is a regulatory screening, not a tap-water safety determination.

ParametersJSON Schema
NameRequiredDescriptionDefault
pws_idYesCalifornia public water system ID, e.g. CA0110005.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior5/5

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

Annotations cover safety (readOnly/idempotent/openWorld), and the description goes well beyond them: it discloses the data source and view, that results carry `data_as_of` and refresh only on pipeline reload, that `has_risk_assessment: false` means fields are null-by-ignorance rather than 'none', and explicitly bounds interpretation ('regulatory screening, not a tap-water safety determination'). That is unusually rich behavioral context.

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?

Front-loaded with the one-line purpose, then provenance, then return contents, then the null-semantics caveat and the screening disclaimer. The enumerated field list is long but it is bounded and each element is informative; nothing reads as filler.

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

Completeness5/5

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

Despite an output schema existing, the description supplies the semantics the schema cannot: provenance/freshness, the null-vs-none distinction, the meaning of has_risk_assessment, and an explicit interpretive caveat. An agent can call and correctly interpret this tool with no further context.

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

Parameters3/5

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

One parameter with 100% schema coverage, including a regex pattern and an example. The description adds nothing about pws_id format or origin, so the schema does all the work; baseline 3 applies.

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?

States a specific verb (get), resource (profile/compliance/risk of ONE water system), and scope. The 'one water system' framing distinguishes it cleanly from sibling list/near-zip tools, and 'compliance/risk summary' separates it from the chemistry-focused siblings.

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?

Usage is implied by the single-system framing and the required pws_id, but the description never says when to pick this over waterboards_get_system_source_chemistry or the near-zip finders, nor does it state a prerequisite beyond having the ID. Adequate context, no explicit routing.

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

waterboards_list_datasetsList waterboard datasetsA
Read-onlyIdempotent

List the water datasets (Gold views) available through this server.

Source: Unity Catalog information_schema for the configured Gold schema. Refresh cadence: reflects the current catalog; underlying data refresh varies by dataset (see each view's comment).

Returns a pagination envelope; each result has table_name and table_comment.

ParametersJSON Schema
NameRequiredDescriptionDefault
paginationNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior4/5

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

Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint, so the safety profile is covered. The description adds genuinely useful context beyond them: the data source (Unity Catalog information_schema for the configured Gold schema), a refresh-cadence caveat that underlying data varies per view, and the shape of the return. It does not state auth/permission requirements, which keeps it short of a 5.

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?

Four short, labelled sentences, each earning its place: what it lists, where the data comes from, a freshness caveat, and the return shape. Front-loaded with the core action.

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?

With an output schema present and rich read-only annotations, the description does not need to explain return values, yet it helpfully summarises them. What remains unaddressed is whether the listing is scoped/filtered by catalog permissions or returns everything in the Gold schema.

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

Parameters3/5

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

Reported schema description coverage is 0%, so the description nominally carries the burden, and it only mentions the pagination envelope on the output side, not the limit/offset inputs. The nested schema does in fact document limit (1-100) and offset, so the agent is not fully in the dark, but the description adds no input-level semantics of its own.

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?

States a specific verb (List) plus a specific resource (water datasets / Gold views) and scopes it to this server. It reads as a metadata-discovery tool, which cleanly separates it from siblings like waterboards_get_water_system or waterboards_get_source_chemistry_near_zip that return actual data.

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 phrase 'available through this server' implies this is a discovery/first-step tool, but there is no explicit 'use this before calling X' or 'do not use this to retrieve measurements' guidance. The when-to-use is inferable rather than stated, so a 3 is warranted.

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. 7 tool updatesv0.1.0
    • First observedwaterboards_find_water_systems_near_zip
    • First observedwaterboards_get_school_lead_near_zip
    • First observedwaterboards_get_source_chemistry_near_zip
    • First observedwaterboards_get_system_source_chemistry
    • First observedwaterboards_get_tap_water_for_zip
    • First observedwaterboards_get_water_system
    • First observedwaterboards_list_datasets

TDQS

A4.4/5.0

Scored across 7 tools

Disambiguation4/5

Tools are largely distinct by resource and input key (ZIP vs pws_id), so most selections are clear. The two source-chemistry tools could be briefly confused because they share the same output schema, but the descriptions and name qualifiers ('near_zip' vs 'system') clarify scope.

Naming Consistency5/5

All names use a consistent waterboards_ prefix and snake_case verb_noun pattern. Verb choices (list/find/get) are appropriate and no mixed conventions appear.

Tool Count5/5

7 tools is well-scoped for a read-only water-data server. Each tool covers a clear data domain or lookup mode, with no redundant or filler tools.

Completeness4/5

The surface covers dataset discovery, water-system lookup, source chemistry by geography and system, school lead, and ZIP tap water. Minor gaps remain, such as direct tap-water or school-lead lookup by pws_id/system and broader search by county or system name, but core workflows are supported.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Enables querying USGS water data including real-time and historical streamflow, gage height, and water temperature from USGS gauges across the United States.
    3
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables search and retrieval of calibrated SWAT+ watershed models, national groundwater lithology and PFAS inventories, plus API-key-secured ordering and downloading of custom watershed models.
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides governed Text2SQL over PostgreSQL with AST-guarded SQL validation, read-only transactions, and allowlisted analysis tools, enabling safe natural-language data querying and reproducible evaluation via MCP.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables querying, sampling, and exporting datasets from over twenty keyless free public data sources spanning the EU, Switzerland, Germany, the US, and global providers through one uniform set of tools. A single consistent interface lets users list databases and tables, inspect schemas and query guides, run queries, and export results as CSV or JSON without per-API clients.
    Apache 2.0