You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Fixed
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.
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 by ir.*/base.* models.
Binary fields: 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.
Search resource: 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.
Formatters: Float precision honors digits metadata; unset names render Record {id} instead of False; 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/ConnectionError no 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_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.
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_VERSION derives 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.SSLContext to 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; ConnectionPool simplified to the proxy factory it actually was.
CI: Push trigger limited to 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.
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; vacuous if records: guards replaced with real fixtures and assertions; MCP test client read_resource returned "" (matched the wrong content type).