標籤: ERP 系統

  • 把 ERP 變成 AI 的執行單元:iDempiere OData × MCP Server 整合策略

    重點摘要(TL;DR)

    • iDempiere(開源 ERP)的 REST/OData API 包裝成 MCP server,任何支援 MCP 的 AI 工具(Claude / ChatGPT / Cursor / Claude Code / VS Code)都能直接呼叫 ERP。
    • Microsoft 已經做了 Dynamics 365 ERP MCP server(2026/4 文件更新),三類工具設計:Data tools(OData CRUD)、Form tools(模擬使用者操作)、Action tools(直接呼叫 business class)。這個設計可以直接借鏡到 iDempiere
    • iDempiere REST 已經提供 api/v1/auth(JWT)、api/v1/models(OData CRUD)、api/v1/windowsapi/v1/processes 四類 endpoint — 剛好對應 Microsoft 的三類工具
    • 整合到腦子系統:LLM Gateway + MCP Server 雙軌設計,Gateway 管 LLM 流量,MCP 管 ERP tool calls,各自有 audit log,iDempiere 內建的 AD_Role 直接當權限層,不用自寫 ABAC。
    • 本文是腦子系統四部曲的第五篇延伸(ERP 整合層)。前四篇:Why / How / Scale / Tools

    一、為什麼 iDempiere OData 是腦子系統缺的拼圖

    前四篇文章把 AI 治理系統蓋好了:LLM Gateway、雙引擎、Harness、Chat-native Agent。但有個關鍵問題沒解決 — AI 怎麼安全地讀寫公司的真實業務資料?

    大部分公司現況:業務資料躺在 ERP 裡,AI 透過 prompt 拿不到;或者員工自己貼資料給 AI(踩到 A 級資料禁令)。第一篇的核心哲學「AI 時代不做 UI,做給 AI 安全取資料的入口」需要一個具體載體。

    iDempiere(開源 ERP)的 REST/OData API 剛好就是這個載體 — 它本來就是「給機器讀的標準介面」,而且整套權限、Audit、租戶隔離都已經 30 年累積在 iDempiere 的 AD(Application Dictionary)裡。不用重新造輪子,直接接到 AI

    二、事實核對:iDempiere REST API 真實狀態(2026/5)

    本文涉及的 iDempiere 技術細節都來自官方來源,以下是 2026/5 撰文時的驗證結果:

    事實 驗證來源
    REST plugin 由 BX Service GmbH 維護,GPLv2,used in production iDempiere Wiki
    支援 iDempiere release 12 及 master,plugin 已運作於 v9/v10 GitHub Repo
    官方文件站 idempiere-rest-docs
    Swagger UI 互動式 API 探索 hengsin/idempiere-rest-swagger-ui

    2.1 四個主要 API 端點

    • POST/PUT api/v1/auth/tokens — JWT 認證(token 1 小時有效)
    • api/v1/models/{tableName} — PO(Persistent Object)CRUD,支援 OData filter
    • api/v1/windows/{windowSlug}/tabs/{tabSlug} — Window/Tab 互動(對應 ERP UI 的視窗結構)
    • api/v1/processes/{processSlug} — Process 呼叫(DocAction、報表、自動化流程)

    以及附加端點:檔案存取、Reference 資料、Cache 管理、Workflow、Scheduler 資訊。[來源]

    三、業界典範:Microsoft Dynamics 365 ERP MCP Server

    Microsoft 在 2026/4/27 發表了 Dynamics 365 Finance & Operations 的 MCP server 完整文件 — Microsoft Learn這是目前業界最完整的 ERP × AI 整合範例,值得借鏡。

    3.1 Microsoft 的三類工具設計

    類別 用途 代表工具(Microsoft)
    Data tools OData CRUD operations data_find_entities、data_create_entities、data_update_entities、data_delete_entities
    Form tools 模擬使用者在 UI 上的操作(點按鈕、填表、開分頁) form_click_control、form_set_control_values、form_save_form
    Action tools 直接呼叫 ERP 內部 business logic class api_find_actions、api_invoke_action

    3.2 三個關鍵設計原則(直接可借鏡)

    1. 動態 context:MCP server 每次 tool call 都根據 agent 安全角色和環境配置「動態」回傳 context — 「the security role of the authenticated user for the agent determines which objects are returned in the view model」(原文)
    2. 角色限制 = scope 限制:Agent 只看到自己角色能存取的 menu / entities / API,既是安全也是 prompt 效率(context 不會塞太多無關資訊)
    3. Allowed MCP Clients:Microsoft 預設只允許 Copilot Studio 和 VS Code 兩個 client ID 存取 MCP,其他 agent platform 必須在 Microsoft Entra ID 註冊後加入白名單 — 不是「誰來都能接」

    四、把這個設計搬到 iDempiere

    關鍵 insight:iDempiere REST 的四個 endpoint,剛好對應 Microsoft 的三類工具設計,直接 mapping:

    Microsoft 三分類 iDempiere REST 對應 endpoint 說明
    Data tools api/v1/models/{table} PO CRUD + OData filter,直接套
    Form tools api/v1/windows/{slug}/tabs/{slug} Window/Tab 結構,可模擬「打開視窗、切分頁、設欄位」
    Action tools api/v1/processes/{slug} Process 呼叫(DocAction、報表、自動化)

    結論:你不用設計 MCP server 的工具分類,直接複製 Microsoft 的三分類,把 iDempiere REST 端點包裝進去即可。

    五、MCP 是什麼,為什麼是關鍵

    Model Context Protocol 是 Anthropic 2024/11 發布的開源協議,定義 AI 應用怎麼跟外部資料來源、工具、工作流溝通。官方比喻:「USB-C port for AI applications」。[來源]

    5.1 為什麼是 ERP × AI 的關鍵

    • 標準協議,一次寫多處用:同一個 MCP server 可以同時被 Claude Desktop / Claude Code / Cursor / VS Code / ChatGPT 接([來源])
    • 不是 prompt engineering 的小聰明,是基礎建設層
    • 已成 industry standard:Anthropic / OpenAI / Microsoft 都採納

    5.2 寫 MCP server 的工具(2026/5 驗證)

    • Python SDK:modelcontextprotocol/python-sdk v1.x stable(v2 pre-alpha 開發中)
    • 安裝:uv add "mcp[cli]"pip install "mcp[cli]"
    • Transport:stdio、SSE、Streamable HTTP 三種
    • 認證:OAuth 2.1 resource server 標準

    六、實作範例:iDempiere MCP server v0

    下面是用 FastMCP + httpx 實作的最小可行版本,展示三類工具的骨架。注意:這是教學範例,production 版需要加上錯誤處理、重試、token refresh、審計 log 等。

    # idempiere_mcp_server.py
    from mcp.server.fastmcp import FastMCP
    import httpx
    from typing import Optional
    
    mcp = FastMCP("iDempiere-MCP")
    IDEMPIERE_BASE = "https://idempiere.example.com/api/v1"
    
    # ───────── Auth ─────────
    @mcp.tool()
    async def authenticate(
        username: str,
        password: str,
        client_id: int,
        role_id: int,
        organization_id: int = 0,
        warehouse_id: int = 0,
        language: str = "en_US"
    ) -> dict:
        """One-shot authentication with all parameters.
        Returns session token valid for 1 hour."""
        async with httpx.AsyncClient() as client:
            resp = await client.post(
                f"{IDEMPIERE_BASE}/auth/tokens",
                json={
                    "userName": username,
                    "password": password,
                    "parameters": {
                        "clientId": client_id,
                        "roleId": role_id,
                        "organizationId": organization_id,
                        "warehouseId": warehouse_id,
                        "language": language,
                    }
                }
            )
            resp.raise_for_status()
        return resp.json()
    
    # ───────── Data Tools (OData CRUD) ─────────
    @mcp.tool()
    async def query_records(
        token: str,
        table_name: str,
        filter_expr: Optional[str] = None,
        top: int = 50
    ) -> dict:
        """Query iDempiere PO records via OData.
    
        Filter examples (note iDempiere uses 'neq' not 'ne'):
          - "IsCustomer eq true and contains(Name, 'Acme')"
          - "Created gt 2026-04-01T00:00:00Z"
        """
        params = {"$top": top}
        if filter_expr:
            params["$filter"] = filter_expr
        async with httpx.AsyncClient() as client:
            resp = await client.get(
                f"{IDEMPIERE_BASE}/models/{table_name}",
                params=params,
                headers={"Authorization": f"Bearer {token}"}
            )
            resp.raise_for_status()
        return resp.json()
    
    @mcp.tool()
    async def create_record(token: str, table_name: str, data: dict) -> dict:
        """Create a PO record. Caller must include all mandatory fields.
        Tip: query AD_Column WHERE IsMandatory='Y' to discover them first."""
        async with httpx.AsyncClient() as client:
            resp = await client.post(
                f"{IDEMPIERE_BASE}/models/{table_name}",
                json=data,
                headers={"Authorization": f"Bearer {token}"}
            )
            resp.raise_for_status()
        return resp.json()
    
    # ───────── Action Tools (Process call) ─────────
    @mcp.tool()
    async def run_process(
        token: str,
        process_slug: str,
        parameters: dict
    ) -> dict:
        """Execute an iDempiere Process (e.g. DocAction, scheduled job, report).
    
        Parameters must be FLAT top-level keys, NOT a 'parameters' array:
          Correct:  {"StatementYear": 2026, "StatementPeriod": "2"}
          Wrong:    {"parameters": [{"parameterName": ..., "value": ...}]}
        """
        async with httpx.AsyncClient() as client:
            resp = await client.post(
                f"{IDEMPIERE_BASE}/processes/{process_slug}",
                json=parameters,
                headers={"Authorization": f"Bearer {token}"}
            )
            resp.raise_for_status()
        return resp.json()
    
    if __name__ == "__main__":
        mcp.run(transport="streamable-http")

    這支 script 跑起來後,任何支援 MCP 的 client(Claude Desktop / Claude Code / Cursor / VS Code)都可以連到 http://localhost:8000 並使用上述工具。

    6.1 範例對話(架構驗證)

    員工(在 chat 工具中):
      「幫我查最近 10 筆訂單金額大於 100 萬的客戶」
    
    AI agent(透過 MCP 自動執行):
      1. authenticate(...) → 拿到 session token
      2. query_records(
           token=...,
           table_name="C_Order",
           filter_expr="GrandTotal gt 1000000",
           top=10
         )
      3. 解析結果,回給員工
    
    員工看到:
      「最近 10 筆大於 100 萬的訂單列表如下:...」

    注意:第 1 步的 authenticate 只執行一次,session token 1 小時有效,後續 query 都用同一個 token。

    七、整合進腦子系統:雙軌架構

    員工 chat app (LINE / Mattermost / Telegram / Slack)
        ↓
    Chat-native Agent (QwenPaw) 或 Coding Agent (Claude Code)
        │
        ├─ LLM 流量 ───→ 公司 LLM Gateway (LiteLLM + Portkey)
        │                ├─ 分級/脫敏/路由
        │                └─ → 雲端 frontier 或本地 Ollama
        │
        └─ Tool calls ─→ iDempiere MCP Server (自製)
                          ├─ OAuth 2.1 / Allowed Clients 白名單
                          ├─ Data tools (OData CRUD)
                          ├─ Form tools (Window/Tab 互動)
                          ├─ Action tools (Process call)
                          └─ Audit log → SIEM
                          ↓
                      iDempiere REST API (api/v1/*)
                          ↓ (內建 AD_Role 過濾)
                      iDempiere PostgreSQL

    關鍵設計:

    • LLM Gateway 跟 MCP Server 是兩條平行軌道:Gateway 管 prompt,MCP 管 tool calls。兩者都要 audit log,可獨立縱深防禦
    • 權限不重複設計:iDempiere 內建 AD_Role 直接當權限層,MCP server 帶 user 的 token 進去,iDempiere 自動套 role 過濾資料 — 不用自寫 ABAC 規則
    • Allowed MCP Clients 白名單:借鏡 Microsoft 設計,只允許特定 agent platform 接 MCP server,不是「誰來都能接」

    八、權限層的對應(這是最大紅利)

    員工從 chat app 問問題時的完整權限路徑:

    1. 員工 LINE/Slack ID → Agent 認 ALLOWED_USERS 白名單
    2. Agent → MCP Server,帶員工的 iDempiere session token
    3. MCP Server → iDempiere REST,帶 token
    4. iDempiere 自動套員工的 AD_Role 過濾資料:
       - 業務員角色 → 只看自己的 SalesRep 訂單
       - CFO 角色 → 看全公司
       - RD 角色 → 完全看不到業務資料
    5. 回應只含「員工角色應該看到」的資料

    iDempiere 30 年累積的 AD_Role / AD_Window_Access / AD_Column 權限設計直接拿來用。這比自己在 Gateway 寫 ABAC 簡單一個量級

    九、為什麼這比 Dynamics 365 / NetSuite MCP server 適合中小規模製造業

    特性 Dynamics 365 ERP MCP iDempiere + 自製 MCP
    License Microsoft 訂閱 + Copilot 點數 GPLv2 開源
    Hosting Cloud only(Tier 2+) self-host / air-gapped 可
    Tool 計費 0.1 Copilot Credits per tool call(非 Copilot Studio 環境) 0(自架)
    A 級資料 需透過 Cloud,法規場景受限 完全本地處理
    客製化 透過 ICustomAPI + AI tool framework 直接改 plugin / 加 process

    對製造業中小集團、要 air-gapped 的法規場景、預算有限的公司:iDempiere + 自製 MCP server 是唯一既可離線又能整合 AI 的開源路徑

    十、工程藍圖:漸進式 v0 → v1 → v2

    v0:Read-only Data Tools(2-4 週,1 RD)

    • 實作 authenticate + query_records(本文範例)
    • 支援 5-10 個常用 table:C_BPartner、C_Order、M_Product、M_InOut、M_Movement、AD_User、R_Request 等
    • OData filter 支援 eq / neq / contains / gt / lt
    • 串接 Claude Desktop 或 Claude Code 測試

    v1:加入 Action Tools(2-4 週)

    • 實作 run_process(DocAction、報表、自動化)
    • 實作 create_record / update_record(POST/PUT)
    • 處理 mandatory field 偵測(自動查 AD_Column WHERE IsMandatory=’Y’)
    • token 自動 refresh(1 小時過期)
    • 串接公司 LLM Gateway,流量都過 audit

    v2:Form Tools + 進階 Window 互動(4-8 週)

    • 包裝 api/v1/windows/{slug}/tabs/{slug}
    • 讓 AI 模擬「打開視窗、切分頁、設欄位、按按鈕」
    • 處理複雜流程(發票核銷、應收沖帳等)
    • 整合 Allowed MCP Clients 白名單機制

    對中小企業:v0 可能就夠用 80%。對中大型集團:v0 → v1 → v2 漸進式投資,12-16 週完整版可上線。

    十一、結語:把 AI 變成 ERP 的執行單元

    前四篇腦子系統的 AI 仍然是「跟業務資料分開的工具」 — 員工問問題,AI 回答。

    加上 iDempiere MCP Server,AI 變成能直接動 ERP 的執行單元:查訂單、開請款單、跑 process、生報表。員工從 chat app 一句話完成原本要打開 ERP 點 5 個選單的工作。

    這才是「AI 時代不做 UI,做給 AI 安全取資料的入口」的真實落地。RD 不再被 UI 工單吃掉,而 80% 不寫 code 的員工終於能用一句中文操作 ERP。

    對企業 IT 主管的具體下一步:

    1. 裝 bxservice/idempiere-rest plugin 到既有 iDempiere(若還沒)
    2. 用 Postman 測 4 個主要 endpoint(repo 內有 collection)
    3. 用本文 v0 範例寫 MCP server,跑在開發機 localhost
    4. 掛到 Claude Desktop / Claude Code 試用,驗證權限層運作
    5. 確認可用後,搬上公司內網,接入 LLM Gateway

    延伸閱讀:腦子系統四部曲 + 本篇

    可運作的 Reference Links(2026/5 撰文時驗證)

    iDempiere 官方資源

    MCP 官方資源

    業界 ERP MCP server 參考

    OData 標準