My Awesome MCP Server
Provides governed MCP access to Odoo ERP, allowing AI agents to read, write, and delete records across allowed Odoo models such as res.partner and sale.order, with policy checks, risk scoring, throttling/blocking, and tamper-evident audit logging. It also supports controlled access to execute_method with strict allowlists and refuses ORM write primitives and credential fields.
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., "@My Awesome MCP Servershow the last 10 sale orders for Acme and their totals"
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-governed
An MCP server for Odoo where every tool call is policy-checked, risk-scored, and written to a tamper-evident audit log before it is allowed to touch the ERP.
Giving an AI agent write access to the system that holds your invoices, customers and stock is a straightforward thing to build and a hard thing to justify. The usual MCP server hands the model a set of tools and hopes. This one assumes the agent will eventually do something wrong — because its instructions changed, because it was prompt-injected, because its token leaked — and is built so that when that happens the damage is bounded and the evidence survives.
Status: v0.1 — all 18 tools, stdio and HTTP transports, admin panel and Docker deployment work end to end against Odoo 19. 215 tests.
How a call is handled
Tools cannot reach Odoo except through the governance engine. Every call passes three stages, in this order:
ToolCall
│
├─ 1. Policy ──────── permitted at all? → deny, and record the denial
│ (allowlists, per-operation
│ gates, volume ceiling)
│
├─ 2. Detection ───── what does this look like → signals, with weights
│ in context? (8 behavioural detectors)
│
├─ 3. Risk ────────── signals + this actor's → allow / flag / throttle / deny
│ decayed recent history
│
└─ 4. Audit ───────── hash-chained record of what was asked,
what was decided, and what happenedRisk can narrow what policy permits. It can never widen it. A policy denial is final regardless of how low the risk score is, and no threshold setting can make a forbidden call allowed. If that relationship were bidirectional, tuning a number would be a way to grant permissions, and the allowlist would stop being the authority on what is allowed.
1. Policy — the preventive layer
Default deny. An empty ALLOWED_MODELS permits nothing, so every widening is
something a human typed.
Read, write and delete are separate gates. Being allowed to read res.partner
does not imply writing it; being allowed to write does not imply deleting.
Most agent integrations need to read broadly and write narrowly, and a single
"allowed models" list cannot express that.
execute_method — arbitrary method on arbitrary model, the widest surface in
any Odoo API wrapper — is off by default, takes a method allowlist, and
refuses the ORM write primitives outright. Calling unlink through it would
otherwise delete records while never touching the delete gate.
Credential fields (password, api_key, totp_secret, …) are refused on
every model, always.
2. Detection — the behavioural layer
Eight detectors, each looking for something a legitimate integration does once and almost never does repeatedly:
Detector | What it catches |
| Repeated denied calls, especially spread across models — the shape of someone mapping what is reachable. Weighted highest: it has no benign high-volume explanation. |
| Several deletes in quick succession. A loop that lost its exit condition, or an injected instruction being carried out. |
| Large cumulative read volume. Each call is under the per-call cap and perfectly legal; only the sum reveals exfiltration. |
| One call writing or deleting a batch far above routine, but below the hard cap. |
| Volume spikes, both absolute and relative to this actor's own baseline. The relative check is what catches a quiet actor suddenly getting busy. |
| A run of failures — an agent guessing at schema it does not have. |
| An established actor touching a model it never has before. |
| Activity outside the configured window. Weighted low on purpose. |
Detectors describe; they never decide. Each emits weighted signals and the engine applies the thresholds. A heuristic that can refuse a call on its own is a heuristic that can take down production by itself — and relatedly, a detector that crashes is dropped with a note in the log rather than failing the call, because a bug in statistical code should not be an outage.
Each detector's docstring states how it produces false positives. A detector whose failure mode you cannot describe is one you will end up ignoring in practice. The defaults are tuned to start noisy and be tightened against real traffic, rather than start silent and hope.
3. Risk — turning signals into a decision
Signal weights are summed, plus the actor's recent signals discounted by an exponential half-life (15 minutes by default). Patterns worth catching are built from individually unremarkable calls — the third delete is only alarming because of the first two — so history has to count. It has to decay just as much, or an actor that tripped a detector once is permanently degraded and the operator switches governance off.
Scoring is per-actor: one misbehaving agent does not raise another's risk.
Score | Decision |
below throttle |
|
≥ |
|
≥ |
|
4. Audit — the forensic layer
An append-only SQLite log where each event stores the hash of the one before it, so every hash depends on the entire history.
What the log deliberately does not contain: argument values. Odoo records hold personal and commercial data, and an audit log is the wrong place to accumulate a second copy of it. Stored instead are field names (useful, not sensitive) and a SHA-256 digest of the arguments, which lets you prove two calls were identical without being able to read either.
Denials are recorded, not dropped. A refusal is evidence, and the probing detector is built entirely out of refusals.
Threat model, stated plainly
This is where most "tamper-proof log" claims quietly overreach.
Detected. Editing, deleting, reordering or splicing in a past event. The chain breaks at a named sequence number. Repairing the hash of the row you edited does not help — the next event still commits to the old one, so the break just moves forward one.
Detected only with
AUDIT_HMAC_KEYset. An attacker who can read and write the database file and knows the record format can, with a plain SHA-256 chain, recompute every hash from the altered point forward and produce a chain that verifies. An HMAC chain closes this off for anyone who has the file but not the key.Not detected by this module alone. An attacker who owns the running process has the key and can rewrite everything, head included. No single-host design fixes that. The answer is anchoring: periodically copy the head hash somewhere the server cannot reach back into.
AuditLog.anchorexists for this and it is a two-line cron job, not a product.Also not detected. Anything that bypasses this server and talks to Odoo directly. Governance here covers the MCP surface; claiming more would be dishonest.
Known limitation. The audit event is written once, after the outcome is known. If the process dies mid-call, the attempt leaves no trace. Recording intent before and outcome after would close that gap at the cost of doubling the log; for now the single event wins, and startup self-check verifies the log is writable so the common failure is caught before it matters.
Related MCP server: OdooSurface MCP
Running it
Claude Desktop (stdio)
{
"mcpServers": {
"odoo": {
"command": "odoo-mcp-governed",
"args": ["stdio"],
"env": {
"ODOO_URL": "https://erp.example.com",
"ODOO_DB": "mydb",
"ODOO_USERNAME": "bot@example.com",
"ODOO_API_KEY": "…",
"ODOO_ENVIRONMENT": "staging",
"ALLOWED_MODELS": "res.partner,sale.order",
"AUDIT_HMAC_KEY": "…",
"DATA_DIR": "/home/me/.local/share/odoo-mcp-governed"
}
}
}
}HTTP, with the admin panel
cp .env.example .env && chmod 600 .env # fill in JWT_SECRET, ADMIN_PASSWORD, AUDIT_HMAC_KEY
mkdir -m 700 -p data
docker compose up -d # listens on 127.0.0.1:8020
odoo-mcp-governed token --actor claude # one token per agentThe MCP endpoint is POST /mcp (POST / also works). The admin panel at
/admin manages Odoo profiles (switching is instant, no restart), edits the
policy live — tools on/off, model lists, execute_method — shows the token,
and has an audit view with filters and a per-actor risk table. It contains no
JavaScript at all and is served under a CSP that forbids scripts.
Administrative actions — logins, profile switches, policy changes — go into the same hash chain as agent calls. They are recorded but never risk-scored: an administrator switching a tool off at 03:00 during an incident must not be locked out by off-hours signals.
Command line
odoo-mcp-governed audit verify # exit 1 if the chain is broken — cron it
odoo-mcp-governed audit anchor # append the head hash to data/anchors.logProduction safeguards
Pointing a development setup at the live ERP is the most ordinary way to lose data, so there are two independent guards:
Unlabelled means production, and production is read-only. Every profile carries an
environment. A profile without one — typically copied in from another tool — is treated asproduction, where creating, writing, deleting andexecute_methodare refused at the policy layer. Writing to a test database requires labelling itdevelopmentorstaging; writing to production requires tickingallow writeson that one profile, which the panel then shows in red.PROTECTED_HOSTSlists hosts the server must never contact. A profile pointing at one (or any subdomain) cannot be saved or activated, and if the active profile turns out to point at one, the server starts with no profile rather than connect.
Hardening carried over from day one
/docs and the OpenAPI schema are not served. Bodies are capped by counting
bytes, so chunked uploads are capped too. 500s carry no detail. Security
headers on every response, HSTS in HTTPS mode. Mcp-Session-Id is validated,
never reflected. Rate limits per client IP on /mcp, /health and login,
with X-Forwarded-For honoured only from configured proxies. Credentials and
the audit database are written 0600 from the first byte, atomically. TLS to
Odoo is verified by default. The container runs as a non-root UID on a
read-only root filesystem with all capabilities dropped, no-new-privileges,
memory/CPU/PID limits and its own network, publishing on 127.0.0.1 only. For
publishing beyond localhost, deploy/firewall-docker.sh restricts the port
through the DOCKER-USER chain, since Docker bypasses UFW.
Full mapping, item by item: docs/SECURITY_BASELINE.md.
Tests
The suite has 215 tests. The ones that matter are not the "it records events" kind — they are the adversarial ones:
python -m pytest tests/ -qTamper-evidence (
test_audit_chain.py) reaches past the public API into the SQLite file to simulate an attacker with direct database access, because going through the public API would prove nothing — it has no way to modify a past event. Modification, deletion, hash repair, forged insertion and the unkeyed-vs-HMAC distinction are each asserted to be caught, at the right sequence number.No permission leakage (
test_policy.py) asserts read does not imply write, write does not imply delete, and thatunlinkandwritecannot be laundered throughexecute_methodeven when explicitly allowlisted.Signal, not noise (
test_detectors.py) every detector is tested for silence on ordinary traffic as well as for firing on its pattern. One test runs a realistic workload through all eight and asserts zero signals — if that fails, every alert the system raises is noise.The core invariant (
test_engine.py) risk cannot widen policy; a crashing detector does not fail the call or inflate the score; actors are scored independently; a delete burst that starts permitted ends refused.Concurrency is not a bypass (
test_concurrency.py) ten deletes fired in parallel are judged as ten, not as ten first deletes each seeing an empty history. The test was checked by removing the per-actor lock: without it, all ten get through.Never reaches Odoo (
test_tools.py) tools run against a fake Odoo that records every request, so refusals are asserted as "Odoo saw nothing", not merely "an error came back".Production stays read-only (
test_production_guard.py) and protected hosts cannot be activated, whatever the profile file says.HTTP hardening (
test_http.py)alg: none, expired, wrong-issuer and expiry-less tokens; CSRF; chunked oversized bodies; secrets never in HTML; markup injected through audit data rendered inert.
The key security tests were mutation-checked — CSRF, the chunked body limit, secrets kept out of HTML, the domain password oracle and the per-actor lock: the protection was removed by hand and the test confirmed to fail, so that a passing test means something.
Tests drive a fake clock rather than real time, since every detector is a statement about when things happened.
Configuration
See .env.example. There are no insecure defaults: the server
refuses to start in HTTP mode without ADMIN_PASSWORD and JWT_SECRET, TLS
verification toward Odoo is on, and the model allowlist is empty.
Development
python -m venv .venv && . .venv/bin/activate
pip install -e ".[http,dev]"
pytest -q
ruff check src testsA disposable Odoo 19 CE instance for testing (port 65010):
cd test-env && docker compose up -d
docker compose exec -T web odoo -d govtest -i base \
--stop-after-init --db_host=db --db_user=odoo --db_password=odooRoadmap
Governance core — policy, detectors, risk scoring, hash-chained audit
Odoo client (JSON-RPC, real timeouts) and multi-profile hot swap
The 18 MCP tools, all routed through the engine
stdio transport (Claude Desktop) and HTTP transport (JWT)
Admin panel: profiles, live policy, tokens, audit log with risk view
Production safeguards: read-only by default, protected hosts
Hardened Docker image and compose file
Two-phase audit (intent before the call, outcome after)
Per-token revocation list (today: rotate
JWT_SECRETto revoke all)Signed off-box anchoring (today:
audit anchorwrites a file to ship)
Provenance
Written from scratch and MIT licensed. No code was copied from any existing
Odoo MCP server. The implementation follows the published Odoo external API
and the Model Context Protocol specification; the feature set and the
security baseline come from the author's own experience running and hardening
an internal Odoo MCP deployment, documented in
docs/SECURITY_BASELINE.md.
License
MIT — see LICENSE.
Available Tools
18 toolscache_statsCRead-only
Metadata cache statistics.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered by structured data. The description adds nothing beyond that: it does not say whether the call is cheap, whether it reflects live or cached state, or what kind of counts are reported. With annotations in place the bar is lower, but no added behavioral context is present.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is a single four-word fragment with no waste, but it is under-specified rather than genuinely concise. There is no front-loaded verb or scope statement to anchor the reader.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description carries the burden of conveying what the statistics contain. For a diagnostic tool with no annotations describing its return shape, 'statistics' is too thin to tell an agent what it will receive or whether the result is worth requesting.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, which is the baseline-4 case per the rubric. The description correctly implies no input is needed, and there is no parameter surface to misdescribe.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The fragment 'Metadata cache statistics' names a resource (the metadata cache) and the kind of data returned (statistics), but supplies no verb and does not specify what 'statistics' means. It is distinguishable from write siblings like create_record, but not clearly distinguished from the nearest sibling, server_status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no statement of when to call this tool, when not to, or how it relates to alternatives such as server_status or model_info. The agent is left to infer that this is a diagnostic/introspection call.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
call_onchangeARead-only
Simulate a form change and return the values Odoo would compute. Saves nothing.
| Name | Required | Description | Default |
|---|---|---|---|
| model | Yes | Technical model name, e.g. 'res.partner' | |
| values | Yes | Current form values | |
| field_names | No | Fields that changed (default: keys of values) | |
| fields_spec | No | Optional onchange spec |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare readOnlyHint=true and destructiveHint=false, and 'Saves nothing' reinforces that safety profile rather than contradicting it. However, the description adds little beyond the annotations: it does not describe error behavior, how the simulated values are structured, or any server-side effects. With annotations carrying the safety burden, this lands at a solid 3.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with zero filler; the core behavior is front-loaded and the no-persistence guarantee follows immediately. Every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-parameter tool with a nested object and no output schema, the description usefully signals what comes back ('the values Odoo would compute') and that nothing is written. The remaining gap is that the shape of the returned computed values is left unspecified, but the essential call-correctness information is present.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, with all four parameters (including the nested 'values' and 'fields_spec') documented in the schema itself. The description adds no syntax, format, or default information beyond what the schema already states, so the baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb and resource: 'Simulate a form change and return the values Odoo would compute.' An agent can immediately tell this is a dry-run computation, not a persistence operation. It does not explicitly name or contrast a sibling such as update_record, which keeps it from a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Simulate' plus 'Saves nothing' conveys a clear usage context: call this when you want computed onchange values without mutating data, making it the safe preview counterpart to update_record/create_record. There is no explicit statement of when NOT to use it or a named alternative, so it stops short of 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_recordC
Create a record.
| Name | Required | Description | Default |
|---|---|---|---|
| model | Yes | Technical model name, e.g. 'res.partner' | |
| values | Yes | Field values |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false and destructiveHint=false, which is consistent with 'Create', so there is no contradiction. However, the description adds nothing beyond them: no mention of required permissions, validation/constraint failures, side effects (e.g. computed fields, mail/activity triggers), or that it mutates the database.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One sentence with zero waste, but that brevity comes at the cost of under-specification rather than deliberate concision. Nothing is padded, but nothing is front-loaded that an agent can act on either.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a required two-parameter mutation with a nested free-form 'values' object and no output schema, the agent still lacks essentials: how field values should be encoded (scalar vs relation commands), which model names are valid, and what is returned on success. The description does not fill any of these gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% (both 'model' and 'values' are documented in the schema), so the baseline is 3. The description provides no additional meaning, and notably says nothing about the shape of the free-form 'values' object beyond the schema's 'Field values'.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The sentence does state a specific verb (Create) and resource (record), but it is effectively a restatement of the tool name create_record and adds no distinguishing detail over siblings like update_record or get_record. It is not misleading, but an agent learns nothing beyond the name.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to use this instead of update_record, get_record, or execute_method, and no prerequisites or context of use are stated. The agent must infer the routing entirely from the tool name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_recordBDestructive
Delete one or more records. Irreversible.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | No | Several record ids | |
| model | Yes | Technical model name, e.g. 'res.partner' | |
| record_id | No | One record id |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and readOnlyHint=false, so the dangerous nature is known structurally. The description's 'Irreversible' adds a small amount of context (permanence, no undo), but says nothing about permissions, cascading effects on related records, or partial-failure behavior for batch deletes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, zero filler, with the mutating action stated first and the irreversibility caveat immediately after. Nothing is buried or redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a three-parameter destructive tool with no output schema and full schema coverage, the description covers the essentials but leaves gaps: access requirements, the single-vs-batch id exclusivity rule, and the consequences of deleting a model with linked records are all unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description's 'one or more records' actively explains the relationship between the two mutually exclusive inputs (record_id vs ids) in a way the schema alone does not spell out.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Delete ... records') and clarifies cardinality ('one or more'), so the agent can distinguish it from create_record/update_record in the sibling list. It does not name the alternatives explicitly, but the operation is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives no when-to-use or when-not-to-use guidance and never references alternatives such as update_record or the read tools. 'Irreversible' is a warning rather than usage routing, so the agent must infer context on its own.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
execute_methodBDestructive
Call a public model method, e.g. sale.order.action_confirm. Restricted by policy.
| Name | Required | Description | Default |
|---|---|---|---|
| args | No | Positional args; record ids first | |
| model | Yes | Technical model name, e.g. 'res.partner' | |
| kwargs | No | ||
| method | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=true, so the safety profile is covered structurally. The description adds one piece of context beyond that — execution is 'restricted by policy' — but never explains what the policy allows, whether the call is reversible, or the risk of invoking arbitrary model methods. Moderate added value against an already-informative annotation set.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short clauses, front-loaded with the action and example, with no filler. It is efficient, though the extreme brevity borders on under-specification for a tool that executes arbitrary server-side methods.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive, policy-gated, arbitrary-method executor with four parameters and no output schema, the description is too thin. It omits what the call returns, which methods are permitted, and how the positional args/kwargs map to the target method — information an agent needs to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 50%: 'args' and 'model' are described in the schema, while 'method' and 'kwargs' are not. The description's example clarifies the expected dotted method format, partially compensating, but 'kwargs' remains undocumented in both places.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (call) and resource (a public model method) with a concrete example ('sale.order.action_confirm'), which tells an agent this is a generic method-invocation escape hatch. It does not explicitly contrast itself with the specific CRUD siblings (create_record, update_record, delete_record), so sibling differentiation is only implied.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Restricted by policy' hints at a gate but never states when to prefer this over the specific sibling tools (create_record, update_record, call_onchange, etc.) or when it is forbidden. No when-to-use, when-not-to-use, or alternative routing is provided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_attachmentBRead-only
Download an attachment as base64.
| Name | Required | Description | Default |
|---|---|---|---|
| attachment_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds the one behavior the annotations don't: the payload is returned base64-encoded. It says nothing about size limits, failure modes, or whether the attachment must belong to a record the caller can access.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One short front-loaded sentence with no waste. It is arguably too terse for a tool with zero parameter documentation, but nothing in it is redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read tool with no output schema, the base64 disclosure is the key piece of information and it is present. Missing are anything about response size, binary handling, or error behavior when the id is invalid or inaccessible.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and the single parameter is an undescribed integer. The description implies the id selects 'an attachment' but gives no format, range, or provenance hints (e.g., where attachment_id comes from). With only one obvious parameter, this is a minor but real gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Download') and resource ('attachment'), and adds the return encoding ('as base64'), which cleanly separates it from the sibling upload_attachment. It stops short of naming that sibling explicitly, so full differentiation requires reading the sibling list.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use guidance, no prerequisites, and no reference to alternatives such as upload_attachment or get_record. The agent must infer the context entirely from the name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_field_namesBRead-only
Compact list of a model's field names with type and label.
| Name | Required | Description | Default |
|---|---|---|---|
| model | Yes | Technical model name, e.g. 'res.partner' |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds the notable trait that the payload is 'compact' (field names plus type and label only), implying a lightweight response, but says nothing about pagination, ordering, or behavior for unknown models.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One short, front-loaded sentence fragment with zero filler; the returned attributes are stated immediately. It is efficient but so terse that it borders on under-specification rather than model conciseness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter read tool this covers the essentials, and the description does hint at return content (name, type, label) despite there being no output schema. However, it leaves the sibling-selection question (get_model_fields vs model_info) and edge-case behavior unexplained.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single `model` parameter is already documented with an example ('res.partner'). The description adds nothing about the parameter, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a clear verb-equivalent ('list') and a specific resource ('a model's field names'), plus the returned attributes (type and label). It does not differentiate from the sibling get_model_fields or model_info, which sound like overlapping read paths, so the agent can't tell from the description alone which to pick.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No when-to-use statement, no prerequisites, and no mention of alternatives such as get_model_fields, model_info, or list_models. The agent must infer that this is the lightweight metadata lookup rather than the fuller field-definition tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_model_fieldsBRead-only
Field definitions of a model (type, relation, required...).
| Name | Required | Description | Default |
|---|---|---|---|
| model | Yes | Technical model name, e.g. 'res.partner' | |
| attributes | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds a hint about the shape of the returned data (type, relation, required), which is useful since there is no output schema, but it omits filtering/pagination behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single compact sentence with the core resource front-loaded and no waste. It is arguably too terse for a two-parameter tool, but nothing is padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read tool with no output schema and annotations covering safety, the description is minimally viable, but it leaves the role of 'attributes' and the actual return format unexplained, and gives no routing against closely related siblings like get_field_names.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 50%: 'model' is documented with an example ('res.partner'), but the 'attributes' array parameter has no description in either the schema or the tool description, so the agent cannot tell whether it filters, expands, or restricts the fields returned. With low coverage the description should compensate and does not.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific resource ('field definitions of a model') and enumerates what the definitions contain ('type, relation, required...'). It implicitly distinguishes itself from the sibling get_field_names by promising richer metadata, though it never names that sibling explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to call this versus get_field_names, model_info, or list_models, all of which sound adjacent. No prerequisites or exclusions are given.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_recordARead-only
Read one or more records by id.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | No | Several record ids | |
| model | Yes | Technical model name, e.g. 'res.partner' | |
| fields | No | Fields to return. Omit for all readable fields — prefer naming them. | |
| record_id | No | One record id |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds 'one or more' (clarifying batch behavior) but says nothing about behavior for missing ids, empty results, or record not found — with annotations carrying the main burden, this is adequate but thin.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no wasted words — the core action and lookup key come first. It is arguably too terse for the batch semantics, but nothing is redundant or padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read tool with full schema coverage and annotations covering safety, the description is nearly sufficient. The one gap is that no output schema exists and the description doesn't characterize the return value, though 'read records by id' makes the expected shape fairly predictable.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so ids, model, fields, and record_id are all documented in the schema itself, including the note that omitting 'fields' returns all readable fields. The description adds no parameter meaning beyond that, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description pairs a specific verb ('Read') with a specific resource ('records') and adds the scoping phrase 'by id', which distinguishes it from the sibling search_records. It stops short of naming that sibling explicitly, so a small amount of differentiation is left to inference.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit when-to-use statement, but the phrase 'by id' implicitly signals this tool is for direct lookups when identifiers are already known, as opposed to search_records or name_search. No exclusions or prerequisites are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_reportBRead-only
Render a PDF report (e.g. an invoice) for records. Returns base64.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | No | Several record ids | |
| record_id | No | One record id | |
| report_name | Yes | e.g. 'account.report_invoice' |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true / destructiveHint=false, and the description is consistent with that. It adds one genuinely useful trait beyond the annotations – the base64 return encoding – but says nothing about report availability, size limits, or failure behavior for an invalid report_name.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, zero filler, with the action front-loaded and the return format trailing. Nothing to cut.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-required-parameter render tool with no output schema, the description covers the essentials and even states the return encoding. However it does not resolve whether 'ids' and 'record_id' are alternatives or combinable, or whether omitting both is legal, leaving a real gap for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the three parameters are already documented in the schema, establishing the baseline of 3. The description adds no clarification of the relationship between the two optional id selectors ('ids' vs 'record_id'), which is the one semantic point the schema leaves ambiguous.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Render a PDF report ... for records') with a concrete example ('e.g. an invoice'), which clearly separates it from the record CRUD and attachment siblings. It stops short of explicitly naming what it is not, but the action is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No when-to-use guidance, no prerequisites, and no mention of alternatives. An agent must infer that this is the only reporting tool and cannot tell from the text whether it is a read-only render or has side effects on stored records.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_modelsBRead-only
List the models this server allows access to, optionally filtered.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| filter | No | Substring of the model name |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds a modest amount of context by noting the list is scoped to models 'this server allows access to', hinting at permission-based visibility, but says nothing about pagination, ordering, or default limit behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single tight sentence with the scope constraint front-loaded. No wasted words, though it is arguably too terse for a tool with an undocumented parameter.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read-only list tool with no output schema and only two optional params, the description is minimally adequate. The undocumented 'limit' parameter and absent mention of result ordering or pagination leave small but real gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 50% – the 'limit' parameter has no description in either the schema or the description. The description's 'optionally filtered' weakly gestures at the filter param without explaining matching semantics beyond what the schema already says ('Substring of the model name'), so it does not compensate for the coverage gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (List) and resource (models) with a scope qualifier ('this server allows access to'). However, it does not distinguish itself from siblings like model_info, get_model_fields, or name_search, which an agent could plausibly confuse with a model-listing tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'optionally filtered' implies the filter parameter is available but gives no guidance on when to use this tool versus model_info, get_model_fields, or search_count. No alternatives, prerequisites, or exclusions are named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
model_infoCRead-only
Model description, Odoo access rights and what this server's policy permits.
| Name | Required | Description | Default |
|---|---|---|---|
| model | Yes | Technical model name, e.g. 'res.partner' |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered structurally. The description adds genuinely useful scope beyond that: it discloses that the response includes Odoo access rights and this server's own policy decisions, which is non-obvious for a tool named model_info. It still omits error behavior for unknown models and any auth requirements, so it is useful but not rich.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One short sentence fragment with zero padding and the most distinctive payload (access rights, policy) front-loaded after the generic 'model description'. However, the verbless fragment form makes it under-specified rather than crisply concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With one required parameter, no output schema, and read-only annotations, the tool is simple, and the description does gesture at the three things returned. But without an output schema the description should more concretely characterize those return values (shape, whether policy differences cause errors), and it says nothing about behavior for invalid or inaccessible models.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single 'model' parameter is documented with an example ('res.partner'), so the schema carries the load. The description adds nothing about the parameter, which is acceptable at this coverage but earns only the baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description is a noun phrase listing the payload ('model description, Odoo access rights and what this server's policy permits') rather than a verb+resource statement, so the agent must infer that this is a metadata/inspection read. It does name the resource (a model) and hint at non-obvious return content, but it gives no differentiation from siblings like get_model_fields, get_field_names, or list_models, which all sound like overlapping metadata lookups.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to call this versus get_model_fields, get_field_names, or list_models, and no stated preconditions (e.g. whether the model must exist or the user must have access). The agent is left to guess from the sibling names alone.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
name_searchARead-only
Find records by display name — the quick way to resolve a name to an id.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | ||
| limit | No | Default 10 | |
| model | Yes | Technical model name, e.g. 'res.partner' | |
| domain | No | Odoo search domain, e.g. [["customer_rank", ">", 0]] | |
| operator | No | Default 'ilike' |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds the useful note that output resolves to an id, but says nothing about result shape, pagination, or how limit/operator interact — a modest addition over the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single tight sentence that front-loads the action and resource, then adds the differentiating 'name to id' framing. No filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 5 parameters, no output schema, and no mention of the domain/operator filtering behavior or the nature of the returned name/id pairs, the description is adequate but leaves the agent without a clear picture of return values or how the optional filters shape results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 80%, so the schema already documents model, domain, operator, and limit with examples. The description adds no parameter-level meaning beyond that, which is the baseline when the schema does the work.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Find') and resource ('records') and adds the scoping detail 'by display name', which separates it from a generic search. It stops short of naming the sibling it differs from (search_records / search_count), so the agent must infer the boundary.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'the quick way to resolve a name to an id' implies the intended use case, but no explicit when-to-use, when-not, or named alternative is given. With a close sibling like search_records present, the guidance is only implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_countCRead-only
Count records matching a domain.
| Name | Required | Description | Default |
|---|---|---|---|
| model | Yes | Technical model name, e.g. 'res.partner' | |
| domain | No | Odoo search domain, e.g. [["customer_rank", ">", 0]] |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds nothing beyond that: it doesn't say what happens when 'domain' is omitted (implied: count all records of the model), nor whether the count honors record rules/access rights. For a count tool with annotation coverage, this is thin.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with the verb first and zero filler. It is efficient, though its brevity comes at the cost of the guidance noted in other dimensions.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read-only counting tool with fully documented parameters and no output schema, the minimum needed to call it correctly is present. Missing is the key behavioral note that omitting 'domain' counts every record in the model, plus any routing hint toward search_records when full rows are needed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema already documents both 'model' and 'domain' with concrete examples, so the schema carries the parameter burden. The description only echoes 'domain' and adds no format or edge-case detail beyond it; baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Count') and resource ('records matching a domain'), which lets an agent distinguish it from the sibling search_records that returns records rather than a tally. However, it never names or contrasts with that sibling explicitly, so differentiation is implied rather than stated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to prefer this over search_records, nor any mention of prerequisites or exclusions. The use case (avoid fetching full records when only a total is needed) is only inferable from the tool name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_recordsCRead-only
Search records with an Odoo domain and return the matching rows.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max rows (default 100) | |
| model | Yes | Technical model name, e.g. 'res.partner' | |
| order | No | e.g. 'create_date desc' | |
| domain | No | Odoo search domain, e.g. [["customer_rank", ">", 0]] | |
| fields | No | Fields to return. Omit for all readable fields — prefer naming them. | |
| offset | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, and the description only echoes that read semantics via 'return the matching rows.' It adds nothing about pagination, the default 100-row cap, or behavior when limit is omitted, which are the operationally relevant traits for a search tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One sentence, zero filler, with the action and the return stated up front. It is efficient rather than padded, though its brevity borders on under-specification for a six-parameter tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a six-parameter search with one required field and no output schema, the description leaves major gaps: pagination via limit/offset, default row cap, field-selection guidance, and behavior on an invalid domain are all unaddressed. Annotations cover only the safety profile, so the description should carry far more of this burden.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 83%, so the schema already documents model, domain, fields, order, and limit syntax. The description only restates 'Odoo domain' and adds no format or default guidance beyond the schema; baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource (search records) with the mechanism named (Odoo domain) and the return subject stated (matching rows). It is clear what the tool does, but it never differentiates itself from siblings like search_count or name_search, which sound nearly identical to an outside reader.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No when-to-use, when-not-to-use, or alternative is given. An agent must infer that search_count is for totals and name_search is for name lookups; the description provides no routing signal at all.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
server_statusBRead-only
Server, active Odoo profile and governance status.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds that the payload covers server, profile and governance state, which is meaningful context beyond the annotations, but it says nothing about freshness, cost, or whether the call is cheap or expensive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single short noun phrase with zero padding, so it is maximally concise. It is slightly under-specified rather than bloated, which is a mild structural weakness rather than a verbosity problem.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description is the only place that can tell the agent what comes back, and it only lists three high-level areas without any shape or detail. For a no-parameter diagnostic tool this is adequate but leaves the return content largely opaque.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate; baseline 4 applies. No parameter-related gap exists.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names three resources it reports on (server, active Odoo profile, governance) but omits a verb, so it reads as a label rather than an action. It does implicitly distinguish itself from the CRUD-oriented siblings, which all target records rather than diagnostic state.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No indication of when to call this versus alternatives, no prerequisites, no mention of whether it should be used for health checks or troubleshooting. The agent must infer all usage context from the name alone.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_recordC
Update one or more existing records.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | No | Several record ids | |
| model | Yes | Technical model name, e.g. 'res.partner' | |
| values | Yes | Field values to write | |
| record_id | No | One record id |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=false, so the safety profile is covered. Beyond that the description adds nothing: it does not say what happens to fields omitted from 'values' (merge vs overwrite), whether a partial or id-less failure aborts the whole batch, what permissions are needed, or what the update returns.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with zero filler; the verb and scope arrive immediately. Its weakness is under-specification rather than bloat, and nothing would need to be cut to improve it.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a four-parameter mutation tool with full schema coverage, no output schema, and annotations that already cover the write/safety profile, the definition is minimally viable. It still omits batch-failure semantics and the partial-update behavior of 'values', which genuinely matter for a bulk write.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so each of the four parameters is documented in the schema and the baseline is 3. The description's 'one or more' faintly signals the ids-vs-record_id duality, but it adds no syntax, model naming, or values-format detail beyond what the schema already provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('update') and resource ('existing records') plus the multiplicity ('one or more'), so the agent knows this is a bulk-capable mutation rather than a read or create. It never contrasts itself with create_record or delete_record, so it lacks explicit sibling differentiation, but the verb alone is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this tool versus create_record, delete_record, or execute_method, and no preconditions such as required permissions or model-level write access. The 'one or more' phrasing implies bulk use but never states when the single-record path is preferable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
upload_attachmentC
Attach a file (base64) to a record.
| Name | Required | Description | Default |
|---|---|---|---|
| model | Yes | Technical model name, e.g. 'res.partner' | |
| filename | Yes | ||
| mimetype | No | ||
| record_id | Yes | One record id | |
| data_base64 | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=false, so the agent knows this is a non-destructive write. The description adds only the base64 payload format, which is already implied by the data_base64 parameter name; it says nothing about size limits, permissions, whether an existing attachment is replaced, or what happens on success.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single short sentence with the action and payload format front-loaded and zero filler. It is efficient, though its brevity borders on under-specification rather than being a model of density.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with no output schema, four required parameters, and only 40% schema coverage, the description leaves too much open: no success/return behavior, no permission or size constraints, and no guidance on the optional mimetype. The annotations cover the safety profile but not the operational details.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 40%, so the schema does not carry the load. The description mentions 'base64' and 'record' but does not clarify mimetype (optional), filename semantics, the technical model name convention, or the record_id target beyond what the parameter names already convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (attach/upload) and resource (a base64 file) plus the target (a record), which is enough to distinguish it from the read-side sibling get_attachment. It stops short of explicitly naming that sibling or contrasting upload vs download, so it is clear but not maximally differentiated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use or when-not-to-use guidance, no mention of the alternative get_attachment for retrieval, and no prerequisites such as required access to the target model/record. The agent must infer all routing from the tool name alone.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
18 tool updates
v0.1.0- First observed
cache_stats - First observed
call_onchange - First observed
create_record - First observed
delete_record - First observed
execute_method - First observed
get_attachment - First observed
get_field_names - First observed
get_model_fields - First observed
get_record - First observed
get_report - First observed
list_models - First observed
model_info - First observed
name_search - First observed
search_count - First observed
search_records - First observed
server_status - First observed
update_record - First observed
upload_attachment
TDQS
Scored across 18 tools
Most tools target clearly distinct actions (CRUD, search, metadata, attachments, reports). The main overlap is get_model_fields vs get_field_names (full field definitions vs compact list), and there are three search variants (search_records, name_search, search_count), though their descriptions do clarify the boundaries.
Strong, predictable verb_noun snake_case pattern (get_record, search_records, create_record, update_record, delete_record, execute_method, etc.). Minor deviation: a few noun-only names (model_info, server_status, cache_stats) break the verb-prefix pattern but remain readable and conventional for status/info tools.
18 tools is on the heavier side but each maps to a real capability of an Odoo ORM surface (CRUD, search, metadata introspection, method execution, reports, attachments). Slightly over-scoped rather than bloated, with no obviously redundant tools.
Covers the full lifecycle: create/read/update/delete, multiple search modes, model and field introspection, method calling, onchange simulation, PDF reports, and attachment upload/download, plus status and cache tooling. No obvious dead ends for the domain.
Maintenance
Related MCP Connectors
- odooOAuthcom.odooconsole
Odoo ERP for AI agents: hosted OAuth endpoint, gated writes, one endpoint for every instance.
Connect AI to Odoo ERP 14-20: search, create, update and manage records in natural language.
Context and control for any AI. Agent identity, scoped access, and proof across tools.
Zero-trust gateway for AI agents: score tool calls, verify agent cards, enforce policy, audit.
Related MCP Servers
- AlicenseNot gradedqualityNot gradedmaintenanceEnables AI assistants to interact with Odoo ERP systems through natural language to search records, create entries, update data, and manage business operations. Supports secure authentication and configurable access controls for production environments.Mozilla Public 2.0
- AlicenseAqualityAmaintenanceEnables AI agents to interact with Odoo ERP as the authenticated user, with tools for discovery, planning, and mutations bounded by user permissions.36108 npm2Apache 2.0
- AlicenseNot gradedqualityBmaintenanceProvides permission gates and tamper-evident audit logging for AI agent tool executions, with declarative policies, consent ladders, and hash-chained verification.MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI assistants to securely connect to Odoo and perform CRUD operations, custom tool calls, and data queries through natural language, with OAuth 2.1 and API key authentication.-