AgentStack
MCP verified MIT Self-run

Odoo Mcp Gateway

mcp-parth-unjiya-odoo-mcp-gateway · by parth-unjiya

Security-first MCP server for Odoo 17/18/19 — YAML-driven security, zero Odoo-side code, 27 tools + 5 resources + 7 prompts

No reviews yet
0 installs
9 views
0.0% view→install

Install

$ agentstack add mcp-parth-unjiya-odoo-mcp-gateway

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Odoo Mcp Gateway? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

odoo-mcp-gateway

Security-first, version-agnostic MCP gateway for Odoo 17/18/19. Works with stock and custom modules via YAML configuration. Zero Odoo-side code required.

[](https://www.python.org/) [](LICENSE) [](https://www.odoo.com/) [](#testing) [](#testing)

30-Second Quick Start

pip install odoo-mcp-gateway

Add to Claude Desktop config (claude_desktop_config.json):

{
  "mcpServers": {
    "odoo": {
      "command": "python",
      "args": ["-m", "odoo_mcp_gateway"],
      "env": {
        "ODOO_URL": "http://localhost:8069",
        "ODOO_DB": "your_database"
      }
    }
  }
}

Restart Claude Desktop. In any conversation:

login with method "password", username "admin", credential "your_password"

You're connected. Ask Claude to query, create, or update Odoo records — every call is rate-limited, audit-logged, and runs through two layers of security checks before reaching Odoo.

No Odoo addon required. No Python code to write. Just YAML config for fine-grained access control (optional — secure defaults work out of the box).

What's New in v0.2.1

  • Brute-force protection — per-username (5/5min) + per-source (30/15min) lockout
  • dry_run mode on create_record, update_record, delete_record, execute_method — validate without executing
  • 2 new tools: get_defaults (preview Odoo defaults), get_onchange (preview field side effects)
  • Temporal grouping in read_group: create_date:month, date:quarter, etc.
  • Hardened blocklists: 32 always-blocked models (was 17), 29 always-blocked methods (was 18), 10 always-blocked write fields, 8 always read-only models
  • Server-side admin verification via has_group('base.group_system') (was trusted from auth response)
  • Credential wrapper prevents password leakage via repr()/traceback
  • JSON-RPC retry only fires on OdooSessionExpiredError (was retrying on every auth error)

See [CHANGELOG.md](CHANGELOG.md) for the full list of 21 security fixes.

Why This Exists

Existing Odoo MCP servers share common problems: hardcoded model lists that miss custom modules, security as an afterthought, mandatory custom Odoo addons, and single-version targets. This gateway solves all of them:

  • Two-layer security — MCP restrictions (YAML) + Odoo's built-in ACLs (ir.model.access + ir.rule)
  • YAML-driven configuration — model restrictions, RBAC, field-level access, rate limiting, audit logging
  • Custom module support — auto-discovers models via ir.model, add YAML config and it works
  • Version-agnostic — Odoo 17, 18, 19 with version-specific adapters
  • Zero Odoo-side codepip install + YAML config = done. No custom addon required
  • Full MCP primitives — 31 Tools + 6 Resources + 12 Prompts (most servers only implement Tools)
  • Plugin architecture — extend with pip-installable domain packs via entry_points

Architecture

MCP Client (Claude Desktop / Claude Code / HTTP)
    |  User calls login tool with Odoo credentials
    v
MCP Server (FastMCP)
    |
    |-- security_gate()    --> Rate limit + RBAC tool access + audit logging
    |-- restrictions       --> Model/method/field block lists (YAML + hardcoded)
    |-- rbac               --> Field-level filtering + write sanitization
    |
    |-- tools/             --> 31 MCP tools (auth + schema + CRUD + workflow + plugins)
    |-- resources/         --> 6 MCP resources (odoo:// URIs)
    |-- prompts/           --> 12 reusable prompt templates
    |-- plugins/           --> Entry-point plugin system (HR, Sales, Project, Helpdesk)
    |
    |  JSON-RPC / XML-RPC as authenticated user
    v
Odoo 17/18/19 (security enforced per user via ir.model.access + ir.rule)

Security Pipeline

Every tool and resource call passes through this pipeline:

Request --> Rate Limit --> Authentication Check --> RBAC Tool Access
    --> Model Restriction --> Method Restriction --> Field Validation
    --> Handler Execution --> RBAC Field Filtering --> Audit Log --> Response

Hardcoded safety guardrails that cannot be overridden by YAML:

  • 32 always-blocked models — system internals, auth/TOTP, payment tokens, attachments, mail.mail, base.automation, and more
  • 8 always read-only models — mail.message, mail.followers, mail.activity, discuss.channel, mail.notification, mail.compose.message, mail.alias, discuss.channel.member (reads OK, writes blocked for everyone)
  • 10 always-blocked write fields — password, passwordcrypt, groupsid, totpsecret, signuptoken/type/expiration, api_key, share, active
  • 29 always-blocked methods — sudo, withuser/env/context, sql, write, create, namecreate, load, importdata, export_data, and more
  • 28 ORM methods blocked in execute_method (prevents bypassing field-level checks)
  • Per-username brute-force lockout — 5 failures → 5 minute lockout (fixed duration, cannot be extended)
  • Per-source brute-force lockout — 30 failures / 15 min, prevents username-rotation attacks
  • Credential wrapper class — passwords stored with leak-safe __repr__/__str__, cleared on close
  • Server-side admin verificationhas_group('base.group_system') overrides auth-response is_admin

Quick Start

pip install odoo-mcp-gateway

# Copy and edit config files
cp config/restrictions.yaml.example config/restrictions.yaml
cp config/model_access.yaml.example config/model_access.yaml
cp config/rbac.yaml.example config/rbac.yaml

# Set environment variables
export ODOO_URL=http://localhost:8069
export ODOO_DB=mydb

# Run (stdio mode for Claude Desktop / Claude Code)
python -m odoo_mcp_gateway

# Or HTTP mode for web clients
MCP_TRANSPORT=streamable-http python -m odoo_mcp_gateway

Claude Desktop Configuration

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "odoo": {
      "command": "python",
      "args": ["-m", "odoo_mcp_gateway"],
      "env": {
        "ODOO_URL": "http://localhost:8069",
        "ODOO_DB": "mydb"
      }
    }
  }
}

