Skip to main content
Glama
josefdc

mcp_odoo_fresh

by josefdc

Agente Inteligente QuindíColor: Odoo + MCP + OpenAI

Proyecto Hackathon 🚀

Este proyecto, desarrollado para una hackathon, demuestra la creación de un agente conversacional inteligente capaz de interactuar con un sistema ERP Odoo (específicamente una instancia alojada en Odoo.sh) para realizar tareas clave del flujo de ventas de la empresa ficticia "QuindíColor".

El agente utiliza el SDK de OpenAI Agents para su lógica y razonamiento, se comunica mediante voz o texto a través de interfaces web interactivas creadas con Gradio, y, de manera crucial, accede a las funcionalidades de Odoo de forma estandarizada y desacoplada gracias al Model Context Protocol (MCP).

Objetivo Principal: Simplificar y agilizar operaciones comunes de ventas en Odoo (buscar clientes/productos, crear y confirmar cotizaciones) mediante una interfaz conversacional avanzada (texto y voz), resaltando la flexibilidad que aporta MCP.

Related MCP server: Odoo MCP Server

Tecnologías Utilizadas 🛠️

  • Python: Lenguaje principal de programación (v3.10+ recomendado).

  • Odoo (Odoo.sh): Sistema ERP de destino donde residen los datos y se ejecutan las acciones comerciales.

  • Model Context Protocol (MCP): Protocolo estándar utilizado para la comunicación entre el agente inteligente y el servidor que expone las capacidades de Odoo. Se implementa un servidor MCP personalizado.

    • Se usa el SDK mcp para Python (pip install mcp).

  • OpenAI Agents SDK: Framework para construir la lógica del agente, manejar el flujo conversacional y la integración con herramientas MCP (pip install openai-agents).

  • OpenAI API:

    • LLM (GPT-4o): Para el razonamiento del agente, comprensión del lenguaje natural y selección de herramientas.

    • Whisper: Para la transcripción de Voz a Texto (STT).

    • TTS API: Para la síntesis de Texto a Voz (TTS).

    • Se requiere la librería openai (pip install openai).

  • Gradio: Framework para crear rápidamente las interfaces web interactivas de chat y voz (pip install gradio).

  • Odoo API (XML-RPC): Método de comunicación específico utilizado por nuestro servidor MCP para interactuar con la API externa de Odoo.

  • uv: Gestor de paquetes y entornos virtuales Python (pip install uv).

  • python-dotenv: Para gestionar credenciales y configuraciones de entorno de forma segura.

Arquitectura del Sistema 🏛️

El sistema conecta al usuario con Odoo a través de varias capas, donde MCP juega un papel central como puente estandarizado.

Flujo Típico:

  1. El Usuario interactúa (voz/texto) con la Interfaz Gradio.

  2. La App Backend (Gradio + Python) recibe el input. Si es voz, llama a la API Whisper (STT) de OpenAI para transcribir.

  3. El texto (transcrito o escrito) se pasa a la Lógica del Agente (OpenAI Agents SDK) junto con el historial.

  4. El Agente llama al LLM (GPT-4o) de OpenAI con el prompt, historial y la lista de herramientas disponibles (descubiertas vía MCP).

  5. El LLM decide si responder directamente o usar una herramienta.

  6. Si decide usar una herramienta Odoo (ej. buscar_cliente):

    • La Lógica del Agente instruye al Conector MCP (MCPServerStdio).

    • El Conector MCP envía una petición tools/call usando el Protocolo MCP a nuestro Servidor MCP Odoo (mcp_odoo_server.py), que corre como proceso hijo.

    • Nuestro Servidor MCP traduce la petición MCP a una llamada XML-RPC a la API de Odoo.sh.

    • Odoo.sh procesa la solicitud y devuelve el resultado vía XML-RPC.

    • Nuestro Servidor MCP recibe la respuesta de Odoo y la formatea como un Resultado MCP.

    • El Conector MCP recibe el resultado y lo devuelve a la Lógica del Agente.

    • El Agente envía el resultado de la herramienta de nuevo al LLM para generar la respuesta final al usuario.

  7. La Lógica del Agente recibe la respuesta final en texto del LLM.

  8. La App Backend llama a la API TTS de OpenAI para convertir el texto en audio (si es la interfaz de voz).

  9. La App Backend actualiza la Interfaz Gradio mostrando el texto en el chat y/o reproduciendo el audio.

  10. El Usuario ve/escucha la respuesta.

