「今月、結局いくら残りそう?」と聞かれて、粗利の画面と資金繰りの画面を行ったり来たりする。自分たちで作ったSaaSなのに、答えを組み立てるのは毎回人の手でした。そこで、私たちが作っている受託向けのSFA/CRM「Pipelia」を、AIから直接問い合わせられるようにMCP対応にしました。この記事はその実装の記録です。

結論を先に書くと、MCP対応そのものは、既存のサーバーにPOSTのエンドポイントを1本足すだけで動きました。 時間がかかったのはプロトコルではなく、画面と同じ権限で、画面と同じ数字をAIにも返すことのほうです。ここを雑にすると、AIが画面と違う数字で助言したり、画面では伏せている情報をAI経由で読めたりします。

MCP(Model Context Protocol)対応とは、自分のサービスの機能を「AIが呼び出せる道具(ツール)」として、共通の作法で公開することです。概念の整理はSFAをAIにつなぐMCP連携とはに書いたので、ここでは実装側の話だけをします。

この記事の要点

  • MCPサーバーはステートレスなPOSTエンドポイント1本で足りた。常駐プロセスもストリーミングも要らない
  • 手間の大半は「画面と同じ集計式・同じ権限の伏せ方」をAI向けの出口にも揃えることだった
  • 書き込みは「見込み客の追加」と「活動の記録」の2つだけ。削除や送信はツール自体を作らない
MCPのリクエストが通る関門を示した図。AIアプリからのリクエストが、回数制限・トークン照合・プラン確認・ロールでの絞り込みを順に通ってからツール実行に届き、画面と同じ集計が返る流れが確認できる

MCP対応で、AIから何ができるようになったか

PipeliaがAIに公開しているツールは6つです。読み取りが4つ、書き込みが2つです。

ツール返すもの・やること種類ひも付く画面
profit_report案件ごとの売上・外注費・粗利、全体合計、粗利率20%未満の案件読み取り粗利
sales_pipelineステージ別のオープン商談の件数・金額、受注/失注、成約率読み取り商談
prospects_summary営業リストのステータス別件数、接触・返信・顧客化、今日までの再連絡件数読み取り営業リスト
cashflow_summary未回収と外注の未払い、その差引、期限超過の件数と金額読み取り資金繰り
create_prospect営業リストに見込み客を1件追加(会社名は必須)書き込み営業リスト
create_activity活動履歴にメモ・電話・メール・打合せ・訪問を1件記録書き込み活動

読み取りのツールは、明細の生データではなく集計を返します。「粗利が薄い案件はどれ?」「再連絡が溜まっている件数は?」のように、画面を何枚か見て頭の中で合算していた問いに1回で答えるための道具です。逆に「この会社の電話番号は?」のような1件単位の検索はできません。そこは画面で探したほうが速いので、あえて作っていません。

各ツールには「どの画面にひも付くか」を持たせています。これが後で説明する権限の判定に効いてきます。

MCPサーバーの実装——既存のWorkerにエンドポイントを1本

Pipeliaは Cloudflare Workers 上で動いています。MCPサーバーは別サービスとして立てず、既存のWorkerに /api/mcp というPOSTのエンドポイントを1本足しました。

  1. ステートレスにした。 MCPの通信方式(Streamable HTTP)は、サーバー側でセッションを持つこともできます。今回は持たず、リクエストごとに認証して、JSON-RPC 2.0 の応答をそのまま返すだけにしました。常駐プロセス(Durable Objects)もSSEのストリームも使っていません
  2. 扱うメソッドは4つだけ。 initialize(接続時の自己紹介)、tools/list(使えるツールの一覧)、tools/call(実行)、ping。ツールは全件集計なので、1回のリクエストにまとめられる呼び出しは20件までに制限しています
  3. ツールの中身は既存の集計関数を使う。 画面用のAPIが使っている粗利計算や年換算の式を、そのまま呼び出します。MCP用に計算を書き直さないことが、次の節の話につながります

