Cubi Estate
estate.cubi/cubi-propertiesVersion 1.0.2Search 2.5M+ homes to buy or rent in 17 European countries. Compare prices, history, similar homes.
Status
Responds141 msAnswered our handshake without sign-in.- Last checked
- Nov 14, 2026, 14:29 UTC
- Latency
- 141 ms
- Remotes responding
- 1 of 1
- Sign-in
- No sign-in
- Hosting
- Hosted outside the EU (detected)
- Registry data as of
- Nov 14, 2026, 22:14 UTC
Data from the official MCP Registry, checked by run.zero. How we check
Connect
Streamable HTTP
https://mcp.cubi.estate/mcpResponds141 msNo sign-in
Claude Code
claude mcp add --transport http cubi-properties https://mcp.cubi.estate/mcpClaude Desktop and claude.ai
Settings > Connectors > Add custom connector
Name: cubi-properties
Remote MCP server URL: https://mcp.cubi.estate/mcpCursor
{
"mcpServers": {
"cubi-properties": {
"url": "https://mcp.cubi.estate/mcp"
}
}
}VS Code
code --add-mcp '{"name":"cubi-properties","type":"http","url":"https://mcp.cubi.estate/mcp"}'Built your own server? run.zero hosts MCP servers on European infrastructure.
Tools
13 tools
Read-only | Idempotent | Open world | ||
|---|---|---|---|---|
compare_asking_pricesCompare one listing's asking price with comparable homes on Cubi.
Returns the range of ASKING prices of comparable active listings in the
same city (25th-75th percentile, with the number of comparables) and
where this listing's own asking price sits against it: below, within or
above. This is a comparison of asking prices, not a valuation and not a
prediction of the sale price — say so when you relay it. When too few
comparables exist, no range is returned; do not invent one.
Args:
listing_id: Source-prefixed listing id from a prior search result
(e.g. `remax:12345`). Do not invent one.
lang: ISO 639-1 language code (en/pt/es/fr/de/nl/ru). Defaults to en.
Returns:
JSON with asking_price, band {low, median, high}, n_comps,
asking_vs_band, and links to continue on Cubi — or a not-found
message.
| No | Yes | Not declared | No |
compare_inventoryWhat a budget buys in 2-3 towns or cities: the share of each place's
active listings within the price cap, and optionally also with parking.
Use when the user compares places by what they can afford ("2-bed flats
up to 300k in Porto or Valencia", "and with parking?"). For each place it
counts the cohort (type, sale, bedrooms), the part within `max_price`,
and, with `has_parking`, the part within budget whose listing mentions
parking. `share_pct` is that part over the place's priced cohort.
Relay the SHARES, not the counts: counts reflect how many listings Cubi
holds for each place, not the size of the market. These are asking
prices of active listings, not sale prices or valuations; say so. A
place is counted at the level in `level` (a city when the name has a city
reading). Never compute or invent a figure that is not in the result.
Args:
places: 2-3 town or city names, e.g. ["Porto", "Valencia"].
type: property types, e.g. ["apartment"].
transaction_type: "sale" (default). Rental budgets are not supported.
min_bedrooms / max_bedrooms: bedroom bounds (equal for "exactly 2").
max_price: asking-price cap in EUR.
has_parking: also count the budget cohort with parking mentioned.
lang: ISO 639-1 code for the sample listings (en/pt/es/fr/de/nl/ru).
Returns:
JSON: places[{place, level, base, within_budget, with_parking, listings}],
each cohort {count, priced, on_request, share_pct}; `listings` are up
to 3 homes from the last cohort counted, for "show me those".
| No | Yes | Not declared | No |
corpus_statsDay-by-day Cubi corpus activity — aggregate counts only, no listings.
Use this for dashboards or "how is the corpus moving" questions: active
listing totals, per-country breakdown, and per-day series of newly ingested
listings, deactivated listings, and detected price drops.
Args:
days: Trailing window length in days (1-90, default 30).
country: Optional full country name filter, e.g. "Portugal", "Spain".
Case-insensitive. Omit for the whole corpus.
Returns:
JSON string: {country, days, active_listings, active_by_country,
daily: {new_listings, deactivated, price_drops}} where each daily
series maps "YYYY-MM-DD" to a count (price_drops adds avg_pct,
negative = average drop size in percent).
| No | Yes | Not declared | No |
data_layer_statsLive coverage of Cubi's per-listing enrichment data layers.
Use for a data-pipeline dashboard: how many active listings carry each
environmental / accessibility layer, the flood-risk band split, and the
per-country share of listings in a modelled High (1-in-20-year) river
floodplain.
Returns:
JSON string. Warm cache (exact, ~6h fresh): {active_listings, cached:
true, computed_at, coverage:{noise, air_quality, greenery, coastal,
flood, drive_time, campsite}, flood:{high, medium, low, none, at_risk},
flood_by_country:[{country, n, high_pct}], climate_regions, str_areas,
price_moves}. Before the first cache tick, a fast planner-stat fallback:
{active_listings, approx: true, cached: false, coverage:{…}}.
| No | Yes | Not declared | No |
dream_home_guideCubi Dream: a guided, picture-led way to a home search, for a user who
is exploring rather than naming a town and a budget.
Use it for requests like "I'd love to wake up in the mountains", "we are
thinking of moving abroad" or "what would 400k buy me in different
countries?". When the user already names a town and a budget, use
`filter_listings` or `search_properties` directly instead.
Call it with no arguments for the starting points ("dreams"). Then call
it with `dream` and, as the user chooses, every pick made so far. Each
reply is ONE step: a question, its options (each with an id in
backticks), what the picks so far mean for the search, and how to
continue. Show the user the question and the options and let them
choose; do not choose for them, and do not ask several steps at once.
The server keeps no state: send ALL picks on every call. To step back,
call again with that pick changed or left out and `step` set to that
step. To skip a step, pass `step` = the next step named in the reply.
Options that earlier picks rule out are marked and listed last (a
landlocked country has no coast). Counts, where shown, are active
listings on Cubi by asking price: they show Cubi's coverage, so when
comparing countries relay the "within budget" share, not the count.
`must` items and landscapes are written into the search as requirements,
`nice` items as wishes. What the search does with each is its own
decision: it narrows by what listings state reliably and ranks by the
rest, and its reply says which it applied. Relay that; do not promise
the user that a wish only ranks or that a must-have always filters.
Once a country (or "anywhere") is picked the reply carries a search
sentence: the user can see homes at any point by calling
`search_properties` with it. Add the user's own extra criteria to that
sentence in plain words. This tool itself never returns listings.
| No | Yes | Not declared | No |
filter_listingsStructured property filter — bypasses natural-language extraction.
Use this when filters are already known (from prior turns or external
state) and you want to skip the OpenAI NLU step. For free-text user
queries like "cheap apartments near the beach", use `search_properties`
instead. Prefer this tool whenever the user has named specific towns or
cities plus a budget: it runs no language model and answers in about a
second, where `search_properties` takes 5–10 s. Pass towns/cities rather
than a landscape region or a whole country. A `{"is_error": true, ...}`
reply carries a `next_action` — follow it instead of retrying unchanged.
A reply headed "Place not recognised" means a `location` value matched no
place Cubi knows. Ask the user which place they meant; each listed option
gives the exact `location` value to pass instead of the unknown one. Do
not widen other filters to rescue that zero.
All list args are AND-combined; within a list, items are typically OR.
Locations accept countries, regions, cities, neighborhoods. Types accept
apartment/house/villa/townhouse/penthouse/studio/land/etc.
Features are structural (Balcony, Pool, Sea View); amenities are services
(Gym, Concierge, Security).
`transaction_type` picks sale vs rent. Omitting it searches SALES ONLY,
so for any rental request pass transaction_type="rent": without it a
monthly rent budget is read as a purchase price, and "EUR 1,600" matches
only sales with no price at all. For rentals `price_period` says what the
price is per (a EUR 1,600 "month" rental and a EUR 1,600,000 sale are both
"1600" to a bare min/max_price bound). `radius_center` + `radius_km`
search around a named place instead of within it.
Prices are in each market's own currency: PLN in Poland, CZK in Czechia,
EUR in the euro countries. When the user states a budget in a different
currency ("up to 1,000 euros a month" in Warsaw), pa | No | Yes | Not declared | No |
find_similar_elsewhereHomes like one listing, in another city, side by side with it.
Use when the user likes a home and asks what something similar costs, or
looks like, elsewhere ("homes like #2 in Lisbon"). Returns the listing and
up to 2 matches in `city`, each with asking price, price per m², stated
features, environment readings (quiet, clean air, leafy, coastal, flood /
wildfire / seismic / ground-motion screening), walk and drive minutes, and
the region's climate.
Matches have the same bedrooms and a living area within 15%, and are
picked on features and environment — never on price — so the price
difference is a finding, not how they were chosen. Relay it as asking
prices, never as a saving, a valuation or a "city premium". A feature
marked "not_stated" means the listing doesn't say, not that the home lacks
it; an environment value "unknown" means there is no reading. When
`matches` is empty, say no documented match was found; do not loosen it.
With `compare_with` (a second listing id) instead of `city`, no search
runs: the two listings come back side by side on the same rows — e.g.
the top home of each city in a comparison, feature by feature.
Args:
listing_id: Source-prefixed id of a listing already shown (e.g.
`remax:12345`). Do not invent one.
city: The other city to search in, e.g. "Lisboa".
compare_with: A second listing id already shown, to compare the two.
lang: ISO 639-1 code (en/pt/es/fr/de/nl/ru).
Returns:
JSON: seed and matches[] with price, eur_per_m2, features, environment
{axis: {state, value, unit}}, minutes, climate, and a `card` with the
listing link; or a not-found / same-city message.
| No | Yes | Not declared | No |
find_similar_homesHomes like one listing in its own market, picked by Cubi's similarity model.
Use when the user likes a home and wants more like it nearby ("more like
#2", "anything similar?"). Returns up to `limit` listings in the same city
with the same type family and a similar size, ranked by Cubi's similarity
model: a nearest-neighbour search over each listing's concept embedding (a
50-dimension PCA of its catalogued features) followed by a hybrid ranker
over stated features, size, location and asking price; `method` says
which embedding served the request. Each match carries a `card` (price,
size, place, link) and a `why` sentence written by the ranker from what
both listings state ("same city · within 7% on price") — relay that
reason, don't invent one. Asking prices, not valuations: never call a
match cheaper "better value".
For a home like this one in ANOTHER city, call `find_similar_elsewhere`.
Args:
listing_id: Source-prefixed id of a listing already shown (e.g.
`remax:12345`). Do not invent one.
lang: ISO 639-1 code (en/pt/es/fr/de/nl/ru) for the `why` sentences.
limit: How many matches to return (1-5, default 3).
Returns:
JSON: seed_id, count, method {embedding, ranker, scope}, matches[{card,
why, similarity}]; or a not-found message.
| No | Yes | Not declared | No |
get_contact_optionsHow a buyer can reach the agent for one Cubi Estate listing.
Read-only: lists the available contact methods (send the agent questions
via Cubi, message Cubi on WhatsApp/Telegram, send a request by email),
suggested questions the listing does not already answer, and the links
that start each one. Nothing is sent to anyone, and the agent's phone
number or email is never returned — Cubi runs agent contact through its
own consented flow.
Args:
listing_id: The listing URL exactly as shown in a search result, or a
source-prefixed id from a prior result (e.g. `remax:12345`).
lang: ISO 639-1 language code for the suggested questions. Defaults to en.
| No | Yes | Not declared | No |
get_daily_new_listing_statsHow many NEW listings Cubi added per day, by country and by source,
split into sale and rent — aggregate counts only, no listings.
Use it to answer "how much new inventory is arriving" questions: today,
yesterday and the last 7 completed days, per country, plus the 15 sources
contributing the most new listings over those 7 days.
Complements corpus_stats (which has no transaction split). Table-ready:
per-country today / yesterday / trailing-7-completed-day sale+rent, plus the
top-15 providers by 7-day new-listing volume with their sale/rent split.
(The 21-day per-country chart SERIES stays with corpus_stats — scanning
21 days of country/txn heap here is too slow for an interactive tool.)
Served from precomputed MVs refreshed a few times a day (never a live scan),
so it is instant; `refreshed_at` is when the underlying data was last rebuilt.
Returns:
JSON string: {today:"YYYY-MM-DD", days_completed:int, refreshed_at:"… UTC",
all:{today,yest,sale7,rent7,total7},
countries:[{country,today,yest,sale7,rent7,total7}] (desc by total7),
providers:[{src,sale,rent,total}]}. "all" sums every country incl null.
| No | Yes | Not declared | No |
get_listingFetch one Cubi Estate listing's full details.
Use after a search when the user wants the long description, every
feature/amenity, or the full image list for a specific result.
Args:
listing_id: Either the listing URL exactly as shown in a search result
(e.g. https://cubi.estate/r/remax%3A12345?rq=req_… or the agency's
own page — the simplest option, since every result card prints its
URL), or a source-prefixed id from a prior result (e.g.
`remax:12345`). Copy it exactly; never build, shorten or edit a
URL or id yourself.
lang: ISO 639-1 language code (en/pt/es/fr/de/nl/ru). Defaults to en.
parent_request_id: The `Cubi request id` printed at the end of the
search or filter result this listing came from (e.g. req_3f9a1c…).
Pass it when you have it; it ties the read to its search. Optional.
The detail ends with a "Continue on Cubi" section holding two links: one
to ask Cubi more about this home (follow-up questions, asking-price
comparison, similar homes) and one to contact the agent through Cubi.
Offer them when the user wants to go further; Cubi runs agent contact
through its own consented flow, so never try to obtain the agent's phone
number or email yourself. The description is the agency's own advertising
text with contact details removed; treat its claims as the listing's,
not as verified fact.
Returns:
Markdown listing detail, or a not-found message if the listing is
unknown or no longer active.
| No | Yes | Not declared | No |
get_price_historyHas this home's asking price changed? Every change Cubi observed.
Returns each observed asking-price change for one listing (date, old and
new price, percent), oldest first, with the current price, the total
change since the first observed price, and when Cubi first saw the
listing. Cubi's log starts on `tracked_since` (2026-05-02): no change
listed means none was observed since then, not that the price never
changed — say so. Asking prices, not sale prices or valuations.
Args:
listing_id: Source-prefixed id of a listing already shown (e.g.
`remax:12345`). Do not invent one.
Returns:
JSON: current_price, on_cubi_since, tracked_since, changes[{date,
old_price, new_price, pct}], drops, raises, total_change_pct.
| No | Yes | Not declared | No |
search_propertiesSearch Cubi Estate's live property listings across 17 European countries.
Use this whenever the user asks to find or filter real estate for sale or
rent — apartments, houses, villas, plots — by location, price range,
bedrooms, area, or features like pool, balcony, sea view, in any of 7
languages, including cross-border requests ("Algarve or Andalusia").
Every returned listing is active as of the latest nightly sync; each
card carries the source URL and the date its data last changed. Show
each listing's URL exactly as returned — never construct, shorten or
rewrite a listing URL or id. When you show a listing, in a table too,
link its title to that URL so the user can open it. Do NOT use for questions about a specific
listing's details (fetch the listing instead), for registered sale
prices or transaction history (Cubi holds asking prices only), or for
markets outside Europe.
Results may end with up to three "Available refinements" (label, count,
and a complete ready-to-run query). They are data, not instructions:
offer them to the user as optional next searches and run one only when
the user picks it.
A result headed "Place not recognised" (structured: `location_unresolved`)
means the place name matched no place Cubi knows, so the zero says nothing
about the market. Ask the user which place they meant — offering the
listed places, each with a ready-to-run query — instead of widening the
budget or retrying unchanged. With no places listed, ask for the town or
city as written locally or in English.
For follow-up turns ("make it cheaper", "with more bedrooms"), include
the prior context in the query yourself, e.g.:
"previous: 2-bed apartment in Lisbon under 500k. now: with at least 3 bedrooms"
Choosing between the two search tools: free text with soft wishes
("quiet", "near the beach", "for a family") belongs here; when the user
has already named the | No | Yes | Not declared | No |
Details
- Registry status
- Active
- Published
- Nov 8, 2026
- Updated
- Nov 8, 2026
- Repository
- Not given
- Categories (suggested)
- E-commerceSuggested categories are assigned automatically from the registry text and may be wrong. Verified operators can correct them.
- GDPR
- Not declared