Consultar datos

El servidor MCP y la REST API leen los mismos datos con un solo modelo: una colección, sus campos, un filtro, un orden y una página de resultados. Esta página es ese modelo, y cada punto en el que los dos difieren. Los dos necesitan una cuenta de Bidlo, y cualquiera puede conseguir una agendando una llamada.

Abrir en ChatGPTAbrir en Claudellms.txtllms-full.txtopenapi.json

Contenido

¿Cómo están organizados los datos?

Una clave lee un equipo, un equipo ve colecciones, una colección tiene documentos, y cada documento lleva un valor por cada campo de esa colección. Un documento es una obra, un contratista, una planta. Una colección es un tipo de ellos.

PalabraLo que es
TeamLa cuenta de tu empresa en Bidlo. Una clave de API lee un equipo y nada más.
ColecciónUn solo tipo de cosa: Projects tiene obras, Bid Items tiene precios unitarios tal como se ofertaron, Contractors tiene empresas, Facilities tiene plantas, bancos de material y patios.
DocumentoUno de ellos: una obra, una empresa, una planta.
FieldUna cosa que la colección sabe de cada uno de sus documentos, como un nombre, una fecha, una cantidad o un enlace a un documento de otra colección.
ValueLo que guarda un documento en un campo.

El servidor MCP lee todas las colecciones que tu equipo puede ver. La REST API nombra projects, contractors and facilities en su ruta, https://lite.bidlo.ai/api/v1/{collection_id}, y nada más. Los dos también nombran una colección de forma distinta: en REST es el alias de la ruta, en MCP es el nombre de la colección en query_database_data y su id en get_collection_fields. En MCP los nombres coinciden sin importar mayúsculas, sin espacios sobrantes, y en singular o plural.

¿Cómo encuentro los campos de una colección?

Llama a la herramienta MCP get_collection_fields con los ids de colección que te interesan: devuelve cada campo con su id, su nombre, su tipo y los operadores que acepta ese campo. Primero lista las colecciones con get_team_collections, luego pasa sus ids.

ClaveLo que te dice
field_idEl id con el que un filtro o un orden REST nombra el campo.
nameEl nombre que tu equipo le da al campo, que es con el que lo nombra un filtro MCP.
field_typeCómo se guarda el valor: text, number, date, tag, relation, location, state.
filter_field_typeCómo se filtra el campo, que es lo que organiza las tablas de operadores de abajo.
primary_keySi este es el campo del que sale el nombre del documento.
reference_collection_idPara una relación, la colección a la que apunta su valor. Esa es la colección donde se resuelve un nombre.
description, akaLo que significa el campo, y otros nombres con los que aparece.

La misma respuesta trae la tabla de operadores de cada tipo de filtro, la forma de valor de cada operador y la sintaxis de fórmula, así que una sola llamada le dice a un agente todo lo que necesita para escribir un filtro. Tres tipos de campo se filtran como otra cosa: una fórmula se filtra como lo que dé de resultado, una relación limitada a un solo documento se filtra como tag, y un tipo sin tabla propia se filtra como text.

Un equipo puede renombrar un campo integrado, así que el mismo campo se puede llamar de dos formas en dos cuentas. El id nunca cambia. Por eso en esta página no hay ningún id de campo impreso: lee los tuyos en get_collection_fields, y toma cada nombre de campo de los ejemplos de aquí como un ejemplo. También es la forma más rápida de conseguir ids para una llamada REST, porque hoy REST no tiene ninguna ruta que liste campos. La otra forma es leer el field_id de cualquier valor de una respuesta REST, que todos lo llevan.

Próximamente. Dos rutas REST van a listar las colecciones y sus campos: /api/v1/collections and /api/v1/collections/{collection_id}/fields. Los dos están marcados como próximos en /openapi.json. Ninguna está disponible hoy. Mientras tanto, los ids de campo salen de get_collection_fields con MCP.

¿Cómo funcionan los filtros?

Un filtro es un campo, un operador y un valor, y cada campo acepta un solo filtro; en REST el campo se nombra por su id, y en MCP por su nombre. Mándalos como un arreglo; los operadores que acepta un campo dependen de su tipo de filtro.

