# PropertyList: Spanish Property MLS

> First-party Spanish and Portuguese property listings with notary-verified prices.

- name: es.propertylist/propertylist
- version: 0.1.1
- connection class: R0 (autonomous)
- trust: 82/100 flags: no-repository
- quality: 80/100 (good)
- owner: not claimed
- descriptor: https://api.protogrid.dev/v1/servers/es.propertylist%2Fpropertylist
- tools: https://api.protogrid.dev/v1/servers/es.propertylist%2Fpropertylist/tools

**An agent can connect right now, no human step.** Remote endpoint, no authentication, reachable on the last probe.

## Connection (mcpServers)

```json
{
  "mcpServers": {
    "propertylist": {
      "type": "http",
      "url": "https://mcp.propertylist.es/mcp"
    }
  }
}
```


## Trust

- hygiene: 80
- liveness: 99
- freshness: 100
- provenance: 55
- drivers: +dns-namespace +website +reachable +uptime-97% +updated-26d-ago -no-repository
- Derived from observable signals (official registry feed and our own credential-free probes); no code audit performed.

## Quality 80/100 (good)

### protocol: 75 (weight 25)

- warn `protocol.modern`: newest supported version is 2025-11-25; 2026-07-28 not supported, older versions are deprecated until 2027-07-28
- n/a `protocol.stateless`: only applies to 2026-07-28 servers
- pass `protocol.transport`: streamable HTTP
- n/a `protocol.list_ttl`: only applies to 2026-07-28 servers

### authorization: n/a (weight 15)

- n/a `auth.prm`, `auth.as_metadata`, `auth.cimd`: no remote uses OAuth
- n/a `auth.secret_in_url`: no templated URL

### tool hygiene: 86 (weight 30)

- pass `tools.descriptions`: every tool has a description
- warn `tools.description_length`: 5 tools have descriptions over 1,024 characters, which crowds the context
- pass `tools.schemas`: every tool has an object input schema
- pass `tools.annotations`: 31 of 31 tools declare readOnlyHint or destructiveHint
- pass `tools.directory_hints`: every tool declares readOnlyHint, destructiveHint, idempotentHint and openWorldHint
- warn `tools.token_cost`: about 12,054 tokens to load every tool
- pass `tools.api_dump`: tools are not a one-to-one API dump

### stability: 75 (weight 15)

- warn `stability.changes`: 53 tool changes in 30 days
- pass `stability.rug_pull`: no tool changed its meaning under the same name

### dependencies: n/a (weight 15)

- n/a `deps.known_vulns`, `deps.mcp_sdk_version`, `deps.resolvable`: no npm or PyPI package

