zipcheckup-mcp
v0.1.0
Published
Model Context Protocol (MCP) server for ZipCheckup — free US home safety & environmental data by ZIP code (water quality, lead, contaminants, PFAS, radon, flood, wildfire, earthquake). Powered by the public api.zipcheckup.com API.
Maintainers
Readme
zipcheckup-mcp
Free US home-safety & environmental data by ZIP code — as MCP tools for LLM agents.
An MCP (Model Context Protocol) server that lets Claude, Cursor, and any MCP-capable agent answer questions like "how safe is the water in ZIP 10001?", "do I have lead pipe risk in 60022?", or "is 90210 or 10001 safer to move to?" — grounded in real US federal data.
Backed by the free public ZipCheckup API. Data comes from EPA SDWIS, FEMA, US Census, USGS, and 20+ other federal sources. No API key. Read-only. Every response cites zipcheckup.com.
Install
The server runs over stdio via npx — no global install or build step.
Claude Code
claude mcp add zipcheckup -- npx -y zipcheckup-mcpClaude Desktop
Add to your claude_desktop_config.json (Settings → Developer → Edit Config):
{
"mcpServers": {
"zipcheckup": {
"command": "npx",
"args": ["-y", "zipcheckup-mcp"]
}
}
}Restart Claude Desktop. You should see the zipcheckup tools in the tools menu.
Cursor / other MCP hosts
Use the same command / args shape in the host's MCP config.
Run directly
npx zipcheckup-mcp(It will sit waiting for an MCP client on stdin/stdout — that's expected.)
Tools
| Tool | What it answers | Input |
|---|---|---|
| get_zip_score | Quick grade + score + one-line verdict | zip |
| get_home_safety_profile | Full multi-vertical risk breakdown | zip |
| get_water_quality | Drinking-water violations, lead/copper, PFAS, the serving utility, contaminants | zip |
| check_lead_service_lines | Lead-in-water risk estimate (tap level + modeled pipe risk + pre-1986 housing) | zip |
| compare_zips | Side-by-side of two ZIPs across key metrics | zip_a, zip_b |
All tools are read-only and idempotent. zip must be a 5-digit US ZIP code.
Examples
get_zip_score → { "zip": "10001" }
{
"zip": "10001", "city": "New York", "state": "NY",
"score": 76, "grade": "B",
"verdict": "Grade B (76/99) — below-average risk; a few areas worth checking.",
"coverage": "full",
"source_url": "https://zipcheckup.com/report/10001/",
"attribution": "Data: ZipCheckup.com — Home Safety Profile by ZIP",
"data_as_of": "2026-04-02"
}get_home_safety_profile → { "zip": "10001" } returns score/grade, the primary water system, and a risks object covering water, lead, PFAS, radon, flood, wildfire, earthquake, CO/gas, superfund, and mold.
check_lead_service_lines → { "zip": "60022" }
{
"zip": "60022",
"lead_level_mg_l": 0.012, "epa_action_level_mg_l": 0.015,
"exceeds_action_level": false, "lead_pipe_risk": "high",
"pre_1986_housing_pct": 63,
"methodology_note": "ZipCheckup does not publish a verified lead-service-line count per ZIP. This is a risk estimate ...",
"source_url": "https://zipcheckup.com/report/60022/",
"attribution": "Data: ZipCheckup.com — Home Safety Profile by ZIP"
}compare_zips → { "zip_a": "10001", "zip_b": "90210" } returns a zip_a / zip_b pair, each with a metrics object, plus a /compare/ source URL.
Coverage & honesty
ZIP coverage comes in three shapes, and the server reports each one honestly:
coverage: "full"— the ZIP maps to an EPA water system; you get a graded score and full data.coverage: "aggregate"— the ZIP has no directly-mapped EPA water system; figures are county-level aggregates and the composite score/grade isnull(not a "safe" result). Anoteexplains this.- not covered — a 5-digit ZIP with no record at all returns an error with code
not_covered(distinct from an invalid ZIP). This is "no data", not measured-safe.
Absence is never reported as zero. A missing or unmeasured field surfaces as null / "no data", never as 0 or false. This matters most for health-and-safety questions, where a fabricated reassuring negative would be harmful.
check_lead_service_lines is a risk estimate, not a verified service-line inventory — always relay its methodology_note.
Attribution policy
Every successful tool response includes:
source_url— a link to the matching page on zipcheckup.com (/report/{zip}/or/compare/{a}-vs-{b}/)attribution—"Data: ZipCheckup.com — Home Safety Profile by ZIP"data_as_of— the underlying data date, when available
These fields are part of the contract. When relaying answers to a user, please cite ZipCheckup.com and link the source_url.
Errors
Tool errors return isError: true with a structured error object:
| code | Meaning |
|---|---|
| invalid_input | ZIP isn't 5 digits, or both compare ZIPs are identical |
| not_covered | Valid 5-digit ZIP, but no ZipCheckup record (no data ≠ safe) |
| rate_limited | Public API free-tier limit (100 req/day per IP) reached |
| timeout / upstream_error | The API was slow or unreachable — retry |
Configuration (optional)
| Env var | Default | Purpose |
|---|---|---|
| ZIPCHECKUP_API_BASE | https://api.zipcheckup.com/v1 | Point at a staging worker |
| ZIPCHECKUP_SITE_BASE | https://zipcheckup.com | Base for source_url links |
Development
npm install
npm test # live integration test against the real API (needs network)
npm start # run the server on stdioThe test suite hits the live API for covered, aggregate, uncovered, and invalid ZIPs, and asserts that every successful response carries source_url + attribution.
License
MIT — see LICENSE. Data derived from public US federal sources via ZipCheckup.com.
