Odoo MCP

Self-hosted Python MCP server for Odoo 16-19 with schema tools, diagnostics and gated writes

By erpipe-org Python Local · stdio CRM and salesFinance and paymentsDeveloper tools

59 popularity score ★ 427 ↓ 3.2k/mo

Connect your client

Local (PyPI)

Add in one click

Opens the app, which asks before it adds the server. Then replace each <NAME> in its settings with your value.

Or add it by command or config

It needs ODOO_URL, ODOO_DB, ODOO_USERNAME, ODOO_PASSWORD. Replace each <NAME> with your value.

Claude CodeTerminal
claude mcp add odoo -e ODOO_URL=<ODOO_URL> -e ODOO_DB=<ODOO_DB> -e ODOO_USERNAME=<ODOO_USERNAME> -e ODOO_PASSWORD=<ODOO_PASSWORD> -- uvx odoo-mcp
Claude Desktopclaude_desktop_config.json
{
  "mcpServers": {
    "odoo": {
      "command": "uvx",
      "args": [
        "odoo-mcp"
      ],
      "env": {
        "ODOO_URL": "<ODOO_URL>",
        "ODOO_DB": "<ODOO_DB>",
        "ODOO_USERNAME": "<ODOO_USERNAME>",
        "ODOO_PASSWORD": "<ODOO_PASSWORD>"
      }
    }
  }
}
Cursor~/.cursor/mcp.json
{
  "mcpServers": {
    "odoo": {
      "command": "uvx",
      "args": [
        "odoo-mcp"
      ],
      "env": {
        "ODOO_URL": "<ODOO_URL>",
        "ODOO_DB": "<ODOO_DB>",
        "ODOO_USERNAME": "<ODOO_USERNAME>",
        "ODOO_PASSWORD": "<ODOO_PASSWORD>"
      }
    }
  }
}
VS Code.vscode/mcp.json
{
  "servers": {
    "odoo": {
      "type": "stdio",
      "command": "uvx",
      "args": [
        "odoo-mcp"
      ],
      "env": {
        "ODOO_URL": "<ODOO_URL>",
        "ODOO_DB": "<ODOO_DB>",
        "ODOO_USERNAME": "<ODOO_USERNAME>",
        "ODOO_PASSWORD": "<ODOO_PASSWORD>"
      }
    }
  }
}
Codex~/.codex/config.toml
[mcp_servers.odoo]
command = "uvx"
args = ["odoo-mcp"]

[mcp_servers.odoo.env]
ODOO_URL = "<ODOO_URL>"
ODOO_DB = "<ODOO_DB>"
ODOO_USERNAME = "<ODOO_USERNAME>"
ODOO_PASSWORD = "<ODOO_PASSWORD>"
Gemini CLITerminal
gemini mcp add --scope user -e ODOO_URL=<ODOO_URL> -e ODOO_DB=<ODOO_DB> -e ODOO_USERNAME=<ODOO_USERNAME> -e ODOO_PASSWORD=<ODOO_PASSWORD> odoo uvx -- odoo-mcp
Local (container)

Add in one click

Opens the app, which asks before it adds the server. Then replace each <NAME> in its settings with your value.

Or add it by command or config

It needs ODOO_URL, ODOO_DB, ODOO_USERNAME, ODOO_PASSWORD. Replace each <NAME> with your value.