- tools: 31, about 12,054 tokens to load them all
- tool set hash: b658f5034380162cbaeb913684329ad7
- tools last changed: 2026-10-20T14:30:59.169Z
- badge: [![protogrid quality](https://protogrid.dev/badge/es.propertylist/propertylist.svg)](https://protogrid.dev/servers/es.propertylist/propertylist)
- Checks run on what our credential-free, read-only probes observe; tools are never called and no code audit is performed.

### Tool changes

- 2026-10-20 `area_market_summary` changed (description, inputSchema, outputSchema)
  - before: Return aggregate market context for a Spanish area: number of active listings, median price, mean price, median €/m², and the count broken down by bedroom band. Filterable by property type and search type (for-sale / for-rent / holiday-rentals). Use this before recommending a price or commenting on whether a listing is good value. If the place name is not one we hold, the response says so (error: location_not_found, location_match.matched: false) and returns no figures. Figures are NEVER nationwide fallbacks. Price figures follow the search type and are labelled with price_unit: sale prices are totals, for-rent is per MONTH and holiday-rentals is per WEEK. median_price_per_sqm is reported for sales only. The `oracle` block carries the attested transaction price for the area. Check `oracle.verified` before citing it as verified: it is true only for Spanish figures from the notarial register (Consejo General del Notariado) with at least 10 recorded transactions behind them. Outside Spain the figure comes from a national house-price index, carries no transaction count, and must never be described as notary-backed - `oracle.source` names the real source and `oracle.note` says why it is not verified. Transactions are recorded per municipality, so a sub-area (Sotogrande, Puerto Banus, Nueva Andalucia, Orihuela Costa) is answered with the figure for the municipality containing it, with `oracle.rolled_up_from_sub_area` true and `oracle.municipality` / `oracle.municipality_code` naming it. Quote it as the municipality's figure, never as the sub-area's own. Commercial property (property_types commercial, a family such as offices, retail, cafes-bars-restaurants, hotels-leisure, industrial-warehouses, garages-storage-rooms, buildings or commercial-land, or one type such as shop) is answered with its own figures: the ASKING prices of live commercial listings (figures_basis asking_prices), per family, with the asking price or monthly rent per m² and size bands, and no `oracle` block. Below min_sample priced listings only the count is given. Homes and commercial property are never summarised together.
  - after: Return aggregate market context for a Spanish area: number of active listings, median price, mean price, median €/m², and the count broken down by bedroom band. Filterable by property type and search type (for-sale / for-rent / holiday-rentals). Use this before recommending a price or commenting on whether a listing is good value. If the place name is not one we hold, the response says so (error: location_not_found, location_match.matched: false) and returns no figures. Figures are NEVER nationwide fallbacks. If several places share the name and one of them holds at least 80% of their live listings, the figures are for that place alone, and location_match.other_places names the others (id, municipality, province, listings). If the listings are split between them, or none has any, the response says so (error: ambiguous_location, location_match.ambiguous: true), lists them in location_match.candidates and returns no figures. Either way, ask again with the chosen id as location_id, or with the town or province after a comma ('El Limonar, Mijas'). A town that holds no area of that name is answered as location_not_found, with the areas of that name we do hold in location_match.candidates. Price figures follow the search type and are labelled with price_unit: sale prices are totals, for-rent is per MONTH and holiday-rentals is per WEEK. median_price_per_sqm is reported for sales only. The `oracle` block carries the attested transaction price for the area. Check `oracle.verified` before citing it as verified: it is true only for Spanish figures from the notarial register (Consejo General del Notariado) with at least 10 recorded transactions behind them. Outside Spain the figure comes from a national house-price index, carries no transaction count, and must never be described as notary-backed - `oracle.source` names the real source and `oracle.note` says why it is not verified. Transactions are recorded per municipality, so a sub-area (Sotogrande, Puerto Banus, Nueva Andalucia, Orihuela Costa) is answered with the figure for the municipality containing it, with `oracle.rolled_up_from_sub_area` true and `oracle.municipality` / `oracle.municipality_code` naming it. Quote it as the municipality's figure, never as the sub-area's own. Commercial property (property_types commercial, a family such as offices, retail, cafes-bars-restaurants, hotels-leisure, industrial-warehouses, garages-storage-rooms, buildings or commercial-land, or one type such as shop) is answered with its own figures: the ASKING prices of live commercial listings (figures_basis asking_prices), per family, with the asking price or monthly rent per m² and size bands, and no `oracle` block. Below min_sample priced listings only the count is given. Homes and commercial property are never summarised together.
- 2026-10-20 `autocomplete_location` changed (description, outputSchema)
  - before: Look up property areas (city, suburb, urbanisation, or a group of areas such as a region or a coast) by free-text name. Returns up to 10 matches with id, name, type, the parent province/city and the number of listings, biggest first, so a group that covers a whole region (e.g. 'Region of Murcia') comes before the city of the same name. Use this to resolve place names before calling search_properties, especially for common spellings like 'Nueva Andalucia' (vs 'Nueva Andalucía') or 'Marbella old town', and pass the chosen id to search_properties as location_id.
  - after: Look up property areas (city, suburb, urbanisation, or a group of areas such as a region or a coast) by free-text name. Returns up to 10 matches with id, name, type, the parent province/city and the number of listings, biggest first, so a group that covers a whole region (e.g. 'Region of Murcia') comes before the city of the same name. Each match names the municipality it sits in, which tells apart places that share a name (El Limonar in Mijas, Málaga and in Torrevieja, Alicante). Use this to resolve place names before calling search_properties, especially for common spellings like 'Nueva Andalucia' (vs 'Nueva Andalucía') or 'Marbella old town', and pass the chosen id to search_properties as location_id.
- 2026-10-20 `search_properties` changed (description, outputSchema)
  - before: Search PropertyList's full Spanish property MLS by structured filters. Use for queries that translate cleanly to fields: bedrooms, bathrooms, price ceiling, property type, area name. For free-form briefs use find_properties_by_description instead. Free use, with or without an API key, is for an assistant answering a person's questions and has a daily allowance of results. Reading listings in bulk, or showing them on a website, needs MCP Pro or the agency's website feed. When the day's allowance is used the result is empty and carries `error` and `limits`. Returns a paginated list with summary text plus structured JSON. Every property carries `oracle_verified` and `oracle_attestation_url` — true when PropertyList Oracle holds a verified attestation for the listing's municipality and segment. A `location` we do not hold returns zero results with error: location_not_found and location_match.matched: false. It never substitutes listings from other areas. `location_id` takes an area id from autocomplete_location instead of a name; a group id (type group, e.g. a region or a coast) searches every area in the group. `features` keeps only listings that have ALL the features asked for: pool (any pool, communal included), private_pool, communal_pool, sea_view, garden, parking (a garage or parking space) and terrace. Each result then carries matched_features, saying which tags (and, for parking and terrace, which space count or terrace size) the listing matched on. private_pool means the listing is tagged with a private or heated pool; many agency feeds tag every pool that way, communal ones included, so use pool for "with a pool" and check the listing when a pool must be private. communal_pool means the listing is tagged with a communal (shared) pool: use it when the user asks for a communal pool; pool also finds the communal pools a feed tagged as private, so widen to pool when communal_pool finds too few. `location.city` is whatever tier the listing was filed under and may be a municipality OR a locality inside one. To place a listing administratively use `location.municipality` (with its INE municipality_code); it is null, never guessed, when we cannot resolve it. `location.latitude` / `location.longitude` are present whenever we hold a real coordinate; the fields are absent otherwise, and are never 0,0. `location.coordinates_precision` says what the point is: exact or street is the property itself, approximate is a pin the agent keeps private, rounded to about 1 km, locality or administrative_area is an area centroid (accurate to the town, not the building), unknown is a pin we hold no precision record for.
  - after: Search PropertyList's full Spanish property MLS by structured filters. Use for queries that translate cleanly to fields: bedrooms, bathrooms, price ceiling, property type, area name. For free-form briefs use find_properties_by_description instead. Free use, with or without an API key, is for an assistant answering a person's questions and has a daily allowance of results. Reading listings in bulk, or showing them on a website, needs MCP Pro or the agency's website feed. When the day's allowance is used the result is empty and carries `error` and `limits`. Returns a paginated list with summary text plus structured JSON. Every property carries `oracle_verified` and `oracle_attestation_url` — true when PropertyList Oracle holds a verified attestation for the listing's municipality and segment. A `location` we do not hold returns zero results with error: location_not_found and location_match.matched: false. It never substitutes listings from other areas. `location_id` takes an area id from autocomplete_location instead of a name; a group id (type group, e.g. a region or a coast) searches every area in the group. When several places share the name, the results are for the one holding at least 80% of their live listings, and location_match.other_places names the others; when the listings are split, the results cover all of them and location_match.ambiguous is true, with the places in location_match.candidates (id, municipality, province). Pass one as location_id, or add the town after a comma ('El Limonar, Mijas'). `features` keeps only listings that have ALL the features asked for: pool (any pool, communal included), private_pool, communal_pool, sea_view, garden, parking (a garage or parking space) and terrace. Each result then carries matched_features, saying which tags (and, for parking and terrace, which space count or terrace size) the listing matched on. private_pool means the listing is tagged with a private or heated pool; many agency feeds tag every pool that way, communal ones included, so use pool for "with a pool" and check the listing when a pool must be private. communal_pool means the listing is tagged with a communal (shared) pool: use it when the user asks for a communal pool; pool also finds the communal pools a feed tagged as private, so widen to pool when communal_pool finds too few. `location.city` is whatever tier the listing was filed under and may be a municipality OR a locality inside one. To place a listing administratively use `location.municipality` (with its INE municipality_code); it is null, never guessed, when we cannot resolve it. `location.latitude` / `location.longitude` are present whenever we hold a real coordinate; the fields are absent otherwise, and are never 0,0. `location.coordinates_precision` says what the point is: exact or street is the property itself, approximate is a pin the agent keeps private, rounded to about 1 km, locality or administrative_area is an area centroid (accurate to the town, not the building), unknown is a pin we hold no precision record for.
- 2026-10-17 `qualify_lead` changed (description, inputSchema)
  - before: Stage qualifying a client, as the CRM's Qualified popup does: tags them as a buyer, seller or tenant, puts them on that pipeline board and takes them off the Lead board. Returns a preview, including what it will COST (a place on the Buyer board uses a free place or one credit), and an action_id; NOTHING changes or is charged until you call confirm_action with that id, after the agent has said yes. Without `board`, the type comes from what the CRM already knows (the client's saved search, or a Vendor/Landlord tag); with neither, it asks for their details instead of guessing. With `board`, the client goes onto that board at its first step. Requires a linked PropertyList account: the change is made in the agent's own name, with their CRM permissions, so an agency API key alone cannot make it.
  - after: Stage qualifying a client, as the CRM's Qualified popup does: tags them as a buyer, seller or tenant, puts them on that pipeline board and takes them off the Lead board. Returns a preview, including what it will COST (a place on the Buyer board uses a free place or one credit), and an action_id; NOTHING changes or is charged until you call confirm_action with that id, after the agent has said yes. Without `board`, the type comes from what the CRM already knows (the client's saved search, or a Vendor/Landlord tag); with neither, it asks for their details instead of guessing. With `board`, the client goes onto that board at its first step. `search` saves what a buyer or tenant is looking for with the qualification, homes or commercial premises (offices, retail, a bar, a warehouse, a traspaso...), never both in one search. Requires a linked PropertyList account: the change is made in the agent's own name, with their CRM permissions, so an agency API key alone cannot make it.
