埋め込みWebView・ブラウザ拡張ホストへの統合(遅延注入・登録タイミング・切り分け)
ページ上のエージェントツールはSPAマウント前に同期登録し、リソースidの解決は共有promiseで遅延する
10日前
WebMCPのnavigator.modelContextのように、ページ自体がAIエージェント向けツールを公開するWebアプリでは、ツール登録をReact/Vue等のフレームワークのmount完了後(例: useEffect内)に行うと、ホスト(ブラウザ/エージェントアプリ)がページ読込み直後にツール一覧を問い合わせた時点でまだ何も登録されておらず、「No tools available」と判定される。
原因: フレームワークのmountはネットワーク往復(例: リソース作成API呼び出し)を含む非同期処理の後にやっと完了することがあり、ホストの「ツールは存在するか」の判定タイミング(例: DOMContentLoaded直後)と一致しないことがある。ツール登録自体は本来即時にできる同期処理なのに、フレームワークのライフサイクルに引きずられて遅されてしまう。
適用条件: 「ページ読込み直後の商進判定に間に合う必要がある」ツール/能力公開に限って重要。単なるUI部品の初期描画ならマウント後で問題ない。
対処方法: (1) ツール登録自体はフレームワークのmountより前のモジュール評価タイミング(例ReactならcreateRoot(...).render()を呼ぶ前)で同期実行し、ネットワーク/マウントを一切待たない。(2) 登録時に必要な実行時専用リソース(例: ページ固有のID)は登録時に確定させず、各ツールの実行時に遅延解決する。(3) その遅延解決はモジュールスコープの単一promiseでメモ化し、ツール実行とフレームワーク自身の初期化ロジックの両方が同じpromiseを待つように一元化する(別々の経路が別々にリソースを作成する二重作成バグを構造的に防ぐ)。(4) ツール実行結果をフレームワークのUI状態へ反映するコールバックは、登録時点では未接続でよく、後からsetter関数で接続する方式にし、未接続時の実行はUI更新をスキップするだけで成功扱いにする。
確認方法: ホスト側の最小シム(registerToolを受け取って配列に記録するだけのモックオブジェクト)をpage.addInitScript相当で事前注入し、domcontentloadedイベントの瞬間に登録済みツール数を問い合わせる自動テストで検証できる。さらに、ルート直開き直後にツールを直接呼び出してリソース作成系のエンドポイントを叩き、バックエンドの行数が(1回のページ読込みで)ちょうど+1だけ増えることを確認すれば、ツール実行経路とフレームワークの自己初期化経路が二重作成していないことを実証できる。
教訓: 「WebMCPツールをReactコンポーネントの一部として登録すればよい」という直感は、フレームワークのmountタイミングとホストのツール存在判定タイミングが一致する保証がない限り成り立たない。即時性が求められる公開面(登録)と、遅延してよい実行面(リソース解決・UI接続)を分けて設計するのが安全。
ホストがページ読込み後にグローバルAPIを遅延注入する可能性には、ポーリング+イベント両構えを冗長化ガード付きで対処する
10日前
ブラウザ拡張機能や埋め込みWebView(例: チャットアプリ内蔵ブラウザ)がページに独自API(例: エージェントツール公開用のグローバルオブジェクト)を注入する設計では、その注入タイミングがページの読込み完了と必ずしも一致しない(ページ読込み後に非同期に注入されるホストが存在する)。ページ側のコードがモジュール評価時に一度だけif (!hostAPI) returnで判定していると、そのホストでは永久にno-opになる。
適用条件: 「ページ自体が能力を公開する側であり、ホストの注入タイミングを制御できない」ケースに限られる。自前で提供するグローバルAPIなら不要。
対処方法: 初回判定が失敗したら、(1) 短隔ポーリング(例: 250ms間隔・上限椖20秒)と (2) DOMContentLoaded/loadイベントリスナーの両方で再試行する。どちらか一方だけでは、ポーリング間隔と注入タイミングがたまたまタイミング良く一致しなければ検知が遅れるリスクが残るため、両構えにする。複数のトリガー(ポーリングtickとイベント)が竞合しても実際の登録処理は1回だけ実行されるよう、「登録済みフラグ」を先に立ててから実体登録を行う冗長化ガードを入れる。一定時間(上限)を超えてもAPIが現れなければ「未対応確定」の状態に確定させる。
確認方法: 自動テストでは、ページ読込み時にホストAPIを一切注入せずに通常読込みし、存在しないことを確認した上で数秒後にスクリプト実行で後付け注入し、その後ポーリング/イベントリスナーが検知して実際に登録が完了することを確認すれば、後付け注入をブラウザのaddInitScript相当の事前注入では再現できないことに注意(事前注入は常にページスクリプトより前に実行されるため、遅延注入の再現には使えない)。
教訓: 「ホストのAPIはページ読込み時点ですでに存在するはず」という仮定は、自分が実装を控制できない外部ホスト(ブラウザ拡張、アプリ内蔵WebView)では成り立たない。feature detectionの一回判定だけでなく、登録の冗長化を伴う継続監視が必要になる。
devtoolsのない埋め込みWebViewを切り分けるには、重要な非同期状態遷移をページ上に常表示する
10日前
チャットアプリの内蔵ブラウザのような、開発者ツール(console・networkタブ)を开けないホスト環境では、「実 Chromeでは成功するのに内蔵ブラウザだけ失敗する」種の不具合を、ログやスクリーンショットだけで切り分けようとしても手助けがない。なぜ失敗しているか(未初期化なのか、タイムアウトなのか、不対応確定なのか)を外部から区別できない。
適用条件: 対象ホストがdevtoolsを使えない(または使いづらい)埋め込み環境で、非同期な初期化(ホストAPIの注入待ち、能力登録など)を含む場合に重要。通常のWeb開発(自分のChrome DevToolsで即座に確認できる)なら不要。
対処方法: 非同期初期化の重要な状態遷移(例: waiting→registered(n)→unsupported)を公開 APIとして公開し、ページ上の控えめな1行表示(例: フッターのステータス行)で常時見えるようにする。表示はローディング中の画面でも隠さず、可能な限り早い段階から存在させる。リスナー接続パターン(setter関数で後付け接続し、接続直後に現在値を即時再送信する)にすると、フレームワークコンポーネントがどのタイミングでmountしても遷移を取りこぼす。
確認方法: 実ホスト環境(今回はChatGPTアプリ内蔵ブラウザ)での検証中、開発者はスクリーンを見てこの1行だけで「リトライ中か、確定失敗か、登録済みか」を判別できる。自動テストでも同じセレクタを問い合わせて状態遷移を直接検証できるので、行卐採取やスクリーンショット目視より安価な回帰テストになる。
教訓: 「内部実装の状態はconsole.logで十分」という直感は、自分のツールで検証できる間は正しいが、対象ホスト自体がdevtoolsを提供しない場合は成り立たない。その場合の最小コストな代替手段は、重要な非同期状態をUI自体に常時可視化すること。
埋め込みWebViewターゲットのWebアプリでは window.prompt/alert/confirm に重要操作を依存させない
10日前
チャットアプリ内蔵ブラウザや一部のモバイルWebViewを主要ターゲットにするWebアプリでは、window.prompt()・alert()・confirm()などのブラウザネイティブダイアログAPIを主要機能(例: ノード作成時のテキスト入力)に使うと、その環境ではダイアログ自体が抑制され(表示されない・JS実行がブロックされるなど)、ユーザーに何のエラーも見せずに機能が並発する。実機の内蔵ブラウザで初めて発見されやすく、デスクトップChromeでの検証だけでは見つからない。
適用条件: 主要ターゲット環境が自社で完全に制御できない埋め込みブラウザ(チャットアプリ内蔵・SNSアプリ内蔵・LINEブラウザ等)である場合に重要。自社ネイティブアプリのWebViewや、通常のデスクトップブラウザ向けなら優先度は低い。
対処方法: テキスト入力などの入力取得は、promptではなくページ内のinline編集UI(contentEditable相当なinput/textareaをその場で表示しblur/Enterで確定)に置き換える。確認ダイアログ(confirm相当)も同様に、ページ内のモーダル・ツールバーの確認ボタンに置き換える。
確認方法: 実際の主要ターゲットブラウザ(実機または同等のWebView)で当該機能を実行し、ダイアログが表示されるかを確認する。自動テストではPlaywrightのpage.on('dialog', ...)で自動応答するテストはデスクトップChromium上のテストでしかなく、埋め込みWebViewの抑制挙動を再現しないので、「テストがPASSする」だけでは本番環境での動作を保証しないことに注意。
教訓: 「Web標準APIなのでどこでも動くはず」という直感は、ポリシーでブロックされうるブラウザネイティブダイアログ系 API(prompt/alert/confirm)では成り立たない。主要ターゲットが自社で制御できない埋め込みWebViewなら、設計段階でこれらのAPIを候補から外す。