Skip to main content
Glama
chadru

assessor-lookup-mcp

by chadru

assessor-lookup

Automated county assessor public-records search for real-estate appraisers.

License: MIT Python MCP

Look up a property's public record — owner of record, legal description, above/below-grade square footage, beds/baths, year built, taxes, assessed and market value, lat/lon, and a link to the assessor card — straight from the county assessor. Then diff those records against your MLS data to flag discrepancies before the report goes out.

Built from an appraiser's workflow, for appraisers: the check command takes your MLS export (subject + comps) and prints a field-by-field discrepancy report (GLA, beds, baths, year built, basement sqft) in seconds instead of a county-website tab per property.

assessor-lookup checking six comps against live El Paso County records — flagging GLA, bath, and basement discrepancies

The numbers are from a real run against live El Paso County records (addresses fictionalized). That +893 GLA flag is a tri-level whose lower level the MLS counted as basement — the kind of miss that walks straight into your adjustment grid.

Features

  • One record model, many counties — Spatialest, Tyler EagleWeb, Aumentum, and ArcGIS platforms all normalize to the same dict.

  • MLS discrepancy check — reads standard MLS CSV exports (PPMLS and RESO/REColorado column names both understood) and flags GLA/beds/baths/year/ basement differences.

  • Auto-discovery — point it at a county it doesn't know and it maps the county for you, API-first, then caches the result.

  • No API keys — these are the same public endpoints the county's own property-search website uses.

  • Agent-ready — ships an MCP server so an AI agent can drive the whole thing.

  • Regression + benchmark harness — pins golden records per county and catches the day a county website changes. Packaged live fixtures use only government or institutional properties; user-onboarded records stay in the user's local config directory and are never added to the package.

Requirements

  • Python 3.9+ (the MCP server needs 3.10+).

  • Standard library only for the core — no runtime dependencies. Optional extras pull in Playwright ([card]) and the MCP SDK ([mcp]).

Installation

pip install assessor-lookup

# optional extras
pip install "assessor-lookup[card]"   # print an assessor card to PDF (Playwright)
pip install "assessor-lookup[mcp]"    # run the MCP server for AI agents

# one-time browser install for the card/PDF feature
playwright install chromium

Or from source:

git clone https://github.com/chadru/assessor-lookup-public && cd assessor-lookup-public
pip install -e ".[dev]"

Quick start (CLI)

# Single property
assessor-lookup lookup "123 Main St" --county "El Paso"
assessor-lookup lookup --parcel 0156931101001 --county Adams --json

# List supported counties
assessor-lookup counties

# Auto-detect + cache an assessor source for a new county
assessor-lookup discover "Clear Creek"

# Diff your MLS export against public records (subject + comps)
assessor-lookup check subject.csv comps.csv --county "El Paso"

# Save the assessor property card as a PDF (needs the [card] extra)
assessor-lookup card "https://property.spatialest.com/co/elpaso/#/property/..." card.pdf

Quick start (Python)

from assessor_lookup import lookup, check_public_records

rec = lookup("123 Main St", county="El Paso")
if rec["status"] == "success":
    print(rec["owner"], rec["above_grade_sqft"], rec["year_built"])

results = check_public_records(subject_row, comp_rows, county="Adams")
flagged = [r for r in results if r["has_any_discrepancy"]]

Every lookup returns a dict with a status key (success, not_found, ambiguous, timeout, api_error, parse_error, …). On success it carries the standard record fields: owner, legal, parcel_number, above_grade_sqft, basement_sqft, beds, baths, year_built, tax_amount, assessed_value, market_value, latitude, longitude, assessor_url, and more (availability varies by platform).

County coverage

County (CO)

Platform

Notes

El Paso

Spatialest

Denver

Spatialest

Douglas

Spatialest

Jefferson

Aumentum (jeffco.us)

Arapahoe

ArcGIS MapServer

