Skip to main content
Glama
ddiax09

ASPEL SAE MCP Server

by ddiax09
README.md
# Servidor MCP para ASPEL SAE 8 y 10 (Firebird 2.5) con Multialmacén

Servidor **Model Context Protocol (MCP)** especializado para conectar agentes de Inteligencia Artificial (Antigravity, Claude, ChatGPT, Cursor, Windsurf, etc.) con bases de datos **Firebird 2.5** de **ASPEL SAE 8.0 y 10.0**, con soporte completo para la configuración de **Multialmacén**.

---

## 🎯 Capacidades Principales

- **Conectividad Nativa Firebird 2.5**: Implementado con driver 100% puro (`firebirdsql`), eliminando la necesidad de lidiar con `fbclient.dll` o incompatibilidades de arquitectura 32/64 bits.
- **Soporte de Caracteres en Español**: Configuración de juego de caracteres `WIN1252` / `ISO8859_1` para evitar problemas con acentos y la letra "ñ".
- **Gestión Especializada de Multialmacén**:
  - Consulta de existencias por almacén en la tabla núcleo `MULT[EE]`.
  - Catálogo de almacenes en `ALMACENES[EE]`.
  - Kardex de movimientos por almacén en `MINVE[EE]`.
  - Rastreabilidad de partidas en ventas y pedidos con su respectivo almacén asignado (`NUM_ALM` en `PAR_FACT*[EE]`).
- **Máxima Seguridad de Solo Lectura (Read-Only Guardrails)**:
  - Transacciones con aislamiento `READ COMMITTED` y `ROLLBACK` forzado para jamás retener bloqueos en tablas de un ERP en producción.
  - Validador sintáctico AST (`sqlglot`) que bloquea tajantemente operaciones de modificación (`INSERT`, `UPDATE`, `DELETE`, `DROP`, `ALTER`, `EXECUTE`, etc.).
  - Inyección y acotamiento automático de la cláusula de paginación de Firebird 2.5 (`SELECT FIRST n`).
- **Multi-Empresa Dinámica**: Parámetro de empresa adaptable (`01` a `99`), por defecto configurado en variables de entorno.

---

## 🗄️ Esquema de Datos ASPEL SAE (Multialmacén)

En ASPEL SAE, todas las tablas terminan con un sufijo de dos dígitos correspondiente al número de empresa (ej. `01`).

```
                              ┌──────────────────┐
                              │   ALMACENES01    │
                              │ (CVE_ALM, DESCR) │
                              └────────┬─────────┘
                                       │ 1:N
┌──────────────────┐ 1:N      ┌────────▼─────────┐
│      INVE01      ├─────────►│      MULT01      │
│ (CVE_ART, DESCR, │          │(CVE_ART, CVE_ALM,│
│ CON_CTRL_ALM)    │          │ EXIST, STOCK_MIN)│
└────────┬─────────┘          └──────────────────┘
         │ 1:N
┌────────▼─────────┐
│     MINVE01      │ (Kardex: Historial de movimientos con ALMACEN)
└──────────────────┘
         ▲
         │ Afectado por
┌────────┴─────────┐          ┌──────────────────┐
│   PAR_FACTF01    ├─────────►│     FACTF01      │
│(CVE_ART, NUM_ALM,│ N:1      │ (CVE_DOC, FECHA, │
│ CANT, PREC)      │          │  CVE_CLPV, TOT)  │
└──────────────────┘          └──────────────────┘
```

### Tablas Involucradas

| Tabla Base | Descripción de Negocio | Campos Clave |
| :--- | :--- | :--- |
| `ALMACENES` | Catálogo de almacenes físicos/lógicos. | `CVE_ALM`, `DESCR`, `ENCARGADO`, `STATUS` |
| `INVE` | Catálogo maestro de artículos y servicios. | `CVE_ART`, `DESCR`, `CON_CTRL_ALM` ('S'/'N'), `EXIST`, `STATUS` |
| `MULT` | **Existencias por almacén**. | `CVE_ART`, `CVE_ALM`, `EXIST`, `STOCK_MIN`, `STOCK_MAX`, `STATUS` |
| `MINVE` | Kardex de movimientos de inventario. | `NUM_MOV`, `CVE_ART`, `ALMACEN`, `FECHA_DOCU`, `TIPO_DOC`, `CANT`, `SIGNO` |
| `FACTF` / `PAR_FACTF` | Facturas de venta y sus partidas. | `CVE_DOC`, `CVE_CLPV`, `NUM_ALM` (almacén de donde salió el artículo) |
| `FACTP` / `PAR_FACTP` | Pedidos de clientes y sus partidas. | `CVE_DOC`, `CVE_CLPV`, `NUM_ALM` (almacén asignado para surtir) |
| `CLIE` | Catálogo de clientes y saldos. | `CLAVE`, `NOMBRE`, `RFC`, `SALDO`, `LIMCRED` |

