Skip to main content
Glama
davidsandez

Mini-ERP MCP

by davidsandez
README.md
# Mini-ERP MCP — proyecto de aprendizaje

Servidor de **Model Context Protocol (MCP)** construido sobre un dominio de
ERP simulado (productos, clientes, ventas), para entender de punta a punta
cómo un modelo de lenguaje puede descubrir y usar herramientas y datos
externos de forma estandarizada, en vez de mediante integraciones a medida
por cada proveedor de LLM.

> **Contexto:** este es un proyecto personal de exploración técnica, no un
> producto en producción ni un desarrollo para cliente. El dominio de ERP es
> deliberadamente mínimo (un archivo JSON como "base de datos") — el objetivo
> no es la lógica de negocio, sino comprender el protocolo.

## Qué hace

Simula el flujo de un mensaje entrante (por ejemplo, de WhatsApp) que llega a
un sistema tipo ERP. El sistema arma el contexto relevante para el modelo,
le ofrece un conjunto de herramientas, y el modelo decide de forma autónoma
qué acción tomar para responder — incluyendo una acción con efecto real
(registrar una venta), que requiere confirmación humana antes de ejecutarse.

## Conceptos que este proyecto pone en práctica

MCP define tres primitivas, y este proyecto usa las tres con un criterio de
diseño explícito para cada una:

| Elemento | Tipo | Por qué |
|---|---|---|
| `catalogo://productos` | **Resource** | Dato estable; lo decide inyectar el host de entrada, sin que el modelo lo pida. |
| `cliente://{id}` | **Resource** (URI con parámetro) | El host ya conoce el ID de quien escribe (vendría del webhook); arma la URI y la inyecta. |
| `consultar_stock(producto_id)` | **Tool** | El modelo decide, en medio de la conversación, si la necesita. |
| `registrar_venta(cliente_id, producto_id, cantidad)` | **Tool destructiva** | Acción con efecto real; requiere confirmación humana antes de ejecutarse. |
| `resumen_ventas(dias)` | **Prompt** | Plantilla reutilizable para una tarea recurrente. |

La distinción central que este proyecto permite comprobar en la práctica:
**con una tool, el host le cede al modelo la decisión de invocarla; con un
resource, el host retiene esa decisión para sí.**

## Arquitectura

```
┌───────────────────────┐   stdio   ┌───────────────────────┐
│  Host (host_demo.py)    │◄────────►│  Servidor MCP           │
│  - Arma el contexto       │          │  (server.py, FastMCP)    │
│  - Ofrece tools al LLM     │          │  - Resources             │
│  - Ejecuta el loop de      │          │  - Tools                 │──► data/erp.json
│    tool-calling             │          │  - Prompts               │
│  - Pide confirmación         │          └───────────────────────┘
│    humana en acciones          │
│    destructivas                  │
└──────────┬────────────────┘
           │ API compatible OpenAI (router de HF) o API de Anthropic
           ▼
    ┌─────────────┐
    │     LLM       │
    └─────────────┘
```

`host_demo.py` cumple, en este proyecto, tanto el rol de **host** como de
**client MCP** (en una integración real, el cliente MCP suele ser un módulo
dentro del host, no una pieza aparte).

## Stack

- **`mcp[cli]`** (FastMCP) — SDK oficial de Python para servidores MCP.
- **Hugging Face Inference Providers** — modelos open source, vía el router
  compatible con la API de OpenAI (`openai` SDK apuntando a
  `https://router.huggingface.co/v1`).
- **Anthropic SDK** — soporte alternativo, mantenido en paralelo para
  comparar el mismo servidor MCP funcionando con dos proveedores distintos.
- **MCP Inspector** — herramienta de desarrollo para probar el servidor sin
  necesidad de un cliente real.

## Cómo correrlo en local

```bash
cp .env.example .env   # completar HF_TOKEN (y opcionalmente ANTHROPIC_API_KEY)
uv sync
```

### Probar el servidor de forma aislada (sin LLM)

```bash
uv run mcp dev server.py
```

Abre el MCP Inspector en el navegador, donde se pueden invocar las tools y
leer los resources manualmente.

### Correr el flujo completo (host + LLM real)

```bash
uv run host_demo.py
```

Por defecto usa Hugging Face (`procesar_mensaje_huggingface`). El código
para usar Anthropic (`procesar_mensaje`) queda disponible en el mismo
archivo, comentado en el bloque `if __name__ == "__main__":`.

## Variables de entorno

```dotenv
HF_TOKEN=tu_token_de_huggingface
HF_MODEL=openai/gpt-oss-120b

# Opcional, si se quiere probar con Anthropic en vez de Hugging Face
ANTHROPIC_API_KEY=tu_api_key_de_anthropic
```

## Qué aprendí construyendo esto

- El mecanismo real de descubrimiento e invocación de herramientas en MCP:
  el modelo nunca habla directo con el servidor MCP, es el host quien media
  cada `list_tools()` / `call_tool()` / `read_resource()`.
- La diferencia entre *function calling* nativo de cada proveedor de LLM
  (formatos distintos entre Anthropic y APIs compatibles con OpenAI) y cómo
  MCP estandariza la exposición de herramientas sin depender de esos
  formatos particulares.
- Que no todos los modelos servidos por un proveedor de inferencia soportan
  tool-calling de forma confiable, y cómo verificarlo antes de integrar.
- Dónde vive la responsabilidad del control humano sobre acciones con
  efecto (no es algo que el protocolo resuelva automáticamente, es una
  decisión de diseño del host).
- Portabilidad real: el mismo `server.py`, sin modificaciones, funciona
  igual con dos proveedores de LLM distintos.

## Estado del proyecto

Proyecto de aprendizaje, completo y funcional para el alcance definido.
Pendiente como posible extensión futura: conectar el mismo servidor a
Claude Desktop para comprobar la portabilidad contra un cliente real (no
solo el script `host_demo.py`).

TDQS

A3.6/5.0

Scored across 2 tools

Disambiguation5/5

The two tools are unambiguously distinct: consultar_stock is a read-only stock lookup and registrar_venta is a state-changing sale registration. Descriptions explicitly frame the boundary (read vs. real-effect write), so an agent cannot confuse them.

Naming Consistency5/5

Both names follow the same Spanish verb_noun pattern (consultar_stock, registrar_venta) with consistent snake_case and imperative verbs. There is no mixed convention.

Tool Count2/5

Two tools is far too thin for a server scoped as a 'Mini-ERP' handling inventory and sales; even a minimal ERP needs product listing, restock/adjustment, and sales history. The surface only covers a single narrow checkout flow.

Completeness2/5

Only a read-stock plus create-sale pair exists, with no product catalog/listing, no inventory update or restock, no sale retrieval or cancellation, and no reporting. Agents will hit dead ends for any operation beyond checking one product and recording one sale.

Maintenance

ActivitySlowing
ResponsivenessNo issues