A standalone Model Context Protocol (MCP) server for Odoo, providing Claude with safe, human-approved access to Odoo CRM, projects, and other business domains.
This MCP server exposes Odoo as a set of modular, domain-specific tools that Claude can safely call:
- Sales & Pre-Sales — CRM pipeline, leads, contacts, solution design
- Project Delivery — Fixed-scope projects, tasks, milestones
- Domain-extensible — Easy to add support for other Odoo apps (Support, Finance, Knowledge, ERP Implementation, etc.)
All write operations are safety-checked to prevent accidental outbound communication (emails, SMS, calendar invites). Internal-only notes, activities, and field updates are safe by design.
- This project is an independent, community-maintained MCP server and is not affiliated with, endorsed by, or sponsored by Odoo S.A.
- Odoo is a trademark of Odoo S.A. Any product names, logos, and brands are used for identification only.
- You are responsible for validating security, compliance, and access controls before using this server in production environments.
- Always test write-enabled workflows in a non-production environment first.
- ✅ Safety-first: Outbound communication blocker prevents emails, SMS, and invites
- ✅ Human-in-the-loop: Write operations require upstream approval (Claude conversation)
- ✅ Multi-company: Supports Odoo's multi-company architecture
- ✅ Per-user credentials: Each Claude user stores their own Odoo API credentials locally
- ✅ Async XML-RPC: Non-blocking Odoo API calls
- ✅ Fully typed: Pydantic validation on all tool inputs
- ✅ Easy deployment: Single
pip installentry point
pip install odoo-mcp-serveruvx --from https://github.com/italanta/odoo-mcp-server odoo-mcp-serverSetup is centralized in setup.md.
This project supports two hosting modes only:
- Personal agent hosting on local machines via Claude Desktop, ClawdBot, and Hermes.
- Org-wide hosting on Claude Cowork via plugin installation from your own fork.
See setup.md for install steps, transport settings, update flow, and security guidance.
Do not host this package as a shared central MCP server for multiple users.
Odoo access relies on private API tokens. Central hosting would concentrate those tokens in one service, creating unnecessary credential custody and lateral-access risk.
This package is designed to run inside the supported harnesses documented in setup.md, where each user or managed plugin environment controls its own credentials.
odoo_ping— Validate connectivity and show your profileodoo_search_read— Find records with flexible filtersodoo_read_records— Read specific records by IDodoo_log_internal_note— Add internal chatter notes directly (safe)odoo_schedule_activity— Build staged payload for an internal reminder activityodoo_fields_get— Introspect model field definitionsodoo_search_count— Count records matching a filterodoo_diagnose_call— Diagnose planned model calls without executing themodoo_preview_write— Build a canonical non-executing mutation payloadodoo_validate_write— Validate payload against SafetyGuard and live metadata (when applicable)odoo_execute_approved_write— Execute only with token + confirm + env gateodoo_setup_credentials— Save/update credentialsodoo_check_for_update— Check latest GitHub release/tag and suggest update commandodoo_apply_self_update— Apply local package update (guarded by explicit confirm + env gate)
All mutating operations follow a strict staged approval flow, except internal notes. Internal notes are the only direct mutation endpoint.
odoo_preview_write
- Builds a canonical draft payload (
model,operation,record_ids,values,method,args,kwargs) - Does not validate against live Odoo metadata
- Does not execute anything
odoo_validate_write
- Applies
SafetyGuardpolicy checks - Validates payload fields against live
fields_getmetadata forcreate/write - Issues a short-lived, single-use
approval_token
- User approval in conversation
- Claude/Clawdbot must ask for explicit user confirmation before any execution step
- The validated payload must be accepted by the user as-is
odoo_execute_approved_write
- Requires
confirm=true - Requires a valid unused token from step 2
- Requires
ODOO_MCP_ENABLE_WRITES=1 - Executes only the validated operation (
create,write, orcall)
Fail-closed behavior:
- Missing confirmation, invalid/expired token, or disabled runtime write gate blocks execution.
- Tokens are single-use and payload-bound; replay and drift attempts are rejected.
The MCP can suggest and apply its own update locally, but this is gated by default.
odoo_check_for_update
- Read-only update check against GitHub releases/tags
- Returns current version, latest version, and a suggested upgrade command
odoo_apply_self_update
- Requires
confirm=true - Requires
ODOO_MCP_ENABLE_SELF_UPDATE=1 - Runs a local pip upgrade from the selected git ref/tag
After successful self-update, restart the MCP host/client so the new package is loaded.
odoo_search_opportunities— Find CRM deals with flexible filtersodoo_get_opportunity— Get full deal context (chatter, activities, all fields)odoo_propose_stage_change— Build staged payload to move deals to another stageodoo_propose_log_note— Add internal notes to deals directly (safe)odoo_propose_activity— Build staged payload for a follow-up activityodoo_propose_field_update— Build staged payload for allowed field updatesodoo_search_contacts— Find contacts/companiesodoo_get_pipeline_summary— See pipeline health: deal counts, total revenue by stage
odoo_search_projects— Find projects by name, client, status, stalenessodoo_get_project— Get full project context (tasks grouped by stage)odoo_search_tasks— Find overdue, stale, and at-risk tasks- (Future: task updates, notes, activities — not yet implemented)
odoo://models— List all available Odoo modelsodoo://model/{model_name}— Introspect a specific model's fields and metadata
The SafetyGuard in src/mcp/odoo/utils/safety.py enforces blocklists defined in src/mcp/odoo/utils/safety_blocked_methods.py:
- ❌ All email/SMS sending models:
mail.mail,sms.sms, mailing lists - ❌ Direct email methods:
action_send_mail,action_quotation_send,send_mail, etc. - ❌
message_postwith external message types (comment,email,notification) - ❌ Calendar events with attendees (would send invites)
- ❌ Any method that subscribes followers or changes notification routing
✅ Safe operations allowed:
- Field updates on CRM leads, projects, contacts, etc.
- Internal chatter notes using
message_type='note'(Odoo 18) ormessage_type='comment' + subtype_id=2(Odoo 19) - Activity creation (internal reminders, never external)
- Record creation and searches (read-only)
┌─────────────────────────────────────────────────────────────────────┐
│ MCP Client │
│ (ClawdBot, Claude Desktop or Terminal) │
└────────────────────────────┬────────────────────────────────────────┘
│ stdio
▼
┌────────────────────────────────────────────────────────────────────┐
│ Odoo MCP Server │
│ (FastMCP + Domain Tool Modules + SafetyGuard) │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Generic Tools (search, read, diagnostics, staged writes, etc.)│ │
│ └──────────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Domain Tool Modules │ │
│ │ • sales.py (8 CRM tools) │ │
│ │ • projects.py (6 project tools) │ │
│ │ • (others: support, finance, erp, knowledge — future) │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ OdooClient + SafetyGuard │ │
│ │ • Async XML-RPC calls │ │
│ │ • Credential management (~/.config/odoo-mcp/...) │ │
│ │ • Safety validation before all writes │ │
│ └──────────────────────────────────────────────────────────────┘ │
└────────────────────────────┬───────────────────────────────────────┘
│ XML-RPC
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Odoo Instance │
│ (https://my.odoo.com or your custom URL) │
└─────────────────────────────────────────────────────────────────────┘
# Install dev dependencies
pip install -e ".[dev]"
# Run safety tests (CRITICAL — always run these)
pytest src/mcp/odoo/tests/test_safety.py -v
# Run all tests
pytest tests/ src/mcp/odoo/tests/ -vsrc/
core/
credentials.py ← Per-user credential management
mcp/
odoo/
server.py ← Main MCP server & generic tools
utils/
client.py ← Async Odoo XML-RPC client
safety.py ← SafetyGuard: outbound blocker
tools/
sales.py ← CRM domain tools (8 tools)
projects.py ← Project management tools (6 tools)
_shared.py ← Shared types (ResponseFormat enum)
tests/
test_safety.py ← Safety validation tests (critical!)
tests/ ← Integration tests (future)
pyproject.toml ← Package config
README.md ← This file
-
Create
src/mcp/odoo/tools/my_domain.py:def register(mcp: FastMCP, get_odoo: Any) -> None: """Register my domain tools on the shared MCP server.""" @mcp.tool(name="odoo_my_tool", ...) async def my_tool(input: MyInput, ctx: Context) -> str: odoo = get_odoo(ctx) if isinstance(odoo, str): return odoo # Your implementation here return result
-
Import and register in
src/mcp/odoo/server.py:from src.mcp.odoo.tools import my_domain my_domain.register(mcp, _odoo)
-
Add tests in
src/mcp/odoo/tests/.
All writes go through staged validation and execution gates. Never bypass this — these layers prevent accidental outbound communication:
- Code layer (
SafetyGuard) — blocks dangerous models/methods - Validation layer (
odoo_validate_write) — enforces live schema checks and token issuance - Human layer (Claude conversation) — explicit user approval before execution
- Execution gates (
odoo_execute_approved_write) — requiresconfirm=true, valid token, and runtime env gate - Odoo layer — Odoo's own permissions
- ✅ CRM pipeline (leads, opportunities, contacts)
- ✅ Project delivery (projects, tasks)
- ✅ Generic Odoo tools (search, read, diagnostics, staged write flow)
- ✅ Internal notes & activities (safe only)
- 🔜 Support & Retainers (helpdesk tickets, SLAs)
- 🔜 Finance & Admin (invoices, payments — read-only)
- 🔜 Knowledge & Context (articles, workspaces)
- 🔜 ERP Implementation (hour-based packages, Odoo config)
- 🔜 Orchestration pipelines (daily reports, approval flow, debriefs)
Claude should prompt: Tell Claude: 'Set up my Odoo credentials'
Credentials are stored in ~/.config/odoo-mcp/credentials.json. If this file is missing or incomplete, run credential setup again.
Double-check:
- Odoo URL (must include
https://) - Database name (case-sensitive)
- Email matches your Odoo login
- API key is valid (generate new one in Odoo Settings > Users > API Keys)
This is intentional! The tool attempted an operation that could send external communication. If you believe this is a false positive, please file an issue. Do NOT disable SafetyGuard.
Contributions welcome! Please:
- Add tests for new tools (especially safety tests)
- Update docs and examples
- Run
pytest src/mcp/odoo/tests/test_safety.pybefore submitting PRs - Follow Pydantic & type annotation patterns
MIT Open Source
For questions, issues, or feature requests:
- GitHub Issues: https://github.com/italanta/odoo-mcp-server/issues
- Email: jente@elewa.ke