
MCP AppsでAskUserQuestionライクな質問UIを実装する
はじめに
生成AIやAIエージェントによってUIを生成するGenerative UIという考え方がCopilotKitやAI SDKなどに実装されました。動的に生成して返すというより、コンポーネントを用意し、そこにAIがデータを流し込む形が一般的な気がします。本記事もその内容です。このメリットはAIチャットボットと従来のWebの良さのハイブリッドという体験で、人間がプロンプトを書かなくてよいというメリットがあります。
今回はClaude Codeでよく使われるAskUserQuestionをMCP Appsに取り込んでUIとしてユーザーに返してみようと思います。
AskUserQuestionは、実行中にユーザーへ質問を表示し、選択肢から回答を収集するツールです。Claude Codeを使っていると以下のような画面を見たことがある方も多いと思います。プロンプトを書かずに方針を決められるので便利です。今後よりAIが賢くなることを考えると、AIに要点を整理してもらい、聞いてもらう機会は増えると思っています。

MCP Appsは、Model Context Protocol(MCP)を拡張し、AIチャットの中に「直接操作できるインタラクティブな画面(UI)」を埋め込むための仕組みです。標準化された通信規格MCPでGenerative UIができます。
公式のサンプルだとカラーピッカーの例が紹介されています。

すでにこのような体験は Generative UIで可能です。MCP Appsだとクライアントを選ばないので汎用性が高いというメリットがあります。ただWebのLLMチャットアプリ(HTMLをレンダリングできるMCPクライアント)がMCP及びMCP Appsに対応しているか確認が必要です。現状MCP Appsをサポートしているクライアントは公式の引用より以下となります。
- Claude (Web版)
- Claude Desktop
- Visual Studio Code (Insiders版)
- Goose
- Postman
- MCPJam
動作、設定方法
はじめに
ソースは以下のリポジトリです。
動作の様子は以下の通りです。
新規でネイティブアプリを開発したいです。AskUserQuestionを使って質問してください。 というプロンプトに対して、対話形式でいくつかの質問がUIとして返されます。
※ loading部分は4倍速にしています。
画像でも補足します。

Submitを押すと、選択した内容がホストに送信され、チャット入力欄に挿入されます。ただし、AIへの送信(確定)はユーザーが手動で行う必要があります。(上記の画像は送信済みですが、これは私が手動で送信したためです)

設定方法
npm install -g mcp-ask-user
# or
pnpm install -g mcp-ask-user
Claude Desktopの設定は以下を参照してください
自分の環境ではnixやmiseなど特殊な設定が多いので、フルパスにしたり、nodeのパス設定を入れないと動きませんでした。動作しなかった際に参考にしてください。
MCP Apps構築
Tips
基本的に公式手順のGetting startedに載っているSkillを導入するとよいです。
今回はUI側とサーバー側に分けて実装しています。UI側はツール経由で配信するHTMLを作り、Server側はツール定義(UI側のリソース紐付け)とstdio及びStreamableHTTPのインタフェース提供部分を担っています。
UI側
UI側のビルドとサーバー側のビルドでそれぞれビルドした成果物をdistに格納しています。
vite-plugin-singlefile で CSS/JS を HTML に埋め込んで、単一の dist/mcp-app.html を生成しています。
INPUT=mcp-app.html vite build
サーバー側
ESM 形式で dist/server.mjs を生成しています
tsdown src/server.ts --format esm --out-dir dist --clean false
UI側でビルドした単一のHTMLを、resourceとして登録しています。
こちらでpublish前に両方ビルドします
先ほどUI側でビルドした内容を ui:// スキームとして定義します。ui://ask-user/mcp-app.html スキームと実体のファイル(dist/mcp-app.html)を紐付けします。
そしてツール側の _meta.ui.resourceUri でこのリソースURI (ui://ask-user/mcp-app.html) を参照しています。
これにより、ツール呼び出し時にクライアントがどのUIリソースを表示すべきか分かります。
AIからこのツールが以下のような形で呼び出されます。multiSelect=true の部分は複数選択が可能になります。このスキーマ設計も大切ですね。
{
"questions": [
{
"header": "プラットフォーム",
"options": [
{
"label": "iOS",
"description": "iPhone/iPad向けアプリ"
},
{
"label": "Android",
"description": "Androidスマートフォン/タブレット向け"
},
{
"label": "両方",
"description": "iOS・Android両対応のクロスプラットフォーム"
}
],
"question": "どのプラットフォーム向けのアプリを開発したいですか?",
"multiSelect": true
},
{
"header": "アプリ種類",
"options": [
{
"label": "ビジネス/業務効率化",
"description": "タスク管理、CRM、社内ツールなど"
},
{
"label": "SNS/コミュニケーション",
"description": "チャット、マッチング、コミュニティなど"
},
{
"label": "Eコマース",
"description": "ショッピング、決済、予約など"
},
{
"label": "その他",
"description": "ゲーム、教育、ヘルスケアなど"
}
],
"question": "どのような種類のアプリを作りたいですか?"
},
{
"header": "経験レベル",
"options": [
{
"label": "初心者",
"description": "プログラミング経験が少ない"
},
{
"label": "中級者",
"description": "Web開発経験あり、モバイルは初めて"
},
{
"label": "上級者",
"description": "モバイルアプリ開発経験あり"
}
],
"question": "開発経験はどの程度ありますか?"
},
{
"header": "優先事項",
"options": [
{
"label": "開発スピード",
"description": "早くリリースしたい"
},
{
"label": "パフォーマンス",
"description": "高速で滑らかな動作"
},
{
"label": "コスト削減",
"description": "開発・運用コストを抑えたい"
},
{
"label": "学習機会",
"description": "技術を深く学びたい"
}
],
"question": "優先したいことは何ですか?"
}
]
}
MCP配布
--stdio でローカル MCP として、デフォルト(HTTP)はリモート MCP として動作します。ローカル MCP にも対応したのは、リモートだと mcp-remote を被せたりネットワークポートを塞ぐ必要があるためです。ポートは被らないようにしていますが、ローカルの方が手軽で設定しやすいと思っています。
さいごに
MCP AppsでClaude CodeのAskUserQuestionを再現してみました。AIチャットボットで便利になったけれどプロンプトを打つのが面倒だなーというケースへのソリューションとして良いと思います。
MCP Appsに対応したHTMLをレンダリングできるMCPクライアントが増えて欲しいところですね...(切実








