🚀 Odoo MCP Server Integration
Expose a Model Context Protocol (MCP) server directly from your Odoo instance—no external services required.
With this module, tools like Claude or Cursor can securely query and manage your Odoo data using natural language.
Key benefits:
- No separate backend process
- Native Odoo integration
- Real-time interaction via SSE
- Full ORM access (read/write)
⚙️ Setup
1. Install the Module
Install the module on your Odoo database like any standard addon.
2. Specify the Database
You can pass the database using one of the following methods:
- URL parameter: ?db=<name>
- Header: X-Odoo-Database
- Authorization: Bearer demo_ai:YOUR_API_KEY
3. Connect with Claude / Cursor
Use mcp-remote with:
- --transport sse-only
- --allow-http
4. Fix 404 Errors
If you encounter a 404 error:
[options] server_wide_modules = web,rag_odoo_mcp_server
Restart Odoo after applying changes.
💡 Usage Examples
🔍 Data Retrieval
Natural language queries are translated into MCP tool calls:
- “Show me all customers from Spain”
→ odoo_search_read(res.partner, country_id.code = ES) - “Find products with stock below 10 units”
→ Use product.product or stock.quant - “List today's sales orders over $1000”
→ Filter sale.order by amount - “Search for unpaid invoices from last month”
→ Filter account.move by payment state
✏️ Data Management
- “Create a new customer contact for Acme Corporation”
→ odoo_create(res.partner, {...}) - “Add a new product ‘Premium Widget’ with price $99.99”
→ odoo_create(product.product, {...}) - “Update the phone number for John Doe”
→ odoo_search_read → odoo_write - “Confirm order SO/2024/001”
→ odoo_execute(action_confirm) - “Delete the test contact”
→ odoo_unlink(...)
🔌 API Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /mcp/sse?db=<database> | Open SSE stream |
| POST | /mcp/messages/?session_id=<id>&db=<database> | JSON-RPC calls |
| GET | /mcp/health?db=<database> | Health check |
Authentication (optional):
- Authorization: Bearer <key>
- X-API-Key: <key>
🧰 Available MCP Tools
- list_tables
- describe_table
- get_table_row_count
- run_readonly_query
- get_odoo_models_info
- get_table_schema_pg
- odoo_search_read
- odoo_create
- odoo_write
- odoo_unlink
- odoo_execute
🧪 Standalone Mode (Read-Only)
Run MCP without Odoo ORM:
pip install -r rag_odoo_mcp_server/requirements-mcp.txt python -m rag_odoo_mcp_server.mcp_server --host 0.0.0.0 --port 8000
SSE Endpoint:
http://<host>:8000/sse
⚠️ ORM methods (create, write, etc.) are not available in this mode.
🏢 Company Context (Optional)
Note: Now handled via UI configuration. Manual setup is kept for reference.
Priority order:
- Per-tool argument
- Per-request / session
- System default
Examples:
- Per-tool: company_id=2
- Request: ?company_id=2 or header
- Session: SSE URL param
- Default: Set in module settings
If unset → all companies are visible.
⚡ Nginx Optimization (Important)
For real-time SSE performance, disable buffering:
location /mcp/ {
proxy_pass http://your_odoo_backend;
proxy_buffering off;
proxy_set_header X-Accel-Buffering no;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
chunked_transfer_encoding on;
}
👉 Without this, SSE events may be delayed or blocked.
☁️ Odoo.sh Configuration
No need to specify the database:
{
"mcpServers": {
"odoo": {
"command": "npx",
"args": [
"mcp-remote",
"https://link.dev.odoo.com/odoo/mcp/sse",
"--allow-http",
"--transport",
"sse-only",
"--header",
"Authorization:${MCP_CRM_AUTH}"
],
"env": {
"MCP_CRM_AUTH": "Bearer tokenkey"
}
}
}
}
⚠️ Troubleshooting
Error: HTTP 400 Unknown or expired session
✅ Option 1 (Recommended for VPS)
- SSH into your server
- Set:
workers = 0
- Restart Odoo
✅ Option 2 (Production Setup – Nginx Sticky Sessions)
Use session-based routing:
map $arg_session_id $mcp_sticky {
default $remote_addr;
~.+ $arg_session_id;
}
upstream odoo_mcp {
hash $mcp_sticky consistent;
server 127.0.0.1:8069;
server 127.0.0.1:8070;
}
Then configure:
location ~ ^/(odoo/)?mcp/ {
proxy_pass http://odoo_mcp;
proxy_http_version 1.1;
proxy_buffering off;
proxy_read_timeout 1h;
proxy_send_timeout 1h;
chunked_transfer_encoding on;
add_header X-Accel-Buffering no;
}
⚠️ Important Notes
- Odoo workers share ports by default → true sticky routing requires custom setup
- Single-instance deployments benefit most from Option 1
- Always disable buffering for SSE
Security overview — rag_odoo_mcp_server
Authentication
- Two modes, chosen in Settings → MCP Server → Authentication:
- API Token — two tiers: User (read-only) and Admin (read + write). Tokens are 32-byte URL-safe random (secrets.token_urlsafe), stored in ir.config_parameter, never displayed twice. Compared with constant-time secrets.compare_digest to defeat timing attacks.
- Odoo User Credentials — the LLM operates as a real Odoo user; all ORM access goes through that user's record rules and access rights, just like a logged-in human.
- The Admin token can be disabled entirely; without one, writes are refused even if the token is valid.
Transport
- The MCP server runs inside Odoo (no separate process, no extra ports). All traffic uses your existing Odoo HTTP/HTTPS endpoint — put Odoo behind your normal TLS proxy and the MCP traffic is encrypted with it.
- Auth headers are enforced on every /mcp/messages POST. The SSE GET is just a session bootstrap; no data leaves until the client proves itself on the next POST.
- Sessions are persisted in a DB table (multi-worker safe on Odoo.sh) and auto-expire via a 10-min GC cron.
Authorization (what the LLM can do)
- Write tools (odoo_create/odoo_write/odoo_unlink/odoo_execute, lead/campaign creation) are hidden from tools/list and refused on tools/call unless the Admin token (or an Odoo user with write rights) is used.
- Dashboard-only lockdown: an admin can flip a switch that restricts the LLM to building dashboards and refuses everything else.
- CRM Manager feature flag gates lead-gen and mailing tools behind a security group; off by default.
- Raw SQL is read-only: run_readonly_query accepts SELECT only and rejects any query containing INSERT/UPDATE/DELETE/DROP/CREATE/ALTER/TRUNCATE/GRANT/REVOKE.
Data residency & privacy
- Nothing is sent to RAG Solutions or any third party by the module. Your Odoo data goes only to the MCP client your customer connects (Claude Desktop / Cursor / etc.) — that's their existing AI vendor relationship, not ours.
- No telemetry, no phone-home, no external API calls. The module's only network surface is the MCP endpoint your customer chooses to expose.
Multi-company & audit
- company_id can be set as a default or per call; ORM operations run inside with_company() so Odoo's standard multi-company isolation applies.
- All writes go through the ORM, so Odoo's audit log (mail.message, mail-thread tracking) captures them as if a normal user made them.
What's left to the customer
- TLS termination, IP allow-listing, and reverse-proxy hardening are the customer's responsibility (same as any Odoo deployment).
- Token rotation: tokens have no expiry on our side — the admin should rotate them on a schedule. Re-clicking Generate User/Admin Token invalidates the previous one immediately.
Risk summary for your customers
| Concern | Mitigation |
|---|---|
| Data leakage to a third party | None — module makes no outbound calls; data flows only to the MCP client they choose |
| Token theft | Constant-time comparison, single-display generation, easy rotation, optional require-API-key enforcement |
| Unauthorized writes | Two-tier tokens, dashboard-only lockdown, Odoo-user mode that respects ACLs |
| Multi-tenant Odoo | Works with Odoo's standard multi-company isolation via company_id |
| SQL injection via the LLM | Read-only enforcement on raw SQL; ORM tools go through Odoo's normal sanitization |
| Audit | Standard Odoo audit log applies (writes are normal ORM writes) |
The module adds no new data egress path. Whatever risk model your customers already accept for connecting Claude/Cursor to their data applies here — they just gain a controlled, auditable, tokenized way to do it through Odoo.