### Diferencias Clave: SAE 8.0 vs SAE 10.0
- **SAE 10**: Incorpora campos específicos de CFDI 4.0 (`OBJ_IMP` en partidas de documentos), extensiones de longitud en campos de texto y mayor integración con regímenes fiscales en `CLIE`.
- **SAE 8**: Esquema tradicional con desglose en 4 tasas de impuestos y longitud estándar de 40 caracteres en descripciones de inventario iniciales.

---

## 🛠️ Instalación y Puesta en Marcha

### 1. Requisitos Previos
- **Python 3.10+** (recomendado Python 3.11 - 3.13 en Windows).
- Servicio de **Firebird 2.5** en ejecución (puerto 3050).
- Archivo de base de datos `.FDB` de Aspel SAE accesible en red local o localmente.

### 2. Clonar / Descargar e Instalar Dependencias
```powershell
# Crear y activar entorno virtual
python -m venv .venv
.\.venv\Scripts\Activate.ps1

# Opción A: Instalar dependencias desde requirements.txt
pip install -r requirements.txt

# Opción B: Instalar el paquete en modo editable con su CLI
pip install -e .
```

### 3. Configuración del Archivo `.env`
Copia `.env.example` como `.env` y ajusta las rutas y credenciales según tu entorno:

```ini
SAE_HOST=localhost
SAE_PORT=3050

# Ruta típica para SAE 8:
# SAE_DATABASE_PATH=C:/Program Files (x86)/Common Files/Aspel/Sistemas Aspel/SAE 8.00/Datos/Empresa01/SAE80EMPRE01.FDB

# Ruta típica para SAE 10:
SAE_DATABASE_PATH=C:/Program Files (x86)/Common Files/Aspel/Sistemas Aspel/SAE 10.00/Datos/Empresa01/SAE100EMPRE01.FDB

SAE_USER=SYSDBA
SAE_PASSWORD=masterkey
SAE_CHARSET=WIN1252
SAE_DEFAULT_EMPRESA=01
SAE_VERSION=10
SAE_LOG_LEVEL=INFO
```

### 4. Diagnóstico de Conexión
Ejecuta el script de diagnóstico para verificar que la base de datos y las tablas de multialmacén respondan:

```powershell
python test_connection.py
```

---

## 🔌 Configuración en Clientes MCP

### En Antigravity / Claude Desktop / Cursor (`mcp_config.json` o `claude_desktop_config.json`)

#### Opción 1: Ejecutando el módulo Python con entorno virtual
```json
{
  "mcpServers": {
    "aspel-sae": {
      "command": "C:\\ruta\\a\\tu\\proyecto\\.venv\\Scripts\\python.exe",
      "args": ["-m", "sae_mcp.server"],
      "cwd": "C:\\ruta\\a\\tu\\proyecto",
      "env": {
        "SAE_HOST": "localhost",
        "SAE_PORT": "3050",
        "SAE_DATABASE_PATH": "C:/Program Files (x86)/Common Files/Aspel/Sistemas Aspel/SAE 10.00/Datos/Empresa01/SAE100EMPRE01.FDB",
        "SAE_USER": "SYSDBA",
        "SAE_PASSWORD": "masterkey",
        "SAE_CHARSET": "WIN1252",
        "SAE_DEFAULT_EMPRESA": "01",
        "SAE_VERSION": "10"
      }
    }
  }
}
```

#### Opción 2: Ejecutando el script de consola (`sae-mcp`) tras `pip install -e .`
```json
{
  "mcpServers": {
    "aspel-sae": {
      "command": "C:\\ruta\\a\\tu\\proyecto\\.venv\\Scripts\\sae-mcp.exe",
      "args": [],
      "cwd": "C:\\ruta\\a\\tu\\proyecto"
    }
  }
}
```

---

## 🧰 Catálogo de Herramientas y Recursos

### Herramientas Expuestas al Agente (MCP Tools)

| Herramienta | Parámetros Clave | Descripción |
| :--- | :--- | :--- |
| `list_warehouses` | `empresa` (opcional) | Lista todos los almacenes registrados en `ALMACENES[EE]`. |
| `get_stock_by_warehouse` | `cve_art`, `cve_alm` (opcional), `empresa` | Existencia y límites de stock por almacén en `MULT[EE]`. |
| `get_warehouse_summary` | `cve_alm`, `empresa` | Resumen de piezas, total de artículos y alertas de stock bajo mínimo. |
| `get_product_kardex` | `cve_art`, `cve_alm`, `fecha_inicio`, `limit` | Historial de movimientos (entradas/salidas/traspasos) en `MINVE[EE]`. |
| `search_products` | `query`, `cve_alm`, `linea`, `status`, `limit` | Búsqueda por clave o descripción con desglose multialmacén. |
| `get_product_detail` | `cve_art`, `empresa` | Ficha técnica completa del artículo con existencias por almacén. |
| `get_document_details` | `cve_doc`, `tipo_doc`, `empresa` | Inspección de factura/pedido/remisión con almacén de surtido (`NUM_ALM`). |
| `get_client_info` | `clave_cliente`, `empresa` | Datos comerciales, saldo, límite de crédito y RFC en `CLIE[EE]`. |
| `explain_sae_table` | `table_name` | Diccionario de datos y relaciones de la tabla indicada. |
| `query_sae_read_only` | `sql_query`, `max_rows` | Consulta SQL libre de solo lectura con guardrails y paginación `FIRST n`. |

