A Model Context Protocol server that lets AI
agents query real-estate (RESO / MLS Grid) data — search listings, pull comps,
read market stats — over a database you populate with its companion project,
mlsgrid-sync.
Status: pre-release (v0). The architecture is in place — the public
mls.Sourceseam, a stdio server on the official Go SDK, and the Postgres adapter — and the full curated tool set works end to end: freshness, search, listing detail, comps, price history, market stats, and open houses, plus an opt-in read-only SQL escape hatch. What remains before v0.1.0 is release polish; see the roadmap.
MLS Grid API ──▶ mlsgrid-sync ──▶ PostgreSQL ──▶ mlsgrid-mcp ──▶ your AI agent
(replication) (your data) (this repo) (Claude, …)
mlsgrid-sync does the replication and owns the schema
contract.
mlsgrid-mcp is a thin, read-only projection of that schema onto MCP tools.
It pins the contract's major version and checks it at startup, so the two
projects release independently.
This server ships no MLS data and no credentials. It reads a database you populated under your own license. When you expose that data to an agent:
- You must hold an executed MLS Grid Data License Agreement and per-MLS approval for the feeds in your database.
- Anything an agent produces from the data is still bound by your license tier's display and distribution rules (IDX/VOW/back-office).
- The server is read-only by construction — it opens read-only database sessions and never writes to your feed.
Install a release binary or:
go install github.com/piotrsenkow/mlsgrid-mcp/cmd/mlsgrid-mcp@latestOr run the released container image (published per release to GitHub Container Registry):
docker run --rm -i \
-e MLSGRID_MCP_DATABASE_URL=postgres://user:pass@host:5432/mls \
ghcr.io/piotrsenkow/mlsgrid-mcp:latestPoint it at the database mlsgrid-sync produced and verify the connection:
export MLSGRID_MCP_DATABASE_URL=postgres://user:pass@host:5432/mls?sslmode=disable
mlsgrid-mcp checkcheck asserts the schema-contract version and prints a data-freshness readout
(the same information the get_data_freshness tool returns). Running the binary
with no arguments serves the MCP protocol over stdio — that is how MCP clients
launch it.
claude mcp add mlsgrid -- \
env MLSGRID_MCP_DATABASE_URL=postgres://user:pass@host:5432/mls mlsgrid-mcpAdd to your claude_desktop_config.json:
{
"mcpServers": {
"mlsgrid": {
"command": "mlsgrid-mcp",
"env": {
"MLSGRID_MCP_DATABASE_URL": "postgres://user:pass@host:5432/mls"
}
}
}
}For production, point the URL at a read-only database role — see docs/adapters.md.
Beyond the curated tools, mlsgrid-mcp can expose a single tool, query_sql,
that runs a caller-supplied read-only SELECT. It is off by default and
meant for one-off questions the curated tools don't cover. Because it hands an
agent raw SQL, it is gated on three things at once:
- You enable it —
MLSGRID_MCP_SQL_ENABLED=true(orsql.enabled: true). - A least-privilege role — the server refuses to expose it over a
superuser connection, and every query runs in a read-only transaction under
a statement timeout (
MLSGRID_MCP_SQL_TIMEOUT, default5s) with a row cap (MLSGRID_MCP_SQL_MAX_ROWS, default1000). - A lexical guard — input that isn't a lone
SELECT/WITH, or that contains writes, DDL, multiple statements, or server-side file/IO functions, is rejected before it reaches the database.
Provision a dedicated read-only role and point the server at it (adjust the
schema if you changed it from the default mlsgrid):
CREATE ROLE mlsgrid_ro LOGIN PASSWORD '…'; -- not a superuser
GRANT CONNECT ON DATABASE mls TO mlsgrid_ro;
GRANT USAGE ON SCHEMA mlsgrid TO mlsgrid_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA mlsgrid TO mlsgrid_ro;
-- so future mlsgrid-sync tables are readable too:
ALTER DEFAULT PRIVILEGES IN SCHEMA mlsgrid GRANT SELECT ON TABLES TO mlsgrid_ro;export MLSGRID_MCP_DATABASE_URL=postgres://mlsgrid_ro:…@host:5432/mls
export MLSGRID_MCP_SQL_ENABLED=true
mlsgrid-mcp check # the features line shows sql=true when it will be exposedcheck prints why the tool is withheld if the connection is a superuser or the
source can't back it. Leave MLSGRID_MCP_SQL_ENABLED unset to keep only the
curated tools.
Structured JSON out, whole-dollar money, a data-as-of timestamp on every response. Full catalog in docs/tools.md.
| Tool | Status | Purpose |
|---|---|---|
get_data_freshness |
live | Sync cursors, listing counts by status, media coverage, contract version — trust + liveness check |
search_listings |
live | Area + status/type/price/beds/baths/sqft/year/DOM/keyword filters |
get_listing |
live | Full detail by ListingKey or MLS number |
get_comps |
live | Comparable sales: distance + similarity + suggested range |
price_history |
live | Observed price/status timeline and total reduction |
market_stats |
live | Median/avg price, $/sqft, DOM, sale-to-list, inventory, months-of-supply |
get_open_houses |
live | Scheduled open houses by area and date range |
describe_dataset |
live | Self-describing schema: tables, columns, and the real values of categorical fields |
query_sql |
live (opt-in) | Read-only SQL escape hatch — off by default |
The tools are written against one interface, mls.Source, with no knowledge of
where the data lives. Implement that interface over your own store — in a
separate, even private, repository — and reuse every tool via plain Go
composition. See docs/adapters.md and
docs/architecture.md.
Apache-2.0. Not affiliated with or endorsed by MLS Grid LLC.