プロトコル部分のコードは150行に満たない量です。MCP対応と聞くと大がかりに聞こえますが、既にAPIを持っているSaaSなら、入口を1本足す作業に近いと思います。

MCP対応でいちばん手間がかかったのは「画面と同じ数字」を返すこと

最初の実装では、MCPのツールの中で集計SQLを独自に書いていました。これが画面とずれました。実際に直したものを挙げます。

  • 継続案件の粗利。 独自に「売上−外注費」を書いていたため、保守などの継続案件が「月額−全期間の外注費」で計算され、粗利ページと符号が変わるほど違う数字をAIに返していました
  • 商談金額の単位。 画面は継続商談を年換算で集計しているのに、MCPだけ素の合計でした。月額5万円の継続商談が、画面では60万円、AIには5万円として渡っていました
  • 営業リストの母数。 フォームからの問い合わせと自分から当たったリードを混ぜて数えていたため、「接触した件数」が「リード件数」を上回る応答が返ることがありました
  • 活動の日付。 AI経由の記録だけ時刻付きのUTCで保存していたため、「最近の活動」の並びでAI経由の記録が常に一番上に来ていました

どれも、エラーにはならず、それらしい数字が返ってくる種類の不具合です。AIはその数字をもとに自信を持って助言するので、桁が違えば助言ごと外れます。MCP用の集計は、画面用と同じ関数を呼ぶ。 独自に書かない、というのが一番の学びでした。

粗利の出し方そのものは粗利管理のはじめ方にまとめています。AIに聞く前に、画面側の定義が決まっていることが前提です。

権限の設計——AIにも「画面と同じ範囲」しか見せない

MCPのエンドポイントは、通常の画面用APIとは別の認証で動かしています。画面はログインのセッション、MCPは個人アクセストークンです。入口が別なぶん、画面用のAPIに付けていた制限が自動では効きません。ここを1つずつ付け直しました。

  • トークンは平文で保存しない。 発行時に一度だけ表示し、DBにはハッシュだけを置きます。発行・一覧・失効は組織のオーナーだけができます
  • 発行した人が今も組織のメンバーかを毎回確認する。 組織を抜けた人のトークンで読み続けられないようにするためです
  • トークンにはロール(権限セット)を必ず割り当てる。 ロールが画面ごとに「非表示・閲覧・編集」を決めていて、読み取りのツールは閲覧以上、書き込みのツールは編集権限がないと一覧にすら出ません。ロールが消えてトークンの割り当てが空になった場合は、何も使えない側に倒しています
  • ツールの中身も画面と同じ粒度で伏せる。 粗利は見せるが案件は見せないロールには、画面でも案件の明細を出していません。MCPの粗利レポートも同じで、合計だけを返し、案件名や企業名は返しません
  • プランと支払い状態も確認する。 MCPはProプランの機能で、Freeでは使えません。支払いが確認できず画面がロックされている組織は、MCPからも使えません

画面と同じ権限で絞るというのは、言い換えると「AIに何を見せるかは、社員に何を見せるかと同じ設計で決める」ということです。権限やログの考え方は受託の顧客情報管理とセキュリティにも書いています。

書き込みをどこまで許すか——作れるのは2種類だけ

AIに書き込みを許すと、入力の手間は減ります。一方で、AIが読んだメールやWebページに紛れた指示(プロンプトインジェクション)で、意図しない操作をされる可能性も生まれます。そこで、危ない操作はツール自体を用意しないことにしました。

公開したもの公開していないもの
見込み客を1件追加一括削除・全削除
活動(メモ・電話・打合せ等)を1件記録メンバー・ロール・課金・組織設定の変更
メールの外部送信

