Public HTML catalog (hosted 46-tool contract, 2026-09-18): https://erpipe.com/tools
Source of truth in code:
| Surface | Count | Where |
|---|---|---|
Core phase tools (PHASE1…PHASE4) |
27 | @erpipe/core (packages/core) |
| Prompts | 7 | CLOUD_V1_PROMPTS |
| MCP resources | 5 | CLOUD_V1_RESOURCES |
| Hosted workspace MCP | 46 tools | ALL_TOOLS in erpipe-cloud agent/constants.ts |
| Self-host example | 28 tools (27 phase + ping) |
@erpipe/worker-selfhost |
Python parity contracts still pin the original D14 23-tool names; TypeScript adds model_facts, read_attachment, render_report, and get_current_context on the core phase lists (CLOUD_V1_TOOL_COUNT = 27).
Hosted extras (not in OSS self-host): multi-instance + lifecycle + Odoo event pull + HITL status + per-user memory + named verbs + ping on the cloud agent:
27 phase
+ ping
+ list_instances, search_across_instances, aggregate_across_instances, accounting_health_across_instances
+ create_instance, update_instance, rotate_instance_credentials, update_instance_cloudflare_access, delete_instance
+ preflight_odoo_capability, execute_approved_odoo_capability
+ get_write_approval_status
+ list_odoo_events, ack_odoo_event
+ remember_episode, recall_memory, forget_memory
+ execute_named_verb
= 46
Every Odoo-bound hosted tool requires an explicit instance key (list_instances to discover). Lifecycle tools require OAuth scope erpipe:write.
| Tool | Purpose |
|---|---|
search_records |
Query or domain search. Omit fields. Default limit 10, max 50. Prefer native JSON arrays for domain, e.g. [["id","in",[101,102]]]. Valid JSON strings and {conditions:[{field,operator,value}]} remain supported. Malformed JSON returns VALIDATION_ERROR, retryable:false and a correction hint before any search RPC; correct the filter before retrying. Empty query+domain is rejected (EMPTY_SEARCH). |
model_facts |
Model intent snapshot (search / write / domain / overview) |
read_record |
Read records by id |
build_domain |
Build Odoo domain from structured input |
list_models |
List accessible models |
get_model_fields |
Field metadata for a model |
health_check |
Connection + surface sanity |
| Tool | Purpose |
|---|---|
aggregate_records |
Aggregations over a domain |
search_employee |
HR employee search helpers |
search_holidays |
Leave / holiday search helpers |
get_odoo_profile |
Instance profile snapshot (includes installed modules by default) |
get_current_context |
Timezone, active company, and allowed companies only — no module list. Uses context_get, then a user-record fallback when Odoo omits company keys (Odoo 19+). |
schema_catalog |
Schema browsing helpers (fan-out + cache metrics on hosted) |
diagnose_odoo_call |
Diagnose a failed model call |
diagnose_access |
Access / ACL diagnosis |
inspect_model_relationships |
Relational graph for a model |
read_attachment |
Read ir.attachment payload (bounded) |
| Tool | Purpose |
|---|---|
preview_write |
Preview create/write/unlink + approval token |
validate_write |
Validate values against field policy |
execute_approved_write |
Execute only with a valid approval token (may return outcome=pending while HITL waits) |
chatter_post |
Post a chatter message |
execute_method |
Non-CRUD method only. Not search_read/search_count/read (use search_records/read_record) and not create/write/unlink (use preview_write). |
Writes require host policy. Prefer preview → validate → execute. On hosted product: connection writes_enabled defaults OFF; owner HITL / write_mode and journal gates still apply. When execute_approved_write returns HITL_APPROVAL_REQUIRED, that is a short wait (not failure, not a chat end): keep the approval object, tell the owner to Approve (typically ~30 seconds via dashboard / push / email), sleep 5–10s then poll get_write_approval_status (or retry execute with the same approval). After ~30–45s if still pending, tell the user it remains in Approvals.
Hosted policy refuses unlink before owner approval (UNLINK_DENIED). Core and
the self-host example can execute a validated deletion when their write policy
permits it; enabling hosted Writes does not enable deletion.
| Tool | Purpose |
|---|---|
generate_json2_payload |
Build JSON-2 style payloads; set target_version: "20.0" for the Odoo 20 read_group signature |
upgrade_risk_report |
Upgrade risk analysis report |
fit_gap_report |
Fit/gap workshop report |
business_pack_report |
Business pack summary report |
render_report |
Download a bounded PDF through the optional matching ERPipe Bridge; document permissions remain in force |
| Tool | Purpose |
|---|---|
ping |
Lightweight liveness / agent identity |
| Tool | Purpose |
|---|---|
get_write_approval_status |
Poll a write approval by approval_request_id while waiting for owner Approve (sleep 5–10s between polls; cap ~30–45s) |
| Tool | Purpose |
|---|---|
execute_named_verb |
Run a dashboard-named execute_method alias. Reuses the method allowlist. Not HITL. Server actions are forbidden. |
| Tool | Purpose |
|---|---|
list_instances |
List workspace Odoo instances + status. Best-effort tz / companies (context_status); never fails the list. |
search_across_instances |
Fan-out search (≤10 targets, concurrency 4). Same empty-filter reject and per-instance cap 50 as search_records. |
aggregate_across_instances |
Fan-out aggregates with attribution |
accounting_health_across_instances |
Receivables-style health across instances |
There are no cross-instance write tools.
| Tool | Purpose |
|---|---|
create_instance |
Create instance (HTTPS preflight + auth probe) |
update_instance |
Label / pause-resume (enabled) |
rotate_instance_credentials |
Rotate stored credentials |
update_instance_cloudflare_access |
Owner-only set/clear of a service-token pair. Set accepts an optional new Odoo password to recover both credentials together; otherwise preflights with the saved credential. Clear requires confirm: true and no credentials. Returns configured status only. |
delete_instance |
Delete instance + encrypted secrets |
Governance toggles (writes_enabled, write_mode, field policy, model allowlist, named verbs) stay dashboard-only.
create_instance also accepts optional access_client_id and access_client_secret together; supplying the pair requires workspace owner access. update_instance_cloudflare_access takes instance, action: "set" with both credentials, or action: "clear" with confirm: true and no credentials. Both operations require erpipe:write. Failed verification preserves the old pair. Paused instances can be configured; revoked instances cannot. No saved Client ID or secret is returned.
When both the Odoo credential and Access token have expired or changed, set may include a nonempty password containing the new Odoo password/API key. ERPipe verifies both new credentials together and stores them atomically on the existing connection. Omit password to keep the stored Odoo credential; clear rejects this field. Use protected runtime inputs, never credentials pasted into a conversation.
| Tool | Purpose |
|---|---|
preflight_odoo_capability |
Discover and preflight a capability advertised by the optional companion addon without changing Odoo. |
execute_approved_odoo_capability |
Execute after owner approval and a fresh capability fingerprint check. |
| Tool | Purpose |
|---|---|
recall_memory |
Look up similar past cases when the problem looks repeatable. Do not wait for the user to ask. Untrusted; verify live records. |
remember_episode |
After a verified reusable fix, save a short lesson with source_event_ids. Never raw Odoo, credentials, trivia, or one-off lookups. |
forget_memory |
Drop one saved memory after the user asks, or confirms it is wrong. |
| Tool | Purpose |
|---|---|
list_odoo_events |
List inbound webhook events for a connection |
ack_odoo_event |
Acknowledge a handled event |
Ingress: POST /hooks/{slug} with the connection webhook secret.
| Prompt | Purpose |
|---|---|
diagnose_failed_odoo_call |
Guided diagnosis of a failed call |
fit_gap_workshop |
Structure fit/gap from requirements |
json2_migration_plan |
Plan XML-RPC/JSON-RPC → JSON-2 migration |
safe_write_review |
Review create/write/unlink before execute |
invoice_approval_chain |
Draft invoice find → validate → gated post |
po_to_receipt |
PO / receipt / bill three-way match |
customer_onboarding |
Dedup + gated customer create |
| Name | URI | Purpose |
|---|---|---|
odoo_models |
odoo://models |
List available models |
odoo_model |
odoo://model/{model_name} |
Model + fields |
odoo_record |
odoo://record/{model_name}/{record_id} |
One record |
odoo_record_field |
odoo://record/{model_name}/{record_id}/{field_name} |
One binary field (size-capped; omitted when over the cap) |
odoo_search |
odoo://search/{model_name}/{domain} |
Search with JSON domain |
| Transport | Typical auth | Package |
|---|---|---|
| XML-RPC | username + password / API key as password | @erpipe/odoo-xmlrpc + XmlRpcTransport |
| JSON-2 | API key bearer | Json2Transport |
URL policy rejects unsafe origins (SSRF guard) before calling Odoo. Hosted product rechecks public-unicast DNS at create, session open, and per-call boundaries.
Both transports accept cfAccess: { clientId, clientSecret }. They construct only CF-Access-Client-Id and CF-Access-Client-Secret, including on authentication and version requests, and never follow redirects. The Odoo credential remains separate. Self-host uses CF_ACCESS_CLIENT_ID and CF_ACCESS_CLIENT_SECRET together. Configure the token in a Service Auth policy for the Odoo hostname; older hexadecimal and newer cfast_ secrets are both supported.
JSON-2 version discovery uses the public /web/webclient/version_info JSON-RPC route and validates result.server_version. A blocked or invalid probe does not fabricate a version.
The October 1, 2026 candidate was tested on pinned Community 20.0 source using Python 3.12 and PostgreSQL 17. It is a local qualification, not a production deployment or a guarantee for Enterprise, Odoo.sh, Odoo Online or custom addons.
- JSON-2 uses a dedicated API key with RPC scope. Record methods take
args: []andkwargs: { ids: [1, 2], ...namedParameters }; model methods take named parameters withoutids. XML-RPC record methods retainargs: [[1, 2]]. diagnose_accessreadsir.accesson 20 and the legacy access/rule models on older releases. Restricted users may query their records without permission to list security metadata;access_metadata.availablereports that boundary.search_holidaysuses inclusive requested dates on 20, including day, half-day and hourly leave. Dates are not shifted by the reader's timezone.read_attachmentkeeps its base64datasoutput while reading Odoo 20'srawbinary value. Binary resources also retain their bounded output. Attachment field policies treatdatasandrawas the same protected content: denying either blocks both. Restrictive allow rules must explicitly permit both names; allowing only one refuses content. This also protects raw reads and writes, and metadata-only reads retain their separate field rules.render_reportrequires the 20.0 Bridge and ordinary model, record, company and report-group permissions. Rendering runs as the RPC user in a fresh read-only transaction, bypasses stored print attachments and rolls back. Reports that write while printing are refused; field-restricted connections cannot render opaque PDFs through either the report tool or a raw method. This guard covers the audited native reports, not arbitrary custom Python that opens another cursor or causes external effects.- Legacy XML-RPC
action_archive,action_unarchiveandtoggle_activeare refused before sending because their nativeNoneresponse can fail after changing the record. Use a gatedactivewrite instead. A previous ambiguous call must be reconciled before any new write.
The 16–19 regression matrix covers native reads, schema, aggregation, resources, attachments, HR and gated writes/business methods. Companion-backed PDF is qualified on the 20.0 series; older Bridge series need their own PDF port.