multi-layer lookup; use responsibly

Adams

ArcGIS FeatureServer

Clear Creek

Tyler EagleWeb

scraped; full building data

~40 more CO counties

statewide parcel API

baseline via auto-discovery (no building data)

Auto-discovery (new counties)

Point the tool at a county it doesn't know and it tries to map it for you, API-first, best-data-first:

  1. Spatialest (JSON, national) or EagleWeb (Tyler's JSP app, scraped) — full building data (GLA, beds, baths, year built).

  2. Colorado statewide parcel API (ArcGIS) — a baseline for any of ~40 CO counties: owner, legal, land, assessed/market value. This layer has no building characteristics, so GLA/beds/baths/year come back as N/A until a real county client is added.

assessor-lookup discover "Gilpin"     # probe, then cache the hit

Discovered counties are cached in ~/.config/assessor-lookup/county_registry.json (override with ASSESSOR_LOOKUP_HOME) and reused automatically. A lookup or check for an unknown county runs the same discovery inline. Counties still not matched fall back to Spatialest using the county name as the slug.

MCP server (agent-ready)

The repo ships an all-inclusive MCP server so an AI agent can pull down the repo, spin it up, and use county records with zero extra glue. It exposes the lookup/check/discover/harness functionality as tools, the repo's architecture and registry as resources, ready-made workflows as prompts, and a coordinator operating manual as the server instructions.

git clone https://github.com/chadru/assessor-lookup-public && cd assessor-lookup-public
pip install -e ".[mcp]"          # needs Python 3.10+ (lookup core is 3.9+)

assessor-lookup-mcp              # run the server (stdio)

The MCP is a trusted local stdio service, not an authenticated network server. check_mls_csv can read only .csv files beneath the directory where the server starts. To use a different MLS folder, opt in explicitly:

ASSESSOR_LOOKUP_MCP_DATA_DIR=/path/to/mls assessor-lookup-mcp

Resolved paths and symlinks are kept inside that directory. Do not expose the stdio server through an unauthenticated HTTP/SSE bridge.

Hooking it up to Claude Code

In this repo, nothing to register: Claude Code auto-discovers the bundled .mcp.json at session startup — install the [mcp] extra, restart the session, and approve the server when prompted (/mcp shows its status).

In any other project, register it per-project or user-wide:

claude mcp add assessor-lookup -- assessor-lookup-mcp
claude mcp add --scope user assessor-lookup -- assessor-lookup-mcp

What the agent gets on connect:

Kind

Name

Purpose

tool

lookup_property

one property's record by address or parcel

tool

check_mls_csv

diff an MLS subject+comps export vs public records

tool

list_counties

current coverage (defaults + discovered)

tool

discover_county

auto-map an unknown county (API-first)

tool

probe_county

ping a county live; report field coverage + check-readiness

tool

onboard_county

configure a county for repeated use (discover, probe, pin golden)

tool

run_regression

golden-record regression + latency benchmark

tool

benchmark

offline parser micro-benchmark

resource

assessor://operating-manual

coordinator role, agent topology, data policy

resource

assessor://architecture

live architecture (CLAUDE.md + README)

resource

assessor://counties

the registry as JSON

resource

assessor://golden-records

pinned records the harness checks

resource

assessor://harness-guide

how to run/read the harness

prompt

appraisal_check

run a discrepancy check end-to-end

prompt

onboard_locale

configure all the counties in the user's area

prompt

add_new_county

coordinator workflow to add a county, verified

The server instructions double as the agent's playbook: act as coordinator, read the architecture, and follow the one rule — API-first, scrape only when the API lacks building data (GLA/beds/baths/year).

Predefined agents & skills

The source repository ships auto-discovered definitions for both Claude Code and Codex, so an agent that opens the clone picks up named roles and workflows instead of improvising:

  • Agents (.claude/agents/): coordinator (entry point — routes the work), explorer (maps a new county's site, API-first), reviewer (verifies a new client against the live site + golden), county-onboarder (probes and onboards your counties).

  • Skills (.claude/skills/): onboard-locale, appraisal-check, add-county.

  • Codex agents (.codex/agents/) and shared skills (.agents/skills/): the equivalent coordinator, explorer, reviewer, county-onboarder, and three county/appraisal workflows.

Clone the repo, open it in Claude Code, and say what you want ("set up my counties", "check these comps", "add Teller County") — the coordinator picks up the ball and drives it with the MCP tools.

Development

git clone https://github.com/chadru/assessor-lookup-public && cd assessor-lookup-public
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

pytest -m "not network"      # fast unit tests (no network)
pytest -m network            # live integration tests (hit real county sites)

Regression + benchmark harness

The operational risk of this project is county websites changing silently. The harness pins known properties per county as golden records and re-checks them.

python tests/harness.py            # full: regression + latency + discovery + parser bench
python tests/harness.py --offline  # parser micro-bench only (no network)
python tests/harness.py --capture  # (re)pin golden records after legitimate data changes

Stable fields (parcel, GLA, basement, beds, baths, year) fail on drift; volatile fields (owner, values, taxes) only warn. Exit codes: 0 pass / 1 hard regression / 2 warnings only.

Point it at your own counties

Working in a different area? Probe a county to see how it reacts and what it returns, then onboard it so it's configured once and re-checked every run:

# See the platform, latency, and exactly which fields a county returns
python tests/harness.py --probe "El Paso" --address "1675 W Garden of the Gods Rd"

# Configure a county for repeated use (discover, probe, pin a golden record)
python tests/harness.py --onboard "El Paso" --parcel 0000000001

--probe reports a coverage line like building 5/5 | check-ready: YES — a county is check-ready when the building fields (GLA/beds/baths/year) come through, which is what the discrepancy check needs. Onboarded counties are saved to ~/.config/assessor-lookup/ (user_cases.json + user_golden.json) and run alongside the packaged defaults on every python tests/harness.py.

An AI agent driving the MCP server does the same via the probe_county / onboard_county tools and the onboard_locale prompt — point it at your area and it configures everything for you.

Contributing

New counties and platforms are welcome. To add a county:

  1. Check whether auto-discovery already resolves it: assessor-lookup discover "Your County" --state xx.

  2. If not, look for a JSON API first (county/state ArcGIS, or a vendor JSON platform). Confirm it carries the building fields (GLA/beds/baths/year) — if it doesn't, scrape the assessor's HTML front-end instead.

  3. If it's on a config-driven platform (Spatialest, EagleWeb, Aumentum), a county_registry.json entry is all it takes — no code. For a bespoke ArcGIS flow, add a driver at jurisdictions/<country>/<state>/<name>.py exposing build(entry, timeout=, verbose=) and reference it from the entry's config.driver (jurisdictions/us/co/adams.py is a compact example). For a brand-new platform, add one module in platforms/ whose client returns the standard record dict with a status key (platforms/eagleweb.py is the reference for a scraped platform).

  4. Register a new platform with one line in the PLATFORMS dict in platforms/__init__.py, and add the county_registry.json entry.

  5. Add a golden case in assessor_lookup/harness.py and capture it (python tests/harness.py --capture --filter <id>), then confirm pytest -m "not network" is green.

Open an issue if a county breaks — include the address you searched and the error output. See CLAUDE.md for the full architecture.

Disclaimers

  • Public data only. This tool reads the same public endpoints the county's own property-search website uses. Respect each county's terms of use and rate limits; the regression harness spaces live cases, but individual platform clients do not promise automatic retry or throttling.

  • Records can lag reality (recent sales, new construction). Verify anything material — this is a time-saver, not a substitute for appraiser diligence.

  • Not affiliated with any county government, MLS, or a la mode/CoreLogic.

License

MIT