Skip to main content
Glama
davidsandez

Mini-ERP MCP

by davidsandez

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.

Related MCP server: OdooSurface MCP

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

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

Probar el servidor de forma aislada (sin LLM)

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)

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

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).

Available Tools

2 tools
consultar_stockA

Consulta el stock disponible de un producto específico por su ID.

Usar esta herramienta cuando el cliente pregunte por disponibilidad de un producto puntual.

NameRequiredDescriptionDefault
producto_idYes

Output Schema

NameRequiredDescription
resultYes

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavioral burden. It implies a safe read operation via 'Consulta', but says nothing about behavior when the product ID is unknown, the stock format, or any limits. Minimal but adequate for a simple read tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, purpose front-loaded ahead of the usage condition, with no wasted wording. Appropriately sized for the tool's simplicity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained. For a single-parameter read tool, purpose and when-to-use cover the essentials; only edge-case behavior (unknown ID) is left unstated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% for the single required parameter. The phrase 'por su ID' clarifies that producto_id is an identifier rather than a name, adding some meaning, but no format or validity details are given.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (Consulta) and resource (stock disponible) scoped to a single product by ID, so the agent immediately knows what it does. The purpose is functionally distinct from the sibling registrar_venta, though the description never explicitly contrasts them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The second sentence gives a clear triggering condition: use when the customer asks about the availability of a specific product. It offers no explicit exclusions or named alternatives, which is the only gap.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

registrar_ventaA
Destructive

Registra una venta: descuenta stock y crea el registro de la operación.

ACCIÓN CON EFECTO REAL sobre el estado del sistema. Usar solo cuando
el cliente confirmó explícitamente que quiere comprar.
NameRequiredDescriptionDefault
cantidadYes
cliente_idYes
producto_idYes

Output Schema

NameRequiredDescription
resultYes

TDQS

A3.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=false and destructiveHint=true, and the description usefully adds what actually changes: stock is decremented and an operation record is created. It does not cover reversibility, permission requirements, or failure modes such as insufficient stock, which is the remaining gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Short and front-loaded: the action and its effects come first, then the usage guardrail. The capitalised warning line is deliberate emphasis rather than filler, though it partially restates the destructive annotation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need no explanation, and the mutation's effect on state is described. However, an irreversible write tool with 0% parameter documentation and no auth, stock-check, or error guidance is only minimally complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% and the description adds no information about the three required parameters. Their names (cliente_id, producto_id, cantidad) are self-explanatory, but no format, ID source, or unit/limit semantics are given, so the description fails to compensate for the coverage gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (registra) and resource (una venta) plus the two concrete side effects: descuenta stock and crea el registro de la operación. That side-effect profile cleanly separates it from the read-only sibling consultar_stock, though it never names that sibling explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit gating condition: use only when the customer has explicitly confirmed they want to buy. That is a clear when-to-use rule, but no alternative tool or when-not-to-use path (e.g. first check stock via consultar_stock) is named.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.1.0
    • First observedconsultar_stock
    • First observedregistrar_venta

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

ActivityStale
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables interaction with Odoo ERP systems through 17+ business tools covering sales, purchasing, inventory, and accounting operations. Supports both Claude Desktop integration and web deployment with dual transport modes.
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Enables AI agents to interact with Odoo ERP as the authenticated user, with tools for discovery, planning, and mutations bounded by user permissions.
    36
    23 npm
    2
    Apache 2.0
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables LLM agents to interact with Odoo 17 ERP via external API, providing tools for inventory management, sales order processing, and customer management through natural language.
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to interact with Odoo ERP through natural language, providing tools and prompts for data operations and record management.
    MIT