This release includes breaking changes for platform teams planning a safe upgrade.
✓ No known CVEs patched in this version
Topics
+3 more
Affected surfaces
Summary
AI summaryFixed multiple bugs including schema validation on deletes, XML‑RPC timeouts, keepalive recovery, session persistence, model exclusion in list_models, domain parsing corruption, binary field serialization, default fields handling, resource template reporting, search pagination hints, formatter precision, cache enforcement, error sanitization, error classification, config precedence, auth fallback, and logging redaction.
Changes in this release
| Type | Severity | Summary | CVE |
|---|---|---|---|
| Security | Medium |
Startup warning emitted when binding HTTP transport to non‑loopback host, highlighting lack of built‑in authentication. Startup warning emitted when binding HTTP transport to non‑loopback host, highlighting lack of built‑in authentication. Source: granite4.1:30b@2026-06-11-audit Confidence: low |
— |
| Security | Medium |
Sensitive keys (passwords, API keys, tokens) are redacted in debug‑log payloads. Sensitive keys (passwords, API keys, tokens) are redacted in debug‑log payloads. Source: granite4.1:30b@2026-06-11-audit Confidence: low |
— |
| Performance | Low |
HTTPS transports share a single `ssl.SSLContext` to amortize handshake cost across per‑proxy sockets. HTTPS transports share a single `ssl.SSLContext` to amortize handshake cost across per‑proxy sockets. Source: granite4.1:30b@2026-06-11-audit Confidence: low |
— |
| Bugfix | Medium |
Socket timeouts now apply to XML-RPC connections, causing hung servers to fail fast. Socket timeouts now apply to XML-RPC connections, causing hung servers to fail fast. Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Bugfix | Medium |
Half‑open keepalive recovery retries once on fresh connection for idempotent calls only. Half‑open keepalive recovery retries once on fresh connection for idempotent calls only. Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Bugfix | Medium |
`streamable-http` sessions persist across HTTP sessions, preventing “Not authenticated” errors after the first call. `streamable-http` sessions persist across HTTP sessions, preventing “Not authenticated” errors after the first call. Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Bugfix | Medium |
Blocking XML‑RPC/HTTP calls are offloaded to worker threads, keeping the event loop responsive. Blocking XML‑RPC/HTTP calls are offloaded to worker threads, keeping the event loop responsive. Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Bugfix | Medium |
`list_models` YOLO mode now correctly excludes system models, avoiding domination by `ir.*`/`base.*` entries. `list_models` YOLO mode now correctly excludes system models, avoiding domination by `ir.*`/`base.*` entries. Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Bugfix | Medium |
String domains no longer corrupt values containing substrings like `True` or `False`. String domains no longer corrupt values containing substrings like `True` or `False`. Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Bugfix | Medium |
Search resource skips binary/html fields and references `search_records` for pagination hints. Search resource skips binary/html fields and references `search_records` for pagination hints. Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Bugfix | Medium |
Float formatters honor `digits` metadata; unset names render `Record {id}` instead of `False`. Float formatters honor `digits` metadata; unset names render `Record {id}` instead of `False`. Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Bugfix | Medium |
Memory cache budget is now enforced, preventing unrelated evictions when replacing a key. Memory cache budget is now enforced, preventing unrelated evictions when replacing a key. Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Bugfix | Medium |
Error sanitizer trims server tracebacks to the final error message, redacting sensitive details. Error sanitizer trims server tracebacks to the final error message, redacting sensitive details. Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Bugfix | Low |
`delete_record` falls back to ID label when display name is missing, preventing schema validation failure. `delete_record` falls back to ID label when display name is missing, preventing schema validation failure. Source: granite4.1:30b@2026-06-11-audit Confidence: low |
— |
| Bugfix | Low |
Binary/datetime fields in `search_records`/`get_record` results now serialize cleanly without errors. Binary/datetime fields in `search_records`/`get_record` results now serialize cleanly without errors. Source: granite4.1:30b@2026-06-11-audit Confidence: low |
— |
| Bugfix | Low |
`fields=[]` is treated as smart defaults instead of silently fetching all fields. `fields=[]` is treated as smart defaults instead of silently fetching all fields. Source: granite4.1:30b@2026-06-11-audit Confidence: low |
— |
| Bugfix | Low |
`list_resource_templates` in YOLO mode reports "all models available" rather than `total_models: 0`. `list_resource_templates` in YOLO mode reports "all models available" rather than `total_models: 0`. Source: granite4.1:30b@2026-06-11-audit Confidence: low |
— |
| Bugfix | Low |
Auth fallback after rejected API key uses session auth for permission checks instead of resending the dead key. Auth fallback after rejected API key uses session auth for permission checks instead of resending the dead key. Source: granite4.1:30b@2026-06-11-audit Confidence: low |
— |
| Bugfix | Low |
`SERVER_VERSION` derives from package version; test now pins it to `pyproject.toml`. `SERVER_VERSION` derives from package version; test now pins it to `pyproject.toml`. Source: granite4.1:30b@2026-06-11-audit Confidence: low |
— |
| Refactor | Low |
Dead code (~550 lines) removed, including unused caches, `RequestOptimizer`, browse resource chain, and error metrics API. Dead code (~550 lines) removed, including unused caches, `RequestOptimizer`, browse resource chain, and error metrics API. Source: granite4.1:30b@2026-06-11-audit Confidence: low |
— |
| Refactor | Low |
`ConnectionPool` simplified to the proxy factory it actually implements. `ConnectionPool` simplified to the proxy factory it actually implements. Source: granite4.1:30b@2026-06-11-audit Confidence: low |
— |
Full changelog
Fixed
delete_recordon records without a display name (e.g.mail.message): the unlink succeeded but the result then failed schema validation (deleted_namereceived Odoo'sFalse), so clients believed the delete had failed — now falls back to an ID label.- Timeouts: Socket timeouts now actually apply to XML-RPC connections — a hung Odoo server fails fast instead of blocking forever.
- Half-open keepalive recovery (#68): a timeout on a reused keepalive socket (LB dropped the upstream without FIN/RST) transparently retries once on a fresh connection — idempotent calls only: read-only model methods plus login/version/db-list probes (a timed-out write is never re-sent, it could be double-executed by a slow server); timeouts on fresh connections are NOT retried.
- streamable-http sessions (#70): the Odoo connection and registered handlers now persist across HTTP sessions — previously every tool call after the first failed with "Not authenticated with Odoo" because session teardown disconnected Odoo. stdio still cleans up on exit.
- Event loop: Blocking XML-RPC/HTTP calls offloaded to worker threads; concurrent tool calls and pings stay responsive during slow operations.
- YOLO
list_models: System-model exclusion was a no-op (tautological domain) — results were dominated byir.*/base.*models. - Domain parsing: String domains no longer corrupt values containing
True/Falsesubstrings (e.g.'True North'). - Binary fields:
search_records/get_recordresults 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 oftotal_models: 0.- Search resource:
odoo://{model}/searchskips binary/html fields like the record path; pagination hints now reference thesearch_recordstool instead of emitting unroutable query-param URIs. - Formatters: Float precision honors
digitsmetadata; unset names renderRecord {id}instead ofFalse; long values truncated with a marker; x2many "View all" hints carry real record ids. - Cache: Memory budget actually enforced (looped eviction); replacing a key no longer evicts an unrelated entry.
- Error sanitizer: Server tracebacks reduced to the final error message (multi-line business errors kept intact) — no more leaked source lines, SQL constraint names, or row values; no more literal
{}placeholders in error messages. - Error classification: Builtin
PermissionError/ConnectionErrorno longer misclassified as critical system errors (custom classes shadowed the builtins); access-control infrastructure failures report as connection errors, not "Access denied". - Config precedence:
ODOO_MCP_TRANSPORT/HOST/PORT/LOG_LEVELset only in.envnow take effect; invalidODOO_MCP_PORTorODOO_MCP_SLOW_OPERATION_THRESHOLD_MSno longer crash startup/import with raw tracebacks. - Auth fallback: After a rejected API key falls back to password auth, permission checks use session auth instead of resending the dead key (which 401'd every tool call).
- Logging: Sensitive keys (passwords, API keys, tokens) redacted in debug-log write payloads; database names moved to DEBUG level.
- Version:
SERVER_VERSIONderives from the package version; a test pins it to pyproject.toml (v0.5.2 shipped reporting 0.5.1).
Changed
- TLS: HTTPS transports share one
ssl.SSLContextto amortize handshake cost across the per-proxy sockets introduced in 0.6.0. - HTTP transport: Loud startup warning when binding a non-loopback host (the transport has no built-in authentication); README security notes added.
- Dead code removed (~550 lines): never-wired record/permission caches,
RequestOptimizer, the browse resource chain, and the unused error metrics/conversion API;ConnectionPoolsimplified to the proxy factory it actually was. - CI: Push trigger limited to
main/dev(PRs cover feature branches; kills duplicate runs); Dependabot switched to theuvecosystem souv.lockgets 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 targetdevdirectly (manual retargeting desynced Dependabot's base-branch metadata); codecov-action bumped to v7 (supersedes the stuck Dependabot PR). --help: documentsODOO_MCP_ENABLE_METHOD_CALLS.- Tests: integration suite hardened — MCP-module detection works on multi-database instances (health probe sends
X-Odoo-Database); an env-var leak that wedged spawned transport servers under DEBUG logging fixed; vacuousif records:guards replaced with real fixtures and assertions; MCP test clientread_resourcereturned""(matched the wrong content type).
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About ivnvxd/mcp-server-odoo
Connect AI assistants to Odoo ERP systems for business data access, record management, and workflow automation.
Beta — feedback welcome: [email protected]