Odoo MCP服务器

这是一个连接Odoo 19 ERP系统的MCP服务器,通过JSON-2 API提供模型查询与记录增删改查等能力,适用于AI助手以自然语言方式安全访问和管理Odoo数据。

  • by twtrubiks
  • Python
  • 混合部署
  • 4 星标
  • 更新于
在 GitHub 查看 twtrubiks/odoo19-mcp-server

使用说明

Odoo 19 MCP Server (JSON-2 API)

支援的 MCP Client

Odoo 19 MCP Server,使用 JSON-2 API 連線。

✨ 支援多 user:設定 MCP_MULTIUSER=true 後,每個 client 以自己的 Odoo API key 認證,權限、操作歸屬、審計紀錄都對應真實 user,不再全部掛在同一個服務帳號上,詳見多 user 模式。

本專案基於 Odoo 19 JSON-2 API 完整使用指南 開發。

技術棧

  • Python: 3.13
  • FastMCP: >=4.1.0,<5.0.0
  • odoo-client-lib: 2.0.1 (JSON-2 API)

架構

flowchart TB
    subgraph Client["MCP Client"]
        CC[Claude Code]
        GC[Gemini CLI]
        MI[MCP Inspector]
    end

    subgraph Server["MCP Server (FastMCP)"]
        R[Resources<br/>odoo://models<br/>odoo://user<br/>odoo://company]
        T[Tools<br/>search_records<br/>create_record<br/>update_record]
        DI[Dependency Injection<br/>get_caller_client]
    end

    subgraph RPC["OdooJsonRpcClient"]
        OL[odoolib<br/>json2/json2s protocol]
    end

    subgraph Odoo["Odoo Server"]
        EP["/jsonrpc endpoint"]
    end

    Client -->|MCP Protocol<br/>stdio/http/sse| Server
    R --> DI
    T --> DI
    DI --> RPC
    RPC -->|HTTP/HTTPS| Odoo

MCP 核心概念

Resources vs Tools

特性 Resources Tools
用途 提供上下文資訊 執行操作/動作
觸發 客戶端控制(如 Claude Code) LLM 自動決定呼叫
參數 無(或 URI 參數) 有(需 LLM 生成)
類比 員工手冊(背景知識) 工具箱(按需使用)
HTTP 類比 GET(讀取) POST/PUT/DELETE(操作)

Resources - 動態上下文,LLM 一開始就知道的背景資訊:

odoo://user     → "我是誰"
odoo://company  → "我在哪間公司"
odoo://models   → "有哪些模型可用"

Tools - 需要時才呼叫的操作:

search_records(model="res.partner", domain=[...])  → 搜尋
create_record(model="sale.order", values={...})    → 建立

為什麼不用 Default Prompt?

方式 Default Prompt Resource
資料來源 寫死在程式碼 即時從 Odoo 查詢
更新時機 部署時 每次連線時
換用戶登入 資訊錯誤 自動正確
# ❌ Default Prompt(寫死)
SYSTEM_PROMPT = "當前用戶: Admin"  # 換人登入就錯了

# ✅ Resource(動態)
@mcp.resource("odoo://user")
def get_current_user():
    return client.read("res.users", [uid])  # 即時查詢

結論:Resource 是「動態的上下文」,不是靜態文字。

參考:MCP Resources | MCP Tools

環境變數

變數 說明 預設值
ODOO_URL Odoo 伺服器 URL http://localhost:8069
ODOO_DATABASE 資料庫名稱 -
ODOO_API_KEY API Key 認證 -
READONLY_MODE 唯讀模式(禁止寫入操作) false
MCP_ALLOW_SENSITIVE_MODELS 設 true 停用模型黑名單(預設擋 credential 模型 ir.config_parameter、res.users.apikeys 的讀寫) false
MCP_AUTH_TOKEN HTTP/SSE 模式的 Bearer Token 認證(未設定=無認證;stdio 不適用),見安全機制 -(停用)
MCP_MULTIUSER 多 user 模式:每個 client 拿自己的 Odoo API key 當 Bearer token,見安全機制 false
UPLOAD_TOKEN_SECRET upload token 的 HMAC secret;僅多 worker 部署需要設定(單一程序自動衍生) -

建立 .env 檔案:

cp .env.example .env

安裝

pip install -r requirements.txt

啟動方式

開發模式(MCP Inspector)

fastmcp dev inspector odoo_mcp_server.py

傳輸模式(Transport)

本專案支援三種 MCP 傳輸模式:

模式 說明 適用情境
stdio 標準輸入輸出(預設) Claude Desktop、Cursor IDE、本機開發
http HTTP 協定 遠端服務、n8n、Web 應用整合
sse Server-Sent Events(已棄用) 向下相容舊版 Client

stdio vs HTTP/SSE:算力位置

兩種模式的關鍵差異在於「誰來啟動 MCP Server」以及「算力在哪裡執行」:

stdio 模式(本機算力)

┌─────────────────────────────────────┐
│            你的電腦 💻               │
│                                     │
│  Claude Desktop ──> MCP Server      │
│                     (使用本機算力)   │
└─────────────────────────────────────┘
  • Client(如 Claude Desktop)啟動 MCP Server 作為子進程
  • MCP Server 使用你電腦的 CPU/RAM
  • Server 隨 Client 啟動/關閉

HTTP/SSE 模式(遠端算力)

┌──────────────┐         ┌──────────────────┐
│   你的電腦    │         │     雲端 ☁️       │
│              │         │                  │
│Claude Desktop│ ──網路──>│   MCP Server     │
│  (輕量)      │         │  (使用雲端算力)   │
└──────────────┘         └──────────────────┘
  • MCP Server 獨立運行在雲端/遠端主機
  • 多個 Client 可同時連線同一個 Server
  • 適合團隊共用、n8n 整合、正式環境

啟動不同模式

# stdio 模式(預設)
python odoo_mcp_server.py

# HTTP 模式
python odoo_mcp_server.py --transport http --host 0.0.0.0 --port 8000

# SSE 模式(已棄用,建議使用 HTTP)
python odoo_mcp_server.py --transport sse --host 0.0.0.0 --port 8000

雲端部署(HTTP 模式)

⚠️ 安全提醒:HTTP 模式預設沒有認證——任何連得到該 port 的人都直接繼承 ODOO_API_KEY 的完整權限。除非 server 只在受信任的內網使用, 否則請務必設定 MCP_AUTH_TOKEN(或改用多 user 模式 MCP_MULTIUSER)並搭配 TLS,詳見安全機制

專案提供 docker-compose.example.yml 範本,複製後修改即可使用:

cp .env.example .env                                  # 填入 ODOO_URL / ODOO_DATABASE / ODOO_API_KEY
cp docker-compose.example.yml docker-compose.yml      # 依需求調整
docker compose up -d

範本內容

volumes:
  shared-uploads:

services:
  odoo-mcp:
    build: .
    command: ["python", "odoo_mcp_server.py", "--transport", "http", "--host", "0.0.0.0", "--port", "8000"]
    # 對外暴露 port 8000(host 端 client 可直接連 http://localhost:8000/mcp)。
    # ⚠️ "8000:8000" 會綁定 0.0.0.0:同網段的所有機器都連得到
    # (主機若有公網 IP,就是整個網際網路),且 Docker 發佈的 port 會繞過 ufw 防火牆規則。
    # 建議設定 MCP_AUTH_TOKEN(見 environment);只給本機 client 用可改 "127.0.0.1:8000:8000"。
    # 若只需 Docker 內網存取(例如 client 也在同一個 compose 裡),可整段移除 ports。
    ports:
      - "8000:8000"
    environment:
      - ODOO_URL=${ODOO_URL}
      - ODOO_DATABASE=${ODOO_DATABASE}
      - ODOO_API_KEY=${ODOO_API_KEY}
      - READONLY_MODE=${READONLY_MODE:-false}
      # HTTP 模式的 Bearer Token 認證(未設定=無認證,見 README「安全機制」)
      - MCP_AUTH_TOKEN=${MCP_AUTH_TOKEN:-}
      # 多 user 模式:每個 client 拿自己的 Odoo API key 當 Bearer token,
      # 權限/審計歸屬真實 user(見「多 user 模式」)
      # - MCP_MULTIUSER=true
      # HTTP 模式的 Host 標頭防護(DNS rebinding protection,來自底層 MCP SDK):
      # 用非 localhost 的 IP/網域連進來時,預設會被擋下並回 "Invalid host header"。
      # ⚠️ 快速測試可先全開(勿用於正式環境):
      - FASTMCP_HTTP_ALLOWED_HOSTS=["*"]
    volumes:
      - shared-uploads:/shared   # 圖片傳遞通道;對應 Dockerfile 預建的 /shared/uploads
    restart: unless-stopped

圖片 / 附件傳遞:add_attachment 的 file_path 模式會從 /shared/uploads/ 讀檔上傳到 Odoo,避免大量 base64 佔用 LLM output token。client 與 server 跨機器(不共用此 volume)時,改走 prepare_upload → /upload 把檔案送進 UPLOAD_DIR,詳見安全機制。

連不上、回 Invalid host header? 這是底層 MCP SDK 的 DNS rebinding 防護——用非 localhost 的 IP/網域連進來時,Host 標頭不在允許清單內就會被擋。用 FASTMCP_HTTP_ALLOWED_HOSTS 放行:

# 快速測試(⚠️ 對任何 Host 開放,勿用於正式環境)
FASTMCP_HTTP_ALLOWED_HOSTS=["*"]

# ✅ 正規做法:只列出 client 實際連線的 host(含 port)
FASTMCP_HTTP_ALLOWED_HOSTS=["your-server-ip:8000"]     # 純 IP 部署
FASTMCP_HTTP_ALLOWED_HOSTS=["mcp.example.com"]         # 反向代理/網域(建議搭配 TLS)

官方明確警告:使用萬用字元 * 會讓 server 對任何來源開放,正式環境請務必列出明確 host。必要時另有 FASTMCP_HTTP_ALLOWED_ORIGINS(瀏覽器型 client 的 Origin 白名單)。

# server 有設 MCP_AUTH_TOKEN 時,需帶 Authorization header
claude mcp add --transport http odoo-mcp https://your-cloud-server.com:8000/mcp --header "Authorization: Bearer your_random_token_here"

# 多 user 模式(MCP_MULTIUSER=true):Bearer 改填「自己的 Odoo API key」
claude mcp add --transport http odoo-mcp https://your-cloud-server.com:8000/mcp --header "Authorization: Bearer <你的 Odoo API key>"

# server 未啟用認證(僅限受信任內網)
claude mcp add --transport http odoo-mcp https://your-cloud-server.com:8000/mcp
手動設定 JSON(加到 `~/.claude.json`)
{
  "mcpServers": {
    "odoo-mcp": {
      "type": "http",
      "url": "https://your-cloud-server.com:8000/mcp",
      "headers": {
        "Authorization": "Bearer your_random_token_here"
      }
    }
  }
}

⚠️ 純 HTTP 下 Bearer token 是明文傳輸,僅適合受信任內網/臨時測試;對外請改用 https://(TLS)。

server 未啟用 MCP_AUTH_TOKEN、也未開多 user 模式時,headers 整段可省略。 多 user 模式(MCP_MULTIUSER=true)下 headers 必填,Bearer 改填自己的 Odoo API key,見多 user 模式。

MCP Resources

URI 說明
odoo://models 列出所有模型
odoo://model/{model_name} 取得模型欄位定義
odoo://record/{model_name}/{record_id} 取得單筆記錄
odoo://user 當前登入用戶資訊
odoo://company 當前用戶所屬公司資訊

MCP Tools

Tool 說明 唯讀
list_models 列出/搜尋可用模型 Yes
get_fields 取得模型欄位定義 Yes
search_records 搜尋記錄 Yes
count_records 計數記錄 Yes
read_records 讀取指定 ID 記錄 Yes
create_record 建立記錄 No
update_record 更新記錄 No
delete_record 刪除記錄(需二次確認) No
execute_method 執行任意模型方法(萬用入口,unlink 與黑名單模型已封鎖,見安全機制) No
add_attachment 上傳附件到 Odoo(file_path / base64_data 兩種模式,見安全機制) No
prepare_upload 取得 /upload 端點用法與短效 upload_token(跨機器傳檔,見安全機制) No

Docker 建置

部分 client 的 Docker 設定(Claude Code / Gemini 的 Docker 版本)需要先建置本機映像檔:

docker build -t odoo-mcp-server .

MCP Client 設定

本專案支援以下 MCP Client,各自的完整設定步驟見對應章節:

Client 加入方式 設定檔
Claude Code claude mcp add ~/.claude.json
Gemini CLI gemini mcp add ~/.gemini/settings.json
Antigravity CLI 手動編輯 ~/.gemini/config/mcp_config.json
OpenClaw openclaw mcp set OpenClaw config
Codex CLI codex mcp add + 手動編輯 ~/.codex/config.toml

Claude Code

設定檔位於 ~/.claude.json:

本機執行

claude mcp add odoo-mcp-server -- python odoo_mcp_server.py
手動設定 JSON
{
  "mcpServers": {
    "odoo-mcp-server": {
      "command": "/bin/python",
      "args": [
        "odoo_mcp_server.py"
      ]
    }
  }
}

Docker(host.docker.internal)

適用於 Odoo 執行在本機的情況:

claude mcp add odoo-mcp-server -- docker run -i --rm --add-host=host.docker.internal:host-gateway -e ODOO_URL=http://host.docker.internal:8069 -e ODOO_DATABASE=odoo19 -e ODOO_API_KEY=your_api_key_here odoo-mcp-server
手動設定 JSON
{
  "mcpServers": {
    "odoo-mcp-server": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "--add-host=host.docker.internal:host-gateway",
        "-e",
        "ODOO_URL=http://host.docker.internal:8069",
        "-e",
        "ODOO_DATABASE=odoo19",
        "-e",
        "ODOO_API_KEY=your_api_key_here",
        "odoo-mcp-server"
      ]
    }
  }
}

