cars-search-mcp
Static Go MCP for used / new car inventory
search (dealer + private party) via the
MarketCheck Cars API.
Built for ai-gantry / LOCAL_AGENT: lean
tool surface, stdio only, CGO_ENABLED=0 binary that fits distroless.
Search + recommend + listing handoff — never contacts dealers, submits leads,
or purchases vehicles.
Naming follows
ai-gantry MCP tool naming:
host server id cars → tools like cars__listings_search (tool names do
not repeat the server id).
Publish from a personal GitHub account only (never a work org). Module /
release URLs assume shotah — see TODO.md.
What this is (and is not)
| In scope |
Out of scope |
| Active for-sale car inventory (US / CA) |
Motorcycles / RVs / heavy equipment (later) |
| Dealer + FSBO (+ auction later) |
Financing applications / lead forms |
| VIN decode, fair-price predict, market stats |
Negotiating or buying for the human |
| Listing URL handoff |
Scraping Cars.com / Autotrader directly |
Sibling of rentals-search-mcp and
flights-search-mcp: same packaging vibes, different
upstream.
| Tool |
Description |
listings_search |
Zip/city/radius + year/make/model/price/mileage/body filters |
listings_get |
One listing by MarketCheck listing id |
listings_stats |
Active-inventory snapshot: price / miles / DOM aggregates |
vin_get |
NeoVIN decode (trim, options, fuel economy when available) |
dealers_search |
Dealers near a zip/city (inventory size, type, inventory URL) |
price_predict |
Fair retail price for a VIN + miles (+ zip) |
markets_get |
Market Days Supply for year/make/model (+ zip) |
link_format |
Fallback public search URL (Cars.com; no API) |
account_get |
Local usage counter (used/left) + dashboard link — MarketCheck has no public quota API |
Host names: cars__listings_search, cars__listings_get, cars__listings_stats,
cars__vin_get, cars__dealers_search, cars__price_predict, cars__markets_get,
cars__link_format, cars__account_get.
Agent contract
listings_search → [listings_get] → present listing / dealer url
↘ vin_get / price_predict / listings_stats / markets_get / dealers_search
- Where + budget:
listings_search with zip or city+state, plus
make / model / year_min–year_max / price_max / miles_max.
- Detail:
listings_get when the human picks a candidate id.
- Context (optional):
vin_get for a known VIN; price_predict for fair
value; listings_stats for market price/DOM bands; markets_get for MDS;
dealers_search for rooftops in a market.
- Handoff: give the human the VDP / dealer URL. Do not submit leads
or call the dealer for them.
Setup
- Create a MarketCheck API account
(Free plan ≈ 500 calls / month, 100-mile radius cap — pricing).
- Export the key:
export MARKETCHECK_API_KEY=...
# optional (tests / proxies):
# export MARKETCHECK_BASE_URL=https://api.marketcheck.com/v2
Boot safety (learned the hard way)
This MCP starts stdio even if MARKETCHECK_API_KEY is missing. Tools that
need the network return a clear error. That way a missing key cannot take down
ai-gantry at MCP initialize (pair with gantry’s fail-soft boot).
Docker / containers
The binary is static (CGO_ENABLED=0) and fine in distroless — pass
MARKETCHECK_API_KEY via env/secrets, never bake it into the image.
Local usage counter: on by default — no env needed. Persists under the OS
user config dir (…/cars-search-mcp/usage.json), with home/temp fallbacks.
In Docker that path is usually ephemeral (or unwritable in distroless), so
counts reset on recreate and can undercount real MarketCheck spend.
Mitigations:
- Mount a volume and set
MARKETCHECK_USAGE_FILE=/data/marketcheck-usage.json, or
- Treat the dashboard as source of truth and/or set
MARKETCHECK_USAGE_TRACK=0
Development
make help # list targets
make tools # install goimports-reviser + golangci-lint v2
make install-hooks # git pre-commit: autofix + lint + test + coverage ≥70%
make check # same checks as the pre-commit hook
make cli # static binary → ./bin/cars-search-mcp
make self-test
Coverage ≥70% is mandatory locally (make coverage / pre-commit) and in CI.
Status: MVP client wired. Remaining publish / gantry consumer steps are in
TODO.md.
Releasing
GoReleaser publishes static binaries on v* tags (see .github/workflows/release.yml).
make version # show VERSION + next patch (dry-run)
make release # patch bump, commit VERSION, tag, push
make release BUMP=minor
make release TAG=v0.1.0
Archive name for ai-gantry download_url:
cars-search-mcp_{version}_{os}_{arch}.tar.gz
Run as MCP (stdio)
./bin/cars-search-mcp
Logs go to stderr only — stdout is reserved for the MCP protocol.
Example Cursor / client config:
{
"mcpServers": {
"cars": {
"command": "cars-search-mcp",
"env": {
"MARKETCHECK_API_KEY": "…"
}
}
}
}
ai-gantry wiring (when published)
[[server]]
name = "cars"
command = "cars-search-mcp"
download_tag = "latest"
download_url = "https://github.com/shotah/cars-search-mcp/releases/download/{tag}/cars-search-mcp_{version}_{os}_{arch}.tar.gz"
Also set MARKETCHECK_API_KEY in compose / deploy/gantry.env (regenerate with
make remote-native-env — .env alone is not enough on native).
Do not publish this to a work GitHub org. Use a personal account / org
(module path assumes shotah).
More: docs/ai-gantry.md.
Agent tool recipes: docs/agent.md.
License
MIT