Odoo MCP服务器

Odoo MCP Server 是一个连接AI代理到Odoo ERP系统的MCP服务,提供13个安全的数据访问与管理工具,适用于Claude、n8n等AI工具自动化企业业务流程的场景。

  • by mah007
  • Python
  • 混合部署
  • 4 星标
  • 更新于
在 GitHub 查看 mah007/odooMCP

使用说明

Odoo MCP Server (Enhanced for AI Agents)


Introduction

Odoo MCP Server is a Model Context Protocol server that connects AI agents (Claude Code, Claude, ChatGPT, n8n, …) to any Odoo database, from Odoo 10 to Odoo 20.

It speaks XML-RPC to Odoo 18 and older and Odoo's JSON-2 API to Odoo 19 and newer, picking the right one automatically. XML-RPC is deprecated in Odoo 19 and will be removed in Odoo 22. Responses are kept small and readable for language models: records are paginated, and binaries are summarised instead of sent. Long text is cut, Arabic and other non-Latin text stays as plain UTF-8, and metadata comes back as compact tables.

Odoo connections, API keys and admin accounts are all managed in a built-in web admin UI. Nothing sensitive goes in environment files.

Key features

  • Dual transport: auto-detects the Odoo version and uses JSON-2 on 19+ and XML-RPC on 18 and older; it can be forced per connection.
  • AI-friendly output: small defaults, paginated search, binary summaries, a 100 KB response guard and compact JSON.
  • 13 tools with MCP annotations (read-only / destructive hints), including get_context, aggregate and name_search.
  • Context-aware: the user, time zone and language, plus multi-company and branch control on every tool.
  • Standards-compliant MCP: protocol versions 2024-11-05 to 2025-11-25, tool errors returned as isError results, structuredContent, batching and notifications.
  • Web admin UI: connections with a step-by-step connection test, API keys with read-only or read-write scope, and a dashboard with per-tool response sizes.
  • Secure by design: Odoo credentials are Fernet-encrypted at rest; every MCP request needs an API key; browser origins are blocked unless allowed.
  • One container: two ports, one docker compose up, SQLite storage.

Architecture

The server runs as a single Docker container exposing two ports:

Port (default) Service Purpose
8000 MCP service POST /mcp endpoint for AI agents: checks the API key, calls Odoo, logs each call
8001 Controller / admin UI React SPA and REST API for connections, keys and admins

Both services share one SQLite database (data/mcp.db).

AI agent (Claude Code / n8n / …)
      │  MCP over HTTP (JSON-RPC 2.0), X-API-Key or Authorization: Bearer
      ▼
MCP service :8000 ── protocol.py → tools.py → shaping.py
      │                       │
      │                 odoo_client.py  (version detection, caches, context)
      │                  ┌────┴─────┐
      ▼                  ▼          ▼
   SQLite          JSON-2 (19+)   XML-RPC (≤ 18)
                          └────┬────┘
                               ▼
                         Odoo instance

Transports

Each connection has an API mode:

API mode Behaviour
auto (default) Detects the Odoo version. 19 and newer → JSON-2; 18 and older → XML-RPC (saas 18.x included).
xmlrpc Always XML-RPC. Works on Odoo 10–21; on 19+ Odoo logs a deprecation warning for every call.
json2 Always JSON-2. Fails with a clear message on Odoo 18 and older.