Claude Code Configuration

# Add as MCP server
claude mcp add odoo -- python -m odoo_mcp_gateway

Environment Variables

| Variable | Default | Description | |----------|---------|-------------| | ODOO_URL | http://localhost:8069 | Odoo server URL | | ODOO_DB | (required) | Odoo database name | | MCP_TRANSPORT | stdio | Transport mode (stdio or streamable-http) | | MCP_HOST | 127.0.0.1 | HTTP host (streamable-http mode) | | MCP_PORT | 8080 | HTTP port (streamable-http mode) | | MCP_LOG_LEVEL | INFO | Logging level | | CONFIG_DIR | . | Directory for YAML config files | | SESSION_TIMEOUT_SECONDS | 1800 | Session inactivity timeout | | MAX_CONCURRENT_SESSIONS | 100 | Maximum concurrent sessions | | RATE_LIMIT_GLOBAL | 60 | Requests per minute (global) | | RATE_LIMIT_WRITE | 20 | Write operations per minute |

Security

Two-Layer Security Model

  1. MCP gateway restrictions (YAML config + hardcoded guardrails) — blocks sensitive models, dangerous methods, privileged fields before any Odoo call is made
  2. Odoo's built-in ACLs — enforces per-user access on actual records via ir.model.access and ir.rule

Model Restriction Tiers

| Tier | Effect | Example | |------|--------|---------| | always_blocked | Nobody can access, including admins | ir.config_parameter, res.users.apikeys | | admin_only | Only admin users | ir.model, ir.model.fields | | admin_write_only | Read OK for all, write needs admin | res.company, res.currency |

Hardcoded Safety Guardrails

These cannot be overridden by YAML configuration:

Blocked models (32): ir.config_parameter, res.users, res.users.apikeys, res.users.log, ir.cron, ir.module.module, ir.model.access, ir.rule, ir.mail_server, ir.ui.view, ir.actions.server, ir.logging, ir.attachment, ir.exports, ir.exports.line, iap.account, auth.totp.wizard, auth.totp.device, payment.token, payment.provider, base.automation, digest.digest, res.config.settings, change.password.wizard, change.password.user, base.module.update, base.module.upgrade, base.module.uninstall, fetchmail.server, bus.bus, mail.mail, mail.template

Read-only models (8): mail.message, mail.followers, mail.activity, discuss.channel, mail.notification, mail.compose.message, mail.alias, discuss.channel.member (reads allowed, writes blocked for everyone)

Blocked write fields (10): password, password_crypt, groups_id, totp_secret, signup_token, signup_type, signup_expiration, api_key, share, active

Blocked methods (29): sudo, with_user, with_company, with_context, with_env, with_prefetch, _auto_init, _sql, _register_hook, _write, _create, _read, _setup_base, _setup_fields, _setup_complete, init, _table_query, _read_group_raw, name_create, load, import_data, export_data, flush_recordset, invalidate_recordset, _search_panel_select_range, _search_panel_select_multi_range, _search_panel_domain_image, _search, _read_progress_bar

Additional Security Features

  • Brute-force protection — per-username lockout (5 fails → 5 min) AND per-source IP/connection lockout (30 fails → 15 min, blocks username-rotation attacks). Lockouts have fixed duration — cannot be extended by additional attempts (DoS-resistant).
  • Credential wrapper — passwords/session IDs stored in a Credential class with leak-safe __repr__/__str__, explicit .reveal() for use, and .clear() on close
  • Server-side admin verificationis_admin is re-verified via has_group('base.group_system') after authentication, defending against tampered auth responses
  • Private method guard — underscore-prefixed methods (_compute_*, _inverse_*, etc.) blocked for everyone including admin unless explicitly whitelisted
  • Rate limiting — per-session token bucket with separate global and write budgets
  • RBAC — tool-level access control by user group, field-level response filtering, transparent drop reporting via return_dropped=True
  • Input validation — model names, method names, field names, domain filters, ORDER BY clauses, groupby with temporal operators, write values (size/depth/type)
  • IDOR protection — plugin tools scope data access to the authenticated user
  • Audit logging — structured JSON logs for all allowed and denied operations
  • Error sanitization — strips internal URLs, SQL fragments, file paths, stack traces from error messages
  • XXE protection — XML-RPC responses parsed with defusedxml
  • Domain validation — Odoo domain filters validated for operators, field names, value types, nesting depth, and list sizes
  • Session-expiry retry — JSON-RPC retries only on OdooSessionExpiredError, not generic auth errors (no double round-trips on access denials)