Importancia del Model Context Protocol (MCP):

MCP es la clave para la integración flexible y modular en este proyecto:

  • Estandarización: Define un contrato claro (list_tools, call_tool) entre el agente y las capacidades de Odoo. El agente no necesita saber cómo hablar XML-RPC con Odoo, solo cómo usar las herramientas MCP.

  • Desacoplamiento: La inteligencia del agente (OpenAI SDK) está separada de la implementación específica de Odoo (nuestro servidor MCP). Podemos cambiar el agente o el servidor Odoo (ej. a otra versión de Odoo o API) modificando solo una parte, siempre que se respete el contrato MCP.

  • Reusabilidad: Nuestro mcp_odoo_server.py podría ser utilizado por cualquier otro sistema o agente que entienda MCP, no solo por el SDK de OpenAI Agents.

  • Modularidad: Permite exponer funcionalidades de sistemas complejos (como Odoo) de forma granular y controlada (herramienta por herramienta).

MCP actúa como una capa de abstracción esencial, permitiendo que sistemas inteligentes interactúen con herramientas y datos de forma estandarizada y segura.

Estructura del Proyecto 📂

mcp_odoo_fresh/
├── .venv/                      # Entorno virtual Python (creado por uv)
├── agente_quindicolor_openai.py # Lógica del Agente OpenAI, configuración MCP
├── app_gradio_texto.py        # Interfaz Gradio para chat de texto
├── app_gradio_voz.py          # Interfaz Gradio para chat de voz (STT/TTS)
├── mcp_odoo_server.py         # Servidor MCP -> Odoo (FastMCP, XML-RPC)
├── mcp_odoo_debug.log         # Archivo de log del servidor MCP Odoo
├── .env                       # Archivo para credenciales (¡IGNORADO POR GIT!)
├── .env.example               # Archivo de ejemplo para .env
├── pyproject.toml             # Configuración del proyecto (usado por uv)
├── README.md                  # Este archivo
└── uv.lock                    # Dependencias bloqueadas por uv

Instrucciones de Configuración 🛠️

  1. Clonar/Descargar: Obtén los archivos del proyecto.

  2. Prerrequisitos:

    • Python 3.10 o superior.

    • uv instalado (ver guía oficial).

    • Acceso a una instancia de Odoo (preferiblemente Odoo.sh) con un usuario y Clave API.

    • Una Clave API de OpenAI con crédito/cuota suficiente.

  3. Crear y Activar Entorno Virtual:

    cd ruta/a/mcp_odoo_fresh
    uv venv
    source .venv/bin/activate # Linux/macOS
    # .venv\Scripts\activate # Windows
  4. Instalar Dependencias:

    uv pip install openai-agents openai python-dotenv gradio mcp

    (Puedes también crear un requirements.txt con uv pip freeze > requirements.txt y luego usar uv pip install -r requirements.txt).

  5. Configurar Credenciales:

    • Renombra o copia .env.example a .env.

    • Edita el archivo .env y rellena TODAS las variables con tus valores reales:

      # === Credenciales Odoo.sh ===
      ODOO_URL=https://tu-instancia.odoo.com
      ODOO_DB=nombre_tu_base_de_datos
      ODOO_USER=tu_login_odoo
      ODOO_PASSWORD=TU_CLAVE_API_DE_ODOO_SH # ¡Generada en Odoo.sh!
      
      # === Credenciales OpenAI ===
      OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxxxxxxxxx

Ejecución de la Demo ▶️

Puedes ejecutar la interfaz de texto o la de voz de forma independiente. Ambas iniciarán el servidor MCP Odoo necesario en segundo plano.