Claude CodeTerminal
claude mcp add odoo -e ODOO_URL=<ODOO_URL> -e ODOO_DB=<ODOO_DB> -e ODOO_USERNAME=<ODOO_USERNAME> -e ODOO_PASSWORD=<ODOO_PASSWORD> -- docker run -i --rm -e ODOO_URL -e ODOO_DB -e ODOO_USERNAME -e ODOO_PASSWORD ghcr.io/erpipe-org/mcp-odoo:latest
Claude Desktopclaude_desktop_config.json
{
  "mcpServers": {
    "odoo": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-e",
        "ODOO_URL",
        "-e",
        "ODOO_DB",
        "-e",
        "ODOO_USERNAME",
        "-e",
        "ODOO_PASSWORD",
        "ghcr.io/erpipe-org/mcp-odoo:latest"
      ],
      "env": {
        "ODOO_URL": "<ODOO_URL>",
        "ODOO_DB": "<ODOO_DB>",
        "ODOO_USERNAME": "<ODOO_USERNAME>",
        "ODOO_PASSWORD": "<ODOO_PASSWORD>"
      }
    }
  }
}
Cursor~/.cursor/mcp.json
{
  "mcpServers": {
    "odoo": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-e",
        "ODOO_URL",
        "-e",
        "ODOO_DB",
        "-e",
        "ODOO_USERNAME",
        "-e",
        "ODOO_PASSWORD",
        "ghcr.io/erpipe-org/mcp-odoo:latest"
      ],
      "env": {
        "ODOO_URL": "<ODOO_URL>",
        "ODOO_DB": "<ODOO_DB>",
        "ODOO_USERNAME": "<ODOO_USERNAME>",
        "ODOO_PASSWORD": "<ODOO_PASSWORD>"
      }
    }
  }
}
VS Code.vscode/mcp.json
{
  "servers": {
    "odoo": {
      "type": "stdio",
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-e",
        "ODOO_URL",
        "-e",
        "ODOO_DB",
        "-e",
        "ODOO_USERNAME",
        "-e",
        "ODOO_PASSWORD",
        "ghcr.io/erpipe-org/mcp-odoo:latest"
      ],
      "env": {
        "ODOO_URL": "<ODOO_URL>",
        "ODOO_DB": "<ODOO_DB>",
        "ODOO_USERNAME": "<ODOO_USERNAME>",
        "ODOO_PASSWORD": "<ODOO_PASSWORD>"
      }
    }
  }
}
Codex~/.codex/config.toml
[mcp_servers.odoo]
command = "docker"
args = ["run", "-i", "--rm", "-e", "ODOO_URL", "-e", "ODOO_DB", "-e", "ODOO_USERNAME", "-e", "ODOO_PASSWORD", "ghcr.io/erpipe-org/mcp-odoo:latest"]

[mcp_servers.odoo.env]
ODOO_URL = "<ODOO_URL>"
ODOO_DB = "<ODOO_DB>"
ODOO_USERNAME = "<ODOO_USERNAME>"
ODOO_PASSWORD = "<ODOO_PASSWORD>"
Gemini CLITerminal
gemini mcp add --scope user -e ODOO_URL=<ODOO_URL> -e ODOO_DB=<ODOO_DB> -e ODOO_USERNAME=<ODOO_USERNAME> -e ODOO_PASSWORD=<ODOO_PASSWORD> odoo docker -- run -i --rm -e ODOO_URL -e ODOO_DB -e ODOO_USERNAME -e ODOO_PASSWORD ghcr.io/erpipe-org/mcp-odoo:latest

Settings 12

ODOO_URL Required: Address of the Odoo instance.
ODOO_DB Required: Database name.
ODOO_USERNAME Required: Odoo login used for every call.
ODOO_PASSWORD Required: Password or API key for that login (or use a config file instead of these four).
ODOO_TRANSPORT Optional: xmlrpc (default) or json2 for Odoo 19.
ODOO_API_KEY Optional: API key for JSON-2 mode.
ODOO_CONFIG_FILE Optional: Path to a JSON config file, needed for multiple instances.
ODOO_MCP_ENABLE_WRITES Optional: Set to 1 to let execute_approved_write change data.
ODOO_MCP_ALLOWED_SIDE_EFFECT_METHODS Optional: Exact model.method allowlist for execute_method.
ODOO_MCP_AUDIT_LOG Optional: Path for a JSONL log of write-path events.
ODOO_MCP_TOOLS_INCLUDE Optional: Globs that trim which tools are registered (also _EXCLUDE).
ODOO_LOCALE Optional: Language passed as context.lang on every call.

In a container, a setting goes in with -e NAME=value after docker run.

What it does

Odoo MCP is a Python server that connects an assistant to one or more Odoo databases using an ordinary Odoo login or API key; nothing has to be installed inside Odoo. It talks XML-RPC to Odoo 16-18 and can switch to the External JSON-2 API on Odoo 19 (ODOO_TRANSPORT=json2), and it runs over stdio or as a local Streamable HTTP service.

Most of its tools read: searching and reading records, server-side grouping and totals, attachments, field metadata, and accounting summaries such as aged receivables and payables. A second group helps Odoo developers and consultants: it explains why a call or an access rule fails, previews JSON-2 payloads, maps renamed models across versions, scans local addon source and classifies requirements into fit/gap buckets. It can also build an in-process BM25 search index over a slice of records, and fan read queries out across several configured instances.

Writes go through a three-step flow (preview_write, validate_write, execute_approved_write). The final step runs only with ODOO_MCP_ENABLE_WRITES=1, a token issued by the same process and an explicit confirm. Plain create, write and unlink calls through execute_method are refused.

