ほとんどのAIプラットフォームはREST APIを通じて外部サービスに接続します。これは構造化データ — CRMの連絡先の読み取り、Jiraチケットの作成 — には機能しますが、ブラウザ内で起きているすべてを見逃します。Gmailのメール下書き、読んでいるSlackスレッド、ServiceNowで半分入力されたフォーム。
Model Context Protocol(MCP)を使用してこのギャップを埋めるブラウザ拡張機能を構築しました。この記事ではアーキテクチャの決定、遭遇した問題、システムがエンドツーエンドでどのように動作するかを説明します。
問題:ブラウザの状態はAPIから見えない
APIはデータベースレコードを提供します。画面に表示されているものは提供しません。Gmailでフォローアップメールを作成中の営業担当者は、APIでは捕捉できないコンテキストを持っています — トーン、半分書かれた段落、横に開いているタブ。AIワークフローが、隣に座っている人間のアシスタントのように、このライブブラウザコンテキストで動作することを望みました。
アーキテクチャ概要
拡張機能はWXT 0.20(Manifest V3)上にSvelte 5とTypeScriptで構築されています。JieGouサーバーにWebSocket経由で接続するMCPクライアントを実装し、AIワークフローが呼び出せる60以上のブラウザ自動化ツールを公開しています。
メッセージフローは以下のようになります:
MCP Server → WebSocket → Background Service Worker → Content Script → Page
レシピやワークフローがブラウザインタラクションを必要とする場合、WebSocket接続を通じてJSON-RPC 2.0のツール呼び出しを送信します。拡張機能のバックグラウンドワーカーがそれを適切なツールエグゼキューターにルーティングし、アクティブタブにコンテンツスクリプトを注入して結果を返します。
WebSocketブリッジ
WebSocketプロキシは認証、ハートビート、再接続、トークンリフレッシュを処理します。
認証は接続時に行われます — クライアントがJWTトークン付きのauthenticateメッセージを送信します。サーバーがそれを検証し、ツール呼び出しの受け付けを開始します。
ハートビートはアプリケーションレベルで15秒ごとに実行されます(WebSocketプロトコルのpingに依存しません)。pongが5秒以内に届かない場合、接続は切断されたとみなされ再接続が開始されます。
自動再接続は切断時に3秒の遅延と指数バックオフを使用します。トークンリフレッシュはJWT有効期限の5分前にRESTエンドポイント経由でプロアクティブに行われるため、期限切れの認証情報で接続が切れることはありません。
ツールエグゼキューターパイプライン
すべてのツールは共通ヘルパーを提供するBaseBrowserToolExecutorクラスを拡張しています:injectContentScript()(ping/pong重複排除付き)、sendMessageToTab()、getActiveTabOrThrow()、タブフォーカス管理。
ツールはいくつかのカテゴリに分類されます:
ページインタラクション — click_element、fill_form_field、select_dropdown、check_box、scroll_page、navigate。CSSセレクターまたは自動生成された参照IDで識別されるDOM要素を操作します。
コンテンツ読み取り — read_pageはDOMを解析し、インタラクティブ要素に安定した参照IDを割り当てます。AIがページを「見る」方法です — 生のHTMLではなく、クリック可能な参照付きの構造化テキストを取得します。
プラットフォーム固有エクストラクター — web_fetcherにはGmail、Slack、Jira、Salesforce、ServiceNow、HubSpot用の専用パーサーがあります。汎用的なDOMスクレイピングの代わりに、各プラットフォームのマークアップ構造を理解し、クリーンで型付きのデータを抽出します。
ブラウザ内部 — javascriptはChrome DevTools ProtocolのRuntime.evaluate経由で任意のコードを実行し、network_captureはHTTPトラフィックを監視し、screenshotはビューポートまたは特定の要素をキャプチャし、gif_recorderはマルチステップインタラクションのアニメーション録画を作成します。
インジェクトスクリプト:ページのワールドで実行
アーキテクチャで最もトリッキーな部分はインジェクトスクリプトです。コンテンツスクリプトは分離されたワールドで実行されます — DOMは読めますが、ページのJavaScriptコンテキスト、Reactコンポーネントの状態、フレームワーク内部にはアクセスできません。
カスタムesbuildプラグインを介してIIFEとしてバンドルされ、MAINワールドに注入される16のTypeScriptモジュールがあります。これによりReact内部にアクセスし、ページレベルのAPIを呼び出し、シングルページアプリルーターとインタラクションできます。
インジェクションはping/pong重複排除を使用して同じスクリプトをタブに2回注入することを防ぎ、結果はwindow.postMessageを通じてコンテンツスクリプトに戻り、バックグラウンドワーカーに上がります。
学んだこと
Manifest V3のService Workerは一時的です。 ブラウザによっていつでも終了される可能性があります。WebSocket接続をService Workerの再起動に対して回復力のあるものにする必要がありました — 保留中のツール呼び出しを失うことなく透過的に再接続します。
プラットフォーム固有の解析は汎用スクレイピングに勝ります。 最初のバージョンではすべてに汎用的なDOM抽出を使用していました。GmailのHTMLは深くネストされ、更新ごとに変わります。各プラットフォーム(現在6つ)に対してターゲットを絞ったパーサーを書くことで、信頼性とデータ品質が劇的に向上しました。
MCPはこの用途に適したプロトコルです。 tools/listディスカバリーと型付きtools/call呼び出しを備えたJSON-RPC 2.0ベースは、実装するのに十分シンプルでありながら、信頼性を確保するのに十分な構造を持っています。カスタムプロトコルを構築するよりも拡張が容易だと感じています。
次のステップ
プラットフォーム固有ハンドラーの拡張、シングルページアプリナビゲーションにおける要素ターゲティングの信頼性向上、そしてより広いMCPエコシステムとツール定義を共有する方法の探索に取り組んでいます。
MCP統合やブラウザ自動化ツールを構築している方は、どのようなパターンが有用だったかぜひお聞かせください。プロトコルはまだ若く、コミュニティは一緒にベストプラクティスを模索しています。