結論:本記事では「MCP Elicitation実装ガイド」を具体的なステップとコピペ可能な実例つきで解説します。Elicitationを正しく設計すれば、AIエージェントの自律処理フロー内で人間の承認を安全に挟み、誤送信・誤処理のリスクを大幅に低減できます。
対象読者:MCPサーバーの本番導入を検討している実務担当者・意思決定者。AIエージェントによる業務自動化で「どこに人間の確認を入れるか」を設計したい方。
読了後にできること:Elicitationの仕様・実装パターン・よくある失敗を理解し、自社ワークフローへの組み込み判断と設計レビューに着手できます。
MCP Elicitationは、MCPサーバーが処理の途中で「ユーザーに追加確認したい情報」をクライアント経由で求めるための仕組みです。AIエージェントに予約、申請、社内承認、設定変更のようなワークフローを任せると、途中で「この内容で送信してよいか」「不足しているIDは何か」と確認したくなる場面が必ず出ます。
本稿では、2026年5月11日時点で公開されているMCP公式仕様のElicitationを一次情報として確認し、実装時に見るべきポイントを整理します。MCP全体像はMCPとは?AIエージェントの新標準規格を解説、認可設計はMCP認可の実装ガイドも合わせて参照してください。
MCP Elicitationとは何か
公式仕様では、Elicitationは「サーバーがクライアントを通じてユーザーから追加情報を取得する標準化された方法」と位置づけられています。重要なのは、ユーザーとの対話をサーバーが直接握るのではなく、クライアントがUI・権限・データ共有を制御する点です。
たとえば経費精算エージェントが領収書を読み取り、最後に「部門コードを入力してください」と聞く。あるいはGitHub連携サーバーが「どのリポジトリにIssueを作成しますか」と選択肢を出す。これらはツール呼び出しの単なる引数不足ではなく、人間の判断を安全に挟む設計課題です。
仕様ページには、Elicitationがこのバージョンで新しく導入され、将来のプロトコルで設計が進化する可能性があるとも明記されています。したがって本番導入では、仕様バージョンとクライアント対応状況を固定して検証するのが現実的です。
Elicitation導入で得られるROI
ある中堅製造業(従業員約500名)では、受発注確認のワークフローをAIエージェント化した際にElicitationを組み込みました。従来はメール往復で承認者の確認に平均30分かかっていた工程が、Elicitation経由のフォーム確認で平均5分に短縮。月間約200件の発注処理で、担当者の確認待ち時間が月あたり約83時間削減されたと報告されています。
以下は、Elicitation対応を初期化時に宣言するクライアント側の設定例です。
{
"jsonrpc": "2.0",
"id": 0,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {
"elicitation": {}
},
"clientInfo": {
"name": "MyWorkflowClient",
"version": "1.0.0"
}
}
}
実装前に確認する3つの前提
第1に、クライアントは初期化時にelicitation capabilityを宣言する必要があります。サーバー側は、相手クライアントがこの機能を持つ前提で一方的にelicitation/createを送るのではなく、初期化で得たcapabilitiesに応じて通常のツール引数、リソース、またはエラー応答にフォールバックできる設計にします。
第2に、Elicitationは機密情報の収集に使うべきではありません。公式仕様は、サーバーがElicitationでセンシティブ情報を要求してはならないとしています。パスワード、長期APIキー、秘密鍵、個人番号のような情報は、OAuthや既存の認可フロー、管理画面の安全な設定領域で扱うべきです。
第3に、クライアントUIは「どのサーバーが、何のために、どのデータを求めているか」をユーザーに明示する必要があります。これは単なるUXではなく、AIエージェントが外部ツールを連鎖実行する時代のセキュリティ境界です。関連するMCPの公開・発見設計はMCP Registry完全ガイドで詳しく扱っています。
前提チェックの実例:フォールバック設計
実際のプロジェクトでは、Elicitation非対応のクライアントが混在するケースが頻発します。SaaS企業A社(社員数300名)では、社内ツール連携で3種類のMCPクライアントを併用しており、うち1つがElicitation未対応でした。フォールバック未実装のまま展開した結果、未対応クライアントから呼び出されたツールが約15%のリクエストでエラー終了し、復旧対応に延べ12時間を費やしました。
以下は、サーバー側でcapabilityを判定してフォールバックするTypeScriptの実装例です。
// サーバー側:capability判定とフォールバック
async function requestApprover(
server: McpServer,
clientCapabilities: ClientCapabilities
): Promise<string> {
if (clientCapabilities.elicitation) {
// Elicitation対応クライアントの場合
const result = await server.elicitation.create({
message: "承認者のメールアドレスを入力してください",
requestedSchema: {
type: "object",
properties: {
approver: { type: "string", format: "email", title: "承認者" }
},
required: ["approver"]
}
});
if (result.action === "accept") {
return result.content.approver;
}
throw new Error("ユーザーが承認者の指定を拒否しました");
} else {
// フォールバック:ツール引数から取得、または既定値を使用
return getDefaultApproverFromConfig();
}
}
elicitation/createの基本構造
プロトコル上のメソッド名はelicitation/createです。リクエストには、ユーザーに表示するmessageと、期待する入力構造を表すrequestedSchemaを含めます。仕様ではJSON-RPC 2.0のメッセージとして例示されています。
{
"jsonrpc": "2.0",
"id": 1,
"method": "elicitation/create",
"params": {
"message": "請求書の承認者を選択してください",
"requestedSchema": {
"type": "object",
"properties": {
"approver": {
"type": "string",
"title": "承認者",
"description": "承認依頼を送る担当者"
},
"urgent": {
"type": "boolean",
"title": "至急扱い",
"default": false
}
},
"required": ["approver"]
}
}
}
ここでポイントになるのは、サーバーが自由入力のプロンプトを投げるのではなく、クライアントがフォーム化・バリデーションしやすい構造を渡すことです。ユーザー体験と安全性を両立するには、曖昧な自然文だけに頼らず、入力項目を明確に定義します。
messageフィールドの設計指針
messageはクライアントがユーザーに表示する説明文です。「何を求めているか」だけでなく「なぜ今この情報が必要か」を簡潔に伝えると、ユーザーの判断速度が上がります。ある物流企業では、messageに背景情報を1行加えたことで、Elicitation応答までの時間が平均45秒→18秒に改善されました。
良いmessageの例と悪いmessageの例を示します。
// 悪い例:何の文脈かわからない
{
"message": "値を入力してください"
}
// 良い例:背景と目的を明示
{
"message": "発注書 #PO-2026-0412 の送信先を確認します。取引先「株式会社ABC」宛で送信してよろしいですか?変更する場合は送信先メールアドレスを入力してください。",
"requestedSchema": {
"type": "object",
"properties": {
"confirmed": {
"type": "boolean",
"title": "この宛先で送信する",
"default": true
},
"alternativeEmail": {
"type": "string",
"format": "email",
"title": "変更先メールアドレス(変更する場合のみ)"
}
},
"required": ["confirmed"]
}
}
JSON Schemaで使える型と制約
公式仕様のrequestedSchemaは、JSON Schemaの限定サブセットです。クライアント実装を簡単にするため、ネストした複雑なオブジェクトやオブジェクト配列などの高度な機能は対象外で、フラットなオブジェクトとプリミティブなプロパティに絞られています。
利用できる型としては、string、numberまたはinteger、boolean、文字列enumが示されています。文字列ではemail、uri、date、date-time形式も使えます。数値にはminimumやmaximum、文字列にはminLengthやmaxLengthを設定できます。
この制約は不便に見えますが、AIエージェントの実運用ではむしろ利点があります。サーバーが複雑すぎる入力を求めると、クライアントUIが実装しづらく、ユーザーが何を承認したかも不明瞭になります。Elicitationで聞くのは「最後に人間が決めるべき小さな差分」に限定するのが設計のコツです。
実用的なスキーマ例:承認金額の範囲制約
数値のminimum/maximum制約は、ユーザーの入力ミスを防ぐ実用的な手段です。たとえば社内購買システムで「1回あたり50万円以上の決裁は部長承認が必要」というルールがある場合、Elicitationのスキーマで金額範囲を事前に制約できます。あるIT企業では、この制約を入れたことで金額入力の桁間違いによる差し戻しが月8件→0件に減少しました。
{
"message": "購買申請の最終確認です。金額と優先度を確定してください。",
"requestedSchema": {
"type": "object",
"properties": {
"amount": {
"type": "integer",
"title": "申請金額(円)",
"minimum": 1000,
"maximum": 500000,
"description": "50万円超は部長承認フローに回付されます"
},
"priority": {
"type": "string",
"title": "優先度",
"enum": ["通常", "急ぎ", "至急"]
},
"deliveryDate": {
"type": "string",
"format": "date",
"title": "希望納品日"
}
},
"required": ["amount", "priority"]
}
}
3つの応答:accept・decline・cancel
Elicitationの応答は、accept、decline、cancelの3アクションで整理されます。acceptはユーザーが明示的に承認・送信した状態で、contentにスキーマに沿ったデータが入ります。declineはユーザーが明示的に拒否した状態、cancelはダイアログを閉じた、Escを押したなど明示的な選択をせず中断した状態です。
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"action": "accept",
"content": {
"approver": "tanaka@example.com",
"urgent": false
}
}
}
実装では、declineとcancelを同じ「失敗」として潰さないことが大切です。拒否なら代替案を提示する、中断なら作業状態を保存して再開できるようにする、というようにワークフロー側の扱いを分けると、エージェントがユーザーの意思を尊重できます。
応答分岐の実装パターン
3つの応答を適切にハンドリングするコードを示します。金融系のワークフロー(振込承認)を例にとると、declineの場合は代替振込先の提案、cancelの場合は下書き保存というように処理を分岐させます。ある地方銀行の業務システムでは、cancel時に下書き保存を実装したことで、ユーザーの再入力回数が月約120件削減されました。
// 3つの応答を分岐するサーバー側ハンドラ
async function handleTransferApproval(server: McpServer) {
const result = await server.elicitation.create({
message: "振込内容を確認してください:山田商事 宛 ¥1,250,000",
requestedSchema: {
type: "object",
properties: {
approved: { type: "boolean", title: "この内容で振込を実行する" },
memo: { type: "string", title: "備考", maxLength: 200 }
},
required: ["approved"]
}
});
switch (result.action) {
case "accept":
if (result.content.approved) {
await executeTransfer(result.content);
return { status: "completed", message: "振込を実行しました" };
} else {
return { status: "rejected_by_user", message: "振込を見送りました" };
}
case "decline":
// 明示的拒否 → 代替案を提案
return {
status: "declined",
message: "振込を拒否しました。金額や宛先を変更しますか?",
suggestedActions: ["金額変更", "宛先変更", "キャンセル"]
};
case "cancel":
// 中断 → 下書き保存して再開可能に
const draftId = await saveDraft({ amount: 1250000, recipient: "山田商事" });
return {
status: "draft_saved",
message: `下書きを保存しました(ID: ${draftId})。後から再開できます。`
};
}
}
⚠️ よくある失敗パターンと対策
Elicitationの導入プロジェクトで実際に観測される失敗パターンを整理します。事前にこれらを把握しておくことで、手戻りコストを大幅に削減できます。
失敗1:Elicitationの多用でユーザーが「承認疲れ」を起こす
最も多い失敗は、確認ポイントを増やしすぎてユーザーが毎回「とりあえずaccept」するようになるケースです。ある人材サービス企業では、求人票の自動投稿フローに5段階のElicitationを設けた結果、ユーザーの平均応答時間が初月の12秒から3ヶ月後には2秒に短縮。内容を読まずに即承認している兆候が確認されました。Elicitationは「本当に人間が判断すべきポイント」に絞り、1ワークフローあたり最大2回を目安にしてください。
失敗2:機密情報をElicitationで収集してしまう
仕様が明確に禁止しているにもかかわらず、「パスワードを入力してください」「APIキーを貼り付けてください」というElicitationを実装してしまうケースがあります。Elicitation経由の入力はサーバーとクライアント間をJSON-RPCで平文送信されるため、トランスポート層の暗号化だけに頼ると監査要件を満たせません。機密情報はOAuth 2.1フローや専用の設定画面で扱い、Elicitationでは参照IDやフラグのみを受け渡すようにしてください。
失敗3:タイムアウト設計の欠如
Elicitationはユーザーの応答を待つ同期的な操作です。ユーザーが離席してしまうと、サーバーのツール実行がいつまでも完了しません。仕様自体にはタイムアウトの規定はありませんが、実装側で適切なタイムアウトを設けないと、エージェントの処理パイプライン全体がブロックされます。推奨は5分のタイムアウトを設定し、超過時はcancelと同等の処理(下書き保存・通知送信)を行うことです。
// タイムアウト付きElicitation呼び出し
async function elicitWithTimeout(
server: McpServer,
params: ElicitationParams,
timeoutMs: number = 300000 // 5分
): Promise<ElicitationResult> {
const timeoutPromise = new Promise<ElicitationResult>((_, reject) =>
setTimeout(() => reject(new Error("Elicitation timeout")), timeoutMs)
);
try {
return await Promise.race([
server.elicitation.create(params),
timeoutPromise
]);
} catch (error) {
if (error.message === "Elicitation timeout") {
// タイムアウト時はcancel相当の処理
await notifyUserViaSlack("承認待ちがタイムアウトしました。下書きを保存しています。");
return { action: "cancel" };
}
throw error;
}
}
失敗4:declineとcancelを区別しない
前述の通り、declineは「ユーザーが意図的に拒否した」、cancelは「意図せず中断した」です。両者をまとめて「エラー」として処理すると、ユーザーが明確に拒否した操作をエージェントが再試行してしまう、あるいは単にウィンドウを閉じただけなのに拒否として記録される、といった問題が起きます。監査ログにおいても、declineは「ユーザー判断による却下」、cancelは「未完了」として記録を分けるべきです。
Samplingとの違いを混同しない
MCPには、サーバーがクライアント側のLLMに生成を依頼するSamplingもあります。Samplingはsampling/createMessageでモデル生成を求める機能であり、Elicitationは人間ユーザーの入力・確認を求める機能です。
両者を組み合わせる場面はあります。たとえばサーバーが候補文面をSamplingで作り、最終送信前にElicitationでユーザーの承認を取る、という流れです。ただし、モデルが生成した内容をそのまま承認扱いにしないこと。人間の意思決定が必要な境界では、ElicitationのUIで明示的な確認を取るべきです。
MCPのアーキテクチャ全体では、こうした機能はクライアント機能として定義されています。公式のMCP Architecture overviewも、SamplingとElicitationをサーバーからクライアントへ要求できるプリミティブとして説明しています。
SamplingとElicitationの組み合わせ例
具体的なユースケースとして、顧客対応メールの自動生成と送信承認のフローを考えます。あるEC事業者(月間注文数約1万件)では、返品対応メールをSamplingで生成し、Elicitationで送信前承認を挟むワークフローを導入しました。導入前は担当者がメール文面を手作業で作成しており1件あたり平均8分かかっていたものが、AI生成+承認フローで平均1.5分に短縮されました。
// Sampling → Elicitation の連携フロー
async function handleCustomerReply(server: McpServer, ticketId: string) {
// Step 1: Samplingで返信文面を生成
const draft = await server.sampling.createMessage({
messages: [
{
role: "user",
content: {
type: "text",
text: `チケット ${ticketId} への返信文面を日本語で作成してください。丁寧な敬語で、返品手順を案内する内容にしてください。`
}
}
],
maxTokens: 500
});
// Step 2: Elicitationで人間の承認を取得
const approval = await server.elicitation.create({
message: `以下の返信を送信してよろしいですか?nn${draft.content.text}`,
requestedSchema: {
type: "object",
properties: {
sendApproved: { type: "boolean", title: "この内容で送信する" },
editNote: { type: "string", title: "修正指示(任意)", maxLength: 500 }
},
required: ["sendApproved"]
}
});
if (approval.action === "accept" && approval.content.sendApproved) {
await sendEmail(ticketId, draft.content.text);
return { status: "sent" };
}
// 修正指示がある場合はSamplingを再実行
if (approval.action === "accept" && approval.content.editNote) {
return await regenerateWithFeedback(server, ticketId, approval.content.editNote);
}
return { status: "cancelled" };
}
本番導入のチェックリスト
- 初期化時にクライアントの
elicitationcapabilityを確認する - 機密情報をElicitationで要求しない
- UIにサーバー名、目的、送信先、入力項目を表示する
requestedSchemaはフラットで小さく保つaccept、decline、cancelをワークフロー上で分けて扱う- 監査ログには「承認された事実」と最小限のメタデータを残し、不要な入力値を保存しない
特に社内システム連携では、Elicitationを「不足情報を聞く機能」だけでなく「危険なアクションの直前に人間の明示的な意思を挟む機能」と捉えると設計しやすくなります。削除、送信、決済、権限変更などの前に、ユーザーが内容をレビューしてからacceptできる導線を置きましょう。
導入コスト・期間の目安
Elicitationの実装自体は、MCPサーバーの基盤がすでにある前提であれば工数は小さく済みます。以下は規模別の目安です。
- 小規模(Elicitation箇所1〜2箇所):開発2〜3人日、テスト1人日。既存ツールの引数をElicitationに切り替える程度
- 中規模(3〜5箇所、フォールバック込み):開発5〜7人日、テスト3人日。非対応クライアントへのフォールバック設計が主な工数
- 大規模(全社ワークフロー統合):設計2週間、開発・テスト合わせて1〜1.5ヶ月。監査ログ設計やUIカスタマイズが中心
ROI試算のポイントは「Elicitationが省く人的作業時間」と「誤操作防止による損失回避」の2軸です。前述の受発注確認の例では、月83時間の削減×人件費単価で、投資回収期間は約2ヶ月でした。
<!-- 監査ログの記録フォーマット例 -->
{
"timestamp": "2026-05-17T10:23:45+09:00",
"elicitationId": "elic_abc123",
"serverName": "procurement-server",
"toolName": "submit_purchase_order",
"action": "accept",
"userId": "user_456",
"metadata": {
"orderId": "PO-2026-0412",
"approvedAmount": 125000
}
// 注意:入力値の全文は保存しない。承認事実とメタデータのみ。
}
まとめ:人間参加型AIエージェントの標準部品
MCP Elicitationは、AIエージェントの自律性を高めるための機能ではなく、自律処理の途中に人間の判断を安全に戻すための標準部品です。仕様はまだ進化の余地がありますが、elicitation/create、限定されたJSON Schema、3つの応答アクションという基本形を押さえるだけで、実装の見通しはかなり良くなります。
導入を検討する際のポイントを改めて整理すると、以下の3点に集約されます。
- 設計判断:「どこでユーザーに確認するか」をワークフロー設計の初期段階で決める。後付けは手戻りが大きい
- 実装品質:フォールバック設計、タイムアウト、3応答の分岐処理を省略しない。ここを省くと運用で確実に問題が出る
- 運用監視:承認疲れの兆候(応答時間の短縮傾向)を監視し、Elicitation箇所の見直しを定期的に行う
これからMCPサーバーを公開するチームは、ツール定義や認可だけでなく「どこでユーザーに確認するか」を設計レビューに含めるべきです。AIgent Labでは、MCPサーバー設計、エージェントUI、社内ワークフロー自動化の相談を受け付けています。導入方針に迷っている場合は、お問い合わせフォームからご連絡ください。