Docker(host network)

使用主機網路模式:

claude mcp add odoo-mcp-server -- docker run -i --rm --network host -e ODOO_URL=http://localhost:8069 -e ODOO_DATABASE=odoo19 -e ODOO_API_KEY=your_api_key_here odoo-mcp-server
手動設定 JSON
{
  "mcpServers": {
    "odoo-mcp-server": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "--network",
        "host",
        "-e",
        "ODOO_URL=http://localhost:8069",
        "-e",
        "ODOO_DATABASE=odoo19",
        "-e",
        "ODOO_API_KEY=your_api_key_here",
        "odoo-mcp-server"
      ]
    }
  }
}

Docker(遠端 Odoo)

claude mcp add odoo-mcp-server -- docker run -i --rm -e ODOO_URL=https://example.com/ -e ODOO_DATABASE=odoo19 -e ODOO_API_KEY=your_api_key_here odoo-mcp-server
手動設定 JSON
{
  "mcpServers": {
    "odoo-mcp-server": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-e",
        "ODOO_URL=https://example.com/",
        "-e",
        "ODOO_DATABASE=odoo19",
        "-e",
        "ODOO_API_KEY=your_api_key_here",
        "odoo-mcp-server"
      ]
    }
  }
}

Gemini CLI

gemini mcp add --scope user odoo-mcp docker -- run -i --rm --add-host=host.docker.internal:host-gateway -e ODOO_URL=http://host.docker.internal:8069 -e ODOO_DATABASE=odoo19 -e ODOO_API_KEY=your_api_key_here odoo-mcp-server
手動設定 JSON(加到 `~/.gemini/settings.json`)
{
  "mcpServers": {
    "odoo-mcp": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "--add-host=host.docker.internal:host-gateway",
        "-e",
        "ODOO_URL=http://host.docker.internal:8069",
        "-e",
        "ODOO_DATABASE=odoo19",
        "-e",
        "ODOO_API_KEY=your_api_key_here",
        "odoo-mcp-server"
      ]
    }
  }
}

