El canal para agentes
Quelva expone un servidor MCP (Model Context Protocol) sobre HTTP. Su agente consulta los datos de SECOP II por herramienta, en lugar de suponerlos o de buscarlos por su cuenta.
El canal es de solo lectura sobre los datos de SECOP: ninguna herramienta escribe en SECOP, ni modifica nada. Dos de las herramientas sí escriben un registro de verificación propio de Quelva, para que exista el rastro de qué se evaluó y con qué versión del motor.
Servidor: secop-mcp, versión 0.2.0. Protocolo: MCP sobre HTTP con streaming (Streamable
HTTP). El endpoint es POST /mcp.
Publicar el servidor
Quelva entrega el servidor MCP como un servicio HTTP independiente. Para ponerlo en marcha necesita:
Puerto. Por defecto
8787(variablePORT). El servicio escucha en0.0.0.0.Base de datos. Una conexión a la base de datos de Quelva (
DATABASE_APP_URL). Sin ella el servidor no arranca.Secreto de autenticación.
BETTER_AUTH_SECRET, al menos 32 caracteres aleatorios.Lista de hosts permitidos.
MCP_ALLOWED_HOSTScon los dominios desde los que se servirá (sin puerto). En producción es obligatoria.Token de Socrata (opcional).
SOCRATA_APP_TOKENsube los límites de consulta a datos.gov.co.
El servicio expone GET /healthz sin autenticación: responde 200 con {status, service, version}. Úselo como prueba de vida en su balanceador o en su orquestador. GET / devuelve una
ficha con el protocolo, el endpoint y el método de autenticación.
Cómo conecta su equipo
Una vez publicado, cada empresa obtiene su clave desde el panel (Configuración → Claves) y apunta
su agente a https://<su-dominio>/mcp con el encabezado Authorization: Bearer secop_…. Los
detalles de conexión, por agente, están en la sección "Conectarse".
Conectarse
El servidor habla MCP sobre HTTP con streaming. Dos detalles que hacen que la primera llamada funcione:
El cliente debe enviar
Accept: application/json, text/event-stream. Si envía sólo uno de los dos, el servidor responde406.Las respuestas llegan enmarcadas en SSE (
data: {…}), no como JSON plano.
curl -s https://<su-instancia>/mcp \
-H 'Authorization: Bearer secop_...' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'También existen GET /healthz (estado del servicio, sin autenticación) y GET / (una ficha con
el protocolo, el endpoint y el método de autenticación).
Configuración por agente
Reemplace https://<su-instancia>/mcp por la URL de su servidor y secop_… por su clave.
Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"quelva-secop": {
"type": "http",
"url": "https://<su-instancia>/mcp",
"headers": {
"Authorization": "Bearer secop_..."
}
}
}
}Claude Code (.mcp.json o ~/.claude.json):
{
"mcpServers": {
"quelva-secop": {
"type": "http",
"url": "https://<su-instancia>/mcp",
"headers": {
"Authorization": "Bearer secop_..."
}
}
}
}O desde la línea de comandos:
claude mcp add --transport http quelva-secop https://<su-instancia>/mcp \
--header "Authorization: Bearer secop_..."OpenCode (opencode.json):
{
"mcp": {
"quelva-secop": {
"type": "remote",
"url": "https://<su-instancia>/mcp",
"headers": {
"Authorization": "Bearer secop_..."
}
}
}
}Codex (~/.codex/config.toml):
[mcp_servers.quelva-secop]
url = "https://<su-instancia>/mcp"
[mcp_servers.quelva-secop.headers]
Authorization = "Bearer secop_..."Cualquier cliente MCP que hable Streamable HTTP: apunte la URL al endpoint, envíe el
encabezado Authorization: Bearer secop_… y los dos tipos en Accept. Eso es todo lo que el
servidor exige.
Autenticación
Claves de API por empresa, no por persona.
Se emiten desde el panel, en Configuración → Claves. Sólo el propietario de la organización puede emitirlas y revocarlas.
El secreto se muestra una sola vez, al crearla. Guárdelo en su gestor de secretos; si se pierde, revóquela y emita otra.
El formato es
secop_seguido de 64 caracteres: 70 en total.En la base de datos se guarda el resumen SHA-256 de la clave y sus primeros 20 caracteres (el prefijo
secop_incluido) para poder identificarla en soporte. El resto nunca se guarda.Cada llamada se verifica contra la base de datos y queda registrada con la empresa y la clave que la hizo. La clave lleva el alcance
secop:read.Revocar una clave la invalida de inmediato: la siguiente llamada del agente recibe
401.
Cómo obtener una clave
Entre al panel con la cuenta propietaria de su empresa.
Vaya a Configuración → Claves.
Cree una clave con un nombre que identifique la integración (por ejemplo, "Claude de licitaciones").
Copie el secreto que se muestra una sola vez y péguelo en la configuración de su agente.
Una clave por integración: si una se filtra, revóquela sola; las demás siguen funcionando.
Las siete herramientas
Sobre procesos
search_processes — los procesos abiertos.
| Parámetro | Qué filtra |
|---|---|
text | búsqueda de texto libre |
entidad | entidad, coincidencia sin distinción de mayúsculas |
unspsc | código UNSPSC (usted pasa 43191500; el prefijo lo pone el sistema) |
fase | fase exacta del proceso |
minValue | precio base mínimo |
deadlineAfter / deadlineBefore | ventana de fecha límite de ofertas |
limit | 25 por defecto, máximo 200 |
offset | desplazamiento |
Devuelve count, limit, offset y hasMore. hasMore es una señal, no un conteo: significa
"la página vino llena, puede haber más".
get_process_detail — un proceso y sus contratos adjudicados. Acepta el identificador de
proceso (CO1.REQ.*, CO1.NTC.*) y también el de portafolio (CO1.BDOS.*), en cuyo caso se
resuelve el proceso vinculado.
Sobre documentos
list_process_documents — qué documentos publicó el proceso. No descarga nada: es la
llamada barata. Devuelve id, nombre, tipo y tamaño, y si ya hay una copia en Markdown disponible.
get_process_document — lee un documento.
| Parámetro | Qué hace |
|---|---|
document | el identificador del documento o el nombre exacto de archivo |
offset | dónde seguir leyendo; use el next_offset de la respuesta anterior |
max_chars | 50 000 por defecto, máximo 200 000 |
La respuesta incluye next_offset. Si viene en null, ya leyó todo. Un documento escaneado
vuelve marcado como possibly_scanned, nunca vacío: el original queda guardado y se le dice
que no hay capa de texto.
Sobre requisitos y verificación
get_process_requirements — los requisitos curados del proceso, con la cita de la página de la
que salió cada uno.
Tres respuestas distintas, y la diferencia importa:
| Respuesta | Qué significa |
|---|---|
found | El proceso tiene requisitos publicados. Vienen con su disclaimer. |
nothing_curated | El proceso existe, pero todavía no tiene requisitos. Es una respuesta exitosa, no un error: no hay nada que leer todavía. |
| error | El proceso no existe. |
validate_company_facts — verifica los datos de una propuesta contra esos requisitos.
Usted envía los hechos de la propuesta como un objeto con rutas JSON (RFC 6901). El motor los compara contra las reglas y devuelve un veredicto por requisito:
| Veredicto | Qué significa |
|---|---|
cumple | La regla corrió y el dato la satisfizo. |
no_cumple | La regla corrió y el dato no la satisfizo. |
no_verificable | La regla no se pudo decidir con lo que se recibió. |
requiere_criterio_humano | El requisito no es verificable por máquina tal como está escrito. |
check_document_checklist — verifica la lista de documentos exigidos.
Usted envía nombres de archivos, nunca el contenido. El motor compara el conjunto de palabras
de cada nombre con el conjunto de palabras del nombre curado. El veredicto solo puede ser
cumple o no_verificable: una lista incompleta nunca fabrica un rechazo, y nunca convierte
un cumple en un no_cumple.
El veredicto de esta revisión significa "cumple por nombre del archivo". Es evidencia de nombre, no de contenido: que el certificado exista y sea válido lo revisa una persona.
El folio
Las dos herramientas que escriben devuelven un folio: un resumen del contenido de la revisión. El mismo conjunto de requisitos, los mismos hechos y la misma versión del motor producen el mismo folio y los mismos veredictos.
El folio identifica la revisión, no la empresa. Un registro de verificación pertenece a la empresa que lo pidió y nadie más lo alcanza.
Paginación
search_processes pagina con limit (por defecto 25, máximo 200) y offset. Avance con
offset += limit mientras hasMore sea verdadero. No hay un total: hasMore es la única señal
de que puede haber más filas.
get_process_document pagina por caracteres: use offset con el next_offset de la respuesta
anterior hasta que venga null.
Errores que puede encontrar
| Situación | Respuesta |
|---|---|
| Falta la clave, o la clave no es válida | 401, con WWW-Authenticate: Bearer |
| La base de datos no está disponible al verificar la clave | 503 — no es un 401. El servidor no parece estar rechazando una clave buena. |
El Accept no trae los dos tipos | 406 |
| La ruta no existe | 404 |
| La herramienta falló | No es una excepción: la respuesta llega con isError: true y el motivo en el texto |
| Se alcanzó el límite diario de lecturas | isError: true con daily_cap_reached: ... |
Un error de herramienta casi nunca significa "el dato no existe". Significa que la herramienta no pudo responder, y el texto dice por qué.
Límites diarios (cuotas)
Las lecturas se miden por empresa y por día, en la zona horaria de Bogotá; el contador se reinicia a medianoche en Bogotá.
| Capacidad | Plan gratuito |
|---|---|
Lecturas de conjunto de requisitos (get_process_requirements) | 100 por día |
Ventanas de documento (get_process_document) | 50 por día |
Validaciones (validate_company_facts, check_document_checklist) | 0 por mes |
| Solicitudes de extracción por empresa | 1 por día |
Sólo existe el plan gratuito con los límites anteriores. Cuando se alcanza un límite,
la herramienta responde con daily_cap_reached y el contador se reinicia al día siguiente.
Límites que conviene conocer
Las fechas límite de ofertas solo existen para una parte de los procesos. Alrededor del 84% de los procesos de SECOP no publican fecha límite. Si usted filtra por ella, deje que el filtro se lleve esa parte.
Los valores
No Definido,No AdjudicadoyUNSPECIFIEDno son datos. El sistema los normaliza a "no publicado" y no los presenta como valores.hasMorees una señal, no un conteo. Significa "la página vino llena, puede haber más".Los documentos PDF y TXT se leen; los demás formatos se conservan sin extraer texto.
Los precios y las fechas se muestran tal como los publica la entidad. SECOP publica marcas de tiempo sin zona horaria y el sistema no les inventa una.
Continúe
Datos y privacidad — de dónde vienen los datos y qué se registra.
Qué no hacemos — los límites del producto.