Skip to main content
Glama
Carlos-ortiz23

Siigo MCP

README.md
# Siigo MCP

Servidor MCP (Model Context Protocol) remoto que conecta la API pública de Siigo Colombia con cualquier cliente MCP estándar — Claude, ChatGPT, u otros. Corre como un Cloudflare Worker sin estado, expone `28` herramientas (lectura, escritura y analítica de negocio) y nunca expone las credenciales de Siigo al modelo ni al cliente.

No hay lógica específica de Claude ni de ChatGPT: es un servidor MCP estándar sobre Streamable HTTP, servido en `/mcp`.

## Qué es este MCP

- Traduce llamadas MCP (`tools/call`) a llamadas REST autenticadas contra `api.siigo.com`.
- Cubre clientes, productos, facturas de venta, compras, cotizaciones, recibos de caja, comprobantes contables y un set de tools de inteligencia de negocio (resúmenes de ventas/compras/cartera/inventario/financiero).
- Las credenciales de Siigo (`SIIGO_USERNAME`, `SIIGO_ACCESS_KEY`) viven únicamente como Cloudflare Secrets; ninguna tool las devuelve, registra ni expone.
- Ver [`TOOLS.md`](./TOOLS.md) para la tabla completa de herramientas.

## Arquitectura

```
Cliente MCP (Claude / ChatGPT / otro)
        │  Streamable HTTP
        ▼
https://<tu-worker>.workers.dev/mcp
        │
        ▼
src/index.ts  →  src/server.ts  →  src/tools/*.ts  →  src/siigo/*.ts  →  src/siigo/client.ts
                                                                              │
                                                                              ▼
                                                                    Cloudflare Secrets
                                                                    (SIIGO_USERNAME, SIIGO_ACCESS_KEY)
                                                                              │
                                                                              ▼
                                                                       api.siigo.com
```

```
src/
├── index.ts               # Worker entrypoint: solo wiring (createMcpHandler)
├── server.ts               # construye el McpServer y registra todos los grupos de tools
├── siigo/
│   ├── auth.ts              # POST /auth + cache en memoria del access_token, renovación automática
│   ├── client.ts             # cliente HTTP centralizado: headers, query, JSON, errores
│   ├── types.ts              # tipos de dominio (Customer, Product, Invoice, Purchase, Quotation, Voucher, Journal)
│   ├── customers.ts          # GET/POST /v1/customers
│   ├── products.ts           # GET/POST /v1/products
│   ├── invoices.ts            # GET/POST /v1/invoices
│   ├── purchases.ts           # GET /v1/purchases
│   ├── quotations.ts          # GET/POST /v1/quotations
│   ├── receipts.ts            # GET/POST /v1/vouchers (recibos de caja)
│   └── accounting.ts          # GET/POST /v1/journals (comprobantes contables)
├── tools/
│   ├── customers.ts, products.ts, invoices.ts, purchases.ts,
│   ├── quotations.ts, receipts.ts, accounting.ts   # registro de tools MCP (Zod + descripciones)
│   └── analytics.ts           # tools de inteligencia de negocio (resúmenes)
└── utils/
    ├── pagination.ts          # schema de paginación + recorrido multi-página con tope
    └── errors.ts              # SiigoApiError + formateo seguro de errores para el LLM
```

Cada `siigo/*.ts` es una función pura contra la API (no sabe nada de MCP). Cada `tools/*.ts` traduce input MCP (validado con Zod) → llamada a `siigo/*.ts` → texto + `structuredContent`. `server.ts` es el único punto que ensambla todo.

## Instalación

```bash
git clone <este repo>
cd siigo-mcp
npm install
```

Requiere Node.js reciente (usado con Node 24) y una cuenta de Cloudflare con Wrangler autenticado (`npx wrangler login`).

## Desarrollo local

1. Crea un archivo `.env` (o `.dev.vars`, ambos están en `.gitignore`) con las credenciales de Siigo **solo para tu entorno local**:

   ```
   SIIGO_USERNAME=tu_usuario_de_siigo
   SIIGO_ACCESS_KEY=tu_access_key
   ```

2. Levanta el Worker localmente:

   ```bash
   npm run dev
   ```

3. El MCP queda disponible en `http://localhost:8787/mcp` (o el puerto que Wrangler asigne). Puedes probarlo con cualquier cliente MCP compatible con Streamable HTTP, o con `curl` directamente:

   ```bash
   curl -s -X POST http://localhost:8787/mcp \
     -H "Content-Type: application/json" \
     -H "Accept: application/json, text/event-stream" \
     -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
   ```

**Nunca** subas `.env`/`.dev.vars` al repositorio (ya están en `.gitignore`) ni pegues su contenido en chats, tickets o documentación compartida fuera de la cuenta corporativa aprobada de BBLABS.

## Variables y Secrets

| Nombre | Tipo | Dónde vive | Descripción |
|--------|------|------------|-------------|
| `SIIGO_USERNAME` | Secret | Cloudflare Secret / `.env` local | Usuario de la API de Siigo. Nunca se loguea ni se devuelve. |
| `SIIGO_ACCESS_KEY` | Secret | Cloudflare Secret / `.env` local | Access key de la API de Siigo. Nunca se loguea ni se devuelve. |
| `SIIGO_PARTNER_ID` | Variable pública | `wrangler.jsonc` (`vars`) | Identificador de la integración (`mcpclaude`). No es sensible, se envía en cada request a Siigo. |

Para configurar los secrets en producción (una sola vez, o cuando roten):

```bash
npx wrangler secret put SIIGO_USERNAME
npx wrangler secret put SIIGO_ACCESS_KEY
```

`SIIGO_PARTNER_ID` ya está declarado en `wrangler.jsonc` bajo `vars` y no requiere `wrangler secret put`.

## Despliegue

```bash
npm run typecheck   # tsc --noEmit
npm test            # vitest run
npm run deploy       # wrangler deploy
```

Tras el primer despliegue (o si cambias bindings/vars en `wrangler.jsonc`), regenera los tipos de Workers:

```bash
npm run cf-typegen   # wrangler types
```

El Worker queda publicado en `https://<nombre-del-worker>.<tu-subdominio>.workers.dev/mcp` (Streamable HTTP, endpoint `/mcp`).

## Herramientas disponibles

Ver la tabla completa en [`TOOLS.md`](./TOOLS.md). Resumen por dominio:

- **Clientes**: listar, buscar, obtener, crear.
- **Productos**: listar, buscar, obtener, crear.
- **Facturas de venta**: listar, obtener, crear.
- **Compras**: listar, obtener.
- **Cotizaciones**: listar, obtener, crear.
- **Recibos de caja**: listar, obtener, crear (ver nota de confiabilidad en `TOOLS.md`).
- **Comprobantes contables**: listar, obtener, crear.
- **Analítica**: resumen de ventas, resumen de compras, resumen de cartera, consulta de inventario, resumen financiero.
- **Diagnóstico**: `siigo_auth_test`.

## Ejemplos de uso

Una vez conectado el MCP a un cliente compatible, puedes pedirle en lenguaje natural, por ejemplo:

- *"Busca el cliente con NIT 900123456 en Siigo."* → usa `siigo_buscar_cliente`.
- *"Dame el resumen de ventas de enero 2026 comparado con diciembre 2025."* → usa `siigo_resumen_ventas` con `fecha_inicio_comparacion`/`fecha_fin_comparacion`.
- *"¿Qué productos tienen menos de 3 unidades disponibles?"* → usa `siigo_consultar_inventario` con `umbral_stock_bajo: 3`.
- *"Crea una cotización para el cliente 900123456 con 2 unidades del producto SKU-1 a 50000 cada una."* → usa `siigo_crear_cotizacion`. El modelo debe reunir explícitamente `document_id` (tipo de documento), fecha, identificación del cliente e items antes de ejecutar la tool.

Las tools de escritura (`siigo_crear_*`) están descritas para que el modelo nunca las ejecute con información ambigua o incompleta — Zod rechaza la llamada si falta un campo obligatorio de Siigo.

## Conexión con Claude

1. En Claude (Claude.ai, Claude Desktop o Claude Code), agrega un servidor MCP remoto apuntando a:
   ```
   https://<tu-worker>.workers.dev/mcp
   ```
