Claude Media
MCPのElicitation、フォームとURLで何が違うか

MCPのElicitation、フォームとURLで何が違うか

MCP ElicitationのFormモードとURLモードの違いと、パスワードなど機密情報でURLモードが必須になる理由をClaude Codeの実装で確認します。

MCPのElicitationは、サーバーがタスクの途中でユーザーに追加入力を求める仕組みです。仕様は入力の集め方をフォームモードとURLモードの2つに分けており、パスワード・APIキー・アクセストークンなどの機密情報は、フォームモードでの要求が禁止され、URLモードの使用が必須です。Claude CodeはMCPクライアントとして両モードを扱い、Elicitationフックで応答を自動化することもできます。応答を横取りする実装はElicitation/ElicitationResultフックの使い方で解説しています。

Elicitationとは何をする仕組みか

Elicitationは、MCPサーバーがツール呼び出しの処理中に「情報が足りない」と判断したとき、クライアント経由でユーザーに追加入力を求める機能です。ツール呼び出し1回では終わりません。2026-07-28版の仕様では、サーバーがInputRequiredResultという「入力が必要」の結果を返し、クライアントはユーザーの応答を載せて同じリクエストを再送します。

手順

フォームモードの往復(2026-07-28版の仕様)

  1. 1

    クライアントがツールを呼ぶ

    tools/callを送ります。この時点では追加入力の要求は出ていません。

  2. 2

    サーバーが入力を要求する

    処理を止め、mode: "form"とrequestedSchemaを含むInputRequiredResultを返します。

  3. 3

    ユーザーが入力する

    クライアントがフォームを表示し、ユーザーが埋めて送信します。内容はクライアントを通ります。

  4. 4

    クライアントが再送する

    応答をinputResponsesに載せてtools/callをもう一度送り、サーバーが最終結果を返します。

Claude Codeでは設定が要りません。サーバーが要求すると、フォームならフィールドが並ぶ対話ダイアログが、URLなら「ブラウザでリンクを開いてよいか」という確認が自動で出ます。

フォームモードとURLモードは何が違うか

くらべる

入力の通り道が違う

帯域内

フォームモード

JSON Schemaで定義した構造化データを、クライアント経由で受け取ります。名前・メール・選択肢などの一般情報向けで、クライアントは送信された内容そのものを見られます。

帯域外

URLモード

外部URLへ誘導し、入力はクライアントを経由しません。認証フロー・決済・機密情報の入力向けで、クライアントが見るのはURLだけです。

機密情報の線引きは、仕様の中でも数少ない断定的な必須要件です。

Servers MUST NOT use form mode elicitation to request sensitive information such as passwords, API keys, access tokens, or payment credentials. Servers MUST use URL mode for interactions involving such sensitive information.

「機密情報」の定義も明確です。取引を承認したりアクセス権を与えたりする秘密・資格情報が対象で、氏名・メールアドレス・ユーザー名のような一般的な連絡先情報は対象外です。一般情報をフォームモードで求めるかどうかはサーバーの裁量で、禁止されていません。

公式docsのフォーム例にパスワード欄がある理由

Claude Codeの公式docs(MCPのページ)は、フォームモードを「ユーザー名とパスワードを求めるプロンプトなど、サーバーが定義したフィールドのダイアログ」と例示しています。仕様がパスワードのフォーム要求を禁じているのに、読み合わせると食い違って見えます。

食い違いではなく、禁止の宛先が違います。MUST NOTがかかるのはサーバーの実装で、クライアントが任意のスキーマを表示できることとは別の話です。フックのページにある入力例も、メッセージはPlease provide your credentialsですが、スキーマに入っているのはusernameだけです。

読者が自作サーバーを書く側なら、公式docsの例文を真似てパスワード欄を作らないことが分かれ目です。使う側なら、フォームにパスワードやトークンを求められたとき、そのサーバーが仕様の必須要件に反していると判断できます。入力する前にダイアログを閉じる、という動きにつながる知識です。

フォームモードのスキーマは意図的に制限されている

フォームモードのrequestedSchemaは、JSON Schemaの全機能を使えません。条件は「フラットなオブジェクトで、プロパティはプリミティブ型のみ」です。対応する型は次の4つです。

  • String: minLength / maxLength / format(email / uri / date / date-time)
  • Number(integer含む): minimum / maximum
  • Boolean
  • Enum: 単一選択・複数選択、タイトル付き選択肢にも対応

どの型にもdefaultを付けられ、対応するクライアントはフォームの初期値に使うことが求められています。ネストしたオブジェクトや、enum以外の配列は対象外です。理由は「クライアント側のフォーム生成・入力体験をシンプルに保つため」と明記されています。実際のリクエスト例です。

{
  "method": "elicitation/create",
  "params": {
    "mode": "form",
    "message": "Please provide your GitHub username",
    "requestedSchema": {
      "type": "object",
      "properties": { "name": { "type": "string" } },
      "required": ["name"]
    }
  }
}

応答側は{"action": "accept", "content": {"name": "octocat"}}の形です。仕様はクライアントに送信前のスキーマ検証を、サーバーに受信後の検証を、それぞれSHOULDで求めています。