Antigravity CLI

自 2026/6/18 起個人版 Gemini CLI 停止服務,改用 Antigravity CLI。目前 沒有 mcp add 子指令,需手動編輯設定檔。

設定檔路徑為 ~/.gemini/config/mcp_config.json(Antigravity CLI / IDE / SDK 共用,等同 Gemini CLI 的 --scope user)。

JSON 格式與上方 Gemini CLI 設定相同。

設定後進入 Antigravity CLI 以 /mcp 指令重新載入,並確認連線狀態。

OpenClaw

OpenClaw 透過 CLI 管理 MCP server,設定會寫入 mcp.servers.<name>。

/mcp 指令為 owner-only 且預設關閉,需以 commands.mcp: true 開啟才能在 chat session 中使用。

步驟 1:註冊 MCP server

# 請將 your-server-ip 換成你的 MCP server 位址
openclaw mcp set odoo-mcp '{"type":"http","url":"http://your-server-ip:8000/mcp"}'
手動設定 JSON(寫入 OpenClaw 設定的 `mcp.servers`)

OpenClaw 會自動正規化設定,把 type:"http" 轉成 transport:"streamable-http" 後存入:

{
  "mcp": {
    "servers": {
      "odoo-mcp": {
        "url": "http://your-server-ip:8000/mcp",
        "transport": "streamable-http"
      }
    }
  }
}