### Recursos MCP (MCP Resources)
- `sae://warehouses`: Listado JSON de todos los almacenes.
- `sae://schema/dictionary`: Diccionario de datos completo y diferencias SAE 8 vs 10.
- `sae://company/info`: Configuración actual y estado del servicio.

---

## 🧪 Pruebas Automatizadas

Para ejecutar las pruebas unitarias y de seguridad:

```powershell
.\.venv\Scripts\python.exe -m pytest tests/ -v
```

Las pruebas cubren:
- Inyección de cláusula `FIRST n` y preservación de `SKIP m` en Firebird 2.5.
- Bloqueo de inyecciones de sentencias múltiples con punto y coma.
- Rechazo absoluto de sentencias `INSERT`, `UPDATE`, `DELETE`, `DROP`, `ALTER`, `TRUNCATE`, `EXECUTE`.
- Validación estricta contra inyección SQL en el identificador de `empresa`.
- Mapeo y serialización de modelos Pydantic de Multialmacén y Documentos.

---

## 🔒 Recomendaciones de Seguridad para Producción

1. **Usuario de Base de Datos de Solo Lectura**:
   Aunque el servidor MCP implementa validación de sintaxis AST (Abstract Syntax Tree) con `sqlglot` y transacciones con `ROLLBACK` obligatorio, **se recomienda enfáticamente** crear un usuario específico en Firebird 2.5 con permisos exclusivos de lectura:
   ```sql
   /* En Firebird 2.5 */
   CREATE USER MCP_READONLY PASSWORD 'tu_password_segura';
   GRANT SELECT ON ALMACENES01 TO MCP_READONLY;
   GRANT SELECT ON INVE01 TO MCP_READONLY;
   GRANT SELECT ON MULT01 TO MCP_READONLY;
   GRANT SELECT ON MINVE01 TO MCP_READONLY;
   GRANT SELECT ON FACTF01 TO MCP_READONLY;
   GRANT SELECT ON PAR_FACTF01 TO MCP_READONLY;
   GRANT SELECT ON CLIE01 TO MCP_READONLY;
   ```
2. **Protección del Archivo `.env`**:
   Nunca incluyas tu archivo `.env` en el control de versiones. Asegúrate de mantenerlo en tu `.gitignore`.
3. **Restricción de Red para Firebird (Puerto 3050)**:
   No expongas el puerto 3050 de Firebird a Internet. Mantén la base de datos accesible únicamente por `localhost`, red LAN o a través de un túnel seguro / VPN.

---

## 📄 Licencia

Este proyecto se distribuye bajo la licencia [MIT](LICENSE).

---

## ⚖️ Descargo de Responsabilidad (Disclaimer)

*Este proyecto es una herramienta de código abierto desarrollada de manera independiente por la comunidad y **no** está afiliado, respaldado, patrocinado ni asociado oficialmente con **Siigo** ni **Aspel de México, S.A. de C.V.*** 

*ASPEL, ASPEL SAE, ASPEL COI y sus logotipos son marcas registradas propiedad de sus respectivos titulares. Su mención en este repositorio tiene fines meramente descriptivos y de compatibilidad técnica.*

TDQS

A3.7/5.0

Scored across 10 tools

Disambiguation4/5

Most tools have distinct resource+action targets. Some overlap exists among get_stock_by_warehouse, get_product_detail, and search_products because all expose stock data, but the descriptions make the intended use identifiable.

Naming Consistency5/5

All tool names follow a consistent snake_case verb_noun pattern such as list_warehouses, get_product_kardex, and search_products. The occasional preposition like by_warehouse is still readable and does not break the overall pattern.

Tool Count5/5

Ten tools is well-scoped for an ERP-focused MCP server covering warehouse, product, document, client, and schema lookup. Each tool has a clear role and none feel redundant.

Completeness4/5

The read-only inventory domain is well covered: warehouses, stock, kardex, product search/detail, documents, clients, and table exploration. The main gap is the lack of direct list endpoints, but query_sae_read_only and explain_sae_table provide a workaround.

Maintenance

ActivityMaintained
ResponsivenessNo issues