Documentación
Servidor MCP
Bloques incluye un servidor MCP (Model Context Protocol): el estándar abierto con el que asistentes y agentes IA — Claude, Claude Code, y cualquier cliente MCP — usan herramientas externas. Conectándolo, tu agente puede emitir boletas y facturas, consultar documentos, gestionar el catálogo y revisar tu consumo, conversando: "emite una boleta de S/ 118 por consultoría a CLIENTES VARIOS".
Endpoint y protocolo
| Propiedad | Valor |
|---|---|
| URL | POST https://<tu-host>/api/mcp |
| Transporte | Streamable HTTP, stateless: cada POST es una petición JSON-RPC independiente; no hay sesiones ni SSE (GET responde 405). |
| Versiones de protocolo | 2025-06-18, 2025-03-26, 2024-11-05. |
| Autenticación | OAuth 2.1 (spec de autorización de MCP, 2025-06-18): tu cliente descubre el servidor, se registra solo, te abre el navegador para iniciar sesión y autorizar, y renueva su acceso sin volver a molestarte. No hay ningún secreto que copiar. Sin autorización válida no se revela nada del servidor (401). |
| Capacidades | Solo tools (sin resources ni prompts en v1). |
Cómo conectarlo
En todos los clientes es lo mismo: pega la URL. El cliente recibe un 401 con la dirección de la metadata, se registra solo, te abre el navegador y ahí inicias sesión en Bloques y apruebas lo que el agente podrá hacer. Nada de tokens en archivos de configuración.
Claude Code
claude mcp add --transport http bloques https://<tu-host>/api/mcpLa primera vez que lo uses se abrirá el navegador para autorizar. Verifica con claude mcp list.
claude.ai (conector personalizado)
En claude.ai ve a Settings → Connectors → Add custom connector y registra la URL https://<tu-host>/api/mcp. Claude hace el flujo OAuth solo: no necesitas configurar cabeceras ni pegar ningún secreto.
Otros clientes MCP (config JSON genérica)
{
"mcpServers": {
"bloques": {
"type": "http",
"url": "https://<tu-host>/api/mcp"
}
}
}Cualquier cliente que soporte transporte http (Streamable HTTP) con OAuth funciona. Los documentos de descubrimiento están donde manda la spec: /.well-known/oauth-protected-resource y /.well-known/oauth-authorization-server.
Tokens sk_live_… (método anterior, en retiro)
Los tokens de API siguen aceptándose en /api/mcp durante la transición, con la cabecera Authorization: Bearer sk_live_…. Se retiran del MCP en una versión próxima: reconecta tus agentes por OAuth. En la API REST no cambia nada — ahí los tokens siguen siendo el método.
Herramientas
Cada conexión solo ve las herramientas que sus permisos alcanzan — un agente de solo lectura ni siquiera sabe que create_document existe — y, cuando la herramienta pertenece a un módulo (como create_expense, del módulo Finanzas), solo si tu empresa tiene ese módulo activo. La lista que ve tu agente es, por eso, la intersección de las dos cosas.
create_documentdocuments:writestatus: "processing", SUNAT todavía no contestó: se consulta con get_document en unos segundos, nunca se vuelve a emitir (crearía un segundo comprobante). factura exige cliente con RUC; boleta es para consumidores (DNI, u omite el cliente para CLIENTES VARIOS). Los precios son finales con IGV incluido. Consume el límite mensual del plan. Es una emisión fiscal real: el agente debe confirmar con el usuario antes de llamarla.| Argumento | Requerido | Descripción |
|---|---|---|
type | Sí | "factura" o "boleta". |
series | No | Serie como F001/B001 (por defecto la activa). |
issue_date | No | YYYY-MM-DD, por defecto hoy (Lima). Máx. 7 días atrás. |
currency | No | PEN (por defecto) o USD. |
customer | Factura: sí | Objeto con id (cliente existente) o doc_type ("6" RUC, "1" DNI, "4" CE, "7" pasaporte, "0" ninguno), doc_number, name, email, address. |
items | Sí | 1–100 ítems: product_id o code (catálogo), o description + unit_price (línea libre); más quantity, unit_code, affectation (10/20/30). |
payment | No | { "type": "contado" | "credito", "installments": [{ "amount", "due_date" }] } — las cuotas deben sumar el total. |
notes | No | Texto libre impreso en el PDF. |
send_email | No | Enviar PDF + XML al cliente (por defecto, configuración de la empresa). |
idempotency_key | Recomendado | Clave de reintento seguro: repetirla devuelve el documento original en vez de emitir dos veces. |
get_documentdocuments:readF001-42.| Argumento | Requerido | Descripción |
|---|---|---|
id_or_number | Sí | UUID del documento o número completo (serie-correlativo). |
list_documentsdocuments:read| Argumento | Requerido | Descripción |
|---|---|---|
from | No | Fecha de emisión mínima (YYYY-MM-DD). |
to | No | Fecha de emisión máxima (YYYY-MM-DD). |
type | No | factura | boleta. |
status | No | accepted | rejected | error | processing. |
q | No | Búsqueda por número, nombre o documento del cliente. |
page | No | Por defecto 1. |
per_page | No | Por defecto 25, máx. 100. |
get_document_filesdocuments:readsk_live_… (Configuración → API), no con la autorización OAuth del MCP.| Argumento | Requerido | Descripción |
|---|---|---|
id | Sí | UUID del documento. |
list_productsproducts:read| Argumento | Requerido | Descripción |
|---|---|---|
q | No | Búsqueda por nombre o código. |
page | No | Por defecto 1 (100 por página). |
create_productproducts:writeunit_price es el precio FINAL (IGV incluido cuando la afectación es 10 gravado).| Argumento | Requerido | Descripción |
|---|---|---|
code | Sí | Código único por empresa. |
name | Sí | Nombre del producto. |
description | No | Descripción opcional. |
unit_code | No | Por defecto NIU. |
unit_price | Sí | Precio final, mayor a 0. |
currency | No | PEN (por defecto) | USD. |
affectation | No | "10" (por defecto) | "20" | "30". |
pool_tiers | No | Cuenta global de escalones: con true, la cantidad de este producto se suma con la de los demás productos marcados del documento para alcanzar sus escalones (7 shorts + 5 polos = 12 prendas), cada uno con su propia tabla de precios. Por defecto false. |
search_customerscustomers:read| Argumento | Requerido | Descripción |
|---|---|---|
q | No | Texto de búsqueda; sin él lista los primeros clientes. |
create_expenseexpenses:write · módulo Finanzastotal es lo que dice el papel y es el único monto obligatorio: Bloques sugiere el desglose con la tasa de IGV efectiva de tu empresa, salvo que envíes amounts — y entonces los seis componentes deben sumar el total exacto.| Argumento | Requerido | Descripción |
|---|---|---|
issue_date | Sí | YYYY-MM-DD, la fecha del comprobante del proveedor. |
total | Sí | Total del documento, IGV incluido. Acepta string ("118.00", exacto) o número. |
doc_type | No | 01 factura (por defecto) · 02 recibo por honorarios · 03 boleta · 04 liquidación de compra · 07/08 nota de crédito/débito recibida · 10 arrendamiento · 12 ticket · 13 banco/seguro · 14 servicios públicos · 00 sin comprobante. Fija el valor por defecto de credito_fiscal. |
supplier_id / supplier | No | El UUID de un proveedor del directorio, o uno tipeado (doc_type, doc_number, name). Sin ninguno: PROVEEDOR VARIOS. El tipeado entra al directorio y queda enlazado al gasto —salvo doc_type 0, sin documento—; si su ficha ya existe, no se pisa. |
description | No | Nombre corto del gasto — en qué se gastó, en pocas palabras («Compra de gaseosas para la tienda»). Máx. 200 caracteres. Se muestra en la lista de gastos y entra a la búsqueda. Es distinto de notes, la nota interna larga, y no tiene ningún efecto fiscal. |
series / number | No | Serie y correlativo tal como vienen impresos (texto libre). |
currency / exchange_rate | No | PEN (por defecto) o USD; en USD el tipo de cambio es obligatorio. |
amounts | No | Desglose copiado del documento: base_gravada, igv, base_exonerada, base_inafecta, isc, otros_tributos. Debe sumar el total. |
detraction | No | { code, rate, amount, constancy, date } — la tasa es fracción (0.12 = 12 %) y se guarda tal como se envía (es un snapshot de la constancia). |
category_id / branch_id / cash_register_id | No | UUIDs de la categoría, la sucursal y la caja a las que se imputa. |
due_date / payment_terms | No | Vencimiento y contado | credito (cuentas por pagar). |
items | No | Líneas opcionales que reparten el total (por categoría o producto), no la fuente de los impuestos: si las envías, deben sumar el total. |
deductible / is_fixed_asset / igv_destination / credito_fiscal / notes | No | Marcas contables del gasto. |
list_crm_stagescrm:read · módulo CRMmove_crm_client. Las etapas se configuran dentro de Bloques (Configuración → Etapas del CRM): por MCP no se crean, ni se renombran, ni se reordenan, ni se borran.Sin argumentos.
get_crm_pipelinecrm:read · módulo CRMstage: null — los que están en tu cartera pero no en el embudo. Esa columna es la ausencia de etapa, no una etapa: el cliente que nace de una emisión aparece ahí solo, y sacar a alguien del embudo lo devuelve ahí. Cada columna trae su total real y hasta 50 clientes. Las tarjetas llevan solo nombre y apodo: leer el embudo no es leer el directorio (para el teléfono o el documento está search_customers, que pide customers:read).Sin argumentos.
move_crm_clientcrm:write · módulo CRMstage_id: null. Sacarlo borra su lugar en el tablero: no borra al cliente ni su bitácora. Moverlo a la etapa en la que ya está no hace nada (y no le reinicia la antigüedad).| Argumento | Requerido | Descripción |
|---|---|---|
customer_id | Sí | UUID de un cliente de tu empresa. |
stage_id | Sí | UUID de una etapa tuya (ver list_crm_stages), o null para sacarlo del embudo. |
list_crm_interactionscrm:read · módulo CRMtotal real. Por defecto las vivas; con archived: true, las archivadas — dos conjuntos disjuntos, y archived dice siempre cuántas hay guardadas.| Argumento | Requerido | Descripción |
|---|---|---|
customer_id | Sí | UUID de un cliente de tu empresa. |
archived | No | Con true devuelve las archivadas en vez de las vivas. |
log_crm_interactioncrm:write · módulo CRM · solo OAuthsk_live_… es de la empresa, no de alguien, y una anotación sin autor no se distinguiría de una cuyo autor fue eliminado. No se edita ni se borra: corregir es anotar de nuevo y archivar la vieja.| Argumento | Requerido | Descripción |
|---|---|---|
customer_id | Sí | UUID de un cliente de tu empresa. |
kind | Sí | Canal, no estado: call · whatsapp · meeting · email · visit · note. |
occurred_on | Sí | YYYY-MM-DD, el día en que pasó. Hacia atrás sí; el futuro no. |
note | Sí | Qué pasó, en tus palabras. Máx. 1000 caracteres. |
archive_crm_interactioncrm:write · módulo CRMarchived: false). La bitácora nunca borra: la archivada sale de la lista y se sigue leyendo con list_crm_interactions({ archived: true }). Archivar dos veces no mueve la fecha de archivo.| Argumento | Requerido | Descripción |
|---|---|---|
interaction_id | Sí | UUID de una anotación de tu bitácora. |
archived | Sí | true archiva; false la devuelve. |
get_usagesin scope (cualquier conexión)Sin argumentos.
Modelo de seguridad
- El agente nunca puede más que tú. La conexión se hace con tu cuenta: lo que se ejecuta es lo que aprobaste y tu rol permite, comprobado en cada llamada. Si mañana te quitan un permiso, tu agente lo pierde al instante; si te sacan del equipo, deja de tener acceso. No hay que revocar nada.
- Autorizas en pantalla, y ves qué autorizas. Antes de conectar, Bloques enumera lo que el agente podrá hacer — y lo que no, porque tu rol no lo permite. Sin ese paso no se emite ningún acceso.
- Acceso corto y renovación de un solo uso. El acceso del agente dura una hora y se renueva solo; cada renovación invalida la credencial anterior, así que una copia robada deja de servir apenas el agente legítimo se renueva.
- Los permisos filtran las herramientas visibles.
tools/listsolo devuelve lo que la conexión puede ejecutar; la superficie no autorizada permanece oculta (no solo bloqueada). - Los módulos filtran igual que los scopes. Una herramienta de un módulo que tu empresa no tiene activo no se lista, y llamarla igual falla — el servidor lo vuelve a verificar al ejecutar.
- Gastos y dinero: escritura sin lectura.
create_expenseregistra, pero ninguna herramienta lee gastos, saldos ni movimientos de dinero. Es deliberado: un agente puede anotar lo que se gastó sin poder consultar cuánta plata tienes ni en qué se va. Esa información se ve únicamente iniciando sesión en la app. - Aislamiento por empresa. Toda herramienta opera únicamente sobre los datos de la empresa a la que perteneces. No existe forma de cruzar tenants.
- Auditoría de cada llamada. Toda ejecución de herramienta queda registrada en la bitácora de eventos de la empresa (
mcp.tool_call), con el usuario (o el token) como actor. Las emisiones registran además de dónde vinieron. - Sin estado, sin divulgación anónima. El servidor no responde nada (ni siquiera su nombre) sin autorización válida, y no mantiene sesiones que secuestrar.
Si un agente se comporta mal, desconéctalo
En Configuración → API y MCP ves los agentes que conectaste y cortas el acceso de cualquiera con un clic: sus tokens dejan de valer en el acto y la próxima vez tendrá que volver a pedirte autorización. Y si quieres que un agente pueda menos, la palanca es tu rol —o un rol dedicado— porque la conexión nunca excede lo que su dueño puede hacer.
¿Vas a programar la integración tú mismo (o tu agente)? Sigue con la guía para agentes IA o apunta directamente a /llms-full.txt.