Connect Odoo to MCP, step by step
From install to the first request: requirements, the public URL and reverse proxy, the user the agent acts as, connecting Gental, Claude and ChatGPT, a security checklist, verifying webhooks, and fixing the common errors.
Published
In short
To connect Odoo to MCP: install “MCP Server for AI Agents” from the Odoo App Store on Odoo 17 to 20 (self-hosted or Odoo.sh), set the public https URL and run the reachability check, create a least-privilege “Gental Agent” user in “Gental MCP: Connect”, choose what to expose, then paste your Odoo URL ending in /mcp into Gental, Claude or ChatGPT, sign in and approve.
Before you start
- Odoo 17, 18, 19 or 20, Community or Enterprise, self-hosted or on Odoo.sh. Odoo Online can’t install custom modules.
- A public https URL that reaches Odoo. Gental and cloud clients don’t connect to http or private addresses.
- Administrator rights in Odoo (Settings) to install the module and create users.
Install the module
Get the module
From the Odoo App Store for your Odoo version, or clone the branch that matches it (for example git clone -b 19.0) into your addons path.
Update the apps list
Turn on developer mode, then in Apps choose Update Apps List.
Install “MCP Server for AI Agents”
Search for it in Apps and install it. A “Gental MCP” menu appears at the top; managers see all of it.
The public URL and the reachability check
In Gental MCP › Configuration › General, set Odoo’s public URL (it defaults to web.base.url), copy the MCP URL ending in /mcp, and run the reachability check.
| What you see | What it means | What to do |
|---|---|---|
| The URL is http, not https | Clients only connect over https | Put a TLS certificate on the domain and update the URL |
| A private address (like 10.x or 192.168.x) | Gental and cloud clients can’t reach a private network | Publish Odoo at a public URL through a reverse proxy |
| The check passes but every call returns 401 | Your proxy strips the Authorization header | Pass the header through and turn off buffering (below) |
Reverse proxy settings
If Odoo sits behind nginx, make sure it passes the Authorization header and doesn’t buffer responses. Turn on proxy_mode in Odoo so it knows the URL is https.
# Inside the server block that serves Odoo over https
location / {
proxy_pass http://odoo; # your Odoo upstream
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Authorization $http_authorization;
proxy_buffering off;
}
# odoo.conf: trust the forwarded headers
# proxy_mode = TrueCreate the “Gental Agent” user
Every request runs as an Odoo user, so this user is the limit of what the agent can reach.
A new internal user
In Settings › Users, create a user named “Gental Agent” with its own email.
Only the groups it needs
Give it only the groups the agent needs, such as Sales user without manager rights. Your record rules apply to it.
Add it to “Gental MCP: Connect”
Without it the user can’t connect or approve a client. You can change the allowed groups in Configuration › Access.
Choose what the agent can reach
Ready-made areas
In Configuration › Curated tools, turn on Contacts, Sales, Invoicing, CRM, Inventory or POS, and switch off any single tool. An area whose app isn’t installed shows as disabled with the reason.
Models and methods
In Configuration › Models & methods, add any model, pick read, create, update and delete, and the public methods you allow, each with its kind.
Hidden fields
Hide any field you don’t want the agent to see. Passwords, tokens and binary fields are always hidden.
Extra domains
Add an Odoo domain on top of record rules, such as [('company_id', '=', 1)] for one company only.
Connect an MCP client
Gental
In Connectors choose Custom MCP server, paste your Odoo URL ending in /mcp, sign in as Gental Agent and approve. For the customer-only tools (“my orders”, “my invoices”), paste the identity secret Gental shows once into Configuration › General.
Claude
Add a custom connector with the same URL, sign in with the Odoo user and approve.
ChatGPT
Add a connector (in developer mode) with the same URL, sign in with the Odoo user and approve.
Your scripts
In Clients › New API key, pick the user and an expiry. The key is shown once; send it as Authorization: Bearer.
A first request from a script
An initialize call with an API key; the JSON reply carries the server name and protocol version:
curl https://odoo.example.com/mcp \
-H "Authorization: Bearer $ODOO_MCP_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"my-script","version":"1.0"}}}'Security checklist
- The agent connects as a dedicated least-privilege user, never as an administrator.
- Review the Inbox (Logs › Inbox) weekly, especially denied requests.
- Revoke clients and keys you don’t use in Clients; revoking stops them at once.
- Rotate API keys and the identity secret regularly, and give keys an expiry.
- Keep Odoo and the module up to date.
Subscribe to webhooks
A new subscription
In Configuration › Webhooks: a name, an https URL, the events (invoice posted or paid, order created, confirmed or cancelled, delivery done, lead created or stage changed, contact created, or any record on an exposed model), and the user the data is read as.
Copy the secret
Each subscription has its own secret starting with gmwh_. Keep it on your side; Rotate replaces it.
Test it
Press Send test and follow each delivery in Logs › Outbox › Webhooks. A failed delivery is retried up to eight times over about a day.
Verify the webhook signature
Each delivery carries X-Gental-Event, X-Gental-Delivery, X-Gental-Timestamp and X-Gental-Signature. The signature is HMAC-SHA256 with your secret over timestamp + "." + the raw body, in hex. Compare in constant time and reject timestamps older than five minutes.
import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody: the request body exactly as received (a Buffer), before any JSON parsing.
// headers: Node's lower-cased request headers.
export function verifyGentalWebhook(headers, rawBody, secret, now = Date.now() / 1000) {
const timestamp = headers['x-gental-timestamp'];
const signature = headers['x-gental-signature'];
if (!timestamp || !signature) return false;
// Reject anything older (or newer) than 5 minutes.
const ts = Number(timestamp);
if (!Number.isFinite(ts) || Math.abs(now - ts) > 300) return false;
const expected = createHmac('sha256', secret)
.update(`${timestamp}.`)
.update(rawBody)
.digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(String(signature));
return a.length === b.length && timingSafeEqual(a, b);
}import hashlib
import hmac
import time
def verify_gental_webhook(headers, raw_body: bytes, secret: str, now: float | None = None) -> bool:
"""headers: a case-insensitive mapping (Flask, Django, FastAPI request headers)."""
timestamp = headers.get("X-Gental-Timestamp")
signature = headers.get("X-Gental-Signature")
if not timestamp or not signature:
return False
# Reject anything older (or newer) than 5 minutes.
try:
ts = int(timestamp)
except ValueError:
return False
if abs((time.time() if now is None else now) - ts) > 300:
return False
expected = hmac.new(
secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected.encode(), signature.encode())A delivery can arrive more than once, so ignore any X-Gental-Delivery you have already handled.
Troubleshooting
| Problem | Likely cause | Fix |
|---|---|---|
| 401 | The token or key was revoked, or the user left the connect group or was disabled | Add the user back to the group and reconnect the client |
| 403 | The server is switched off, the API key’s user isn’t in a connect group, or a browser request came from an origin not allowed | Switch the server on, add the user to the connect group, or allow the origin in Configuration › Access |
| 429 | The rate limit was hit (120 requests a minute per client by default) | Wait a minute, or raise the limit in Configuration › Access |
| A tool call was denied | The area, model or field isn’t exposed, or the user’s rights don’t allow it | Open the request in Logs › Inbox to see which wall refused it |
Sources
- The module on the Odoo App Storeas of November 2026
- MCP specification: Authorizationas of November 2026
Common questions
Do I need Gental to use the module?
No. Any MCP client can connect. Gental adds verified customer identity, a confirmation before any change, and replies on WhatsApp and your website. See Gental for Odoo.
Can OAuth and API keys both be on?
Yes. Both are on by default, and you can switch either off in Configuration › General. Gental uses OAuth when it is on.
What happens if I disable the user or remove it from the group?
All its tokens stop working at once, and the client gets 401 until someone signs in again with an allowed user.
Keep reading
- Gental for OdooA free module that makes your Odoo a secure MCP server for Gental and any MCP client.
- Developers (API)Connect your agent to your app and systems with REST, streaming and signed webhooks.
- IntegrationsThe knowledge sources, tools and channels your agent connects to today, and what’s next.
- What is an AI agent?A plain explanation of AI agents, how they differ from chatbots and ChatGPT, and how to keep them accurate.
Start today. It answers tonight.
Add your knowledge, pick a persona, publish it on your website. Free to start.
- No card
- No code
- Data stored in Saudi Arabia