Odoo MCP server for AI agents

Live

BEARER TOKEN

ERP AND CRM

Accounting & Finance

Odoo gives your agent access to your Odoo ERP: list and create contacts, CRM leads, and project tasks, confirm quotations into sales orders, review invoices and products, and search, create, or update records in any Odoo model.

  • Per-user credentials: each call uses the actual user's access token, never a shared bot.
  • Encrypted per-tenant vault: AES-256, resolved at request time, never in LLM context.
  • Scoped before every call: pre-call scope check, 90-day SIEM-exportable audit chain.
Odoo
agent · Acme Q3
Run
Which quotations over $10,000 are still waiting to be confirmed?
S
odoo_list_sale_orders
112ms
ERP agent
5 quotations over $10,000 are unconfirmed, worth $84,600 together. The oldest, S00231 for Azure Interior, was sent 18 days ago.
Sources: Odoo sales orders, 5 quotations
odoo
5 quotations
18:29
Message Claude...

Tools your ERP agent reaches for on Odoo, scoped per user.

CALL ANY TOOL
23 tools for ERP and CRM in Odoo: list contacts, leads, sales orders, invoices, products, tasks, and employees, create contacts, leads, and tasks, confirm quotations, and search or update any model.
odoo_archive_records
Archive records
Archive records of an Odoo model so they are hidden from default searches without deleting them. Returns true on success (some models return an action object instead). Use this instead of odoo_delete_records for reversible removal. Only models with an active field support it.
Parameters
Name
Type
Required
Description
ids
array
Required
Integer IDs of the records to archive to act on. Get IDs from a search or list tool.
model
string
Required
Technical name of the Odoo model to operate on, with dots (for example res.partner, crm.lead, sale.order, account.move, product.product, project.task, hr.employee). Use odoo_get_model_fields to inspect a model.
context
object
Optional
Optional context object passed to the ORM, for example {"lang": "fr_FR"} for translated values or {"active_test": false} to include archived records in searches.
odoo_call_model_method
Call model method
odoo_confirm_sale_order
Confirm sale order
odoo_count_records
Count records
odoo_create_contact
Create contact
odoo_create_lead
Create lead
odoo_create_records
Create records
odoo_create_task
Create task
odoo_delete_records
Delete records
odoo_get_model_fields
Get model fields
odoo_get_user_context
Get user context
odoo_list_contacts
List contacts
odoo_list_employees
List employees
odoo_list_invoices
List invoices
odoo_list_leads
List leads
odoo_list_products
List products
odoo_list_sale_orders
List sale orders
odoo_list_tasks
List tasks
odoo_name_search_records
Name search records
odoo_read_records
Read records
odoo_search_read_records
Search read records
odoo_update_contact
Update contact
odoo_update_records
Update records
Build your Agent
Same auth pattern across LangChain, OpenAI, Anthropic, and Google ADK.
Python · LlamaIndex
import { ScalekitClient } from "@scalekit-sdk/node";
import { createReactAgent } from "@langchain/langgraph/prebuilt";

const sk = new ScalekitClient(env.SCALEKIT_ENV_URL, env.SCALEKIT_CLIENT_ID, env.SCALEKIT_CLIENT_SECRET);

// Odoo tools scoped to this user
const { tools } = await sk.tools.listScopedTools("user_123", {
  filter: { connectionNames: ["odoo"], toolNames: [
    "odoo_list_sale_orders",
    "odoo_confirm_sale_order",
    "odoo_list_invoices"] },
  pageSize: 100,
});

const agent = createReactAgent({ llm, tools });
await agent.invoke({ messages: [{ role: "user", content: "Confirm the approved quotations and list unpaid invoices for those customers" }] });
import OpenAI from "openai";
import { ScalekitClient } from "@scalekit-sdk/node";

const sk = new ScalekitClient(env.SCALEKIT_ENV_URL, env.SCALEKIT_CLIENT_ID, env.SCALEKIT_CLIENT_SECRET);
const openai = new OpenAI();

const { tools } = await sk.tools.listScopedTools("user_123", {
  filter: { connectionNames: ["odoo"] }, pageSize: 100,
});

const res = await openai.chat.completions.create({
  model: "gpt-5",
  messages: [{ role: "user", content: "Confirm the approved quotations and list unpaid invoices for those customers" }],
  tools,
});

// Execute the tool call with the user's vaulted Odoo access token
await sk.tools.executeTool(res.choices[0].message.tool_calls[0], "user_123");
import Anthropic from "@anthropic-ai/sdk";
import { ScalekitClient } from "@scalekit-sdk/node";

const sk = new ScalekitClient(env.SCALEKIT_ENV_URL, env.SCALEKIT_CLIENT_ID, env.SCALEKIT_CLIENT_SECRET);
const anthropic = new Anthropic();

const { tools } = await sk.tools.listScopedTools("user_123", {
  filter: { connectionNames: ["odoo"] }, pageSize: 100,
});

const msg = await anthropic.messages.create({
  model: "claude-sonnet-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Confirm the approved quotations and list unpaid invoices for those customers" }],
  tools,
});

// Tool call runs with the user's vaulted Odoo access token
await sk.tools.executeTool(msg.content, "user_123");
import { Agent } from "@google/adk/agents";
import { ScalekitClient } from "@scalekit-sdk/node";

const sk = new ScalekitClient(env.SCALEKIT_ENV_URL, env.SCALEKIT_CLIENT_ID, env.SCALEKIT_CLIENT_SECRET);

const { tools } = await sk.tools.listScopedTools("user_123", {
  filter: { connectionNames: ["odoo"] }, pageSize: 100,
});

const agent = new Agent({
  name: "erp_agent",
  model: "gemini-2.5-pro",
  instruction: "Odoo tools scoped to this user",
  tools,
});