Un filtro REST
"filters": [
  {
    "field_id": "<field_id of your Bid Date field>",
    "operator": "between",
    "value": { "start": "2026-10-01", "end": "2026-12-31" },
    "conjunction": "AND"
  }
]
El mismo filtro con MCP
"filters": [
  {
    "field_name": "Bid Date",
    "operator": "between",
    "value": { "start": "2026-10-01", "end": "2026-12-31" }
  }
]

Cuatro reglas valen de los dos lados. Cada campo acepta un solo filtro: en REST un segundo filtro sobre el mismo campo es un 400 cuyo mensaje dice que los combines, y en MCP no hay error alguno: el último filtro sobre ese campo gana sin avisar. is_null and is_not_null no llevan valor. Un filtro con valor vacío (null, una cadena en blanco, un arreglo vacío, la mitad de un { start, end }) se descarta en lugar de aplicarse, así que una consulta que parece sin filtro casi siempre lo está. Y un valor con el formato equivocado devuelve un 400 que nombra el formato que esperaba.

Una regla vale de un solo lado. En REST cada filtro lleva su propio conjunction, tampoco AND (el predeterminado) o OR: cada filtro OR se une a un grupo, y ese grupo se combina con AND contra el grupo AND. No hay anidamiento ni paréntesis. En MCP no hay conjunción alguna y todos los filtros se combinan con AND.

Los operadores, completos: is_null, is_not_null, eq, ne, contains, not_contains, starts_with, ends_with, gt, lt, gte, lte, between, relative_range, within_distance, outside_distance. Cuáles acepta un campo dado, y cómo debe verse su valor, está abajo.

title, text

Un nombre, una descripción, un número escrito con letras. El valor siempre es una cadena. En MCP, eq con un valor de cadena en un campo de texto se trata como contains.

OperadorValue
eq, ne, contains, not_contains, starts_with, ends_withUna cadena.
is_null, is_not_nullSin valor.

number, material

Una cantidad, un precio, un estimado. El valor es un número, no una cadena.

OperadorValue
eq, ne, gt, lt, gte, lteUn número.
between{ start, end }. Solo REST.
is_null, is_not_nullSin valor.

date

Una fecha de licitación, una fecha de propuesta, una fecha de adjudicación. El valor es una cadena de fecha ISO o una palabra clave. Mira la sección de fechas de abajo para los tres formatos.

OperadorValue
eq, gt, lt, gte, lteUna cadena de fecha ISO, o una palabra clave.
neUna cadena de fecha ISO, o una palabra clave. Solo REST.
between{ start, end }
relative_range{ period, timeframe, number }
is_null, is_not_nullSin valor.

boolean

Un sí o un no. El valor es un booleano, no la cadena "true".

OperadorValue
eq, netrue or false.

tag

Una opción de una lista, como un estado. Una relación limitada a un solo documento también se filtra como tag.

OperadorValue
eq, neEl id del documento de la opción. Con REST, un arreglo de ids; con MCP, un nombre o un id.
is_null, is_not_nullSin valor.

tags, relation, people

Un vínculo a uno o más documentos de otra colección: el contratista de una obra, el condado en que está, la agencia que la licitó. Un campo de personas lleva ids de usuario en su lugar.

OperadorValue
contains, not_contains, eq, neIds de documento. En REST, un arreglo de ids; en MCP, un nombre, un id, o un arreglo de cualquiera de los dos.
is_null, is_not_nullSin valor.

state

Un estado. El valor es el nombre completo, no la abreviatura de dos letras.

OperadorValue
contains, not_containsNombres completos de estado, como ["Texas"].
is_null, is_not_nullSin valor.

location

Una dirección con coordenadas, como una planta o un patio. REST también acepta within_area y outside_area con un polígono GeoJSON; ese formato está en la página de la REST API.

OperadorValue
within_distance, outside_distance{ lat, lng, radiusInMeters }
is_null, is_not_nullSin valor.

¿Cómo funcionan las fechas?

