Catalog / PNS AI Engine
Odoo module · pns_ai_mcp

PNS AI Engine

AI inside your Odoo, for consultants. The AI Engine runs in your client's Odoo. Providers, agents, knowledge and approvals are Odoo records, and the agents live in plain Odoo modules. Open source, nothing to license. Odoo 14.0 and 19.0.

Version 3.1.931Odoo 14.0 · 19.0Apache-2.028 contexts ↓10 example providers ↓

The pain

AI for your clients as just another module. Open source, no licences. Not a service on the side that you connect client by client. An AI that installs, updates and uninstalls like any other module, on the Odoo the client already runs. Here the agents live in plain Odoo modules.

01 — Tied to one provider? Add several, each for its job: conversation, audio, vision, video. If one fails, the next one answers.
02 — What if the AI writes in production? Its code runs in a read-only sandbox. To change anything it proposes a plan, and a person approves it.
03 — A closed prompt that does not know your client? Contexts are edited and extended inside Odoo: their vocabulary, their rules, their habits.

What you get

Agents that are Odoo

  • An agent is a record that a module brings with it. Install the module and the agent is there; uninstall it and it goes.
  • Each agent has its own list of providers, in the order you set.
  • Everything is in the client's database: no second copy somewhere else.

Providers for each task

  • OpenAI and any OpenAI-compatible endpoint, Anthropic, OpenRouter. A local model (Ollama, LM Studio) on the client's own server works too.
  • Roles per provider: conversation, audio, vision, video, page layout, turning a phrase into a skill's fields, yes-or-no decisions.
  • If one provider fails, the next one in the list answers.
  • The key stays in the client's database, never in the module.

A sandbox and a person

  • The code the AI writes reads in a read-only transaction, with the rights of the person who asked.
  • A change to Odoo is a plan that waits for a person to approve it in Odoo.
  • Every call is logged: each user sees their own activity, the administrator sees all of it.
  • Outside addresses only from a whitelist you keep.

Knowledge you own

  • Contexts: the client's words, rules and habits, edited in Odoo.
  • Skills: procedures written once and launched by name. Their tables and charts come from code, not from the AI.
  • Export the whole AI setup of one client to a file and import it at the next one.

An MCP server in the client's Odoo

  • Cursor and other MCP clients connect to /mcp with a personal key.
  • No admin password goes anywhere. Each key reads with its user's rights.
  • The client's own model does the thinking; Odoo answers its tool calls. No provider needed for that.

Nothing to license

  • Apache 2.0. No fee per user, per database or per year.
  • A plain Odoo module: no external server to run.
  • Same module at every client on a supported series.

Agents, roles and chains

What an agent is. Anything in Odoo that uses AI: a chat, a voice assistant, an MCP endpoint, a job that runs in the background. In the engine it is a record (AI Engine → Agents) with a code, its contexts, its skills and its list of providers.

The natural home of an agent is the Odoo module that needs it: install the module and the agent is there; uninstall it and it goes. One module can bring more than one. This engine brings two:

  • MCP Server (pns_ai_mcp) serves contexts and tools to an outside client such as Cursor. The model runs in that client; Odoo answers its tool calls.
  • AI Admin (pns_ai_admin) does the engine's own jobs: writing a skill, reading an API's documentation.

Chatboo brings the chat agent. An agent can also be created by hand in Odoo.

Two kinds. Inference: Odoo calls the model, through the agent's providers (a chat, a voice assistant). Endpoint: the model is outside and Odoo only serves it (MCP).

Roles. A role says what a provider is for. One provider can hold several.

Role What for
Conversation The ordinary text turn. A provider with no role does the same.
Audio A voice clip into text.
Vision A photo or a scanned page.
Video A video clip.
Decision A closed yes or no, with a confidence. Nobody holding it means no.
Design Lays out a page from the data of a turn. Nobody holding it: Conversation answers.
Parser A short phrase into fields, such as the arguments of a skill. A small model is enough. Nobody holding it: Conversation answers.

The chain. Each agent has its own providers, in the order you set. When a turn needs a role, the engine takes the providers of that agent that hold it, in that order. If one fails or stalls, the next one answers.

  • Each link has its own timeouts: seconds with no data, and seconds for a whole round.
  • A provider that holds only that role goes before the ones that do everything: it was chosen for that job.
  • If the chain has nobody for a role other than Conversation, the engine looks among every active provider.
  • No external failover ends the chain at that provider. Use it on a local model whose data must not go out.
  • A key that reached its monthly spend cap is skipped.
  • An agent with no chain uses every active provider.

For example: a large model for the conversation, a small one with only Parser, and a local model at the end of the chain as backup.

Included in the module

Read from the module’s own files, version 3.1.931.

Contexts

Code Kind Description
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

Example providers

In static/examples/providers/, with no key.

Name Protocol Model 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