- 2026-10-17 `create_listing` changed (description, inputSchema)
  - before: Start a new property listing in YOUR agency's CRM from a description. Use when the agent is describing a property they have taken on and wants it in the system. Creates a DRAFT. It is never published and never appears on the portal from here: the agent adds photographs and the energy rating in the CRM and publishes it themselves. The response includes a direct link to the draft. Location is resolved against the real area tree, so a town, suburb or urbanisation name is enough. Requires a linked PropertyList account (or an agency API key).
  - after: Start a new property listing in YOUR agency's CRM from a description. Use when the agent is describing a property they have taken on and wants it in the system. Creates a DRAFT. It is never published and never appears on the portal from here: the agent adds photographs and the energy rating in the CRM and publishes it themselves. The response includes a direct link to the draft. Location is resolved against the real area tree, so a town, suburb or urbanisation name is enough. Requires a linked PropertyList account (or an agency API key). Commercial premises take a commercial property_type (office, shop, retail, bar, restaurant, warehouse, garage, storage-room, building, commercial-land...), no bedrooms, and their own facts: usable area, the activity licence, a sale's tenure (freehold, or traspaso: the lease and the business taken over, with the rent and years left on the lease), a letting's guarantee, minimum term and VAT. They are checked as the CRM checks them.
- 2026-10-17 `area_market_summary` changed (description, inputSchema, outputSchema)
  - before: Return aggregate market context for a Spanish area: number of active listings, median price, mean price, median €/m², and the count broken down by bedroom band. Filterable by property type and search type (for-sale / for-rent / holiday-rentals). Use this before recommending a price or commenting on whether a listing is good value. If the place name is not one we hold, the response says so (error: location_not_found, location_match.matched: false) and returns no figures. Figures are NEVER nationwide fallbacks. Price figures follow the search type and are labelled with price_unit: sale prices are totals, for-rent is per MONTH and holiday-rentals is per WEEK. median_price_per_sqm is reported for sales only. The `oracle` block carries the attested transaction price for the area. Check `oracle.verified` before citing it as verified: it is true only for Spanish figures from the notarial register (Consejo General del Notariado) with at least 10 recorded transactions behind them. Outside Spain the figure comes from a national house-price index, carries no transaction count, and must never be described as notary-backed - `oracle.source` names the real source and `oracle.note` says why it is not verified. Transactions are recorded per municipality, so a sub-area (Sotogrande, Puerto Banus, Nueva Andalucia, Orihuela Costa) is answered with the figure for the municipality containing it, with `oracle.rolled_up_from_sub_area` true and `oracle.municipality` / `oracle.municipality_code` naming it. Quote it as the municipality's figure, never as the sub-area's own.
  - after: Return aggregate market context for a Spanish area: number of active listings, median price, mean price, median €/m², and the count broken down by bedroom band. Filterable by property type and search type (for-sale / for-rent / holiday-rentals). Use this before recommending a price or commenting on whether a listing is good value. If the place name is not one we hold, the response says so (error: location_not_found, location_match.matched: false) and returns no figures. Figures are NEVER nationwide fallbacks. Price figures follow the search type and are labelled with price_unit: sale prices are totals, for-rent is per MONTH and holiday-rentals is per WEEK. median_price_per_sqm is reported for sales only. The `oracle` block carries the attested transaction price for the area. Check `oracle.verified` before citing it as verified: it is true only for Spanish figures from the notarial register (Consejo General del Notariado) with at least 10 recorded transactions behind them. Outside Spain the figure comes from a national house-price index, carries no transaction count, and must never be described as notary-backed - `oracle.source` names the real source and `oracle.note` says why it is not verified. Transactions are recorded per municipality, so a sub-area (Sotogrande, Puerto Banus, Nueva Andalucia, Orihuela Costa) is answered with the figure for the municipality containing it, with `oracle.rolled_up_from_sub_area` true and `oracle.municipality` / `oracle.municipality_code` naming it. Quote it as the municipality's figure, never as the sub-area's own. Commercial property (property_types commercial, a family such as offices, retail, cafes-bars-restaurants, hotels-leisure, industrial-warehouses, garages-storage-rooms, buildings or commercial-land, or one type such as shop) is answered with its own figures: the ASKING prices of live commercial listings (figures_basis asking_prices), per family, with the asking price or monthly rent per m² and size bands, and no `oracle` block. Below min_sample priced listings only the count is given. Homes and commercial property are never summarised together.
