MCP Server for Odoo 19+ with v2 JSON-2 API Support
____ __ __ _____________ _______
/ __ \____/ /___ ____ / |/ / ____/ __ \ < / __ \ __
/ / / / __ / __ \/ __ \ / /|_/ / / / /_/ / / / /_/ /_/ /_
/ /_/ / /_/ / /_/ / /_/ / / / / / /___/ ____/ / /\__, /_ __/
\____/\__,_/\____/\____/ /_/ /_/\____/_/ /_//____/ /_/Connect Claude and AI assistants to Odoo 19+ via the Model Context Protocol (MCP).
execute_method calls ANY method on ANY model. Combined with batch_execute, execute_workflow, configure_odoo, and read_resource, you have complete Odoo API access--env-file for secrets, mandatory HTTP authPull from Docker Hub:
docker pull alanogik/odoo-mcp-19:latestOr build from source:
git clone https://github.com/AlanOgic/odoo-mcp-19.git
cd odoo-mcp-19
docker build -t odoo-mcp-19:latest .Quick test (verify it connects):
docker run --rm -i \
-e ODOO_URL=https://your-instance.odoo.com \
-e ODOO_DB=your-database \
-e ODOO_USERNAME=your-username \
-e ODOO_API_KEY=your-api-key \
odoo-mcp-19:latestYou should see the server start without errors. Press Ctrl+C to stop.
# 1. Clone the repository
git clone https://github.com/AlanOgic/odoo-mcp-19.git
cd odoo-mcp-19
# 2. Create a virtual environment
python3 -m venv .venv
# 3. Activate it
source .venv/bin/activate # macOS / Linux
# .venv\Scripts\activate # Windows
# 4. Install the package
pip install -e .
# 5. Create your .env file
cp .env.example .env # Then edit with your Odoo credentials
# Or create manually:
cat > .env << 'EOF'
ODOO_URL=https://your-instance.odoo.com
ODOO_DB=your-database
ODOO_USERNAME=your-username
ODOO_API_KEY=your-api-key
EOF
# 6. Test it
python -m odoo_mcppip install odoo-mcp-19Edit your Claude Desktop configuration file:
~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%\Claude\claude_desktop_config.jsonMethod 1: Using run-docker.sh wrapper (simplest)
Create a .env file in the project root with your credentials, then:
{
"mcpServers": {
"odoo": {
"command": "/path/to/odoo-mcp-19/run-docker.sh"
}
}
}Method 2: Inline Docker command
{
"mcpServers": {
"odoo": {
"command": "docker",
"args": ["run", "--rm", "-i",
"-e", "ODOO_URL=https://your-instance.odoo.com",
"-e", "ODOO_DB=your-database",
"-e", "ODOO_USERNAME=your-username",
"-e", "ODOO_API_KEY=your-api-key",
"odoo-mcp-19:latest"
]
}
}
}{
"mcpServers": {
"odoo": {
"command": "/path/to/odoo-mcp-19/.venv/bin/python",
"args": ["-m", "odoo_mcp"],
"env": {
"ODOO_URL": "https://your-instance.odoo.com",
"ODOO_DB": "your-database",
"ODOO_USERNAME": "your-username",
"ODOO_API_KEY": "your-api-key"
}
}
}
}{
"mcpServers": {
"odoo": {
"command": "odoo-mcp-19",
"env": {
"ODOO_URL": "https://your-instance.odoo.com",
"ODOO_DB": "your-database",
"ODOO_USERNAME": "your-username",
"ODOO_API_KEY": "your-api-key"
}
}
}
}Restart Claude Desktop after saving the config file.
Ask Claude: "List the first 5 partners in Odoo"
# Search partners
execute_method("res.partner", "search_read",
kwargs_json='{"domain": [["is_company", "=", true]], "fields": ["name", "email"], "limit": 10}')
# Create a partner
execute_method("res.partner", "create",
args_json='[{"name": "ACME Corp", "is_company": true, "email": "[email protected]"}]')
# Update with auto-resolved Many2one (no need to know user ID)
execute_method("res.partner", "write",
args_json='[[42], {"name": "ACME Corp Updated"}]',
resolve_json='{"user_id": {"model": "res.users", "search": "John"}}')
# Confirm a sale order (2-step safety confirmation with token)
# Step 1: triggers safety gate, returns confirmation_token in hint
result = execute_method("sale.order", "action_confirm", args_json='[[15]]')
# Step 2: confirm with the token from step 1
execute_method("sale.order", "action_confirm", args_json='[[15]]',
confirmed=true, confirmation_token='<token from step 1>')
# Multi-step workflow in one call
execute_workflow("quote_to_cash", '{"order_id": 123}')| Tool | Purpose |
|---|---|
execute_method | Call any method on any Odoo model |
batch_execute | Multiple operations with progress tracking |
execute_workflow | Pre-built multi-step workflows |
configure_odoo | Interactive connection setup |
read_resource | Read any odoo:// resource by URI |
| Resource | Description |
|---|---|
odoo://models | List all models |
odoo://model/{name} | Model info with fields |
odoo://model/{name}/schema | Full fields and relationships |
odoo://model/{name}/fields | Lightweight field list with labels |
odoo://model/{name}/quick-schema | Ultra-compact schema (~1.5KB, short keys) |
odoo://model/{name}/workflow | State machine transitions and side effects |
odoo://model/{name}/docs | Rich docs: labels, help text, selections |
odoo://bundle/{models} | Batch quick-schema for N models (max 10) |
odoo://session-bootstrap | Bootstrap: schemas + workflows for common models |
odoo://record/{model}/{id} | Get a specific record by ID |
odoo://methods/{model} | Available methods (enriched with live signatures) |
odoo://docs/{model} | Documentation URLs |
odoo://concepts | Business term to model mappings |
odoo://find-model/{concept} | Natural language to model name |
odoo://tools/{query} | Search available operations |
odoo://actions/{model} | Discover model actions |
odoo://templates | List all resource templates |
odoo://tool-registry | Pre-built workflows |
odoo://module-knowledge | Special methods knowledge |
odoo://module-knowledge/{name} | Knowledge for a specific module |
odoo://workflows | Business workflows |
odoo://server/info | Odoo server information |
odoo://domain-syntax | Domain operator reference |
odoo://pagination | Pagination guide |
odoo://hierarchical | Parent/child tree query patterns |
odoo://aggregation | Aggregation guide (formatted_read_group) |
odoo://model-limitations | Known model issues + runtime problems |
| Prompt | Purpose |
|---|---|
odoo-exploration | Discover instance capabilities |
search-records | Search for records in a model |
odoo-api-reference | Quick API reference card |
quote-to-cash | Complete sales workflow |
ar-aging-report | Accounts receivable aging |
inventory-check | Stock levels analysis |
crm-pipeline | Pipeline analysis |
customer-360 | Complete customer view |
daily-operations | Operations dashboard |
domain-builder | Build complex domain filters |
hierarchical-query | Query parent/child trees |
paginated-search | Paginate large result sets |
aggregation-report | Aggregation reports |
Pre-execution safety classification gates dangerous operations behind confirmation.
| Level | Behavior | Confirm? |
|---|---|---|
SAFE | Execute immediately | Never |
MEDIUM | Gate based on mode/volume | Conditional |
HIGH | Always require confirmation | Always |
BLOCKED | Always refuse | N/A |
ir.rule, ir.model.access, ir.module.module, ir.config_parameter, ir.model, ir.model.fields, res.users, res.groups
account.move, account.payment, account.bank.statement, hr.payslip, ir.cron
Side effects are surfaced for workflow actions:
sale.order + action_confirm → creates deliveriesaccount.move + action_post → creates journal entries (irreversible)stock.picking + button_validate → updates stock levelspurchase.order + button_confirm → creates incoming receiptsaccount.payment + action_post → creates journal entries + reconciliationexecute_method(model, method, args_json)pending_confirmation=true with safety classification and a confirmation_token in the hint fieldconfirmed=true AND confirmation_token='<token>'Tokens are single-use, expire after 120s, and are bound to the specific model+method. This prevents agents from bypassing the safety gate by always passing confirmed=true.
odoo://model/{model}/quick-schema — Ultra-compact schema with short keys: t (type), req (required), ro (readonly), rel (relation). ~60-80% smaller than /fields.
odoo://bundle/res.partner,sale.order,stock.picking — Batch quick-schema for up to 10 models in one call.
odoo://session-bootstrap — One call to bootstrap a conversation with schemas + workflows for common models. Configure via MCP_BOOTSTRAP_MODELS env var.
odoo://model/{model}/workflow — State machine transitions for 6 main models with side effects and irreversibility flags. Dynamic fallback for unmapped models.
Auto-resolve field names to IDs with resolve_json:
execute_method("res.partner", "write",
args_json='[[1], {"user_id": null}]',
resolve_json='{"user_id": {"model": "res.users", "search": "John"}}')Set MCP_DEFAULT_CONTEXT to apply context to all operations:
export MCP_DEFAULT_CONTEXT='{"lang": "fr_FR", "tz": "Europe/Paris"}'~25 error patterns with actionable suggestions covering 422, 500, 403, 404 errors and fallback patterns.
Run as an HTTP server with Bearer token authentication for remote or multi-client access.
Add to your .env:
MCP_TRANSPORT=streamable-http
MCP_API_KEY=your-secret-bearer-tokenThen:
docker compose up -dThe MCP endpoint is available at http://localhost:8080/mcp.
docker run -d -p 8080:8080 \
-e ODOO_URL=https://your.odoo.com \
-e ODOO_DB=mydb \
-e ODOO_USERNAME=admin \
-e ODOO_API_KEY=xxx \
-e MCP_TRANSPORT=streamable-http \
-e MCP_API_KEY=your-secret-token \
odoo-mcp-19:latest{
"mcpServers": {
"odoo": {
"type": "streamable-http",
"url": "http://localhost:8080/mcp",
"headers": {
"Authorization": "Bearer your-secret-token"
}
}
}
}# Should succeed (200)
curl -i -X POST http://localhost:8080/mcp \
-H "Authorization: Bearer your-secret-token" \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "method": "initialize", "id": 1}'
# Should fail (401/403) — wrong or missing token
curl -i -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "method": "initialize", "id": 1}'| Variable | Required | Default | Description |
|---|---|---|---|
ODOO_URL | Yes | — | Odoo server URL |
ODOO_DB | Yes | — | Database name |
ODOO_USERNAME | Yes | — | Username |
ODOO_API_KEY | Yes | — | API key (Settings > Users > API Keys) |
ODOO_PASSWORD | No | — | Password (fallback if no API key) |
ODOO_TIMEOUT | No | 30 | Request timeout in seconds |
ODOO_VERIFY_SSL | No | true | SSL certificate verification |
MCP_TRANSPORT | No | stdio | Transport: stdio or streamable-http |
MCP_API_KEY | Yes (HTTP) | — | Bearer token for HTTP auth (required for streamable-http) |
MCP_HOST | No | 0.0.0.0 | HTTP bind address |
MCP_PORT | No | 8080 | HTTP port |
MCP_SAFETY_MODE | No | strict | strict or permissive |
MCP_SAFETY_AUDIT | No | — | true to log safety audit to stderr |
MCP_DEFAULT_CONTEXT | No | — | JSON object merged into all contexts |
MCP_BOOTSTRAP_MODELS | No | res.partner,sale.order,account.move,product.product,stock.picking | Models for session-bootstrap |
Full documentation in the Wiki:
MCP_API_KEY — server refuses to start without itodoo:// only).env, .mcp.json, odoo_config.jsonODOO_VERIFY_SSL=false with HTTPSMCP_DEFAULT_CONTEXT — capped at 4KB, MCP_BOOTSTRAP_MODELS capped at 20 modelsMIT
AlanOgic/odoo-mcp-19
January 26, 2026
April 13, 2026
Python