URLモードの「accept」は完了を意味しない

URLモードの応答は{"action": "accept"}だけで、contentは付きません。仕様は、このacceptが「ユーザーがURLを開くことに同意した」という意味にとどまり、操作の完了は意味しないと明記しています。入力はブラウザ側で行われ、クライアントには結果が届かないためです。

手順

URLモードの往復(2026-07-28版の仕様)

  1. 1

    サーバーがURLを返す

    mode: "url"とurlを含むInputRequiredResultを返します。必要なら、待っている操作の情報をrequestStateに入れます。

  2. 2

    クライアントが同意を取る

    URL全体を見せ、ユーザーの同意を得てからブラウザで開きます。

  3. 3

    クライアントが再送する

    acceptを載せて元のリクエストを再送します。

  4. 4

    サーバーが完了を判断する

    requestStateなどの状態から操作の完了を確かめ、最終結果か、もう一度のInputRequiredResultを返します。

仕様はクライアントに、再試行やキャンセルの手動操作を用意することも求めています(SHOULD)。ブラウザで入力を終えたのに処理が進まないときは、サーバーがまだ完了を確認できていない可能性があります。

前版(2025-11-25)には、サーバーがnotifications/elicitation/completeで完了を通知する仕組みがありました。elicitationIdでどの要求の完了かを結び付ける形です。2026-07-28版の仕様には、この完了通知がありません。

Claude Codeの通知フックには、Notificationの種別が4つ並んでいます(公式のフックのページ)。elicitation_dialogはフォームを開いて約6秒操作がない状態、elicitation_url_dialogはURLを開く確認に約6秒操作がない状態です。elicitation_completeはサーバーがURLモードの完了を報告したときで、前版の完了通知に対応するイベントです。2026-07-28版で接続したサーバーからは届かない可能性があります。elicitation_responseは応答をサーバーへ返したときに出ます。端末を離れた状態で、入力待ちを通知で知りたいときの手がかりです。

URLモードは「MCPクライアントの認証」とは別物

URLモードは、MCPクライアントとサーバー間の認証(MCP authorization)とは目的が異なります。MCPサーバーが第三者サービスの認可を取り付ける仕組みです。

典型はサーバーが外部SaaSのOAuthクライアントとして動く場面です。サーバーはURLモードのElicitationでユーザーをブラウザへ誘導し、ユーザーは第三者の認可画面で同意し、サーバーはコールバックで受け取ったトークンをユーザーIDに紐づけて保存します。この間、MCPクライアント自身のベアラートークンは変わりません。仕様が挙げる重要なセキュリティ要件は4つです。

  1. 第三者の認証情報がMCPクライアントを経由してはならない
  2. サーバーはMCPクライアントの認証情報を第三者サービスへ横流ししてはならない(token passthrough禁止)
  3. ユーザーは、MCPサーバーを直接認可しなければならない
  4. 第三者トークンの保管と管理は、MCPサーバーの責任である

これとは別に、フィッシング対策の節が本人確認を求めています。悪意あるユーザーがElicitationのURLを生成させ、別の被害者に開かせると、被害者が完了した第三者認可が悪意あるユーザーのセッションに紐づく恐れがあります。仕様はサーバーに、開始した本人と完了する本人が同一であることをセッションCookie等で確認するよう求めています。

Claude CodeはURLの長さに上限を持つ

Claude CodeはURLモードのURLをコマンドライン引数としてOSのURLハンドラーへ渡します。この設計上、渡せる長さに上限があります。エスケープが必要な文字(%や&など)は、その文字自身とエスケープ用の3文字を合わせて4倍分で数えます。

自作のMCPサーバーがURLモードを使うなら、認可URLに長大なクエリパラメータを積み込む設計は避けたほうが安全です。

クライアントはURLをどう扱うか

URLモードは「クリックする前に何が起きるURLか分かること」が安全性の前提です。仕様の安全なURL処理の節は、クライアントに次の扱いを求めています。

必須(MUST)は4つです。

  • URLやそのメタデータを事前に自動フェッチしない
  • ユーザーの明示的な同意なしにURLを開かない
  • URL全体をユーザーに見せてから同意を取る
  • クライアントやLLM自身が、ページの中身や入力内容を覗けない方式で開く

推奨(SHOULD)は3つです。ドメイン部分の強調表示と、Punycode(見た目が紛らわしい国際化ドメイン)を含むURLへの警告があります。クリック可能なURLとして描画してよいのはurlフィールドだけ、という扱いも推奨されています。

覗けない方式の例として、仕様はiOSのSFSafariViewControllerを挙げ、WKWebViewのような埋め込み方式は不適切としています。URLモードの目的は「LLMやクライアントに機密情報を触れさせない」ことなので、開いたページの中身をクライアントから覗ける実装ではこの前提が崩れます。

応答は3種類 — Accept / Decline / Cancel