Opción 1: Interfaz de Texto

python app_gradio_texto.py

Abre la URL que indica la consola (normalmente http://127.0.0.1:7861).

Opción 2: Interfaz de Voz

python app_gradio_voz.py

Abre la URL que indica la consola (normalmente http://127.0.0.1:7860). Recuerda dar permiso al navegador para usar el micrófono.

Guiones de Prueba

Puedes usar los guiones proporcionados anteriormente (adaptados a tus productos/clientes) para probar el flujo completo en cualquiera de las interfaces.

Troubleshooting Básico 🐛

  • Error Odoo: Revisa URL, DB, User, Password (API Key!) en .env. Mira mcp_odoo_debug.log.

  • Error OpenAI: Verifica tu API Key y cuota en platform.openai.com.

  • Error Gradio (Micrófono): Revisa permisos del navegador/OS. Usa URL local. Prueba otro navegador.

  • Otros: Revisa los logs en la terminal donde ejecutas la app Gradio.

Ideas Futuras 💡

  • Implementar la herramienta crear_factura_desde_pedido (requiere investigar método Odoo no privado).

  • Añadir más herramientas (ej. consultar stock, estado pedido).

  • Mejorar el manejo de errores y la robustez.

  • Implementar streaming de texto en la interfaz de voz.

  • Optimizar el arranque/conexión del servidor MCP.

  • Usar un gr.Chatbot visible en la app de voz para mostrar historial

este proyecto nos hizo ganadores jeje

Available Tools

5 tools
buscar_clienteA

Busca clientes en Odoo cuyos nombres coincidan (parcialmente, sin importar mayúsculas/minúsculas) con el nombre proporcionado. Devuelve ID, Nombre, Email, Teléfono (máx 5).

ParametersJSON Schema
NameRequiredDescriptionDefault
nombre_clienteYes

TDQS

A4/5.0
Behavior4/5

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

With no annotations provided, the description carries the full burden. It discloses the partial match, case-insensitivity, return fields, and the 5-result limit. It does not mention error handling or no-match behavior, but for a read-only search this is sufficient.

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

Conciseness5/5

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

The description is a single, efficient sentence that front-loads the action and includes key details (matching behavior, return fields, limit) without any unnecessary wording.

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?

For a low-complexity tool with one parameter and no output schema, the description covers the essential information: what it searches, how it matches, and what it returns. It lacks explicit error or empty-result behavior, but that is not critical for a simple search.

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?

The schema has only one parameter (nombre_cliente) with 0% description coverage. The description adds that the provided name is used for partial, case-insensitive matching, which clarifies the parameter's semantics beyond the raw name. However, it does not provide format or length constraints.

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

Purpose5/5

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

The description clearly states the verb (busca), the resource (clientes en Odoo), the matching behavior (parcial, case-insensitive), and the return fields with a max limit. This distinguishes it from sibling tools like buscar_producto which search a different entity.

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

Usage Guidelines3/5

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

The description implies its usage for searching customers by name, but it does not explicitly mention when to use it over alternatives or any exclusion criteria. For a simple search tool, this is adequate but not explicit.

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

buscar_productoA

Busca productos en Odoo cuyos nombres coincidan (parcialmente, sin importar mayúsculas/minúsculas) con el nombre proporcionado. Devuelve ID, Nombre, Código, Precio, Cant. Disponible (máx 5).

ParametersJSON Schema
NameRequiredDescriptionDefault
nombre_productoYes

TDQS

A4.2/5.0
Behavior4/5

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

With no annotations, the description carries the behavioral burden. It discloses partial matching, case-insensitivity, and a maximum of 5 results, which is useful. However, it does not mention read-only nature or error handling, though these are typical for search tools.

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

Conciseness5/5

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

Two sentences with no fluff: the first states the purpose and matching rules, the second lists return fields and the result cap. Information is front-loaded and efficient.

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?

For a simple one-parameter search with no output schema, the description covers the essential aspects: what it searches, how it matches, and what it returns. It does not specify empty-result behavior, but that is a minor gap for a search tool.

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

Parameters4/5

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

The schema has no description for 'nombre_producto', so the description compensates by explaining the parameter's role as the name to match and how matching works (partial, case-insensitive). This adds meaning beyond the schema.

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

Purpose5/5

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

The description clearly states the verb 'Busca' (searches), the resource 'productos en Odoo', and the matching criteria (partial, case-insensitive). It distinguishes from siblings like listar_productos by focusing on name-based search and explicitly lists returned fields.

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

Usage Guidelines3/5

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

The description implies usage when a product name is known, but does not explicitly mention when not to use it or compare it to alternatives like listar_productos. The context is clear but lacks explicit routing guidance.

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

confirmar_cotizacionA

Confirma una cotización (Orden de Venta) en Odoo usando su ID. Verifica el estado antes y después. La cotización debe estar en 'draft' o 'sent'.

ParametersJSON Schema
NameRequiredDescriptionDefault
cotizacion_idYes

TDQS

A3.7/5.0
Behavior3/5

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

With no annotations provided, the description carries the full burden. It discloses that the tool verifies state before and after and requires 'draft' or 'sent' status, which is useful. However, it doesn't disclose what happens on failure, whether the operation is reversible, or what the response contains. The state verification detail adds value but the mutation's side effects are not fully described.

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

Conciseness5/5

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

Three short sentences with no filler. The core action is front-loaded, followed by the state precondition and verification behavior. Every sentence earns its place.

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?

For a single-parameter tool with no output schema, the description covers the essential action and precondition. However, it lacks information about error handling, what the confirmation result looks like, and any side effects (e.g., sending the order, locking it). Given no annotations and no output schema, a bit more behavioral context would make it complete.

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 description coverage is 0%, so the description must compensate. It explains that cotizacion_id is the ID of the quote to confirm, which adds meaning beyond the bare schema. However, it doesn't specify the ID format beyond being an integer, which the schema already provides, so the added value is modest.

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?

The description clearly states the action ('Confirma una cotización (Orden de Venta) en Odoo usando su ID') and identifies the resource (cotización/Orden de Venta). It distinguishes itself from siblings like crear_cotizacion by focusing on confirmation, though it doesn't explicitly name a sibling alternative.

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 description provides clear context: the cotización must be in 'draft' or 'sent' state, and it verifies state before and after. This implies when to use the tool (when confirming a quote) and gives a precondition, though it doesn't explicitly state when not to use it or name alternatives.

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

crear_cotizacionA
Crea una nueva cotización (Orden de Venta) en Odoo para un cliente específico con las líneas de producto dadas.
Args: cliente_id (ID del cliente), lineas (Lista de dicts {'product_id': ID_PROD, 'product_uom_qty': CANTIDAD}).
Returns: ID de la cotización creada o mensaje de error.
Ejemplo lineas: [{'product_id': 40, 'product_uom_qty': 2}, {'product_id': 35, 'product_uom_qty': 1}]
ParametersJSON Schema
NameRequiredDescriptionDefault
lineasYes
cliente_idYes

TDQS

A4.3/5.0
Behavior3/5

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

With no annotations, the description carries the behavioral burden. It does disclose that this is a creation operation and that it returns either the new quote ID or an error message. However, it omits details about authorization requirements, side effects, idempotency, or failure modes beyond the generic 'mensaje de error', leaving meaningful gaps for a mutation tool.

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

Conciseness5/5

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

The description is compact, well-organized, and front-loaded with the core purpose. The Args, Returns, and example sections each earn their place, adding concrete usage detail without unnecessary verbosity.

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?

For a two-parameter creation tool with no annotations and no output schema, the description covers the essential context: what the tool does, the exact parameter shapes, and the return value. It falls slightly short of a 5 only because it does not mention preconditions such as the client and products needing to exist, or what error conditions can be expected.

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

Parameters5/5

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

Schema description coverage is 0%, so the description must compensate, and it does thoroughly. It explains cliente_id as the client ID and lineas as a list of dicts with product_id and product_uom_qty keys, and it provides a concrete example. This tightens the loose schema, where lineas items have additionalProperties: true.

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

Purpose5/5

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

The description opens with a specific verb and resource: 'Crea una nueva cotización (Orden de Venta) en Odoo para un cliente específico con las líneas de producto dadas.' This clearly states what is created, in which system, and for whom. It is also easy to distinguish from the sibling tools, which are search/listing/confirmation operations rather than creation.

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 description makes the invocation context obvious: an agent should call this tool when it needs to create a new sales quotation with specified product lines for a client. It does not explicitly name alternatives or list when not to use it, but the creation-oriented wording is unambiguous enough to avoid confusion with the search/confirm siblings.

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

listar_productosA
Lista los primeros 20 productos vendibles disponibles en Odoo.

Args:
    None

Returns:
    Una cadena de texto formateada con la lista de productos (ID, Nombre, Código, Precio)
    o un mensaje de error.
ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.8/5.0
Behavior3/5

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

With no annotations, the description carries the full burden and does disclose the return shape: a formatted string with ID, name, code, and price, or an error message. However, it does not mention ordering, pagination, read-only status, or side effects, leaving moderate gaps.

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

Conciseness5/5

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

The description is compact and well-structured, with the main action front-loaded and an Args/Returns section. Every sentence adds meaningful information, aside from the minor redundancy of 'Args: None' with the schema.

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?

For a simple 0-parameter listing tool with no output schema, the description is nearly complete: it identifies the resource and specifies the returned fields and error fallback. It omits details about pagination beyond the first 20, but that is a minor gap for this complexity level.

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

Parameters4/5

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

The tool has zero parameters, so the 0-parameter baseline of 4 applies. The description reinforces this with 'Args: None', and the input schema already fully covers the empty parameter set.

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

Purpose5/5

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

States a specific verb and resource: listing the first 20 sellable products in Odoo, with a clear scope. This distinguishes it from the sibling buscar_producto, which searches for a specific product rather than listing a range.

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

Usage Guidelines2/5

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

No guidance is given about when to use this tool versus buscar_producto or the other siblings. The only signal is implicit, and the description never states exclusions or the selection rule that would route an agent to this tool.

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. 5 tool updatesv0.1.0
    • First observedbuscar_cliente
    • First observedbuscar_producto
    • First observedconfirmar_cotizacion
    • First observedcrear_cotizacion
    • First observedlistar_productos

TDQS

A4.1/5.0

Scored across 5 tools

Disambiguation4/5

buscar_cliente and buscar_producto are clearly separated by entity, and crear_cotizacion/confirmar_cotizacion distinguish creation from state transition. There is minor overlap between buscar_producto and listar_productos, but the descriptions make the search-vs-list distinction explicit.

Naming Consistency5/5

All tool names follow a consistent Spanish verb_noun pattern: buscar_cliente, buscar_producto, crear_cotizacion, confirmar_cotizacion, listar_productos. No mixed casings or inconsistent verb styles are present.

Tool Count5/5

Five tools tightly cover the core quotation workflow: find customer, find product, list products, create quote, and confirm quote. The count is appropriate and nothing feels redundant or out of scope.

Completeness4/5

The create-and-confirm quotation lifecycle is covered, and customer/product search avoids dead ends. Missing operations like updating, canceling, or viewing quotation details are minor gaps for this focused server, though they would matter for broader sales-order management.

Maintenance

ActivityInactive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables LLMs to interact with Odoo ERP systems through MCP protocol. Supports multi-server architecture with secure authentication, allowing natural language access to Odoo data, resources, and business operations.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    Bridges AI agents to Odoo ERP via MCP, enabling CRUD operations, model introspection, and report generation with secure API key and connection management through a web admin UI.
    4
    GPL 3.0
  • F
    license
    Not graded
    quality
    D
    maintenance
    Full-featured MCP connector for Odoo ERP via XML-RPC, enabling natural language interaction with CRM, sales, inventory, and other Odoo modules.
    -