The v1.3.0 source registers 41 tools, matching the README's count, but the README's tool tables list only 39: data_quality_report (duplicate, missing-value and orphan checks on one model) and analyze_upgrade_log (classifies failures in a pasted Odoo upgrade log) are registered in the code but missing from the tables. The same maintainer runs a separate hosted service, ERPipe, with its own codebase and tool set; this page covers the self-hosted package only.

What it needs

RuntimePython 3.10+orDocker

Python 3.10 or newer; the documented launch uses uvx, which needs uv (pip or pipx also work). An Odoo 16 or newer database (Community or Enterprise) reachable over HTTPS, plus its URL, database name and a username with password or API key. Odoo 19 JSON-2 mode needs an Odoo API key. Docker is an alternative to Python.

Limits

Without ODOO_MCP_ENABLE_WRITES=1 nothing can be changed except through allowlisted methods. Side-effect methods such as sale.order.action_confirm need an exact entry in ODOO_MCP_ALLOWED_SIDE_EFFECT_METHODS. MCP resources (odoo://...) always use the default instance in this release. search_records returns 10 rows by default (tools_read.py). read_attachment returns up to 1 MiB of content by default (ODOO_MCP_MAX_ATTACHMENT_BYTES, hard cap 16 MiB). The HTTP transport has no built-in authentication unless the OAuth introspection settings are configured, and it refuses non-local binds without --allow-remote-http. Odoo's own access rules still apply to the user it signs in as.

Who it suits

Fits Odoo admins, partners and developers who want an assistant to query and audit their own Odoo databases from a local client, especially across several client databases or when planning a move to Odoo 19's JSON-2 API. Because writes are off by default and gated, it also suits teams that want read access first and controlled writes later. People who would rather not run a process, or who want a fixed remote URL for ChatGPT, are pointed by its README to the maintainer's hosted ERPipe service instead. It is a community project, not published by Odoo SA.

Tools 41

list_models
Lists model technical names and labels
get_model_fields
Returns field metadata for one model
search_records
Read-only search with domain or free-text query; 10 rows by default
read_record
Reads one record by model and ID
aggregate_records
Group-by sums, counts and averages computed in Odoo
search_employee
Finds employees by name
search_holidays
Finds leave records in a date range
get_odoo_profile
Server version, user context, transport and installed modules
schema_catalog
Builds a bounded catalog of models with optional fields
build_domain
Turns structured conditions into a validated Odoo domain
read_attachment
Attachment metadata plus size-capped base64 content
preview_write
Prepares a create, write or unlink payload without running it
validate_write
Checks a write payload against live field metadata and issues a token
execute_approved_write
Runs a validated, confirmed write when writes are enabled
execute_method
Calls a reviewed model method; side-effect methods need an allowlist
chatter_post
Posts a chatter message on a record, gated by default
diagnose_odoo_call
Explains why a model call would fail, without running it
diagnose_access
Shows ACL and record-rule visibility for the signed-in user
inspect_model_relationships
Groups relation and required fields with create/write hints
generate_json2_payload
Converts an XML-RPC call into its JSON-2 request
upgrade_risk_report
Flags transport, method and migration risks between versions
analyze_upgrade_log
Classifies known failure patterns in an Odoo upgrade log
lookup_model_history
Maps old model names to their current names by version
scan_addons_source
Scans local addon source without importing it
fit_gap_report
Sorts requirements into standard, configuration, Studio or custom buckets
business_pack_report
Expected modules and models for sales, CRM, inventory, accounting or HR
data_quality_report
Checks one model for duplicates, missing values and orphaned links
index_knowledge
Builds a local BM25 index over a slice of records
search_knowledge
Ranked free-text search over the local index
knowledge_stats
Index sizes and remaining document budget
receivable_payable_aging
Aged receivables and payables by overdue bucket and partner
accounting_health_summary
Open receivable/payable counts and draft invoice backlog
submit_async_task
Starts an allowlisted long read job in the background
get_async_task
Polls a background job's status and result
cancel_async_task
Cancels a pending or running background job
list_async_tasks
Lists live and recently finished background jobs
search_across_instances
Runs one search on several instances and merges the rows
aggregate_across_instances
Per-instance aggregates with combined totals
accounting_health_across_instances
Receivable/payable aging summed across instances
health_check
Reports runtime posture, rate-limit counters and field-ACL status
list_instances
Lists configured instances without credentials

Similar servers