Odoo Claude MCP
This server lets Claude and other MCP clients securely manage Odoo ERP instances and their data, while also providing federated tools for development, communication, and automation.
Odoo data operations: CRUD, search/read/count, list models, inspect fields, execute any model method, post chatter messages, upload/download attachments, and generate PDF/XLSX/CSV reports.
Multi-connection & multi-tenant: add, list, switch, and remove Odoo connections per user; activate personal profiles mid-session.
Web session & portal access: log into the Odoo web interface, call controllers, export data, download reports, browse portal pages (invoices, orders, tickets, shop), and fetch public files/images/barcodes.
Website & content management: list/add/update/remove website snippets, translate plain and HTML/XML fields, and perform context-aware AI translation of records.
Bulgaria localization: configure and inspect fiscal positions, tax action maps, VAT types, and НАП-related settings.
Stock & manufacturing utilities: flip products to storable, safely delete draft manufacturing orders, import/delete opening stock balances, create closing journal entries, and back up records before destructive changes.
Memory & semantic search: read/write/share personal and shared memory files, with Qdrant/Ollama-powered similarity search.
External integrations: Google Gmail/Calendar, Telegram messaging, remote SSH/git operations, GitHub API calls, and a desktop Connection Manager GUI.
Identity & orchestration: identify the current user, check active connection, and manage personal Odoo/SSH/Portainer settings from within the session.
Provides tools for interacting with GitHub, enabling AI agents to manage repositories, issues, pull requests, and other GitHub resources.
Provides tools for interacting with Odoo instances, enabling AI agents to perform CRUD operations on records, search, manage modules, handle file attachments, and execute RPC calls.
Provides integration with local LLMs via Ollama for privacy-first deployments and semantic search embeddings.
Provides tools for interacting with Portainer, enabling AI agents to manage Docker containers and Kubernetes clusters via the Portainer API.
Odoo Claude MCP
Production-grade Model Context Protocol (MCP) server suite for Odoo ERP
Odoo MCP server for Claude Code and Claude.ai — turn any self-hosted Odoo instance (15 → 19) into an AI assistant via the Model Context Protocol. 197+ tools, multi-tenant, Bulgaria localization (НАП, ДДС, ЕИК) included.
Connect Claude, Claude Code, and any MCP-compatible client to Odoo, GitHub, filesystem, Portainer, Teams, and more — through a unified, authenticated gateway.
Quick Start · Why this vs alternatives? · Use Cases · Architecture · MCP Servers · Companion Modules · Deployment
Two tracks. You are on branch
3.0— the preview track for integrators, current v3.3.8. The stable track is branch2.0, current v2.31.0. All releases: Releases · details in Two Tracks.
See Claude editing a live Odoo sale order — real CRUD, real auto-recalculation

