El dolor
IA para tus clientes como un módulo más. Código abierto, sin licencias. No un servicio al lado que conectas cliente a cliente. Una IA que se instala, se actualiza y se desinstala como cualquier otro módulo, en el Odoo que el cliente ya tiene. Aquí los agentes viven en módulos normales de Odoo.
| 01 — ¿Atado a un proveedor? | Pon varios, cada uno para lo suyo: conversación, audio, imagen, vídeo. Si uno cae, responde el siguiente. |
| 02 — ¿Y si la IA escribe en producción? | Su código corre en una caja de arena de solo lectura. Para cambiar algo propone un plan, y lo aprueba una persona. |
| 03 — ¿Un prompt cerrado que no conoce a tu cliente? | Los contextos se editan y se amplían en el propio Odoo: su vocabulario, sus reglas, sus costumbres. |
Qué te llevas
Agentes que son Odoo
- Un agente es un registro que trae un módulo. Instalas el módulo y el agente está; lo desinstalas y se va.
- Cada agente tiene su propia lista de proveedores, en el orden que tú pongas.
- Todo está en la base de datos del cliente: no hay una segunda copia en otro sitio.
Proveedores para cada tarea
- OpenAI y cualquier servicio compatible con OpenAI, Anthropic, OpenRouter. También un modelo local (Ollama, LM Studio) en el propio servidor del cliente.
- Roles por proveedor: conversación, audio, imagen, vídeo, maquetar una página, pasar una frase a los campos de un skill, decisiones de sí o no.
- Si un proveedor falla, responde el siguiente de la lista.
- La clave se queda en la base de datos del cliente, nunca en el módulo.
Una caja de arena y una persona
- El código que escribe la IA lee en una transacción de solo lectura, con los permisos de quien preguntó.
- Un cambio en Odoo es un plan que espera a que una persona lo apruebe en Odoo.
- Cada llamada queda registrada: cada usuario ve su actividad, el administrador la de todos.
- Direcciones de fuera, solo las de una lista blanca que tú mantienes.
Conocimiento tuyo
- Contextos: las palabras, reglas y costumbres del cliente, editadas en Odoo.
- Skills: procedimientos que se escriben una vez y se lanzan por su nombre. Sus tablas y gráficos salen del código, no de la IA.
- Exportas toda la configuración de IA de un cliente a un fichero y la importas en el siguiente.
Un servidor MCP en el Odoo del cliente
- Cursor y otros clientes MCP se conectan a
/mcpcon una clave personal. - Ninguna contraseña de administrador sale de Odoo. Cada clave lee con los permisos de su usuario.
- Piensa el modelo del propio cliente MCP; Odoo responde a sus llamadas. Para eso no hace falta proveedor.
Nada que licenciar
- Apache 2.0. Sin cuota por usuario, por base de datos ni por año.
- Un módulo de Odoo normal: sin servidor externo que mantener.
- El mismo módulo en todos los clientes de una versión soportada.
Agentes, roles y cadenas
Qué es un agente. Cualquier cosa de Odoo que use IA: un chat, un asistente de voz, un punto MCP, un trabajo que corre por detrás. En el motor es un registro (Motor de IA → Agentes) con un código, sus contextos, sus skills y su lista de proveedores.
Lo natural es que un agente venga con el módulo de Odoo que lo necesita: instalas el módulo y el agente está; lo desinstalas y se va. Un módulo puede traer más de uno. Este motor trae dos:
- MCP Server (
pns_ai_mcp) da contextos y herramientas a un cliente de fuera, como Cursor. El modelo corre en ese cliente; Odoo contesta a sus llamadas. - AI Admin (
pns_ai_admin) hace los trabajos del propio motor: escribir un skill, leer la documentación de una API.
Chatboo trae el agente del chat. También se puede crear un agente a mano en Odoo.
Dos tipos. Inferencia: Odoo llama al modelo, con los proveedores del agente (un chat, un asistente de voz). Endpoint: el modelo está fuera y Odoo solo le sirve (MCP).
Roles. Un rol dice para qué sirve un proveedor. Un proveedor puede tener varios.
| Rol | Para qué |
|---|---|
| Conversación | El turno de texto normal. Un proveedor sin rol hace lo mismo. |
| Audio | Un audio de voz a texto. |
| Visión | Una foto o una página escaneada. |
| Vídeo | Un vídeo. |
| Decisión | Un sí o un no cerrado, con su confianza. Si nadie lo tiene, es no. |
| Diseño | Maqueta una página con los datos de un turno. Si nadie lo tiene, contesta Conversación. |
| Parser | Una frase corta a campos, como los argumentos de un skill. Basta un modelo pequeño. Si nadie lo tiene, contesta Conversación. |
La cadena. Cada agente tiene sus proveedores, en el orden que pongas. Cuando un turno necesita un rol, el motor toma los proveedores de ese agente que lo tienen, en ese orden. Si uno falla o se queda colgado, contesta el siguiente.
- Cada enlace tiene sus tiempos de espera: segundos sin datos y segundos para una ronda entera.
- Un proveedor que solo tiene ese rol va antes que los que hacen de todo: se eligió para eso.
- Si la cadena no tiene a nadie para un rol que no sea Conversación, el motor busca entre todos los proveedores activos.
- Sin failover externo corta la cadena en ese proveedor. Sirve para un modelo local cuyos datos no deben salir.
- Una clave que llegó a su tope de gasto del mes se salta.
- Un agente sin cadena usa todos los proveedores activos.
Por ejemplo: un modelo grande para la conversación, uno pequeño solo con Parser, y un modelo local al final de la cadena como respaldo.
Qué trae el módulo
Leído de los propios ficheros del módulo, versión 3.1.931. Las descripciones van en inglés, como en el módulo.
Contextos
| Código | Tipo | Descripción |
|---|---|---|
system_prompt |
core | Odoo Autonomous Agent - Strict Tool Execution Protocol |
account |
domain | Odoo Accounting module knowledge - invoices, refunds, payments |
business_documents |
domain | Read, create and modify business documents (quotations, orders, invoices, pickings) |
cost_accounting |
domain | Analytic accounting: correct ORM fields and group-by patterns |
dates |
domain | ORM date/period composition: calendar Date/Datetime vs identifier text |
email |
domain | Email in Odoo — mail.mail (mailbox) vs mail.notification (delivery) |
fetch_url_recipe |
domain | How to fetch external data: use the fetch_url safe operation, never import urllib/requests in relaxaicode |
filters |
domain | ir.filters knowledge - creating and managing filters |
geography |
domain | Geographic knowledge - country/province/city model, administrative levels (region is NOT a state), resolution funnel, and HONEST handling of missing geo data (report, never invent) |
hr_contracts |
domain | [RECOMMENDED] Employment contracts: employee master first, hr.contract only if installed, never contract.contract |
hr_payroll |
domain | [RECOMMENDED] Payroll: accounting first, payslip fallback, honest empty state |
hr_payroll_accounting_present |
domain | Payroll accounting: partner totals → hr.employee match + structured rows (all locales) |
hr_payroll_de_DE |
domain | Payroll Germany - account 4100 SKR first, payslip fallback |
hr_payroll_en_US |
domain | Payroll US - accounts 5100-5300 first, payslip fallback |
hr_payroll_es_AR |
domain | Payroll Argentina - account 411 first, payslip fallback |
hr_payroll_es_ES |
domain | Payroll Spain - accounting 640/4751/476 first, payslip fallback [RECOMMENDED] |
hr_payroll_es_MX |
domain | Payroll Mexico - account 5100 first, payslip fallback |
hr_payroll_fr_FR |
domain | Payroll France - account 421 first, payslip fallback |
hr_payroll_it_IT |
domain | Payroll Italy - account 2120 first, payslip fallback |
hr_payroll_pt_BR |
domain | Payroll Brazil - accounts 311/3.1.1 first, payslip fallback |
invoice_concepts |
domain | Invoice field mappings, calculations and coding conventions |
partners |
domain | res.partner knowledge - customers, suppliers, location search |
sale_order_duplicate_workflow |
domain | How to duplicate a sale order and change its lines in two separate steps. Includes how to get the URL of the new order. |
self_mcp |
domain | MCP endpoint identity — knowledge layer for an external LLM |
trial_balance |
domain | Trial balance - accounts with debits, credits and balance by date range (generic fallback) |
trial_balance_es_ES |
domain | Trial balance - accounts with debits, credits and balance by date range (Spain) |
corporate_terms |
locale | English corporate terminology mapping - Odoo field and model translations (factory fallback) |
corporate_terms_es_ES |
locale | Spanish corporate terminology mapping - Odoo field and model translations |
Proveedores de ejemplo
En static/examples/providers/, sin clave.
| Nombre | Protocolo | Modelo | Roles |
|---|---|---|---|
| Anthropic | anthropic |
claude-opus-4-8 |
|
| Lemonade | openai |
gpt-oss-20b-NPU |
|
| OpenAI | openai |
gpt-4-turbo |
|
| OpenRouter - Conversation | openrouter |
anthropic/claude-opus-5.5 |
conversation, vision, design |
| OpenRouter - Vision | openrouter |
~anthropic/claude-sonnet-latest |
vision |
| OpenRouter - Design | openrouter |
~anthropic/claude-opus-latest |
design |
| OpenRouter - Audio | openrouter |
google/gemini-3.5-transcribe |
audio |
| OpenRouter - Parser | openrouter |
anthropic/claude-haiku-5.5 |
parser |
| OpenRouter - Decision | systemone |
typesafe/jev-1.13 |
decision |
| OVH - Backup | openai |
Qwen3.8-27B |
conversation, vision |
Qué no hace
- Sin pantalla de chat. El motor no trae ventana de conversación para los usuarios. Eso es Chatboo.
- No escribe por su cuenta. La IA nunca cambia Odoo sola. Cada cambio es un plan que aprueba una persona en Odoo.
- Cuatro protocolos de proveedor. Compatible con OpenAI, OpenRouter, Anthropic y SystemOne (decisiones de sí o no). Una API que no hable ninguno no se conecta.
- Dos versiones probadas. 14.0 y 19.0. De la 15.0 a la 18.0 construyes tú el módulo y lo pruebas antes; la 13.0 y anteriores no funcionan (ver Versiones de Odoo).
Preguntas
¿Es un servidor MCP? Lo incluye, en el mismo módulo. POST /mcp habla Streamable HTTP. /mcp/sse con /mcp/message es para clientes que solo hablan SSE. /mcp/<agente> ata la llamada a un agente. Tres herramientas: relaxaicode (Python de solo lectura contra Odoo), safe_plan_request (proponer un cambio) y safe_plan_status (ver si se aprobó).
¿Adónde van los datos del cliente? A los proveedores que configures, y a las direcciones de fuera y servidores de API que permitas. Cuando se conecta Cursor u otro cliente MCP, su propio modelo ve lo que devuelven las herramientas.
¿Necesito Chatboo? No. El motor solo ya sirve a clientes MCP. Chatboo añade el chat dentro de Odoo y trae el motor con él.
¿Qué es pns_base? La capa común de los módulos PNS: las mismas pantallas cargan en Odoo 14 y 19, y trae utilidades pequeñas (avisos, importar y exportar, PDF). Se instala como dependencia.
Puesta en marcha
- Instala. ¿Quieres también el chat? Instala Chatboo; trae este módulo. Solo el motor: clona la rama
14.0o19.0depns_basey depns_ai_mcp, añade cada clon aladdons_pathe instala PNS AI Engine desde Aplicaciones. En Odoo 14, antes,pip install "openpyxl>=3.1.2". - Hazte administrador de IA. Ajustes → Usuarios: date IA Administrador. Sal y vuelve a entrar.
- Saca tu clave API. Motor de IA → Seguridad → Usuarios: tu fila, marca IA Conversacional y pulsa Generar Clave API.
- Pon un proveedor. Motor de IA → Conexiones → Proveedores: protocolo, dirección, clave API y modelo, con el rol Conversación.
O parte de uno hecho: en
static/examples/providers/están OpenRouter, OVH, OpenAI, Anthropic y Lemonade, sin clave. Ajustes → Motor de IA → Importar configuración, eliges el fichero y escribes tu clave en cada proveedor.
Listo. Apunta Cursor (o cualquier cliente MCP) a https://<tu-odoo>/mcp con la clave en la cabecera X-MCP-API-Key. Ejemplo más abajo, en Conectar un cliente MCP.
Para el equipo de IT
Conectar un cliente MCP
En Cursor, añade el servidor a ~/.cursor/mcp.json (todos los proyectos) o a
.cursor/mcp.json (un proyecto). Los demás clientes MCP piden lo mismo: la
dirección y la cabecera.
{
"mcpServers": {
"odoo-de-mi-cliente": {
"url": "https://odoo.example.com/mcp",
"headers": {
"X-MCP-API-Key": "<tu-clave-api>"
}
}
}
}
La clave es la del paso 3 y lee con los permisos de ese usuario. Una clave por persona; no subas a git el fichero con la clave dentro.
Estructura del repositorio
main es un solo árbol para todas las versiones. Una rama plana (14.0, 19.0) es ese árbol construido para una versión, listo para el addons path.
| Ruta | Contenido |
|---|---|
common/ |
Código común a todas las versiones, skills, contextos, datos, ficha de Apps |
stacks/owl1/pns_ai_mcp/ |
Odoo 14 |
stacks/owl2/pns_ai_mcp/ |
Odoo 15 y siguientes |
El instalador copia common/ y encima la pila; si un fichero está en los dos,
gana el de la pila. Lo común se edita solo en common/. El módulo plano de
una versión se construye con ./install_flat.sh 19.0 /ruta/a/addons.
Utilidades de interfaz del dominio: utils/mcp_ui.py.
Python: no añadas requests ni reportlab (ya vienen con Odoo). pns_base
no necesita nada de pip. En la 14, usa un openpyxl reciente (los antiguos
fallan con NumPy moderno).
Versiones de Odoo
| Versión | Estado |
|---|---|
| 14.0, 19.0 | Probadas. Rama plana publicada. |
| 15.0 – 18.0 | Sin probar, ni por personas ni por el CI. Sin rama publicada. |
| 13.0 y anteriores | No soportadas. |
De la 15.0 a la 18.0. No hay rama que clonar. Construye tú el módulo
plano desde main, que tiene el código de todas las versiones (owl2 para
la 15.0 y siguientes). Lo mismo con pns_base:
git clone https://github.com/patanegra-soft/pns_base.git
git clone https://github.com/patanegra-soft/pns_ai_mcp.git
cd pns_base && ./install_flat.sh 17.0 /ruta/a/addons && cd ..
cd pns_ai_mcp && ./install_flat.sh 17.0 /ruta/a/addons
El script avisa de que esa versión no está probada. Nadie ha usado el motor en estas versiones: pruébalo antes en una base de pruebas y pasa ahí los tests de Odoo (ver Tests). Si funciona, o si no, cuéntanoslo (CONTRIBUTING.md).
Odoo 13 no tiene OWL, sobre el que está hecho el chat. De Odoo 8 a 12 usan un
Python anterior al 3.7, que es el que necesita el motor. La suite mantiene dos
pilas (owl1 para la 14.0 y owl2 para la 15.0 y siguientes).
Tests
| Dónde estás | Qué se ejecuta |
|---|---|
main |
python -m pytest unit_tests, sin Odoo. Después los tests de Odoo, tras ./install_flat.sh 19.0 <dir>. |
Una rama plana (14.0, 19.0) |
Los tests de Odoo en tests/. Los unitarios viven en main. |
odoo-bin -d <db> -i pns_ai_mcp --test-enable --stop-after-init --test-tags /pns_ai_mcp
Licencia, marcas, entrenamiento
Apache-2.0: ver LICENSE y NOTICE.
PATANEGRA Soft™ y PAAP™ (Patanegra Application Agent Protocol, que implementa
este motor) son marcas de PATANEGRA Soft SL; la licencia no las concede. Se
permite la minería de textos y el entrenamiento de IA (TDMRep tdm-reservation = 0).
Seguridad: SECURITY.md. Errores, ideas y preguntas: CONTRIBUTING.md.
