desktop-mcp について
desktop-mcpは、ローカルデスクトップを観察・操作するWindows専用のMCPサーバーです。ツール群は個別に制御され、マウスとキーボードの入力はデフォルトで無効です。
主な機能
- 01PNGファイルとしてスクリーンショットを取得
- 02ウィンドウの一覧表示と情報確認
- 03ウィンドウのフォーカス、サイズ変更、最小化、復元
- 04マウスとキーボードの入力送信
- 05ffmpegによる画面録画
活用シーン
- 01表示中のデスクトップウィンドウを確認
- 02デスクトップ作業に合わせてウィンドウを配置
- 03明示的な有効化後にマウスとキーボード操作を自動化
- 04画面を録画
MCP 対応
ローカルのstdio MCPホストに、デスクトップの観察、ウィンドウ管理、入力、録画ツールを提供します。ウィンドウ、入力、録画の各ツール群は環境変数による明示的な有効化が必要で、入力はデフォルトで無効です。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
desktop-mcp
Windows デスクトップ操作用 MCP サーバーです。スクリーンショット、ウィンドウ管理、 マウス/キーボード入力、ffmpeg による画面録画に対応し、ツール グループごとに設定で有効化できます(入力はデフォルトで無効)。mcp-factory や rag-mcp と同じ 基準で構築されており、独自の pyproject、fastmcp サーバー、誠実な README、実際のテストスイートを備えています。
クイックスタート
pip install desktop-mcp
// ~/.claude.json (or any MCP-host stdio client config)
{
"mcpServers": {
"desktop-mcp": {
"command": "desktop-mcp"
// or: "command": "python", "args": ["-m", "desktop_mcp"]
}
}
}
window/input/record ツールグループは環境変数で有効化されます。明示的にオプトインしない限り、input は無効のままです。詳細な表については、以下の環境変数を参照してください。
ツールグループ
| グループ | ツール | デフォルトの状態 |
|---|---|---|
observe | screenshot, list_windows, get_active_window, window_info | 常に有効 |
window | focus_window, move_resize_window, minimize_window, restore_window | 環境変数で有効化。DESKTOP_MCP_ENABLE_WINDOW=1 でない限り無効 |
input | mouse_move, mouse_click, mouse_drag, mouse_scroll, key_press, hotkey, type_text | 環境変数で有効化。デフォルトで無効 — DESKTOP_MCP_ENABLE_INPUT=1 が必要 |
record | record_start, record_status, record_stop | 環境変数で有効化。DESKTOP_MCP_ENABLE_RECORD=1 でない限り無効 |
無効なグループは、構造化された policy_refusal エラーを返します(何もせずに終了したり、クラッシュしたりすることはありません)。入力操作にはさらにレート制限が適用されます(デフォルトでは毎分 60
操作。DESKTOP_MCP_RATE_LIMIT_PER_MIN で調整可能)。上限を超えると、構造化された rate_limited エラーが返されます。
これは多層防御です。ハーネスレベルの権限確認プロンプトが最初の 関門ですが、サーバー自体も、独自の環境変数で明示的に有効化されない限り入力/ウィンドウ/録画操作を拒否します。そのため、ハーネスの設定ミスや過度に許容的な設定によって、オペレーターがこのプロセスで有効化していない機能が 有効になることはありません。
セキュリティ
desktop-mcp を登録する前に、以下をお読みください。これはデスクトップ制御サーバーです。このサーバーがマシン上で実行できる操作をここに明記していますので、十分な情報に基づいて判断できます。影響範囲について包み隠さず伝えることが、この説明の目的です。
observeグループ(screenshot、ウィンドウの列挙)は常に有効で、無効にできません。 このサーバーにアクセスできる呼び出し元は、マシン上で表示されているあらゆるウィンドウの内容をキャプチャできます。パスワードマネージャー、認証アプリ/2FA アプリ、シードフレーズ、銀行のタブ、プライベートメッセージも対象です。screenshotが返すのは、インラインのピクセルデータではなく、スクラッチディレクトリ(デフォルトは%TEMP%\desktop-mcp-scratch)に書き込まれた PNG のファイルパスです。そのため、キャプチャした画面の内容は、そのディレクトリを消去するまでディスク上にも残ります。スクラッチディレクトリは機密情報として扱い、信頼できるマシンとハーネス上でのみこのサーバーを実行してください。DESKTOP_MCP_ENABLE_INPUT=1を設定すると、フォーカス中のウィンドウへのキー入力とマウス入力が可能になります。これは RCE と同等です。inputグループを有効にすると、呼び出し元は現在フォーカスされている任意のウィンドウ(ターミナル、ブラウザー、管理ツールなど)でコマンドを入力し、ホットキーを押し、マウスを操作できます。まさにこの理由から、デフォルトでは無効です。呼び出し元にキーボードを渡すことになると理解したうえでのみ、有効にしてください。無効の場合、すべての入力ツールは構造化されたpolicy_refusalを返します(何も通知せずに操作することはありません)。- 最小権限: 特定のタスクで必要な場合を除き、
input、window、recordグループは無効のままにし、このサーバーは信頼できるホストで実行してください。また、管理者権限での入力グループによって、昇格された権限のウィンドウを操作できることを受け入れていない限り、昇格した権限(管理者)で実行しないでください。
上記で説明した環境変数による制御、デフォルトで無効の入力グループ、構造化された拒否、レート上限は、実際に適用される制御です。このセクションでは、設計上なおアクセス可能な範囲を、曖昧にせず明記しています。
正直な機能一覧
以下の各主張は、それを実装するファイルと検証するテストに対応しています。対応する実装とテストがない機能は主張していません。
| 主張 | 実装 | 検証 |
|---|---|---|
| モニター/領域/ウィンドウのスクリーンショットを PNG として撮影 | desktop_mcp/groups/observe.py::screenshot | tests/test_observe.py::TestScreenshot、実機テスト:tests/test_live_smoke.py::test_live_screenshot_real_png |
| ウィンドウの一覧表示/アクティブウィンドウの取得/タイトルによる検索 | desktop_mcp/groups/observe.py | tests/test_observe.py::TestListWindows、TestGetActiveWindow、TestWindowInfo |
| ウィンドウのフォーカス/移動とサイズ変更/最小化/復元 | desktop_mcp/groups/window.py | tests/test_window.py |
| マウスの移動/クリック/ドラッグ/スクロール、キーの押下/ホットキー、テキスト入力 | desktop_mcp/groups/input_tools.py | tests/test_input.py(pyautogui のモックのみ。制限事項を参照) |
| ffmpeg gdigrab による画面録画、最大録画時間の制限、正常な停止 | desktop_mcp/groups/record.py | tests/test_record.py、実機テスト:tests/test_live_smoke.py::test_live_record_real_mp4 |
| 入力グループはデフォルトでオフ。無効時は構造化された拒否を返す | desktop_mcp/config.py::group_enabled、gated | tests/test_config.py::TestGroupEnabled、tests/test_input.py::TestGateDisabledByDefault |
| 入力アクションのレート制限(デフォルトは毎分 60 回) | desktop_mcp/config.py::TokenBucket、RateLimiterRegistry | tests/test_config.py::TestTokenBucket、tests/test_input.py::TestRateLimit |
| DPI 対応の初期化(per-monitor-v2)により、拡大縮小されたディスプレイ上で mss と pyautogui の座標を一致させる | desktop_mcp/config.py::ensure_dpi_awareness | tests/test_config.py::TestDpiAwareness(冪等性のみ。座標の視覚的な一致は自動テストされません。制限事項を参照) |
| マウス操作の前に、仮想デスクトップの境界に照らして座標を検証 | desktop_mcp/groups/input_tools.py::_validate_point | tests/test_input.py(範囲外のケース) |
| 孤立プロセスの保護:クラッシュしたプロセスの古いレコーダーを、新しいレコーダーの起動前に終了 | desktop_mcp/groups/record.py::_orphan_guard | tests/test_record.py::TestRecordStart::test_orphan_guard_kills_stale_recorder |
制限事項(利用する前にお読みください)
- UIPI(ユーザーインターフェイス特権分離)はウィンドウハンドルを保護しますが、入力は保護しません。 中程度の整合性レベルのプロセス(このサーバーは、昇格しない限り該当します)は、より高い整合性レベル(昇格済み/管理者)のプロセスが所有する ウィンドウ を操作できません。
focus_window、move_resize_window、minimize_window、restore_windowはpygetwindowのウィンドウハンドル API(desktop_mcp/groups/window.py)を経由します。Windows は UIPI のもとでこれらをブロックし、このサーバーは UIPI の名前を明示した構造化エラーwindow_action_failedを返します。何も通知せずに処理を無視することはありません。 キーボード/マウス入力ツールも UIPI の対象です。しかも、こちらはより深刻なことに、失敗が通知されません。type_text、hotkey、key_press、およびマウス操作(desktop_mcp/groups/input_tools.py)はpyautoguiを経由します。その Windows バックエンドは、実際にはレガシーのkeybd_event/mouse_eventWin32 関数を呼び出します(SendInputではありません。pyautoguiはSendInputを試した後、以前の呼び出しに戻しました。インストール済みパッケージの_pyautogui_win.pyを参照してください)。Microsoft のドキュメントにもあるように、UIPI は一般に合成入力に適用されます。「[アプリケーション]が入力を注入できるのは、同じかそれ以下の整合性レベルのアプリケーションに限られます」(MSDN のSendInputリファレンス)。さらに重要なことに、「GetLastErrorも戻り値も、失敗の原因が UIPI によるブロックであることを示しません」。keybd_event/mouse_eventは失敗を通知する手段のない void 戻り値のレガシー関数なので、pyautogui(およびこのサーバー)は原理的にブロックを検出できません。昇格済みのウィンドウにフォーカスがある場合、Windows が注入されたキー入力やクリックを何も通知せずに破棄しても、これらのツールは{"ok": true}を返します。例外もエラーフィールドもなく、対象には何も届きません。これは、少なくとも UIPI の名前を明示した構造化エラーwindow_action_failedを発生させる上記のウィンドウハンドル経由の場合よりも 深刻です。コードレベルでの回避策はなく、サーバーを昇格して実行する(または入力グループを有効にしない)以外に回避方法はありません。このプロジェクトでは、どちらも行っておらず、推奨もしていません。オペレーターは、フォーカスされる可能性が十分にある昇格済みアプリを、このサーバーの入力ツールでは 通知なく到達不能 なものとして扱ってください。入力を通過させる隙として扱ってはいけません。 将来的に追加する可能性のある機能(未実装):入力を注入する前にフォアグラウンドウィンドウの整合性レベルを検出し、誤った{"ok": true}を返す代わりに構造化された拒否応答を返す機能です。これは v2 のアイデアとして追跡中で、現時点の機能ではありません。 - DPI スケーリング。 サーバーは起動時にモニターごとの v2 DPI 認識を設定するため、スケーリングされたディスプレイでは
mssのピクセル座標とpyautoguiのポイント座標が一致するはずです。この初期化処理は、冪等性とクラッシュしないことのみをユニットテストしています。実際の複数 DPI・複数モニター環境で座標が一致するかどうかは自動テストされていないため、スケーリングが 100% でないモニターを対象にする場合は、実機で抜き取り確認してください。 - UAC セキュアデスクトップ。 Windows がセキュアデスクトップに切り替わると(UAC の昇格プロンプト、Ctrl+Alt+Del、ロック画面など)、このサーバーを含め、通常のデスクトップで実行されているプロセスは、その画面を表示したり操作したりできません。スクリーンショットには切り替え前の通常のデスクトップの内容が表示されます。入力呼び出しはセキュアデスクトップには届きません。
- 単一マシン内のみで動作します。 ネットワークトランスポートもリモート操作もありません。同じマシン上で MCP ホストによって起動される stdio のみを使用します。
- このリポジトリ独自の登録では、入力グループはデフォルトで無効です。
~/.claude.jsonのdesktop-mcpエントリを参照してください。そこではDESKTOP_MCP_ENABLE_INPUTは設定されていません。有効化は、コード変更ではなく、登録ごとにオペレーターが意図して選択するものです。 pyautoguiのフェイルセーフ。FAILSAFE=Trueは意図的な設定です。操作の途中でカーソルを画面の隅に勢いよく移動させると、pyautogui内で例外が発生し、呼び出しが中止されます。実行中のmouse_dragが中断されることがあります。これは v1 では許容される動作として扱っています(計画の未解決事項を参照)。バグではなく、意図的に備えた人間向けのキルスイッチです。- OCR/視覚分析には対応していません。 スクリーンショットは生の PNG であり、内容の解釈はこのサーバーではなく利用側が行います。
- クリップボードツールはありません。 認証情報に関わる可能性のある領域のため、独自の安全設計を備えた v2 まで延期しています。
- mcp-factory ハブには登録されていません。 rag-mcp モデルにならい、独立したリポジトリとして提供します(独自の pyproject と、仮想環境を使わない system-Python312 インストールを備えます)。ハブ/レジストリとの統合は v2 の候補です。
環境変数
| 変数 | 効果 | デフォルト |
|---|---|---|
DESKTOP_MCP_ENABLE_WINDOW | window ツールグループを有効化します | 未設定(オフ) |
DESKTOP_MCP_ENABLE_INPUT | input ツールグループを有効化します | 未設定(オフ) |
DESKTOP_MCP_ENABLE_RECORD | record ツールグループを有効化します | 未設定(オフ) |
DESKTOP_MCP_RATE_LIMIT_PER_MIN | input グループのレート上限 | 60 |
DESKTOP_MCP_SCRATCH_DIR | スクリーンショット/録画/pidfiles の保存先 | %TEMP%\desktop-mcp-scratch |
DESKTOP_MCP_LIVE | 実機でスモークテストを実行するには 1 を設定します(テストを参照) | 未設定(スキップ) |
使用例
// A tool call from the MCP host, illustrative -- not a shell command.
{"tool": "screenshot", "arguments": {"monitor": 0}}
// -> {"ok": true, "path": "C:\\Users\\...\\Temp\\desktop-mcp-scratch\\screenshot-....png", "w": 3840, "h": 1080, "monitor": 0}
{"tool": "record_start", "arguments": {"fps": 30, "max_duration_s": 30}}
// -> {"ok": true, "path": "...\\recording-....mp4", "pid": 12345, "fps": 30, "max_duration_s": 30}
{"tool": "record_stop", "arguments": {}}
// -> {"ok": true, "path": "...\\recording-....mp4", "bytes": 800560, "duration_s": 3.13}
// input group disabled (default):
{"tool": "mouse_click", "arguments": {"x": 500, "y": 500}}
// -> {"ok": false, "error": {"type": "policy_refusal", "group": "input", "required_env": "DESKTOP_MCP_ENABLE_INPUT", ...}}
テスト
CI(.github/workflows/ci.yml)では、push/PR のたびにこのテストスイートを実行します(pywin32/PyGetWindow は Windows 専用の依存関係で、Linux ランナーにはインストールできないため、windows-latest で実行します)。テストスイートの実際の結果と上記の Tests バッジにずれがある場合は、ビルドが失敗します。詳しくは scripts/check_readme_counts.py を参照してください。
# unit suite (mocked backends, no real screen/input/recording touched)
python -m pytest -q
# handshake check -- prints every registered tool name
python scripts/list_tools.py
# real-hardware smokes (real screenshot PNG, real ~3s screen recording;
# never input-injection -- see safety rails above)
DESKTOP_MCP_LIVE=1 python -m pytest -q -k live_screenshot
DESKTOP_MCP_LIVE=1 python -m pytest -q -k live_record
インストール
pip install -r requirements.txt # or: pip install .
# deps: fastmcp==3.4.2, mss==10.2.0, pyautogui==0.9.54, PyGetWindow==0.0.9, pywin32==312
# also requires ffmpeg + ffprobe on PATH for the record group
~/.claude.json に desktop-mcp として登録されています(stdio、システムの Python312、observe+window+record グループが有効、input グループは env に含まれていません)。
商用サポート
Jaimen Bell がメンテナンスしています。本番環境での MCP 統合、カスタムサーバー、またはエージェントの信頼性に関する取り組みについては、jaimenbell.dev をご覧ください。
独自の MCP サーバーを構築していますか? MCP Starter Kit には、テンプレート、構築手順書、そしてこのサーバーのリリース時に得たパッケージングの実践例が含まれています。
mcp-name: io.github.jaimenbell/desktop-mcp
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。