▶ Watch the full 4-minute demo: Running Odoo 18 Entirely Through AI — Claude Code + MCP Server
🎯 What is odoo-claude-mcp?
odoo-claude-mcp is a self-hosted MCP server suite that turns any Odoo instance into a first-class citizen in the Claude ecosystem. It exposes Odoo data and operations through the Model Context Protocol, while bundling complementary MCP servers for everything an Odoo developer, consultant, or business user needs — GitHub, OCA modules, Kubernetes/Portainer, Microsoft Teams, and a full Claude Code terminal running in the browser.
Unlike single-purpose MCP wrappers, this stack is built for real production use:
🔐 Unified authentication across all MCP endpoints via token-based auth
🏢 Multi-connection, multi-tenant — one stack serves dozens of Odoo databases
🌐 Claude.ai connector ready — public HTTPS endpoint with token auth
☸️ K3s / Kubernetes native with Kustomize overlays for dev and prod
🐳 Docker Compose for solo developers and small teams
🖥️ Web-based terminal — xterm.js + tmux + Claude Code in the browser
📦 Odoo module deployment via direct RPC (no filesystem access needed)
🔍 Qdrant vector store integration for semantic search across records
🤖 Ollama integration for local LLMs and privacy-first deployments
Related MCP server: MCP Server for Odoo
🚀 Quick Start
Option 1: Docker Compose (local dev)
git clone https://github.com/rosenvladimirov/odoo-claude-mcp.git
cd odoo-claude-mcp
# Configure
cp .env.example .env
nano .env # set ODOO_URL, DB, credentials, tokens
# Create the shared Cloudflare tunnel network (one-time per host)
docker network create cloudflare-net
# Start the stack
docker compose up -d
# Verify
docker compose ps
curl http://localhost:8084/healthNo Cloudflare tunnel? If you're deploying locally without a Cloudflare sidecar, use the override file instead of creating the external network:
docker compose -f docker-compose.yml -f docker-compose.local.yml up -dThis turns
cloudflare-netinto a regular Docker bridge network created on demand.
Optional services (Portainer / Teams). The default
docker compose up -dstarts only the core stack (Odoo RPC, Filesystem, OCA, EE, GitHub, Qdrant, Ollama, Claude Terminal). To also enable the Portainer MCP or Microsoft Teams MCP, opt in with a Compose profile:docker compose --profile portainer up -d # core + Portainer docker compose --profile teams up -d # core + Teams docker compose --profile full up -d # everythingEach optional service has its own required env vars (see
.env.example). Without them the service container would crash-loop, so they're skipped by default.
First-run troubleshooting. If
docker compose psshowsmcp-odoo-rpcinRestartingstate, it means the values in.env(ODOO_URL,ODOO_DB,ODOO_USERNAME,ODOO_API_KEY/ODOO_PASSWORD) don't point to a reachable Odoo instance. Edit.envto match a real Odoo and re-rundocker compose up -d. The healthcheckcurl http://localhost:8084/healthwill succeed only aftermcp-odoo-rpcisUp.
Option 2: Quick installer script
Linux / macOS:
curl -fsSL https://raw.githubusercontent.com/rosenvladimirov/odoo-claude-mcp/2.0/install.sh | bashWindows (PowerShell as Administrator):
iwr -useb https://raw.githubusercontent.com/rosenvladimirov/odoo-claude-mcp/2.0/install.ps1 | iexOption 3: Connect to Claude Code
After the stack is running, add it to Claude Code:
claude mcp add odoo-mcp \
--url https://your-domain.com/mcp \
--header "Authorization: Bearer YOUR_TOKEN"Or use the included .mcp.json:
cp claude-terminal/.mcp.json ~/.config/claude-code/mcp.json⚖️ Why this vs alternatives?
Capability | Raw XML-RPC | Single-purpose MCP-Odoo wrappers | odoo-claude-mcp |
Auth | Per-call password | Single shared token | ✅ Bearer + per-user profiles + OAuth |
Multi-tenant | ❌ One DB per process | ⚠️ Single connection | ✅ N tenants per stack |
Tools available | ~6 RPC verbs | 20–40 | ✅ 197+ (92 native + 105 proxied) |
Memory / context layer | ❌ | ❌ | ✅ Qdrant vector + Ollama embeddings, per-user + shared |
Skills / orchestration | ❌ | ❌ | ✅ |
Web terminal in browser | ❌ | ❌ | ✅ xterm.js + tmux + Claude Code |
GitHub / Portainer / Teams / OCA | ❌ | ❌ | ✅ 8 federated MCP servers |
Bulgaria localization | ❌ | ❌ | ✅ НАП-ready, fiscal positions, VAT |
Production deployment | DIY | Docker only | ✅ Docker Compose + K3s/Kustomize |
Audit logging / rate limit | ❌ | ⚠️ | ✅ Per-user logs, Cloudflare AI Gateway |
License | LGPL (client lib) | Mixed (some MIT, some commercial) | ✅ AGPL-3.0 (open, share-alike) |
The shorter version: most other MCP-Odoo bridges are great for "one developer wants to talk to one Odoo." odoo-claude-mcp is for teams, agencies, and SaaS providers that need multi-user, multi-database, audited deployments with a unified AI surface.
🎨 Use Cases
For Odoo Developers
Live module development with Claude assisting directly on your running instance
RPC-based module deployment — update code, views, data without filesystem access
Multi-environment workflows — dev, staging, production from a single Claude session
OCA contribution flows — clone, search, test, submit PRs through Claude
For Odoo Consultants
Manage multiple client databases from one authenticated session
Per-client memory — Claude remembers context for each customer
Shared team knowledge —
memory_sharedistributes institutional know-howНАП / Bulgaria localization — built-in tools for fiscal positions, VAT compliance
For Business Users
"Ask Claude about our sales data" — natural language queries against real Odoo records
Document extraction workflows — vision LLMs parse invoices into
account.moveSemantic search — find similar records, contracts, tickets across the whole database
Email & calendar integration — Claude coordinates work across Odoo, Gmail, Calendar
For Platform Operators (SaaS / MSP)
Multi-tenant hosting — each client gets an isolated MCP endpoint
Billing integration — usage tracking per tenant via Cloudflare AI Gateway
White-label terminals — brand
claude-terminalfor your customersKubernetes scaling — scale MCP replicas independently based on load
🏗 Architecture
flowchart TD
A["🧑 Claude.ai · Claude Code · Claude Desktop · IDE"]
A -->|HTTPS + Bearer token| B
subgraph GW["odoo-claude-mcp gateway"]
B["Unified MCP Router · server.py<br/>• Proxies to 7 backend MCP servers<br/>• Per-user profiles & connections<br/>• Shared memory store · audit log"]
end
B --> C1["odoo-rpc-mcp<br/>(core, 100+ tools)"]
B --> C2["ee-mcp<br/>(Odoo EE)"]
B --> C3["oca-mcp<br/>(OCA repos)"]
B --> C4["github-mcp"]
B --> C5["portainer-mcp"]
B --> C6["filesystem-mcp"]
B --> C7["teams-mcp"]
B --> C8["claude-terminal<br/>(xterm.js + tmux)"]
C1 --> D1["Odoo 15→19<br/>(N tenants)"]
C1 --> D2["Qdrant<br/>(vector DB)"]
C1 --> D3["Ollama<br/>(local LLMs)"]
classDef gw fill:#e0e7ff,stroke:#4338ca,color:#1e1b4b;
classDef be fill:#f3f4f6,stroke:#6b7280,color:#111827;
classDef st fill:#fef3c7,stroke:#d97706,color:#451a03;
class B gw;
class C1,C2,C3,C4,C5,C6,C7,C8 be;
class D1,D2,D3 st;🧰 MCP Servers
Core: odoo-rpc-mcp
The flagship MCP server. 197+ MCP tools (92 native + 105 proxied across Portainer, GitHub, Teams, EE, OCA, filesystem) covering every aspect of Odoo development and operations.
Capabilities:
CRUD & Search:
odoo_search_read,odoo_create,odoo_write,odoo_unlink,odoo_executeIntrospection:
odoo_fields_get,odoo_list_models,odoo_module_infoMulti-connection: Switch between databases on the fly —
odoo_connect,user_connection_activateWeb session support:
odoo_web_login,odoo_web_call,odoo_web_export,odoo_web_reportFile operations:
odoo_attachment_upload,odoo_attachment_download,public_access_downloadReporting:
odoo_report,public_access_report_pdf,public_access_report_xlsxPortal access:
public_access_portal_orders,public_access_portal_invoices,public_access_portal_ticketsBulgaria l10n:
odoo_fp_configure,odoo_fp_list,odoo_fp_details— fiscal positions tailored for НАП complianceTranslations ★ 2.10:
odoo_list_translatable_fields,odoo_get_field_translations,odoo_translate_field(simple translate=True),odoo_translate_html(html_translate / xml_translate withextract/terms/replacemodes). Version-aware (Odoo 16+ native JSONB API, <16 ir.translation fallback). Auto-ZWSP marks identical translations as "kept intentionally" so the website editor stops flagging them as untranslated.Website snippets ★ 2.10:
odoo_website_list_snippets,odoo_website_list_page_snippets,odoo_website_add_snippet,odoo_website_update_snippet,odoo_website_remove_snippet. Lxml-based HTML parsing with xpath substitutions — covers blog posts, website pages (via arch_db), product descriptions, mega-menus. Supports background image swaps, CTA insertions, position-relative placement (end/begin/after/before/replace).AI integration:
ai_tokenize_record,ai_search_similar,ai_collection_info— Qdrant vector embeddings per Odoo recordMemory system: Per-user and shared memory with
memory_read,memory_write,memory_share,memory_pullGoogle services: OAuth, Gmail search/read/send, Calendar CRUD
Telegram: MTProto client — send messages, search contacts, read dialogs
SSH & Git: Remote command execution, git operations
ee-mcp — Odoo Enterprise Tools
Tools specific to Odoo Enterprise workflows:
License validation & status checks
EE module repository management
Dependency analysis (CE + EE)
Selective EE module linking into CE addons paths
Conflict detection between Enterprise and OCA modules
oca-mcp — OCA Module Management
Deep integration with the Odoo Community Association ecosystem:
Clone individual OCA repos or
oca-clone-everythingSearch across all local OCA repos
Generate READMEs, icons, requirements.txt via
oca-gen-*Version migration via
oca-migrate-branchChangelog generation from newsfragments
claude-terminal — Browser-based Claude Code
A complete xterm.js + tmux + Claude Code setup running in a Docker container:
WebSocket gateway (
gateway.js) with session isolationPer-user tmux sessions that persist across reconnects
Themable terminal (see
themes.json— includes Catppuccin, Dracula, Tokyo Night, Gruvbox, etc.)Landing page (
landing.html) for public deploymentsAuthenticated via the same token system as other MCP servers
Infrastructure MCPs
github-mcp— GitHub API wrapper (search code, issues, PRs, repos)portainer-mcp— Docker/Kubernetes environment managementteams-mcp— Microsoft Teams messaging integrationfilesystem-mcp— Scoped filesystem operations for AI agents
📦 Companion Odoo Modules & Theme
The MCP stack is paired with several Odoo modules and a website theme. Together they turn any Odoo instance into a fully MCP-aware, multi-tenant, billing-ready AI workstation — with an optional editorial website skin on top.
l10n_bg_claude_terminal — Odoo ↔ MCP integration (free / LGPL-3)
Odoo module that exposes MCP + Claude Terminal configuration as user preferences and company settings. Works on Odoo 16, 18 and 19 (each major series has a dedicated branch).
Repos:
Odoo 18:
l10n-bulgaria→l10n_bg_claude_terminal· current: 18.0.1.36.1Odoo 19:
l10n-bulgaria→l10n_bg_claude_terminal· current: 19.0.1.34.1Odoo 16: same repo, branch
16.0
🌍 Non-Bulgarian deployments? Use the international fork
rosenvladimirov/odoo-mcp-terminal— same module, redistributed without the BG localization dependencies (no НАП fields, no Bulgarian transliteration mixin). Branches16.0/18.0/19.0, AGPL-3.0. Recommended for any Odoo instance outside Bulgaria.
What it adds to Odoo:
Per-user MCP endpoint + Bearer token config (Odoo UI → Preferences)
Per-user Odoo RPC connector (URL, DB, API key, protocol,
verify_sslflag with TOFU cert pinning)Per-user Web Session credentials (for MCP's web session support)
Per-user Anthropic API key / OAuth token passthrough (billing account or Claude Pro/Teams/Max)
Telegram + Viber MTProto / bot token config
18 terminal themes (Catppuccin, Dracula, Tokyo Night, Gruvbox, …)
Claude.ai OAuth login button → one-click auth to Claude API
Live refresh bus — MCP
odoo_create/odoo_writetriggers open form / list views to update in real time (no full reload)Test Connections button — smoke-tests Odoo RPC + MCP + Web Session
Qdrant + Ollama in one click (sticky notifications)
Save to MCP button — register the user's Odoo alias into the MCP connection store without touching
/data/connections.jsonmanuallyDynamic XML-RPC db list — populates the Database dropdown from the Odoo instance's
list_dbs()(multi-tenant friendly)
l10n_bg_ai_billing — SaaS billing module (OPL-1, paid)
Odoo module for hosting providers and reseller tier management. Tracks per-user MCP usage, calculates bills, provisions Portainer stacks per tenant, ships licensed memory packs.
Repo:
Odoo 19:
OCA/l10n-bulgaria-expert→l10n_bg_ai_billing· current: 19.0.1.3.0
What it adds:
8 models:
ai.billing.{bundle, tenant, usage.line, invoice.batch, skill.catalog, memory.pack, memory.deployment, tenant.addon}Bundle pricing — Starter €49 / Business €129 / Professional €299 / Enterprise €599 tiers with per-user / per-call / per-skill usage meters
Millicents precision ($0.00001) on usage lines — prevents the 30–40% rounding loss common on cent-based billing
Portainer client wrapper —
portainer.clientwizard creates a per-tenant MCP stack with auto-provisioned port, env, and networkAES-256 encrypted ZIP export (via
pyzipper) of tenant config bundles for offline demos or DR backupsSkill catalog —
ai.skillrecords with L1/L2/L3 disclosure tiersMemory packs — versioned markdown playbooks distributable to tenants via MCP
/admin/memory/uploadendpointBG Trade Registry integration — fetches EIK / VAT / legal form from portal.registryagency.bg for tenant bootstrap
sale.order integration — selling a bundle SKU auto-provisions the tenant + deploys memory + activates skills
MCP Terminal addons — extra per-tenant features (dedicated subdomain, white-label branding)
Dependency on the MCP stack: uses the MCP_ADMIN_TOKEN endpoint
family (/admin/memory/*) added in odoo-rpc-mcp 2.8.0.
odoo-mcp-theme — Custom Odoo website theme (free / AGPL-3)
Editorial "paper" theme purpose-built for the MCP Works landing site and any Odoo website that wants a clean, content-first presentation.
Repo:
rosenvladimirov/odoo-mcp-theme
· branch 19.0 · current: 19.0.x
What it ships:
OKLCH paper/ink + indigo colour system — perceptually-uniform palette that prints well, scans well, and adapts to dark mode without losing contrast
Custom snippets — pricing cards (5-tier layout), feature grids, setup-wizard CTAs, demo-link blocks, MCP architecture diagrams
Vertical timeline language switcher — replaces Odoo's default language dropdown with a sidebar timeline (works well for trilingual EN / BG / RU sites)
Editorial typography — measured for long-form posts, runs ≤ 70 chars per line, optimised vertical rhythm
Fully
#wrapwrap-scoped — no leakage into Odoo backend or Website Builder editor (avoids the$o-color-palettes-nameglobal leak that breaks editor UI in older themes)
Installable on any Odoo 19 instance. Drop it into your addons path, install via Apps, then assign in Website → Configuration → Themes.
Installation order
odoo-mcp-theme ← optional: editorial website skin
l10n_bg_claude_terminal ← every user of the MCP terminal
↓
l10n_bg_ai_billing ← hosting providers / resellers / BL-tier opsl10n_bg_ai_billing depends on l10n_bg_claude_terminal — installing
the billing module auto-pulls the terminal integration.
odoo-mcp-theme is independent and can be used with or without the
MCP stack.
☸️ Deployment
Kubernetes / K3s (recommended for production)
Full Kustomize-based deployment in k3s/:
k3s/
├── base/ # Base manifests
│ ├── namespace.yaml
│ ├── configmaps.yaml
│ ├── secrets.example.yaml
│ ├── pvcs.yaml
│ ├── odoo-rpc-mcp.yaml
│ ├── ee-mcp.yaml
│ ├── oca-mcp.yaml
│ ├── github-mcp.yaml
│ ├── teams-mcp.yaml
│ ├── portainer-mcp.yaml
│ ├── filesystem-mcp.yaml
│ ├── claude-terminal.yaml
│ ├── qdrant.yaml
│ ├── ollama.yaml
│ └── ingress.yaml
└── overlays/
├── direct/ # NodePort + cert-manager-example
└── prod/ # Ingress with TLS for public endpointsDeploy:
cd k3s/overlays/prod
cp .env.example .env
cp ../../base/secrets.example.yaml secrets.yaml
# edit secrets.yaml with real values
kubectl apply -k .
kubectl -n odoo-claude-mcp get podsSee k3s/README.md for complete deployment guide including cert-manager, Cloudflare tunnels, and horizontal scaling.
Public deployment pattern
For Claude.ai connector access, the recommended production topology is:
Internet → Cloudflare (DNS + WAF) → Nginx reverse proxy → MCP gateway
│
▼
Backend MCP serversToken-based authentication on the gateway ensures only authorized Claude sessions connect. Cloudflare's Zero Trust or simple tunnel setup both work.
🔌 Claude.ai Connector
The stack is designed to be registered as a Custom Connector in Claude.ai (Team/Enterprise) or via the API.
Configuration:
Deploy the stack with a public HTTPS endpoint (e.g.,
https://mcp.yourdomain.com)Generate a user token (see
odoo_connect_cli.pyor Qt GUI)In Claude.ai Settings → Connectors → Add Custom Connector:
URL:
https://mcp.yourdomain.com/mcpAuth: Bearer token
The gateway will expose all MCP tools to your Claude conversations
Security features:
Per-user profile isolation (
/data/users/{username}/)Shared memory vs. personal memory separation
Connection-level access control (users only see their own Odoo connections)
All tool calls logged per user
🛠 Developer Tools
Beyond MCP servers, the repo includes standalone desktop and CLI tools:
Connection Manager
tools/odoo_connect_qt.py — PyQt6 desktop GUI for managing Odoo connections, SSH keys, and MCP endpoints. Cross-platform (Linux/Windows/macOS).
tools/odoo_connect.py — GTK4/Adwaita alternative for Linux/GNOME users.
odoo-rpc-mcp/odoo_connect_cli.py — Terminal CLI for CI/CD and scripting.
Module Analyzer
tools/odoo_module_analyzer.py — Analyzes Odoo module source for:
Manifest validation
Dependency graph extraction
Model relationships
View definitions
Security rules
GLB Viewer
tools/glb_viewer.py — 3D model inspection tool for the MRP Design Matrix workflows.
Windows Installer
Pre-packaged NSIS installer (packaging/windows/) produced automatically via GitHub Actions (.github/workflows/build-windows.yml).
📚 Documentation
README_BG.md — Пълна документация на български
CHANGELOG.md — Version history and release notes
claude-terminal/CLAUDE.md — Claude Code workspace documentation
k3s/README.md — Kubernetes deployment guide
🔐 Security
No credentials in code — all secrets via environment variables or Kubernetes secrets
Token-based MCP auth — no shared passwords
Per-user data isolation — filesystem and memory scoped to authenticated user
OAuth for third-party services — Google, GitHub, Telegram all use standard OAuth flows
Connection encryption — HTTPS/WSS everywhere in production deployments
Rate limiting — via Cloudflare AI Gateway or ingress controller
Audit logging — all MCP tool calls logged with user context
Admin console behind a second factor — TOTP (RFC 6238) with one-time recovery codes, optional
MCP_ADMIN_REQUIRE_TOTPpolicy, every session-issuing path gated ★ new in 3.3.8 (see below)
Reporting security issues: please email vladimirov.rosen@gmail.com rather than opening a public issue.
Admin console (/admin)
A hidden web console inside odoo-rpc-mcp for the people who operate a
stack: per-user Odoo connections, user provisioning with one-time keys,
backups and filestore. Optional and off unless configured.
Env | Meaning |
| Mount path, default |
| Odoo login that becomes admin on first sign-in |
| Signs session cookies. Required — the console refuses to mount without one |
| ≥32 chars. Encrypts TOTP secrets at rest (same pepper as |
|
|
| Optional knock token ( |
Second factor. Each user enrols TOTP from Security: password → QR →
confirming code → eight one-time recovery codes. Once enrolled, every way of
obtaining a session (MCP password, Odoo re-auth, one-time key) asks for the
code. Secrets live in /data/users/<login>/totp.json; an admin can reset a
user's factor from Users, and the last admin recovers by deleting that file
on the volume. Everything is written to /data/admin_audit.log — never the
secret or a code.
🌍 Bulgaria Localization
This project is maintained by the OCA l10n-bulgaria maintainer. Bulgarian-specific features are first-class:
НАП integration — fiscal position tax action maps, VAT reports
l10n_bg_*module family support — fiscal positions, VAT reports, payroll, HRОбразец 1 — monthly NAP declaration (Наредба №Н-13/2019)
Bulgarian partner identification — UIC/ЕИК, legal forms, NACE activity codes
Transliteration — BG ⇄ EN ⇄ GR mixin for partner names
НАП справка-декларация — SQL-engine based audit reports
See the Bulgaria-specific OCA modules for the complete ecosystem.
❓ FAQ
Does it work with Odoo Enterprise?
Yes. Odoo 15→19 (Community + Enterprise) are all supported via XML-RPC + JSON-RPC. EE-specific tools (license check, EE module discovery, dependency analysis) live in the dedicated ee-mcp server. No Odoo SH-specific hooks — works on any self-hosted Odoo (bare metal, Docker, K3s, Odoo SH outbound).
Is this OCA-approved / on the Odoo Apps Store?
The companion modules (l10n_bg_claude_terminal, the broader l10n-bulgaria family) are maintained by the OCA l10n-bulgaria maintainer. The MCP server itself is an independent project (not an Odoo app), AGPL-3.0, distributed via Docker Hub + GitHub. Track 3.x will publish a SaaS billing module on the Odoo Apps Store separately.
BYOK (bring your own Anthropic key) — is it safe?
Yes. Each authenticated user can configure their own Anthropic API key via the Odoo UI (Preferences → Claude Terminal). Keys are stored encrypted in the per-user profile (/data/users/{username}/) and never leak to other tenants. The server never bundles a shared Anthropic key into multi-tenant deployments.
Can one stack serve multiple Odoo databases?
Yes — that's the core design. One MCP server instance proxies to N Odoo connections, each with its own credentials, language, and timezone. odoo_connect / user_connection_activate switch the active database mid-session. Per-user memory namespaces keep context isolated.
Does it require Cloudflare?
No. Cloudflare is the recommended production topology (Tunnel + WAF + AI Gateway), but the stack runs fine behind any reverse proxy (Nginx, Traefik, Caddy) or directly on a LAN/VPN. The docker-compose.local.yml override removes the Cloudflare network requirement for local dev.
What's the difference between this and Rutger Klabbers's odoo-mcp-pro?
Different scope. odoo-mcp-pro is a focused single-user/single-DB Odoo MCP wrapper. odoo-claude-mcp is a multi-tenant gateway with 7+ federated MCP servers (Odoo + GitHub + OCA + Portainer + filesystem + Teams + claude-terminal), per-user authentication, audit logging, and a companion Odoo billing module for SaaS providers. Both projects are open source — pick the one that matches your deployment shape.
Can I run only the parts I need?
Yes. Compose profiles gate optional services: docker compose up -d starts only the core (no Portainer, no Teams). Use --profile portainer / --profile teams / --profile full to add them. K3s overlays are similarly modular.
🛤 Two Tracks — Which Branch Do You Want?
This project ships on two parallel branches, each targeting a different audience. Most users want Track 2.x; the 3.x track is for integrators building hosted offerings on top of the stack.
🧑💼 Track 2.x — End Users (current stable, branch 2.0)
Who: Odoo end-users, accountants, Bulgarian SMEs, developers who work with a single Odoo stack, content teams managing website/blog.
What you get:
All 188+ MCP tools for day-to-day Odoo work (CRUD, search, RPC, introspection, attachments, reports, web session)
Multi-language field management (
odoo_translate_field+odoo_translate_html+ 2 helpers — covers blog.post, product descriptions, website pages, arch_db) ★ new in 2.10Website snippet management (list / add / update / remove snippets on blog posts and pages, with background image swaps and substitutions) ★ new in 2.10
Bulgaria localization (fiscal positions, VAT, НАП integration)
AI tokenizer (Qdrant + Ollama embeddings per Odoo record)
Memory system (shared + per-user + licensed memory packs)
Google / Telegram / Teams integration
Claude.ai connector — Bearer-token HTTPS endpoint ready
Docker tags: :latest, :stable, :2.x.y (current: 2.31.0)
Documentation: this README
🔧 Track 3.x — Implementers / Integrators (preview, branch 3.0)
Who: Odoo implementation partners, OCA community contributors, SaaS MSPs running multiple client instances, integrator agencies.
What's planned (development — not production yet):
Admin lifecycle tools —
odoo_module_install/upgrade/uninstall/ diff,odoo_config_apply,odoo_health_check,odoo_backup_db / restore_dbIndustry skill packs — Manufacturing, Retail, Services, BG Localization, AI Accounting Assistant. Each pack = modules +
ai.skillrecords + memory packs + pipeline steps.Demo builder — one-command generator of fresh demo environments (
mcp demo create --industry=... --seed=...). Tenant + Odoo DB + demo data + skills + memory in < 5 minutes.Module dev + test toolkit —
odoo_module_scaffold / lint / test / install_from_path / explain,odoo_xml_validate.
Already shipping on 3.x: self-service provisioning (/provision), supervisor + fleet
tools, OAuth hardening with mandatory PKCE (3.3.6), TOTP second factor for MCP
name-identify (3.3.0) and for the admin console (3.3.8, see Security above).
Docker tags: :next, :3.x.y (current: 3.3.8)
Documentation: docs/integrator-platform.md
(coming soon — see project memory roadmap_integrator_platform.md
for the full 4-track spec)
Positioning: the 3.x track shifts buyer persona from the final Odoo user to the integrator / partner / agency — giving them the tools to deploy, configure, and demo Odoo + AI workflows for their own clients at scale.
🗺 Roadmap
Billing module —
l10n_bg_ai_billing19.0.1.3.0 shipped (4 tiers, millicents precision, Portainer auto-provisioning)Invoice AI integration —
l10n_bg_ai_pipeline+l10n_bg_ai_invoice_glueshipped (98.5% conf EAD OCR, Odoo 18+19, end-to-end tokenize)Self-service tenant provisioning — v3
/provisionendpoint LIVE onmcp.odoo-shell.space(Apr 2026)Multi-tenant admin dashboard — admin UI for managing hosted MCP instances
Skills marketplace — publish and subscribe to pre-built Odoo workflows (
ai.skillrecords + memory packs)Audit log UI — searchable web UI for MCP tool call history
Self-healing connections — automatic retry with token refresh on auth failures
🤝 Contributing
Contributions welcome! This project follows OCA conventions:
Fork the repo
Create a feature branch (
git checkout -b feature/amazing-thing)Follow PEP 8 / Odoo coding guidelines
Add tests where applicable
Submit a PR with a clear description
For large changes, please open an issue first to discuss approach.
📜 License
This project is licensed under the AGPL-3.0 license. See LICENSE for details.
🙏 Credits & Acknowledgements
Anthropic — Claude, Claude Code, and the Model Context Protocol specification
Odoo SA — The ERP platform this project extends
Odoo Community Association (OCA) — The open-source Odoo ecosystem
Qdrant — Vector database
Ollama — Local LLM inference
⭐ Star History
If this project helps you, please consider starring the repo — it helps others discover the work and motivates further development.
👤 Maintainer
Rosen Vladimirov — Terraros Commerce Ltd.
Odoo Silver Partner · OCA l10n-bulgaria maintainer · 10+ years of Odoo specialization
📧 Email: vladimirov.rosen@gmail.com 🐙 GitHub: @rosenvladimirov 🏢 Company: Terraros Комерс ЕООД · Bulgaria
Made with ❤️ and ☕ in Bulgaria 🇧🇬
Available Tools
138 toolsai_collection_infoB
Return info about the per-database Qdrant collection: vector size, distance metric, point count, indexed-vectors count, plus Odoo-side indexed-document count for cross-check.
Inputs: connection.
ai_invoice_extractA
Extract structured invoice data from an Odoo attachment via Anthropic Vision. Auto-routes to haiku/sonnet/opus based on pages+size. Writes extract_prefill_data back to account.move (if write_back). Logs usage to the billing ledger with token counts + cost in millicents.
Inputs: attachment_id (required), source, move_id, connection, +4 more.
ai_invoice_pipeline_runB
Execute the full registered step pipeline for one move+attachment. Steps: probe_move → guard_already_extracted → extract_vision → log_usage → write_back_move → invoke_posting_skill + any loaded plugins. Returns step-by-step audit trail.
Inputs: move_id (required), source, connection, tenant_code, +3 more.
ai_invoice_pipeline_stepsA
List currently registered pipeline steps with sequence, description, and plugin vs built-in provenance. Useful for debugging which steps will run against a move.
Inputs: no named parameters.
ai_invoice_pipeline_summaryB
Dashboard aggregation: counts of vendor bill moves by next_step across the last 60 days. Use for header KPI cards.
Inputs: connection, tenant_code.
ai_invoice_plugins_reloadA
Reload plugin steps from the plugins directory (default /data/plugins/ai_invoice). Called after uploading a new plugin without restarting the server.
Inputs: plugins_dir.
ai_invoice_scan_pendingA
List vendor-bill drafts that have an attachment and no successful extraction yet. Oldest first (FIFO). Feeds batch processing. Set requested_only=true to return only moves explicitly queued by the l10n_bg_ai_invoice_glue attachment auto-trigger (ai_pipeline_requested=True) — recommended for cron drivers.
Inputs: limit, connection, tenant_code, requested_only.
ai_invoice_stack_inspectA
Cross-layer snapshot for a single account.move: Odoo state + attachments + extraction history + skill status + decided next_step with blockers and hints. Read-only.
Inputs: move_id (required), connection, tenant_code.
ai_list_documentsC
List ai.composite.document records (tokenized records tracked by Odoo). Useful for monitoring tokenization progress / errors.
Inputs: limit, model, state, connection.
ai_pipeline_runA
Execute an Odoo-defined pipeline (ai.pipeline.step records for the given pipeline name, ordered by sequence). Respects skill_id + trigger_domain + on_error. MCP-native steps (model starts with 'mcp') are dispatched to the local registry; other steps are invoked via RPC on the configured Odoo model.method. Writes back last_run_state/message per step.
Inputs: pipeline (required), source_id (required), source_model (required), extra_ctx, +4 more.
ai_pipeline_step_executeA
Execute a single ai.pipeline.step by name via Odoo RPC. Looks up the step in Odoo by name, builds a ctx from the supplied parameters, calls env[step.model].step.method(ctx), and returns the updated ctx (with composite_fields populated by the step). Use ai_pipeline_steps_list to discover available step names.
Inputs: source_id (required), step_name (required), source_model (required), extra_ctx, +2 more.
ai_pipeline_steps_listA
List ai.pipeline.step records in Odoo for a given pipeline name. Shows sequence, model.method, skill_id, on_error, last run state — mirrors the Odoo Settings view.
Inputs: pipeline, connection, include_inactive.
ai_search_similarA
Semantic search across tokenized Odoo records. Embeds the query with the configured provider, searches the per-DB Qdrant collection. Filterable by model, view_type, company. Returns ranked hits with score + snippet.
Inputs: query (required), limit, model, view_type, +3 more.
ai_tokenize_collectionA
Tokenize ALL records of a model via the registry entry. Heavy operation — for large models prefer the nightly cron. Returns count of processed records.
Inputs: model (required), view_type, connection.
ai_tokenize_recordB
Tokenize a single Odoo record: build composite document, embed via configured provider, upsert to Qdrant. Synchronous — returns final state.
Inputs: id (required), model (required), view_type, connection.
ai_usage_budget_statusB
Current month's vision spend vs configured cap. Reads res.company.ai_monthly_budget_eur (requires l10n_bg_ai_invoice_glue) and sums billed ai_usage_log rows. Returns spent, limit, remaining, % used. limit_eur=0 means no cap configured.
Inputs: connection, tenant_code.
ai_usage_log_exportA
Export billing ledger rows as CSV for a tenant + date range. Used for month-end invoice generation and audit trails.
Inputs: date_to, date_from, connection, tenant_code.
ai_usage_log_queryA
Query the AI usage billing ledger. Filter by tenant, state, source, date range. Returns detailed rows for audit + monthly reconciliation.
Inputs: limit, state, offset, source, +5 more.
ai_usage_log_statsA
Dashboard KPIs for the billing ledger: totals, per-model breakdown, 30-day timeseries, recent errors, derived metrics (cache hit rate, avg cost per billed doc, monthly €).
Inputs: period, connection, tenant_code.
github_apiA
Call GitHub REST API directly. Uses the GitHub token from local_profile.json. For operations not covered by the GitHub MCP server, or when it's not running.
Inputs: endpoint (required), body, method, params.
git_remoteB
Run git commands on a remote server via SSH. Shortcut for common git operations (pull, status, log, branch, diff) on remote repositories. Provide the connection alias and repo path.
Inputs: operation (required), repo_path (required), connection (required), args, +2 more.
google_authA
Authenticate with Google OAuth2 for Gmail and Calendar access. Requires credentials.json from Google Cloud Console (Desktop app type). First call opens browser for consent. Token is saved for reuse.
Inputs: credentials_file.
google_auth_statusB
Check Google authentication status.
Inputs: no named parameters.
google_calendar_create_eventC
Create a new calendar event.
Inputs: end (required), start (required), summary (required), location, +4 more.
google_calendar_delete_eventC
Delete a calendar event.
Inputs: event_id (required), calendar_id.
google_calendar_eventsB
List upcoming calendar events. Supports time range and text search.
Inputs: query, time_max, time_min, calendar_id, +1 more.
google_calendar_listA
List all available Google calendars.
Inputs: no named parameters.
google_calendar_update_eventB
Update an existing calendar event. Only provided fields are changed.
Inputs: event_id (required), end, start, summary, +5 more.
google_gmail_labelsA
List all Gmail labels (folders).
Inputs: no named parameters.
google_gmail_readA
Read a specific Gmail message by ID. Returns full body, headers, and labels.
Inputs: message_id (required).
google_gmail_searchA
Search Gmail messages. Uses Gmail search syntax (e.g. 'from:user@example.com', 'subject:invoice', 'after:2026/01/01', 'is:unread').
Inputs: query (required), label_ids, max_results.
google_gmail_sendB
Send an email or reply to an existing message.
Inputs: to (required), body (required), subject (required), cc, +3 more.
identifyA
Identify yourself to load your personal settings and connections. Call this at the start of a session. Returns your saved connections.
Inputs: name (required).
mcp_terminal_get_configA
Generate a password-protected ZIP archive with the Claude Terminal configuration keys (MCP, Qdrant, Embeddings) for the requesting tenant. Anthropic API key is NOT included by default (set include_anthropic=true to embed it). Returns base64-encoded ZIP + a temporary password (shown ONCE — copy it now). Use the password in the Odoo Setup Wizard (Settings → Technical → Настройка с ZIP конфигурация) to apply the config across res.company + res.users records in 5 guided steps.
Inputs: connection, tenant_code, include_anthropic.
memory_deleteC
Delete a memory file from personal or shared storage.
Inputs: filename (required), scope.
memory_listA
List memory files. Shows personal files (for current user) and shared files. Use scope='personal', 'shared', or 'all' (default).
Inputs: scope.
memory_pullA
Pull shared memory file(s) into your personal storage. Use filename='*' to pull ALL shared files at once. If a file already exists locally, it gets overwritten with the shared version.
Inputs: filename (required).
memory_readA
Read a memory file by filename. Searches personal first, then shared.
Inputs: filename (required), scope.
memory_writeA
Write/update a memory file. Saves to personal storage by default. Content should be markdown with YAML frontmatter (name, description, type).
Inputs: content (required), filename (required), scope.
odoo_attachment_downloadA
Download an ir.attachment from Odoo by ID. Returns filename, mimetype, size, and base64-encoded content.
Inputs: attachment_id (required), save_path, connection.
odoo_attachment_uploadA
Upload a file as an ir.attachment on an Odoo record. Returns the attachment ID which can be used with odoo_message_post (attachment_ids parameter) or linked to any record.
Inputs: model (required), res_id (required), filename (required), content_base64 (required), +2 more.
odoo_cert_infoA
Return the pinned SSL certificate details (issuer, subject, notAfter, fingerprint) for a connection. Useful to verify which self-signed cert MCP has trusted. Requires verify_ssl=False on the connection.
Inputs: alias.
odoo_cert_refreshA
Re-fetch the peer SSL certificate for a connection and overwrite the pinned copy. Use after the server's self-signed cert was rotated. Fails if the connection has verify_ssl=True (standard CA verification does not need pinning).
Inputs: alias.
odoo_connectA
Add/update an Odoo connection. Auth via password or API key. Protocol: xmlrpc (Odoo 8+) or jsonrpc (Odoo 14+).
Inputs: db (required), url (required), username (required), alias, +4 more.
odoo_connectionsA
List all configured Odoo connections.
Inputs: no named parameters.
odoo_createB
Create one or more records. Returns list of new IDs.
Inputs: model (required), values (required), connection.
odoo_disconnectC
Remove an Odoo connection.
Inputs: alias.
odoo_executeA
Execute any model method via execute_kw. Use for workflow actions (action_confirm, action_done), custom methods, or anything not covered by CRUD tools.
Inputs: model (required), method (required), args, kwargs, +1 more.
odoo_fields_getB
Get field definitions for an Odoo model. Returns field names, types, labels, and attributes.
Inputs: model (required), attributes, connection.
odoo_fp_configureA
Add or update a tax action map entry for a fiscal position (l10n_bg_tax_admin). Provide action_id to update existing entry, or position_id to create new. Use odoo_fp_types for available selection values.
Inputs: action_id, move_type, account_id, connection, +9 more.
odoo_fp_detailsB
Get detailed fiscal position with all tax action map entries (l10n_bg_tax_admin). Returns position info plus full action mappings: move types, BG move types, VAT types, document types, narrations, replacement rules, etc.
Inputs: position_id (required), connection.
odoo_fp_listA
List fiscal positions with tax action map summary (l10n_bg_tax_admin). Shows position name, auto_apply, country, company, and action mapping count.
Inputs: name, limit, company_id, connection, +1 more.
odoo_fp_remove_actionC
Remove a tax action map entry from a fiscal position (l10n_bg_tax_admin).
Inputs: action_id (required), connection.
odoo_fp_typesA
Get available selection values for fiscal position configuration (l10n_bg_tax_admin). Returns move_types, bg_move_types, doc_types, and type_vat. Use as reference when calling odoo_fp_configure. Set live=true to fetch current values from Odoo instead of cached.
Inputs: live, connection.
odoo_get_field_translationsA
Read current translations for a translatable field on a record. For simple translate=True fields returns {lang: value}. For html_translate/xml_translate fields returns {lang: {term: value}}. Auto-detects the translate type via fields_get. Works on blog.post.content, blog.post.name, product.template.website_description, ir.ui.view.arch_db, any res.partner.name with translate=True, etc.
Inputs: model (required), res_id (required), field_name (required), connection.
odoo_list_modelsC
List available Odoo models (ir.model). Optionally filter by name pattern.
Inputs: limit, pattern, connection.
odoo_list_translatable_fieldsA
List all translatable fields on an Odoo model. Returns per-field: translate type ('simple' for translate=True, 'html' for html_translate, 'xml' for xml_translate, 'callable' for other), field type, label, help. Use this to discover which fields on blog.post, product.template, website.page, res.partner, etc. can be translated.
Inputs: model (required), connection.
odoo_message_postA
Post a message or internal note on any Odoo record (chatter). Body supports Markdown formatting (headers, bold, italic, tables, code blocks) — automatically converted to HTML. Use message_type='note' for internal notes (employees only) or 'comment' for public messages (visible to followers).
Inputs: body (required), model (required), res_id (required), subject, +4 more.
odoo_module_infoA
Get detailed information about an Odoo module: Odoo RPC state + filesystem locations (OCA, EE, custom repos). Shows where the module physically exists, its manifest, dependencies, and installation status.
Inputs: module (required), connection.
odoo_readB
Read specific records by IDs.
Inputs: ids (required), model (required), fields, connection.
odoo_record_backupA
Read full field snapshot of one or more records (any model) and return as a JSON structure. Use BEFORE destructive operations to capture state for possible rollback. Returns a dict with metadata (date, connection alias, model, ids, field count) plus the records themselves. Does NOT write to disk — caller decides what to do with the JSON.
Inputs: ids (required), model (required), connection, include_related.
odoo_refreshA
Send a refresh notification to the user's Odoo browser tab. Call this after creating, updating, or deleting records so the user's list/form/kanban view reloads automatically. Requires l10n_bg_claude_terminal module installed on the Odoo instance.
Inputs: model, res_id, connection.
odoo_reportC
Generate a PDF report for records. Returns base64-encoded PDF.
Inputs: ids (required), report_name (required), connection.
odoo_searchB
Search for record IDs matching a domain filter.
Inputs: model (required), limit, order, domain, +2 more.
odoo_search_countC
Count records matching a domain.
Inputs: model (required), domain, connection.
odoo_search_readC
Search and read records in one call. Most common operation.
Inputs: model (required), limit, order, domain, +3 more.
odoo_stock_close_unaccounted_valueA
Create an Inventory Valuation journal entry (Dr stock valuation / Cr GRNI/stock-input) for a stocked movement that was valued but not accounted (account_move_id=false), then bind the new account.move back to the record. Version-aware: works on stock.valuation.layer (Odoo 14-18) OR stock.move (Odoo 19+). GRNI account auto-detection order: (a) v14-18 → category.property_stock_account_input_categ_id; (b) v19 with l10n_bg_stock_account → category.l10n_bg_stock_input_account_id; (c) v19 vanilla → category.account_stock_variation_id (fallback). User can override via grni_account_id parameter. Refuses if record is already accounted or category is not real_time. ALWAYS use dry_run=true first.
Inputs: record_id (required), date, dry_run, connection, +2 more.
odoo_stock_initial_deleteA
Delete wrong opening stock balances — cascade-removes stock.move (is_inventory=TRUE) records, their stock.move.line, and (v14-v18) stock.valuation.layer; then zeros out stock.quant on affected (product, location, lot). Bypasses Odoo's ORM guard 'You can not delete product moves if the picking is done' via raw SQL in an ir.actions.server + env.cr.commit(). Version-aware: v14-v18 DELETEs SVL→SML→SM; v19 DELETEs SML→SM (SVL doesn't exist). PRE-FLIGHT: refuses if any affected SVL/stock.move has account_move_id set (orphan journal entries would remain). Caller must reverse those first. ALWAYS writes a full JSON backup to /backups// BEFORE any delete. ALWAYS use dry_run=true first to see the impact scope.
Inputs: tz (required), company_id (required), date_to, dry_run, +3 more.
odoo_stock_initial_importA
Import opening stock balances (initial inventory) via direct SQL INSERT, bypassing ORM overrides (e.g. custom modules that null out stock.move.name). Version-aware: v14-v18 creates stock.move + stock.move.line + stock.valuation.layer + stock.quant; v19 creates stock.move (with value/price_unit/remaining_qty/remaining_value/is_valued=true/is_in=true fields — SVL does not exist) + stock.move.line + stock.quant. All datetimes anchored at 23:59:59 in the caller's timezone, converted to UTC before INSERT. Writes a JSON snapshot to /backups// before execution. ALWAYS use dry_run=true first to preview the plan and catch category/location/product errors. For real_time valuation, follow up with odoo_stock_initial_opening_journal to book the account entry (SQL INSERT does NOT create account.move).
Inputs: tz (required), items (required), company_id (required), accounting_date (required), +3 more.
odoo_stock_initial_opening_journalA
Book the opening-balance journal entry for a previous SQL-inserted initial stock (complements odoo_stock_initial_import — SVL/stock.move written via raw SQL do NOT auto-create account.move even for real_time valuation). Creates ONE account.move (MISC journal, state=posted) with: DR lines grouped by category.property_stock_valuation_account_id (e.g. 302 Materials, 303 Products); CR contra account (default: code '122000' Retained earnings from prior years). Version-aware: v14-v18 reads stock.valuation.layer for the date; v19 reads stock.move (is_inventory=true, is_valued=true) for the same date — totals the 'value' column. DUPLICATE GUARD: before create, scans account.move.line for the same accounts on the same date — if any posted lines found, refuses and returns them (Alpinter lesson: comprehensive opening entries often already include the stock lines). ALWAYS use dry_run=true first.
Inputs: tz (required), company_id (required), accounting_date (required), ref, +5 more.
odoo_stock_mo_delete_draftA
Safely DELETE a draft or cancelled mrp.production with cascade (raw stock.moves, finished stock.moves, procurement.group if orphaned). Bypasses Odoo's ORM 'cannot be deleted' constraint by forcing state='cancel' via raw SQL, then using DELETE statements inside an ir.actions.server. Refuses if MO has any SVL, stock.move with quantity > 0, or an already-done state. ALWAYS use dry_run=true first.
Inputs: mo_id (required), dry_run, connection.
odoo_stock_product_flip_to_storableA
Flip a product from consumable (is_storable=false) to storable (is_storable=true) when the product ALREADY has stock.move records. Bypasses Odoo's ORM constraint 'You can not change the inventory tracking of a product that was already used' via raw SQL in an ir.actions.server. Then inserts a stock.quant DIRECTLY (not through an inventory adjustment wizard) so no duplicate SVL is created — existing SVLs stay intact. Use when a GRN/bill was recorded for a product that was mistakenly set as consu. ALWAYS use dry_run=true first to preview impact. Returns full pre/post snapshot.
Inputs: in_date (required), quantity (required), company_id (required), product_id (required), +3 more.
odoo_translate_context_awareA
Translate Odoo records using Claude with rich domain context for natural, fluent results (not literal). AUTO-DETECTS field kind and handles both: (1) simple char/text fields (e.g. ir.ui.menu.name, account.account.name) → batch translate via update_field_translations; (2) HTML/XML fields (e.g. ir.ui.view.arch_db, website.page, product.template.website_description) → extracts canonical terms via get_field_translations, translates each term preserving inline HTML tags (, , ...), writes back in terms mode. Context to LLM: Odoo model, parent chain for menus, existing translations, user-supplied domain hint, field kind (simple/html/xml). Requires Odoo 16+. Requires ANTHROPIC_API_KEY env var (per-tenant override: ANTHROPIC_API_KEY_). Recommended models: 'claude-haiku-4-5' (menu labels, fast), 'claude-sonnet-4-6' (balanced, website pages), 'claude-opus-4-7' (complex legal/accounting terminology). ALWAYS use dry_run=true first to review proposals.
Inputs: ids (required), model (required), target_lang (required), dry_run, +6 more.
odoo_translate_fieldA
Write translations for a simple translate=True field (Char/Text/Selection). Use for: blog.post.name/subtitle/teaser, product.template.name, res.partner.name, account.account.tag.name — any short/plain field. Format: translations = {lang_code: value, ...}. Pass value=null/'' to remove a language's translation (falls back to source_lang). Errors if field.translate is not True (use odoo_translate_html for HTML). Auto-detects Odoo version: v16+ uses update_field_translations() native API, older falls back to ir.translation.
Inputs: model (required), res_id (required), field_name (required), translations (required), +4 more.
odoo_translate_htmlA
Write/read translations for translate=html_translate / translate=xml_translate fields. Use for: blog.post.content, product.template.website_description, hr.job.description, ir.ui.view.arch_db (covers website.page), website.menu.mega_menu_content, event.track.description, forum.forum.guidelines, res.partner.website_description. Three modes: (a) mode='extract' — READ-ONLY. translations arg ignored. Returns the list of translatable terms that Odoo's html_translate engine extracts from source_lang. Each term is an HTML-serialised translatable block (preserves inline tags like ). Use this to see what must be translated before writing. (b) mode='terms' — translations = {lang: {source_term: translated_term, ...}} Direct term map. Terms must match exactly those from mode='extract'. Calls update_field_translations() with JSONB payload. (c) mode='replace' — translations = {lang: full_html_string}. The tool writes each language's HTML through Odoo's native ORM with {'lang': } context. Odoo's html_translate engine aligns terms automatically — no manual term extraction required. This is what Odoo's Website editor does internally. Errors if field.translate is not callable. Requires Odoo 16+.
Inputs: model (required), res_id (required), field_name (required), mode, +5 more.
odoo_unlinkC
Delete records by IDs.
Inputs: ids (required), model (required), connection.
odoo_versionC
Get Odoo server version info.
Inputs: connection.
odoo_web_callB
Call any Odoo model method via web session (JSON-RPC /web/dataset/call_kw). Works like odoo_execute but uses cookie session instead of XML-RPC.
Inputs: model (required), method (required), args, kwargs, +1 more.
odoo_web_exportC
Export records to structured data via web session (Odoo export_data).
Inputs: model (required), fields (required), domain, connection, +1 more.
odoo_web_loginA
Login to Odoo web interface with user/password. Creates a persistent cookie session for accessing web controllers, exports, reports, and any frontend URL. Session is reused until logout or expiry.
Inputs: db, url, login, password, +1 more.
odoo_web_logoutC
Destroy web session and logout.
Inputs: connection.
odoo_web_readB
Search and read records via web session (frontend web_search_read format). Supports field specification, domain, limit, offset, order.
Inputs: model (required), fields (required), limit, order, +3 more.
odoo_web_reportC
Download PDF report via web session. Returns base64-encoded PDF.
Inputs: ids (required), report_name (required), save_path, connection.
odoo_web_requestC
Raw HTTP request to any Odoo controller URL via web session. Access frontend pages, custom controllers, website routes, etc.
Inputs: path (required), data, method, params, +1 more.
odoo_website_add_snippetA
Insert a snippet into a target HTML field. Fetches the snippet template from ir.ui.view by key, extracts its root HTML element, applies optional substitutions (text/attr/src changes before insertion), and places it at the specified position relative to an anchor. Positions: 'end' (after last element), 'begin' (before first), 'after' (after anchor_xpath), 'before' (before anchor_xpath), 'replace' (replace anchor_xpath). Substitutions format: {'relative_xpath': value} for text; {'relative_xpath/@attr': value} for attribute; {'relative_xpath/@style:background-image': 'url(...)'} for CSS property; {'relative_xpath/@src': 'https://...'} for image URL.
Inputs: model (required), res_id (required), field_name (required), snippet_key (required), +6 more.
odoo_website_list_page_snippetsA
List snippets currently embedded in a target HTML field. Target can be a blog.post (field=content), website.page (field=arch_db via view_id), product.template (field=website_description), or any HTML field. Parses the HTML with lxml, finds all elements bearing a data-snippet attribute. Returns per-snippet: index, data_snippet, data_name, xpath, text_preview, has_background (detects url() in style), background_url.
Inputs: model (required), res_id (required), field_name (required), lang, +1 more.
odoo_website_list_snippetsA
List available Odoo website snippet templates (ir.ui.view records with key matching 'website.s_*' or similar). Returns per-snippet: key, name, module, category (structure|content|dynamic|effect|unknown), preview. Use this to discover what snippets can be added to a page. Categories inferred from snippet key patterns and modules.
Inputs: limit, module, search, category, +1 more.
odoo_website_remove_snippetB
Remove a snippet block from a target HTML field. Locates via snippet_xpath (absolute). Writes back the reduced HTML.
Inputs: model (required), res_id (required), field_name (required), snippet_xpath (required), +3 more.
odoo_website_update_snippetA
Update an existing snippet on a target HTML field. Locates the snippet via snippet_xpath (absolute in target), applies substitutions to its descendants via relative xpaths, and writes back. Common use cases: swap background image, change H1/H2 text, update CTA button text+href, change card content. Substitution syntax identical to odoo_website_add_snippet.
Inputs: model (required), res_id (required), field_name (required), snippet_xpath (required), +4 more.
odoo_writeD
Update existing records.
Inputs: ids (required), model (required), values (required), connection.
open_connection_managerA
Open the Connection Manager GUI (desktop app). Launches GTK4 version on Linux, Qt6 on Windows/macOS. Use this when the user wants to visually manage connections, configure Portainer/GitHub, manage SSH keys, or see active sessions.
Inputs: no named parameters.
proxy_callA
Forward a tool call to an internal MCP sub-service. Available services: 'portainer' (Docker/K8s management, 38 tools), 'github' (repos/issues/PRs, 20 tools), 'teams' (MS Teams messaging, 6 tools). Use proxy_discover first to see available tools on each service.
Inputs: tool (required), service (required), arguments.
proxy_discoverA
List available tools on an internal MCP sub-service. Call this first to see what tools are available on portainer/github/teams.
Inputs: service (required).
proxy_refreshA
Re-discover tools from all internal MCP sub-services. Use after starting a new sub-service or if tools are missing.
Inputs: no named parameters.
public_access_barcodeC
Generate barcode image via /report/barcode/{type}/{value}.
Inputs: value (required), width, height, save_path, +2 more.
public_access_downloadB
Download attachment/binary content by ID via /web/content/{id}. Public route.
Inputs: content_id (required), save_path, connection.
public_access_export_csvC
Export Odoo list data as CSV via web session.
Inputs: model (required), fields (required), domain, save_path, +2 more.
public_access_export_xlsxC
Export Odoo list data as XLSX file via web session. Returns base64.
Inputs: model (required), fields (required), domain, save_path, +2 more.
public_access_imageB
Download image field from record via /web/image/{model}/{id}/{field}.
Inputs: model (required), record_id (required), field, save_path, +1 more.
public_access_portal_homeC
Get portal home page content via web session (/my/home).
Inputs: connection.
public_access_portal_invoicesC
Get list of portal invoices (/my/invoices). Returns HTML page content.
Inputs: page, connection.
public_access_portal_ordersC
Get list of portal sale orders (/my/orders). Returns HTML page content.
Inputs: page, connection.
public_access_portal_purchasesC
Get list of portal purchase orders (/my/purchase).
Inputs: page, connection.
public_access_portal_ticketsB
Get list of portal helpdesk tickets (/my/tickets).
Inputs: page, connection.
public_access_report_htmlC
Render report as HTML via web session.
Inputs: ids (required), report_name (required), connection.
public_access_report_pdfC
Download PDF report via web session. Route: /report/pdf/{report_name}/{doc_ids}.
Inputs: ids (required), report_name (required), save_path, connection.
public_access_report_xlsxC
Download XLSX report via OCA reporting-engine (/report/xlsx/{name}/{ids}).
Inputs: ids (required), report_name (required), save_path, connection.
public_access_shopC
Get website shop product listing (/shop). Returns HTML.
Inputs: page, search, connection.
public_access_sitemapC
Download sitemap.xml from Odoo website.
Inputs: connection.
session_listA
List MCP session-store rows. Regular callers see their own sessions; admin principals (MCP_ADMIN_PRINCIPALS) see all.
Inputs: principal, include_orphaned.
session_revokeA
Revoke one MCP session (admin, or your own): marks the row revoked and tears down its live resources (connection, web, telegram client when unreferenced).
Inputs: session_key (required).
ssh_executeA
Execute a command on a remote server via SSH. Uses SSH config from connections.json (connection's ssh section). Provide either a connection alias (to use saved SSH config) or explicit host/user.
Inputs: command (required), host, port, user, +3 more.
telegram_agent_create_quoteC
Create a DRAFT sale.order in the scenario's company. Returns a shareable summary. Honors quote_send (approve → confirm before sending; auto → may send).
Inputs: lines (required), chat_id (required), connection, partner_id.
telegram_agent_enrollA
Enroll a Telegram chat for agentic handling with a scenario. mode: auto (self-converse) | advisory (suggest, human sends) | notify. scenario (optional dict): persona, default_toolset, data_scope {models,fields,pricelist}, forbid[], company_id, quote_send (approve|auto), skills[] ({name,toolset,triggers[]}). Confidentiality is enforced by data_scope. Enrolments are per-principal — you never see or change another principal's.
Inputs: chat_id (required), mode, scenario.
telegram_agent_get_scenarioA
Get the full scenario for an enrolled chat.
Inputs: chat_id (required).
telegram_agent_listA
List enrolled chats with their mode/persona/skills.
Inputs: no named parameters.
telegram_agent_product_lookupA
Search products for an enrolled chat, returning ONLY the scenario's allowlisted fields (confidential fields withheld by design). Uses the scenario's company + pricelist.
Inputs: query (required), chat_id (required), limit, connection.
telegram_agent_routeA
Classify the conversation direction for an enrolled chat and return the active TOOLSET to switch to (based on the scenario's skills), plus the data_scope/forbid guardrails and a memory_search_hint. Call this on each incoming message before acting.
Inputs: text (required), chat_id (required).
telegram_agent_set_scenarioB
Replace the scenario for an enrolled chat.
Inputs: chat_id (required), scenario (required).
telegram_agent_unenrollC
Stop agentic handling for a chat.
Inputs: chat_id (required).
telegram_authA
Authenticate with Telegram. Two-step process: 1) Call with phone → code is sent to Telegram. 2) Call with phone + code → authenticated. If 2FA enabled, provide password too.
Inputs: phone (required), code, password.
telegram_auth_statusA
Check Telegram authentication status.
Inputs: no named parameters.
telegram_configureA
Set Telegram API credentials (api_id and api_hash from my.telegram.org).
Inputs: api_id (required), api_hash (required).
telegram_download_mediaA
Download media/document from a Telegram message into the MCP download root (MCP_DOWNLOAD_ROOT, default /data/downloads). Returns the saved path.
Inputs: chat (required), filename (required), message_id (required).
telegram_get_dialogsC
List recent Telegram chats (users, groups, channels).
Inputs: limit.
telegram_get_messagesA
Read messages from a Telegram chat. Chat can be @username, phone, or numeric ID.
Inputs: chat (required), limit, search.
telegram_search_contactsC
Search Telegram contacts by name or username.
Inputs: query (required).
telegram_send_fileA
Send a file/document to a Telegram chat. The file must live under the MCP download root (MCP_DOWNLOAD_ROOT, default /data/downloads) — pass a relative path or an absolute path inside it. Chat can be @username, phone, or numeric ID.
Inputs: chat (required), path (required), caption, reply_to.
telegram_send_messageB
Send a Telegram message. Chat can be @username, phone, or numeric ID.
Inputs: chat (required), message (required), reply_to.
telegram_sub_addA
Subscribe a principal's Telegram listener to a chat (admin allow-list). No subscriptions for a principal = listen to ALL (back-compat). Once a principal has any subscription, only those chats are pushed to its telegram: channel. Use 'principal' to assign for another user.
Inputs: chat_id (required), mode, note, title, +1 more.
telegram_sub_listB
List a principal's Telegram chat subscriptions. allow_all=true means no filter (listens to all).
Inputs: principal.
telegram_sub_removeC
Remove a chat from a principal's Telegram subscription allow-list.
Inputs: chat_id (required), principal.
user_connection_activateA
Activate one of your personal connections as the working connection.
Inputs: alias (required).
user_connection_addB
Add/update a personal Odoo connection (saved per-user). Supports Odoo, SSH, and Portainer settings.
Inputs: db (required), url (required), user (required), alias (required), +8 more.
user_connection_deleteA
Delete a personal connection.
Inputs: alias (required).
user_connection_listB
List your personal connections.
Inputs: no named parameters.
who_am_iA
Show current user identity and active connection.
Inputs: no named parameters.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 138 tools
Many tools have clear distinct purposes, but there are several overlapping pairs like odoo_search vs odoo_search_read vs odoo_search_count, odoo_report vs odoo_web_report vs public_access_report_pdf, and odoo_execute vs odoo_web_call. The separation between web-session and XML-RPC tools is subtle and could cause misselection. Some AI and stock tools are also highly specialized, but their descriptions help.
Most tools follow a consistent snake_case prefix_domain_action pattern (e.g., odoo_read, telegram_send_message, google_calendar_list). Some outliers like identify, who_am_i, and open_connection_manager break the pattern but are still readable. Overall the naming is predictable and grouped by domain.
138 tools is far beyond any reasonable scope for a single MCP server. Even for a multi-domain personal assistant, this is excessive and will overwhelm an agent's tool selection. The server would benefit from splitting into separate MCP servers per domain (Odoo, Telegram, Google, AI, etc.).
The tool surface covers a wide range of operations across Odoo (CRUD, web, reports, translations, website snippets, fiscal positions, stock), Telegram, Google (Gmail, Calendar), AI pipelines, and memory. Some minor gaps exist (e.g., no Gmail draft/delete, no Odoo user management), but core workflows are well covered. The sheer number suggests high coverage, though it could be more focused.
Maintenance
Related MCP Connectors
Odoo MCP connector for ChatGPT and Claude: ask sales, invoices and stock in plain words. Read-only.
Connect your Odoo to Claude and ChatGPT: ask about sales, stock, customers and invoices.
MCP server unifying ERPs, CRMs, APIs and knowledge base for Claude, ChatGPT and Gemini.
Connect any AI assistant to Odoo 16–19 via OAuth 2.0 + PKCE. 400 free calls, no local install.
Related MCP Servers
- AlicenseBqualityCmaintenanceAn MCP server implementation that integrates with Odoo ERP systems, enabling AI assistants to interact with Odoo data and functionality through the Model Context Protocol.411,602 PyPI427MIT
- AlicenseNot gradedqualityAmaintenanceAn MCP server that enables AI assistants like Claude to interact with Odoo ERP systems through natural language, allowing users to search, create, update, and manage business data in their Odoo instance.39,303 PyPI413Mozilla Public 2.0
- FlicenseNot gradedqualityDmaintenanceMCP server to connect Claude with Odoo 18, enabling CRUD operations on Odoo models via natural language.2-
- AlicenseBqualityCmaintenanceConnects MCP clients like Claude to your Odoo ERP, enabling natural language queries, reports, and optional data modifications.942 npmMIT