Skip to main content
Glama
monthop-gmail

cf-odoo-mcp-server

cf-odoo-mcp-server

MCP server for Odoo ERP running on Cloudflare Workers

Uses stateless HTTP transport — no Durable Objects, no container, no long-running process, fits comfortably in the Workers free tier.

Ported from odoo-mcp-claude which provides the same set of 10 tools but runs as a Python process talking over XML-RPC.

Odoo 19 has 2 kinds of API keys — check which one to use

Odoo 19 issues API keys in 2 scopes, and they are not interchangeable.

Key type

Talks to

What you get

mcp

Odoo's own /mcp

Built-in MCP server, 5 tools read-only — no write scope to enable

rpc

/jsonrpc

Full ORM access this project uses this one

The scope split is strict in both directions: an rpc key hitting /mcp gets 401, and an mcp key cannot authenticate via JSON-RPC either.

So you may not need this project at all if your agent is read-only. Odoo's built-in MCP server needs no deployment, no hosting, and no moving your Odoo password elsewhere — just point the client at https://<odoo-ของคุณ>/mcp with an mcp key and you're done. It also refuses technical models, which is a guardrail this project doesn't have.

Work that must write data needs an rpc key, which is the reason this project exists — including the case of connecting to multiple Odoo instances through a single endpoint, and having a default cap on reads. Both can run side by side.

NOTES.md has the full comparison, including the points where the built-in MCP server's schema will trip up an agent.

Related MCP server: Odoo 18 MCP Server

Why JSON-RPC instead of XML-RPC

Python's xmlrpc.client needs a raw socket, which the Workers runtime doesn't provide. But Odoo exposes the same execute_kw set over JSON-RPC at /jsonrpc, which is plain HTTP usable directly with fetch without any adaptation.

Requirements

Odoo must be reachable from the internet over HTTPS — a Worker can't reach into a LAN. If Odoo isn't public yet, put a Cloudflare Tunnel in front of it.

Tools

Tool

Odoo method

odoo_list_servers

— (shows configured servers)

odoo_search_read

search_read

odoo_search_count

search_count

odoo_read

read

odoo_create

create then reads back written fields

odoo_write

write then reads back written fields

odoo_delete

unlink

odoo_execute

any method

odoo_fields_get

fields_get

odoo_version

common.version

Configuration

Variable

What it does

MCP_AUTH_TOKEN

Required bearer token callers must attach

ODOO_SERVERS

JSON accepting one or multiple servers; if set, overrides the one below

ODOO_URL ODOO_DB ODOO_USERNAME ODOO_PASSWORD

Optional fallback for a single server

ALLOWED_ORIGIN_HOSTNAMES

Optional; comma-separated list of hostnames allowed to call /mcp with a browser Origin, or *; if unset, only localhost and the Worker's own workers.dev hostname are accepted — server-side clients don't send Origin anyway, so they're unaffected

Use an Odoo API key instead of an account password, and grant that account only the permissions the tools actually need — odoo_delete and odoo_write can access everything that account can access.

ODOO_SERVERS looks like this:

{
  "default_server": "prod",
  "servers": {
    "prod": { "url": "https://odoo.example.com", "db": "mydb", "username": "bot@example.com", "password": "api-key" }
  }
}

Local development

npm install
cp .dev.vars.example .dev.vars   # แล้วกรอกค่าให้ครบ
npm run dev

.dev.vars is already gitignored — don't commit passwords.

Deploy

npx wrangler login
npx wrangler deploy

npx wrangler secret put MCP_AUTH_TOKEN     # openssl rand -hex 32
npx wrangler secret put ODOO_URL
npx wrangler secret put ODOO_DB
npx wrangler secret put ODOO_USERNAME
npx wrangler secret put ODOO_PASSWORD

Secrets are encrypted at rest and never appear in wrangler.jsonc.

Connecting a client

.mcp.json in this folder is a Claude Code file, not a Worker one — it just tells the client which endpoint to call with which token. The file references values from the environment, so it contains no secrets and can be committed to git.

{
  "mcpServers": {
    "odoo": {
      "type": "streamable-http",
      "url": "${ODOO_MCP_URL:-https://cf-odoo-mcp-server.<subdomain>.workers.dev/mcp}",
      "headers": { "Authorization": "Bearer ${MCP_AUTH_TOKEN}" }
    }
  }
}

Claude Code doesn't load .env for you — export into the environment before invoking.

cp .env.example .env    # แล้วกรอก MCP_AUTH_TOKEN
set -a; . ./.env; set +a
claude

If you forget to export, the variables won't be substituted, claude mcp list will warn you that the variables aren't found, and the Worker will respond 401 — failing loudly, not silently.

ODOO_MCP_URL exists to point at different Workers, e.g. staging and production, without editing .mcp.json.

Other clients that don't read .mcp.json should set the endpoint and Authorization: Bearer <token> header themselves following the same pattern.

GET /health needs no auth and returns {"status":"ok"}.

Security

This endpoint is public, so every request to /mcp must carry a bearer token, compared in constant time. If MCP_AUTH_TOKEN isn't set, the Worker returns 500 instead of opening up.

A single shared token suits a personal server or internal use. If you need per-user identity, put a Workers OAuth Provider in front instead.

Notes from real-world use

NOTES.md records what was tested against a real Odoo and important caveats when an AI agent is the one calling these tools — especially how Odoo silently drops values written to readonly fields.

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables interaction with Odoo 18 (JSON-RPC API) via MCP, supporting model exploration, CRUD operations, and secure API key authentication.
    Apache 2.0
  • F
    license
    Not graded
    quality
    D
    maintenance
    Full-featured MCP connector for Odoo ERP via XML-RPC, enabling natural language interaction with CRM, sales, inventory, and other Odoo modules.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables interaction with Odoo ERP systems, providing tools and resources to access models, records, and methods via stdio or HTTP streamable transport.
    MIT