はじめに
Anthropicが2026年4月22日に公開した記事で、本番エージェントとMCPサーバーの設計パターンが整理されていました。
Building agents that reach production systems with MCP
この記事の中で、特に実装者が気をつけるべきだと思ったのがこれです。
MCPサーバーは、APIを1対1でラップするだけでは弱い。ツールはエンドポイントではなくintent単位で設計する。
この記事では、この考え方を「実装前に見るチェックリスト」に落とします。
私は FORMLOVA というフォームサービスを作っています。FORMLOVAでは、フォーム作成だけではなく、公開後の回答確認、営業メール分類、分析、通知、ワークフローまでをMCP経由で扱えるようにしています。
その実装で学んだことをベースに、既存APIをMCP化するときに見落としやすいポイントをまとめます。
チェックリスト全体
先に結論です。
MCPサーバーを作る前に、最低限これを確認するとよいです。
- API名ではなく、ユーザーの作業単位でツールを切っているか
- モデルに毎回やらせている後処理を、サーバー側に寄せられないか
- ドメインルールをプロンプトではなくツール仕様に埋め込めているか
- ラベルやステータスを表示用で終わらせず、次の操作に接続しているか
- AIの判断を人間が上書きでき、その上書きを守れるか
- 返すべきものがテキストだけで十分か、UIや確認ステップが必要か
以下、順番に見ていきます。
1. エンドポイントをそのままツールにしない
既存APIをMCP化するとき、最初にやりがちな設計です。
server.registerTool("list_responses", ...);
server.registerTool("get_response", ...);
server.registerTool("update_response", ...);
server.registerTool("delete_response", ...);
server.registerTool("export_responses", ...);
これは必要な場面もあります。
ただし、この粒度だけだと、エージェントは毎回APIの組み合わせを考える必要があります。
たとえばユーザーがこう言ったとします。
営業メールを除いて今月の問い合わせ数を見たい
APIラップ型だと、エージェントはだいたいこう動きます。
1. list_responses を呼ぶ
2. 日付で絞る
3. spam_label を読む
4. sales を除外する
5. 件数を集計する
デモなら動きます。
でも本番では、ページング、権限、未分類データ、手動修正済みラベル、失敗時処理、監査ログが入ります。
これを毎回モデルに考えさせるのは不安定です。
Good: intentをパラメータとして表現する
FORMLOVAでは、回答取得や分析系のツールに exclude_sales を持たせています。
server.registerTool("get_responses", {
inputSchema: {
form_id: z.number().int(),
limit: z.number().int().min(1).max(100).default(50),
exclude_sales: z.boolean().default(false),
},
});
server.registerTool("get_form_analytics", {
inputSchema: {
form_id: z.number().int(),
exclude_sales: z.boolean().default(false),
},
});
こうすると、ユーザーの発話はそのままツール引数に落ちます。
営業メールを除いて分析して
{
"exclude_sales": true
}
小さいパラメータですが、MCP設計としては大事です。
「営業を除く」という業務上の意図が、モデルの後処理ではなく、サーバー側の仕様になります。
2. 除外ルールをモデルに任せない
exclude_sales=true の中身は、単純なようで意外と重要です。
やってはいけないのは、モデル側にこう指示することです。
取得した回答から営業メールっぽいものを除外してください。
これだと、毎回判断が揺れます。
サーバー側では、ルールを固定できます。
if (exclude_sales) {
query = query.or("spam_label.is.null,spam_label.neq.sales");
}
このルールでは、sales だけを除外します。
suspicious は残します。null も残します。
理由は、判別困難な回答や未分類回答を勝手に消すと、本物の問い合わせを見落とす可能性があるからです。
こういう判断は、MCPツールの説明文やプロンプトにだけ書くより、サーバー側のクエリとして固定したほうが安定します。
チェックポイント
MCPツールの中に、次のような暗黙ルールがないか確認します。
- どのステータスを含めるか
- どのラベルを除外するか
- 未分類をどう扱うか
- 手動修正を優先するか
- エラー時に空配列を返すのか、明示的に失敗を返すのか
これらをモデルの推論に任せるほど、本番ではブレます。
3. ラベルは表示用ではなく、操作条件にする
LLM分類を入れると、ついラベル表示で満足しがちです。
type SpamLabel = "legitimate" | "sales" | "suspicious";
ダッシュボードで sales バッジを出す。
これだけだと、分類は見た目の情報で終わります。
MCPサーバーで扱うなら、ラベルを次の操作に接続します。
legitimate -> 分析に使う / 通知する
sales -> 分析から除外する / 営業除外リストへ送る
suspicious -> 人間の確認キューに回す
FORMLOVAでは、分類結果がワークフローのトリガーにもなります。
await executeWorkflows(formId, "response.classified", {
form_id: formId,
response_id: responseId,
spam_score: spamResult.score,
spam_label: spamResult.label,
});
これで、分類後に条件分岐できます。
when response.classified
if spam_label == "legitimate"
then Slackへ通知
when response.classified
if spam_label == "suspicious"
then 担当者へ確認依頼
when response.classified
if spam_label == "sales"
then 通知しない
ここまで来ると、MCPサーバーが返しているのは「回答データ」ではありません。
回答を次の業務に流すための状態です。
--
4. AIの判断は、人間が上書きできるようにする
LLM分類を本番で使うときに、避けたい失敗があります。
ユーザーが誤判定を直したのに、後から自動分類が再実行されて元に戻ることです。
これは一度起きると信頼を失います。
FORMLOVAでは、ラベルの出所を持たせています。
type LabelSource = "auto" | "manual";
自動分類のUPDATEでは、手動修正済みの行を対象外にします。
await db
.from("responses")
.update({
spam_label: spamResult.label,
spam_score: spamResult.score,
spam_label_source: "auto",
spam_classified_at: new Date().toISOString(),
})
.eq("id", responseId)
.or("spam_label_source.is.null,spam_label_source.eq.auto");
手動修正時は manual にします。
await db
.from("responses")
.update({
spam_label: newLabel,
spam_label_source: "manual",
spam_classified_at: new Date().toISOString(),
})
.eq("id", responseId);
MCPツール側では、update_response に spam_label を持たせます。
server.registerTool("update_response", {
inputSchema: {
response_id: z.number().int(),
status: z.enum(["new", "in_progress", "resolved", "spam"]).optional(),
notes: z.string().optional(),
tags: z.array(z.string().max(50)).max(20).optional(),
spam_label: z.enum(["legitimate", "sales", "suspicious"]).optional(),
},
});
ユーザーの発話はこうです。
この回答は営業ではなく正当な問い合わせなので、分類を直して
ここで spam_label_source = "manual" に切り替わるので、以後の自動処理から守れます。
チェックポイント
分類や推論をMCPに載せるなら、必ず考えてください。
- 人間は結果を直せるか
- 直した結果は自動処理から守られるか
- 直した理由をメモできるか
- 後続の分析やワークフローは、修正済みラベルを見ているか
「AIが当てる」より、「外れても運用が壊れない」のほうが重要です。
5. 破壊的操作だけでなく、自動実行も確認対象にする
MCPサーバーでは、削除やメール送信のような破壊的操作に確認を入れるのは自然です。
ただ、業務系のMCPでは「保存した後に自動で動く設定」も注意が必要です。
たとえばワークフローです。
server.registerTool("set_workflow", {
inputSchema: {
form_id: z.number().int(),
name: z.string().min(1),
trigger_type: z.enum([
"response.created",
"response.updated",
"capacity.reached",
"deadline.approaching",
"response.classified",
]),
conditions: z.array(conditionSchema).optional(),
actions: z.array(actionSchema),
},
});
ワークフローは、作成した瞬間には何も起きないかもしれません。
でも次の回答が来たときに、自動でメール送信やWebhookが走ります。
つまり、設定保存そのものは静かでも、後で外部副作用が出ます。
このタイプのツールは、単なる create ではなく「将来の自動実行を作る操作」として扱う必要があります。
実装上のルール
ツール説明に、次のような制約を入れておきます。
Workflows with send_email or webhook actions execute automatically on trigger.
Always summarize trigger, conditions, and actions, then confirm before saving.
加えて、サーバー側でも確認トークンや状態機械で止めるのが理想です。
プロンプトに「確認して」と書くだけだと、モデルが省略する可能性があります。
6. テキスト返却だけで足りるかを確認する
Anthropicの記事では、MCP AppsやElicitationのようなrich semanticsにも触れられています。
実装者目線では、これは「何でもチャットに返せばよいわけではない」と読めます。
たとえば次のものは、テキストよりUIのほうが向いています。
- 回答一覧
- 分類比率
- 公開前チェックリスト
- 誤判定の修正画面
- 分析グラフ
一方で、次のものはチャットが向いています。
- 「営業を除いて分析して」
- 「要確認だけ見せて」
- 「この分類を直して」
- 「本物の問い合わせだけ通知して」
MCPサーバー設計では、この分担を先に考えておくとよいです。
Chat: intent を受け取る
MCP: 意味・制約・権限・実行を担当する
UI: 一覧、比較、確認、修正を担当する
テキストだけで返すと、確認しづらいものがあります。
逆に、UIだけでやると、意図を渡すのが重くなります。
MCPは、その間に置く意味レイヤーとして設計すると使いやすくなります。
7. Skills / Workflow と分けて考える
最後に、MCPだけで全部を背負わないことも大事です。
Anthropicの記事では、MCPとSkillsは補完関係だと説明されています。
MCPは、ツールとデータへのアクセス。
Skillsは、そのツールで仕事を進める手続き知識。
フォーム運用でも同じです。
MCPツールで get_responses や set_workflow を提供できます。でも、セミナー運用でどの順番に自動返信、リマインド、アンケートを組むべきかは、API仕様ではありません。運用の型です。
この部分は、WorkflowやSkillとして別に扱うほうが自然です。
MCPサーバーに全部の知識を詰め込むのではなく、能力と手順を分けます。
MCP = 何ができるか
Workflow = どう進めるか
Skill = どう判断するか
この分け方をしておくと、ツール仕様が肥大化しにくくなります。
まとめ
MCPサーバーは、既存APIをそのまま公開するだけでも作れます。
でも、本番エージェントに使わせるなら、APIラップだけでは足りません。
実装前に、次を確認するとよいです。
- エンドポイントではなく、ユーザーの作業単位でツールを切る
- モデルが毎回やっている後処理を、サーバー側のルールに寄せる
- ラベルやステータスを、次の操作条件にする
- AIの判断を人間が上書きできるようにする
- 手動上書きは自動処理から守る
- 将来の自動実行を作る操作には確認を入れる
- チャットとUIの分担を設計する
- MCPとWorkflow / Skillを分けて考える
MCPサーバーを作るときに考えるべきなのは、「どのAPIを公開するか」だけではありません。
ユーザーの仕事のどの単位を、エージェントに安全に渡せる形にするかです。
同じテーマを、別の角度でも書いています。
- MCPサーバーをAPIラップで終わらせない設計 -- Anthropicの記事をFORMLOVAで読み解く(Zenn)
- AnthropicのMCP記事を読んで、FORMLOVAの設計に確信を持った話(note)
- Anthropicが推奨するMCP設計と、FORMLOVAの思想が重なるところ
- 営業メール自動検知の使い方
FORMLOVAは無料で始められます。MCP経由で、フォーム作成だけでなく公開後の運用まで試せます。
