MY HATCH

Odoo × Claude(MCP)で変わる業務の景色 — 小さな MCP サーバーを書いて試した

Claude などの AI から Odoo を「道具」として呼ぶ仕組みが MCP です。標準ライブラリだけで小さな MCP サーバーを書き、見本のデータの Odoo 19 Community で見積の一覧から見積の作成まで呼び、権限で止まることも確かめました。

7分
最終更新: 2026年10月26日
Odoo CommunityMCPClaudeAI 連携API キー権限
目次

目次

このページには目次がありません

Odoo × Claude(MCP)で変わる業務の景色 — 小さな MCP サーバーを書いて試した

「先月の未入金を出して」「この得意先に見積を作っておいて」。こうした頼みごとを AI に投げて、AI が業務システムを呼ぶ。その「呼び口」の決まりが MCP(Model Context Protocol)です。

この記事では、外部の MCP サーバーを入れずに、当社で小さな MCP サーバーを書き、見本のデータの入った Odoo 19 Community で実際に呼びました。何が呼べて、どこで止まるかを、件数で確かめています。

MCP とは:AI に「道具の一覧」を渡す決まり

MCP は、AI のアプリ(クライアント)と、業務システムの前に立つ小さなプログラム(サーバー)とのやり取りの決まりです。

  • サーバーは 道具の一覧(名前・説明・受け取る値の形)を出す
  • AI のアプリはその一覧を見て、必要な道具を 名前と値を添えて呼ぶ
  • サーバーは業務システムを呼び、結果を返す

やり取りは JSON-RPC 2.0 という形の短い文で、手元で動かすときは 標準入出力に 1 行ずつ 流します。AI が何をするかは「一覧にある道具を呼べる」ところまでで、道具に無いことはできません。どの道具を置くかが、そのまま AI に任せる範囲になります。

当社が書いた小さな MCP サーバー

Python の標準ライブラリ(json・sys・urllib.request)だけで書きました。外部の部品は入れていません。行数は 219 行です。Odoo 19 には、API キーで呼べる /json/2/<モデル>/<メソッド> という呼び口があり、サーバーはこれを使います。

置いた道具は 5 個です。

道具中身種類
list_quotations確定前の見積の一覧読む
list_sales_orders確定した受注の一覧(請求の状態つき)読む
stock_on_hand品目ごとの手持ちと見込みの数読む
unpaid_invoices未入金の請求書(支払期日の順)読む
create_quotation見積を下書きで作る(確定・送付はしない)作る

要は、Odoo を呼ぶところです。誰として動くかは API キーの持ち主で決まるので、Odoo の権限がそのまま効きます。

def odoo(model, method, **kwargs):
    """POST /json/2/<model>/<method>。引数は名前つきで渡す(Odoo 19 の JSON-2 API)。"""
    kwargs.setdefault("context", CTX)
    req = urllib.request.Request(
        f"{ODOO_URL}/json/2/{model}/{method}",
        data=json.dumps(kwargs).encode("utf-8"),
        headers={
            "Content-Type": "application/json; charset=utf-8",
            "Authorization": f"bearer {API_KEY}",   # キーは環境変数から
            "X-Odoo-Database": ODOO_DB,
        },
        method="POST",
    )
    ...

見積を作る道具は、得意先と品目を 1 件に絞れなければ作らずに断ります。曖昧なまま作らないためです。

def create_quotation(customer, lines, note=""):
    """見積を下書きで 1 件作る。確定(受注にする)はしない。"""
    partners = odoo("res.partner", "search_read",
                    domain=[["name", "ilike", customer], ["customer_rank", ">", 0]],
                    fields=["id", "name"], limit=2)
    if len(partners) != 1:
        raise OdooError(f"得意先「{customer}」が {len(partners)} 件見つかりました。1 件に絞れる名前で。")
    ...
    ids = odoo("sale.order", "create", vals_list=[vals])

MCP の側は、initialize(あいさつ)・tools/list(道具の一覧)・tools/call(道具を呼ぶ)の 3 つを手で書けば足ります。道具の中で起きた失敗は、通信の失敗とは分けて isError: true で返します。AI のアプリが、断られた理由を読んで次の手を選べるようにするためです。

実際に呼んだやり取り

検証用のクライアントからサーバーを子プロセスで起こし、1 行ずつ送りました。API キーは環境変数でだけ渡しています(下では伏せています)。

$ ODOO_URL=http://localhost:8079 ODOO_DB=sb_mcp ODOO_API_KEY=**** python odoo_mcp.py

→ {"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-11-25",...},"id":1}
← {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-11-25",
   "capabilities":{"tools":{"listChanged":false}},"serverInfo":{"name":"odoo-mcp-prototype",...}}}

→ {"jsonrpc":"2.0","method":"tools/list","id":2}
← {"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"list_quotations","title":"見積の一覧",...},
   {"name":"list_sales_orders",...},{"name":"stock_on_hand",...},{"name":"unpaid_invoices",...},
   {"name":"create_quotation","title":"見積を作る",...}]}}

