Electron Driver について
Electron Driver は、ビルド済み Electron アプリを AI エージェントから操作する Playwright ベースの MCP サーバーです。テスト、デバッグ、アクセシビリティ確認に使え、画面操作や状態確認、複数ウィンドウ制御、レンダラーとメインプロセスでの JavaScript 実行に対応します。
主な機能
- 01Electron アプリの起動と終了
- 02クリック、入力、ドラッグ、スクロール
- 03スクリーンショット撮影とコンソールログ取得
- 04DOM、ARIA ツリー、要素状態の確認
- 05アプリウィンドウの一覧表示と切り替え
- 06レンダラーまたはメインプロセスでの JavaScript 実行
活用シーン
- 01実装したアプリ機能の動作確認
- 02ビジュアルリグレッションテスト
- 03ARIA スナップショットによるアクセシビリティ監査
- 04説明された画面上の不具合の再現
MCP 対応
MCP サーバーとして、Electron アプリの操作・検査用ツールを 38 種類、MCP 対応のエージェントホストに提供します。README では Claude Code、Claude Desktop、Cursor が例示されています。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
electron-driver
AI エージェントから Electron アプリを操作できます。 クリック、入力、ドラッグ、スクリーンショットの撮影、レンダラープロセスまたはメインプロセスでの JavaScript の評価、コンソールログの読み取り、マルチウィンドウアプリへの対応、アクセシビリティスナップショットの取得など、すべて Claude Code、Claude Desktop、Cursor、その他あらゆる MCP 対応エージェントホストに接続できる MCP(Model Context Protocol)サーバーを介して実行できます。
https://github.com/user-attachments/assets/a95500d2-28d2-4ee1-9965-8f7e1ef54caa
Playwright の実験的な _electron API を基盤としています。コンパイル済みのメインプロセスのエントリーポイントを指定できれば、React、Vue、Svelte、バニラなど、あらゆる Electron アプリで動作します。
ステータス: v0.3.0。初の一般公開リリースです。実際のワークフローをカバーする 38 個のツールを備えています。
このパッケージが生まれた理由
AI エージェントはデスクトップアプリがどう動作すべきかを推論できますが、自力でアプリを見たり操作したりすることはできません。ウェブブラウザーにはエージェントによる自動化の選択肢が豊富にありますが、Electron にはほとんどありません。このパッケージはそのギャップを埋めます。コンパイル済みの Electron アプリへのパスをエージェントに渡せば、人間と同じようにアプリを操作できます。
よくあるユースケース:
- エージェントが、実装した機能を実際にアプリを起動して表示結果を確認することで検証する
- リファクタリング中のビジュアルリグレッションテスト
- ARIA ツリースナップショットによるアクセシビリティ監査
- 自然言語による説明をもとにしたバグの再現
- サブエージェントに、仕様を満たすまで UI を反復改善する方法を教える
インストール
Node 18 以降と、ビルド済みの Electron アプリが必要です。
npm install electron-driver
Playwright ブラウザーを別途インストールする必要はありません。_electron が Electron バイナリを直接操作します。
エージェントホストへの登録
Claude Code(プロジェクトスコープ)
リポジトリのルートに .mcp.json を作成します:
{
"mcpServers": {
"electron-driver": {
"command": "npx",
"args": ["electron-driver"]
}
}
}
Claude Code(ユーザースコープ — すべてのプロジェクトで利用可能)
claude mcp add electron-driver --scope user -- npx electron-driver
Claude Desktop / Cursor / その他
ホストの MCP 設定に npx electron-driver、または node_modules/electron-driver/index.mjs の絶対パスを指定して追加します。
基本的な考え方
サーバーは一度に厳密に 1 つの Electron セッションを管理します。start_app でアプリを起動し、それ以外のツールですべて操作し、stop_app で終了します。スクリーンショットはセッションディレクトリに保存され、start_app のたびに削除されます。古いファイルが溜まったり、残存した成果物が残ったりすることはありません。セッション中のすべてのツール呼び出しは
<project>/.electron-driver/driver.log に記録されます。
エラーには安定した code フィールドが含まれるため、呼び出し元はメッセージの正規表現マッチングを使わず、プログラムから分岐できます:
| コード | 意味 |
|---|---|
NOT_RUNNING | 実行中のセッションが必要なツールですが、セッションがありません |
ALREADY_RUNNING | セッションが存在する状態で start_app が呼び出されました |
TIMEOUT | アクションがタイムアウトしました |
NOT_FOUND | セレクターまたはファイルに一致するものがありません |
FILE_NOT_FOUND | パス指定の入力が存在しないファイルを指しています |
BAD_ARGUMENT | 引数の検証に失敗しました |
UNKNOWN_TOOL | ツール名が認識されません |
ERROR | その他すべて |
ツール
目的別に分類された全 38 個のツールです。セレクターを使うツールはすべて、Playwright の完全なセレクターエンジンに対応しています:CSS、text=、role=、[aria-label=]、:has-text()、スコープ指定(main >> button)など。
ライフサイクル
start_app — アプリを起動します。main(コンパイル済みメインエントリーの絶対パス)、省略可能な cwd、args、env、screenshotsDir、timeoutMs を受け取ります。{ title, url, viewport, screenshotsDir, logFile } を返します。シングルインスタンスロックによる失敗を検出し、接続が予期せず切れたというエラーの代わりに、役立つヒントを提示します。
stop_app — 正常に終了します。すでに停止しているセッションに対しても安全に実行できます。
info — { title, url, viewport: {width, height, devicePixelRatio}, uptimeMs }。ビューポートは window.innerWidth/innerHeight から取得されます。
キャプチャ
screenshot — ページ全体を PNG で撮影します。ファイル名を指定するには、拡張子を付けずに name を渡します。{ path } を返します。
cleanup_screenshots — 現在のセッションのスクリーンショットディレクトリを空にします。
console_logs — 最近のレンダラーのコンソールメッセージ(log/info/warn/error/debug/pageerror)とメインプロセスの stdout/stderr を取得します。1000 件のローリングバッファです。source(renderer/main/all)、type、limit でフィルターできます。読み取り後にバッファを空にするには、clear: true を渡します。
操作
click — 要素をクリックします。オプション:timeoutMs、button(left/right/middle)、clickCount、force(操作可能性のチェックを省略)、position(要素内のオフセット位置をクリック)。
type — テキスト入力を fill し、既存の内容を置き換えます。高速ですが、実際の入力要素でのみ動作します。エディター/CodeMirror/contenteditable では keyboard_type を使用してください。
keyboard_type — 実際のキー押下イベントを文字ごとに発生させて入力します。最初に要素をクリックするには、focusSelector を渡します。フォーカスがなく、フォーカスセレクターも渡されていない場合は、結果に警告が含まれます。
press — キーまたはキーの組み合わせを押します:"Escape"、"Enter"、"Control+S"、"Shift+Tab"、"Control+Shift+P"。
press_sequence — フォーカスセレクターなしの keyboard_type の別名です。
hover — 要素にカーソルを合わせます。オプション:timeoutMs、force。
drag — 実際の Chromium 入力イベントを使用して、ある地点から別の地点へドラッグします(Playwright の CDP マウスパイプライン経由)。信頼されたブラウザーイベントであるため、Chromium のポインターパイプラインは副作用として対応する PointerEvents を生成します。そのため、React の onPointerDown、ネイティブの pointerdown リスナー、setPointerCapture、CSS の :hover/:active、その他すべてのポインター処理は、実際のユーザーがドラッグした場合とまったく同じように動作します。座標の単位は CSS ピクセルです。detectSelector を渡すと、ドライバーがドラッグ前後の要素を測定し、結果に detect.moved を含めます。これは、最小値/最大値の制限に気付かれないまま当たったドラッグを検出する唯一の信頼できる方法です。
{
"from": { "x": 275, "y": 400 },
"to": { "x": 420, "y": 400 },
"detectSelector": ".sidebar-resize-handle"
}
主な方法で検出対象が移動しなかった場合、ドライバーは React のハンドラーを fiber プロパティへのアクセス経由で直接呼び出し、document と window の両方で move/up イベントをディスパッチする方法に自動的にフォールバックします。これにより、既知の React スプリッターのパターンをすべてカバーします。fiberFallback: false でフォールバックを無効にできます。結果には strategy("pointer-capture" または "react-fiber")が含まれます。
clear_input — 入力欄またはテキストエリアを空にします。
select_option — <select> から value、label、または index で選択します。
check — チェックボックスまたはラジオボタンにチェックを入れます。オプション:timeoutMs、force。
uncheck — チェックボックスのチェックを外します。
scroll — コンテナー(selector を渡します)またはウィンドウをスクロールします。絶対位置(x、y)または相対量(dx、dy)に対応しています。
scroll_into_view — 要素が表示されるようにします。すでに表示されている場合も安全に使用できます。
drop_file — 合成 DragEvents と DataTransfer を使って再構築した File により、ファイルをターゲットにドロップする操作をシミュレートします。Web API(FileReader、File.text() など)経由でファイルを読み取るアプリで動作します。file.path は設定されません。webUtils.getPathForFile() に依存するアプリでは、eval_main を使って独自の IPC ハンドラーを直接呼び出す必要があります。
set_input_files — ファイルアップロード UI のテストに適した方法です。ネイティブのダイアログを使わず、<input type="file"> にファイルを設定します。アプリが実際のファイル入力を使う場合、drop_file よりもはるかに信頼性があります。
待機
wait — ミリ秒単位で指定する固定の一時停止です。他の方法を優先してください。
wait_for_selector — セレクターが指定した状態(attached/detached/visible/hidden)になるまで待機します。timeoutMs に対応しています。成功時には count、box、elapsedMs を返します。タイムアウト時のエラーには、elapsed vs requested が含まれます。
wait_for — JavaScript の述語(関数本体。return を使用)をポーリングし、真の値を返すまで待機します。オプション:timeoutMs、pollMs。
状態の確認と読み取り
exists — 待機せずに { exists, count } で素早く確認します。完全なセレクターエンジンに対応しています。
get_text — 最初に一致した要素のテキストコンテンツを取得します。完全なセレクターエンジンに対応しています。{ exists, text } を返します。
get_attribute — 名前を指定して HTML 属性を読み取ります。{ exists, value } を返します。
get_value — input/textarea/select の現在の値を読み取ります。
get_bbox — CSS ピクセル単位の境界ボックスを { x, y, width, height } で取得します。ドラッグする前や、オフセットを指定してクリックする前に使用します。
get_computed_style — 1 つ以上の計算済み CSS プロパティを読み取ります。properties 配列を渡してください。
elements_list — セレクターに一致する要素について、タグ、id、クラス、テキストの抜粋、ボックス、主要な属性を列挙します。「この画面にはどのボタンがあるか」を調べるのに便利です。デフォルトでは最大 50 件です。limit で調整できます。
focused_element — 現在フォーカスされている要素のタグ/id/クラス/テキストと境界ボックスを取得します。意味のある要素にフォーカスされていない場合は { focused: false } を返します。
accessibility_snapshot — ARIA ツリーを JSON として取得します。アクセシビリティ監査や、ロールによる要素の検索に役立ちます。すべてのノードを含めるには interestingOnly: false を渡します。サブツリーを取得するには root を渡します。
複数ウィンドウ
windows_list — アプリで開いているすべての BrowserWindow について、id、タイトル、URL、フォーカス/表示状態/状態フラグを取得します。
switch_window — 後続のツール呼び出しの対象を別のウィンドウに切り替えます。index または titleMatch を渡してください。
ダイアログ
dialog_handler — JavaScript ダイアログ(alert/confirm/prompt/beforeunload)に対する自動応答を設定します。action: "accept" | "dismiss" と、prompt() 用の任意の text を渡します。once: true(デフォルト)を指定すると、最初のダイアログの後に自動応答設定を解除します。
評価のための抜け道
eval_renderer と eval_main はどちらも同じ仕様です。関数本体を渡し、値を返すには return を使用します。async/await に対応しており、任意の arg ペイロードはローカル変数 arg として使用できます。
eval_renderer — レンダラー(ページ)コンテキストで評価します。
{
"js": "return document.querySelectorAll(arg.selector).length",
"arg": { "selector": ".item" }
}
eval_main — Electron のメインプロセスで評価します。関数本体には electron(Electron モジュール全体)と arg が渡されます。
{
"js": "return electron.app.getName()"
}
{
"js": "const w = electron.BrowserWindow.getAllWindows()[0]; w.webContents.send('open-file', arg.path); return true",
"arg": { "path": "C:/docs/README.md" }
}
DOM 側からアクセスできないものには、eval_main を抜け道として使用します。IPC ハンドラーの呼び出し、ユーザーパスの読み取り、セカンダリウィンドウの操作、ネイティブダイアログを使用するアプリでのネイティブダイアログの回避などができます。
セレクター早見表
text=Open File // exact text match
text=/^Save$/ // regex
[aria-label="Settings"] // ARIA attribute
role=button[name="Close"] // ARIA role
button:has-text("Save") // CSS with text predicate
button.primary // plain CSS
main >> text=Save // scoped
セレクターエンジンの一貫性
セレクターベースのすべてのツール(click、hover、wait_for_selector、get_text、get_attribute、get_value、get_bbox、exists、elements_list、scroll_into_view、select_option、check、uncheck、set_input_files)は、Playwright の完全なロケーターエンジンを使用します。Playwright が受け付けるものは、このツールでも受け付けられます。text=、role=、:has-text() も含まれます。
内部で eval_renderer を使って低レベル DOM を読み取るツール(get_computed_style、scroll、focused_element、drop_file)は、ネイティブの document.querySelector を使用するため、CSS のみをサポートします。該当する場合は、各ツールの説明に記載されています。
トラブルシューティング
「Electron プロセスが起動直後に終了する。」 アプリの別のコピーがすでに実行中で、単一インスタンスロックを取得しています。2つ目のプロセスは app.requestSingleInstanceLock() によって終了します。実行中のインスタンスを閉じて(タスクバーとバックグラウンドプロセスを確認してください)、再試行してください。
drag は ok:true を返したのに、detect.moved が false になる。 最もよくある原因は、ドラッグ先に最小値または最大値の制限があることです(例:MAX_WIDTH に達した、サイズ変更可能なサイドバー)。反対方向にドラッグして、ドラッグ処理が機能していることを確認してください。制限が原因でない場合は、React ファイバーのフォールバックが自動的に検出するはずです。結果の strategy フィールドを確認してください。それでも moved:false が返る場合は、eval_renderer または eval_main を使って、アプリ独自のドラッグ API を直接呼び出してください。
type が「Element is not an <input>...」をスローする。 ボタン、div、または contenteditable 要素を指定しています。代わりに focusSelector を指定して keyboard_type を使ってください。
要素が明らかに存在するのに、click がタイムアウトする。 モーダルの背景、ツールチップ、トーストなど、何かが要素を覆っています。まず exists を使って数を確認し、次に force: true を試すか、eval_renderer を使って getComputedStyle(el).pointerEvents を確認してください。
ネイティブダイアログが表示されない。 Playwright では、OS レベルのファイル選択ダイアログ、保存ダイアログ、システムアラートを確認できません。eval_main を使って、UI ボタンが使うものと同じ IPC ハンドラーを呼び出してください。JavaScript ダイアログ(alert/confirm/prompt)には、dialog_handler を使って自動応答してください。
開発ビルドとコンパイル済みアプリ。 これは開発サーバーの出力ではなく、ビルド済みアプリを操作します。start_app の前にビルドコマンドを実行し、ソースを変更した後は再ビルドしてセッションを再起動してください。
一度に実行できるセッションは1つです。 セッション実行中に start_app を呼び出すと、ALREADY_RUNNING が返されます。先に stop_app を呼び出してください。
ログ。 セッションがアクティブな間、すべてのツール呼び出しは <project>/.electron-driver/driver.log に記録されます。エージェントが停止した原因のデバッグに役立ちます。
スクリーンショットの保存場所。 デフォルトでは <project>/.electron-driver/screenshots に保存されます。<project> は .git または package.json を含む最も近いディレクトリです。start_app の screenshotsDir で変更できます。
セキュリティ
このサーバーは、接続されたエージェントに Electron アプリへの完全な制御を与えます。これには、メインプロセスでの任意のコード実行(eval_main 経由)も含まれます。 Electron のメインプロセスは、ファイルシステム、ネットワーク、子プロセスなど、Node.js への無制限のアクセス権を持ちます。これは意図された仕様です。実際のアプリを操作できるほどドライバーを強力にするためです。
これが意味すること:
- stdio 経由でのみ使用してください(デフォルト)。このサーバーを HTTP、WebSocket、その他のネットワークトランスポート経由で公開しないでください。stdio を使うことで、このサーバーは起動元のプロセス(ローカルの Claude Code または Claude Desktop のセッション)に結び付けられます。
- エージェントを信頼してください。 これらのツールを呼び出すエージェントは、
eval_mainを使ってあなたのマシン上で何でも実行できます。信頼できるエージェントのみ接続してください。 - マルチテナント環境では使用しないでください。 これは単一ユーザー向けのローカルマシン用ツールです。共有サーバー、信頼できない入力を扱う CI パイプライン、または呼び出し元が悪意を持つ可能性のある環境での使用を想定していません。
drop_fileとset_input_filesはローカルファイルを読み込み、内容をレンダラーに渡します。 ファイルパスは信頼できるソースから取得してください。
Claude Code でこれを実行する場合、リスクの度合いは Claude Code にターミナルへのアクセス権を与える場合と同じです(すでにアクセス権がある場合)。このドライバーは、ターミナルでの eval で実行できる以上の機能を追加するものではありません。エージェントが使いやすい形で機能を提供するだけです。
既知の制限事項
- Playwright の
_electron名前空間は、上流では公式に実験的な機能です。 動作の遅いマシンでは起動がタイムアウトすることがありますが、通常は再試行で解決します。 - 主に Windows で開発されています。Playwright が対応しているため Mac と Linux でも動作するはずですが、テストは十分ではありません。バグ報告をお待ちしています。
switch_windowは以降の呼び出しを選択したウィンドウに振り分けますが、コンソールログのバッファーには最初のウィンドウのログが格納されます。複数ウィンドウのコンソールキャプチャーは v0.4 で予定されています。drop_fileではfile.pathは設定されません。webUtils.getPathForFile()を使用するアプリでは、独自の IPC を使ってeval_mainを使用する必要があります。- ネットワークリクエストのキャプチャー機能はまだ組み込まれていません。v0.4 で予定されています。
実装に関するメモ
このプロジェクトに関心がある方や、貢献を検討している方向けの情報です。
- MCP サーバープロセスが所有する単一の Electron セッションです。
start_appのたびにスクリーンショットを消去します。これは意図的な動作です。- コンソールログは、最大 1000 件のローリングバッファーに記録されます。
- すべてのツール呼び出しがログに記録されます。エラーには安定した
codeフィールドが含まれます。 - エラーメッセージは、基盤となる Playwright のメソッドではなく、ドライバーツールに起因するものとして書き換えられます。
- 単一インスタンスロックの検出には、「起動から 5 秒以内にプロセスが切断された」という条件を使用します。これは実際の障害の発生パターンに基づいています。
- 評価コードは async IIFE でラップされるため、
returnとawaitの両方が機能します。 - MCP クライアントが
argフィールドを文字列化する場合に備え、argのペイロードはサーバー側で変換されます(文字列の場合は JSON として解析)。 - ステータスメッセージには stderr を使用し、stdout は MCP プロトコルフレーム専用です。
コントリビュート
Issue と PR を歓迎します。ローカルで実行するには、次のコマンドを使用します。
cd electron-driver
npm install
node index.mjs # stdio MCP server
サーバーは stderr に起動完了のバナーを出力し、stdin で MCP フレームを待ち受けます。任意の MCP クライアントを使って、実際の Electron アプリでテストしてください。
ライセンス
MIT
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。