- 2026-10-17 `get_property` changed (description, outputSchema)
  - before: Fetch the full record for one PropertyList listing by its reference code (the human-readable id like 'PLE-12345'). Use this once the user has settled on a listing from search_properties or find_properties_by_description and wants details or photos. To contact the listing agency, use submit_enquiry - direct phone or email details are not part of the payload. Works without an API key up to a daily allowance of listings, counted per IP address, or per account for an app linked to a PropertyList account; an API key raises that allowance. The free allowance is for an assistant answering a person's questions: reading listings in bulk, or showing them on a website, needs MCP Pro or the agency's website feed. When the day's allowance is used the result carries `error` and `limits` instead of `property`. `photos` holds the listing's first photos on the free tier and all of them with MCP Pro; `photos_total` says how many the listing has. `location.city` is whatever tier the listing was filed under and may be a municipality OR a locality inside one. To place a listing administratively use `location.municipality` (with its INE municipality_code); it is null, never guessed, when we cannot resolve it. `location.latitude` / `location.longitude` are present whenever we hold a real coordinate; the fields are absent otherwise, and are never 0,0. `location.coordinates_precision` says what the point is: exact or street is the property itself, approximate is a pin the agent keeps private, rounded to about 1 km, locality or administrative_area is an area centroid (accurate to the town, not the building), unknown is a pin we hold no precision record for.
  - after: Fetch the full record for one PropertyList listing by its reference code (the human-readable id like 'PLE-12345'). Use this once the user has settled on a listing from search_properties or find_properties_by_description and wants details or photos. To contact the listing agency, use submit_enquiry - direct phone or email details are not part of the payload. Works without an API key up to a daily allowance of listings, counted per IP address, or per account for an app linked to a PropertyList account; an API key raises that allowance. The free allowance is for an assistant answering a person's questions: reading listings in bulk, or showing them on a website, needs MCP Pro or the agency's website feed. When the day's allowance is used the result carries `error` and `limits` instead of `property`. `photos` holds the listing's first photos on the free tier and all of them with MCP Pro; `photos_total` says how many the listing has. `location.city` is whatever tier the listing was filed under and may be a municipality OR a locality inside one. To place a listing administratively use `location.municipality` (with its INE municipality_code); it is null, never guessed, when we cannot resolve it. Commercial premises (`commercial` true) carry no bedrooms: read `commercial_facts` (usable area, licence, price per m², a letting's terms, a traspaso's rent and years left) and `tenure` (freehold, or traspaso for a sale of the lease and the business). `location.latitude` / `location.longitude` are present whenever we hold a real coordinate; the fields are absent otherwise, and are never 0,0. `location.coordinates_precision` says what the point is: exact or street is the property itself, approximate is a pin the agent keeps private, rounded to about 1 km, locality or administrative_area is an area centroid (accurate to the town, not the building), unknown is a pin we hold no precision record for.
- 2026-10-17 `find_properties_by_description` changed (description)
  - before: Match Spanish properties from a free-form description of what the user wants. Best for vague, conditional, or lifestyle-heavy briefs ("3-bed near a good international school, flexible on budget if there's a sea view"). The matcher translates the brief into one to three parallel structured searches across PropertyList's full MLS, then merges and ranks the results. Returns up to 9 listings with match scores and an explanation of how the brief was interpreted (and what couldn't be resolved). Features in the brief are filters too: a private pool, a communal pool, a sea view, a garden, a garage and the like. Works without an API key up to a daily allowance per user (allowance.daily_answers in the result; briefs that find nothing do not count). An API key has its own, larger allowance. When the allowance is used up the call returns an error naming the limit; search_properties stays available for structured queries.
  - after: Match Spanish properties from a free-form description of what the user wants. Best for vague, conditional, or lifestyle-heavy briefs ("3-bed near a good international school, flexible on budget if there's a sea view"). The matcher translates the brief into one to three parallel structured searches across PropertyList's full MLS, then merges and ranks the results. Returns up to 9 listings with match scores and an explanation of how the brief was interpreted (and what couldn't be resolved). Features in the brief are filters too: a private pool, a communal pool, a sea view, a garden, a garage and the like. Commercial premises are matched too (an office, a local, a bar, a warehouse, a traspaso), never mixed with homes. Works without an API key up to a daily allowance per user (allowance.daily_answers in the result; briefs that find nothing do not count). An API key has its own, larger allowance. When the allowance is used up the call returns an error naming the limit; search_properties stays available for structured queries.
