README
¶
rentals-search-mcp
Static Go MCP for US residential listing search — long-term rentals or homes for sale (apartments, houses, condos, townhomes) via the RentCast API.
Built for ai-gantry / LOCAL_AGENT: lean
tool surface, stdio only, CGO_ENABLED=0 binary that fits distroless.
Search + recommend + listing handoff — never applies, makes offers, contacts
landlords/agents, or signs anything.
Naming follows
ai-gantry MCP tool naming:
host server id rentals → tools like rentals__listings_search (tool names do
not repeat the server id).
Publish from a personal GitHub account only (never a work org). CI / GoReleaser / badges are scaffolded and light up after the personal remote exists — see TODO.md.
What this is (and is not)
| In scope | Out of scope |
|---|---|
| Long-term for-rent and for-sale residential listings (US) | Short-term / vacation stays (Airbnb, Google Hotels) |
| Apartments, houses, condos, townhomes, multi-family (+ land when buying) | Retail / office / industrial commercial leases |
| Rent estimates + zip rental market stats | Applying, touring, offers, escrow, or signing |
| Listing URL / contact handoff | Acting as the renter or buyer |
Commercial / tiny retail spaces (e.g. a small Seattle grocery mart) need a different data source and almost certainly a different MCP package — see docs/commercial-spaces.md.
Tools (MVP)
| Tool | Description |
|---|---|
listings_search |
For-rent by default (/listings/rental/long-term); pass intent=buy only for sale. City/zip/neighborhood + beds/price/type |
listings_get |
One listing by RentCast listing id (same intent — sale and rental catalogs differ) |
rent_estimate_get |
Long-term rent AVM for an address (+ comps when available) |
markets_get |
Aggregate rent / listing stats for a US zip code |
areas_resolve |
Local neighborhood → zips / lat/lng (Seattle presets; no API) |
link_format |
Fallback public search URL (no API call) |
account_get |
Local usage counter (used/left/cap_state) + dashboard link — RentCast has no public quota API |
Host names: rentals__listings_search, rentals__listings_get,
rentals__rent_estimate_get, rentals__markets_get, rentals__areas_resolve,
rentals__link_format, rentals__account_get.
Agent contract
ASK if they want to buy → [areas_resolve] → listings_search (defaults to rent) → [listings_get] → handoff
↘ markets_get / rent_estimate_get (rent context)
- For-rent by default:
listings_search/listings_get/link_formathit the long-term rental catalog unless you passintent=buy.price_min/price_maxare monthly rent when renting and purchase price when buying. - Where + budget:
listings_searchwithcity+state,zip_code/zip_codes, orneighborhood(e.g.Capitol Hill), plus beds/price/type. Usenew_this_weekordays_old_maxfor fresh listings. - Neighborhoods:
areas_resolve(list_all=trueorneighborhood=Ballard) when the human names a Seattle area — then passneighborhoodinto search. - Detail:
listings_getwhen the human picks a candidate id (sameintent). - Context (optional):
markets_getfor zip rental averages;rent_estimate_getwhen comparing a specific address to “fair rent”. - Handoff: give the human
agent/office(name, phone, email, website). RentCast has no portal listing URL;search_urlis a Google address fallback. Do not impersonate the renter or buyer, apply, or make offers.
pets_wanted / parking_wanted / laundry_wanted are soft preferences only
— RentCast does not expose those filters; confirm with the listing agent/office.
Setup
- Create a RentCast account (Developer plan = 50 free API requests / month).
- Export the key:
export RENTCAST_API_KEY=...
# optional (tests / proxies):
# export RENTCAST_BASE_URL=https://api.rentcast.io/v1
# optional caps (defaults shown):
# export RENTCAST_MONTHLY_QUOTA=50
# export RENTCAST_SOFT_CAP=40
# export RENTCAST_ALLOW_OVERAGE=1 # human-only; paid plan / intentional overage
Billed tools (listings_search, listings_get, rent_estimate_get,
markets_get) are gated before any HTTP:
| Cap | Default | What happens | Who can continue |
|---|---|---|---|
| Soft | 40 used | Error: re-call the same tool with confirm_spend=true |
The model (explicit bump on each remaining call) |
| Hard | 50 used | Error: do not retry; use link_format or wait for period_resets |
A human setting RENTCAST_ALLOW_OVERAGE=1 in MCP env — not a tool |
A lock file the model can reset would not protect the free tier: if the model can clear it, it will, and you still go over 50. The hard cap lives in the HTTP client and cannot be unlocked from a tool.
RENTCAST_USAGE_TRACK=0 disables the counter and both caps.
Docker / containers
The binary is static (CGO_ENABLED=0) and fine in distroless — pass
RENTCAST_API_KEY via env/secrets, never bake it into the image.
Local usage counter caveat: account_get / search usage persist under
RENTCAST_USAGE_FILE (default: OS user config dir). In Docker that path is
usually ephemeral (or unwritable in distroless), so counts (and therefore
caps) reset on recreate and can undercount real RentCast spend. Mitigations:
- Mount a volume and set
RENTCAST_USAGE_FILE=/data/rentcast-usage.json, or - Treat the dashboard as source of truth and/or set
RENTCAST_USAGE_TRACK=0(that also turns off the soft/hard caps)
Multiple containers without a shared usage file each keep their own counter.
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/rentals-search-mcp
make self-test
Hooks, tests, and make tools run with CGO_ENABLED=0 (same as the shipped
binary). That matters on SteamOS and other hosts that have gcc but no libc
headers — otherwise Go's net package tries cgo and fails.
Coverage ≥70% is mandatory locally (make coverage / pre-commit) and in CI.
Status: MVP client wired. US-wide long-term residential search (Seattle is just the first use case). Remaining publish steps are in TODO.md.
Releasing
GoReleaser publishes static binaries on v* tags (see .github/workflows/release.yml).
Requires a personal GitHub remote first.
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:
rentals-search-mcp_{version}_{os}_{arch}.tar.gz
Run as MCP (stdio)
./bin/rentals-search-mcp
Logs go to stderr only — stdout is reserved for the MCP protocol.
Example Cursor / client config:
{
"mcpServers": {
"rentals": {
"command": "rentals-search-mcp",
"env": {
"RENTCAST_API_KEY": "…"
}
}
}
}
ai-gantry wiring (when published)
[[server]]
name = "rentals"
command = "rentals-search-mcp"
download_tag = "latest"
download_url = "https://github.com/shotah/rentals-search-mcp/releases/download/{tag}/rentals-search-mcp_{version}_{os}_{arch}.tar.gz"
Do not publish this to a work GitHub org. Use a personal account / org
(module path assumes shotah).
More consumer notes: docs/ai-gantry.md. Agent tool recipes / TOOLS.md sketch: docs/agent.md.
License
MIT
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
release
command
Command release bumps the semver tag, updates VERSION, and pushes.
|
Command release bumps the semver tag, updates VERSION, and pushes. |
|
rentals-search-mcp
command
|
|
|
internal
|
|