NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI
A Model Context Protocol server for Odoo ERP systems
Last release 1 months ago
26 Aug 2026
Release timing varies
gaps range from 9 days to 5 months
Nearly every release is documented
notes for 21 of 21 stable releases
Nothing withdrawn
no release was ever pulled
1 years old
21 releases · first in 2025
One column per month.
get_current_context tool : returns the connected user, timezone, company scope and UTC guidance as structured data.
get_current_context tool: returns the connected user, timezone, company scope and UTC guidance as structured data.initialize instructions: the same context block is sent at handshake. When the user read fails it falls back to the UTC guidance plus a note naming the server's own reason (e.g. the caller is not in the MCP User group), or the likely cause when the server gives none.get_fields tool: schema discovery with curated attributes by default; honors field_names and attributes.odoo://{model}/record/{id}/{field} and odoo://attachment/{id}, served with per-read mimeTypes.bin_size).get_record returns display-name summaries for small collections in related_summaries.res_id inverses and domain-restricted one2manys keep the id-in domain).limit, floored at 10,000 rows.aggregate_records pagination: reports has_more and next_hint.aggregate_records overall totals: an omitted or empty groupby returns a single overall row.post_message subject: optional subject argument.active_test=False), matching what get_record already returned.all_fields_fallback: new field_selection_method value, reported when smart selection is unavailable and every field was read.res_model is checked alongside ir.attachment, so enabling that one model no longer exposes every attachment body on the database.search_records/get_record/aggregate_records tools and the record/search/count resources scope ir.attachment to res_models the caller may read — a row carries url and index_content (the extracted document text). An allowlist the module cannot report fails closed, as a retryable "could not verify access".create_record/update_record/delete_record and post_message's attachment_ids check the attached-to model. An ungated update_record could repoint an excluded model's attachment at an allowed one and then read it back, bypassing the read gate entirely.run_http() host/port removed: it reassigned app.settings.host after FastMCP had already chosen transport security from config.host, so an embedder could bind loopback with DNS-rebinding protection left off; the bind now always follows config.host/config.port.ERROR_MAPPINGS branch returned before the removal patterns ran.ODOO_MCP_ALLOWED_HOSTS Origin ports: an entry that pins a port now pins it for Origin too; the wildcard :* origin trusted a page served from any other port on the same host.ODOO_MCP_MAX_BINARY_SIZE (default 50 MB): bounds a single binary/attachment read, checked before the payload is fetched.*password/*_pass/*secret/*_token, the *_api_key/*_hmac_key/*_signature_key compounds, OAuth refresh tokens, passkeys, salts, OTP secrets and PINs, including trailing-underscore spellings (pass_, api_key_).smtp_pass/webhook_secret are redacted instead of logged in cleartext.call_model_method alias gaps: copy_data, copy_multi, update, get_view and get_views are refused alongside the primitives they alias.ERROR_MAPPINGS.call_model_method hardening: rejects ir.actions.*/ir.cron, the web_* family, and ORM CRUD primitives; list results truncate at 100 items.compute key fields_get() never returns; it now gates on store=False (related fields exempt).create_record/update_record values and call_model_method arguments are all range-checked, instead of an OverflowError surfacing as Connection error: Operation failed.list_models cap: the silent 200-model cap is gone — listings truncate at 500 with an explicit note and the real total.list_models filter: the system-model exclusion is prefix-anchored (! + =like); plain like matched substrings and hid every model merely containing ir./base. (repair.*, ...).faultCode (2 = UserError/ValidationError, 4 = AccessError), the only signal /xmlrpc/2/* sends — previously every business error read as Connection error: Operation failed:.CONTEXT:/HINT: lines are no longer stripped from business exceptions (psycopg2 diagnostics still are).ODOO_MCP_ALLOWED_HOSTS is set.ODOO_MCP_ALLOWED_HOSTS port-less authority: a port-less entry now also allows the bare Host/Origin a proxy sends on 80/443, which previously matched nothing.ODOO_MCP_ALLOWED_HOSTS IPv6: bracketed and bare literals are parsed and normalized instead of producing junk patterns that locked the deployment out.run_http() under a default stdio config no longer strands tool handlers on a dead connection (#70).aggregate_records groupby collision: aggregating a field that is also a groupby key is refused on the pre-19 path instead of returning corrupted groups.aggregate_records duplicate aggregates: two aggregates over one field are refused on the pre-19 path, where read_group dropped one and mislabeled the other.__extra_domain drilldown contract: documented — AND it with the caller's domain (group condition only on Odoo 19; already the full domain on older servers).list_resource_templates: unreadable models are filtered out when /mcp/models reports per-model operations (best-effort — older modules omit the block and everything stays listed).id: aggregates on Odoo 15/16: refused with an explanation — read_group deletes the id key before returning, so the value silently never arrived.__extra_domain on overall totals: the key is always present for groupby=[], filled in as [] on Odoo 15/16, which omit it entirely (17/18 return the caller's domain, 19 a trivially-true condition — re-ANDing any of them is a no-op).&/|/! operators lack operands is rejected with a clear message before any RPC, instead of reaching Odoo as a server-side "syntactically not correct".domain/fields strings: rejected on their own nesting depth rather than on the parser failing, so a 2 KB [[[[...]]]] is an invalid parameter on every interpreter — CPython 3.12 raised the JSON scanner's recursion ceiling, making the same input a stack-exhausting success there and a RecursionError on 3.11. Depth is counted quote-aware, so bracket characters inside values do not trip it.search(), read() and search_read() copy the context they are given — execute_kw injects the locale into it, so a caller reusing one dict accumulated our keys.lang: no longer blamed on ODOO_MCP_LOCALE — a bad context language used to null the configured locale for every later request in the process.ModelInfo.operations description: was "Allowed operations (standard mode only)"; now states it is null in YOLO mode, where the flags are global and reported once under yolo_mode.operations.Cookie: session_id=None.Permission denied for this operation, so the module's actionable wording (e.g. a user outside the MCP User group) never reached the caller; only a bare refusal maps to the generic text now.list_models swallowed the reason: an access failure reported Failed to list models: Permission denied for this operation instead of what the server actually said.Model 'x' is not enabled for MCP access.) is surfaced instead of a generic Access denied to MCP endpoints, which pointed at a credential problem that did not exist.ODOO_USER, Odoo 17+ HTML escaping, handle_error contract), documented ODOO_MCP_LOG_FORMAT/ODOO_MCP_SLOW_OPERATION_THRESHOLD_MS, and consolidated duplicated comments.list_models counts: total is the number of models returned and total_available the database count; both are emitted in every mode.bin_size and rendered as fetchable URIs. Non-stored ones stay out — bin_size cannot short-circuit a compute (sale.order.tax_totals made a 10-row read 3.3x slower).odoo://{model}/search reads only the fields its one-line summary renders, instead of every safe field of every record and then discarding them.odoo:19, and the MCP job now sources the module from much-GmbH/much-mcp-server@19.0 (was ivnvxd/odoo-apps@18.0) using the MCP_MODULE_PAT secret. The module's declared Python dependencies are installed into the Odoo image so mcp_server can install.mcp floor raised to 1.27: session_idle_timeout is passed to StreamableHTTPSessionManager, which does not accept it before 1.27.build_search_uri: unused public helper removed from uri_schema.Cache.invalidate_pattern: orphaned when the record cache was removed.ODOO_MCP_ALLOWED_HOSTS : comma-separated Host headers to accept for the streamable-http transport (DNS-rebinding protection). Needed when running behi
ODOO_MCP_ALLOWED_HOSTS: comma-separated Host headers to accept for the streamable-http transport (DNS-rebinding protection). Needed when running behind a reverse proxy that forwards an external host; unset preserves the prior default (no host validation) (#45, @Miriup).ODOO_MCP_SESSION_IDLE_TIMEOUT: seconds of inactivity before a streamable-http session is closed and its server-side state freed; unset preserves the prior behavior (sessions never expire).mcp 1.26.0 → 1.27.2 (streamable-http idle timeout, mid-request transport-close handling, pydantic 2.13 compat), pydantic 2.11 → 2.13, starlette 0.47 → 1.3, plus dev tooling (ruff, ty, pytest 9).ODOO_MCP_ALLOWED_HOSTS: comma-separated Host headers to accept for the streamable-http transport (DNS-rebinding protection). Needed when running behind a reverse proxy that forwards an external host; unset preserves the prior behavior (the SDK auto-enables protection for a loopback bind only) (#45, @Miriup).ODOO_MCP_SESSION_IDLE_TIMEOUT: seconds of inactivity before a streamable-http session is closed and its server-side state freed; unset preserves the prior behavior (sessions never expire).mcp 1.26.0 → 1.27.2 (streamable-http idle timeout, mid-request transport-close handling, pydantic 2.13 compat), pydantic 2.11 → 2.13, starlette 0.47 → 1.3, plus dev tooling (ruff, ty, pytest 9). Full suite green against the new stack.delete_record on records without a display name (e.g. mail.message ): the unlink succeeded but the result then failed schema validation ( deleted_name
delete_record on records without a display name (e.g. mail.message): the unlink succeeded but the result then failed schema validation (deleted_name received Odoo's False), so clients believed the delete had failed — now falls back to an ID label.list_models: System-model exclusion was a no-op (tautological domain) — results were dominated by ir.*/base.* models.True/False substrings (e.g. 'True North').search_records/get_record results with binary/datetime fields serialize cleanly instead of erroring.fields=[]: Treated as smart defaults instead of silently fetching ALL fields.list_resource_templates: YOLO mode reports "all models available" instead of total_models: 0.odoo://{model}/search skips binary/html fields like the record path; pagination hints now reference the search_records tool instead of emitting unroutable query-param URIs.digits metadata; unset names render Record {id} instead of False; long values truncated with a marker; x2many "View all" hints carry real record ids.{} placeholders in error messages.PermissionError/ConnectionError no longer misclassified as critical system errors (custom classes shadowed the builtins); access-control infrastructure failures report as connection errors, not "Access denied".ODOO_MCP_TRANSPORT/HOST/PORT/LOG_LEVEL set only in .env now take effect; invalid ODOO_MCP_PORT or ODOO_MCP_SLOW_OPERATION_THRESHOLD_MS no longer crash startup/import with raw tracebacks.SERVER_VERSION derives from the package version; a test pins it to pyproject.toml (v0.5.2 shipped reporting 0.5.1).ssl.SSLContext to amortize handshake cost across the per-proxy sockets introduced in 0.6.0.RequestOptimizer, the browse resource chain, and the unused error metrics/conversion API; ConnectionPool simplified to the proxy factory it actually was.main/dev (PRs cover feature branches; kills duplicate runs); Dependabot switched to the uv ecosystem so uv.lock gets update/security PRs; publish workflow verifies the built version against the release tag (manual dispatch requires typed version confirmation); Python 3.13 classifier added; MCP integration job skips Dependabot PRs — repository secrets aren't available to them, so the private-module checkout failed with "Input required and not supplied: token"; Dependabot version-update PRs now target dev directly (manual retargeting desynced Dependabot's base-branch metadata); codecov-action bumped to v7 (supersedes the stuck Dependabot PR).--help: documents ODOO_MCP_ENABLE_METHOD_CALLS.X-Odoo-Database); an env-var leak that wedged spawned transport servers under DEBUG logging fixed; vacuous if records: guards replaced with real fixtures and assertions; MCP test client read_resource returned "" (matched the wrong content type).aggregate_records tool : Server-side aggregation via Odoo's grouping methods ( #64 ). Dispatches to formatted_read_group on Odoo 19+ and falls back to
aggregate_records tool: Server-side aggregation via Odoo's grouping methods (#64). Dispatches to formatted_read_group on Odoo 19+ and falls back to read_group with response normalization on older versions.call_model_method tool (YOLO-only, opt-in via ODOO_MCP_ENABLE_METHOD_CALLS): generic XML-RPC execute_kw escape hatch for workflow actions not covered by CRUD.post_message tool : Post to an Odoo record's chatter via mail.thread.message_post() ( #50 ).
post_message tool: Post to an Odoo record's chatter via mail.thread.message_post() (#50).notifications/progress from search_records and list_models — strict MCP clients treated the post-response notification as a fatal protocol error and tore down the transport.CI : Skip MCP integration tests for fork PRs where repository secrets are unavailable
max_limit instead of resetting to default_limit (#57, @litnimax)ODOO_MCP_DEFAULT_LIMIT now applies when clients omit limit — previously hardcoded, bypassing the env var in the common case (#61)Lifespan hooks : Consolidated duplicated setup/teardown from run_stdio() and run_http() into a single _odoo_lifespan() async context manager passed to
run_stdio() and run_http() into a single _odoo_lifespan() async context manager passed to FastMCP — transport methods are now thin wrappersctx: Context for client-visible observability — sends info/warning/progress notifications to MCP clients in real timeGET /health HTTP route via FastMCP custom_route — returns connection status and version as JSON for load balancers and monitoringmodel parameters in resource URIsstatus, version, and connected only — removed url, database, error_metrics, recent_errors, and performance fieldsFalse values displayed as "Not set" instead of "No" in formatted output due to branch precedence bug in _format_simple_value()Completion(values=...), breaking MCP protocol contracttry blockDocumentation : --help , .env.example , and README now document all environment variables including ODOO_YOLO , ODOO_LOCALE , ODOO_MCP_MAX_SMART_FIELD
--help, .env.example, and README now document all environment variables including ODOO_YOLO, ODOO_LOCALE, ODOO_MCP_MAX_SMART_FIELDS, ODOO_MCP_LOG_JSON, and ODOO_MCP_LOG_FILE.gitignore, removed redundant httpx dependency, registered asyncio pytest marker--help, .env.example, and README with all current environment variables and test commands.gitignore, trim redundant dependencies, consolidate dev tooling configAuth matrix integration tests : Comprehensive test suite ( test_auth_matrix.py ) covering all authentication scenarios — Standard mode (S1–S7), YOLO r
test_auth_matrix.py) covering all authentication scenarios — Standard mode (S1–S7), YOLO read (Y1–Y6), and YOLO full (F1–F6) with config validation, connect/read, write cycles, access control, and restricted operationsuse_api_keys=True so API key validation is real; MCP integration test failures are no longer suppressed/mcp/xmlrpc/*, /mcp/models) returned 404 when multiple databases existed in. PostgreSQL. Added X-Odoo-Database header injection via custom XML-RPC transport and AccessController REST calls/xmlrpc/db for database listing regardless of modeOdooConnection.connect() resolves the target database before creating MCP proxies in standard mode, setting the X-Odoo-Database header for _test_connection() and all subsequent callsAccessController auth: Supports session-cookie authentication as fallback when no API key is configured — authenticates via /web/session/authenticate and retries once on session expiryODOO_DB — all connect, authenticate, and pass access control. Previously S1–S6 could fail with 404 on multi-DB servers/mcp/xmlrpc/*, /mcp/models, /mcp/health, etc.) returned 404 when multiple databases existed in PostgreSQL because Odoo couldn't determine which DB to route to. Now sends X-Odoo-Database header on all MCP requests (XML-RPC and REST)/xmlrpc/db endpoint instead of /mcp/xmlrpc/db, since the latter requires a DB context that isn't available yetCI : Reduce Python matrix to 3.10 + 3.13; add YOLO and MCP integration test jobs with Odoo 18 + PostgreSQL 17 Docker containers
integration → mcp, e2e → yolo, drop odoo_required; add markers to 30 previously-invisible tests; remove RUN_MCP_TESTS gateDocker support: Multi-stage Dockerfile with uv for fast builds, and GitHub Actions workflow for multi-platform images (amd64 + arm64) published to bot
uv for fast builds, and GitHub Actions workflow for multi-platform images (amd64 + arm64) published to both GHCR and Docker HubMulti-language support: New ODOO_LOCALE environment variable to get Odoo responses in any installed language (e.g. es_ES, fr_FR, de_DE). Validates loc
ODOO_LOCALE environment variable to get Odoo responses in any installed language (e.g. es_ES, fr_FR, de_DE). Validates locale at startup and falls back to default language if not installed. (#23)name field: create_record and update_record failed on models like mail.activity that lack a name field due to hardcoded field list in post-operation read-backcreate_record and update_record now generate /odoo/{model}/{id} URLs for Odoo 18+ instead of the legacy /web#id=... format (which is still used for Odoo ≤ 17)Structured output: All tools return typed Pydantic models with auto-generated JSON schemas for MCP clients (SearchResult, RecordResult, ModelsResult,
SearchResult, RecordResult, ModelsResult, CreateResult, UpdateResult, DeleteResult)readOnlyHint, destructiveHint, idempotentHint, and openWorldHint via MCP ToolAnnotationsaudience and priority via MCP Annotationstitle for better display in MCP clients>=1.9.4 to >=1.26.0,<2get_record structured output: Returns RecordResult with separate record and metadata fields instead of injecting _metadata into record datasearch_records domain/fields type hints from Union to Optional[Any] to avoid anyOf JSON schemas rejected by VertexAI/Google ADK (#27)read() to prevent returning stale field values (e.g. active) when records change in Odoo between calls (#28)ODOO_URL for server detection, deduplicated server checks, fixed async test handling, updated assertions for structured output types, halved suite runtimeToolError, ResourceError, ResourceNotFoundError, ResourcePermissionError) — use ValidationError, NotFoundError, PermissionError directly_setup_handlers() method from OdooMCPServerAuthentication bypass: Add missing @property on is_authenticated — was always truthy as a method reference, bypassing auth guards
@property on is_authenticated — was always truthy as a method reference, bypassing auth guardsYOLO Mode: Development mode for testing without MCP module installation
Direct Record URLs: Added url field to create_record and update_record responses for direct access to records in Odoo
url field to create_record and update_record responses for direct access to records in Odoocreate_record and update_record tool responses to return only essential fields (id, name, display_name) to minimize LLM context usageODOO_MCP_MAX_SMART_FIELDSResource Templates: Updated list_resource_templates tool to clarify that query parameters are not supported in FastMCP resources
list_resource_templates tool to clarify that query parameters are not supported in FastMCP resourcesWrite Operations: Enabled full CRUD functionality with create_record, update_record, and delete_record tools
create_record, update_record, and delete_record tools (#5)search_records tool to accept both JSON strings and Python-style domain strings, supporting various format variationsResource Discovery: Added list_resource_templates tool to provide resource URI template information
list_resource_templates tool to provide resource URI template informationHTTPS Connection: Fixed SSL/TLS support by using SafeTransport for HTTPS URLs instead of regular Transport
SafeTransport for HTTPS URLs instead of regular TransportMCP Server: Full Model Context Protocol implementation using FastMCP with stdio transport
odoo:// URI schema with 5 operations (record, search, browse, count, fields)search_records, get_record, list_models with smart field selectionYour coding agent can read these notes before it upgrades. Set up the MCP server →