- 2026-10-17 `search_properties` changed (inputSchema)
- 2026-10-15 `find_price_reductions` changed (inputSchema)

Full history: https://api.protogrid.dev/v1/servers/es.propertylist%2Fpropertylist/changes

## Remotes

- https://mcp.propertylist.es/mcp (streamable-http, auth none, reachable true, uptime30d 0.9655172)

## Packages

_none_

## Tools (31)

- `area_market_summary`: Return aggregate market context for a Spanish area: number of active
listings, median price, mean price, median €/m², and the count
broken down by bedroom band. Filterable by property type and search
type (for-sale / for-rent / holiday-rentals). Use this before
recommending a price or commenting on whether a listing is good
value.

If the place name is not one we hold, the response says so
(error: location_not_found, location_match.matched: false) and
returns no figures. Figures are NEVER nationwide fallbacks.

If several places share the name and one of them holds at least
80% of their live listings, the figures are for that place alone,
and location_match.other_places names the others (id, municipality,
province, listings). If the listings are split between them, or
none has any, the response says so (error: ambiguous_location,
location_match.ambiguous: true), lists them in
location_match.candidates and returns no figures. Either way, ask
again with the chosen id as location_id, or with the town or
province after a comma ('El Limonar, Mijas'). A town that holds no
area of that name is answered as location_not_found, with the
areas of that name we do hold in location_match.candidates.

Price figures follow the search type and are labelled with
price_unit: sale prices are totals, for-rent is per MONTH and
holiday-rentals is per WEEK. median_price_per_sqm is reported for
sales only.

The `oracle` block carries the attested transaction price for the
area. Check `oracle.verified` before citing it as verified: it is
true only for Spanish figures from the notarial register (Consejo
General del Notariado) with at least 10 recorded transactions
behind them. Outside Spain the figure comes from a national
house-price index, carries no transaction count, and must never be
described as notary-backed - `oracle.source` names the real source
and `oracle.note` says why it is not verified.

Transactions are recorded per municipality, so a sub-area
(Sotogrande, Puerto Banus, Nueva Andalucia, Orihuela Costa) is
answered with the figure for the municipality containing it, with
`oracle.rolled_up_from_sub_area` true and `oracle.municipality` /
`oracle.municipality_code` naming it. Quote it as the
municipality's figure, never as the sub-area's own.

Commercial property (property_types commercial, a family such as
offices, retail, cafes-bars-restaurants, hotels-leisure,
industrial-warehouses, garages-storage-rooms, buildings or
commercial-land, or one type such as shop) is answered with its
own figures: the ASKING prices of live commercial listings
(figures_basis asking_prices), per family, with the asking price
or monthly rent per m² and size bands, and no `oracle` block.
Below min_sample priced listings only the count is given. Homes
and commercial property are never summarised together.

- `assign_listing`: Stage assigning one of YOUR agency's listings to a colleague. Returns a
preview and an action_id; NOTHING changes until you call
confirm_action with that id, after the agent has said yes.

Only an agency admin (or someone allowed to edit every listing), or
the agent the listing is assigned to now, may move it; an unassigned
listing can be taken by anyone who may edit listings. Name the
colleague as they appear in the CRM; if several match, the answer
lists them.

Requires a linked PropertyList account: the change is made in the agent's own name, with their CRM permissions, so an agency API key alone cannot make it.

- `autocomplete_location`: Look up property areas (city, suburb, urbanisation, or a group of
areas such as a region or a coast) by free-text name. Returns up to
10 matches with id, name, type, the parent province/city and the
number of listings, biggest first, so a group that covers a whole
region (e.g. 'Region of Murcia') comes before the city of the same
name. Each match names the municipality it sits in, which tells
apart places that share a name (El Limonar in Mijas, Málaga and in
Torrevieja, Alicante). Use this to resolve place names before calling
search_properties, especially for common spellings like
'Nueva Andalucia' (vs 'Nueva Andalucía') or 'Marbella old town',
and pass the chosen id to search_properties as location_id.

- `cancel_action`: Drop a change you staged and the agent does not want. Nothing in the
CRM changes; the action_id can no longer be confirmed. An unconfirmed
preview also lapses by itself after 30 minutes.

Requires a linked PropertyList account: the change is made in the agent's own name, with their CRM permissions, so an agency API key alone cannot make it.

- `confirm_action`: Make a change you staged with move_pipeline_stage, qualify_lead,
assign_listing, set_listing_status, send_listings_to_contact or
schedule_viewing. Call it ONLY after showing the agent the preview
and hearing them say yes. It can charge credits and send email,
exactly as the preview said.

Everything is checked again first; if the client, listing or stage
has changed so the action no longer applies, nothing happens and the
answer says why. A preview is valid for 30 minutes, and each one can
be confirmed once.

Requires a linked PropertyList account: the change is made in the agent's own name, with their CRM permissions, so an agency API key alone cannot make it.

- `create_contact`: Add a new contact to YOUR agency's CRM. Use when the agent wants to
save a person (buyer, owner, enquirer). If a contact with the same
email already exists it is returned rather than duplicated. The contact
is created unassigned and flagged as needing attention so it surfaces
in the CRM. Requires a linked PropertyList account (or an agency API
key).

- `create_listing`: Start a new property listing in YOUR agency's CRM from a description.
Use when the agent is describing a property they have taken on and
wants it in the system.