步驟 2:開啟 /mcp 指令

openclaw config set commands.mcp true

步驟 3:重啟 Gateway 套用設定

openclaw gateway restart

若想等進行中的工作排空再重啟,可改用 openclaw gateway restart --safe。

驗證

# server 是否註冊成功
openclaw mcp list
openclaw mcp show odoo-mcp

# /mcp 開關狀態(應回傳 true)
openclaw config get commands.mcp

完成後請開一個新的 chat session(或硬重整 dashboard),再輸入 /mcp 確認 odoo-mcp 連線狀態。

Codex CLI

Codex 的 codex mcp add 只支援 stdio(command / args),並不支援 url(streamable HTTP)形式的遠端 server。因此要連雲端 HTTP 模式的 MCP server,需先用佔位指令建立設定,再手動編輯 ~/.codex/config.toml。

codex mcp add odoo-mcp -- echo placeholder
手動設定 TOML(修改 `~/.codex/config.toml`)

codex mcp add 產生的佔位設定:

[mcp_servers.odoo-mcp]
command = "echo"
args = ["placeholder"]

手動改為 url(streamable HTTP):

[mcp_servers.odoo-mcp]
url = "https://your-cloud-server.com:8000/mcp"

若 server 端設定了 MCP_AUTH_TOKEN,需加上 bearer_token_env_var = "ODOO_MCP_TOKEN", 並在執行 Codex 的環境中 export ODOO_MCP_TOKEN=<與 server MCP_AUTH_TOKEN 相同的值>; 或改用自訂 http_headers 直接填 Authorization header。 多 user 模式(MCP_MULTIUSER=true)下同理,ODOO_MCP_TOKEN 改 export 自己的 Odoo API key。

