odoo-mcp
Provides tools for interacting with Odoo Enterprise accounting workflows, including trial balance, profit and loss, balance sheet, aged receivables and payables, cashbook, unmatched bank statement detection, bank reconciliation proposals, invoices, supplier bills, credit notes, and payment registration.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@odoo-mcpShow the trial balance for December 2024."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Odoo MCP
odoo-mcp is a workflow-native MCP server for Odoo Enterprise. It exposes
read-only capability discovery, trial-balance reporting, aged receivables and
payables reporting, cashbook visibility, unmatched bank-line detection, and
proposal-only bank reconciliation. It also exposes bounded, read-only Payroll
evidence for exact periods, batches, payslips, employees, salary rules, and
payroll work entries, plus deterministic Payroll comparison and anomaly review.
The initial accounting release also supports bounded invoice, supplier-bill, credit-note, payment-registration, and manual-journal workflows. Mutating tools are preview-only by default and require explicit execution plus idempotency.
The internal Odoo adapter provides bounded, typed, company-scoped primitives for the accounting and Payroll workflows. It enforces fixed model/action allowlists, strips denied fields, normalizes dates, decimals, relations, and cursor pages, and translates Odoo authentication, permission, and transport failures into safe errors. It does not expose generic CRUD or an Odoo configuration surface.
Supported connection targets are Odoo.sh and self-hosted Odoo Enterprise:
Odoo 18 through external JSON-RPC
Odoo 19 through JSON-2
Other Odoo versions, Odoo Online, Community edition, and custom forks are not supported. This package is not an Odoo module and does not expose generic model CRUD.
Installation
The product and commands are named odoo-mcp; the PyPI distribution is
odoo-erp-mcp because the odoo-mcp distribution name is owned by another
project.
pipx install odoo-erp-mcp
odoo-mcp --helpThe package also exposes odoo-erp-mcp as a compatibility launcher for MCP
Registry clients. It starts the same server as odoo-mcp.
Related MCP server: zCloak Xero Accounting MCP
Local Development
Requires Python 3.11 or newer and uv.
Copy-Item .env.example .env.local
# Replace the placeholders in .env.local, then:
uv sync --locked --all-extras
uv run odoo-mcp --profile local --config config/config.example.yamlLocal Development uses stdio. Process-environment settings take precedence over
.env.local. Never commit .env.local.
Remote profiles
Dedicated Remote and Shared Hosted use the same server, registry, workflow, and Odoo adapter as Local Development, exposed through Streamable HTTP:
uv run odoo-mcp --profile dedicated --host 127.0.0.1 --port 8000
uv run odoo-mcp --profile shared --host 127.0.0.1 --port 8000Dedicated Remote reads its single Odoo connection from the process environment;
it never loads .env.local. Every /mcp request must carry a deployment-issued
HS256 bearer JWT. The server validates its signature, fixed issuer, fixed
audience, expiry, issued-at time, subject, and client ID before MCP routing;
permissions and company grants remain server-owned. Configure
ODOO_MCP_AUTH_ISSUER, ODOO_MCP_AUTH_AUDIENCE, and a secret-store supplied
ODOO_MCP_AUTH_SIGNING_KEY of at least 32 characters.
Shared Hosted is a runnable open-enrollment application in the same image. It
serves OAuth discovery, dynamic client registration, authorization, token,
revocation, browser enrollment, protected-resource metadata, and MCP routes.
The application verifies each Odoo connection, lets the user select from the
companies that Odoo returned, stores the credential encrypted, and binds every
token to exactly one active connector. Configure it from
.env.shared.example; its versioned 256-bit encryption keys must come from an
operator-controlled secret store. Missing configuration, invalid ciphertext,
inactive grants, unsafe Odoo destinations, and unauthorized connector bindings
fail closed. Shared Hosted supports either one writable process with a durable
local SQLite volume or qualified PostgreSQL with pooled runtime and direct
migration connections. TLS, public ingress, monitoring, and production
deployment remain operator responsibilities.
Reproducible Dedicated Remote Docker and systemd templates, Shared Hosted
integration requirements, and network hardening guidance are in
docs/deployment.md. Do not expose the example HTTP
listener directly to the public internet.
Durable state
odoo_mcp.storage.Storage provides ordered SQLite and PostgreSQL migrations and tenant-scoped
repositories for audit records, proposals, artifacts, idempotency reservations,
capability snapshots, and encrypted Shared Hosted connections. Audit rows are
append-only and SHA-256 hash-chained per tenant. Idempotency reservations bind
the tenant, company, tool, key, and request payload for 24-hour replay, while
in-progress and unknown outcomes remain blocked for explicit recovery. Final
outcomes must retain a replayable response and match the reserving company.
Audit failure text is derived from registered error codes; free-form upstream
error text is not persisted.
SQLite backups use a consistent snapshot. Restore writes to a new destination and is accepted only after database integrity, migrations, tenant audit chains, idempotency state, capability data, and encrypted connections verify. Encryption keys must be backed up and restored separately. PostgreSQL uses database-level migration serialization and preserves the same repository, OAuth, audit, idempotency, encryption, and lifecycle contracts.
The server stores local durable state at .odoo-mcp/state.sqlite3 by default.
Use --storage <path> to select a different SQLite file. Successful accounting
reports atomically persist their Markdown artifact and a compact audit outcome;
failed and denied report calls persist a secret-safe failure audit when an
isolation identity has been resolved.
Write safety
odoo_mcp.policy.WriteSafetyCoordinator is the required boundary for
write-capable workflows. It enforces registry risk metadata, resolved identity,
permission, company, and capability gates before workflow preparation. Calls
default to preview-only behavior. Execution requires explicit dry_run: false
and a non-empty idempotency key, then reserves that key and appends the attempt
audit before fresh-state validation and the Odoo mutation.
Successful, rejected, conflicting, replayed, known-failed, and unknown outcomes remain distinct and replay-safe across restart. An uncertain mutation is never automatically retried; if outcome persistence fails after a possible mutation, the in-progress reservation continues to block duplicate execution. Odoo permission denials that prove no mutation occurred remain structured known failures. Audit and response text suppress raw exception details.
Human confirmation belongs to the MCP client host. The server does not issue
approval tokens, provide an approval UI, or automatically turn a preview into
execution. Reconciliation calls default to preview. An explicit
dry_run: false call with an idempotency key stores a server-owned proposal and
Markdown artifact, but never finalizes reconciliation or changes a bank
statement line in Odoo.
Configuration
Local and Dedicated Odoo settings are documented in .env.example; Shared
Hosted operator settings are documented in .env.shared.example. Do not configure
an Odoo version: the adapter detects it and fails explicitly for unsupported or
malformed responses. config/config.example.yaml is the safe MCP permission-map
example and enables the current read tools. A tool is authorized only when it is
listed under its registry-defined
permission; unknown or mismatched entries prevent startup.
Use a dedicated non-production Odoo technical user with only the required company and module access. Company IDs are an additional MCP authorization boundary and never expand the technical user's Odoo permissions.
Accounting workflows
get_currency_rate_historyreturns the selected company's Odoo rate history for one exact currency, preserving company-over-shared precedence and both rate directions without performing conversions.get_trial_balancereturns posted opening balances, inclusive-period debit and credit movement, closing balances, totals, and a Markdown artifact.get_profit_and_lossclassifies posted lines by Odoo's income and expense account types for an inclusive period. Income uses credit-minus-debit, expenses use debit-minus-credit, and net profit is income minus expenses.get_balance_sheetclassifies posted lines by Odoo's asset, liability, and equity account types through an inclusive date. It reports unclosed earnings separately within total equity and verifies the accounting equation at company-currency precision.get_aged_receivablesandget_aged_payablesreconstruct posted residuals as of a date, including later partial reconciliations, and group them into not-yet-due, 1–30, 31–60, 61–90, and 90+ day buckets.get_cashbookreturns posted move lines from cash and bank journals with opening balance, period debit and credit, and closing balance.flag_unmatched_statement_linesidentifies unreconciled statement lines without one unique eligible match at or above the requested threshold.reconcile_bank_statement_linesscores exact one-to-one candidates by amount, partner, normalized reference, and date proximity. Ties and candidate reuse remain explicit unmatched results. The tool can persist a proposal locally; it does not perform Odoo reconciliation.list_open_invoicesandlist_open_billsreconstruct record- and company-currency residuals as of a date, including later partial reconciliations.create_customer_invoiceandcreate_supplier_billprovide non-mutating, input-only previews by default. Explicit execution creates one unposted draft and reads back Odoo's effective accounting results.create_credit_notecreates one linked full draft reversal. Before posting,validate_invoicereports locally determinable balance, currency, account, total, and tax blockers while explicitly deferring Odoo-only posting checks.register_paymentdelegates route discovery and execution to Odoo's standard payment-registration wizard. Preview may create bounded ephemeral wizard records but never executes a payment or alters accounting records. Non-manual or unidentified methods report their possible external effect as unknown. Execution requires an idempotency key and a freshly revalidated wizard route.list_journal_entriesreturns filtered draft and posted manual entries with bounded line details and opaque continuation cursors.create_journal_entrycreates only a balanced draft, while the separatepost_journal_entrytool revalidates and posts one existing draft after explicit execution.
Report results are deterministically ordered and cursor-paginated with a default limit of 100 and maximum of 500. Empty data is a successful empty report; upstream denial, timeout, malformed data, or partial retrieval is a structured failure rather than an empty result. Paginated Markdown artifacts label the row range, continuation state, and whole-report totals explicitly.
Profit-and-loss and balance-sheet analytic filters apply Odoo's percentage distribution to exact analytic account IDs. These reports do not infer custom chart-of-accounts groups, fiscal-year closing rules, consolidation, eliminations, or localization-specific report layouts. Unsupported account classifications fail explicitly instead of being guessed.
Payroll evidence workflows
list_payroll_periodslists exact date pairs observed on company-scoped payslips, with bounded status, batch, and currency evidence.get_payroll_batchreturns one exact batch, compact payslips, complete employee and state counts, and currency-partitioned observed rule/category totals.list_payslipslists compact payslip facts for an exact batch, exact period, or both.get_payslipreturns one exact payslip with all bounded calculated lines, worked days, and one-off inputs.get_employee_payroll_contextreturns only the employee and contract/version evidence applicable to an exact period.list_salary_rulesreports rule metadata observed on eligible payslip lines; it is not a Payroll configuration catalogue.get_attendance_summarysummarizes Payroll work entries by employee, type, code, and state. It does not read raw attendance or claim proof of physical attendance.compare_payroll_periodscompares exact employee sets, contract facts, work-entry hours, and currency-partitioned observed line, rule, and category totals across two non-overlapping periods.analyze_employee_payroll_changeseparates directly observed changes from correlations and unresolved causes for one exact employee.detect_payroll_anomaliesapplies fixed public percentage thresholds and an optional request-time modified-z-score check. It does not learn or retain a customer baseline; its source-linked Markdown summary is returned inline and is not stored as an artifact. The percentage thresholds are 5% (strict), 10% (standard), and 20% (relaxed). Statistical checks require six comparable history values and an absolute modified z-score of at least 3.5; fewer values or a zero median absolute deviation is reported as unavailable.explain_paysliporganizes Odoo-returned line arithmetic, categories, worked-day evidence, and contract context without evaluating salary-rule code or reproducing gross-to-net calculation.prepare_payroll_approval_packreturns one request-time JSON and inline Markdown review pack with source-linked totals, optional prior-period variance, anomalies, exceptions, limitations, recommended human review actions, and a sign-off checklist. It neither approves payroll nor creates a durable pack or workflow record.
These evidence tools require payroll_read and the accessible Odoo Payroll
capability. They are read-only, use request-bound pagination, return Odoo source
IDs, and store only non-sensitive invocation metadata in the audit chain.
Payroll responses are not persisted as artifacts, proposals, idempotency
records, or workflow state. Monetary comparisons never combine or convert
currencies; recognized BASIC, GROSS, and NET totals require those exact
Odoo rule codes, and employer cost remains explicitly unavailable.
Controlled draft Payroll writes
set_draft_payroll_inputpreviews or explicitly creates or updates one eligible one-off input on one editable payslip.remove_draft_payroll_inputpreviews or explicitly removes one exact, eligible one-off input.recalculate_draft_payslipseparately invokes Odoo's standard payslip calculation and returns fresh payslip evidence.
These tools require payroll_draft_write and the accessible Odoo Payroll
capability. Every call defaults to a mutation-free preview. Execution requires
dry_run: false and a non-empty idempotency key, rechecks the complete current
input state, and serializes writes per payslip. Input changes never trigger
recalculation automatically. Recalculation remains limited to an editable
payslip and cannot confirm, post, close, pay, or otherwise finalize Payroll.
Only operational IDs and statuses are retained for audit and replay; input
descriptions, amounts, employee details, and calculated Payroll values are not
stored by these write workflows.
Verification
One command runs formatting checks, static analysis, tests, package builds, and artifact inspection in temporary directories:
uv run python scripts/verify.pyAutomated tests use synthetic Odoo responses. They do not establish live Odoo version compatibility, real permissions, installed modules, or deployment networking.
Storage backup, restore, integrity verification, monitoring, upgrade, rollback,
and incident procedures are documented in
docs/operations.md. See SECURITY.md for
the security boundary and vulnerability reporting, and SUPPORT.md
for supported configurations and support requests.
docs/demo.md provides a bounded accounting demonstration that
keeps every write-capable call in preview mode unless the operator separately
authorizes execution in a non-production Odoo environment.
This server cannot be deployed
Maintenance
Related MCP Connectors
Fortnox accounting for one company: invoices, reports, ledger, documents. Read-only until enabled.
- LedgerHQOAuthpro.ledgerhq
Double-entry bookkeeping: accounts, journal entries, bank feeds, reconciliation, and reports.
Open-source AI accounting skills verified by licensed accountants (tax, VAT, payroll).
Startup books from live bank and billing feeds: P&L, balances, approvals, period close.
Related MCP Servers
- AlicenseBqualityCmaintenanceMCP server that provides full read/write access to Odoo accounting data including invoices, vendor bills, journal entries, payments, reconciliation, and financial reports, as well as BOQ/Project Budget data when available.34MIT
- FlicenseNot gradedqualityBmaintenanceEnables accountants to connect Xero, read accounting data, analyze, and execute controlled draft operations after explicit confirmation, with Xero as the source of truth.-
- AlicenseNot gradedqualityCmaintenanceEnables read-only interaction with TallyPrime accounting data, letting users ask accounting questions and retrieve live company information, financial reports, and ledgers without altering anything in Tally.10 npmMIT
- AlicenseNot gradedqualityAmaintenanceEnables users to query exact balances, expense changes, trial balances, and traceable ledger evidence from a synthetic company through read-only MCP tools and review prompts. It provides deterministic, integer-arithmetic accounting data for supervised evaluation without requiring an account or network connection.MIT