先に結論
スマートフォンのChatGPTは、フルスクリーンのMCP Appの上にタイトルバーとメッセージ入力欄を重ね、それらをセーフエリアのインセットとして報告します。私たちの実行では約47 pxと127 pxで、アプリを開いているあいだは、下のインセットが動きます。また、フルスクリーンに入るたび、出るたびに、会話の最初のツールの結果からパネルを作り直しました。インセットの分は、独自の余白を足さずに空けておき、スクリプトで配置した要素は、コンテキストが変わるたびに置き直し、結果が古くないかを読み込み時に確認してください。テストしたのは、iPhoneシミュレーター上のChatGPTのモバイル版ウェブサイトで、ネイティブアプリではありません。
スマートフォンのChatGPTでMCP Appを開くと、何が壊れましたか?
壊れたのは4つで、どれもフルスクリーンに関係していました。ScoreStarlingのパネルは、チャットの中に楽譜を表示します(接続方法)。フルスクリーンでは、音符をタップし、スマートフォンではプレーヤーの上にドッキングする選択カードから、その音符についてチャットに尋ねられます。2026年10月7日に、iOSシミュレーターのiPhone 17(402 × 874ポイント)のSafariで、ChatGPTのモバイル版ウェブサイトに載せて動かしました。パネルのフレームはクロスオリジンなので、スクリーンショットから状態を読み取りました。
| 起きたこと | 原因 | 修正 |
|---|---|---|
| プレーヤーが、画面の下から約3分の1の高さに浮いた | メッセージ入力欄の分をすでに含む下のインセット(約127 px)の上に、さらに固定の96 pxを足していた | インセットか96 pxの、大きいほうにする |
| 入力中のメッセージが長くなったり、「Thinking」バーが出たりすると、プレーヤーは上がったが、ドッキングしたカードは動かなかった | ChatGPTがインセットを動かす。プレーヤーはCSSで追従するが、カードはスクリプトで配置している | コンテキストが変わるたびにカードを置き直す |
| 編集のあと、フルスクリーンに入る、出るたびに、古い楽譜が読み取り専用で表示された | ChatGPTが、最初のツールの結果から新しいパネルを作った | 結果が古ければ、現在のバージョンを読み込む |
| チャットでの編集のあと、カードがプレーヤーの約40 px上に浮いた | 私たちの「Updated from the chat」(チャットから更新)の通知が、カードの位置の基準にしている領域を動かした | その領域のサイズが変わったときにもカードを置き直す |
なぜ、フルスクリーンのMCP Appは、スマートフォンのChatGPTのメッセージ入力欄の後ろに隠れるのですか?
ChatGPTが、メッセージ入力欄をこちらのフレームの上に描き、どれだけ覆うかを、下のセーフエリアのインセットで伝えてくるからです。インセットを無視すると、操作部が入力欄の下に入り込みます。私たちがやったように、その上に独自の余白を足すと、浮いてしまいます。MCP Appsの仕様は、safeAreaInsetsを「Safe area boundaries in pixels」(ピクセル単位のセーフエリアの境界)としか説明しておらず、OpenAIのドキュメントは、フルスクリーンでもメッセージ入力欄(composer)は重ねて表示されたままだと述べるだけで、サイズは示していません(どちらも2026年10月7日確認)。47 pxと127 pxは、スクリーンショットからの推定です。
10月6日、スマートフォンでの会話で、プレーヤーがChatGPTのタイトルバーの下に隠れていたため、ヘッダーを上のインセットの下に、プレーヤーを下に移しました。ただしCSSは、下のインセットの上にさらに、メッセージ入力欄用の96 pxを空けていました。34 pxのインセットでのローカルのテストでは問題なく見えましたが、ChatGPTの127 + 96では、そうなりませんでした。いまは、パネルは大きいほうを空けるようにし、ChatGPTのwindow.openai.safeAreaも読みます:
// panel.js, simplified: the host's inset or 96 px, never both
const bottom = Math.max(context.safeAreaInsets?.bottom || 0, window.openai?.safeArea?.insets?.bottom || 0);
root.style.setProperty('--safe-bottom', `${bottom}px`);
root.style.setProperty('--composer-space', fullscreen ? `${Math.max(0, 96 - bottom)}px` : '0px');
/* panel.css */
.stage > .player { bottom: calc(10px + var(--safe-bottom) + var(--composer-space)); }
フルスクリーンのMCP Appをスマートフォンで開いているあいだに、何が変わりますか?
インセットです。私たちの実行では、入力中のメッセージが2行に増えたときと、「Thinking」バーがメッセージ入力欄に替わったときに、下の端が動きました。仕様では、ホストはコンテキストのフィールドが変わるたびにui/notifications/host-context-changedを、変わったフィールドだけを付けて送ってよく、ビューはそれをマージします。
プレーヤーはCSS変数を基準にしているので、追従しました。選択カードはスクリプトで配置しているため取り残されたので、いまは、コンテキストが変わるたびに置き直しています。自分たちの通知が、2つ目のずれを起こしました。「Updated from the chat」は、楽譜の上に20秒間表示され、楽譜の領域を押し下げます。その領域の上端を基準にしていたカードは、プレーヤーに重なるようにずれ、途中で何かが置き直すと、通知が消えたあとに、プレーヤーの上に浮きました。いまは、その領域のResizeObserverでもカードを置き直し、アニメーションフレームごとに1回に抑えています:
this.context = {...this.context, ...params}; // host.js, simplified: merge the partial update
function hostContext(next) { applyContext(next); editor.queuePlace(); }
new ResizeObserver(() => { updateClip(); editor.queuePlace(); }).observe($('stage'));
なぜMCP Appは、フルスクリーンから戻ると、古いバージョンを表示するのですか?
スマートフォンのChatGPTは、フルスクリーンに入るたび、出るたびに新しいパネルを作り、編集の前に作られた、会話の最初のツールの結果をそれに渡したからです。仕様では、ホストはいつでもビューを破棄してよく、ツールの結果は、そのツールが動いた時点を記録しているだけです。
私たちのパネルは、半分がスナップショットで、半分が最新でした。そのリビジョンのページ画像と音符に加え、パネルが取得するときに読む現在のリビジョンのフィールドがあります。再構築されたパネルは、リビジョン0を描き、楽譜がリビジョン3にあることを知って、読み取り専用になりました。チャットでの編集を確認する処理は、サーバーのリビジョンを、すでに最新だったそのフィールドと比べていたため、追いつけませんでした。いまは、ホストが渡した結果を楽譜がすでに追い越している場合、パネルは現在のリビジョンを一度読み込みます。ただし、結果に承認待ちの提案された変更が含まれているときは除きます。それは、食い違っていてよいものだからです。
ChatGPTのスマートフォンでの動作は、スマートフォンなしでどうテストしますか?
本物のホストを再現環境で模倣し、修正を外すとどのテストも失敗することを確かめます。使っているのはsunpeakで、Playwrightのテスト用にChatGPTとClaudeを再現します。そのモバイル版ChatGPTシェル(バージョン0.20.91)は、タイトルバーとメッセージ入力欄を描き、入力欄を92 pxのインセットとして報告します。これらのバグのために書いた3つのテストが、402 × 874のタッチスクリーン上で、残りを補います。ChatGPTのフレームと同じようにパネルの親になっている、シェルのサンドボックスフレームを通して、127、160、70、127 pxの新しいインセットを順に送り、編集のあとはパネルを読み込み直して、最初のツールの結果を渡します。もう1つのテストは、チャットと同じように、パネルの外で楽譜を編集します。プレーヤーは、インセットと96 pxの大きいほうの0〜24 px上に、カードはプレーヤーの0〜16 px上に収まる必要があります。
- 本物のホストで動かすシミュレーター上のChatGPTのモバイル版サイト
- 原因を1つ直すデプロイし、アプリのツールを更新して、もう一度確かめる
- ホストを模倣する再現環境のフレームからホストのメッセージを送る
- 修正を取り消す修正がなければ、新しいテストは失敗しなければならない
| 取り消した修正 | 失敗したもの |
|---|---|
| インセットか96 px。両方を足さない | メッセージ入力欄のテスト:「プレーヤーが入力欄に近い」 |
| コンテキストの変更でカードを置き直す | メッセージ入力欄のテスト:「カードがプレーヤーに重ならない」 |
| 再構築されたパネルでの現在のバージョン | 再構築のテスト:「元に戻す」が無効のまま |
| 領域のサイズ変更でカードを置き直す | 通知のテスト:「カードがプレーヤーにドッキングしている」 |
この3つを1回実行すると、59.9秒かかりました。同じ日に4つ目を加え、スマートフォンのエディターの1行のヘッダーとプレーヤーを確認しています。貢献者向けのルールでは、フルスクリーンのレイアウト、インセット、カード、結果の処理を変えたら、スマートフォンのテストを必須としています。スマートフォンでの実行では、「この音をCに変えて」という依頼に対して、ChatGPTが選択されたB4を、7度下のC4に変えてしまうことも見つかりました。ツールの説明に、最も近いオクターブを選ぶルールを加えると、本物のモデルを使ったローカルのテストは、3回中2回の成功から、6回すべての成功になりました。
まだ確認できていないこと
スマートフォンでの確認は、iOSシミュレーターとChatGPTのモバイル版ウェブサイトで行いました。実機もネイティブアプリも使っていません。まだ残っているのは次のことです:
- ChatGPTが実際に送るインセットの正確な値と、そのタイミング。私たちの値は推定で、再現環境はそれを使い回しています;
- Safariのツールバーが広がった状態。メッセージ入力欄とプレーヤーの下端が半分隠れます;
- キーボードが閉じたあとにSafariが残した、約156ポイントのページのスクロール。クロスオリジンのパネルでは元に戻せません;
- ChatGPTがまだ回答しているあいだにフルスクリーンを開くと現れる、最大約777ポイントの不透明な帯;
- ChatGPTの返信シートと、ChatGPT自身によるiframeの破棄。再現環境はこれらを模倣しません;
- パソコン版のChatGPT。フルスクリーンは、メッセージ入力欄がその外にあるサイドパネルです。そのインセットは読み取っていません。
スマートフォン向けMCP Appのチェックリスト
- 上下どちらのインセットも空けておく。メッセージ入力欄用のフォールバックも持つ場合は、大きいほうを使い、合計にはしない。
host-context-changedの通知は毎回マージし、スクリプトで配置したものはすべて、フレームごとに1回、置き直す。- 通知などでずれることがあるので、自分のコンテナのサイズ変化を監視する。
- 古いツールの結果からの再構築を想定し、読み込み時に、そのバージョンをサーバーのバージョンと比べる。
- ローカルのテスト用ホストには、本物のホストで測ったインセットを与える。
- OpenAIのドキュメントの勧めに従い、UIリソースのURIをキャッシュキーとして扱い、新しいものを公開したあとは、ChatGPTでアプリのツールを更新する(プラグインの設定で「Manage app」(アプリを管理)、「Refresh tools」(ツールを更新)の順に選ぶ。2026年10月7日確認)。
- 本物のホストで見つけたことは、すべて、修正がなければ失敗する再現環境のテストにする。
出典
- SEP-1865: MCP Apps: Interactive User Interfaces for MCP (stable, 2026-01-26)、Model Context Protocol
- Add UI to your MCP server、OpenAI
- UI guidelines、OpenAI
- Reference(
window.openaiコンポーネントブリッジ)、OpenAI - MCP Testing Framework、sunpeak
よくある質問
ChatGPTは、スマートフォンでメッセージ入力欄をsafeAreaInsetsに含めますか?
私たちの実行では、含めていました。ChatGPTのモバイル版ウェブサイト(iPhone 17シミュレーター、2026年10月7日)では、下のインセットは約127 pxでメッセージ入力欄の分が入っており、上のインセットは約47 pxでタイトルバーの分が入っていました。MCP Appsの仕様は、インセットに何が含まれるかを定めていません。そのため、報告された分の空きはそのまま空けておき、その上に独自の余白を足さないでください。
ChatGPTは、フルスクリーンのアプリを開いているあいだに、インセットを変えますか?
私たちの実行では、変えました。入力中のメッセージが2行になったときと、ChatGPTの「Thinking」バー(思考中を示す表示)が出たときに、どちらも下の端が動きました。ui/notifications/host-context-changedが届くたびに、新しいインセットを適用し、通知に含まれるフィールドを手元の値にマージして、スクリプトで配置しているものはすべて置き直してください。
スマートフォンでフルスクリーンに切り替えると、なぜMCP Appに古いデータが表示されるのですか?
スマートフォンのChatGPTは、フルスクリーンに入るたび、出るたびに、会話の最初のツールの結果から新しいパネルを作ったため、私たちのパネルには、その後の編集より前の楽譜が表示されました。ビューが読み込まれたときに、結果のバージョンをサーバーのバージョンと比べ、結果が古ければ、現在の状態を一度読み込んでください。
ChatGPTのスマートフォンでのレイアウトは、スマートフォンなしでテストできますか?
一部は可能です。スマートフォンで見えたことを、sunpeakのモバイル版ChatGPTシェルでPlaywrightを使って再現しています。動くインセット、再構築されたパネル、チャットからの編集です。どのテストも、修正を取り消すと失敗します。ChatGPTが実際に送る値、Safariのツールバー、キーボード、ChatGPTの返信シート、ネイティブアプリには、やはり実機が必要です。