安全機制

部署定位與 HTTP 認證(MCP_AUTH_TOKEN)

設定 MCP_AUTH_TOKEN 環境變數即可啟用 Bearer Token 認證(opt-in):

# 產生隨機 token
openssl rand -hex 32
  • 啟用後 /mcp 端點要求 Authorization: Bearer <token>,未帶或錯誤一律回 401
  • 未設定時行為與過去版本相同(無認證),但 HTTP/SSE 模式啟動時會在 stderr 印出警告

多 user 模式(MCP_MULTIUSER)

預設情況下,所有操作都透過 ODOO_API_KEY 這一個 Odoo 帳號執行——多人共用時, Odoo 端的權限、chatter、審計紀錄全部歸到同一個 user。設定 MCP_MULTIUSER=true 後改為 pass-through 認證:

  • 每個 user 在 Odoo「偏好設定 → 帳戶安全」產生自己的 API key,直接當 HTTP/SSE 連線的 Bearer token(json2 protocol 的 API key 即完整憑證, Odoo 會從 key 解析出擁有者)
  • Server 收到 token 後拿去問 Odoo 驗證(結果快取 5 分鐘、驗證失敗快取 30 秒 以防暴力破解),驗證成功即建立綁該 user API key 的專屬連線
  • 權限(ACL / record rules)、操作歸屬、審計全部由 Odoo 原生機制處理; 撤銷存取 = 在 Odoo 刪除該 API key(最遲 5 分鐘後生效)
  • Server 端不保存任何使用者資料,新增/移除 user 都在 Odoo 操作即可
  • MCP_AUTH_TOKEN 可並存,作為 admin fallback(走 ODOO_API_KEY 的共享連線); stdio 模式不受影響
  • 純多 user 部署(不設 MCP_AUTH_TOKEN、不跑 stdio)可連 ODOO_API_KEY 都不設 ——server 端零長效憑證,設定檔外洩也沒東西可偷。誤走到 fallback 路徑時會收到 明確的設定錯誤訊息(不會拿 placeholder 去打 Odoo)
  • Bearer token 就是 Odoo API key 本體,務必搭配 TLS

