---
title: "Herramientas públicas"
description: "Referencia de las herramientas MCP de Matchai search_listings, get_listing y get_professional. Entradas, salidas, ejemplos y el widget de tarjetas de anuncios."
last_updated: "2026-10-21"
lang: "es"
canonical: "https://matchai.co/docs/es/developers/mcp-server/public-tools"
---

# Herramientas públicas

> Referencia de las herramientas MCP de Matchai search_listings, get_listing y get_professional. Entradas, salidas, ejemplos y el widget de tarjetas de anuncios.

Source: https://matchai.co/docs/es/developers/mcp-server/public-tools

El [servidor MCP](https://matchai.co/docs/es/developers/mcp-server) ofrece tres herramientas. Todas son de solo lectura, no necesitan cuenta y solo devuelven datos publicados.

| Herramienta | Propósito |
|---|---|
| `search_listings` | Encontrar anuncios disponibles por lugar, precio, habitaciones, baños, tipo de propiedad, y venta o alquiler. |
| `get_listing` | Obtener todos los detalles públicos de un anuncio a partir de cualquiera de sus direcciones o de su id. |
| `get_professional` | Obtener el perfil público de un corredor y sus anuncios disponibles. |

## Cómo se comportan las herramientas [#how-the-tools-behave]

- **Solo lectura.** Cada herramienta está anotada con `readOnlyHint: true`, `destructiveHint: false`, `idempotentHint: true` y `openWorldHint: false`. Solo leen el catálogo publicado de Matchai.
- **Tres partes en cada resultado.** `content` es texto plano que un modelo puede citar. `structuredContent` son datos tipados que corresponden al `outputSchema` de la herramienta. `_meta` es para el [widget de tarjetas de anuncios](#the-listing-card-widget), y los demás clientes pueden ignorarlo.
- **Lo desconocido es null.** Un dato que el corredor no indicó es `null`, nunca `0`. Un anuncio sin precio indicado tiene `price: null`.
- **Argumentos flexibles.** Los valores `null` y las cadenas vacías se descartan, y los números enviados como cadenas, como `"600000"` o `"$600,000"`, se convierten antes de la validación.
- **Los errores que un modelo puede corregir** vuelven como un resultado con `isError: true` y un mensaje. Una llamada que nombra una herramienta desconocida, o cuyo `arguments` no es un objeto, es un error JSON-RPC `-32602`. Consulta los [errores](https://matchai.co/docs/es/developers/mcp-server#errors).

## El objeto de anuncio [#the-listing-object]

`search_listings` y `get_professional` devuelven objetos de anuncio, y `get_listing` devuelve el mismo objeto con campos de detalle adicionales.

<TypeTable
  type={{
    id: { type: 'string', description: 'El id del anuncio, un UUID. Pásalo a get_listing.' },
    title: { type: 'string', description: 'El título del anuncio.' },
    url: { type: 'string', description: 'La página del anuncio. Es el dominio propio del corredor una vez que ese dominio está activo, y es la dirección que debes citar y enlazar.' },
    price: { type: 'number | null', description: 'El precio de venta, o el alquiler, en la moneda del anuncio. null cuando el corredor no indicó ninguno.' },
    currency: { type: 'string', description: 'Código de moneda ISO 4217, normalmente CAD.' },
    priceText: { type: 'string | null', description: 'El precio tal como lo lee una persona, por ejemplo $589,000. null cuando no hay precio.' },
    transaction: { type: '"sale" | "rent" | null', description: 'Si la propiedad está en venta o en alquiler.' },
    status: { type: '"available" | "sold" | "rented"', description: 'Disponibilidad. La búsqueda solo devuelve anuncios disponibles.' },
    propertyType: { type: '"house" | "condo" | "apartment" | "townhouse" | "plex" | "land" | "cottage" | "commercial" | null', description: 'Tipo de propiedad. plex abarca desde los dúplex hasta los quíntuplex.' },
    bedrooms: { type: 'number | null', description: 'Número de habitaciones.' },
    bathrooms: { type: 'number | null', description: 'Número de baños. Un medio baño cuenta como 0.5.' },
    livingAreaSqft: { type: 'number | null', description: 'Superficie habitable en pies cuadrados, convertida cuando el corredor la ingresó en metros cuadrados.' },
    neighbourhood: { type: 'string | null', description: 'Vecindario o distrito.' },
    city: { type: 'string | null', description: 'Ciudad o municipio.' },
    region: { type: 'string | null', description: 'Provincia o región.' },
    location: { type: 'string | null', description: 'La línea de ubicación que muestra la página del anuncio, por ejemplo Plateau-Mont-Royal, Montréal.' },
    image: { type: 'string | null', description: 'URL de la foto de portada.' },
    broker: { type: '{ name: string, brokerage: string | null } | null', description: 'El corredor del anuncio.' },
    site: { type: '{ name: string | null, url: string }', description: 'El sitio de Matchai del corredor que publica el anuncio.' },
    updatedAt: { type: 'string', description: 'Cuándo cambió el anuncio por última vez, como marca de tiempo ISO 8601.' },
  }}
/>

## search_listings [#search_listings]

Busca anuncios publicados que están disponibles en este momento. Devuelve hasta 10, primero la mejor coincidencia.

### Entrada [#input]

Todos los campos son opcionales. Sin argumentos, se devuelven los anuncios actualizados más recientemente.

<TypeTable
  type={{
    query: { type: 'string', description: 'Lo que pidió la persona, con sus propias palabras y en cualquier idioma, por ejemplo condominio de 3 habitaciones cerca del metro en Laval. Hasta 300 caracteres.' },
    city: { type: 'string', description: 'Ciudad o municipio, por ejemplo Montréal o Gatineau. Hasta 80 caracteres. Se compara como nombre de lugar completo, sin tener en cuenta los acentos.' },
    neighbourhood: { type: 'string', description: 'Vecindario o distrito, por ejemplo Plateau-Mont-Royal. Hasta 80 caracteres.' },
    transaction: { type: '"sale" | "rent"', description: 'sale para comprar, rent para alquilar.' },
    propertyType: { type: '"house" | "condo" | "apartment" | "townhouse" | "plex" | "land" | "cottage" | "commercial"', description: 'Tipo de propiedad.' },
    minPrice: { type: 'integer', description: 'Precio mínimo en dólares, o alquiler mensual en el caso de los alquileres. De 0 a 1 000 000 000.' },
    maxPrice: { type: 'integer', description: 'Precio máximo en dólares, o alquiler mensual en el caso de los alquileres. De 0 a 1 000 000 000.' },
    minBedrooms: { type: 'integer', description: 'Al menos esta cantidad de habitaciones. De 0 a 20.' },
    minBathrooms: { type: 'number', description: 'Al menos esta cantidad de baños. De 0 a 20. Un medio baño cuenta como 0.5.' },
    limit: { type: 'integer', default: '6', description: 'Cuántos anuncios devolver. De 1 a 10.' },
  }}
/>

`query` se interpreta con el mismo analizador que la [búsqueda de anuncios](https://matchai.co/docs/es/developers/listing-search#what-the-query-understands): los precios, las habitaciones, los baños, la venta o el alquiler y el tipo de propiedad se extraen de la frase, en inglés, francés o español. Un argumento explícito siempre prevalece sobre lo que dice la frase, y la frase completa lo que los argumentos omiten. Si `minPrice` es mayor que `maxPrice`, se intercambian.

### Salida [#output]

<TypeTable
  type={{
    kind: { type: '"listings"', description: 'Identifica el tipo de resultado.' },
    filters: { type: 'object', description: 'Los filtros que aplicó la búsqueda: keywords, city, neighbourhood, transaction, propertyType, minPrice, maxPrice, minBedrooms y minBathrooms. Cada uno es null cuando no se aplicó. keywords contiene hasta 6 palabras en minúsculas y sin acentos, que se compararon con los títulos, los lugares, los tipos de propiedad y las descripciones.' },
    count: { type: 'integer', description: 'La cantidad de anuncios devueltos.' },
    listings: { type: 'listing[]', description: 'Los anuncios coincidentes, primero la mejor coincidencia. Consulta el objeto de anuncio más arriba.' },
  }}
/>

El texto de `content` empieza con la cantidad de anuncios y una indicación de cómo están ordenados. Luego enumera una entrada por anuncio con el título, el precio y el tipo, los datos, el lugar, el corredor del anuncio y la URL.

Un resultado vacío indica si Matchai revisó todos los anuncios o solo los actualizados más recientemente. Nunca afirma que "no existe ninguno" en una búsqueda que no llegó más atrás. Una ciudad o un rango de precios permite que la búsqueda llegue más lejos.

### Ejemplo [#example]

Envía esto como una solicitud `tools/call`:

```json
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "search_listings",
    "arguments": { "query": "3 bedroom condo in Laval under 600k" }
  }
}
```

Un resultado abreviado. Esta búsqueda encontró un anuncio, y el objeto `filters` muestra cómo se interpretó la frase:

```json
{
  "content": [
    {
      "type": "text",
      "text": "1 published listing on Matchai. Results are ordered by relevance to the request and by how recently the listing was updated. No broker can pay to change the order or to be included.\n\n1. Bright 3-bedroom condo near Montmorency metro — $585,000, condo for sale\n   3 bedrooms · 1.5 bathrooms · 1,120 sq ft · Montmorency, Laval, Québec\n   Listed by Alice Tremblay (Agence Exemple)\n   https://alice.matchai.co/bright-condo-montmorency"
    }
  ],
  "structuredContent": {
    "kind": "listings",
    "filters": {
      "keywords": ["laval"],
      "city": null,
      "neighbourhood": null,
      "transaction": null,
      "propertyType": "condo",
      "minPrice": null,
      "maxPrice": 600000,
      "minBedrooms": 3,
      "minBathrooms": null
    },
    "count": 1,
    "listings": [
      {
        "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
        "title": "Bright 3-bedroom condo near Montmorency metro",
        "url": "https://alice.matchai.co/bright-condo-montmorency",
        "price": 585000,
        "currency": "CAD",
        "priceText": "$585,000",
        "transaction": "sale",
        "status": "available",
        "propertyType": "condo",
        "bedrooms": 3,
        "bathrooms": 1.5,
        "livingAreaSqft": 1120,
        "neighbourhood": "Montmorency",
        "city": "Laval",
        "region": "Québec",
        "location": "Montmorency, Laval, Québec",
        "image": "https://media.example.com/listings/bright-condo-montmorency/cover.jpg",
        "broker": { "name": "Alice Tremblay", "brokerage": "Agence Exemple" },
        "site": { "name": "Alice Tremblay Immobilier", "url": "https://alice.matchai.co" },
        "updatedAt": "2026-10-12T14:03:22.000Z"
      }
    ]
  },
  "_meta": {
    "ui": { "resourceUri": "ui://matchai/listing-cards.html?v=3f9a1c2b7d10" }
  }
}
```

El objeto `_meta` real tiene algunas claves más para el widget. Los nombres, las direcciones y el host de las fotos de estos ejemplos son ficticios.

## get_listing [#get_listing]

Devuelve todos los detalles públicos de un anuncio publicado.

### Entrada [#input-1]

Envía `url` o `id`. Si envías ambos, se usa `id`.

<TypeTable
  type={{
    url: { type: 'string', description: 'Cualquier dirección de la página del anuncio. Hasta 2048 caracteres. Se puede omitir el esquema.' },
    id: { type: 'string', description: 'El id del anuncio, un UUID, de un resultado anterior.' },
  }}
/>

Todas estas direcciones nombran el mismo anuncio, con o sin barra final o parámetros de seguimiento:

- el sitio de Matchai del corredor, como `https://alice.matchai.co/bright-condo-montmorency`;
- la forma anterior con `/listing/` antes del slug, como `https://alice.matchai.co/listing/bright-condo-montmorency`;
- el dominio propio del corredor, cuando el sitio tiene uno;
- la forma de ruta en la plataforma, como `https://matchai.co/site/alice/bright-condo-montmorency`;
- la copia en Markdown de cualquiera de ellas, terminada en `.md`;
- el id del anuncio por sí solo.

### Salida [#output-1]

<TypeTable
  type={{
    kind: { type: '"listing"', description: 'Identifica el tipo de resultado.' },
    listing: { type: 'listing object + details', description: 'El objeto de anuncio de arriba, más los campos siguientes.' },
  }}
/>

Los campos adicionales de `listing`:

<TypeTable
  type={{
    description: { type: 'string | null', description: 'La descripción del corredor, recortada a 1500 caracteres.' },
    highlights: { type: 'string[]', description: 'Hasta 8 puntos destacados que eligió el corredor.' },
    yearBuilt: { type: 'number | null', description: 'Año en que se construyó el edificio.' },
    lotSizeSqft: { type: 'number | null', description: 'Superficie del terreno en pies cuadrados.' },
    address: { type: 'string | null', description: 'La línea de la calle. Solo está presente cuando el corredor publicó la dirección.' },
    postalCode: { type: 'string | null', description: 'Solo está presente cuando el corredor publicó la dirección.' },
    images: { type: 'string[]', description: 'Hasta 8 URL de fotos, primero la foto de portada.' },
    datePosted: { type: 'string | null', description: 'Cuándo se publicó el anuncio por primera vez.' },
  }}
/>

La búsqueda solo devuelve anuncios disponibles. `get_listing` también puede devolver un anuncio publicado que desde entonces se haya vendido o alquilado, así que revisa `status`.

Si ningún anuncio publicado coincide, el resultado tiene `isError: true` e indica que es posible que el anuncio se haya dejado de publicar o que la dirección esté incompleta.

### Ejemplo [#example-1]

```json
{
  "name": "get_listing",
  "arguments": { "url": "https://alice.matchai.co/bright-condo-montmorency" }
}
```

El `structuredContent` del resultado, abreviado:

```json
{
  "kind": "listing",
  "listing": {
    "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "title": "Bright 3-bedroom condo near Montmorency metro",
    "url": "https://alice.matchai.co/bright-condo-montmorency",
    "price": 585000,
    "currency": "CAD",
    "priceText": "$585,000",
    "transaction": "sale",
    "status": "available",
    "propertyType": "condo",
    "bedrooms": 3,
    "bathrooms": 1.5,
    "livingAreaSqft": 1120,
    "neighbourhood": "Montmorency",
    "city": "Laval",
    "region": "Québec",
    "location": "Montmorency, Laval, Québec",
    "image": "https://media.example.com/listings/bright-condo-montmorency/cover.jpg",
    "broker": { "name": "Alice Tremblay", "brokerage": "Agence Exemple" },
    "site": { "name": "Alice Tremblay Immobilier", "url": "https://alice.matchai.co" },
    "updatedAt": "2026-10-12T14:03:22.000Z",
    "description": "A bright corner unit on the fourth floor, five minutes from the metro. …",
    "highlights": ["Two balconies", "Indoor parking", "Five minutes from the metro"],
    "yearBuilt": 2012,
    "lotSizeSqft": null,
    "address": null,
    "postalCode": null,
    "images": [
      "https://media.example.com/listings/bright-condo-montmorency/cover.jpg",
      "https://media.example.com/listings/bright-condo-montmorency/living-room.jpg"
    ],
    "datePosted": "2026-09-30"
  }
}
```

Aquí el corredor no publicó la dirección, así que `address` y `postalCode` son `null`.

## get_professional [#get_professional]

Devuelve el perfil público de un corredor con sus anuncios disponibles, hasta 10.

### Entrada [#input-2]

Envía `username` o `url`. Si envías ambos, se usa `username`.

<TypeTable
  type={{
    username: { type: 'string', description: 'El nombre de usuario de Matchai del corredor, por ejemplo alice_tremblay. Se ignora una @ inicial. Hasta 64 caracteres.' },
    url: { type: 'string', description: 'El sitio de Matchai del corredor, como https://alice.matchai.co, su Página Matchai, como https://alice.matchai.pro, su tarjeta de presentación o su perfil, o cualquiera de las páginas de sus anuncios. Hasta 2048 caracteres.' },
  }}
/>

### Salida [#output-2]

<TypeTable
  type={{
    kind: { type: '"professional"', description: 'Identifica el tipo de resultado.' },
    professional: { type: 'object', description: 'El corredor. Consulta los campos siguientes.' },
    listings: { type: 'listing[]', description: 'Los anuncios publicados y disponibles del corredor, hasta 10.' },
  }}
/>

Los campos de `professional`:

<TypeTable
  type={{
    name: { type: 'string', description: 'El nombre del corredor.' },
    username: { type: 'string | null', description: 'Su nombre de usuario de Matchai.' },
    jobTitle: { type: 'string | null', description: 'Su título profesional.' },
    brokerage: { type: 'string | null', description: 'La agencia con la que trabaja.' },
    licence: { type: '{ number: string, body: string | null } | null', description: 'Su número de licencia y el organismo regulador, por ejemplo OACIQ. null cuando no hay ninguna licencia registrada.' },
    photo: { type: 'string | null', description: 'URL de su foto de perfil.' },
    siteUrl: { type: 'string | null', description: 'Su sitio de Matchai, cuando está publicado.' },
    pageUrl: { type: 'string | null', description: 'Su Página Matchai, cuando está publicada.' },
    contact: { type: '{ email: string | null, phone: string | null }', description: 'Su dirección de correo electrónico y su número de teléfono. Ambos son null a menos que el corredor los haya publicado en su tarjeta de presentación.' },
  }}
/>

Un corredor se devuelve solo si eligió ser público en algún lugar: un sitio publicado, una Página Matchai publicada o una tarjeta de presentación activada. De lo contrario, el resultado tiene `isError: true` e indica que ningún corredor con un perfil público de Matchai coincide.

### Ejemplo [#example-2]

```json
{
  "name": "get_professional",
  "arguments": { "username": "alice_tremblay" }
}
```

La parte `professional` del `structuredContent` del resultado. El array `listings` que la acompaña contiene objetos de anuncio como los de `search_listings`.

```json
{
  "name": "Alice Tremblay",
  "username": "alice_tremblay",
  "jobTitle": "Real estate broker",
  "brokerage": "Agence Exemple",
  "licence": { "number": "123456", "body": "OACIQ" },
  "photo": "https://media.example.com/avatars/alice-tremblay.jpg",
  "siteUrl": "https://alice.matchai.co",
  "pageUrl": "https://alice.matchai.pro",
  "contact": { "email": null, "phone": null }
}
```

## El widget de tarjetas de anuncios [#the-listing-card-widget]

Cuando una herramienta devuelve anuncios, los asistentes compatibles con MCP Apps pueden mostrarlos como tarjetas dentro de la conversación. Las tarjetas provienen de un único recurso al que apuntan todas las herramientas.

| Detalle | Valor |
|---|---|
| URI | `ui://matchai/listing-cards.html?v=…`, donde `v` es una versión que cambia cada vez que cambia el widget |
| Tipo | `text/html;profile=mcp-app` |
| Nombre | `listing-cards` (título "Listing cards") |
| Referenciado desde | `_meta.ui.resourceUri` en cada herramienta de `tools/list` y en cada resultado. ChatGPT lee el mismo URI desde su propia clave en `_meta`. |

Lee el URI actual en `tools/list` en lugar de escribirlo directamente en el código. `resources/read` también acepta el URI sin `v`, y cualquier `v` anterior, y responde con el widget actual.

`resources/list` devuelve ese único recurso, incluida la política de seguridad de contenido que debe aplicar el asistente:

```json
{
  "resources": [
    {
      "uri": "ui://matchai/listing-cards.html?v=3f9a1c2b7d10",
      "name": "listing-cards",
      "title": "Listing cards",
      "description": "Interactive cards for the listings a Matchai tool returned.",
      "mimeType": "text/html;profile=mcp-app",
      "_meta": {
        "ui": {
          "csp": {
            "connectDomains": ["https://matchai.co"],
            "resourceDomains": ["https://matchai.co", "https://media.example.com"]
          },
          "prefersBorder": true
        }
      }
    }
  ]
}
```

`resources/read` devuelve el widget como un único documento HTML autónomo en `contents[0].text`. No usa ningún framework ni ninguna fuente o script externos.

Qué hace el widget:

- **Muestra el resultado según su tipo.** `kind: "listings"` se convierte en una fila desplazable de tarjetas, `kind: "listing"` en una tarjeta más grande con una descripción breve, y `kind: "professional"` en el nombre y el cargo del corredor con un enlace **Visitar el sitio**, sobre una fila de tarjetas.
- **Se adapta al asistente.** Usa el tema de colores y el idioma del asistente (inglés, francés o español) y abre los enlaces a través del asistente.
- **Gestiona los fallos.** Un resultado con `isError: true`, o sin `structuredContent`, muestra "Nada que mostrar".
- **Informa que se vio una tarjeta.** Cuando una tarjeta ha estado en pantalla el tiempo suficiente para leerse, el widget le indica a Matchai qué anuncios estuvieron visibles, durante cuánto tiempo y qué parte de cada uno, además del tipo general de plataforma del asistente (web, escritorio o móvil). Si el resultado no incluye un token para este informe, no se envía nada. Nunca envía texto de la conversación ni un identificador de usuario.

Un asistente que no renderiza MCP Apps ignora el widget. El texto de `content` y los datos de `structuredContent` contienen la respuesta completa. Si creas tus propias tarjetas, enlaza cada una a la `url` del anuncio.

<Cards>
  <Card title="Servidor MCP" href="https://matchai.co/docs/es/developers/mcp-server">
    Endpoints, transporte y cómo conectar un asistente.
  </Card>
  <Card title="Búsqueda de anuncios" href="https://matchai.co/docs/es/developers/listing-search">
    La misma búsqueda como un endpoint HTTP simple.
  </Card>
</Cards>

## Sitemap

- [Todas las páginas de la documentación de Matchai](https://matchai.co/docs/sitemap.md)
- [llms.txt](https://matchai.co/docs/es/llms.txt)
