How to connect Claude, Cursor, ChatGPT or any MCP client to an Odoo instance hosted on OdooConsole. The MCP server is open source; see also pricing and the blog.
Every connector points at https://mcp.odooconsole.com/mcp. What a token carries decides what it reaches: a token for all instances reaches every instance's ERP on your account — choosing which per call — plus the provisioning tools; a token for one instance reaches only that instance's ERP; a token for the account reaches the provisioning tools alone. Adding an instance issues no new hostname and needs no change in your client — and an all-instances connector reaches it the moment it exists, with nothing to re-authorise.
Point any MCP client at https://mcp.odooconsole.com/mcp. It will get a 401 carrying a WWW-Authenticate header that names the metadata document below, discover the authorization server from there, register itself, and send you to a consent screen. Nothing has to be configured by hand.
GET /.well-known/oauth-protected-resource names https://auth.odooconsole.com as the authorization server.POST /oauth2/register there. No pre-shared client id.Authorization: Bearer <token> on every request.The choice is part of the grant, not a setting in the client. A single-instance connector authorised for acme cannot see beta, and re-authorising is the only way to point it elsewhere. An all-instances connector names the instance in each call (see the instance argument below) and reaches every instance you own, including ones created after it was authorised.
| Grant | Scopes | Reaches |
|---|---|---|
| All instances & account | odoo:*, instances:* |
Every instance's ERP on the account — named per call via the instance argument — and the provisioning tools. The default consent choice. |
| One instance | odoo:read, odoo:write |
That instance's ERP data — records, models, views. Takes no instance argument; the grant fixes it. |
| The account | instances:read, instances:write |
Create, restart, inspect and delete instances. No route to any instance's data. |
The scope families stay separable: a single-instance connector holds only odoo:* and can never provision, and an account-only connector holds only instances:* and can never read a record — so text injected into an Odoo record cannot reach a provisioning tool. The all-instances grant deliberately holds both, for one agent that manages the account and works inside its instances; grant the narrower kinds when a connector should see less.
tools/list is authoritative — what follows is what a token of each kind sees today.
instances:*| Tool | Writes | What it does |
|---|---|---|
list_instances |
– | Every instance on the account, with state |
instance_status |
– | State plus live container health |
instance_logs |
– | Recent log lines; source is odoo or sidecar |
create_instance |
yes | Provisions an instance (Odoo 19) and returns its admin password once |
restart_instance |
yes | Restarts the containers; the database is untouched |
delete_instance |
yes | Permanent. confirm_name must repeat the instance name |
odoo:*odoo_search_read, odoo_search_count, odoo_export, odoo_list_models, odoo_get_fields, odoo_model_info, odoo_get_view, odoo_connection_info, odoo_doctor are available to any token with odoo:read.
odoo_create, odoo_update, odoo_delete, odoo_execute, odoo_set_default, odoo_modify_action additionally require odoo:write. A read-only grant refuses them before the request reaches Odoo.
The instance argument. Under an all-instances grant, every odoo_* tool takes instance as its first argument — the name of the instance to act on, which must be one you own; an unknown or unowned name is refused the same way a missing one is. Under a single-instance grant the tools take no instance argument; the grant fixes it.
Installing modules. odoo_execute runs a model method, so it installs and uninstalls Odoo modules: call it on ir.module.module with button_immediate_install (or button_immediate_uninstall) and the module's record id. This needs odoo:write.
The connector authenticates to Odoo as a dedicated account with the rights to manage that instance's configuration, so Odoo's own access rules still apply on top of the scope — but a token's scope, not the account, is the outer gate: a read-only grant never reaches a write tool.
The endpoint is shared; the instances are not. Each instance runs its MCP server inside its own network namespace with no route to any other instance, and that server independently re-checks that the token names the instance it serves before answering. A routing mistake fails closed at the destination rather than handing one instance's data to another. An all-instances token carries no single-instance claim; the shared router resolves the instance named in each call, refuses any your account does not own, and forwards to that instance's own server — so the same per-instance scope check, audit trail and network isolation apply exactly as they do to a single-instance connector.