MCP Server for Odoo
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@MCP Server for OdooShow me all leads from Spain that haven't been contacted in 30 days"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
MCP Server for Odoo
An Odoo module that enables Model Context Protocol (MCP) integration, allowing AI assistants to securely access and interact with your Odoo data. Supports full CRUD operations - create, read, update, and delete records through natural language.
The module speaks MCP natively: it exposes a built-in MCP endpoint at /mcp, so any
MCP client — Claude (web, desktop, mobile, Claude Code), ChatGPT, Microsoft Copilot,
Perplexity, Mistral Le Chat, Gemini CLI, Cursor, VS Code, MCP Inspector — can connect
directly to your Odoo URL with no separate process to install (see the
client compatibility table). There are three ways to connect,
from simplest to most involved:
Log in with Odoo (OAuth 2.1) — paste your Odoo URL into the client and sign in. No keys to create or copy.
API key (Bearer token) — mint a key once and configure it as an
Authorization: Bearerheader.Standalone local client (
uvx mcp-server-odoo) — a bridge process for stdio-only MCP clients, using the same module APIs.
Features
🔌 Native MCP Endpoint: Built-in
/mcpendpoint (Streamable HTTP, JSON-RPC 2.0) — connect MCP clients straight to Odoo, no extra software🔑 Log in with Odoo (OAuth 2.1): browser-based clients connect with a normal Odoo login and a per-request consent screen — no API key handling
🔐 Secure API Access: API key or OAuth 2.1 authentication with rate limiting
📖 Read-Only Consent: grant an AI assistant read-only access with one checkbox at the OAuth consent screen (
mcp:read/mcp:writescopes)🗝️ "MCP Only" API Keys: mint keys that work only on the MCP endpoint — a leaked key cannot be used for general RPC
🧭 User Context on Connect: the handshake tells the assistant who is connected, their timezone and companies, so it stops guessing
🧩 Custom Tools: expose curated verbs (e.g.
confirm_sale_order) backed by server actions instead of generic CRUD🎯 Granular Permissions: Control read/write/create/delete access per model
🌐 REST & XML-RPC APIs: Dual protocol support for maximum compatibility
👥 Role-Based Access: MCP access is granted per user through the MCP User and MCP Administrator security groups
📊 Audit Log: every call, denial and auth attempt is recorded in
mcp.log🖥️ User-Friendly UI: Integrated configuration in Odoo settings
What Can You Do With This Module?
This module opens up powerful AI-assisted workflows for your Odoo instance:
Data Retrieval & Analysis:
Customer Service Automation: Let AI assistants look up customer orders, check inventory, and provide instant support
Sales Intelligence: Query your CRM data naturally - "Show me all leads from Spain that haven't been contacted in 30 days"
Inventory Management: Ask questions like "Which products are low in stock?" or "What were our top selling items last month?"
Financial Insights: Get quick answers about invoices, payments, and financial status without complex reports
HR Queries: Find employee information, leave balances, or department structures through natural language
Project Management: Track project progress, find overdue tasks, or check team workloads conversationally
Data Management & Automation:
Contact Management: Create new customers, update contact information, or manage supplier records
Product Catalog: Add new products, update prices, or modify inventory levels
Order Processing: Create sales orders, update order status, or manage deliveries
Task Creation: Add new tasks to projects, assign team members, or update progress
Event Scheduling: Create calendar events, schedule meetings, or manage appointments
Data Cleanup: Remove test records, archive old data, or maintain data quality
Requirements
Odoo 19.0 (Community or Enterprise). This module targets Odoo 19.0 and uses v19-only APIs.
You always need this Odoo module installed and configured (it provides the MCP
protocol, access control, and security layer). Whichever way a client connects — OAuth,
API key, or the standalone bridge — every call runs as a real Odoo user and shares the
same mcp.enabled.model permissions, audit log, and rate limiting.
The module needs the authlib (>=1.6.12,<1.7.0), defusedxml and packaging Python
packages — declared in the manifest's external_dependencies and mirrored in the
requirements.txt shipped inside the module. authlib is intentionally
capped below 1.7.0: 1.7.x pulls in a newer cryptography (via joserfc) that many Odoo
deploy images cannot install over their system cryptography/pyOpenSSL, which breaks
installation and the server's import OpenSSL. packaging lets Odoo parse the version pin.
Install them into the Python environment that runs Odoo:
pip install -r mcp_server/requirements.txt
# or explicitly:
pip install "authlib>=1.6.12,<1.7.0" defusedxml packagingOn Odoo.sh (and similar platforms that auto-install Python dependencies): only the
requirements.txt at the root of your repository (or of a submodule) is installed —
a requirements.txt inside an addon folder is ignored. Reference the module's file from
your repo-root requirements.txt:
-r path/to/mcp_server/requirements.txtor copy its lines there verbatim.
Before connecting anything: go to Settings > MCP Server, turn on the master switch, and enable the models you want to expose with their per-operation permissions. See Configure the Module.
Connecting AI Assistants
Option 1: Log in with Odoo (OAuth 2.1) — recommended
The simplest way to connect: give the client your Odoo MCP URL and sign in with your normal Odoo login. No API key is created, copied, or stored in a config file. The module is itself a complete OAuth 2.1 authorization server, so any MCP client that supports OAuth for remote servers works out of the box:
Claude.ai (web): Settings > Connectors > Add custom connector, paste
https://your-company.odoo.com/mcp, then complete the Odoo login and consent screen.Claude Desktop: same Connectors UI (Settings > Connectors > Add custom connector).
Claude Code (CLI):
claude mcp add --transport http much-odoo https://your-company.odoo.com/mcpthen run
/mcpinside Claude Code and choose Authenticate — the browser opens your Odoo login.ChatGPT: enable Developer Mode, then Settings > Apps & Connectors > Advanced settings > Create app with the same URL — the Odoo login opens when you connect (web, paid plans; details in the compatibility table).
Perplexity / Mistral Le Chat: add the URL as a custom connector in their Connectors settings — the OAuth login starts automatically.
Cursor / VS Code: add the server URL without an
Authorizationheader (see the JSON snippets below, minus theheaderskey); recent versions detect the OAuth challenge and open the login flow automatically.
What happens under the hood (all automatic):
The client
POSTs to/mcpwith no token and gets a401whoseWWW-Authenticate: Bearerheader carries an RFC 9728resource_metadatapointer to/.well-known/oauth-protected-resource.From the discovery documents the client locates the authorization server and registers itself at
/mcp/oauth/register(RFC 7591 dynamic client registration; public PKCE client, no secret).The user is sent to
/mcp/oauth/authorize, signs in with their normal Odoo login, and approves a per-request consent screen. The consent screen includes an "Allow creating and modifying data" checkbox — uncheck it to grant the assistant read-only access (see OAuth Read-Only Consent).The client exchanges the authorization code at
/mcp/oauth/token(PKCE S256) for an opaque access token bound to that user. Access tokens are short-lived (1 hour); long-lived sessions are kept alive by rotating refresh tokens.
Every call runs as the logged-in user, so the same mcp.enabled.model permissions, Odoo
access rights, audit log and rate limiting apply as with an API key.
Managing OAuth clients & tokens (admins). OAuth activity is managed under Settings > Technical > MCP (MCP Administrator group):
OAuth Clients — applications registered through the authorization flow. Bulk Deactivate is available from the list view; deactivating a client blocks new logins and cuts off every token it already issued.
OAuth Tokens — issued tokens, listing the user, client, audience, scope and expiry. Open a token and click Revoke (or use the bulk list action) to invalidate it immediately; the client must then re-authenticate.
A daily scheduled action (ir.cron) garbage-collects spent credentials — expired
authorization codes, revoked-and-expired tokens, and stale dynamically-registered
clients — so the tables stay clean. Only credentials that can no longer authenticate
anything are removed.
The OAuth front door is enabled by default whenever MCP is enabled. To accept API
keys only, turn off Allow OAuth 2.1 login in Settings > MCP Server (system
parameter mcp_server.enable_oauth).
Option 2: API key (Bearer token)
For clients where you prefer (or need) to paste a static credential — headless setups, service accounts, CI, or MCP clients without OAuth support:
In Odoo, go to My Profile > Account Security > New API Key.
Set Access to MCP only (recommended — see "MCP Only" API Keys) or keep All APIs (default).
Enter a description and copy the key — it is shown only once.
Configure the client with the URL and an
Authorization: Bearer <API_KEY>header (theBearerprefix is optional — a bareAuthorization: <API_KEY>also works). A missing or invalid key returns401with aWWW-Authenticate: Bearerchallenge;GET /mcpreturns405(the endpoint is POST-only).
Claude Code (CLI):
claude mcp add --transport http much-odoo \
https://your-company.odoo.com/mcp \
--header "Authorization: Bearer YOUR_API_KEY"Cursor (~/.cursor/mcp.json or a project .cursor/mcp.json, mcpServers key):
{
"mcpServers": {
"much-odoo": {
"url": "https://your-company.odoo.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}VS Code (.vscode/mcp.json — note the top-level servers key, not mcpServers):
{
"servers": {
"much-odoo": {
"type": "http",
"url": "https://your-company.odoo.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}Claude Desktop: remote (HTTP) servers are added through the Connectors UI
(Settings → Connectors → Add custom connector) pointed at
https://your-company.odoo.com/mcp — which uses
OAuth and needs no key. To use an
API key instead, bridge a local stdio entry to the remote endpoint with
mcp-remote.
MCP Inspector (protocol testing):
npx @modelcontextprotocol/inspectorPoint it at https://your-company.odoo.com/mcp and add the header
Authorization: Bearer YOUR_API_KEY (or leave the header out and use its OAuth flow).
Option 3: Standalone local client (uvx mcp-server-odoo)
The companion open-source client mcp-server-odoo runs as a small local process on the machine where your MCP client lives and bridges stdio MCP to this module's XML-RPC/REST API. Use it when your MCP client only speaks stdio (no remote HTTP servers), or when you want a local process you can pin and configure per project.
{
"mcpServers": {
"odoo": {
"command": "uvx",
"args": ["mcp-server-odoo"],
"env": {
"ODOO_URL": "https://your-company.odoo.com",
"ODOO_API_KEY": "your-api-key-here",
"ODOO_DB": "your-database-name"
}
}
}
}Requires Python 3.10+ and uv on the client machine
(ODOO_DB is optional when the server exposes its database list). The bridge goes
through the same mcp.enabled.model permissions, audit log and rate limits, but it
talks to this module's legacy /mcp/xmlrpc/* and /mcp/* REST endpoints rather than to
/mcp. Those delegate authentication to Odoo core (scope rpc), so this client needs a
global (all-APIs) or rpc-scope key — an MCP only key works
on /mcp only and is rejected here.
Besides stdio, the bridge can also serve Streamable HTTP itself
(--transport streamable-http) if you want to host it on a trusted network — note it
adds no authentication of its own. See the
mcp-server-odoo repository for full
instructions: transports, username/password auth, multi-language output, and a
module-less "YOLO" test mode (not recommended for production — it bypasses this module's
per-model access control).
Client Compatibility
Verified against this module's endpoint (Streamable HTTP, MCP protocol 2025-11-25 /
2025-06-18, OAuth 2.1 with dynamic client registration + PKCE, or Bearer API keys):
Client | Connect with | Notes |
Claude.ai (web) / Claude Desktop (docs) | OAuth (Connectors UI) | Custom connectors on all plans (Free: one connector). Connectors call your server from Anthropic's cloud (egress range |
Claude mobile (iOS/Android) | OAuth (synced) | Add the connector on claude.ai (web) once — it syncs to the mobile apps automatically. |
Claude Code (CLI) | OAuth or API key | Runs locally, so it can also reach private/localhost instances. |
ChatGPT (docs) | OAuth (custom app / connector) | Verified end-to-end against this module: OAuth login gives full read + write tool access in normal chat. Set up on ChatGPT web (paid plans): enable Developer Mode, then Settings > Apps & Connectors > Advanced settings > Create app with the |
Microsoft Copilot Studio / M365 Copilot (docs) | OAuth (dynamic discovery/DCR) or API key | Add via the MCP onboarding wizard; transport |
Perplexity (web & desktop) (docs) | OAuth or API key | Custom remote connectors are a paid-tier feature; Streamable HTTP supported. |
Mistral Le Chat (docs) | OAuth (auto-detected, DCR) or Bearer token | Add under Connectors > Custom MCP Connector; the auth method is detected automatically. |
Gemini CLI (docs) | OAuth (auto-discovery) or Bearer header | Use |
Gemini Enterprise (docs) | OAuth | Google Cloud offering: add the endpoint as a custom MCP data store; users authorize via the OAuth flow. |
Gemini app (web/mobile) | — | Not supported: the consumer Gemini app has no custom MCP connectors (as of July 2026). Use Gemini CLI or Gemini Enterprise instead. |
Cursor / VS Code / Windsurf & other MCP IDEs | API key header or OAuth | Config snippets above; header-less entries trigger the OAuth flow in recent versions. |
MCP Inspector | OAuth or API key | Protocol testing. |
Stdio-only clients (LM Studio, many desktop wrappers) | API key via the standalone bridge | Run |
Two practical rules cover most of the table:
Cloud-hosted clients (Claude.ai/mobile connectors, ChatGPT, Perplexity web, Le Chat, Copilot, Gemini Enterprise) connect from the vendor's infrastructure: your Odoo instance must be reachable on public HTTPS, and OAuth is usually the only authentication offered for custom connectors — which is exactly what Option 1 provides.
Locally-running clients (Claude Code, Cursor, VS Code, Gemini CLI, Inspector, the uvx bridge) can reach private or localhost instances and may use either OAuth or an API-key header.
Native MCP Endpoint (/mcp)
The module exposes a native MCP server at POST /mcp (Streamable HTTP, JSON-RPC 2.0).
The server implements MCP protocol revisions 2025-11-25 (preferred) and 2025-06-18;
the revision is negotiated during the initialize handshake. /mcp/rpc is kept as a
legacy alias of the same endpoint, so clients configured against the old path keep
working.
Available Tools
The endpoint exposes 12 built-in tools (plus any admin-defined Custom Tools):
Tool | Operation | Description |
| read | Search records with smart field selection and pagination |
| read | Fetch a single record, formatted for LLMs |
| read | Describe a model's fields (type, label, relation, etc.) |
| read | List the MCP-enabled models |
| read | Group/aggregate records ( |
| read | Advertise the available |
| read | Return the caller's user / timezone / company context |
| create | Create a record |
| write | Update a record |
| unlink | Delete a record |
| method call | Call a public business method (opt-in per model) |
| write | Post a message to a record's chatter |
Access Control
Every call runs as the authenticated user (the API key's owner, or the user who
logged in via OAuth), so Odoo's own access rights and record rules always apply. On top
of that, MCP access is gated by mcp.enabled.model:
A model is reachable only if it is MCP-enabled, with the requested operation allowed (
allow_read/allow_create/allow_write/allow_unlink).call_model_methodadditionally requiresallow_method_calls = Trueon the model — an admin opt-in, default off. It exposes only the model's public business methods; private (leading-underscore),web_*, and generic ORM/CRUD methods are always rejected, and methods that map to a CRUD operation still require the matching per-operation permission. Such business methods may have side effects on this and related records (posting messages, scheduling activities, managing followers, etc.), but only ever do what the authenticated user could already do in Odoo. Leave it off unless you need it — and in particular do not enable it on infrastructure/meta models such asir.actions.server,ir.cron,base.automation,ir.modelorir.rule, whose public methods (e.g.run()) execute arbitrary configured logic.post_messageis gated as a write operation.Fields whose name looks like a credential (
*password,*secret,*_token,api_key/secret_key/private_key, …) are omitted from bulk reads — the smart-default selection and the["__all__"]sentinel ofget_record/search_records— as a convenience guard. This is best-effort and name-based only: explicitly-named fields are still returned, so protect real secrets with Odoo field-levelgroups=, which is the enforced boundary.Under a read-only OAuth session (scope
mcp:read), write tools are hidden fromtools/listand refused if called (see OAuth Read-Only Consent).
All tool calls are written to the audit log (mcp.log, browsable under Settings >
Technical > MCP > MCP Logs) and subject to per-user rate limiting (in-memory, enforced
per worker process).
The whole POST /mcp request body is capped at 10 MiB; since binary fields are written
base64-encoded, this limits a single create_record / update_record binary write to
roughly 7.5 MiB of decoded data — a larger payload is rejected with HTTP 413.
Resources
The endpoint serves odoo:// resources so binary/image data is fetched on demand
instead of inlined as base64:
odoo://record/{model}/{id}/{field}— a binary/image field on a recordodoo://attachment/{id}— anir.attachmentby ID
User Context, Scoped Keys, Read-Only Consent & Custom Tools
These four capabilities were added in 19.0.2 to tighten security and give admins more control over what MCP clients can do.
User Context on Connect
LLMs otherwise guess the timezone and pick a wrong company on multi-company databases —
and those guesses turn into wrong writes. To prevent that, the initialize handshake
returns a personalized instructions string that spec-compliant clients inject into
the model's context automatically (zero extra round-trips). It describes the connected
user, their timezone (or a note that none is set), the active company, and
any other allowed companies, plus the rule that all datetimes are stored and
returned in UTC.
For clients that ignore instructions, the same information is available on demand
through the read-only get_current_context tool. The context only ever exposes the
caller's own user/company information — nothing else.
"MCP Only" API Keys
By default an Odoo API key is a global credential that works on every RPC surface. You can instead mint a key that is confined to the MCP endpoint:
Go to My Profile > Account Security > New API Key.
In the key wizard, set Access to MCP only (the default is All APIs).
Enter a description and copy the key — it is shown only once.
An MCP only key authenticates only on /mcp, so a leaked key has a much smaller
blast radius — it cannot be used for general XML-RPC/JSON-RPC access to your database
(including this module's own legacy /mcp/xmlrpc/* and REST endpoints). Existing keys
are unaffected: global (all-APIs) keys and rpc-scope keys keep working on /mcp
exactly as before.
OAuth Read-Only Consent
When a browser client connects via OAuth, the per-request consent screen shows an "Allow creating and modifying data" checkbox (checked by default):
Leave it checked → the session is granted the
mcp:writescope: the client can read and write, exactly as before.Uncheck it → the session is granted the read-only
mcp:readscope. Write tools (create_record,update_record,delete_record,call_model_method,post_message, and any non-read-only custom tool) are not even listed for that session, and any attempt to call one is refused with a clear read-only error.
If the client explicitly requests only mcp:read, the checkbox is not shown and the
token is read-only. The granted scope is computed server-side and can only ever be
narrower than what the client registered for — never wider. Refresh-token rotation
preserves the scope.
The read/write gate applies to OAuth tokens only. API keys stay full-access — the per-model registry and Odoo's own access rights remain their control (and an MCP only key already narrows where a key can be used).
Custom Tools
Instead of enabling generic create/write on a model, an administrator can expose a
curated verb — e.g. confirm_sale_order with a narrow input schema — by wrapping an
Odoo server action in a custom tool. Manage them under Settings > Technical >
MCP > Custom Tools (MCP Administrator group):
Field | Meaning |
Name | The tool name the LLM calls ( |
Description | The tool's contract with the LLM — say what it does and what each argument is |
Input Schema | JSON Schema (a JSON object) advertised to clients in |
Read-only | Advertised as the tool's |
Server Action | The |
The tool's arguments and result flow through a shared mcp dict exposed to the
server action's Python code: read mcp['args'] (the client-supplied arguments) and
assign mcp['result'] (returned to the client, serialized as JSON text). A minimal
copy-paste code action:
# Server action -> Python Code
mcp['result'] = {'echo': mcp['args'].get('x')}Calling this tool with {"x": "hello"} returns {"echo": "hello"}.
Custom tools must wrap a Python Code action. A custom tool must wrap a Python Code server action that reads
mcp['args']and assignsmcp['result'](returned to the client as JSON text). Other server-action types (Update a Record, Create a new Record, Duplicate a Record, Send Webhook Notification, Multi Actions, and Discuss actions such as Send Email) are not supported and are rejected when you save the tool: they run once per selected record, and a tool call passes no record, so the action would silently do nothing while still reporting success. A Python Code action that assigns nothing tomcp['result']still runs — the client just receives a generic success message instead of a structured result.
Access model — read this before exposing a tool:
The action runs as the calling user, so their Odoo access rights (ACLs) apply to everything the action does — a custom tool never grants elevated rights.
Who may call the tool is the action's own Allowed Groups: a user must be a member of one of them. If the action has no Allowed Groups, access falls back to requiring write access on the action's model (Odoo's core rule) — so even a read-only tool then needs model write access. Set Allowed Groups explicitly to control access cleanly.
The action's model does not need to be in the Enabled Models list, and — for a model that is enabled — its per-operation Allow Read/Create/Write/Delete flags do not apply to a custom tool. The tool-level access gate (plus the caller's Odoo ACLs) is the control, so a custom tool can perform an operation you disabled there (e.g. create a contact while
res.partnerAllow Create is off). Scope the wrapped action narrowly.A tool a user may not run is hidden from their
tools/listand refused (with a sanitized access-denied error) if called directly.Under a read-only OAuth session (
mcp:read), only tools marked Read-only are listed and callable.
If an action raises, the whole call is rolled back and the client receives a sanitized
error (no traceback or SQL is leaked). Every custom-tool call is written to the audit
log (mcp.log).
Metadata is visible to authorized callers. A custom tool's name, description and input schema are shown (via an internal privileged read in the MCP controller — there is no direct ACL grant on the model) to exactly the users allowed to run it: members of the action's Allowed Groups (including portal users), or — when no groups are set — users with write access to the action's model. The wrapped action's code is never exposed by this read (it stays restricted to the Settings/Technical group on
ir.actions.server). Still, do not put secrets in a tool's name or description.
Installation
Step 1: Install from Odoo App Store
Download the module
Copy to Odoo addons
cp -r mcp_server /path/to/odoo/addons/Update the module list:
Navigate to Apps in Odoo
Click "Update Apps List"
Search for "MCP Server"
Click Install on the MCP Server module
Step 2: Configure the Module
Navigate to Settings:
Go to Settings > MCP Server
Turn on Enable MCP Server (the master switch). Optional switches live here too: Allow OAuth 2.1 login, rate limiting and its request limit, logging and its retention, the default/maximum record limits for tool responses (plus the smart field and related-item caps), and an optional Allowed Browser Origins allowlist (empty by default = any Origin accepted; when set, browser requests from other Origins get HTTP 403 — native clients send no Origin header and are never affected).
Enable Models:
Click "Configure Models" or go to Settings > Technical > MCP > MCP Available Models
Add models you want to expose (e.g., res.partner, product.product)
Set permissions for each model:
✅ Can Read
✅ Can Write
✅ Can Create
✅ Can Delete
(optional) Allow Method Calls — opt-in for
call_model_method
Connect a client — see Connecting AI Assistants: OAuth needs no further setup; for API keys, mint one under My Profile > Account Security (choose MCP only for the smallest blast radius).
Security Groups
The module creates two security groups:
MCP Administrator: Can configure MCP settings, manage enabled models, custom tools, OAuth clients/tokens, and read the audit log
MCP User: Required to use MCP. Only members can authenticate on any MCP surface — the native
/mcpendpoint (API key or OAuth), the REST endpoints and the XML-RPC proxy — and only members can complete the OAuth consent flow. A valid credential whose user is not a member is refused with an audited 403 (the OAuth consent screen shows a clear error instead), and removing the group cuts off the user's outstanding API keys and OAuth tokens immediately. What a member can read or write is still bounded by the per-model MCP permissions and the user's own Odoo access rights.
Assign users to appropriate groups in Settings > Users & Companies > Users. MCP Administrator implies MCP User, and system administrators (Settings access) are MCP Administrators automatically. Portal users cannot be members (the group implies Internal User), so portal accounts can never connect to MCP.
Upgrading: users with existing MCP activity (an MCP only API key, an OAuth token, or an audit-log entry recording a completed MCP operation) are added to the group automatically; assign it manually for anyone else. The audit-log signal only reaches users active within
mcp_server.log_retention_days(default 30) — the daily cleanup cron deletes older entries — so users connecting with a global orrpc-scope API key on a longer cadence will need the group assigned manually.
Multi-Database Deployment
Before it can authenticate a request, Odoo must resolve which database the request
targets — and the Bearer token / OAuth session can't select one, because the API key and
OAuth token live inside a database. So on an instance serving more than one
database, every /mcp* route (including the public /mcp/health and the OAuth
discovery documents) returns 404 "No database is selected" until the target database
is resolved from the request itself. Resolve it at the transport layer:
Hostname per database (recommended) — the standard Odoo multi-tenant setup: give each database its own host and let Odoo map host → database.
# odoo.conf proxy_mode = True list_db = False # hide the database manager dbfilter = ^%d$ # subdomain → db (use ^%h$ to match the full host)Point each client at its own host (e.g.
https://acme.example.com/mcp). This is the only option that works for browser OAuth clients (Claude.ai web, Gemini): they can't send a custom header, but the whole flow stays on one host, sodbfilterresolves it. Discovery documents and consent redirects are built from the request host, so each tenant automatically advertises its own correct URLs.X-Odoo-Databaseheader — Bearer / API-key clients that can set headers (Claude Code,curl, custom integrations) can sendX-Odoo-Database: <db>alongsideAuthorization, with no hostname routing. This does not help browser OAuth clients.Single database — pin the instance with
db_name = <db>(ordbfilter = ^<db>$).
A single-database instance needs none of this — Odoo auto-selects the only database.
API Endpoints
Native MCP Endpoint
Endpoint | Method | Description |
| POST | Native MCP server (Streamable HTTP, JSON-RPC 2.0). Auth via |
| POST | Legacy alias of |
OAuth 2.1
The module is an OAuth 2.1 authorization server, so browser clients can log into Odoo (see Option 1). These endpoints are public (no API key required).
Endpoint | Method | Description |
| GET | RFC 9728 protected-resource metadata for |
| GET | RFC 8414 authorization-server metadata |
| GET/POST | Odoo-login + per-request consent screen; issues an authorization code |
| POST | Exchange an authorization code / refresh token for opaque tokens (PKCE S256) |
| POST | RFC 7591 dynamic client registration (public PKCE client, IP rate-limited) |
REST API
All REST endpoints except /mcp/health require API key authentication via X-API-Key header.
Endpoint | Method | Description |
| GET | Health check (no auth required) |
| GET | Get database and server information |
| GET | Validate API key |
| GET | List all MCP-enabled models |
| GET | Check access permissions for a model |
XML-RPC API
MCP-specific XML-RPC endpoints with enhanced access control (used by the standalone client):
Endpoint | Description |
| Authentication services |
| Database operations |
| Model operations with MCP access control |
Usage Example
Testing the Installation
Check health endpoint:
curl https://your-odoo.com/mcp/healthValidate API key:
curl https://your-odoo.com/mcp/auth/validate \ -H "X-API-Key: your-api-key-here"List enabled models:
curl https://your-odoo.com/mcp/models \ -H "X-API-Key: your-api-key-here"Exercise the MCP handshake (or use MCP Inspector for an interactive session):
curl -X POST https://your-odoo.com/mcp \ -H "Authorization: Bearer your-api-key-here" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
Security Considerations
Prefer OAuth for interactive users: tokens are short-lived, bound to one resource, revocable per client, and never need to be copied around
API Key Security: keep API keys secure and rotate them regularly; prefer MCP only keys so a leak cannot reach general RPC
Least privilege: only enable models that are necessary; grant read-only OAuth consent where write access is not needed
Model Access:
call_model_methodis off by default — leave it off unless neededHTTPS: always use HTTPS in production environments
Rate Limiting: the module includes per-user rate limiting for API endpoints
Audit Trail: all MCP operations, denials and auth attempts are logged (Settings > Technical > MCP > MCP Logs)
Development
Running Tests
# Run all MCP module tests
/path/to/odoo-bin \
-d your_database \
-u mcp_server \
--test-enable \
--test-tags /mcp_server \
--stop-after-initTroubleshooting
Ensure the module is in the correct addons path
Update the apps list in Odoo (Apps > Update Apps List)
Check module dependencies are satisfied
Verify the module manifest file is valid
Confirm MCP is enabled (Settings > MCP Server) and the Allow OAuth 2.1 login switch is on (system parameter
mcp_server.enable_oauth)The authorize/consent screen requires a normal Odoo login for the connecting user — check the user is active and can log into the web client
Check whether the token was revoked or its client deactivated under Settings > Technical > MCP — the client must re-authenticate after a revocation
Access tokens expire after 1 hour; clients refresh automatically via the refresh token. If the refresh token was already used (rotation reuse), the whole grant is revoked as a safety measure — log in again
Behind a reverse proxy, make sure Odoo sees the correct external HTTPS URL (
web.base.url) so the discovery documents and consent redirects point at the right origin
Verify the key is active in the user's API Keys tab
A 403 means the key is valid but the user is not in the MCP User (or MCP Administrator) group — membership is required on every MCP surface; the refusal is recorded in the audit log with the user
Ensure the header is properly formatted (
Authorization: Bearer <key>on/mcp;X-API-Keyon the REST endpoints)An MCP only key works only on
/mcp— it is rejected on the REST/XML-RPC endpoints by designTry regenerating the API key
Check that the user account is active and not archived
Confirm the model is in the MCP enabled models list (Settings > Technical > MCP > MCP Available Models)
Check the specific permissions (read/write/create/delete) for that model
Verify the user's security group membership
Ensure the user has Odoo permissions for that model too
Check if record rules are blocking access
On an OAuth session: a read-only consent (
mcp:read) hides and refuses all write tools — reconnect and leave the write checkbox checked if writes are needed
Verify your Odoo URL is accessible from the client
Check if HTTPS is properly configured
Ensure firewall rules allow the connection
Test with the health endpoint first:
curl https://your-odoo.com/mcp/healthCheck Odoo logs for any error messages
The Odoo instance serves more than one database, so Odoo can't tell which one the request targets — every
/mcp*route 404s and the body reads "No database is selected"Resolve the target database at the transport layer — see Multi-Database Deployment: hostname-based
dbfilter(required for browser OAuth clients), anX-Odoo-Database: <db>header (Bearer / API-key clients), or pin a single database withdb_nameSingle-database instances are unaffected — Odoo auto-selects the only database
Enable only necessary models to reduce overhead
Use field filtering in API calls to limit data transfer
Consider implementing caching in your client
Check if rate limiting is affecting your requests
Monitor Odoo server resources (CPU, memory, database)
License
This module is licensed under OPL-1. See LICENSE for the full text.
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
- odooOAuthcom.odooconsole
Odoo ERP for AI agents: hosted OAuth endpoint, gated writes, one endpoint for every instance.
- mcpOAuthcom.vibgrate
Query your team's drift, vulnerability, and upgrade data from any AI assistant. OAuth 2.1, 51 tools.
- OpenOakOAuthorg.openoak
Secure AI access to OpenOak tasks, notes, and Kanban boards.
Document sharing, invoicing, and personal finance platform. 15+ AI tools via OAuth 2.1.