2. No se requiere configuración de autenticación adicional del lado del cliente: las credenciales de Siigo están únicamente en el Worker (Cloudflare Secrets), nunca en el cliente MCP.
3. Claude descubrirá las 28 tools automáticamente vía `tools/list`.

## Conexión con ChatGPT

1. En la configuración de conectores/MCP de ChatGPT, agrega un conector remoto con la misma URL:
   ```
   https://<tu-worker>.workers.dev/mcp
   ```
2. El servidor usa Streamable HTTP estándar (sin sesión obligatoria, `legacy: "stateless"` por defecto en `agents/mcp/server`), compatible con la forma en que ChatGPT invoca servidores MCP remotos.
3. Igual que con Claude, no hay credenciales que configurar del lado de ChatGPT.

## Seguridad

- Las credenciales de Siigo (`SIIGO_USERNAME`, `SIIGO_ACCESS_KEY`) solo existen como Cloudflare Secrets (producción) o en `.env`/`.dev.vars` local (ambos en `.gitignore`); nunca se hardcodean ni se commitean.
- Ninguna tool devuelve `access_key`, `access_token`, `username` ni headers de autenticación. El `access_token` se cachea en memoria del Worker (por isolate, no persistente) y se renueva automáticamente antes de expirar; nunca sale de `src/siigo/auth.ts`.
- Los errores de Siigo se traducen con `src/utils/errors.ts` a mensajes tipo `"Siigo rechazó la solicitud. Código: <status>. Detalle: <detalle>"`, sin incluir secretos ni headers.
- Las tools de escritura son explícitas: exigen todos los campos obligatorios de Siigo (sin adivinar valores) y sus descripciones advierten que crean documentos reales e irreversibles. `siigo_crear_factura` nunca timbra ante la DIAN ni envía correo salvo que se indique `enviar_a_dian`/`enviar_por_email` como `true` explícitamente. `siigo_crear_comprobante_contable` valida que la partida cuadre (débito = crédito) antes de enviarla a Siigo.
- Al trabajar con datos reales de clientes (nombres, identificaciones, correos, teléfonos) obtenidos vía este MCP, sigue las políticas de BBLABS: usa solo la cuenta corporativa aprobada, minimiza los datos que compartes fuera del MCP, y anonimiza/resumes antes de pegar resultados en canales o documentos que no sean estrictamente necesarios.

## Troubleshooting

- **`Siigo rechazó la solicitud. Código: 401...`**: revisa que `SIIGO_USERNAME`/`SIIGO_ACCESS_KEY` estén correctamente configurados como secrets (`npx wrangler secret put ...`) y que el usuario de Siigo tenga permisos de API habilitados.
- **`Siigo rechazó la solicitud. Código: 404...` en `siigo_obtener_*`**: el ID (UUID) no existe o pertenece a otro tipo de documento. Usa primero la tool `siigo_listar_*`/`siigo_buscar_*` correspondiente.
- **`Siigo rechazó la solicitud. Código: 500/503/504...`**: en pruebas, Siigo devolvió ocasionalmente errores transitorios de servidor (p. ej. `document_query_service` no disponible); reintenta la operación. Para `siigo_listar_recibos_caja`/`siigo_obtener_recibo_caja`, un 500 puede indicar que ese endpoint de lectura simplemente no está soportado de forma confiable por la API pública (ver `TOOLS.md`).
- **El cliente MCP no ve las tools**: confirma que el endpoint termina en `/mcp` y que el cliente hace `initialize` antes de `tools/list` (algunos clientes lo hacen automáticamente).
- **Cambié `wrangler.jsonc` y los tipos no reflejan el cambio**: corre `npm run cf-typegen`.
- **Necesito confirmar un endpoint/parámetro de Siigo que no está en este README**: consulta la documentación oficial en `https://developers.siigo.com/docs/siigoapi/` antes de asumir el shape de un payload; varios recursos de esta integración (compras, recibos de caja) tienen partes no documentadas públicamente y están anotadas como tales en `TOOLS.md` y en los comentarios de `src/siigo/*.ts`.