Creates a DRAFT. It is never published and never appears on the
portal from here: the agent adds photographs and the energy rating
in the CRM and publishes it themselves. The response includes a
direct link to the draft.

Location is resolved against the real area tree, so a town,
suburb or urbanisation name is enough. Requires a linked
PropertyList account (or an agency API key).

Commercial premises take a commercial property_type (office, shop,
retail, bar, restaurant, warehouse, garage, storage-room,
building, commercial-land...), no bedrooms, and their own facts:
usable area, the activity licence, a sale's tenure (freehold, or
traspaso: the lease and the business taken over, with the rent and
years left on the lease), a letting's guarantee, minimum term and
VAT. They are checked as the CRM checks them.

- `find_contacts`: Search YOUR agency's CRM contacts by name, company, email or phone.
Use to look a person up before logging a note or to check if they're
already in the CRM. Requires a linked PropertyList account (or an
agency API key); only ever searches the calling agency's own
contacts.

- `find_price_reductions`: Live listings whose asking price has been reduced recently, biggest
reduction first. Each listing carries the price before the cut, the
price now, the reduction in euros and percent, and when it was cut.
Use for "what has dropped in price in Estepona this month?" or to
spot motivated sellers.

Filters work as in search_properties (location, type, bedrooms,
price). A location we do not hold returns zero results with
error: location_not_found. Listings shown as price on application
are never included.

- `find_properties_by_description`: Match Spanish properties from a free-form description of what the
user wants. Best for vague, conditional, or lifestyle-heavy briefs
("3-bed near a good international school, flexible on budget if
there's a sea view"). The matcher translates the brief into one
to three parallel structured searches across PropertyList's full
MLS, then merges and ranks the results. Returns up to 9 listings
with match scores and an explanation of how the brief was
interpreted (and what couldn't be resolved). Features in the brief
are filters too: a private pool, a communal pool, a sea view, a
garden, a garage and the like. Commercial premises are matched too
(an office, a local, a bar, a warehouse, a traspaso), never mixed
with homes.

Works without an API key up to a daily allowance per user
(allowance.daily_answers in the result; briefs that find nothing do
not count). An API key has its own, larger allowance. When the
allowance is used up the call returns an error naming the limit;
search_properties stays available for structured queries.

- `find_similar_properties`: Listings similar to one property: same market (sale or rent), same
town, same kind of home, a similar number of bedrooms and a price
within about 25%. Closest in price first. Use it for "show me
alternatives to PL12345" or "anything like this but cheaper?".

Every property carries `oracle_verified` and
`oracle_attestation_url`, as in search_properties.

- `get_contact`: The full picture of one client in YOUR agency's CRM: contact details,
where they sit on the pipeline boards, the agents they belong to,
their saved searches, their enquiries, recent notes and history,
viewings with outcomes, listings already sent to them, when they
were last in touch and what is booked next.

Get the contact_id from find_contacts or my_pipeline first. Use
this before advising an agent on a client, and before
match_buyer_to_inventory, so you know what has already been sent.

Requires a linked PropertyList account (or an agency API key); only ever reads the calling
agency's own contacts, and only those the caller may see in the CRM.

- `get_guide`: Read one PropertyList guide. Answer from its text and link its url. If last_reviewed is set, say the page was checked on that date; if it is null, say the page shows no review date. If truncated is true, call again with offset set to next_offset. It is general information, not legal or tax advice.
- `get_property`: Fetch the full record for one PropertyList listing by its reference
code (the human-readable id like 'PLE-12345'). Use this once the
user has settled on a listing from search_properties or
find_properties_by_description and wants details or photos. To
contact the listing agency, use submit_enquiry - direct phone or
email details are not part of the payload.

Works without an API key up to a daily allowance of listings,
counted per IP address, or per account for an app linked to a
PropertyList account; an API key raises that allowance. The free
allowance is for an assistant answering a person's questions:
reading listings in bulk, or showing them on a website, needs MCP
Pro or the agency's website feed. When the day's allowance is used
the result carries `error` and `limits` instead of `property`.

`photos` holds the listing's first photos on the free tier and all
of them with MCP Pro; `photos_total` says how many the listing has.

`location.city` is whatever tier the listing was filed under and
may be a municipality OR a locality inside one. To place a listing
administratively use `location.municipality` (with its INE
municipality_code); it is null, never guessed, when we cannot
resolve it.

Commercial premises (`commercial` true) carry no bedrooms: read
`commercial_facts` (usable area, licence, price per m², a
letting's terms, a traspaso's rent and years left) and `tenure`
(freehold, or traspaso for a sale of the lease and the business).

`location.latitude` / `location.longitude` are present whenever
we hold a real coordinate; the fields are absent otherwise, and
are never 0,0. `location.coordinates_precision` says what the
point is: exact or street is the property itself, approximate is
a pin the agent keeps private, rounded to about 1 km, locality or
administrative_area is an area centroid (accurate to the town,
not the building), unknown is a pin we hold no precision record
for.

- `list_agencies`: Find PropertyList agencies (estate agents) in a given Spanish area,
ranked by how many active listings they carry there. Returns up to
10 agencies with name, location, listing count, and a public URL
the user can visit. Use this after a buyer has narrowed in on an
area and wants to know which agencies dominate it.

- `listing_health`: Checks YOUR agency's own listings for what is holding them back, and
lists the ones with problems, most problems first:
  - stale: online for 90 days or more, no enquiry in the last
    60 days and no price change in the last 90
  - too_few_photos: fewer than the 5 photos a listing needs to
    publish
  - no_energy_rating
  - missing_description: no Spanish or no English description
  - no_price: no asking price and not marked price on application
Defaults to online listings; status 'draft' checks what is not
published yet (what stops it publishing), 'all' checks both.

Requires a linked PropertyList account (or an agency API key); only the calling agency's own
listings.

- `log_note`: Log a note against a contact or a listing in YOUR agency's CRM (e.g.
record a call outcome or a viewing). Provide the note text plus a
contact_id (from find_contacts) and/or a property reference. The note
appears on the contact/property timeline. Requires a linked
PropertyList account (or an agency API key).

- `match_buyer_to_inventory`: The listings that fit one client's search in YOUR agency's CRM: your
own listings first, then shared MLS listings your agency can sell,
newest first. Each listing says whether it is your own.

Uses the client's saved search (or, if none is saved, the search
their latest debrief described), the same search the CRM runs for
that client. A client with no search returns an explanation rather
than a guess; set one up in the CRM or ask the agent for the brief
and use search_properties instead.