Authentication

Three stock Odoo auth methods — no custom addon needed:

| Method | Protocol | Use Case | |--------|----------|----------| | api_key | XML-RPC | Server-to-server, CI/CD pipelines | | password | JSON-RPC | Interactive users, Claude Desktop | | session | JSON-RPC | Reuse existing browser session (development) |

# Example: login via the MCP tool
> login(method="password", username="admin", credential="admin", database="mydb")

Core MCP Tools (13)

| Tool | Description | |------|-------------| | login | Authenticate with Odoo (api_key / password / session) | | list_models | List accessible models with metadata and keyword filter | | get_model_fields | Get field definitions for a model with optional filter | | search_read | Search records with domain filters, field selection, ordering | | get_record | Get a single record by ID | | search_count | Count matching records | | create_record | Create a new record (supports dry_run for validation-only) | | update_record | Update existing record (supports dry_run for validation-only) | | delete_record | Delete a single record by ID (supports dry_run) | | read_group | Aggregated grouped reads with temporal operators (date:month, date:quarter, etc.) | | get_defaults | Preview Odoo default values before create_record | | get_onchange | Preview field side effects (with RBAC filtering) | | execute_method | Call allowed model methods (supports dry_run) |

Workflow Tools (2)

| Tool | Description | |------|-------------| | get_create_requirements | Get required fields and validation rules before creating a record | | get_record_actions | Get available workflow actions for an existing record |

MCP Resources (6)

| URI | Description | |-----|-------------| | odoo://models | List all accessible models | | odoo://models/{name} | Model detail with field definitions | | odoo://record/{model}/{id} | Single record data with RBAC field filtering | | odoo://schema/{model} | Field schema with type info and importance ranking | | odoo://categories | Model categories with counts | | odoo://workflow/{model} | Workflow definition with stages and actions for a model |

MCP Prompts (12)

| Prompt | Description | |--------|-------------| | analyze_model | Comprehensive model structure analysis | | explore_data | Natural language data exploration guide | | create_workflow | Guide through model-specific workflows | | compare_records | Side-by-side record comparison | | generate_report | Analytical report generation | | discover_custom_modules | Find and understand custom modules | | debug_access | Troubleshoot access and permission issues | | workflow_guide | Step-by-step workflow execution guide for a model | | record_creation_guide | Guided record creation with field validation | | bulk_operations | Guide for performing bulk operations safely | | field_mapping | Map fields between Odoo versions (v17/v18/v19) | | data_migration | Guide for migrating data between models or versions |

Built-in Domain Plugins

HR Plugin

| Tool | Description | |------|-------------| | check_in | Record attendance check-in | | check_out | Record attendance check-out | | get_my_attendance | View attendance records (with month filter) | | get_my_leaves | View leave requests (with state filter) | | request_leave | Submit a leave request | | get_my_profile | View employee profile |

Sales Plugin

| Tool | Description | |------|-------------| | get_my_quotations | List quotations/orders (with state filter) | | get_order_details | Full order details with line items | | confirm_order | Confirm a draft/sent quotation | | get_sales_summary | Aggregated sales statistics (with period filter) |

Project Plugin

| Tool | Description | |------|-------------| | get_my_tasks | List assigned tasks (with state/project filter) | | get_project_summary | Project stats: task counts by stage, overdue | | update_task_stage | Move a task to a different stage |

Helpdesk Plugin

| Tool | Description | |------|-------------| | get_my_tickets | List assigned tickets (with state/priority filter) | | create_ticket | Create a new helpdesk ticket | | update_ticket_stage | Move a ticket to a different stage |

Custom Module Support

Add custom Odoo modules without writing Python code. Edit model_access.yaml:

custom_models:
  full_crud:
    - custom.delivery.route
    - custom.warehouse.zone
  read_only:
    - custom.delivery.log

allowed_methods:
  custom.delivery.route:
    - action_dispatch
    - action_complete
    - action_cancel

Then all CRUD tools (search_read, create_record, update_record, delete_record) and execute_method work on the custom models with full security enforcement.

Plugin System

Extend the gateway with pip-installable plugins:

from odoo_mcp_gateway.plugins.base import OdooPlugin

class ManufacturingPlugin(OdooPlugin):
    @property
    def nam

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [parth-unjiya](https://github.com/parth-unjiya)
- **Source:** [parth-unjiya/odoo-mcp-gateway](https://github.com/parth-unjiya/odoo-mcp-gateway)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.