# Mcp Odoo

> MCP Server for Odoo

- **Type:** MCP server
- **Install:** `agentstack add mcp-tuanle96-mcp-odoo`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [tuanle96](https://agentstack.voostack.com/s/tuanle96)
- **Installs:** 0
- **Category:** [Integrations](https://agentstack.voostack.com/c/integrations)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [tuanle96](https://github.com/tuanle96)
- **Source:** https://github.com/tuanle96/mcp-odoo
- **Website:** https://tuanle96.github.io/mcp-odoo/

## Install

```sh
agentstack add mcp-tuanle96-mcp-odoo
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Odoo MCP

  The Odoo MCP that is fluent in every Odoo version.
  Five-minute install. Zero Odoo-side setup. Safe writes, real diagnostics, JSON-2 ready years before the Odoo 22 XML-RPC removal.

  
  
  
  
  
  
  

Odoo MCP turns any Odoo 16+ database into a Model Context Protocol server — using only your existing credentials. **No App Store module, no permission setup, no admin access required.** Built for local agents, IDEs, and automation tools that need real Odoo context without hand-rolled scripts or unsafe direct write access.

It speaks XML-RPC for Odoo 16-18 and External JSON-2 for Odoo 19+. It exposes a compact MCP surface with read tools, diagnostics, schema discovery, migration helpers, local addon scanning, and a gated write workflow. One server can serve [multiple named Odoo instances](#multiple-odoo-instances) at once.

## Try it in 30 seconds

Once configured (see [Setup](#setup)), ask your agent things like:

> "Show me all customers from Spain with unpaid invoices."
>
> "Find products with stock below 10 units in the main warehouse."
>
> "Audit the `custom_billing` addon for upgrade risks before we move to Odoo 19."

## Highlights

| Capability | What it gives you |
| --- | --- |
| 39 MCP tools | Read records and attachments, aggregate server-side, post chatter, inspect schema, build domains, scan addons, diagnose calls, access rules, resolve model renames, validate writes, and fan out across instances. |
| Field-level ACL | Opt-in per-instance, per-model field allow/deny enforced on every read path (records, aggregates, knowledge index, resources). First open-source Odoo MCP with it. See [docs/field-acl.md](docs/field-acl.md). |
| Cross-instance queries | Read-only fan-out across many client DBs with merged, attributed, partial-failure-tolerant results — no warehouse, no sync. See [docs/partner-playbook.md](docs/partner-playbook.md). |
| Workflow prompts | 10 prompts including 5 end-to-end business workflows (invoice approval, PO match, onboarding, expense review, month-end close) that route writes through the gate. |
| Background tasks | `submit_async_task` runs long read operations (addon scans, knowledge indexing, AR/AP aging) on a bounded worker pool; poll with `get_async_task` while the agent keeps reasoning. |
| Local-first knowledge search | `index_knowledge` + `search_knowledge` give BM25 relevance ranking over a bounded record slice — accent-insensitive, in-process, no embeddings service, no data leaving the machine. |
| Accounting pack | `receivable_payable_aging` and `accounting_health_summary` answer the most common finance questions in one call instead of hand-built domains. |
| Rate limiting | Opt-in sliding-window budget per instance and tool (`ODOO_MCP_RATE_LIMIT_MODE=warn\|block`), surfaced in `health_check`. |
| Multi-instance | One server, several named Odoo instances — optional `instance` parameter on every tool, `list_instances` discovery, instance-bound approval tokens, per-instance schema caches. |
| 5 agent prompts | Reusable workflows for failed calls, fit/gap workshops, JSON-2 migration, safe writes, and module audits. |
| Odoo 16-19 coverage | XML-RPC by default, JSON-2 opt-in for Odoo 19. |
| Streamable HTTP | Local HTTP/SSE support for clients that do not use stdio. |
| Smart field selection | `search_records` and `read_record` curate business-relevant fields when no `fields` argument is supplied — drops audit, message, binary, and unstored compute noise. Pass `fields=["*"]` to opt out. |
| Server-side aggregation | `aggregate_records` pushes groupby/sum/count/avg into Postgres via `formatted_read_group` (Odoo 19+) or `read_group` (16-18). |
| Chatter integration | `chatter_post` adds messages to any `mail.thread` record under the same approval-token gate as writes — or directly via `MCP_CHATTER_DIRECT=1`. |
| Locale plumbing | `ODOO_LOCALE` injects `context.lang` automatically on every Odoo call (caller can override). |
| Structured logging | JSON formatter and rotating file handler via `ODOO_MCP_LOG_LEVEL`, `ODOO_MCP_LOG_JSON`, `ODOO_MCP_LOG_FILE`. |
| Safe writes | Direct `create`, `write`, and `unlink` are blocked; approved writes require live metadata, a same-session token, explicit confirmation, and an env gate. |
| Human-in-the-loop approval | `ODOO_MCP_ELICIT_WRITES=1` shows a native MCP confirmation form (with a diff summary) before any approved write executes — token flow stays as fallback. |
| Audit trail | `ODOO_MCP_AUDIT_LOG` appends one JSONL line per write-path event (preview, validate, execute, chatter) with instance and token digest. |
| Resilience | Read-only calls retry connection errors with exponential backoff; schema caches are TTL- and LRU-bounded; `health_check` flags N+1 read loops. |
| Real smoke tests | Docker Compose validation boots disposable Odoo 16.0, 17.0, 18.0, and 19.0 stacks, including restricted users, custom record rules, and packaged addon XML install/update. |

## Why Odoo MCP

| Trait | Odoo MCP | Other MCP-Odoo bridges |
| --- | --- | --- |
| Setup steps on Odoo side | **0** — works with any Odoo 16+ instance using credentials you already have. | Often require installing an App Store module, configuring enabled models, and granting per-tool permissions. |
| Safe write workflow | Approval token + live `fields_get` validation + explicit confirm + env gate. | Often expose direct `create`/`write`/`unlink` or a "yolo" bypass. |
| Diagnostics | `diagnose_odoo_call`, `diagnose_access`, `inspect_model_relationships`, `upgrade_risk_report`, `fit_gap_report`, `business_pack_report`, `scan_addons_source`. | Usually CRUD only. |
| Transport | XML-RPC (16+) **and** External JSON-2 (Odoo 19+). Ready for the Odoo 22 XML-RPC removal years early. | Usually XML-RPC only — deprecated since Odoo 19, removed in Odoo 22. |
| Migration helpers | `generate_json2_payload` previews the JSON-2 body for any XML-RPC call before you migrate. | None. |
| Multi-instance | Named instances in one config file, per-tool routing, tokens and caches isolated per instance. | Usually one global connection per server process. |
| Agent prompts | 5 ready-made prompts for diagnose / fit-gap / JSON-2 migration / safe-write / module-audit. | Usually none. |
| HTTP transport security | DNS-rebinding protection, host/origin allowlists, local-bind by default. | Often missing. |
| Real Odoo smoke tests | Docker Compose harness boots disposable Odoo 16/17/18/19 stacks per release. | Often mock-based only. |
| Framework examples | Copy-paste adapters for Cursor, Claude Code, OpenAI Agents, LangGraph, CrewAI, and n8n in [`examples/`](./examples/). | None. |
| Audit & approval UX | JSONL audit trail + native elicitation confirm forms — without installing anything in Odoo. | Audit features usually require an Odoo-side module. |

Comparing specific projects? See the per-project breakdown in [docs/comparison.md](./docs/comparison.md).

## Setup

Two paths to a working server: set it up yourself, or paste one prompt and let your coding agent do it for you.

### For humans

The fastest path is the interactive wizard via `uvx`, which fetches the package on demand:

```bash
uvx odoo-mcp --setup
```

The wizard asks for your Odoo URL, database, and credentials, tests the connection live, writes the config file, and prints ready-to-paste snippets for Claude Code, Cursor, and Claude Desktop. Prefer a quick smoke check instead? `uvx odoo-mcp --health`.

Using Claude Desktop on macOS? It reads MCP configuration from:

```text
~/Library/Application Support/Claude/claude_desktop_config.json
```

Use an absolute Python path because GUI apps may not inherit your shell `PATH`:

```json
{
  "mcpServers": {
    "odoo": {
      "command": "/opt/homebrew/bin/python3",
      "args": ["-m", "odoo_mcp"],
      "env": {
        "ODOO_URL": "https://your-odoo-instance.com",
        "ODOO_DB": "your-database",
        "ODOO_USERNAME": "your-user",
        "ODOO_PASSWORD": "your-password-or-api-key",
        "ODOO_TRANSPORT": "xmlrpc"
      }
    }
  }
}
```

More client configs (Windsurf, VS Code, Zed, Continue.dev, Streamable HTTP) are in [docs/client-configs.md](./docs/client-configs.md).

Other ways to install:

```bash
pip install odoo-mcp
# or: pipx install odoo-mcp
```

Prefer a container? See [Docker](#docker). For local development:

```bash
git clone https://github.com/tuanle96/mcp-odoo.git
cd mcp-odoo
uv sync --extra dev
```

### For AI agents

Paste this into Claude Code, Cursor, Codex, or any coding agent and it will install the server for you:

```text
Install the odoo-mcp MCP server (https://github.com/tuanle96/mcp-odoo) in this environment:

1. Ask me for my Odoo URL, database name, username, and password or API key.
   Treat them as secrets: never echo, print, or log these values.
2. Register the server as a stdio MCP server:
   - Claude Code: claude mcp add odoo --env ODOO_URL= --env ODOO_DB=
     --env ODOO_USERNAME= --env ODOO_PASSWORD= -- uvx odoo-mcp
   - Any other client: write the equivalent config with "command": "uvx",
     "args": ["odoo-mcp"], and the same four env vars.
3. Verify the install: run `uvx odoo-mcp --health`, then call the health_check
   MCP tool and confirm the Odoo connection is reachable.
4. Leave writes disabled (do not set ODOO_MCP_ENABLE_WRITES) unless I
   explicitly ask you to enable them.

Full machine-readable instructions: https://github.com/tuanle96/mcp-odoo/blob/main/llms-install.md
```

Already know your client? One-liners and config snippets:

```bash
claude mcp add odoo --env ODOO_URL=https://mycompany.odoo.com --env ODOO_DB=mycompany \
  --env ODOO_USERNAME=agent@mycompany.com --env ODOO_PASSWORD=your-api-key -- uvx odoo-mcp
```

- Claude Code `.mcp.json` and Codex CLI `config.toml`: [`examples/README.md`](./examples/README.md)
- Cursor `.cursor/mcp.json` + agent rules: [`examples/cursor/`](./examples/cursor/)
- Windsurf, VS Code, Zed, Continue.dev, Cline, Streamable HTTP, Docker: [`docs/client-configs.md`](./docs/client-configs.md)
- Machine-readable install guide for agents (Cline-style): [`llms-install.md`](./llms-install.md)

#### Framework SDKs

Copy-paste-runnable integrations live in [`examples/`](./examples/):

| Client | Example |
| --- | --- |
| Cursor | [`examples/cursor/`](./examples/cursor/) — `.cursor/mcp.json` + agent rules |
| Claude Code / Codex CLI | snippets in [`examples/README.md`](./examples/README.md) |
| OpenAI Agents SDK | [`examples/openai-agents/`](./examples/openai-agents/) — local + hosted variants |
| LangGraph | [`examples/langgraph/`](./examples/langgraph/) — `langchain-mcp-adapters` |
| CrewAI | [`examples/crewai/`](./examples/crewai/) — native `mcps=[...]` agent |
| n8n | [`examples/n8n/`](./examples/n8n/) — importable workflow JSON |

## Configuration reference

Set connection values in the environment:

```bash
export ODOO_URL="https://your-odoo-instance.com"
export ODOO_DB="your-database"
export ODOO_USERNAME="your-user"
export ODOO_PASSWORD="your-password-or-api-key"
export ODOO_TRANSPORT="xmlrpc"
```

For Odoo 19 JSON-2:

```bash
export ODOO_TRANSPORT="json2"
export ODOO_API_KEY="your-odoo-api-key"
export ODOO_JSON2_DATABASE_HEADER="1"
```

`ODOO_JSON2_DATABASE_HEADER=1` sends `X-Odoo-Database` on JSON-2 calls. Set it to `0` only when host or dbfilter routing already selects the intended database.

Optional environment variables:

| Variable | Default | Effect |
| --- | --- | --- |
| `ODOO_CONFIG_FILE` | unset | Explicit path to a config file, checked before the standard locations. |
| `ODOO_LOCALE` | unset | Inject `context.lang` on every Odoo call. Caller-supplied `context.lang` always wins. |
| `ODOO_MCP_MAX_SMART_FIELDS` | `15` | Cap for smart-field selection when caller omits `fields`. |
| `ODOO_MCP_LOG_LEVEL` | `INFO` | Process logger level (DEBUG/INFO/WARNING/ERROR/CRITICAL). |
| `ODOO_MCP_LOG_JSON` | `0` | Truthy → emit JSON-formatted log lines. |
| `ODOO_MCP_LOG_FILE` | unset | Path → enable rotating file handler (10MB × 3 backups). |
| `ODOO_MCP_ENABLE_WRITES` | `0` | Required for `execute_approved_write`. |
| `ODOO_MCP_ALLOWED_SIDE_EFFECT_METHODS` | empty | Exact `model.method` allowlist (e.g. `sale.order.action_confirm`). |
| `ODOO_MCP_POLICY_FILE` | `./odoo_mcp_policy.json` if present | Version-controllable side-effect allowlist with review metadata (see `odoo_mcp_policy.json.example`); merged with the env allowlist. |
| `ODOO_MCP_ALLOW_UNKNOWN_METHODS` | `0` | Broad mode for `execute_method`. Prefer the exact allowlist above. |
| `ODOO_MCP_AUDIT_LOG` | unset | Path → append one JSONL line per write-path event (preview/validate/execute/chatter), tokens stored as digests. |
| `ODOO_MCP_ELICIT_WRITES` | `0` | Truthy → `execute_approved_write` asks the human via MCP elicitation (native confirm form with a diff summary) before executing; falls back to the token flow when the client cannot elicit. |
| `ODOO_MCP_RETRY_ATTEMPTS` | `2` | Extra attempts for read-only calls on connection errors (0–5). Writes never retry. |
| `ODOO_MCP_RETRY_BACKOFF` | `0.5` | Base retry backoff seconds; doubles per retry. |
| `ODOO_MCP_SCHEMA_CACHE_TTL` | `600` | Schema cache entry lifetime in seconds. |
| `ODOO_MCP_SCHEMA_CACHE_MAX` | `256` | Max schema cache entries (LRU eviction). |
| `ODOO_MCP_RATE_LIMIT_MODE` | `off` | `warn` tracks per-`instance:tool` call rates in `health_check`; `block` refuses over-budget calls on the hot read tools and `execute_method`. |
| `ODOO_MCP_RATE_LIMIT_WINDOW` | `60` | Sliding window length in seconds for rate tracking. |
| `ODOO_MCP_RATE_LIMIT_MAX_CALLS` | `120` | Calls allowed per window per `instance:tool`. |
| `ODOO_MCP_ASYNC_MAX_WORKERS` | `2` | Worker threads for `submit_async_task`. |
| `ODOO_MCP_ASYNC_MAX_TASKS` | `50` | Max retained background tasks (finished tasks evicted oldest-first). |
| `ODOO_MCP_ASYNC_RESULT_TTL` | `3600` | Seconds a finished background task result stays pollable. |
| `ODOO_MCP_KNOWLEDGE_MAX_DOCS` | `5000` | Total documents allowed across all local BM25 knowledge indexes. |
| `ODOO_MCP_FIELD_POLICY_FILE` | shared policy file | Field ACL policy (a `field_acl` key in the policy file, or a dedicated file here). Denied fields are removed from every read path. See [docs/field-acl.md](docs/field-acl.md). |
| `ODOO_MCP_CROSS_INSTANCE_WORKERS` | `4` | Bounded concurrency for cross-instance fan-out tools. |
| `MCP_CHATTER_DIRECT` | `0` | Truthy → `chatter_post` skips the approval token gate and posts immediately. |
| `MCP_ALLOW_REMOTE_HTTP` | `0` | Truthy → permit non-local HTTP binds (still requires external auth/TLS). |
| `MCP_ALLOWED_HOSTS` / `MCP_ALLOWED_ORIGINS` | local | CSV allowlists for HTTP transports. |
| `ODOO_MCP_MAX_ATTACHMENT_BYTES` | `1048576` | Download cap for `read_attachment` content (hard cap 16 MiB). |
| `ODOO_MCP_AUTH_ISSUER_URL` | unset | OAuth 2.1: authorization server issuer. With the two vars below, the HTTP transport becomes a protected resource server (RFC 9728 metadata + bearer validation). |
| `ODOO_MCP_AUTH_INTROSPECTION_URL` | unset | RFC 7662 token introspection endpoint of the authorization server. |
| `ODOO_MCP_AUTH_RESOURCE_URL` | unset | Canonical URL of this MCP server (RFC 8707 audience check when the AS binds tokens). |
| `ODOO_MCP_AUTH_REQUIRED_SCOPES` | empty | CSV scopes required on every request. |
| `ODOO_MCP_AUTH_CLIENT_ID` / `_CLIENT_SECRET` | unset | Credentials for the introspection call when the AS requires client auth. |

You can also use `odoo_config.json`:

```json
{
  "url": "https://your-odoo-instance.com",
  "db": "your-database",
  "username": "your-user",
  "password": "your-password-or-api-key"
}
```

### Multiple Odoo instances

One server can talk to several Odoo databases. Add an `instances` map to your config file (auto-detected — a file without `instances` keeps the flat single-instance shape above):

```json
{
  "default": "acme",
  "instances": {
    "acme": {
      "url": "https://acme.odoo.com",
      "db": "acme",
      "username": "bot",
      "api_key": "...",
      "tr

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [tuanle96](https://github.com/tuanle96)
- **Source:** [tuanle96/mcp-odoo](https://github.com/tuanle96/mcp-odoo)
- **License:** MIT
- **Homepage:** https://tuanle96.github.io/mcp-odoo/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-tuanle96-mcp-odoo
- Seller: https://agentstack.voostack.com/s/tuanle96
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