await agent.run("Confirm the approved quotations and list unpaid invoices for those customers");
Try these prompts
Copy any prompt into your agent. Each maps directly to a Odoo tool. Click to copy, paste into your agent, done.
CRM and sales
Copy the prompt
Copied
List open opportunities for Azure Interior.
Copy the prompt
Copied
Create a lead for Deco Addict from today's inbound call.
Copy the prompt
Copied
Confirm quotation S00231.
Finance and catalog
Copy the prompt
Copied
List customer invoices from last month that are still unpaid.
Copy the prompt
Copied
List the products that match 'desk'.
Copy the prompt
Copied
How many vendor bills are still in draft?
Projects and records
Copy the prompt
Copied
Create a task in the Office Design project to order samples.
Copy the prompt
Copied
List tasks assigned to Marc Demo.
Copy the prompt
Copied
What fields does the crm.lead model have?
SEE HOW AUTH WORKS
Each user connects their own Odoo access token once, and Scalekit sends it with every call. Access tokens stay vaulted, every call is scope checked, and every action is logged.
1
Authorize
Your user connects
Odoo
once. We tie it to their identity and the meetings they approved — no shared bot account, no org-wide access
Who:
user ‘A’
when:
Once per user
access:
Limited to user
2
Store
Their
Odoo
token lives in a vault scoped to them. User A's meetings are never reachable by an agent acting for user B, even on the same connection
vault:
encrypted
scope:
per-user
tokens:
auto-refreshed
3
Resolve
When your agent calls a
Odoo
tool, we fetch the right token server-side. It never touches your agent, never appears in the LLM context, never shows up in your logs
speed:
~40ms
check:
before every call
seen by:
nobody
4
Audit
Every
Odoo
tool call is logged — who triggered it, which meeting was fetched, what came back. 90 days of history, tied to the user who authorized it
history:
90 days
export:
SIEM-ready
logged:
every call
Test other agents
See the same per-user auth pattern across other accounting and CRM connectors.
GTM and RevOps Teams
CRM AI agent
Reads the Granola transcript after every call, extracts next steps and updates the HubSpot record, drafts the follow-up in Gmail, and confirms in Slack, all on the rep's own delegated OAuth.
GTM and RevOps Teams
Revenue forecast commentary
Pulls open pipeline from Salesforce and HubSpot, calculates coverage against quota, flags at-risk stages, posts commentary to Slack, and logs every snapshot to Google Sheets.
Test other agents
See the same per-user auth pattern across other accounting and CRM connectors.
GTM
CRM AI agent
Turn each Granola call transcript into a HubSpot record update, a drafted Gmail follow-up, and a Slack recap.
GTM
Revenue forecast agent
Score pipeline coverage against quota across Salesforce and HubSpot, post forecast commentary to Slack, log snapshots to Sheets.
Why Scalekit
Secure your agent's access. Connectors ship in minutes
01.
Shared tokens break per-user analytics
A shared Odoo access token looks fine in a demo. In production every record change looks like one service account, and you cannot tell which user triggered it. Scalekit resolves the credential of the actual user who triggered the agent, never a shared bot.
// shared key
audit → bot_service_account

// scalekit
audit → user_abc ✓
02.
Authentication is not authorization
03.
Multi-tenancy is architectural
04.
Odoo today. Ten connectors tomorrow.
“Our agents act across Salesforce, Gong, Google Drive, and more, on behalf of every customer. Scalekit behind the scenes meant we can keep adding tools without ever rebuilding how credentials or tool calling work.”
Venu Madhav Kattagoni
Head of Engineering / Von
FAQs
Frequently Asked Questions
Does the agent access Odoo as the user or through a shared access token?
As the user. Each user connects their own Odoo access token once, and Scalekit sends it with every call. Audit logs attribute every record the agent creates or updates to that user, not a shared service account.
Where is the Odoo access token stored?
In Scalekit's managed AES-256 token vault, namespaced per tenant. To rotate it, the user reconnects with a new access token. Revocation is a single dashboard action. Credentials never appear in prompts, logs, or LLM context.
Can I limit what the agent does in Odoo?
Yes. Filter by tool name in listScopedTools to expose only what you want, for example the list and read tools without odoo_delete_records, which permanently deletes records, or odoo_call_model_method, which runs any public method on a model. Scalekit also enforces scope checks before every API call.
What happens when a user revokes Odoo access?
The connection is invalidated on the next tool call. Subsequent requests for that user fail closed with a clear error. Other users in the tenant remain unaffected. The event is logged for audit.
Can the agent work with custom Odoo models?
Yes. odoo_get_model_fields describes the fields of any model, odoo_search_read_records searches any model with a domain filter, and odoo_create_records and odoo_update_records write to any model. odoo_archive_records hides records without deleting them.
Start in your coding agent
Up and running in one command
Install the Scalekit skill in your editor of choice. Connector, auth, tools, prompt, all wired up
Claude Code REPL
/plugin marketplace add scalekit-inc/claude-code-authstack
/plugin install agentkit@scalekit-auth-stack
Cursor Code REPL
# ~/.cursor/mcp.json
{
""mcpServers"": {
""odoo"": {
""url"": ""https://mcp.scalekit.com/odoo"",
""headers"": { ""Authorization"": ""Bearer $SCALEKIT_TOKEN"" }
}
}
}
Codex Code REPL
# ~/.codex/config.toml
[mcp_servers.odoo]
url = ""https://mcp.scalekit.com/odoo""
auth_env = ""SCALEKIT_TOKEN""
Copilot Code REPL
# .vscode/mcp.json
{
""servers"": {
""odoo"": {
""url"": ""https://mcp.scalekit.com/odoo"",
""type"": ""http""
}
}
}