odoo-mcp-server
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server gives access to an Odoo database as the signed-in user, with that user's access rights. Start with odoo_context (current user, company and date) and odoo_list_models. Call odoo_describe_model before filtering a model you have not used yet. Domains use Odoo syntax: a list of [field, operator, value] conditions, all combined with AND unless you add '|' (OR) or '!' (NOT) prefixes, e.g. [["state", "=", "sale"], ["date_order", ">=", "2026-01-01"]]. Dates are 'YYYY-MM-DD'; datetimes are UTC 'YYYY-MM-DD HH:MM:SS'. Use odoo_aggregate for totals and counts per group instead of reading many records. Results are paged: use offset when has_more is true.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| odoo_contextA | Return the session context: who you are connected as (user id, name, login), the current company and its currency, the companies you can access, language, time zone, today's date in that time zone ('YYYY-MM-DD'), the current UTC date and time ('YYYY-MM-DD HH:MM:SS') and the Odoo version. Use it to resolve relative dates such as 'last quarter', the current user or company, or the current company's currency. It reads no business records. No arguments. |
| odoo_list_modelsA | List the Odoo models (tables) you may query, all in one response: technical name, label, the administrator's hint about what each one holds, and can_write (whether changes are allowed on it). Only models enabled by the administrator that you can read are included. Use it to find a model name you do not know yet; use odoo_describe_model for a model's fields and odoo_search for its records. |
| odoo_describe_modelA | Describe the fields of one model: for each field its type, label, related model (and whether that model is available too), selection values, help text, and required, readonly or not-stored flags. Also returns the model's hint, default_fields and max_records_per_call. Use it before filtering, sorting, grouping or choosing fields on a model you have not used yet; it returns no records (use odoo_search for data). Passwords, tokens, keys, files and fields hidden by the administrator are never listed and cannot be used. |
| odoo_searchA | Find the records of one model that match a domain and return one page of them with the requested fields, plus total (all matches), offset and has_more; page with offset while has_more is true. Use it when you need the records themselves. For known ids use odoo_read, for a number only odoo_count, for totals per group odoo_aggregate. You only see records your access rights allow. Many2one values come as {id, name}, one2many and many2many as {count, ids} (first 50 ids), long texts are cut at 4,000 characters. |
| odoo_readA | Read specific records of one model by id, at most 100 per call, and return their fields as {model, count, records}. Use it when you already have ids (from odoo_search, a relation field or the user); to find records by criteria use odoo_search, for a number only odoo_count. Ids that do not exist are listed in missing_ids instead of failing; if one of the records is outside your access rights, the call fails with an access error. Values are formatted as in odoo_search. |
| odoo_countA | Count the records of one model that match a domain and return {model, count}. Use it when you need only the number: it reads no records, so it is cheaper than odoo_search. Without a domain it counts every record you can see, archived ones excluded. For counts per group use odoo_aggregate with '__count'; to get the records use odoo_search. |
| odoo_aggregateA | Group the records of one model that match a domain and compute totals per group, e.g. sales per customer per month, and return {model, groupby, measures, count, rows} with one key per groupby item and measure in each row; many2one groups come as {id, name}. Without groupby it returns one row for all matching records. Use it for sums, averages or counts per group instead of reading many records; use odoo_count for a single number and odoo_search for the records themselves. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 7 tools
Each tool targets a distinct operation: context, model discovery, field description, search, read-by-id, count, and aggregate. Descriptions explicitly cross-reference the alternatives (e.g. 'for a number only use odoo_count'), leaving no realistic room for misselection.
All names share a consistent odoo_ prefix and snake_case, with clear verb_noun patterns (list_models, describe_model, search, read, count, aggregate). Only odoo_context is a bare noun rather than a verb phrase, a minor deviation.
Seven tools is well-scoped for a generic, model-agnostic query layer over Odoo. Every tool earns its place, and the fact that Odoo has hundreds of models is handled through discovery rather than per-model tools.
Read coverage is complete: discovery, schema, search, read-by-id, count, aggregate, and session context, with no dead ends for querying. However, odoo_list_models exposes a can_write flag implying write access, yet no create/update/delete tool exists, so any mutation request is a guaranteed dead end.