| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
Discuss (mail)
|
| Lines of code | 2962 |
| Technical Name |
muk_mcp |
| License | LGPL-3 |
| Website | http://www.mukit.at |
| Versions | 15.0 16.0 17.0 18.0 19.0 |
MuK MCP Server
Model Context Protocol Server for AI Agent Integration
MuK IT GmbH - www.mukit.at
Overview
Turn your Odoo instance into a native MCP (Model Context Protocol)
server. Any MCP-compatible AI client — Claude Code, OpenCode,
Claude Desktop, Cursor, Windsurf, Codex CLI — can connect to the
/mcp endpoint and interact with your business data through
a standardized set of tools. No middleware, no extra processes.
Odoo is the MCP server.
Ships with 15 production-ready tools covering the full Odoo ORM surface: model discovery, schema introspection, search, read, create, update, delete, grouped aggregation, method execution, and chatter integration. Add your own tools through the backend UI — no code deployment required.
Native MCP Endpoint
The /mcp endpoint speaks the full MCP Streamable HTTP
protocol natively, handling JSON-RPC 2.0 messages for
initialize, tools/list, and
tools/call. Supports stateful sessions, Server-Sent
Events for real-time notifications, and batch requests. Authentication
uses Bearer tokens — either dedicated MCP keys scoped to
Read Only or Read & Write, or standard Odoo
API keys.
Client Setup
Connect your preferred AI coding assistant in seconds. Generate an MCP key from your Odoo user preferences, then add the server to your client configuration.
Claude Code
claude mcp add odoo \ --transport http \ --url https://your-odoo.com/mcp \ --header "Authorization: Bearer YOUR_MCP_KEY"
Or add to your claude_code_config.json:
{
"mcpServers": {
"odoo": {
"type": "url",
"url": "https://your-odoo.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_MCP_KEY"
}
}
}
}
Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"odoo": {
"type": "url",
"url": "https://your-odoo.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_MCP_KEY"
}
}
}
}
OpenCode
Add to your .opencode/config.json or opencode.json:
{
"mcp": {
"odoo": {
"type": "remote",
"url": "https://your-odoo.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_MCP_KEY"
}
}
}
}
Cursor
Add to your .cursor/mcp.json:
{
"mcpServers": {
"odoo": {
"url": "https://your-odoo.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_MCP_KEY"
}
}
}
}
Codex CLI
codex --mcp-server "odoo=https://your-odoo.com/mcp" \
--mcp-header "odoo=Authorization: Bearer YOUR_MCP_KEY"
17 Built-in Tools
Ships with a complete set of ORM tools covering the full lifecycle of Odoo data — from discovery to manipulation. Every tool is manageable via the backend UI and can be enabled, disabled, or customized individually.
| Tool | Category | Description |
|---|---|---|
list_models |
Read | Discover available Odoo models by substring search |
list_modules |
Read | List installed modules with versions and states |
describe_model |
Read | Get field definitions, types, relations, and selection values |
whoami |
Read | Authenticated user info, company, language, and security groups |
get_access_rights |
Read | Check CRUD permissions and list access control rules |
search_read |
Read | Search records by domain with pagination and sorting |
read_records |
Read | Read specific records by their database IDs |
search_count |
Read | Count records matching a domain filter |
read_group |
Read | Grouped aggregation (GROUP BY) with sum/count |
get_messages |
Read | Retrieve chatter history, comments, and field tracking |
print_report |
Read | Render PDF/text/HTML reports and return the binary as base64 |
export_records |
Read | Export records to CSV or XLSX with relation traversal via / paths |
create_records |
Write | Create new records with relational field support |
update_records |
Write | Partial updates on existing records by ID |
delete_records |
Write | Permanently delete records by ID |
post_message |
Write | Post comments or internal notes on chatter threads |
call_method |
Write | Call any public method on a model (private methods blocked) |
Extensible Tool Registry
Ship tools from your own Odoo addons with the
@mcp_tool decorator — version-controlled,
testable, and running without the sandbox overhead. Inherit the
muk_mcp.mixin abstract model and decorate any public
method: the scanner picks it up at startup and exposes it to every
MCP client on the next tools/list. Declare
category='read' or 'write' to gate it
against the caller's key scope.
For admins and power users, the backend UI at
Settings > MCP Server > Tools adds tools without a
code deployment. Each DB tool carries a name, description, JSON
Schema for input parameters, and Python code executed in a sandboxed
safe_eval context. Tools can be enabled, disabled, and
reordered individually. Database tools with the same name as a
Python tool shadow it — handy for runtime overrides. The AI
client is automatically notified when the tool list changes.
An optional registry field lets downstream
modules restrict a tool to a specific surface. External MCP
clients, in-Odoo AI agents, and scheduled jobs can share the
same tool catalog or be scoped to their own subset — a
tool left unset is visible to every caller, and values can be
added via Odoo's selection_add pattern without
touching muk_mcp itself.
MCP Label
Every chatter message authored by an MCP tool is tagged with a small MCP badge next to the author name, so it is immediately obvious which comments, status changes, and tracking entries came from an AI client and which came from a human. The badge's tooltip shows the name of the MCP key that created the message — handy when multiple clients share the same user account. Annotation is on by default and can be toggled via Settings > General Settings > MCP Server > Annotate Messages.
Security & Access Control
Every MCP key carries a scope —
Read Only keys can only call tools in the read
category, Read & Write keys can call both. All calls
run as the key's owner, so Odoo's record rules, field-level ACLs,
and model access rights apply on top of the scope check.
Built-in rate limiting (configurable per key, default 60 req/min) protects against runaway AI loops. A full audit log records every MCP request with method, tool, model, duration, and status — accessible at Settings > MCP Server > Audit Log.
Session Management
The server maintains stateful sessions per the MCP specification. Sessions track the last activity timestamp and are automatically cleaned up after the configured timeout (default: 24 hours). Users can view and revoke their active MCP sessions from their user preferences. Administrators can manage all sessions from the backend menu.
Want more?
Are you having troubles with your Odoo integration? Or do you feel
your system lacks of essential features?
If your answer is YES
to one of the above questions, feel free to contact us at anytime
with your inquiry.
We are looking forward to discuss your
needs and plan the next steps with you.
Our Services
Odoo
Development
Odoo
Integration
Odoo
Infrastructure
Odoo
Training
Odoo
Support
MuK MCP Server
Implements a native MCP (Model Context Protocol) server inside Odoo, exposing business data and operations to any MCP-compatible AI client. The server speaks MCP Streamable HTTP at a single /mcp endpoint using Bearer token authentication (Odoo API keys or dedicated MCP keys).
Compatible clients include Claude Desktop, Claude Code, OpenCode, Cursor, Windsurf, Codex CLI, and any tool that supports the MCP Streamable HTTP transport.
Installation
To install this module, you need to:
Download the module and add it to your Odoo addons folder. Afterward, log on to your Odoo server and go to the Apps menu. Trigger the debug mode and update the list by clicking on the "Update Apps List" link. Now install the module by clicking on the install button.
Upgrade
To upgrade this module, you need to:
Download the module and add it to your Odoo addons folder. Restart the server and log on to your Odoo server. Select the Apps menu and upgrade the module by clicking on the upgrade button.
Configuration
Creating an MCP Key
- Log in to Odoo and navigate to your user preferences (Settings > Users > Preferences).
- In the Account Security tab, click Add MCP Key.
- Enter a description (e.g. "Claude Code") and pick a Scope (Read Only or Read & Write).
- Click Generate Key and copy the key immediately — it will not be shown again.
Server Settings
Navigate to Settings > General Settings > MCP Server to configure:
- Session Timeout — Hours after which inactive MCP sessions are cleaned up (default: 24).
- Log Retention — Days after which audit log entries are automatically deleted (default: 30).
API Key Scopes
Each MCP key has a scope that gates which tool categories it can call: Read Only keys can call tools declared with category='read', Read & Write keys can call both. Scope enforcement happens before the tool executes; Odoo's record rules and model ACLs still apply on top, so a key can never exceed the permissions of its owning user.
Rate Limiting
Each key has a configurable rate limit (requests per minute). Set to 0 for unlimited. The default is 60 requests per minute.
Client Setup
The MCP server endpoint is https://<your-odoo>/mcp. All clients authenticate via a Bearer token — either a dedicated MCP key (created in user preferences) or a standard Odoo API key with rpc scope.
Claude Code
claude mcp add odoo \ --transport http \ --url https://your-odoo.com/mcp \ --header "Authorization: Bearer YOUR_MCP_KEY"
Or add it directly to your claude_code_config.json:
{ "mcpServers": { "odoo": { "type": "url", "url": "https://your-odoo.com/mcp", "headers": { "Authorization": "Bearer YOUR_MCP_KEY" } } } }
Claude Desktop
Add to your claude_desktop_config.json:
{ "mcpServers": { "odoo": { "type": "url", "url": "https://your-odoo.com/mcp", "headers": { "Authorization": "Bearer YOUR_MCP_KEY" } } } }
OpenCode
Add the server to your .opencode/config.json or opencode.json:
{ "mcp": { "odoo": { "type": "remote", "url": "https://your-odoo.com/mcp", "headers": { "Authorization": "Bearer YOUR_MCP_KEY" } } } }
Cursor
Add to your .cursor/mcp.json:
{ "mcpServers": { "odoo": { "url": "https://your-odoo.com/mcp", "headers": { "Authorization": "Bearer YOUR_MCP_KEY" } } } }
Codex CLI
export MCP_ODOO_URL="https://your-odoo.com/mcp" export MCP_ODOO_KEY="YOUR_MCP_KEY" codex --mcp-server "odoo=$MCP_ODOO_URL" \ --mcp-header "odoo=Authorization: Bearer $MCP_ODOO_KEY"
cURL (testing)
curl -X POST https://your-odoo.com/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_MCP_KEY" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {} }'
Usage
Once connected, the AI client automatically discovers all available tools via the tools/list MCP method. The module ships with 17 built-in tools organized into two categories:
Read Tools (12)
- list_models — Discover available Odoo models by substring search.
- list_modules — List installed modules with versions and states.
- describe_model — Get complete field definitions for any model (types, labels, relations, selection values).
- whoami — Retrieve the authenticated user's name, company, language, timezone, and security groups.
- get_access_rights — Check the current user's CRUD permissions on a model and list all access control rules.
- search_read — Search records by domain and return field values with pagination and sorting.
- read_records — Read specific records by their database IDs.
- search_count — Count records matching a domain filter.
- read_group — Grouped aggregation (SQL GROUP BY equivalent) with automatic sum/count for numeric fields.
- get_messages — Retrieve chatter history, comments, and field tracking for a record.
- print_report — Render an ir.actions.report (PDF, text, HTML) for one or more records and return the binary as base64. Accepts the report xmlid, report_name, or numeric id.
- export_records — Export records to CSV or XLSX (base64). Field paths use / to traverse relations (partner_id/name, order_line/product_id/default_code). Honours record rules and field access through Odoo's export_data.
Write Tools (5)
- create_records — Create new records with support for relational field command tuples.
- update_records — Update existing records by ID (partial writes).
- delete_records — Permanently delete records by ID.
- post_message — Post comments or internal notes on a record's chatter thread.
- call_method — Call any public method on a model or recordset (private methods starting with _ are blocked for safety).
Extending the Tool Set
There are two ways to add tools. Choose based on audience:
- Python tools (@mcp_tool decorator) — the recommended path for Odoo developers shipping tools inside their own addons. Code lives in your module, is version-controlled, testable, and runs without the safe_eval sandbox overhead.
- UI tools (database-backed) — for administrators or power users who want to add ad-hoc tools without deploying code.
Both coexist. If a database tool has the same name as a decorated method tool, the database tool shadows the method tool (useful for runtime overrides during development).
Python Tools — From Other Addons
Any Odoo addon can register MCP tools by inheriting muk_mcp.mixin and decorating public methods with @mcp_tool. The muk_mcp scanner walks the mixin's MRO at worker startup and exposes every decorated method automatically — no explicit registration call needed.
Step 1 — declare the dependency in your __manifest__.py:
{ 'name': 'My Sales Tools', 'depends': ['muk_mcp', 'sale'], ... }
Step 2 — extend muk_mcp.mixin and decorate your methods:
# my_sale_mcp/models/mcp_tools.py from odoo import api, models from odoo.addons.muk_mcp.core.tool import mcp_tool class MCPMixin(models.AbstractModel): _inherit = 'muk_mcp.mixin' @api.model @mcp_tool( name='confirm_sale_order', description=( 'Confirm a quotation by ID. Transitions the order from ' "'draft' to 'sale' and generates the delivery." ), input_schema={ 'type': 'object', 'properties': { 'id': { 'type': 'integer', 'description': 'sale.order record ID.', }, }, 'required': ['id'], }, category='write', ) def confirm_sale_order(self, id): order = self.env['sale.order'].browse(id) order.action_confirm() return {'id': order.id, 'state': order.state}
Step 3 — restart or upgrade the module. The tool appears in the next tools/list response from any MCP client.
Decorator reference
@mcp_tool(name=None, description=None, input_schema=None, category='read', registry=None)
- name — MCP tool name exposed to the AI client. Defaults to the Python method name. Must be unique across all installed addons.
- description — Human-readable explanation shown to the AI. When omitted, the first line of the method's docstring is used.
- input_schema — JSON Schema object describing the tool arguments. When omitted, an empty-object schema is used. Keys in properties become kwargs on the method call.
- category — 'read' or 'write'. Read-scoped MCP keys can only call 'read' tools; write-scoped keys call both. Scope is enforced at call time via MCPScopeDenied.
- registry — Optional surface restriction. Leave None (the usual case) so the tool is visible to every caller. Set to 'mcp' to restrict the tool to external MCP clients, or to a value added by a downstream module (muk_ai adds 'ai' via selection_add) to target a specific agent. Callers pass registry= to get_tools() / get_tool_index() to filter. Comma-separated values are supported (registry='mcp,cron' → visible on both surfaces).
How arguments flow
- The MCP client sends tools/call with a JSON arguments dict.
- muk_mcp.tool._call pops any context key, merges it into self.env.context, then invokes the decorated method with the remaining keys as keyword arguments.
- The return value is serialized via RecordEncoder (recordsets become [(id, display_name), ...]; datetimes and bytes are coerced to strings).
- Raising UserError or AccessError bubbles the message back to the AI client as a tool error.
Helpers available on ``muk_mcp.mixin``
Because your class inherits the mixin, you get two small helpers for free:
- self._resolve_model(name) — returns self.env[name] and raises UserError if the model does not exist.
- self._normalize_ids(ids) — accepts None, a single int, or a list of ints; always returns a list.
Testing your tools
Call muk_mcp.tool._call directly from a TransactionCase:
from odoo.tests import common class TestMySaleTools(common.TransactionCase): def test_confirm_sale_order(self): order = self.env['sale.order'].create({...}) text, info = self.env['muk_mcp.tool']._call( 'confirm_sale_order', {'id': order.id}, self.env, ) self.assertEqual(info['res_id'], order.id) self.assertEqual(order.state, 'sale')
The second return value is a record_info dict (res_id / res_ids) used by the audit log; ignore it if you don't need it.
UI Tools — From the Admin Backend
Additional tools can be created through the backend UI at Settings > MCP Server > Tools. Each tool consists of:
- A name and description (exposed to the AI client).
- A JSON Schema defining the input parameters.
- Python code executed in a sandboxed safe_eval context with access to env, arguments, json, UserError, and logger.
Tools are categorized as Read or Write and can be enabled/disabled individually. When a UI tool shares a name with a Python tool, the UI tool wins — useful for overriding decorator tools at runtime without redeploying code.
Audit Log
Every MCP request is logged with the method, tool name, target model, duration, and status (ok, error, denied, rate_limited). Logs are accessible at Settings > MCP Server > Audit Log and are automatically cleaned up based on the configured retention period.
Sessions
The server maintains stateful sessions per the MCP specification. Active sessions are visible at Settings > MCP Server > Sessions (in debug mode) and can be revoked from user preferences. Sessions are automatically cleaned up after the configured timeout.
Credits
Contributors
- Mathias Markl <mathias.markl@mukit.at>
Author & Maintainer
This module is maintained by the MuK IT GmbH.
MuK IT is an Austrian company specialized in customizing and extending Odoo. We develop custom solutions for your individual needs to help you focus on your strength and expertise to grow your business.
If you want to get in touch please contact us via mail (sale@mukit.at) or visit our website (https://mukit.at).
Please log in to comment on this module