公開した2つにも制限を付けています。

  • IDは必ずサーバー側で採番し、見込み客のステータスはAIに選ばせず組織の既定値を使う
  • AIから追加した見込み客は、流入元が「AI連携」と記録される
  • Freeプランの件数上限(営業リスト100件)は、画面からの追加と同じようにAI経由でも効かせる
  • 書き込みはすべて監査ログに残す
  • 3ヶ月の無料トライアル中は読み取りのツールだけ。書き込みは有料化してから使える

「AIに任せたら重複登録が増えた」という事故は、消せない操作よりずっと小さく済みます。最初に開ける口は、間違っても取り返しがつくものだけにしておくのが無難です。

MCP対応したSFAの使いどころ

6つのツールで実際に答えられるのは、たとえば次のような問いです。

  1. 「粗利率が20%を切っている案件は?」 — 粗利レポートの薄利案件を、低い順に返します。Web制作の粗利率の目安と照らして、どこから手を付けるかを決める材料になります
  2. 「今月の未回収と、外注への未払いは差し引きいくら?」 — 資金繰りのサマリーから、期限を過ぎたものも含めて返します
  3. 「今日までに再連絡すべき見込み客は何件?」 — 営業リスト画面の要対応と同じ条件で数えます
  4. 「さっきのA社との打合せ、メモしておいて」 — 活動の記録として1件追加します

向いていないのは、1件ずつの詳細を引く問いと、集計の切り口を細かく変える問いです。「去年の同じ月と比べて」のような比較は、今のツールでは返せません。ツールを増やせば答えられる範囲は広がりますが、増やすたびに「画面と同じ数字・同じ権限」を揃える作業が付いてくるので、今は数を絞っています。

同じくMCPを使って、このブログの執筆から公開までを回している構成はCMSとMCPでブログ運用を自動化した構成に書きました。こちらは「使う側」の話です。

自分のSaaSをMCP対応にするなら先に決める4つ

  1. どの画面の権限で判定するか。 ツールごとにひも付く画面を決めておくと、既存のロール設計がそのまま使えます
  2. 集計は既存の関数を呼ぶか。 独自に書くと、画面とAIで数字がずれます
  3. 書き込みで何を作らないか。 公開するものより、公開しないものの一覧を先に書くほうが漏れません
  4. 別の入口で抜ける制限はないか。 支払いロック、プランの制限、件数上限、レート制限。画面用のAPIの手前に付けていたものは、MCPの入口にも付け直す必要があります

PipeliaのAI連携はProプランで使えます。画面そのものはデモで、登録なしに触れます。

Pipeliaの粗利画面。案件別の粗利率と月次推移、粗利率20%未満の警告が確認でき、MCPの粗利レポートはこの画面と同じ集計を返す

登録なしで触れる公開デモを、共有環境のまま壊させない仕組みは登録不要のデモを作った理由と実装にまとめています。

よくある質問

MCPサーバーは別のサーバーとして立てる必要がありますか?

必須ではありません。Pipeliaでは既存のWorkerにPOSTのエンドポイントを1本足しただけで、セッションも持たないステートレスな作りにしています。既にAPIと認証の仕組みを持っているSaaSなら、入口を1本追加する作業に近く、プロトコル部分のコードも150行ほどに収まりました。

AIに書き込みを許しても大丈夫ですか?

許す範囲を「間違っても取り返しがつく操作」に限れば、実用的な範囲に収められます。Pipeliaでは見込み客の追加と活動の記録だけを公開し、削除・メール送信・設定変更はツール自体を作っていません。さらに書き込みは監査ログに残し、無料トライアル中は読み取りだけにしています。

MCP対応で一番気をつけることは何ですか?

AIに返す数字と権限を、画面と完全に揃えることです。MCPの入口は画面用のAPIと別になることが多く、集計式や権限の判定を別に書くと、画面と違う数字や、画面で伏せている情報がAIに渡ります。集計は画面と同じ関数を呼び、権限は画面と同じロールで判定するのが安全です。