Servidor MCP
Conecta tu agente LLM al servidor MCP remoto de Notta por HTTP, dos herramientas sobre un catálogo de 31 operaciones, con OAuth, techo por empresa y aprobación humana
Notta expone un servidor MCP (Model Context
Protocol) remoto en https://app.notta.cl/api/mcp. Es un solo servidor, por
HTTP streamable, con OAuth: no hay paquete que instalar ni API key que pegar en
un archivo de configuración.
El catálogo son 31 operaciones: veinticuatro de consulta y siete detrás de un
permiso de escritura: emitir documentos tributarios, dejar un documento
preparado como borrador para que alguien lo revise, acusar o reclamar las
facturas de tus proveedores ante el SII, destrabar un documento cuyo
seguimiento ante el SII se detuvo, reenviarle a tu cliente uno que ya
emitiste, y guardar o corregir una contraparte de tu catálogo. Esos permisos
vienen apagados de fábrica: los
enciende tu empresa en /app/mcp y quien conecta la app tiene que marcar su
casilla al autorizar.
Las siete no se gobiernan igual, y la diferencia importa. Un documento que emite
tu agente sale directo si está bajo el umbral que fijes; un acuse o un reclamo
lo apruebas siempre, sin umbral y sin excepción, porque no se deshace: dado un
acuse ya no se puede reclamar, y una factura aceptada que tu proveedor cede a
factoring no se corrige con una nota de crédito. Destrabar un documento no pasa
por ningún umbral ni por aprobación: no crea ni cambia nada, solo vuelve a
preguntarle el estado al Servicio. Preparar un borrador tampoco: crea una fila
en tus borradores y nada más, sin folio, sin llegar al SII y sin avisarle a
nadie, y después sigue haciendo falta que alguien lo emita. Reenviar tampoco:
manda otra vez, al mismo destino que Notta ya resolvió, un documento que ya
existe. Esas tres comparten el permiso dte:write con la emisión, así que hoy no
se pueden conceder por separado.
Mantener el catálogo de contrapartes es la excepción del grupo: tiene permiso
propio (empresas:write), no sale de Notta y no crea ningún documento. Sirve
para que el agente que te acaba de facturar a un cliente nuevo lo GUARDE, en vez
de que se lo dictes otra vez la próxima. Y llega hasta ahí: por este canal no
puede tocar el correo al que le llegan los documentos de esa contraparte (ese
correo decide a dónde va cada factura que le emitas) ni borrarla del catálogo.
Las dos cosas se hacen desde /app/empresas.
Conéctalo
Con Claude Code:
En claude.ai: Ajustes → Conectores → Agregar conector personalizado, y pega esa misma URL. En ChatGPT, Ajustes → Conectores en modo desarrollador. Cualquier otro cliente con HTTP streamable y OAuth (Cursor, Gemini CLI) usa la URL tal cual.
Al conectar por primera vez, tu agente pide autorización OAuth, y lo que esa
conexión se lleva lo decides tú en la pantalla de consentimiento. Puede leer:
tus documentos (DTE) y su estado, y generar un enlace de 15 minutos a su PDF
o a su XML firmado (sin sesión, para reenviarlo o cederlo); en qué quedaron las emisiones que dejaste
esperando aprobación (si se aprobaron, si se rechazaron y por qué, o si
vencieron);
el historial de tus documentos de venta recientes, para prellenar uno nuevo;
tus folios (CAF); tu Registro de Compras y Ventas (RCV); tu bandeja de compras
por Casilla de Intercambio con sus plazos; el perfil de tu empresa y sus sucursales del SII, tu estado de certificación ante el SII y tu
catálogo de contrapartes; el inventario de tus certificados digitales (nunca la
clave de firma); y tu plan y cuota de uso. La escritura va aparte, en casillas
que marcas tú y que solo aparecen si tu empresa la habilitó: emitir documentos
tributarios a tu nombre (facturas de venta y de compra, notas de crédito y de
débito, guías de despacho y facturas de exportación); sobre el umbral que fije
tu empresa, cada documento espera la aprobación de una persona. Y acusar recibo o reclamar las facturas de tus
proveedores ante el SII; cada acción la aprueba una persona antes de
registrarse, porque son irreversibles y excluyentes entre sí. La casilla de
emisión concede además destrabar un documento cuyo seguimiento ante el SII se
detuvo: Notta vuelve a preguntarle el estado al Servicio. No crea documentos ni
cambia ninguno, y no espera aprobación porque no hay nada que aprobar. Y
preparar documentos y dejarlos como borrador para que los revises antes de
emitir; un borrador no tiene folio, no llega al SII y no le avisa a nadie, y
sigue haciendo falta que alguien lo emita.
Y reenviarle a tu cliente un documento que ya emitiste, al correo que Notta ya
tiene resuelto para él; no crea ningún documento, no cambia el que se manda, y
por este canal no se puede elegir otra dirección.
En su propia casilla, aparte de la de emisión, va
crear y editar las contrapartes de tu catálogo (razón social, giro, dirección,
comuna y si es cliente o proveedor), para que tu agente guarde al cliente nuevo
en vez de que se lo dictes otra vez; no puede tocar el correo al que le llegan
los documentos de esa contraparte ni borrarla del catálogo.
Administra o revoca las apps conectadas desde /app/mcp en el dashboard.
La pantalla de consentimiento te dice qué app pide acceso, a qué empresa y a qué dominio van los datos (salen de Notta hacia el proveedor de esa app, que puede procesarlos fuera de Chile), y marca como no verificada a cualquier app que no pueda comprobar. Si administras varias empresas, ahí mismo eliges a cuál conectar la app (una conexión por empresa), sin cancelar ni volver a empezar. Solo se ofrecen las empresas en las que tienes membresía.
Autorización
El servidor implementa OAuth 2.1 con PKCE (S256 obligatorio) y registro
dinámico de clientes. Tu cliente MCP descubre los endpoints por metadata; no
hay que copiarlos a mano:
| Metadata | URL |
|---|---|
| Recurso protegido (RFC 9728) | https://app.notta.cl/.well-known/oauth-protected-resource |
| Servidor de autorización (RFC 8414) | https://app.notta.cl/.well-known/oauth-authorization-server |
| Manifiesto MCP | https://notta.cl/.well-known/mcp.json |
Una solicitud de autorización que pida un permiso que no se puede conceder no se
rechaza: se recorta. El grant sale con los permisos que sí correspondían y la
respuesta del token declara cuáles son (RFC 6749 §3.3), así que tu cliente
siempre sabe con qué se quedó. scope=read sigue siendo válido y significa toda
la lectura. El token queda atado a una empresa y al ambiente SII de esa empresa
(cert o prod): ningún parámetro del cliente lo cambia.
Los permisos efectivos de cada llamada son la intersección de tres cosas (lo que
la app pidió, el techo de la empresa y lo que permite tu rol) y se recalculan en
cada petición: bajar el techo en /app/mcp corta la escritura de todas las apps
conectadas en la petición siguiente, sin esperar a que expire ningún token. El
día que el SII autoriza a tu empresa a emitir en producción, las conexiones MCP
de esa empresa se revocan y el techo vuelve a apagarse: hay que reconectar y
encenderlo de nuevo, a propósito, sabiendo que ahora los documentos tienen
validez tributaria.
El manifiesto es público y sin token: publica el catálogo completo. Lo que un
token concreto puede ejecutar depende del permiso que le diste al autorizar. Si
tu agente no habla MCP, el corpus de estas docs está en https://notta.cl/llms.txt
y /llms-full.txt.
Las dos herramientas
El servidor no expone una tool por endpoint: expone dos, sobre un catálogo de operaciones que se descubre en caliente.
| Tool | Argumentos | Qué hace |
|---|---|---|
discover | { operation? } | Sin argumentos, devuelve a qué empresa y ambiente SII está conectado tu agente, qué puede y qué no puede hacer (con el porqué y el cómo habilitarlo) y el catálogo de operaciones que tu conexión alcanza. Con operation, el esquema completo de parámetros de esa operación y un ejemplo de llamada. |
execute | { operation, params?, body? } | Ejecuta una operación del catálogo. params lleva los parámetros de ruta, los filtros y los headers que la operación declara (Idempotency-Key, X-Cert-Id); body, el cuerpo JSON de las que escriben. Todo se valida contra el esquema OpenAPI: una clave no declarada nunca llega a la API. |
Lo que limita a execute es el catálogo, no el verbo: una operación que no está
en él no viaja, y el guardia de /api/v1 la vuelve a comparar contra el mismo
catálogo con tu token. Emitir se ve así:
La Idempotency-Key la pone tu agente, no el servidor. Genera una por
documento y reutiliza EXACTAMENTE ese valor si tienes que reintentar: una clave
nueva en el reintento no es un reintento, es un segundo documento real ante el
SII. discover({ operation: "emitDte" }) publica el esquema completo del cuerpo
y de los headers que la operación exige.
También puedes llamar POST /api/v1/dtes directo con el token de la conexión:
recibe el mismo guardia, el mismo techo y el mismo umbral. Y si prefieres no
encender la escritura por MCP, la API REST con una
API key sigue siendo el camino: mismo shape, misma
cuenta, pero sin pasar por el techo ni por el panel de aprobación.
Catálogo de operaciones
| Operación | Endpoint | Qué devuelve |
|---|---|---|
listDtes | GET /dtes | Tus DTEs, con limit (máx. 100), sort (folio, monto_total, fecha_emision, sii_status, created_at) y dir |
emitDte | POST /dtes | La única operación del catálogo que crea un documento: emite una factura, nota de crédito, nota de débito o guía de despacho. Exige el permiso dte:write y pasa por el gate de abajo |
previewDte | POST /dtes/preview | La vista previa antes de emitir: guarda un borrador y devuelve su enlace (/app/dtes/drafts/{id}, con el PDF y el botón para emitirlo) más los montos que calcularía el motor. No firma, no pide folio, no llega al SII y no manda correo. Valida con el MISMO schema que emitDte, así que un 200 aquí significa que la emisión no va a fallar por el payload. Exige dte:write porque crea la fila del borrador |
getDte | GET /dtes/{id} | El detalle de un DTE con sus líneas |
getDteEvents | GET /dtes/{id}/events | La historia de estados SII de ese DTE (queued → sending → EPR/RPR/RFR/RCT/RSC). Este es el endpoint para seguir un documento, no el de abajo |
refreshDteStatus | POST /dtes/{id}/refresh-status | La escotilla para un documento sin veredicto confirmado (stuck, sin_permiso_sii, o un reloj frío de más de 25 h): vuelve a consultarle el estado al SII si tiene Track ID. Sin Track ID responde 409 dte.refresh_status.no_track_id, con next_action: operator_review; revisa sii_glosa y contacta a soporte. Exige el permiso dte:write, el mismo que emitir. Tres respuestas 2xx, y solo una actúa: refresh_enqueued (202). already_terminal (200) significa que el SII ya juzgó el documento (puede ser un rechazo: mira sii_glosa) y poll_in_progress (200) que el seguimiento sigue vivo y trae retry_after en segundos. Llamarlo en bucle no acelera nada |
resendDelivery | POST /dtes/{id}/resend-delivery | Reenvía al receptor un documento ya emitido: no crea nada, no pide folio y no toca al SII. Exige dte:write, el mismo permiso que emitir. Por MCP no admite cuerpo, así que el destino lo resuelve Notta (catálogo de contrapartes y casilla de intercambio del receptor) y el agente no puede elegir otra dirección; con una API key clásica sí se puede indicar correo_receptor. El 202 dice que el envío quedó encolado, no que el correo llegó |
getDtePdfUrl | GET /dtes/{id}/pdf-url | Una URL firmada (vence en 15 minutos) para descargar el PDF sin sesión, para reenviarla a tu cliente |
getDteXmlUrl | GET /dtes/{id}/xml-url | Una URL firmada (vence en 15 minutos) para descargar el XML firmado sin sesión, para reenviarlo o cederlo |
getDteTotals | GET /dtes/totales | Conteo y suma (neto/exento/IVA/total) por período, tipo y estado: agregado en SQL, sin el detalle de cada documento |
getPendingAction | GET /dtes/pendientes/{id} | En qué quedó una emisión que el gate dejó esperando: pending, approved (con el DTE resultante), rejected (con el motivo que escribió la persona) o expired |
listSiiRecentDocuments | GET /sii/documents/recent | Tus últimas ventas (33/34) emitidas, para prellenar un documento nuevo |
getSiiDocumentTemplate | GET /sii/documents/{id}/template | El detalle de un documento anterior, como plantilla para uno nuevo |
listCaf | GET /caf | Tus CAF (folios), con folios restantes y aviso de folios bajos |
listRcv | GET /rcv | Tu Registro de Compras y Ventas por período, o el detalle de un RUT |
listRcvDocuments | GET /rcv/documents | El mismo registro documento por documento, con el evento del receptor y la referencia de cada NC/ND |
listRecepcion | GET /recepcion | Tu bandeja de compras por Casilla de Intercambio, con el plazo de 8 días corridos para acusar o reclamar ya calculado por fila |
registrarEventoRecepcion | POST /recepcion/{id}/evento | La operación que declara algo ante el SII a tu nombre: acusa recibo o reclama una factura de proveedor. Exige el permiso recepcion:write y siempre queda esperando aprobación humana, sin umbral |
getProfile | GET /profile | El perfil de tu empresa: giro, dirección, comuna, actividad económica y la Unidad del SII que se imprime en el PDF |
listSucursales | GET /sucursales | Las sucursales del SII de tu empresa, con el id que se manda como sucursal_id al emitir desde una de ellas. Solo lectura: registrarlas, editarlas o borrarlas no se puede por MCP, porque el código lo entrega el SII y lo copia una persona desde Mi SII |
getSiiCertState | GET /sii/cert-state | El estado de tu proceso de certificación ante el SII |
getSiiReadiness | GET /sii/readiness | El resumen de aplicabilidad y estado de tu certificación SII |
getOnboardingReadiness | GET /onboarding/readiness | Qué le falta a tu empresa para poder emitir |
getSuggestedDteTypes | GET /onboarding/suggested-types | Los tipos de DTE sugeridos según tu postulación ante el SII |
getTiposAutorizados | GET /sii/tipos-autorizados | Qué tipos de documento tiene habilitados tu empresa, uno por uno |
listEmpresas | GET /sii/empresas | Tu catálogo de contrapartes |
getEmpresa | GET /sii/empresas/{id} | La ficha completa de una contraparte |
createEmpresa | POST /sii/empresas | Guarda un cliente o proveedor nuevo en tu catálogo, para no volver a dictar sus datos. Exige empresas:write, un permiso propio (no el de emitir). El RUT es la identidad de la fila y no se puede editar después; si ya existe, la respuesta es 409 y lo que corresponde es editarla. El campo correos no viaja por este canal: decide a qué dirección le llega cada documento que le emitas a esa contraparte, así que mandarlo responde 422 empresa.correos_no_editable_por_mcp y no crea nada. Se edita en /app/empresas, o con una API key clásica |
updateEmpresa | PUT /sii/empresas/{id} | Corrige los datos de una contraparte que ya está en tu catálogo. El cuerpo es completo, no un parche: lo que omitas queda vacío. El RUT no se toca. Mismo trato para correos que en createEmpresa, con una diferencia: por MCP omitirlo tampoco los borra, quedan como estaban |
listCertificates | GET /certificates | Tus certificados digitales vigentes (nunca la clave de firma) |
getBillingPlan | GET /billing/plan | Plan activo y cuota del mes |
Son 31 operaciones. Para leer un DTE por id, primero lista con listDtes y toma
el id de la fila: el mismo usage_hint que devuelve discover, que
también trae la regla de corte de más abajo.
Para seguir un documento recién emitido, el camino es getDteEvents (o
listDtes filtrando por estado), no refreshDteStatus: la emisión publica un
seguimiento durable que lleva el documento a su estado final por su cuenta.
refreshDteStatus existe para cuando ese seguimiento se murió, y si está vivo
responde poll_in_progress sin consultarle nada al SII. Notta tiene además
webhooks salientes (/docs/webhooks), pero sus cinco endpoints
están fuera de este catálogo a propósito: registrar una URL de callback
redirige el flujo de documentos de la empresa hacia un tercero, y esa es una
decisión de infraestructura de la persona que integra, no del agente que consume.
Un agente que necesita el estado lo tiene completo por pull. Corta cualquier bucle de espera por estado.terminal, NUNCA por sii_status === "EPR": un rechazo (RFR, RCT, RSC y RCH) también es final, y ese bucle no saldría nunca. stuck, sin_permiso_sii, emision_fallida y envio_repetido no son terminales (no hay un veredicto confirmado en Notta) pero tampoco avanzan solos: stuck y sin_permiso_sii los destraba POST /dtes/{id}/refresh-status si tienen Track ID; sin él, revisa sii_glosa y el documento en el SII, y contacta a soporte; emision_fallida y envio_repetido los resuelve soporte: no tienen Track ID, así que no hay nada que consultar.
Emitir por MCP: el techo, el umbral y la aprobación
Emitir es la escritura que más gobierno necesita del catálogo (es la que crea un documento con valor tributario), y ninguna empresa la tiene encendida sin decidirlo. Tienen que darse las tres cosas a la vez:
- El techo de tu empresa, arriba. Un
owneroadminenciende «Escritura por MCP» en/app/mcp. Viene apagado; con el techo abajo la casilla ni siquiera aparece al autorizar, y bajarlo corta la escritura de todas las apps conectadas en la petición siguiente. - El permiso, marcado al conectar. Quien autoriza la app marca la casilla de emisión en la pantalla de consentimiento. Sin marcarla, la conexión queda de solo lectura.
- El rol de quien autorizó. Un
viewerno otorga emisión ni marcando la casilla, y degradar a esa persona corta la escritura de su app.
Con eso puesto, cada emisión pasa por el gate:
| Situación | Qué pasa |
|---|---|
| Ambiente de certificación | Emite directo: los documentos son de prueba |
| Producción, monto ≤ tu umbral | Emite directo |
| Producción, monto > tu umbral | Queda esperando aprobación humana |
| Tu empresa pidió revisar todo | Queda esperando aprobación humana, siempre |
| Documento sin monto comparable (guía de traslado interno) | Emite directo |
El umbral se guarda en UF (no en pesos, para que no proteja cada año un poco
menos sin que nadie lo decida) y se compara con el valor del día. Por defecto son
60 UF y un tope de 60 documentos por minuto, ambos configurables en /app/mcp.
Cuando un documento queda esperando, la respuesta trae status: "pending", el
pending_id, un approve_url que apunta a /app/mcp/pendientes y el motivo en
español. Ese documento no quema folio ni llega al SII: espera hasta 24 horas
a que una persona con permiso para emitir lo apruebe o lo rechace desde el
dashboard, y vence solo si nadie lo mira. Aprobar dos veces no duplica el
documento. Tu agente consulta en qué terminó con getPendingAction: si lo
rechazaron, ahí está el motivo que escribió la persona, para corregir y volver a
pedirlo.
Lo que un agente todavía no hace por este canal
Por MCP no hay ruta para anular un documento, subir un certificado, pedir o subir folios, ni sincronizar el RCV: leerlos sí, con las operaciones de arriba. La boleta electrónica (39/41) y la boleta de honorarios no están en el catálogo mientras sigan pendientes de certificación ante el SII.
Próximos pasos
- Agentes: patrones para que tu agente emita con confirmación humana y maneje la emisión async.
- Emitir Factura 33: el shape completo del request de emisión y los estados SII.
- Autenticación: API keys, scopes y ambientes cert/prod.
- Referencia de la API: los endpoints que el catálogo del MCP refleja.
Última actualización