→ {"jsonrpc":"2.0","method":"tools/call","params":{"name":"unpaid_invoices","arguments":{}},"id":6}
← {"jsonrpc":"2.0","id":6,"result":{"content":[{"type":"text","text":"[
   {\"番号\":\"INV/2026/00003\",\"得意先\":\"青空スーパー\",\"支払期日\":\"2026-10-16\",\"未入金\":128920.0},
   {\"番号\":\"INV/2026/00004\",\"得意先\":\"ほしぞら機工株式会社\",\"支払期日\":\"2026-10-24\",\"未入金\":133100.0},
   {\"番号\":\"INV/2026/00005\",\"得意先\":\"つばさ物産株式会社\",\"支払期日\":\"2026-10-30\",\"未入金\":462000.0}]"}],
   "isError":false}}

→ {"jsonrpc":"2.0","method":"tools/call","params":{"name":"create_quotation","arguments":{
   "customer":"ほしぞら機工","lines":[{"product":"FG-CU100","qty":2},{"product":"SV-INST","qty":1}],
   "note":"MCP の試作から作った見積"}},"id":7}
← {"jsonrpc":"2.0","id":7,"result":{"content":[{"type":"text","text":
   "{\"番号\":\"S00012\",\"得意先\":\"ほしぞら機工株式会社\",\"合計\":133100.0,\"状態\":\"draft\",\"id\":12}"}],
   "isError":false}}

読む道具が返した件数は、見積 3 件・受注 8 件・未入金の請求書 3 件で、どれも DB を直接数えた件数と一致しました。見積を作る道具を呼ぶ前と後で数えると、見積は 3 件 → 4 件、販売オーダの総数は 11 件 → 12 件 に増えています。

Odoo の画面で開くと、MCP から作った見積が下書きのまま入っていました。明細は 2 行、税抜 121,000 円、合計 133,100 円です。

見積の画面。番号 S00012、顧客ほしぞら機工株式会社、明細に制御ユニット CU-100 が 2 個と設置作業が 1 件、状態は「見積」のまま

MCP の道具で作った見積。確定はされていない(画面は見本のデータです)

見積の一覧。いちばん上に S00012 が「見積」の状態で並ぶ

見積の一覧の先頭に入った(画面は見本のデータです)

右側の記録には、作った人として API キーの持ち主 の名前が出ます。AI 用の利用者を分けておけば、誰が画面で作り、何が AI 経由で作られたかを見分けられます。

権限で止まる

Odoo の販売・購買には「読むだけ」の役割が標準でありません。そこで、販売・購買・請求・在庫の記録を 読むだけ の役割を作り(読み取り 8 個)、その利用者の API キーで同じ道具を呼びました。

→ {"jsonrpc":"2.0","method":"tools/call","params":{"name":"create_quotation",...},"id":7}
← {"jsonrpc":"2.0","id":7,"result":{"content":[{"type":"text","text":
   "Odoo が 403 で断りました。odoo.exceptions.AccessError:
    あなたは '販売オーダ' (sale.order) レコードの作成権がありません。
    この操作は、次のグループで許可されています: - 販売/管理者 - 販売/ユーザ: 自分のドキュメントのみ"}],
   "isError":true}}

見積は作られず、販売オーダの総数は 12 件のままでした。止めたのは MCP サーバーではなく Odoo の権限 です。サーバーの側で道具を隠し忘れても、キーの持ち主に権限が無ければ書けません。

もう 1 つ分かったことがあります。読むだけの役割で、最初は 在庫の道具も 403 で止まりました。在庫の数を出すとき、Odoo は部品表(mrp.bom)も読みに行くためです。部品表と明細の読み取りを 2 個 足すと通りました。役割は、紙の上で決めるより、道具を通して呼んでみて 足りない読み取りを見つける方が確実です。

なお、存在しない API キーで呼ぶと 401 で断られます。

Claude などの AI から使うときの決まり

この記事の作業では、Claude のアプリなど実際の AI からはつないでいません。検証用のクライアントから MCP の通りに呼んだところまでです。AI のアプリからつなぐときは、アプリの設定にこのサーバーを起こす命令を登録し、AI はそこに並んだ道具を呼びます。そのときの決まりを、試作から分かったこととしてまとめます。

  • 読むだけの利用者で始める。AI 用の利用者を別に作り、まずは読む役割だけを付けます。書く道具を足すのは、読む道具の答えが業務で使えると分かってから
  • 書く道具は下書きまで。見積・発注・請求書の確定、メールの送付、入金の消込は道具に置きません。確定は人が画面で行います
  • キーは環境変数で渡す。コードや設定のファイルにキーを書きません。Odoo の API キーには期限を付けられ、利用者の画面から消せます
  • AI 用の利用者の名前を分ける。作られた記録に、誰の名前で入ったかが残ります
  • 答えを Odoo の数と突き合わせる。道具の答えは、最初のうちは画面の一覧の件数と比べます。この試作でも、件数は DB を直接数えて確かめました
  • 仕様の版を見ておく。この試作は initialize で始まる版(2025-11-25)の形で書きました。最新の版(2026-07-28)はこの手続きを持たず、1 通ごとに版を添える形です。AI のアプリがどちらで話すかは、つなぐ前に確かめます

当社が用意する物

  • 業務に合わせた道具の一覧:どの問いに答え、どこまで作るかを、業務の流れから決めて道具にします
  • 読むだけの役割:Odoo の権限で、AI 用の利用者に渡す読み取りの範囲を作り、道具を通して試します
  • AI 用の利用者とキーの運用:利用者の分け方・キーの期限・止め方を用意します
  • 呼ばれた道具の記録:いつ・どの道具が・どの値で呼ばれたかを残す仕組みを足します
  • 新しい版の MCP への対応:今回の試作は古い形だけに応じています。最新の版の形も足します

試作のサーバーのために、Odoo へアドオンは何も足していません(道具で作った見積の下書きは、Odoo の標準の見積として残ります)。Odoo 19 Community の標準の呼び口と権限だけで動いています。自社の業務でどの道具から始めるかは、「本格拡張はアドオンで」とあわせてご相談ください。

「作り方」の記事をもっと見る

ERP を、借りずに所有する。

Odoo Community を日本の業務に合わせ、ソース一式を人数無制限でお渡しします。

相談する(無料)