JSON-2 needs an Odoo API key with RPC scope; a password is not accepted. To create one in Odoo, go to avatar → Preferences → Account Security → New API Key and choose scope RPC. (In Odoo 20, keys created for Odoo's own MCP have scope "mcp" and do not work here.) The same key also works for XML-RPC.

If a connection on Odoo 19+ stores a password, auto keeps it on XML-RPC so existing set-ups keep working. get_context and the connection card then suggest switching to an API key.

Version detection tries GET /web/version (Odoo 19+), then /web/webclient/version_info, then XML-RPC version(). The connection's Test button shows each step: detection → transport → authentication → reading the user.


Available MCP tools

Tool What it does Read-only Destructive
get_context Odoo version and transport, user, language, time zone, active company, all companies and branches with ids, installed apps. Call it first. ✔
list_models Models as a model|name|explanation table; search filters by name ✔
get_model_fields Fields as a pipe table (type, required, readonly, stored, related model, selection values…); filter="required" before creating ✔
model_info Purpose, field and record counts, key fields and, on Odoo 19+, the model's public methods with signatures ✔
search_records Paginated search: {records, total, next_offset}; returns display_name unless you pass fields ✔
search_count Count records matching a domain ✔
get_record Read records by id (max 100); compact default field set; reports missing_ids ✔
name_search Turn a name into ids (before setting many2one values) ✔
aggregate Group by fields (and date parts) with sums, averages, counts and more ✔
create_record Create one record (values) or many (values_list, max 500)
update_record Write values to records ✔
delete_record Delete records ✔
execute_method Call any public model method with ids and named kwargs ✔

All tools accept two optional properties:

  • company_ids: the companies to work in. The first is the active company, where new records are created.
  • context: extra Odoo context, e.g. {"lang": "ar_001"} or {"active_test": false}.

Domains are JSON lists such as ["|", ["name", "ilike", "arc"], ["ref", "=", "A1"]]; a Python-literal string is also accepted. Domains are checked before they are sent to Odoo. Unknown field names are rejected with suggestions ("Did you mean: phone?"), and Odoo errors come back to the model with a hint for fixing the call.

Output rules

Rule Default Change with
search_records fields display_name (+ id) fields
search_records limit 50 (max 500) limit, offset; env MCP_MAX_LIMIT
get_record fields every field except binaries, one2many lists and chatter/activity fields fields
Binary / image fields {"binary": true, "size": …} include_binary: true (max 5 records)
Text / HTML over 2,000 chars cut; HTML turned into plain text full_text: true
x2many lists over 50 ids first 50 + a count —
Any response ≤ 100 KB: records dropped from the end with a note env MCP_MAX_RESPONSE_BYTES
JSON compact, UTF-8 (no \uXXXX escapes) —

Deployment

Prerequisites

  • Docker and Docker Compose
  • An Odoo instance, version 10.0 to 20.0

Step 1: Clone the repository

git clone https://github.com/mah007/odooMCP.git
cd odooMCP

Step 2: Configure the environment

Create a .env file. It holds only infrastructure settings, not Odoo credentials (those go in the web UI):

# Generate a secure key: openssl rand -hex 32
SECRET_KEY=your_secret_key_here

# Published ports (defaults shown)
MCP_PORT=8000
CONTROLLER_PORT=8001

Optional settings:

Variable Default Meaning
MCP_MAX_LIMIT 500 Highest limit a search or aggregate may ask for
MCP_MAX_RESPONSE_BYTES 100000 Size guard for every tool response
MCP_ALLOWED_ORIGINS (empty) Comma-separated browser origins allowed to call /mcp. Empty blocks every request that carries an Origin header (CLI and server clients send none), which protects against DNS-rebinding attacks.
MCP_READ_KEY_BLOCKLIST ir.config_parameter,res.users.apikeys,res.groups,base.automation Models that read-only API keys may not touch
DB_PATH ./data/mcp.db SQLite database path

Step 3: Build and start

docker compose up --build -d

Step 4: First-run setup

  1. Open http://<your_server_ip>:8001 in your browser.
  2. Create your first admin account on the Setup page.
  3. Go to Connections → Add Connection and fill in the fields below.
  4. Click Test: the card shows the detected Odoo version, the transport and each test step.
  5. Click Activate to make it the connection the MCP endpoint uses.
  6. Go to API Keys → Generate to create a key for your AI client.

Connection fields

Field Notes
Name, URL, database, username The URL includes the protocol, e.g. https://erp.example.com
Credential and type An API key (needed for JSON-2 on Odoo 19+) or a password (XML-RPC only)
API mode auto (recommended), xmlrpc or json2; see Transports
Verify SSL On by default for new connections. Connections created before v3 keep v2's behaviour (off) until you change it.
Timeout Seconds per Odoo call (default 120)
Default context JSON merged under every call, e.g. {"lang": "ar_001", "allowed_company_ids": [110, 108]}

API key scopes

Scope Can use
Read & write All 13 tools
Read only The 9 read tools. The write tools are hidden from tools/list and refused if called; the models in MCP_READ_KEY_BLOCKLIST are refused. Use it to give a client's staff safe access.

Connecting clients

The endpoint is http://<your_server_ip>:8000/mcp (Streamable HTTP, JSON responses, no sessions). Authenticate with X-API-Key: <key> or Authorization: Bearer <key>.

Claude Code

claude mcp add --transport http odoo http://<your_server_ip>:8000/mcp \
  --header "X-API-Key: <your_generated_key>"

Then run /mcp in Claude Code to check that the 13 tools are listed. After upgrading the server, reconnect (/mcp → reconnect, or restart the session) so the new tool schemas load.

n8n

  1. Add an MCP Client Tool node to your AI agent.
  2. Endpoint: http://<your_server_ip>:8000/mcp; Server transport: HTTP Streamable.
  3. Authentication: Header Auth with name X-API-Key and your generated key as the value (or Bearer Auth with the key).

Other clients

Any MCP client that can send a header or a bearer token works. Clients that only support OAuth (for example, claude.ai custom connectors) are not supported yet.


Upgrading from v2

v3 is a drop-in upgrade: rebuild the image and restart. On start-up the SQLite database is migrated in place: new columns are added, existing connections get API mode auto, and existing keys get the read & write scope. Tool names are unchanged, and v2-style calls such as search_records {model, domain, fields, limit} or execute_method {model, method, args} still work on both transports.

Behaviour that changed (see CHANGELOG.md):

  • search_records returns {records, total, offset, limit, next_offset}, display_name only by default, and at most 50 records unless you pass limit.
  • get_record leaves out binaries, one2many lists and chatter fields unless you name them.
  • Tool failures come back as a result with isError: true (the model sees the message) instead of a JSON-RPC error.
  • create_record returns {ids, warnings} (plus id for a single record); execute_method returns {result}.
  • get_model_fields and list_models return compact pipe tables.

Admin UI overview

Page What you can do
Dashboard Total requests and errors, per-tool usage with average response size and duration, recent requests with size and transport
Connections Add, edit, test and activate Odoo connections; see the detected version, the transport in use and the last test steps
API Keys Generate, scope (read only / read & write) and revoke keys; see last use and request counts
Settings Change your admin password; add or remove admin accounts

Development

uv venv -p 3.11 .venv
uv pip install -p .venv/bin/python -e ".[dev]"
.venv/bin/python -m pytest tests/unit            # no Odoo needed

# Live tests against a sandbox (writes create and delete their own test partner)
ODOO_URL=http://127.0.0.1:8069 ODOO_DB=mydb ODOO_LOGIN=admin ODOO_API_KEY=<rpc key> \
  ODOO_ALLOW_WRITES=1 .venv/bin/python -m pytest tests/integration -v

See tests/integration/test_live.py for the other switches. The frontend lives in frontend/ (npm install && npm run build).


Acknowledgements

This project is an enhanced version of the original odoo-mcp-server created by Václav Zeman. We extend our sincere thanks to him for providing the excellent foundation for this project.

This enhanced version was fixed and improved by Mahmoud Abdel Latif to be a more general-purpose, secure, and AI-ready solution for the Odoo, n8n, and AI communities.


Feedback and Contributions

This project is made with ❤️ by Mahmoud Abdel Latif for the Odoo, n8n, and AI communities.

If you encounter any errors or bugs, or have suggestions for improvements, please open an issue on the GitHub repository. We welcome all feedback and contributions!


License

This project is licensed under the MIT License. See the LICENSE file for details.