Un valor de fecha es una cadena de fecha ISO o una palabra clave como today, between recibe un inicio y un fin, y relative_range recibe un periodo, un lapso y un número. Una ventana de licitación es un between; "los próximos tres meses" es un relative_range.

Tres filtros de fecha, con MCP
{ "field_name": "Bid Date", "operator": "gte", "value": "2026-10-01" }

{ "field_name": "Bid Date", "operator": "between",
  "value": { "start": "2026-10-01", "end": "2026-12-31" } }

{ "field_name": "Bid Date", "operator": "relative_range",
  "value": { "period": "next", "timeframe": "month", "number": 3 } }

Los dos lados aceptan today, yesterday, tomorrow en lugar de una fecha. REST también acepta last_week, next_week, last_month, next_month, last_year, next_year.

relative_range usa tres claves. period is past, next or this; timeframe is day, week, month or year; y number es obligatorio para past and next, que es cuántos de ese lapso hay que recorrer hacia atrás o hacia adelante. this no necesita número.

¿Cómo filtro por contratista, condado o agencia?

Esos campos enlazan a un documento de otra colección, así que el valor es el id de ese documento; con MCP puedes mandar el nombre y el servidor lo resuelve por ti. Un condado en una obra es un documento de la colección Counties, igual que cualquier otro enlace.

Con MCP basta un nombre
"filters": [
  { "field_name": "County", "operator": "contains", "value": "Travis County" }
]
Con REST, el valor es un arreglo de ids
"filters": [
  {
    "field_id": "<field_id of your County field>",
    "operator": "contains",
    "value": ["<document_id of that county>"]
  }
]

Con MCP el servidor busca el nombre en la colección a la que apunta el campo, que es la que nombra reference_collection_id en el campo, y usa la coincidencia. Un valor que ya es un id se usa tal cual. Si el nombre coincide con más de un documento, la llamada vuelve con un error que nombra las coincidencias y te pide resolverlo tú.

Para eso sirve resolve_documents_by_name , y también es como consigues ids para una llamada REST. Dale el id de la colección referenciada y un query para buscar o una lista de names para consultar, y responde con coincidencias exactas y aproximadas, cada una con su id y su nombre. Lee los primeros 10 nombres que mandes y devuelve de 1 a 25 filas por nombre, 10 de forma predeterminada. Necesita una de las dos entradas: sin ninguna, falla.

Lleva al filtro los ids que regresan. En REST un valor de relación siempre es un arreglo de ids, incluso cuando hay uno solo, y un id suelto se lee como un arreglo de uno.

¿Cómo busco cerca de un lugar?

Filtra un campo de ubicación con within_distance y un valor de lat, lng y radiusInMeters, donde el radio va en metros. outside_distance es el mismo filtro al revés.

Plantas, bancos de material y patios a 50 km de un punto
"filters": [
  {
    "field_name": "Location",
    "operator": "within_distance",
    "value": { "lat": 30.2672, "lng": -97.7431, "radiusInMeters": 50000 }
  }
]

El radio va en metros, así que 50 km son 50000. Las coordenadas de la obra son el centro de la búsqueda cuando buscas quién puede llegar a ella. is_null and is_not_null en el mismo campo son la forma de saber qué documentos traen coordenadas.

REST también puede filtrar una ubicación contra un área dibujada en vez de un radio. La página REST API tiene esa forma.

¿Cómo funcionan los órdenes?

Un orden es un campo y una dirección asc o desc, se permiten varios en el orden en que los mandas, y sin ninguno los documentos más nuevos van primero. Un orden nombra su campo igual que un filtro: por id en REST, por nombre en MCP.

Un orden, en las dos vías
REST   "sorts": [{ "field_id": "<field_id of your Bid Date field>", "direction": "asc" }]

MCP    "sorts": [{ "field_name": "Bid Date", "direction": "asc" }]

Envía varios y se aplican en el orden del arreglo, así que el primero manda. Un campo acepta un solo orden. Ordenar por un campo de ubicación es ordenar por distancia y necesita un punto desde el cual medir, que REST recibe en el orden mismo. Sin ningún orden, los documentos más nuevos vienen primero.

¿Cómo funcionan las páginas?

