Skip to main content
Glama
davidsandez

Mini-ERP MCP

by davidsandez

Mini-ERP MCP — учебный проект

Сервер Model Context Protocol (MCP), построенный на симулированном ERP-домене (товары, клиенты, продажи), чтобы от начала до конца понять, как языковая модель может обнаруживать и использовать внешние инструменты и данные стандартизированным образом, вместо индивидуальных интеграций под каждого LLM-провайдера.

Контекст: это личный проект технического исследования, а не production-продукт и не заказная разработка. ERP-домен намеренно минимален (JSON-файл в роли «базы данных») — цель не бизнес-логика, а понимание протокола.

Что делает

Симулирует поток входящего сообщения (например, из WhatsApp), которое приходит в систему типа ERP. Система собирает релевантный контекст для модели, предоставляет ей набор инструментов, и модель автономно решает, какое действие предпринять для ответа — включая действие с реальным эффектом (регистрацию продажи), требующее подтверждения человеком перед выполнением.

Related MCP server: OdooSurface MCP

Концепции, которые реализует этот проект

MCP определяет три примитива, и этот проект использует все три, с явным дизайн-решением для каждого:

Элемент

Тип

Зачем

catalogo://productos

Resource

Стабильные данные; их решает внедрить входной хост, не дожидаясь запроса модели.

cliente://{id}

Resource (URI с параметром)

Хост уже знает ID автора сообщения (пришёл бы из вебхука); собирает URI и внедряет его.

consultar_stock(producto_id)

Tool

Модель решает в процессе разговора, нужен ли ей этот инструмент.

registrar_venta(cliente_id, producto_id, cantidad)

деструктивный Tool

Действие с реальным эффектом; требует подтверждения человеком перед выполнением.

resumen_ventas(dias)

Prompt

Многоразовый шаблон для повторяющейся задачи.

Центральное различие, которое этот проект позволяет проверить на практике: в случае tool хост уступает модели решение о вызове; в случае resource хост сохраняет это решение за собой.

Архитектура

┌───────────────────────┐   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 в этом проекте выполняет как роль host, так и роль client MCP (в реальной интеграции клиент MCP обычно является модулем внутри хоста, а не отдельной частью).

Stack

  • mcp[cli] (FastMCP) — официальный Python SDK для MCP-серверов.

  • Hugging Face Inference Providers — модели open source через роутер, совместимый с OpenAI API (SDK openai, указывающий на https://router.huggingface.co/v1).

  • Anthropic SDK — альтернативная поддержка, поддерживаемая параллельно для сравнения одного и того же MCP-сервера, работающего с двумя разными провайдерами.

  • MCP Inspector — инструмент разработки для проверки сервера без реального клиента.

Как запустить локально

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

Проверить сервер изолированно (без LLM)

uv run mcp dev server.py

Откройте MCP Inspector в браузере, где можно вызывать tools и читать resources вручную.

Запустить полный поток (host + реальная LLM)

uv run host_demo.py

По умолчанию используется Hugging Face (procesar_mensaje_huggingface). Код для использования Anthropic (procesar_mensaje) остаётся доступным в том же файле, закомментирован в блоке if __name__ == "__main__":.

Переменные окружения

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

Чему я научился, создавая это

  • Реальный механизм обнаружения и вызова инструментов в MCP: модель никогда не говорит напрямую с MCP-сервером, именно хост выступает посредником при каждом list_tools() / call_tool() / read_resource().

  • Разница между нативным function calling каждого LLM-провайдера (форматы различаются между Anthropic и API, совместимыми с OpenAI) и тем, как MCP стандартизирует предоставление инструментов, не завися от этих частных форматов.

  • Не все модели, предоставляемые инференс-провайдером, надёжно поддерживают tool-calling, и как это проверить до интеграции.

  • Где находится ответственность за контроль человека над действиями с эффектом (это не то, что протокол решает автоматически, это дизайн-решение хоста).

  • Реальная переносимость: один и тот же server.py без изменений одинаково работает с двумя разными LLM-провайдерами.

Статус проекта

Учебный проект, завершённый и функциональный в рамках заданного объёма. В качестве возможного будущего расширения: подключить тот же сервер к Claude Desktop, чтобы проверить переносимость на реальном клиенте (не только в скрипте 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