Client 端設定與 MCP_AUTH_TOKEN 完全相同(完整範例見雲端部署(HTTP 模式)), 只是 Bearer 換成各自的 Odoo API key:

"headers": {
  "Authorization": "Bearer <你的 Odoo API key>"
}

附件檔案讀取範圍(UPLOAD_DIR)

add_attachment 的 file_path 模式是本 server 唯一會讀取 MCP 主機本地檔案的入口。 若不設限,被 prompt injection 的 LLM 可用 file_path="/app/.env" 把 server 機密 (含 ODOO_API_KEY 本身)讀出、上傳成 Odoo 附件外洩——這是 confused deputy, MCP_AUTH_TOKEN 擋不住(LLM 本來就是合法持 token 的 client)。

因此 file_path 被限制在 UPLOAD_DIR(預設 /shared/uploads)底下:

  • 路徑經 Path.resolve() 正規化後,必須落在 UPLOAD_DIR 內,否則回 ToolError
  • resolve() 會一併解掉 symlink,所以「白名單目錄裡放一個指向外部的 symlink」也擋得掉
  • 預設值對齊 compose 的 /shared/uploads 圖片傳遞通道,Docker 部署無需額外設定
  • 純本機 stdio 若要放行任意路徑,設 UPLOAD_DIR=/(等於解除限制,自負風險)
  • 檔案不在磁碟上(如 Discord 上傳的圖片)時,改用 base64_data 模式,不受此限制

跨機器上傳圖片(prepare_upload → /upload)

當 client 與 server 不在同一台機器時,shared-uploads volume 用不到,file_path 沒有共用檔案系統可讀;若改走 base64_data,整包 base64 會流經 LLM 的 token stream,又慢又貴。

/upload 提供一條 out-of-band 的檔案通道:client 用普通 HTTP POST 把位元組直接推到 server (不經 LLM),server 存進 UPLOAD_DIR 後回傳 file_path,client 再用這個路徑呼叫 add_attachment——只有短路徑字串會進 token stream。

整個工作流透過 MCP 協定自我描述,client 端零安裝、零設定:agent(如 Claude Code)呼叫 prepare_upload 工具就拿到端點用法與短效 upload_token,接著自己上傳:

# upload_token 由 prepare_upload 簽發(server 未設 MCP_AUTH_TOKEN 時免帶 header)
curl -fsS -F "file=@/local/invoice.png" \
     -H "Authorization: Bearer <upload_token>" \
     https://your-server:8000/upload