What it does not do

  • No chat screen. The engine has no conversation window for users. That is Chatboo.
  • No writes on its own. The AI never changes Odoo by itself. Every change is a plan that a person approves in Odoo.
  • Four provider protocols. OpenAI-compatible, OpenRouter, Anthropic and SystemOne (yes-or-no decisions). An API that speaks none of them does not connect.
  • Two tested series. 14.0 and 19.0. From 15.0 to 18.0 you build the module yourself and try it first; 13.0 and older do not run (see Odoo versions).

Questions

Is it an MCP server? It includes one, in the same module. POST /mcp speaks Streamable HTTP. /mcp/sse with /mcp/message is for clients that only speak SSE. /mcp/<agent> ties the call to one agent. Three tools: relaxaicode (read-only Python against Odoo), safe_plan_request (propose a change) and safe_plan_status (see whether it was approved).

Where do the client's data go? To the providers you set up, and to the outside addresses and API servers you allow. When Cursor or another MCP client connects, its own model sees what the tools return.

Do I need Chatboo? No. The engine alone serves MCP clients. Chatboo adds the chat inside Odoo and brings the engine with it.

What is pns_base? The shared layer of the PNS modules: the same screens load on Odoo 14 and 19, plus small helpers (notifications, import and export, PDF). It installs as a dependency.

Getting started

  1. Install. Want the chat too? Install Chatboo instead; it brings this module. For the engine alone, clone the 14.0 or 19.0 branch of pns_base and pns_ai_mcp, add each clone to addons_path and install PNS AI Engine from Apps. On Odoo 14, first run pip install "openpyxl>=3.1.2".
  2. Become AI administrator. Settings → Users: give yourself AI Administrator. Log out and back in.
  3. Get your API key. AI Engine → Security → Users: your row, tick AI Conversational and press Generate API Key.
  4. Add a provider. AI Engine → Connections → Providers: protocol, endpoint, API key and model, with the Conversation role. Or start from a ready one: static/examples/providers/ has OpenRouter, OVH, OpenAI, Anthropic and Lemonade, with no key. Settings → AI Engine → Import configuration, pick the file, then type your key in each provider.

Done. Point Cursor (or any MCP client) at https://<your-odoo>/mcp with the key in the X-MCP-API-Key header. Example below, in Connecting an MCP client.

For IT staff

Connecting an MCP client

In Cursor, add the server to ~/.cursor/mcp.json (all projects) or to .cursor/mcp.json (one project). Other MCP clients take the same two things: the URL and the header.

{
  "mcpServers": {
    "my-client-odoo": {
      "url": "https://odoo.example.com/mcp",
      "headers": {
        "X-MCP-API-Key": "<your-api-key>"
      }
    }
  }
}

The key is the one from step 3 and reads with that user's rights. One key per person; do not commit the file with the key in it.

Repository layout

main is one tree for every series. A flat branch (14.0, 19.0) is that tree built for one series, ready for the addons path.

Path Contents
common/ Version-agnostic code, skills, contexts, data, Apps sheet
stacks/owl1/pns_ai_mcp/ Odoo 14
stacks/owl2/pns_ai_mcp/ Odoo 15 and later

The installer overlays common/ then the stack; a stack file with the same path wins. Edit agnostic files only in common/. Build a flat module with ./install_flat.sh 19.0 /path/to/addons. Domain UI helpers: utils/mcp_ui.py.

Python: do not add requests or reportlab (Odoo already ships them). pns_base needs no extra pip. On 14, use a current openpyxl (old wheels break with recent NumPy).

Odoo versions

Series Status
14.0, 19.0 Tested. Flat branch published.
15.0 – 18.0 Not tested, by people or by CI. No branch published.
13.0 and older Not supported.

15.0 to 18.0. There is no branch to clone. Build the flat module yourself from main, which has the code for every series (owl2 for 15.0 and later). Do the same for 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 /path/to/addons && cd ..
cd pns_ai_mcp && ./install_flat.sh 17.0 /path/to/addons

The script warns that the series is not tested. Nobody has run the engine on these series, so try it on a test database first and run the Odoo tests there (see Tests). If it works, or does not, tell us (CONTRIBUTING.md).

Odoo 13 has no OWL, which the AI chat is built on. Odoo 8 to 12 run on a Python older than 3.7, which the engine needs. The suite keeps two stacks (owl1 for 14.0, owl2 for 15.0 and later).

Tests

Where you are What runs
main python -m pytest unit_tests, with no Odoo. Then the Odoo tests, after ./install_flat.sh 19.0 <dir>.
A flat branch (14.0, 19.0) The Odoo tests in tests/. The unit tests live on main.
odoo-bin -d <db> -i pns_ai_mcp --test-enable --stop-after-init --test-tags /pns_ai_mcp

License, marks, training

Apache-2.0 — see LICENSE and NOTICE. PATANEGRA Soft™ and PAAP™ (Patanegra Application Agent Protocol, which this engine implements) are trademarks of PATANEGRA Soft SL; the license does not grant them. TDM / AI training is allowed (TDMRep tdm-reservation = 0).

Security: SECURITY.md. Bugs, ideas and questions: CONTRIBUTING.md.