Metadata-Version: 2.4
Name: facturama-mcp
Version: 1.0.0rc2
Summary: Servidor MCP para Facturama — facturación electrónica CFDI 4.0 en México
Project-URL: Homepage, https://facturama.mx
Project-URL: Repository, https://github.com/facturamamx/mcp-facturacion
Project-URL: Documentation, https://github.com/facturamamx/mcp-facturacion#readme
Project-URL: Changelog, https://github.com/facturamamx/mcp-facturacion/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/facturamamx/mcp-facturacion/issues
Author-email: Facturama <soporte@facturama.mx>
License-Expression: MIT
License-File: LICENSE
Keywords: cfdi,facturacion,facturama,mcp,mexico,model-context-protocol,sat
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Natural Language :: Spanish
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial :: Accounting
Requires-Python: >=3.11
Requires-Dist: fastmcp>=3.0
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.5
Requires-Dist: python-dotenv>=1.0
Provides-Extra: dev
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=4.1; extra == 'dev'
Requires-Dist: pytest>=7.4; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

<!-- mcp-name: mx.facturama/facturacion -->

# 🧾 Facturama MCP Server

> Servidor MCP (Model Context Protocol) basado en **FastMCP 3.x** que expone la API de [Facturama](https://facturama.mx) como herramientas para agentes LLM (Claude Desktop, Claude Code, Cursor, y cualquier cliente MCP). Cubre toda la operación de facturación electrónica CFDI 4.0 en México.

[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![FastMCP 3.x](https://img.shields.io/badge/FastMCP-3.x-purple.svg)](https://gofastmcp.com)
[![CFDI 4.0](https://img.shields.io/badge/CFDI-4.0-green.svg)](https://www.sat.gob.mx)
[![Status](https://img.shields.io/badge/status-beta-orange.svg)]()

---

## Tabla de contenidos

- [¿Qué hace?](#qué-hace)
- [Características](#características)
- [Herramientas disponibles](#herramientas-disponibles-27-tools)
- [Instalación](#instalación)
- [Configuración](#configuración)
- [Uso](#uso)
  - [Claude Desktop](#claude-desktop)
  - [Claude Code](#claude-code)
  - [Cursor](#cursor)
  - [VS Code](#vs-code)
  - [n8n, Make y Zapier](#n8n-make-y-zapier)
- [Ejemplos de uso](#ejemplos-de-uso)
- [Particularidades de la API de Facturama](#particularidades-de-la-api-de-facturama)
- [Estructura del proyecto](#estructura-del-proyecto)
- [Desarrollo](#desarrollo)
- [Despliegue](#despliegue)
- [Troubleshooting](#troubleshooting)
- [Roadmap](#roadmap)
- [Stack técnico](#stack-técnico)
- [Contribuir](#contribuir)

---

## ¿Qué hace?

Permite que un agente de IA (o cualquier cliente MCP) pueda operar la facturación electrónica completa con lenguaje natural:

- 🧾 **Crear, consultar, cancelar y descargar CFDIs** (Ingreso, Egreso, Pago)
- 💸 **Generar Complementos de Pago** (REP 2.0) para facturas PPD
- 👥 **CRUD de clientes y productos** del catálogo
- 📚 **Buscar en catálogos del SAT** sin salir del agente
- 📧 **Enviar facturas por email** al receptor
- 💰 **Manejar descuentos, retenciones, multiconcepto**, monedas extranjeras

Ejemplo de prompt al agente:

> *"Hazme una factura para EMPRESA EJEMPLO, RFC ABC010101AAA, régimen 612, CP 78290. Un concepto: Software, $500 + IVA 16%. Mándamela por email a contabilidad@ejemplo.com"*

El agente usa las herramientas del MCP para construir el CFDI correcto, timbrarlo y enviarlo.

---

## Características

✅ **CFDI 4.0** — Ingreso, Egreso (notas de crédito), Traslado, Pago
✅ **Complemento de Pagos REP 2.0** — para facturas PPD
✅ **Multiconcepto** — múltiples ítems en una sola factura
✅ **Descuentos por concepto** — con cálculo automático de bases imponibles
✅ **Retenciones e IVA traslado** — IVA, ISR, IEPS
✅ **Cancelación con todos los motivos SAT** — incluyendo motivo 01 con UUID sustitución
✅ **CRUD completo** — clientes y productos
✅ **Búsqueda en catálogos SAT** — productos, unidades, regímenes, usos CFDI, formas de pago, monedas, códigos postales
✅ **Envío por email automático** — con confirmación explícita
✅ **Descargas PDF / XML / HTML** — guardadas a disco
✅ **Emisor tomado de la cuenta** — el modelo no puede inventarse en nombre de quién se factura
✅ **Guardarraíles de producción** — dos cerrojos independientes antes de timbrar con valor fiscal

---

## Herramientas disponibles (27 tools)

### Módulo 0 — Emisión en una sola llamada (1 tool)

| Tool | Descripción |
|------|-------------|
| `facturama_factura_emitir` | **La forma habitual de facturar.** Resuelve las claves del SAT de cada concepto, recupera al cliente si ya está dado de alta (o lo crea), toma emisor y lugar de expedición de la cuenta, calcula impuestos y timbra. Si el cliente ya existe, basta su RFC. Solo resuelve claves cuando la búsqueda es inequívoca: ante varias candidatas se detiene y las enumera. |

### Módulo 1 — CFDI (7 tools)

Control fino del comprobante, para lo que no cubre `facturama_factura_emitir`.

| Tool | Descripción |
|------|-------------|
| `facturama_cfdi_crear` | Crea un CFDI 4.0 (Ingreso/Egreso/Traslado/Pago) con timbrado SAT. Soporta multiconcepto, descuentos, retenciones, multimoneda. |
| `facturama_cfdi_crear_pago` | Genera Complemento de Pagos (REP 2.0) para liquidar facturas PPD. |
| `facturama_cfdi_consultar` | Consulta los datos completos de un CFDI por su Id (busca tanto en activos como cancelados). |
| `facturama_cfdi_listar` | Lista CFDIs con filtros: rfc_receptor, fechas, tipo, status, rango de folios. |
| `facturama_cfdi_cancelar` | Cancela un CFDI ante el SAT. Soporta los 4 motivos (01-04) con UUID de sustitución. |
| `facturama_cfdi_descargar` | Descarga PDF / XML / HTML del CFDI a disco local. |
| `facturama_cfdi_enviar_email` | Envía el CFDI (PDF + XML adjuntos) por correo al receptor con confirmación explícita de envío. |

### Módulo 2 — Clientes (5 tools)

| Tool | Descripción |
|------|-------------|
| `facturama_clientes_crear` | Crea un cliente en el catálogo. |
| `facturama_clientes_listar` | Lista o busca clientes por RFC/nombre. |
| `facturama_clientes_obtener` | Obtiene los datos completos de un cliente por su Id. |
| `facturama_clientes_actualizar` | Actualiza un cliente. Solo necesitas pasar los campos a cambiar — el MCP hace `GET → merge → PUT` internamente. |
| `facturama_clientes_eliminar` | Elimina un cliente del catálogo. |

### Módulo 3 — Productos (5 tools)

| Tool | Descripción |
|------|-------------|
| `facturama_productos_crear` | Crea un producto/servicio. Auto-detecta el `Unit` (texto libre) desde el catálogo SAT a partir de la clave de unidad. Acepta SKU opcional. |
| `facturama_productos_listar` | Lista o busca productos en catálogo. |
| `facturama_productos_obtener` | Obtiene un producto por Id. |
| `facturama_productos_actualizar` | Actualiza un producto (mismo patrón GET → merge → PUT que clientes). |
| `facturama_productos_eliminar` | Elimina un producto. |

### Módulo 4 — Catálogos SAT (7 tools)

| Tool | Descripción |
|------|-------------|
| `facturama_sat_buscar_producto` | Busca productos/servicios SAT por texto o clave. ⚠️ Sensible a acentos. |
| `facturama_sat_buscar_unidad` | Busca claves de unidad (HUR, E48, KGM, etc.). |
| `facturama_sat_codigo_postal` | Valida un código postal contra el catálogo SAT. |
| `facturama_sat_regimenes_fiscales` | Lista los 20 regímenes fiscales SAT (601, 612, 626, etc.). |
| `facturama_sat_usos_cfdi` | Lista los usos CFDI (G01, G03, P01, S01, etc.). |
| `facturama_sat_formas_pago` | Lista formas de pago SAT (01-99). |
| `facturama_sat_monedas` | Lista monedas ISO 4217 (MXN, USD, EUR, etc.). |

### Módulo 5 — Cuenta (2 tools)

| Tool | Descripción |
|------|-------------|
| `facturama_cuenta_emisor` | En nombre de qué contribuyente se factura: RFC, razón social, régimen y lugares de expedición disponibles. El emisor lo determinan las credenciales del servidor, nunca los parámetros. |
| `facturama_cuenta_sucursales` | Sucursales de la cuenta, cada una con su código postal de expedición. |

---

## Instalación

> **Estado:** el paquete todavía **no está publicado en PyPI**. Hasta que lo esté,
> usa la instalación desde el repositorio que se describe más abajo; los comandos
> con `uvx facturama-mcp` funcionarán en cuanto se publique la v1.0.

### Requisitos

- **Python 3.11+** — salvo con el bundle `.mcpb` o con Docker, que lo llevan dentro
- **Credenciales de Facturama**, de sandbox o de producción

> **Las credenciales de sandbox son tuyas, no genéricas.** No existe un usuario de
> demo público: cada cuenta tiene las suyas y se piden a Facturama. Si ves un
> `401 Credenciales inválidas`, es esto.

### Cuatro formas, según quién lo instale

| Vía | Para quién | Cómo |
|---|---|---|
| **Bundle `.mcpb`** | Claude Desktop, sin saber de programación | Doble clic y rellenar un formulario |
| **PyPI** | quien ya usa Python, agentes, n8n | `uvx facturama-mcp` |
| **Docker** | integración en infraestructura propia | `docker run facturama/mcp` |
| **Repositorio** | contribuidores | `git clone` + `uv sync` |

#### Bundle para Claude Desktop

Descarga `facturama-facturacion.mcpb` de la [última versión publicada](https://github.com/facturamamx/mcp-facturacion/releases)
y arrástralo a Claude Desktop. Te pedirá usuario, contraseña y entorno en un
formulario. No hace falta Python ni tocar ningún archivo de configuración.

Para construirlo tú mismo: `./mcpb/construir.sh`

#### Desde PyPI

```bash
uvx facturama-mcp          # ejecuta sin instalar nada permanente
# o
pip install facturama-mcp  # instalación normal
```

#### Con Docker

```bash
docker run --rm -p 8000:8000 \
  -e FACTURAMA_USER=tu_usuario \
  -e FACTURAMA_PASSWORD=tu_contraseña \
  facturama/mcp
```

Arranca en HTTP (`http://localhost:8000/mcp`), que es lo que necesitan n8n, Make
y Zapier. Las credenciales van por variables de entorno y nunca dentro de la
imagen.

#### Desde el repositorio

```bash
git clone https://github.com/facturamamx/mcp-facturacion.git
cd mcp-facturacion
uv sync
cp .env.example .env      # y edítalo con tus credenciales
uv run facturama-mcp
```

---

## Configuración

Cuatro variables de entorno, y solo las dos primeras son obligatorias:

| Variable | Obligatoria | Por defecto | Qué hace |
|---|---|---|---|
| `FACTURAMA_USER` | sí | — | Usuario de tu API de Facturama |
| `FACTURAMA_PASSWORD` | sí | — | Contraseña de tu API de Facturama |
| `FACTURAMA_ENV` | no | `sandbox` | `sandbox` (sin valor fiscal) o `production` |
| `FACTURAMA_ALLOW_PRODUCTION` | no | `false` | Cerrojo independiente: mientras valga `false`, nada se timbra ante el SAT |

**Por qué hay dos cerrojos para producción.** `FACTURAMA_ENV=production` no basta
por sí solo: hace falta además `FACTURAMA_ALLOW_PRODUCTION=true`, que solo puede
poner una persona en el entorno del servidor, y una confirmación explícita en cada
llamada que timbre. Timbrar es irreversible y consume folios, así que llegar ahí
por descuido no debería ser posible.

Otras variables opcionales: `FACTURAMA_TIMEOUT` (30 s), `FACTURAMA_MAX_RETRIES`
(3), `FACTURAMA_DOWNLOAD_DIR` (`./downloads`) y `FACTURAMA_MCP_TRANSPORT`
(`stdio` o `http`).

---

## Uso

### Claude Desktop

Lo más sencillo es el bundle `.mcpb` de arriba. Si prefieres configurarlo a mano,
edita `~/Library/Application Support/Claude/claude_desktop_config.json` en macOS o
`%APPDATA%\Claude\claude_desktop_config.json` en Windows:

```json
{
  "mcpServers": {
    "facturama": {
      "command": "uvx",
      "args": ["facturama-mcp"],
      "env": {
        "FACTURAMA_USER": "tu_usuario",
        "FACTURAMA_PASSWORD": "tu_contraseña",
        "FACTURAMA_ENV": "sandbox"
      }
    }
  }
}
```

> 💡 Si Claude Desktop no encuentra `uvx`, pon su ruta absoluta (`which uvx`): la
> aplicación no hereda el `PATH` de tu terminal.

### Claude Code

```bash
claude mcp add facturama \
  --env FACTURAMA_USER=tu_usuario \
  --env FACTURAMA_PASSWORD=tu_contraseña \
  --env FACTURAMA_ENV=sandbox \
  -- uvx facturama-mcp
```

### Cursor

En `~/.cursor/mcp.json`, o en `.cursor/mcp.json` dentro del proyecto:

```json
{
  "mcpServers": {
    "facturama": {
      "command": "uvx",
      "args": ["facturama-mcp"],
      "env": {
        "FACTURAMA_USER": "tu_usuario",
        "FACTURAMA_PASSWORD": "tu_contraseña",
        "FACTURAMA_ENV": "sandbox"
      }
    }
  }
}
```

### VS Code

En `.vscode/mcp.json`. VS Code sabe pedir los secretos al arrancar, así que no
hace falta escribirlos en el archivo:

```json
{
  "inputs": [
    {
      "id": "facturama-password",
      "type": "promptString",
      "description": "Contraseña de la API de Facturama",
      "password": true
    }
  ],
  "servers": {
    "facturama": {
      "command": "uvx",
      "args": ["facturama-mcp"],
      "env": {
        "FACTURAMA_USER": "tu_usuario",
        "FACTURAMA_PASSWORD": "${input:facturama-password}",
        "FACTURAMA_ENV": "sandbox"
      }
    }
  }
}
```

### n8n, Make y Zapier

Estos no ejecutan procesos locales: necesitan una URL. Levanta el servidor en
HTTP y apúntalos a ella.

```bash
docker run -d -p 8000:8000 \
  -e FACTURAMA_USER=tu_usuario \
  -e FACTURAMA_PASSWORD=tu_contraseña \
  --name facturama-mcp facturama/mcp
```

En n8n, añade un nodo **MCP Client** con la URL `http://localhost:8000/mcp` y
transporte **HTTP Streamable**.

> ⚠️ Ese contenedor expone tus credenciales de facturación a quien alcance el
> puerto. Publícalo solo dentro de tu red, detrás de un proxy con autenticación, y
> nunca directamente a internet. El servidor remoto con OAuth llega en la v1.5.

---

## Ejemplos de uso

### 1. Factura simple (1 concepto, IVA 16%)

> "Factura 2 servicios de facturación a 1.000 € cada uno al RFC EKU9003173C9, folio FLUJO-1."

→ Una sola llamada a `facturama_factura_emitir`. Si el cliente ya está dado de alta,
no hace falta nada más: su razón social, régimen y CP salen de su ficha, el emisor y
el lugar de expedición de la cuenta, y la clave del SAT de la descripción. Devuelve el
UUID timbrado junto con las claves que resolvió, para poder revisarlas.

Con control total del comprobante —o para un CFDI que no sea de ingreso— se usa
`facturama_cfdi_crear`, pasando cada clave del SAT explícitamente.

### 2. Factura con múltiples conceptos y descuento

> "Crea factura para [datos receptor]. 3 conceptos: (1) Hosting $500, (2) Dominio $200 con descuento de $50, (3) Certificado SSL $100. Todos con IVA 16%."

→ Usa el campo `Discount` por concepto. La base imponible se calcula como `subtotal − descuento` antes del IVA.

### 3. Factura de honorarios con retención

> "Genera factura para [datos]. 1 concepto: Consultoría legal $10,000. Aplica IVA 16% traslado y retención de ISR 10%."

→ Resultado: subtotal $10,000 + IVA $1,600 − ISR $1,000 = **$10,600**.

### 4. Cancelar y sustituir un CFDI

> "Cancela el CFDI con id `swkZKaX...` motivo 01 y sustitúyelo por el UUID `4e805b06-...`"

→ Llama `facturama_cfdi_cancelar` con `motivo="01"` y `uuid_sustitucion`.

### 5. Complemento de pago (PPD)

> "Crea complemento de pago para liquidar la factura UUID `bb993a7f-...` folio 21 por $5,800 mediante transferencia bancaria el 2026-05-06."

→ Llama `facturama_cfdi_crear_pago` con el array de pagos y documentos relacionados.

### 6. Buscar clave SAT

> "¿Cuál es la clave SAT para servicios de marketing digital?"

→ El agente prueba `buscar_producto_sat("publicidad")` y sugiere `82101603 - Publicidad en internet`.

### 7. Crear cliente desde constancia fiscal

> "Aquí está la constancia fiscal de mi cliente [adjunto PDF]. Crea el cliente en el catálogo y emite una factura por $1,000."

→ El agente extrae los datos del PDF (RFC, nombre, régimen, CP) y llama `facturama_clientes_crear` + `facturama_cfdi_crear`.

---

## Particularidades de la API de Facturama

> ⚠️ Estos son hallazgos validados en producción que **no están claramente documentados**. Anótalos para tu equipo.

### 1. Validación nombre↔RFC (CFDI 4.0)

Desde CFDI 4.0, el SAT valida que el `receptor_nombre` coincida exactamente con el nombre registrado para ese RFC en su cédula fiscal.

- ❌ RFC inventado + nombre ficticio → `400 Bad Request`
- ✅ RFC real con nombre tal cual SAT → OK
- ✅ RFCs genéricos del SAT siempre funcionan:
  - `XAXX010101000` — Público en general
  - `XEXX010101000` — Receptor extranjero

### 2. Endpoints CFDI ≠ `/api-lite/Cfdi`

Documentación parcial sugiere `/api-lite/Cfdi/...`. Esos endpoints **no existen** en producción. Los reales son:

| Operación | Endpoint correcto |
|-----------|-------------------|
| Listar | `GET /cfdi` (con query params) |
| Cancelar | `DELETE /cfdi/{id}` |
| Descargar PDF | `GET /cfdi/pdf/issued/{id}` |
| Descargar XML | `GET /cfdi/xml/issued/{id}` |
| Enviar email | `POST /Cfdi?cfdiType=issued&cfdiId=X&email=Y` |
| Crear | `POST /3/cfdis` |

`POST /3/cfdis` toma el emisor y el CSD de la cuenta autenticada. La variante
`/api-lite/3/cfdis` es para revendedores que timbran en nombre de varios
contribuyentes y exige un nodo `Issuer` en el payload; queda fuera del alcance
de la v1.0, que factura siempre en nombre del titular de las credenciales.

### 3. PUT estricto en clientes y productos

La API rechaza payloads parciales en `PUT /Client/{id}` y `PUT /Product/{id}`:

- Exige todos los campos requeridos del modelo
- Rechaza strings vacíos en campos opcionales (`CuentaPredial: ""` falla)

**Este MCP implementa automáticamente** `GET → merge → clean → PUT`, así que solo pasas los campos a cambiar (`facturama_clientes_actualizar`, `facturama_productos_actualizar`).

### 4. CFDI tipo P (Pago) tiene un payload muy distinto

- ❌ NO debe llevar `Items` ni `PaymentForm` a nivel raíz
- ✅ `CfdiUse` debe ser `CP01` obligatoriamente
- ✅ Toda la info real va en `Complemento.Payments`
- La herramienta `facturama_cfdi_crear_pago` aplica esto automáticamente.

### 5. Endpoints DELETE para clientes

- ❌ `DELETE /Client/{id}` → 404
- ✅ `DELETE /api-lite/client/{id}` → 200

(El MCP usa el correcto.)

### 6. Comportamiento del sandbox vs producción

| Operación | Sandbox | Producción |
|-----------|---------|------------|
| Crear/Cancelar CFDI | ✅ Real (timbra) | ✅ Real |
| Enviar email | ✅ Llega | ✅ Llega |
| Eliminar cliente | ⚠️ Responde 200 pero NO elimina | ✅ Elimina |
| Eliminar producto | ✅ Elimina | ✅ Elimina |

> El sandbox NO tiene validez fiscal pero SÍ envía correos reales. Cuidado con los destinatarios.

### 7. Búsqueda sensible a acentos

El catálogo SAT del endpoint `/catalogs/ProductsOrServices` distingue acentos:

```
buscar_producto_sat("consultoria")    → []           ❌
buscar_producto_sat("consultoría")    → 4 resultados ✅
buscar_producto_sat("80101500")       → 1 resultado  ✅ (clave directa)
```

### 8. Límite de envíos por email

Facturama bloquea el reenvío múltiple de la **misma factura** al **mismo email** (anti-spam). Si necesitas reenviar, crea un CFDI nuevo o cambia de destinatario.

Mensaje de error explícito: `{"success": false, "msj": "Has alcanzado el límite de envíos para esta factura."}`

### 9. Header `Content-Type: application/json` (sin charset)

Algunos endpoints rechazan `Content-Type: application/json; charset=utf-8` (lo que httpx envía por default). El MCP envía solo `application/json`.

---

## Estructura del proyecto

```
mcp-facturacion/
├── src/facturama_mcp/
│   ├── server.py          # Las 27 herramientas MCP
│   ├── client.py          # Cliente HTTP: reintentos, timeouts, credenciales
│   ├── config.py          # Configuración diferida desde el entorno
│   ├── account.py         # Emisor y lugares de expedición, desde la cuenta
│   ├── resolucion.py      # Resolución de claves del SAT y de clientes
│   ├── respuestas.py      # Paginación, truncado y formato de las respuestas
│   ├── catalogs.py        # Catálogos del SAT con caché por proceso
│   ├── validators.py      # Validación estructural antes de llamar a la API
│   ├── errors.py          # Traducción de errores de la API
│   └── cli.py             # Punto de entrada `facturama-mcp`
├── tests/
│   ├── fixtures/          # Respuestas capturadas del sandbox real
│   ├── sandbox_falso.py   # Sirve esas capturas sin red
│   └── capturar_fixtures.py
├── mcpb/                  # Bundle para Claude Desktop
├── server.json            # Metadatos para el MCP Registry
├── Dockerfile
└── ROADMAP-v1.md          # Plan, decisiones cerradas y su porqué
```

### Por dónde empezar a leer

- **`server.py`** — cada herramienta es una función `async` con `@mcp.tool()`.
- **`ROADMAP-v1.md` §6** — las decisiones de diseño y, sobre todo, por qué se
  tomaron. Ahorra re-litigar cosas ya discutidas.
- **`tests/sandbox_falso.py`** — reproduce las rarezas reales de la API. Si algo
  te sorprende del código, probablemente esté explicado ahí.

---

## Desarrollo

```bash
uv sync                    # instalar
uv run facturama-mcp       # arrancar en stdio

# En HTTP, para n8n o pruebas remotas
FACTURAMA_MCP_TRANSPORT=http uv run facturama-mcp
```

### Pruebas

```bash
uv run pytest -m "not sandbox"   # 310 pruebas, sin red ni credenciales
uv run pytest -m sandbox         # contrato contra la API real (necesita .env)
uv run ruff check src/ tests/
uv run mypy src/facturama_mcp/
```

Las pruebas offline corren contra capturas reales del sandbox, no contra mocks
escritos a mano. Si la API cambia, se recapturan:

```bash
uv run python tests/capturar_fixtures.py
```

El motivo está en `ROADMAP-v1.md` §6.12, y se resume en que los fallos que ha
tenido este servidor **respondían todos 200**: un mock inventado los habría dado
por buenos.

### Inspeccionar las herramientas

```bash
npx @modelcontextprotocol/inspector uv run facturama-mcp
```

---

## Despliegue

Para uso personal o de escritorio no hace falta desplegar nada: el bundle `.mcpb`
o `uvx facturama-mcp` corren en el equipo de cada persona con sus propias claves.
Esto es para compartir el servidor con un equipo o conectarlo a n8n, Make o Zapier.

### Docker

```bash
docker run -d --name facturama-mcp -p 8000:8000 \
  -e FACTURAMA_USER=tu_usuario \
  -e FACTURAMA_PASSWORD=tu_password \
  -e FACTURAMA_ENV=production \
  -e FACTURAMA_ALLOW_PRODUCTION=true \
  --restart unless-stopped \
  facturama/mcp
```

**Antes de poner `FACTURAMA_ALLOW_PRODUCTION=true`, lee esto.** Ese contenedor
puede timbrar comprobantes con valor fiscal ante el SAT, y quien alcance el puerto
puede hacerlo. Hoy el servidor **no autentica a quien se conecta**: da por hecho
que corre en el equipo de su dueño. Si lo expones en red:

- Detrás de un proxy con TLS y autenticación, nunca directo a internet.
- Solo en la red interna, sin publicar el puerto al exterior.
- En sandbox mientras estés probando la integración.

El servidor remoto pensado para varios usuarios, con OAuth 2.1 y aislamiento por
cuenta, llega en la v1.5. Hasta entonces, esto es un despliegue de un solo
inquilino disfrazado de servicio.

### Comprobar que está sano

```bash
curl -s http://localhost:8000/mcp -H 'Accept: text/event-stream' | head -1
docker logs facturama-mcp
```

La imagen trae un `HEALTHCHECK` que comprueba que el servidor puede listar sus
herramientas, no solo que el puerto conteste.

---

## Troubleshooting

### "401 Unauthorized" al iniciar el servidor

- Verifica que `.env` tenga las credenciales correctas
- Si el password tiene caracteres especiales (`&`, `@`, `$`), está bien tal cual — NO escapes
- Verifica que `FACTURAMA_ENV` esté en `sandbox` o `production` correctamente

### "El campo Nombre del receptor, debe pertenecer al nombre asociado al RFC"

- El RFC inventado no pasa validación SAT (CFDI 4.0)
- Usa un RFC real con su nombre correcto, o un RFC genérico (`XAXX010101000`)

### "Has alcanzado el límite de envíos para esta factura"

- Facturama bloquea reenvíos al mismo email
- Crea una nueva factura o usa otro destinatario

### La tool no aparece en Claude Code/Desktop/Cursor

- Verifica que el config apunte a `uv` con **ruta absoluta** (`which uv`)
- Reinicia el cliente tras editar el config
- En Claude Code: `/mcp` te muestra los servidores conectados
- Revisa que el `.env` esté presente en el directorio del servidor

### Búsqueda en SAT devuelve vacío

- ¿Tiene acentos? `consultoria` ❌ vs `consultoría` ✅
- Si conoces la clave SAT, búscala directa: `buscar_producto_sat("80101500")`

### `CSD` o "no encontrado el certificado del emisor"

- El usuario sandbox `lovableapi` SÍ tiene CSD para `EKU9003173C9`
- Si usas otro RFC emisor, asegúrate de tener su CSD configurado en Facturama
- El MCP hace fallback automático a la API regular si la Lite falla por CSD

### `127 command not found: uv`

- Falta instalar `uv` o no está en el PATH
- Usa la ruta absoluta en el config: `which uv` te la dice

---

## Roadmap

### v0.3 — En curso

- [ ] **Tests automatizados** — pytest + coverage > 80%
- [ ] **Dockerfile** + docker-compose
- [ ] **Auth en HTTP remoto** — token Bearer compartido
- [ ] **Resources MCP** — plantillas de CFDIs comunes
- [ ] **Logging estructurado** — JSON logs

### v0.4 — Planeado

- [ ] **Nómina** — CFDIs con complemento 1.2
- [ ] **Carta Porte 3.1** — Para transportistas
- [ ] **Multiemisor** — Soporte para múltiples RFCs emisores en una sesión
- [ ] **Comercio Exterior** — Complemento para exportaciones
- [ ] **Reportes fiscales** — Resúmenes por período

### v0.5 — Futuro

- [ ] **Webhooks** — Notificaciones de cancelación/aceptación
- [ ] **Validador XML** — Verificación previa antes de timbrar
- [ ] **Plantillas reutilizables** — Para clientes y productos frecuentes

### Completado ✅

- [x] CFDI 4.0 (Ingreso, Egreso, Pago)
- [x] CRUD Clientes y Productos
- [x] Catálogos del SAT
- [x] Cancelaciones (todos los motivos 01-04)
- [x] Multi-concepto, descuentos, retenciones
- [x] Envío por email con confirmación
- [x] Descargas PDF/XML/HTML
- [x] Complemento de Pagos REP 2.0
- [x] Validación end-to-end con 24 herramientas

---

## Stack técnico

| Componente | Versión | Propósito |
|------------|---------|-----------|
| **FastMCP** | 3.1+ | Framework MCP (stdio + HTTP) |
| **httpx** | latest | Cliente HTTP async |
| **Pydantic** | 2.x | Modelos de datos y validación |
| **Python** | 3.11+ | Runtime |
| **uv** | latest | Gestor de paquetes y entornos |
| **Decimal** | stdlib | Precisión fiscal en cálculos monetarios |
| **Facturama API REST** | v3 / API Lite | Backend de facturación |

### Decisiones de diseño

- **Async I/O end-to-end** con `httpx.AsyncClient` para no bloquear en operaciones de red.
- **`Decimal` con `ROUND_HALF_UP`** para todos los cálculos monetarios (compatible con regla SAT).
- **Pydantic models** para `Item` y `Tax` con tipado fuerte que se traduce a JSON Schema MCP automáticamente.
- **Fallback automático** a API regular cuando API Lite falla por falta de CSD.
- **GET → merge → clean → PUT** en updates para evitar payloads parciales rechazados por la API.

---

## Contribuir

Pull Requests / Merge Requests bienvenidos.

### Flujo recomendado

1. Fork del repo
2. Crea una branch: `feature/mi-tool` o `fix/bug-x`
3. Implementa + prueba localmente con MCP Inspector o Python directo
4. Push + abre PR/MR a `main`
5. Espera revisión de un Maintainer

### Convenciones de commits

```
feat: nueva funcionalidad
fix: corrección de bug
docs: cambios en documentación
chore: mantenimiento (deps, configs)
test: agregar/modificar tests
refactor: cambios sin alterar comportamiento
```

### Reportar bugs

Abre un issue con:
- **Versión del MCP** (`git rev-parse HEAD`)
- **Comando ejecutado** o prompt al agente
- **Output esperado** vs **output real**
- **Logs** (con `HTTPX_LOG_LEVEL=DEBUG` si es problema HTTP)

---

## Licencia

(Por definir — sugerencia: MIT o Apache 2.0)

---

## Créditos

Desarrollo y validación: **Antonio Tembleque** ([@facturamamx](https://github.com/facturamamx)) — Facturama Engineering.

Hallazgos de bugs en la API documentados durante validación end-to-end con todas las 24 herramientas en sandbox.

---

## Soporte

- **Issues / bugs**: abrir issue en este repo
- **Documentación API Facturama**: https://apisandbox.facturama.mx/docs
- **Documentación FastMCP**: https://gofastmcp.com
- **Especificación MCP**: https://modelcontextprotocol.io