# → {"file_path": "/shared/uploads/<uuid>.png", "file_name": "invoice.png"}

接著呼叫 add_attachment(file_path="/shared/uploads/<uuid>.png", file_name="invoice.png", ...)。 不經 MCP 的手動整合(腳本、CI 等)也可以直接拿 MCP_AUTH_TOKEN 本體打同一個端點。

安全機制:

  • /upload 是會寫檔的端點,設了 MCP_AUTH_TOKEN 就要求 Bearer token(custom route 不受 MCP 認證保護,故自行驗證)
  • prepare_upload 簽發的是 HMAC 衍生短效 token(MCP_AUTH_TOKEN 為根秘密簽出、預設 10 分鐘、只對 /upload 有效、無狀態驗證)——master token 不進 LLM context,就算對話 transcript 外流,外洩的也只是效期內的上傳權限
  • 磁碟檔名由 server 端 uuid 產生,client 給的檔名絕不進入路徑,無法逃出 UPLOAD_DIR
  • 單檔大小上限 UPLOAD_MAX_BYTES(預設 25 MiB),超過回 413
  • 不自動清理 UPLOAD_DIR,請搭配定期清理或使用 ephemeral volume

模型黑名單(MCP_ALLOW_SENSITIVE_MODELS)

預設封鎖以下兩個機密模型的所有讀寫,防範 prompt injection——LLM 讀到藏在資料裡的惡意指令,拿著你的 API key 做出「權限上合法、但你沒要求」的操作:

  • ir.config_parameter:存第三方 API key、webhook token 等機密,一句 search 就全外洩
  • res.users.apikeys:可產生新的長效 API key——事後 rotate 原 key 也擋不住,等於留後門

正常的 agent 工作流程用不到這兩個模型,封鎖不影響日常使用;所有工具與 Resources(含 execute_method)都會檢查。

黑名單只是多一層保險,不是權限控管——權限仍由 Odoo ACL 決定,請給 MCP 低權限使用者的 API key (res.users、ir.rule 等安全模型因此不在黑名單內)。 真的需要透過 MCP 管理這兩個模型時,設 MCP_ALLOW_SENSITIVE_MODELS=true 停用。

唯讀模式

設定 READONLY_MODE=true 啟用唯讀模式,適用於生產環境查詢:

  • 寫入工具(create_record、update_record、delete_record、execute_method、add_attachment)在註冊時即被停用——LLM 看不到這些工具,直接呼叫也會被拒絕
  • 停用發生在模組層級,任何啟動方式(python odoo_mcp_server.py、fastmcp run、fastmcp dev)都同樣生效

刪除二次確認與 execute_method 的安全邊界

delete_record 內建 confirm 機制:LLM 必須先以 confirm=False 呼叫取得確認提示,經使用者同意後才能以 confirm=True 執行刪除。

注意:confirm 參數由 LLM 自行填入,屬於「引導 LLM」層級的防護, 並非強制性的安全邊界(LLM 理論上可直接傳 confirm=True)。

execute_method 是萬用入口(escape hatch),用來呼叫沒有專用工具的模型方法,請理解其風險:

  • ORM 原語 unlink 已被攔下,會導向 delete_record 的二次確認流程,無法藉此繞過確認
  • 模型黑名單同樣適用:credential 模型(ir.config_parameter、res.users.apikeys)全擋
  • 但 action_confirm、action_post、button_validate 這類會改資料的業務方法不設防—— Odoo 有上千個模型方法,server 無法枚舉哪些會寫入資料庫
  • 真正的安全邊界應該是給 MCP 使用低權限的 Odoo 使用者 API key(最小權限原則)

健康檢查

HTTP/SSE transport 模式下提供 /health 端點:

curl http://localhost:8000/health
# {"status": "healthy", "service": "odoo-mcp-server", "version": "1.2.0"}

適用於 Docker healthcheck、Kubernetes probe、load balancer 探活。stdio 模式下不影響。 此端點不受 MCP_AUTH_TOKEN 保護(不需帶 token)。

License

Apache 2.0