The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the MCP Server Odoo listing page.
An MCP server that enables AI assistants like Claude to interact with Odoo ERP systems. Access business data, search records, create new entries, update existing data, and manage your Odoo instance through natural language.
Works with any Odoo instance! Use YOLO mode for quick testing and demos with any standard Odoo installation. For enterprise security, access controls, and production use, install the Odoo MCP module.
ir.attachment files via resource URIsThe MCP server runs on your local computer (where Claude Desktop is installed), not on your Odoo server. You need to install UV on your local machine:
After installation, restart your terminal to ensure UV is in your PATH.
Add this configuration to your MCP settings:
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
Add to .mcp.json in your project root:
Or use the CLI:
Add to ~/.cursor/mcp.json:
Add to .vscode/mcp.json in your workspace:
Note: VS Code uses
"servers"as the root key, not"mcpServers".
Add to ~/.codeium/windsurf/mcp_config.json:
Add to ~/.config/zed/settings.json:
Run with Docker — no Python installation required:
Note: Use
host.docker.internalinstead oflocalhostto connect to Odoo running on the host machine.
For HTTP transport:
⚠️ Security: the HTTP transport has no built-in authentication — anyone who can reach the port gets Odoo access through the server's credentials. Publish the port only on trusted networks, or front it with an authenticating reverse proxy. See Transport Options.
The image is also available on GHCR: ghcr.io/ivnvxd/mcp-server-odoo
Then use mcp-server-odoo as the command in your MCP configuration.
Then use the full path to the package in your MCP configuration.
The server requires the following environment variables:
| Variable | Required | Description | Example |
|---|---|---|---|
ODOO_URL | Yes | Your Odoo instance URL | https://mycompany.odoo.com |
ODOO_API_KEY | Yes* | API key for authentication | 0ef5b399e9ee9c11b053dfb6eeba8de473c29fcd |
ODOO_USER | Yes* | Username (if not using API key) | admin |
ODOO_PASSWORD | Yes* | Password (if not using API key) | admin |
ODOO_DB | No | Database name (auto-detected if not set) | mycompany |
ODOO_LOCALE | No | Language/locale for Odoo responses | es_ES, fr_FR, de_DE |
ODOO_YOLO | No | YOLO mode - bypasses MCP security (⚠️ DEV ONLY) | off, read, true |
ODOO_MCP_ENABLE_METHOD_CALLS | No | Enable the call_model_method tool — requires ODOO_YOLO=true (⚠️ Dangerous, see call_model_method) | false, true |
*Either ODOO_API_KEY or both ODOO_USER and ODOO_PASSWORD are required. In YOLO mode, ODOO_USER is required even when using an API key.
Notes:
ODOO_DB.env file in the working directory| Variable | Default | Description |
|---|---|---|
ODOO_MCP_DEFAULT_LIMIT | 10 | Default number of records returned per search |
ODOO_MCP_MAX_LIMIT | 100 | Maximum allowed record limit per request |
ODOO_MCP_MAX_SMART_FIELDS | 15 | Maximum fields returned by smart field selection |
ODOO_MCP_LOG_LEVEL | INFO | Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL) |
ODOO_MCP_LOG_JSON | false | Enable structured JSON log output |
ODOO_MCP_LOG_FILE | — | Path for rotating log file (10 MB, 5 backups) |
ODOO_MCP_LOG_FORMAT | — | Custom Python logging format string (default: %(asctime)s - %(name)s - %(levelname)s - %(message)s) |
ODOO_MCP_SLOW_OPERATION_THRESHOLD_MS | 1000 | Threshold in milliseconds above which an operation is logged as slow |
ODOO_MCP_TRANSPORT | stdio | Transport type (stdio, streamable-http) |
ODOO_MCP_HOST | localhost | Host to bind for HTTP transport |
ODOO_MCP_PORT | 8000 | Port to bind for HTTP transport |
ODOO_MCP_ALLOWED_HOSTS | — | Comma-separated Host headers to accept for HTTP transport (DNS-rebinding protection). Set when running streamable-http behind a reverse proxy that forwards an external host, e.g. odoo.example.com,localhost. IPv6 literals may be bracketed or bare ([::1]:8000, ::1). Unset, protection is only auto-enabled for a loopback bind — binding any other host (e.g. 0.0.0.0) runs with no Host/Origin validation at all. |
ODOO_MCP_SESSION_IDLE_TIMEOUT | — | Seconds of inactivity before a streamable-http session is closed and its server-side state freed, e.g. 600. Unset means sessions never expire. |
ODOO_MCP_MAX_BINARY_SIZE | 52428800 | Maximum bytes returned by a single binary/attachment resources/read. Checked before the payload is fetched (a bin_size probe for record fields, the stored file_size for attachments), so an oversized read is refused with a clean error instead of being pulled into memory and re-encoded to base64 for the wire. |
The server supports multiple transport protocols for different use cases:
Standard input/output transport - used by desktop AI applications like Claude Desktop.
Standard HTTP transport for REST API-style access and remote connectivity.
⚠️ Security: this transport has no built-in client authentication. Any client that can reach the port can use every tool and resource with the Odoo credentials the server holds — including writes in YOLO full-access mode. Keep the default
localhostbind unless the network is trusted, and front the server with an authenticating reverse proxy (e.g. nginx with basic auth or OAuth) for remote access. The server logs a warning when binding a non-loopback host.
The HTTP endpoint will be available at: http://localhost:8000/mcp/
Note: SSE (Server-Sent Events) transport has been deprecated in MCP protocol version 2025-03-26. Use streamable-http transport instead for HTTP-based communication. Requires MCP library v1.27.0 or higher.
Install the MCP module:
Enable models for MCP access:
Generate an API key:
YOLO mode allows the MCP server to connect directly to any standard Odoo instance without requiring the MCP module. This mode bypasses all MCP security controls and is intended ONLY for development, testing, and demos.
🚨 WARNING: Never use YOLO mode in production environments!
Read-Only Mode (ODOO_YOLO=read):
Full Access Mode (ODOO_YOLO=true):
✅ Appropriate Uses:
❌ Never Use For:
Once configured, you can ask Claude:
Search & Retrieve:
Create & Manage:
search_recordsSearch for records in any Odoo model with filters.
Field Selection Options:
fields or set to null: Returns smart selection of common fields[] is treated like null (smart defaults)["__all__"]: Returns all fields (use with caution) — credential-like fields are withheld and listed in the response's note; request them explicitly by name if neededget_recordRetrieve a specific record by ID.
Field Selection Options:
fields or set to null: Returns smart selection of common fields with metadata[] is treated like null (smart defaults)["__all__"]: Returns all fields — credential-like fields are withheld and noted in the response metadata; request them explicitly by name if neededResponses also include related_summaries: display names for one2many/many2many collections holding at most 5 ids, so small relations are readable without extra lookups.
get_fieldsDescribe a model's fields — type, label, required/readonly, relation target, and selection options. Use it to discover a model's schema before reading or writing records. Omit attributes for the curated default set (type, string, required, readonly, relation, selection); an explicit list replaces the curated set — include the defaults in your list if you still need them (e.g. ["type", "string", "help", "store"]). Omit field_names to describe every field on the model. An empty list [] for either parameter is treated like omitting it.
get_current_contextReturn the current session context: the connected user, their timezone, the active company plus any other allowed companies, and UTC datetime-handling guidance. Useful when unsure which user or company a request runs as, or how to interpret datetimes. Spec-compliant clients also receive this context through the initialize response instructions.
list_modelsList all models enabled for MCP access.
list_resource_templatesList available resource URI templates and their patterns.
create_recordCreate a new record in Odoo.
update_recordUpdate an existing record.
delete_recordDelete a record from Odoo.
post_messagePost a message to a record's chatter (mail.thread). subtype="note" (default) is an internal log; subtype="comment" notifies followers. Set body_is_html=true for HTML markup. Optional subject sets a message subject line; optional partner_ids and attachment_ids reference existing partners and attachments.
aggregate_recordsServer-side aggregation. Use this whenever the question is "totals/counts/groupings" rather than "list of records" — it pushes the work down to PostgreSQL instead of pulling raw rows. Dispatches to formatted_read_group on Odoo 19+ (the new dedicated method) and falls back to read_group with response normalization on older versions. Callers see a consistent response shape on every supported version. When aggregates is omitted, defaults to ["__count"] so each group always carries a count. When more groups exist beyond the requested page, the response sets has_more: true and a next_hint with the follow-up offset.
call_model_methodGeneric XML-RPC execute_kw escape hatch — invokes public business methods, for workflow actions not covered by CRUD (post invoice, confirm sale order, validate picking, etc.). Available only when both ODOO_YOLO=true (full YOLO) and ODOO_MCP_ENABLE_METHOD_CALLS=true are set; otherwise the tool is not registered. Only public ASCII Python identifiers are accepted as method names — dotted, dashed, whitespace, non-ASCII, and _-prefixed names are rejected.
Some calls are blocked for safety even in full YOLO mode:
ir.actions.* / ir.cron models — their methods run with elevated privileges (server actions, scheduled jobs)run / method_direct_trigger on any model — same escalation risk via proxiescreate, write, unlink, read, search*, copy, sudo, ...) — use the dedicated tools insteadweb_* methods — the web-client data-access familyList results are truncated to 100 items.
[!WARNING] This tool can still invoke destructive workflow methods (e.g.
button_draft,action_cancel,toggle_active, custom methods). Enable only in trusted environments where you accept the blast radius. Odoo's record rules and ACLs still apply for the authenticated user.
When you omit the fields parameter (or set it to null), the server automatically selects the most relevant fields for each model using a scoring algorithm:
id, name, display_name, and active are always included*password, *_pass like smtp_pass, passwd, *secret, *_token, *apikey, or a *_key compound such as api_key/secret_key) are excluded from smart defaults and withheld from ["__all__"] reads with an explanatory note — requesting such a field explicitly by name still returns itThe default limit is 15 fields per request. Responses include metadata showing which fields were returned and how many total fields are available. You can adjust the limit with ODOO_MCP_MAX_SMART_FIELDS or bypass it entirely with fields: ["__all__"].
The server also provides direct access to Odoo data through resource URIs:
| URI Pattern | Description |
|---|---|
odoo://{model}/record/{id} | Retrieve a specific record by ID |
odoo://{model}/search | Search records with default settings (first 10 records) |
odoo://{model}/count | Count all records in a model |
odoo://{model}/fields | Get field definitions and metadata for a model |
odoo://{model}/record/{id}/{field} | Fetch a binary/image field from a record, served with the correct mimeType |
odoo://attachment/{id} | Fetch an ir.attachment by ID (url-type attachments return their URL as text) |
Examples:
odoo://res.partner/record/1 — Get partner with ID 1odoo://product.product/search — List first 10 productsodoo://res.partner/count — Count all partnersodoo://product.product/fields — Show all fields for productsodoo://res.partner/record/1/image_128 — Get partner 1's avatar imageodoo://attachment/42 — Download attachment 42Populated binary fields in get_record/search_records results are returned as these resource URIs instead of inline base64 — read the URI to retrieve the actual bytes. Binary content is served exclusively via MCP resources: tool results carry URIs, never inline base64, so your MCP client must support resources/read to fetch it.
Record and search resource reads withhold credential-like fields the same way the tools' bulk reads do — to read such a field, request it explicitly by name via the tools' fields parameter.
Binary and attachment reads are served whole, up to ODOO_MCP_MAX_BINARY_SIZE (default 50 MB). The size is checked before the payload is fetched, so an oversized field or attachment is refused with a clean error rather than buffered into a correspondingly large response.
Note: Resource URIs don't support query parameters (like
?domain=...). For filtering, pagination, and field selection, use thesearch_recordstool instead.
The server translates MCP tool calls into Odoo XML-RPC requests. It handles authentication, access control, field selection, data formatting, and error handling — presenting Odoo data in an LLM-friendly hierarchical text format.
If you're getting connection errors:
https://your-odoo.com/mcp/healthIf authentication fails:
If you can't access certain models:
This error means UV is not installed or not in your PATH:
Solution 1: Install UV (see Installation section above)
Solution 2: macOS PATH Issue Claude Desktop on macOS doesn't inherit your shell's PATH. Try:
Solution 3: Use Full Path Find UV location and use full path:
Then update your config:
If you see "Access Denied" when listing databases:
ODOO_DB in your configurationExample configuration:
Note: ODOO_DB is required if database listing is restricted on your server.
This error occurs when Python cannot verify SSL certificates, often on macOS or corporate networks.
Solution: Add SSL certificate path to your environment configuration:
This tells Python where to find the system's SSL certificate bundle for HTTPS connections. The path /etc/ssl/cert.pem is the standard location on most systems.
Enable debug logging for more information:
This project is licensed under the Mozilla Public License 2.0 (MPL-2.0) - see the LICENSE file for details.
Contributions are very welcome! Please see the CONTRIBUTING guide for details.
Thank you for using this project! If you find it helpful and would like to support my work, kindly consider buying me a coffee. Your support is greatly appreciated!
And do not forget to give the project a star if you like it! :star: