元の動画を見る

短い答え:モデルにはデータを渡し、描画は別のステップにする

MCPサーバーを運営していて、検索結果をClaudeやChatGPTの中でプレーンテキストではなく本格的なインターフェースとして表示したいとします。カルーセルをひとつ表示して止まるのではなく、モデルが結果を見て推論し、検索を続けられるようにするにはどうすればよいのでしょうか。まず用語を整理します。MCP(Model Context Protocol)は、AIチャットクライアントが外部ツールを呼び出すための仕組みです。MCPサーバーは、そうしたツールを提供するサービスです。MCP Apps、およびChatGPT向けのOpenAI Apps SDKを使うと、ツールは会話の中に表示されるインタラクティブなインターフェース、つまりウィジェットを返せます。

IndeedのDustin Mihalik氏は、3つのルールでこれに答えます。第1に、ユーザーがウィジェットで見るものはすべて、データとしてモデルにも届けること。第2に、ユーザーがウィジェットで行う操作はすべてモデルに伝え返すこと。第3に、データの取得とインターフェースの描画は別々のツールで扱うこと。そうすれば、モデルはタスクに必要なだけ検索し、何を表示するかは最後に選べます。同氏は、3つ目のルールが他の2つより優先されると述べています。また、モデルが1回の検索で諦めてしまう問題に対処するルールは3つ目だけです。

同氏はこの講演をAI Engineerのセッションで行い、録画はAI EngineerのYouTubeチャンネルで公開されました。セッションがいつ開催されたかは、録画からはわかりません。

教訓の出どころ

Mihalik氏は、Indeedの求人検索に携わるとともに、ガードレール、ゲートウェイ、コンプライアンスを扱うAIプラットフォームチームにも所属していると話します。同氏によれば、このチームはAIベンダー各社とすでに関係があり、その流れでMCPコネクターやMCPアプリを作ることになったといいます。

同氏によると、IndeedはClaude向け、ChatGPT向け、そしてCareer Scout向けにMCPサーバーとアプリを構築しました。Career Scoutは、同氏が求職者向けのIndeed独自のエージェントと説明しているものです。提供開始時期や提供状況には触れていません。同氏はこのセッションを入門ではなく「現場からの教訓(lessons from the trenches)」と位置づけ、MCPアプリの基本は先行する入門セッションですでに扱われたと述べています。

そもそも求人サイトがチャット内にUIを求める理由

同氏はまず、オースティンのバリスタ求人を検索したときのClaudeのプレーンテキストの回答を示します。同氏はこの回答を有用だと評価します。Claudeが市内と近郊の求人をまとめて示してくれるからです。しかし、結果がIndeed由来であることを示すものは何もなく、何が表示されるかにパブリッシャーが関与できる余地もほとんどありません。

より大きな問題はリンクです。Mihalik氏によると、ClaudeやChatGPTに外部リンクを一貫して貼らせるのは難しいといいます。同氏は、プラットフォーム側がユーザーをチャット内にとどめたいからではないかと推測していますが、これはあくまで推測であり、根拠は示していません。リンクのない求人リストは体験として良くない、と同氏は言います。同氏のチームは、Claudeが確実にリンクを貼るようになるまでに、本人いわく途方もない時間とeval(モデルの挙動を自動で試験するテスト)を費やしました。具体的な数字は示していません。

同氏の説明では、ウィジェットがこの状況を変えます。Indeedは各結果に応募ボタンを付け、どの項目を上部に表示するかを選べます。さらに「詳細を見る」ボタンを追加して、求人票の全文をポップアップウィンドウ、つまりモーダルで開けるので、ユーザーはチャットを離れずに済みます。同氏はこれをIndeedとチャットプラットフォームの双方にとってのウィンウィンだと述べています。ウィジェットは画面上で示されましたが、ここでの説明は同氏が口頭で語った内容に基づいています。

安易な移植:ウィジェットがモデルにとってブラックボックスになる

MCPアプリを、自社サイトをそのままチャットに持ち込む手段とみなしたくなるかもしれません。ウィジェットがHTMLを読み込み、そのHTMLがサイトで使っている既存のAPIを呼び出せば完成、というわけです。Mihalik氏はそれほど簡単ではないと言い、まさにこのやり方で作られたMCPアプリを見たことがあると話します。

問題は、モデルが何を知っているかです。モデルがツールを呼び出し、ツールが結果を画面に表示しても、モデルはその結果が何なのかをまったく知りません。すると「最初の結果について教えて」や「これらの企業をランク付けして」といったごく普通のフォローアップが失敗します。データはAPIからウィジェットへ直接渡り、モデルを一度も経由していないからです。

ルール1:ユーザーが見るものは、モデルにもデータとして渡す

同氏の第1のルールは、インターフェースに表示されるすべての情報を、データとしてモデルにも渡さなければならないというものです。

同氏が説明するMCPおよびMCP Appsの仕様では、ツールのレスポンスには2つのものが含まれます。1つは構造化コンテンツ(structured content)で、モデルが読む機械可読な結果です。テキスト専用のMCPツールであれば、もともと返していたものにあたります。もう1つはリソースURIで、チャットクライアントがウィジェットのHTMLを読み込むためのポインターです。両方を返す必要があり、同氏はこの2つを同期させておかなければならないと言います。ウィジェットの背後にあるAPIに給与レンジのような新しい項目が加わったら、モデルが読む構造化コンテンツにも同じ項目を加えるべきです。そうしないと、ユーザーには見えているのに、モデルはそれについて語れない情報が生まれます。

ウィジェットがすでに表示している内容をモデルに繰り返させない

ルール1には副作用があります。データを受け取ったモデルは、他のツール出力と同じように結果リスト全体をテキストで書き出します。それも、同じリストをすでに表示しているウィジェットのすぐ下にです。ユーザーはすべてを二度見ることになります。

同氏の対処法は、ツールの説明文(モデルがツールの使い方を判断する際に読むテキスト)の冒頭に1行加えることです。その1行は、結果がUIコンポーネントとして自動的にユーザーに表示されたと伝えるものです。同氏によれば、これがあるとモデルは短い要約だけを書き、一覧表示はウィジェットに任せます。同氏はインターフェースが表示するものとモデルが書くものとの間に一種のせめぎ合いがあると表現し、この1行で「かなりのケースをカバーできる」と述べています。

ルール2:ユーザーが何をクリックしたかをモデルに伝える

ウィジェットが情報を隠してしまうケースは他にもあります。検索で求人が10件返り、ユーザーがそのうち1件の詳細を開いたとします。モデルはそれがどの求人なのかを知りません。すると「この求人向けにカバーレターを書いて」や「この職務内容を要約して」といった依頼が失敗します。求人票の全文はウィジェットに読み込まれただけで、モデルには渡っていないからです。

Mihalik氏のルールは、ユーザーがウィジェットで行う操作に関する情報もモデルに送らなければならないというものです。同氏は「ルール2」と口に出してはいませんが、説明の順序では2番目にあたり、後にデータと描画の分離をルール3と呼んでいます。

同氏によると、MCP Appsにはそのためのメソッドがあり、同氏はそれをupdate model contextと呼んでいます。同氏の説明では、このメソッドは文字列を1つしか受け取らないため、時間の経過とともに複数のイベントを伝えたい場合は、その1つの文字列に追記していく必要があります。同氏は、MCP Appsのドキュメントにあるというショッピングカートの例を挙げています。このアプリはカート内の商品と合計金額をモデルに渡すので、ユーザーは自分のカートについて質問できます。

転換点:UIツールが多段階の検索を途中で打ち切ってしまう

ルール1と2を適用すれば、モデルは何が起きているかをおおむね把握できます。それでもMihalik氏は、アプリはまだ良くないと言います。人々はClaudeに簡単な問題を持ち込まないからです。1回検索すれば済むならWebサイトに行く、と同氏は言います。Claudeに持ち込むのは難しい問題なのです。

同氏の例は、転居も視野に入れているユーザーです。このユーザーは、ひとつの職種名で複数の都市を検索し、最も給与の高い選択肢を選び出し、特定の業界は除外したいと考えています。同氏によると、プレーンテキストのMCP検索ツールなら、Claudeはこれをうまくこなします。10〜15回ほど検索し、結果を絞り込み、関連する詳細を抜き出して、最後に要約表を作ります。この回数は大まかな例であり、ベンチマークではありません。

では、その検索ツールにウィジェットの描画もさせ、説明文に結果がユーザーに表示されると書いたらどうなるでしょうか。Mihalik氏によると、Claudeは1回呼び出して止まります。結果はすでに画面に出ているので、それ以上掘り下げる必要はないと判断しているようだといいます。同氏は実際的な理由も挙げます。カルーセルが10個続けて並ぶのを望む人はいませんし、Claudeもすでに何かを表示したことに気づいているようだというのです。結果として、体験を良くするはずのUIが、ユーザーの求めていた多段階の作業を途中で打ち切ってしまいます。同氏はこれを、Claudeに限った自チームの観察として伝えています。

ルール3:データ処理とUI描画を分離する

Mihalik氏の第3のルールはデータ処理とUI描画を分離することで、同氏はこれが他の2つより優先されると述べています。この表現はOpenAIのApps SDKのドキュメントから借りたもので、ドキュメントの何か所かに書かれていると同氏は言います。

Indeedの実装は次のとおりです。同氏がsearch jobsと呼ぶツールは、インターフェースを持たない標準的なテキスト専用のMCPツールになり、Claudeは好きなだけ呼び出せます。別のrender-jobsウィジェットは求人IDのリストを受け取ります。Claudeは検索して候補の求人を100件ほど集め、それを5件に絞り込み、その5件のIDだけを描画ツールに渡せます。ユーザーには、モデルが選んだ最終候補を収めたウィジェットが1つ表示されます。100件から5件という数字は同氏が挙げた例です。

同氏によれば、描画ツールはモデルが表示したいデータそのものを受け取ることも、それへの参照を受け取ることもできます。Indeedは参照、つまりIDを選びました。いずれの場合も、描画ツールの説明文には、データがどこから来るのか、どんな形式でなければならないのか、そしてそれを得るにはまずデータツールのいずれかを呼び出す必要があることを書いておかなければなりません。同氏はこれを、ツールの説明文をかなり簡単に変えるだけの作業だと述べています。

Mihalik氏が説明する2つのツール構成。挙動は同氏がClaudeで観察したものであり、計測結果ではありません。
検索と描画を兼ねる単一ツールテキスト検索ツール+独立した描画ツール
モデルが検索する回数同氏の説明では1回。表示された結果を完成した答えとみなすタスクに必要なだけ。複数都市にまたがる依頼で10〜15回の検索を挙げている
モデルが見られるものルール1に従っていれば、返されたデータ何かが表示される前に、すべての検索結果をデータとして見られる
ユーザーが見るもの1回のクエリから作られたウィジェットモデルが選んだ最終候補を収めたウィジェット1つ(同氏の例では約100件の候補を5件に絞り込み)
描画ステップが受け取るもの1回のクエリが返したものそのままモデルが選んだ求人IDのリスト

他分野での同じ分離と、モデルの推論を運ぶ描画ツール

Mihalik氏は、ほとんどの業界で同様の分離が見つかると言います。モデルにデータを探索させ、その後で何を描画するかを選ばせるというものです。同氏は、ECの商品検索結果や、モデルが選んだ5つの住所を受け取る地図ウィジェットを例に挙げています。

さらに同氏は、描画ツールがID以外のものも受け取れると提案します。テキストのみの回答では、モデルは各結果を選んだ理由を説明することが多い、と同氏は指摘します。描画ツールが、IDとその求人が合っている理由の短い説明、あるいはIDと職務内容から強調したい一節を受け取るようにもできます。そうすれば、モデルの推論は別の文章ではなくウィジェットの中に表れます。同氏はIndeedがこれを追加できる「可能性がある」と述べているにとどまるので、これらはIndeedがすでに提供している機能ではなく、設計上のアイデアです。

自分のMCPアプリのための設計チェックリスト

Mihalik氏は2つの要点で締めくくります。1つ目は、インターフェースより先にデータを設計することです。同氏は、これがMCPアプリをChatGPTにUIを入れる手段とみなす一般的な見方に反することを認めています。モデルにどんなデータを持たせ、それで何ができるようにすべきかを決め、描画はモデルがそのデータを探索した結果としてついてくるようにする、ということです。2つ目は、小さく組み合わせ可能なツールを作ることです。同氏の例は、求人の探し方ごとに2〜3個の検索ツールと、1〜2個の描画ツールです。描画ツールは、たとえば求人一覧用と、1件の求人を目立たせる用の2つです。それぞれにシンプルな説明文を付ければ、モデルに負荷をかけすぎず、それでいてどう探索し何を見せるかをモデル自身が選べます。

同氏の助言をまとめると、開発者向けの短いチェックリストになります。

  • データの一致:ウィジェットが表示するものはすべて構造化データとしてモデルにも返し、両者を同期させておく。
  • 説明文による誘導:ツールの説明文に結果はすでに表示されていると書き、モデルが結果を繰り返さず要約するようにする。
  • 操作のコンテキスト:ウィジェット内のクリックや選択をモデルに伝え、フォローアップの依頼が文脈に沿うようにする。
  • データと描画の分離:検索はモデルが何度でも呼び出せるテキスト専用ツールにし、最終的に選んだものだけを別ツールで描画する。そのツールの説明文には、先にどのデータツールを呼び出す必要があるかを書く。

まだ証明されていないこと

これは約15分の講演で語られた1チームの経験であり、指標も、eval結果も、導入前後の数値も、質疑応答もありません。Claudeがいつ検索をやめるかについての同氏の主張は、モデルの挙動の観察です。新しいモデルでは変わる可能性があり、同氏が同じようには説明していないChatGPTや他のクライアントでは当てはまらないかもしれません。

仕様上の用語、update model contextメソッドとその単一文字列の制約、ドキュメントの例は、いずれも同氏の説明に基づくものです。私たちはこれらをMCP AppsやApps SDKのドキュメントと照合しておらず、この挙動を再現するアプリも作っていません。正確なフィールド名やメソッド名は最新のドキュメントで確認し、このチェックリストは実証済みのレシピではなく、自分のサーバーで試す価値のある、よく練られた出発点として扱ってください。

出典

AI Engineer · @aiDotEngineer

あわせて

資料と編集方針

約15分のAI Engineerの録画について、自己紹介から締めくくりの発言まで、英語の自動生成字幕をすべて読みました。質疑応答はありません。画面上のスライドやスクリーンショットは確認できず、講演者が口頭で述べた内容を通じてのみ記述しています。判読しにくい字幕の断片は、控えめに解釈するか、除外しました。MCP、MCP Apps、OpenAI Apps SDKの公式ドキュメントは参照しておらず、本記事のためにMCPアプリを構築・テストしてもいません。