odoo-json2
odoo-json2
Cursor plugin that exposes Odoo 19 External JSON-2 as two MCP tools. The domain type is a single OdooCall — not one tool per model, and not XML-RPC or JSON-RPC.
OdooCall = { model, method, ids?, context?, params }
POST {ODOO_URL}/json/2/{model}/{method}
Authorization: bearer {ODOO_API_KEY}
X-Odoo-Database: {ODOO_DATABASE}Tools
Tool | What it does |
| One JSON-2 call. Named kwargs only. |
|
|
The odoo-json2 skill documents search, search_read, read, create, write, unlink, and search_count, plus the one-transaction-per-call rule.
Requirements
Cursor IDE (desktop). Grok Bot cannot load
~/.cursor/plugins/local. Grok Bot plugins are account-wide marketplace connectors, not this local folder.Node.js 18 or newer (global
fetch, no npm dependencies).An Odoo 19 database on a Custom pricing plan (the external API is not available on One App Free or Standard).
A user API key and the database name.
Install in Cursor IDE
Copy this directory to
~/.cursor/plugins/local/odoo-json2.Reload the window (Developer: Reload Window).
Open Plugins → Configure and set the variables below.
Confirm the
odoo-json2MCP server and theodoo-json2skill appear under Customize.After changing plugin variables, toggle the
odoo-json2MCP server off and on. Reload Window alone does not pick up new env values.
Do not put API keys in this repo. The plugin only declares variable names.
Plugin variables
Declared in .cursor-plugin/plugin.json and substituted into mcp.json:
Variable | Required | Meaning |
| yes | Host or origin. |
| yes | User API key (see below). |
| yes | Database name. Sent as |
mcp.json launches ./server.mjs as a plugin-relative executable. Cursor's local plugin loader does not expand ${PLUGIN_ROOT} in args.
Mint an API key
In Odoo: Preferences → Account Security → New API Key.
Give the key a description and a duration (maximum three months). The value is shown once — copy it into the Cursor plugin variable. For integrations, Odoo recommends a dedicated bot user with the minimum access rights rather than a personal admin account.
The server sends Authorization: bearer … with a lowercase bearer, matching the Odoo 19 docs.
Example: search_read
This request and result are the deco / company example from the official Odoo 19.0 External JSON-2 documentation (dummy host mycompany.example.com). They are not live data from this plugin.
HTTP (docs):
POST /json/2/res.partner/search_read HTTP/1.1
Host: mycompany.example.com
X-Odoo-Database: mycompany
Authorization: bearer …
Content-Type: application/json; charset=utf-8
{
"context": { "lang": "en_US" },
"domain": [
["name", "ilike", "%deco%"],
["is_company", "=", true]
],
"fields": ["name"]
}Documented success body:
[{ "id": 25, "name": "Deco Addict" }]Same call through odoo_call:
{
"model": "res.partner",
"method": "search_read",
"context": { "lang": "en_US" },
"params": {
"domain": [
["name", "ilike", "%deco%"],
["is_company", "=", true]
],
"fields": ["name"]
}
}Prefer search_read over search then read. Each JSON-2 request is its own SQL transaction.
Run locally (dev)
export ODOO_URL=https://mycompany.odoo.com
export ODOO_API_KEY=your-key
export ODOO_DATABASE=mycompany
node server.mjsThe process speaks MCP over stdin/stdout (newline-delimited JSON-RPC). Logs go to stderr.
npm run check # node --check server.mjs
npm test # stdio initialize + tools/list; mocked odoo_call (no live Odoo)Tests never use a real API key or a public Odoo instance.
Layout
.cursor-plugin/plugin.json
mcp.json
server.mjs
skills/odoo-json2/SKILL.md
package.json
LICENSELicense
MIT