Pide un límite y un número de página, empezando en la página 1; ninguno de los dos lados devuelve un conteo total, así que lees hasta que una página regresa corta. Los dos lados permiten tamaños distintos.

limitpageSaber cuándo terminaste
MCPDe 1 a 100, 25 por defectoDesde 1, hasta 20has_more and next_page vienen con los resultados
RESTDe 1 a 2000, 100 por defectoDesde 1, repetido en metaNo regresa nada: sigue leyendo hasta que una página regrese corta

Un límite fuera del rango se rechaza en vez de ajustarse: en REST es un 400, y en MCP es un error de validación de entrada en la llamada a la herramienta. Ninguno de los dos lados devuelve un conteo total de documentos que coinciden, y ninguno tiene cursor, así que paginar es subir el número de página hasta que se acaban los resultados.

¿Qué devuelve?

Una página de documentos, cada uno con su id, su nombre y un valor por campo, donde el formato de un valor sigue el tipo del campo. Los dos lados identifican los campos de un documento de forma distinta: REST por id de campo, MCP por nombre de campo.

Una respuesta REST
{
  "data": [
    {
      "id": "<document_id>",
      "name": "…",
      "collection_id": "<collection_id>",
      "created_at": "…",
      "fields": {
        "<field_id>": {
          "value": "2026-10-14",
          "field_name": "Bid Date",
          "field_type": "date",
          "field_id": "<field_id>"
        }
      }
    }
  ],
  "meta": { "page": 1, "collection_id": "<collection_id>" }
}
Un resultado MCP
{
  "collection_id": "<collection_id>",
  "collection_name": "Projects",
  "page": 1,
  "selected_fields": [{ "field_id": "<field_id>", "field_name": "Bid Date" }],
  "documents": [
    {
      "document_id": "<document_id>",
      "document_name": "…",
      "fields": { "Bid Date": "2026-10-14" }
    }
  ],
  "returned_row_count": 1,
  "has_more": true,
  "next_page": 2
}

Con MCP, pide por nombre los campos que quieres en return_fields. Si lo omites regresa un conjunto predeterminado de hasta 8 campos con una advertencia que lo dice, y rara vez es el conjunto que querías. Un valor de texto de más de 400 caracteres se recorta. En REST, fields recibe una lista de ids de campo y hace lo mismo; si lo omites vuelven todos los campos.

La forma de un valor depende del tipo del campo. Estas son las formas de REST; con MCP un documento enlazado vuelve como un id, un nombre y una mención.

Tipo de campoValue
text, number, booleanTal como se guarda.
dateUna cadena de fecha ISO.
tag, un relation limitada a un solo documentoUn objeto con un id, un value and a document_id.
tags, un relation a variosUn arreglo de esos objetos.
locationUn objeto con un id, un name, un address, un lat and a lng.
stateUn objeto con un id, un name y un abbreviation.
peopleIds.

Un caso avanzado cambia el formato de toda la respuesta: REST acepta un grouping de un id de campo y un tipo de campo, y entonces la respuesta vuelve como grupos de documentos y no como una lista plana, con page selecciona grupos. La página REST API lo cubre.

¿A dónde voy después?

La página del servidor MCP tiene las herramientas y sus entradas, la página de la REST API tiene la única ruta y sus parámetros, y la página de acceso tiene la clave. Todo lo de esta página vale para los dos; las páginas de referencia llevan lo que vale para uno solo.

  • Servidor MCP: las cinco herramientas, qué recibe y devuelve cada una, cómo conectar un cliente, y cómo regresan los errores.
  • REST API: la única ruta, sus parámetros, la envoltura de la respuesta y los códigos de estado, más /openapi.json.
  • Acceso y claves: cómo conseguir una clave y cómo mandarla.

Si la lectura que necesitas no está en el modelo de arriba, escribe a support@bidlo.ai.

Se necesita una cuenta de Bidlo. Cualquiera puede conseguir una agendando una llamada en https://cal.com/matt-wolfe-yecrho/30min — Bidlo prepara el equipo, y luego un dueño o administrador del equipo crea la clave de API en Settings → Features → API. No hay un plan público ni se puede abrir una cuenta por cuenta propia.