フォーム・URLどちらのモードでも、ユーザーの応答は3つのアクションに分かれます。

  • Accept: 明示的に承認して送信(フォームならcontentにデータを含む)
  • Decline: 明示的に拒否(「いいえ」を選択した状態)
  • Cancel: ダイアログを閉じる・Escapeを押す・ブラウザが読み込めない等、明確な選択をせずに離脱した状態

DeclineとCancelを同一視するとユーザー体験を損ないます。仕様はサーバー側に「Declineには代替案の提示」「Cancelには後で再度促す」といった別処理を求めています。サーバーは、要求が必ず成功する前提で組んではいけません。

応答を自動化するとき、どのフックを選ぶか

ダイアログを毎回手動で処理したくない場合はElicitationフックで自動応答できます。ツール呼び出し中にMCPサーバーが入力を求めたタイミングで発火し、mcp_server_name・message・modeが渡されます。フォームモードならrequested_schema、URLモードならurlも入ります。elicitation_idが付くこともあります。

{
  "hook_event_name": "Elicitation",
  "mcp_server_name": "my-mcp-server",
  "message": "Please provide your credentials",
  "mode": "form",
  "requested_schema": {
    "type": "object",
    "properties": { "username": { "type": "string", "title": "Username" } }
  }
}

出力側はhookSpecificOutputにaction(accept / decline / cancel)を入れます。contentはacceptのときだけ使われ、フォームのフィールド値を渡します。マッチャーはMCPサーバー名に対して効きます。

{
  "hookSpecificOutput": {
    "hookEventName": "Elicitation",
    "action": "accept",
    "content": { "username": "alice" }
  }
}

使い分けは、介入する場所で決まります。

やりたいこと使うフック出力
ダイアログを出さず自動で答える使うフックElicitation出力actionとcontent
要求そのものを拒否する使うフックElicitation出力exit code 2
ユーザーの応答を送信前に書き換える使うフックElicitationResult出力actionとcontentで上書き
ユーザーの応答を送信前に止める使うフックElicitationResult出力exit code 2(decline扱い)

注意点が3つあります。exit code 2のとき、stderrのメッセージはどこにも表示されません。exit 2と同時に返したhookSpecificOutputは無視されます。JSONのsystemMessageとcontinueも、この2つのフックでは使われません。拒否の理由をユーザーに見せたい用途には向かないので、理由を残すなら別の経路が要ります。

ElicitationResultは、ユーザーが応答した後、サーバーへ送り返す前に走ります。入力値の検査や、特定サーバー宛ての応答の差し替えに使えます。フックイベント全体の設定方法はClaude Code Hooks完全ガイドにまとめています。

使っているクライアントがURLモードに対応しているか

Elicitationを使うサーバーは、クライアントが宣言した対応モードを前提に要求を作ります。2026-07-28版の仕様では、クライアントは各リクエストの_metaにelicitationの対応を宣言します。空のオブジェクトは「フォームモードのみ」と同じ扱いで、サーバーは未対応のモードを送ってはいけません。

Claude Codeは、2026-07-28版のプロトコルを使う接続でelicitation: {form: {}, url: {}}を宣言します。公式のMCPページによると、MCPクライアントには2つの実行系(v1とv2)があり、2026-07-28版を話せるのはMCP TypeScript SDK 2.0に載ったv2です。v2は、機能フラグを取得するセッションではClaude Code v2.1.232以降で使われます。フラグを取得しないセッション(Amazon Bedrockなどのクラウド経由、Claude appsゲートウェイ経由、テレメトリ停止)では、v2.1.274以降でv2が既定です。

実行系は起動のたびに選ばれ、終了まで保たれます。2025-11-25版のサーバーが更新後に接続できなくなったときの設定はbareElicitationCapabilityの解説にあります。

v2.1.292以降は、stdioサーバーにも既定で2026-07-28版を話せるかを尋ねます。どのサーバーも従来のハンドシェイクで接続させたいときは、MCP_PROTOCOL_NEGOTIATIONをlegacyに設定します。

MCPサーバー側の実装で守る制約

自分でMCPサーバーを書いてElicitationを実装するなら、仕様が課す制約は次のとおりです。

  • フォームモードで機密情報(パスワード・APIキー・アクセストークン・決済情報)を要求しない
  • URLモードのURLに、事前認証済みでアクセスできるURL(マジックリンク)を含めない。クライアントによるなりすましの恐れがあるため
  • URLに、エンドユーザーの個人情報や認証情報そのものを含めない
  • 非開発環境ではHTTPSのURLを使う(SHOULD)
  • Elicitationを開始したユーザーに状態を紐づけ、ユーザーの識別をクライアントが送った自己申告に頼らない

権限設計はMCPセキュリティガイドでも扱っています。リモートMCPサーバーでOAuth認可を実装する場合は、Elicitationとは別レイヤーの話になるためリモートMCPのOAuth認証を合わせて確認してください。

まとめ

自作サーバーで資格情報を扱うなら、最初からURLモードで設計し、完了の確認はrequestStateなどサーバー側の状態で行う。2026-07-28版の仕様は、この組み立てを前提にしています。

この記事を共有:XはてブLinkedIn