Get the contact_id from find_contacts. Call get_contact first if you
need to know what has already been sent to them.

Requires a linked PropertyList account (or an agency API key); only the calling agency's
clients that the caller may see in the CRM.

- `match_inventory_to_buyers`: The clients in YOUR agency's CRM whose saved search fits one listing:
who to call about it. Works for your own listings and for shared MLS
listings your agency can sell.

Only online listings can be matched (a saved search only ever shows
online stock); a draft returns an explanation. Clients are listed
with their assigned agent, pipeline stage and the search they
matched on. Use get_contact on any of them for the full picture.

Requires a linked PropertyList account (or an agency API key); only the calling agency's
clients that the caller may see in the CRM.

- `move_pipeline_stage`: Stage a move of a client's card to another step on a pipeline board
they are already on (buyer, seller, lead or tenant), as dragging the
card on the CRM board does. Returns a preview and an action_id;
NOTHING changes until you call confirm_action with that id, after
the agent has said yes.

Use the stage name as it reads on the board (my_pipeline lists them).
Putting a client on a board they are not on yet, or qualifying a
lead, is qualify_lead. Won and Lost need details the CRM asks for, so
they are done in the CRM. For an offer stage, pass offer_amount (and
property_reference if the card has no listing yet).

Requires a linked PropertyList account: the change is made in the agent's own name, with their CRM permissions, so an agency API key alone cannot make it.

- `my_leads`: List recent enquiries (leads) for YOUR agency's CRM - newest first.
Use when the agent asks what has just come in (e.g. "what came in
today?").

This is the enquiry inbox: an enquiry that arrived from the portal,
a website or a microsite. Once an agent starts working a person
they live on a pipeline board instead - for those, use my_pipeline.
An agency that works everything off the boards can have a busy CRM
and an empty inbox here, which is not the same as having no leads.

Requires a linked PropertyList account (or an agency API key); only
ever returns the calling agency's own leads.

- `my_listings`: List YOUR agency's own property listings (newest first), optionally
filtered by status or a specific reference. Use for "show my listings"
/ "is reference X still online?". Requires a linked PropertyList
account (or an agency API key); only ever returns the calling
agency's own properties.

- `my_pipeline`: List the cards on YOUR agency's CRM pipeline boards (Kanban), with
each board's own stage names and card counts.

Boards: lead, buyer, seller, tenant, nurture (each card is a
contact) and property (each card is a listing). Stage names are
whatever the agency renamed them to, so read them from the reply
rather than assuming.

Call with no pipeline for a summary of every board - use this to
answer "what's in my pipeline?" or "how many buyers do I have?".
Call with a pipeline for that board's cards, newest activity first.

This is the board view. For enquiries that have just arrived and
have not been worked yet, use my_leads instead.

Returns the WHOLE agency's boards, not one agent's cards: CRM access
belongs to the agency, not to a person. Requires a linked
PropertyList account (or an agency API key); only ever returns the
calling agency's own data.

- `qualify_lead`: Stage qualifying a client, as the CRM's Qualified popup does: tags
them as a buyer, seller or tenant, puts them on that pipeline board
and takes them off the Lead board. Returns a preview, including what
it will COST (a place on the Buyer board uses a free place or one
credit), and an action_id; NOTHING changes or is charged until you
call confirm_action with that id, after the agent has said yes.

Without `board`, the type comes from what the CRM already knows (the
client's saved search, or a Vendor/Landlord tag); with neither, it
asks for their details instead of guessing. With `board`, the client
goes onto that board at its first step.

`search` saves what a buyer or tenant is looking for with the
qualification, homes or commercial premises (offices, retail, a
bar, a warehouse, a traspaso...), never both in one search.

Requires a linked PropertyList account: the change is made in the agent's own name, with their CRM permissions, so an agency API key alone cannot make it.

- `schedule_viewing`: Stage a viewing in the agent's own CRM diary, with the client and the
listing on it. Returns a preview and an action_id; NOTHING is saved
until you call confirm_action with that id, after the agent has said
yes.

The listing can be the agency's own or a shared MLS listing. This
only books the agent's diary: for an MLS listing, the listing agency
is not contacted, so the agent arranges access with them. Times are
read in Spanish time (Europe/Madrid) unless they carry an offset.

Requires a linked PropertyList account: the change is made in the agent's own name, with their CRM permissions, so an agency API key alone cannot make it.

- `search_guides`: Search PropertyList's guides on buying, selling and renting in Spain: taxes such as ITP, IBI and non-resident income tax, the NIE, the buying process, laws and area guides. Use it for how-to, cost and legal questions, then call get_guide for the page. Spanish words need their accents (for example tributación, tasación).
- `search_properties`: Search PropertyList's full Spanish property MLS by structured filters.
Use for queries that translate cleanly to fields: bedrooms, bathrooms,
price ceiling, property type, area name. For free-form briefs use
find_properties_by_description instead.

Free use, with or without an API key, is for an assistant answering a
person's questions and has a daily allowance of results. Reading
listings in bulk, or showing them on a website, needs MCP Pro or the
agency's website feed. When the day's allowance is used the result is
empty and carries `error` and `limits`.

Returns a paginated list with summary text plus structured JSON. Every
property carries `oracle_verified` and `oracle_attestation_url` —
true when PropertyList Oracle holds a verified attestation for the listing's municipality and segment.

A `location` we do not hold returns zero results with
error: location_not_found and location_match.matched: false. It
never substitutes listings from other areas. `location_id` takes an
area id from autocomplete_location instead of a name; a group id
(type group, e.g. a region or a coast) searches every area in the
group. When several places share the name, the results are for
the one holding at least 80% of their live listings, and
location_match.other_places names the others; when the listings
are split, the results cover all of them and
location_match.ambiguous is true, with the places in
location_match.candidates (id, municipality, province). Pass one
as location_id, or add the town after a comma ('El Limonar, Mijas').

`features` keeps only listings that have ALL the features asked
for: pool (any pool, communal included), private_pool,
communal_pool, sea_view, garden, parking (a garage or parking space)
and terrace. Each
result then carries matched_features, saying which tags (and, for
parking and terrace, which space count or terrace size) the listing
matched on. private_pool means the listing is tagged with a private
or heated pool; many agency feeds tag every pool that way, communal
ones included, so use pool for "with a pool" and check the listing
when a pool must be private. communal_pool means the listing is
tagged with a communal (shared) pool: use it when the user asks for
a communal pool; pool also finds the communal pools a feed tagged
as private, so widen to pool when communal_pool finds too few.

`location.city` is whatever tier the listing was filed under and
may be a municipality OR a locality inside one. To place a listing
administratively use `location.municipality` (with its INE
municipality_code); it is null, never guessed, when we cannot
resolve it.

`location.latitude` / `location.longitude` are present whenever
we hold a real coordinate; the fields are absent otherwise, and
are never 0,0. `location.coordinates_precision` says what the
point is: exact or street is the property itself, approximate is
a pin the agent keeps private, rounded to about 1 km, locality or
administrative_area is an area centroid (accurate to the town,
not the building), unknown is a pin we hold no precision record
for.

- `send_listings_to_contact`: Stage an email from the agent to one of their clients with listings
attached, as the CRM sends listings (it appears on the client's
timeline). You write the subject and message, in the client's
language. Returns a preview with the address, subject, message and
listings, and an action_id; NOTHING is sent until you call
confirm_action with that id, after the agent has read it and said
yes.

Listings can be the agency's own or shared MLS listings it can sell
(match_buyer_to_inventory finds them). Up to 10 per email.

Requires a linked PropertyList account: the change is made in the agent's own name, with their CRM permissions, so an agency API key alone cannot make it.

- `set_listing_status`: Stage putting one of YOUR agency's listings online (it goes into
approval, as the CRM's Publish button does), taking it offline, or
marking it sold. Returns a preview, including any credit cost (a
private, unshared listing costs credits to publish), and an
action_id; NOTHING changes until you call confirm_action with that
id, after the agent has said yes.

Publishing checks what the CRM checks (photos, required details,
your agency's listing limit) and the preview says what is missing.
Sold is for a sale listing that is online or offline; sale_price is
recorded for market statistics unless hide_sale_price is true.

Requires a linked PropertyList account: the change is made in the agent's own name, with their CRM permissions, so an agency API key alone cannot make it.

- `submit_enquiry`: Submit an enquiry (lead) about a specific listing on behalf of a prospective
buyer or tenant. Use this once the user has chosen a property and wants the
agency to contact them. The listing agency receives the enquiry in their CRM
and follows up directly.

This is the supported way to contact a listing agency - direct phone or
email details are not exposed by other tools. Works without an API key up
to a small daily allowance, counted per IP address, or per account for
an app linked to a PropertyList account; an API key raises it to
40 enquiries per key in any 24 hours.
Provide the property
`reference` (from search results) plus the enquirer's contact details.
Only submit with the person's clear intent and consent - this sends their
details to a real estate agency.

- `verified_valuation`: Get a valuation for a property or area in Spain, Portugal or Cyprus
from recorded market data, not asking prices. Returns the €/m²
(`price_per_sqm`) for the municipality and segment, an indicative
valuation when you give a size (build_sqm) or a listing reference, and
a content-hashed attestation URL the figure can be cited from.

Check `verified` before citing. It is true ONLY for Spanish figures
drawn from the notarial register (Consejo General del Notariado) with
at least 10 recorded transactions behind them.
Portuguese figures come from the INE house-price index and Cypriot
ones from the Cyprus Statistical Service house-price index: they are
estimates, `transaction_backed` is false, `source_label` names the
source, and they must not be described as notary-verified. Other
countries have no figure yet. `indicative_band_pct` widens as the
evidence thins.

Transactions are recorded per municipality, so a query for a
sub-area (Sotogrande, Puerto Banus, Nueva Andalucia, Orihuela
Costa) is answered with the containing municipality's attestation:
`rolled_up_from_sub_area` is true, `location` and
`municipality_code` name the municipality, `requested_area` is what
you asked for and `rollup_note` is the sentence to repeat. Never
quote a rolled-up figure as the sub-area's own price.

Only two segment attestations exist per municipality, so a request for
penthouse or townhouse resolves to the nearest attested segment.
`property_type` is the segment actually used and
`requested_property_type` is what you asked for; when they differ the
text says so. Also returns our proprietary network achieved-sale figure
where available. If no attestation covers the area yet, it says so
plainly (use area_market_summary for asking-price stats instead).
The registers price homes only: a commercial listing or segment
(office, shop, hotel...) gets no figure, `verified` false and
`reason` commercial.

