JetKVM MCP Server
Enables remote control of a computer connected to a JetKVM device by capturing its screen and sending mouse, keyboard, and scroll inputs via Playwright and the JetKVM's local Web UI.
README
JetKVM MCP Server
JetKVMの公式ローカルWeb UIをPlaywrightで開き、接続先コンピューターの画面取得とHID入力をMCP Toolとして提供するstdioサーバーです。
この文書では、JetKVMに接続されて操作を受けるコンピューターを「PC1」、MCP ServerとPlaywrightを実行するコンピューターを「PC2」と呼びます。HIDは、JetKVMがPC1へ送るマウス・キーボード入力を指します。
実装済み機能
- PC1から受信した映像フレームと同じピクセル寸法でのPNG取得
- 絶対マウス移動、クリック、ダブルクリック、スクロール
- 単一キー、macOS向けhotkey、printable ASCII入力
- 複数の画面特徴を必要とするmacOSロック画面判定と、最大1回に制限したロック解除試行
- PC2内で完結するOCR Vision解析と、候補座標・confidenceを含む画面構造化
- risk上限、直列入力、before/after画像差分を一体化したSafe Action Planning
- BrowserContext・WebRTC・HID DataChannelの常駐再利用
- WebRTC切断時の1回限定再接続と、HTML/PNG診断保存
- 出力先制限、ディレクトリ外を指すファイル名の拒否、資格情報のログ抑止
調査に基づく方式
2026-08-18時点のJetKVM公式 jetkvm/kvm リポジトリ(dev、commit b3c29a44d9e2862b8ff7530830781803ce27b060)を確認しました。
- ローカル認証UIは
POST /auth/login-localを使い、成功時にHttpOnlyのauthTokenCookieを設定します。 - ローカルWebRTC signalingは認証保護された
GET /webrtc/signaling/clientを使います。 - UIは
RTCPeerConnectionにrecvonlyvideo transceiverを追加し、受信MediaStreamを<video>のsrcObjectに設定します。 - 本実装はこの公式UIをそのままPlaywrightで実行し、デコード済みvideo frameをcanvasへ描画してPNG化します。
独自signaling、Developer Mode、独自Firmware、Cloud/Remote Access、JetKVM設定変更は使いません。仮想メディア、Wake on LAN、Terminal、Serial等も公開しません。
アーキテクチャ
MCP Server起動時にPlaywright Chromium、BrowserContext、pageを各1つだけ作成し、JetKVMへ一度ログインしてWebRTC videoがreadyになるまで待機します。全Toolは同じpageとWebRTC/DataChannelセッションを共有し、同時呼び出しは順番に処理します。通常のTool呼び出しでブラウザ再起動や再ログインは行いません。
入力にはPlaywrightの page.mouse / page.keyboard を使いません。これらはPC2上のChromiumを操作するだけで、PC1へ届くことを保証できないためです。
マウスとキーボードは、JetKVM公式Web UIがE2Eテスト用に公開している window.__kvmTestHooks を優先して呼び出します。hookが利用できない場合は、公式UIが <video> と document に登録したイベントlistenerへDOMイベントを送ります。スクロールは常に公式UIのvideo用wheel listenerを経由します。この設計により、独自のHIDパケットを実装せず、公式UI内のHID RPC handshake、DataChannel選択、旧バージョン向けfallbackを再利用します。
__kvmTestHooks はJetKVMの安定した外部APIではありません。本実装は上記commitで実装を確認しているため、JetKVM更新後は入力系の互換性を再検証してください。
主要コンポーネント:
| ファイル | 責務 | 設計理由 |
|---|---|---|
server.ts |
MCP schemaとstdio lifecycle | Playwrightや資格情報をMCP境界へ露出させない |
session.ts |
Browser/WebRTC常駐、直列化、再接続 | 競合を避け、全Toolで同一DataChannelを使う |
capture.ts |
受信映像の元のピクセル寸法でframe取得、障害診断 | JetKVM UI全体ではなくPC1映像だけを扱う |
input.ts |
公式HID hook・wheel RPCへのdispatch | PC2ブラウザ操作ではなくPC1へ確実に届ける |
keyboard.ts |
MCP key名、KeyboardEvent.code、USB HIDの対応 | キー変換と送信処理を分離する |
unlock.ts |
OCR三値判定と最大1回の認証 | 誤判定時の通常アプリへの秘密入力を防ぐ |
vision.ts |
ローカルOCR、画面領域、操作候補の抽出 | 画像を外部へ送らず、低confidence時は候補を返さない |
planner.ts |
操作候補、期待結果、riskの構造化 | 入力前に対象と許容riskを明示する |
Tool呼び出しのFLOW:
MCP client
→ Zod引数検証
→ JetKvmSession内の直列実行キュー
→ WebRTC video健全性確認
→ 映像取得、または公式UIのHID/RPC経路
→ MCP response
WebRTC切断時は同じpageを1回だけreloadして再接続します。30秒以内に復旧しない場合は診断ファイルを保存し、別のJetKVM WebRTCセッションが存在する可能性を含むエラーを返します。
JetKVMは同時WebRTCセッションが競合する場合があります。MCP Server利用中は、通常のChrome/Safari等で同じJetKVM KVM画面を開かないでください。
公式資料:
- https://github.com/jetkvm/kvm
- https://github.com/jetkvm/kvm/blob/dev/ui/src/routes/login-local.tsx
- https://github.com/jetkvm/kvm/blob/dev/ui/src/routes/devices.%24id.tsx
- https://github.com/jetkvm/kvm/blob/dev/ui/src/components/WebRTCVideo.tsx
セットアップ
Node.js 20以上が必要です。
npm install
npx playwright install chromium
export JETKVM_URL=http://jetkvm.local
export JETKVM_PASSWORD='your-local-password'
export JETKVM_SCREENSHOT_DIR=./screenshots
export JETKVM_PC_PASSWORD='your-pc1-macos-password'
npm run build
.envを使う場合、サーバー自身はdotenvを自動読込みしないため、起動シェルで読み込ませます。
cp .env.example .env
# .envへ実値を設定(Gitにはcommitしない)
set -a
source .env
set +a
npm run build
npm start
初回のみChromiumを導入します。依存関係を更新しない通常起動では再実行不要です。
npx playwright install chromium
JETKVM_PC_PASSWORD はPC1 macOSのロック解除専用です。Tool引数には渡さず、PC2上のローカルな .env だけで管理してください。.env はgitignore済みですが、誤って別名で複製しないでください。Hermes等の設定ファイルへ平文で記載せず、起動シェルから環境変数を継承する運用を推奨します。
PNG取得の直接検証
npm run screenshot -- current-screen.png
成功時は screenshots/current-screen.png を保存します。ファイル名は JETKVM_SCREENSHOT_DIR の外へ出られず、.png のみ許可します。
各実行ではSPA初期化のため5秒待機した後、video待機より先に次の診断情報も保存・stderr表示します。videoが取得できない場合も診断ファイルは残ります。
- 現在URL、ページタイトル、本文先頭2000文字
video、password input、form、#root、text=JetKVMの要素数screenshots/debug-page.htmlscreenshots/debug-page.png(full-page)
MCP設定例
{
"mcpServers": {
"jetkvm": {
"command": "node",
"args": ["/path/to/jetkvm-mcp/dist/server.js"],
"env": {
"JETKVM_URL": "http://jetkvm.local",
"JETKVM_PASSWORD": "<local-password>",
"JETKVM_SCREENSHOT_DIR": "/path/to/jetkvm-mcp/screenshots"
}
}
}
}
公開ToolとMCP引数:
| Tool | 引数 | 動作 |
|---|---|---|
take_screenshot |
filename?: string |
受信映像と同じピクセル寸法のPNGを保存・返却 |
analyze_screen |
filename?: string |
ローカルOCRでactive application、visible text、主要領域、操作候補、confidenceを返す |
plan_action |
action、target、expected result、各action固有引数 | 最新画面を解析し、候補座標とriskを返す。PC1入力は行わない |
execute_action |
plan用引数、approvedRisk、filenamePrefix? |
計画、risk gate、1操作、before/after差分確認を直列実行 |
move_mouse |
x: int, y: int |
PC1映像座標へ絶対移動 |
click |
x, y, button?: left|right|middle |
指定位置を1回クリック |
double_click |
x: int, y: int |
左button down/upを2組送信 |
scroll |
dx: number, dy: number |
公式UIのwheel listener経由でスクロールRPCを送信 |
press_key |
key: string |
対応キーをdown/up |
hotkey |
keys: string[] |
順にdown、逆順にup。META/CMD対応 |
type_text |
text: string |
macOSのABC入力ソースでprintable ASCIIをUS配列として入力 |
unlock_pc |
なし | 明確なロック画面だけ認証を最大1回試行 |
ensure_unlocked |
なし | 解除済みなら無入力、lockedだけ共通解除処理 |
スクリーンショットの書込み先は、サーバープロセスのカレントディレクトリ直下にある screenshots/ のみに制限されます。JETKVM_SCREENSHOT_DIRを指定する場合も、正規化後にこの場所と一致する必要があります。../や絶対パスなど、このディレクトリ外を指すファイル名は拒否します。
Hermes Integration
2026-08-18時点のHermes Agent公式ドキュメントでは、stdio MCP Serverを ~/.hermes/config.yaml の mcp_servers に登録します。HermesはMCP subprocessへshell環境をすべて継承しないため、必要な環境変数を env で明示する必要があります。
公式資料:
- https://hermes-agent.nousresearch.com/docs/user-guide/features/mcp
- https://hermes-agent.nousresearch.com/docs/reference/mcp-config-reference
まずJetKVM MCPをbuildし、Hermesを起動するshellへ資格情報を設定します。
cd /absolute/path/to/jetkvm-mcp
npm install
npx playwright install chromium
npm run build
export JETKVM_URL=http://jetkvm.local
export JETKVM_PASSWORD='your-local-password'
export JETKVM_SCREENSHOT_DIR=/absolute/path/to/jetkvm-mcp/screenshots
export JETKVM_PC_PASSWORD='your-pc1-macos-password'
次の設定を ~/.hermes/config.yaml の既存内容へmergeします。同じ内容を examples/hermes/config.yaml にも収録しています。commandを実行する際のカレントディレクトリがプロジェクトrootになるよう、Hermes自体もこのディレクトリから起動してください。
mcp_servers:
jetkvm:
command: "node"
args: ["/absolute/path/to/jetkvm-mcp/dist/server.js"]
env:
JETKVM_URL: "${JETKVM_URL}"
JETKVM_PASSWORD: "${JETKVM_PASSWORD}"
JETKVM_SCREENSHOT_DIR: "/absolute/path/to/jetkvm-mcp/screenshots"
JETKVM_PC_PASSWORD: "${JETKVM_PC_PASSWORD}"
enabled: true
trust: untrusted
timeout: 120
connect_timeout: 60
idle_timeout_seconds: 0
supports_parallel_tool_calls: false
tools:
include: [take_screenshot, analyze_screen, plan_action, execute_action, move_mouse, click, double_click, scroll, press_key, hotkey, type_text, unlock_pc, ensure_unlocked]
resources: false
prompts: false
tools.include はHermesが登録するToolのallowlistです。この例は専用検証機で実機確認済みのToolを含みます。一般環境ではread-only Toolから開始し、必要なwrite Toolだけへ絞ってください。
trust: untrusted はwrite-capableと判定されたToolにHermesの承認を要求するfail-closed設定です。idle_timeout_seconds: 0 はHermesによるstdio subprocessのidle再起動を無効化し、JetKVM WebRTC sessionを1つだけ常駐させます。
| JetKVM MCP Tool | Hermesでの登録名 | 今回の確認範囲 |
|---|---|---|
take_screenshot |
mcp__jetkvm__take_screenshot |
登録・呼出し可能であることを確認対象とする |
analyze_screen |
mcp__jetkvm__analyze_screen |
ローカルOCRで画面状態と操作候補を構造化。PC1入力なし |
plan_action |
mcp__jetkvm__plan_action |
最新画面から対象座標・期待結果・riskを生成。PC1入力なし |
execute_action |
mcp__jetkvm__execute_action |
計画・risk承認・入力・before/after差分確認を1つの直列処理で実行 |
move_mouse |
mcp__jetkvm__move_mouse |
PC1映像座標へpointerを移動。クリックなし |
click |
mcp__jetkvm__click |
Visionで確認したPC1映像座標を1回クリック |
double_click |
mcp__jetkvm__double_click |
確認済み座標で左ダブルクリック |
scroll |
mcp__jetkvm__scroll |
公式wheel RPC経路でスクロール |
press_key |
mcp__jetkvm__press_key |
対応キーのdown/upを1組送信 |
hotkey |
mcp__jetkvm__hotkey |
modifierを含むキー列を順序保証して送信 |
type_text |
mcp__jetkvm__type_text |
ABC入力ソース時にprintable ASCIIを公式keypress経路で入力 |
unlock_pc |
mcp__jetkvm__unlock_pc |
高確度のmacOSロック画面で認証を最大1回試行 |
ensure_unlocked |
mcp__jetkvm__ensure_unlocked |
解除済みなら無入力、locked時だけ最大1回認証 |
PC1は本プロジェクト専用の検証機として扱い、上記ToolをHermesへ登録します。trust: untrusted の実行ゲートと supports_parallel_tool_calls: false を維持し、共有WebRTC sessionへの並行入力を防ぎます。認証Toolは1 callにつき最大1試行で、自動retryしません。
実パスへ置換後、プロジェクトrootからHermesを起動します。
cd /absolute/path/to/jetkvm-mcp
hermes chat
Hermes起動中に設定を変更した場合は、Hermes内で /reload-mcp を実行します。通常ブラウザで同じJetKVM KVM画面を開いているとWebRTC sessionが競合する可能性があるため、Hermes起動前に閉じてください。
資格情報の実値を config.yaml や本Repositoryへ保存しないでください。上記の ${VAR} はHermes公式がサポートする環境変数参照です。特に ensure_unlocked はパスワードをPC1へ送る可能性があるため、Tool登録とTool実行を別の判断として扱ってください。
PC1ロック解除の安全仕様
ロック状態はPC1映像をPC2上のTesseract.js(WASM、英語・日本語データ同梱)で領域別OCRし、locked / unlocked / unknown の三値で判定します。画像やOCR結果を外部サービスへ送信しません。
OCR実装: https://github.com/naptha/tesseract.js
locked: 所定領域で時刻、日付、パスワード案内の3種類をすべて確認unlocked: パスワード案内がなく、画面上端で既知のmacOSメニューバー語を3種類以上確認unknown: 上記の証拠が揃わない状態。パスワードもEnterも送信しない
これはmacOSの状態をOS APIから取得する方式ではなく、画面上の文字配置に基づく保守的な判定です。表示言語、解像度、壁紙、macOSのUI変更によって unknown になる可能性があります。誤入力を避けるため、証拠不足時にロック解除を試みないことを優先します。
unlock_pc() と ensure_unlocked() はMCP引数を取りません。資格情報は JETKVM_PC_PASSWORD からのみ読み、ログ、例外、MCP response、ファイル名へ含めません。資格情報入力は診断ログを出さない専用の内部HID経路を使います。1回のTool呼び出しにつきパスワード入力とEnterは最大1回で、自動リトライは行いません。判定用画像は unlock-before.png、ensure-unlocked-before.png、結果確認は unlock-after.png として screenshots/ 内だけに保存します。
戻り値の status は unlocked、already_unlocked、not_lock_screen、state_unknown、unlock_failed のいずれかです。
テスト
npm test
npm run build
業務アプリ検証と運用上の前提
専用検証機では、Hermes経由のスクリーンショット、ローカルVision、risk gate付き入力、Slackの下書き入力と消去、Jira・Google Sheets・Figmaのread-only表示、VS Code workspaceとCopilot Chatの応答確認まで実施しています。業務データの削除・送信・更新は検証対象にしていません。
- 社内Jira等へ接続する前に、PC1のGlobalProtectが接続済みであることを確認します。MFAや資格情報入力が必要な場合は人間へ引き継ぎます。
type_textは文字列をUSB HID key usageへ変換するため、PC1の現在の入力ソースに従います。ASCIIを確実に入力する前にmacOSの入力ソースをABCにしてください。日本語IMEへの直接入力は対象外です。- Google Sheetsの実機確認は保護された業務Sheetのread-only表示までです。明示された安全なテストセルがない限り編集しません。
- VS CodeはDockの右クリックメニューから
workspaceを選択し、対象workspaceを明示してからCopilotへ委譲します。
ロードマップ
今後の候補:
- OCR workerのセッション内再利用による状態判定レイテンシ短縮
- macOSの表示言語・解像度・壁紙バリエーションを増やしたlock判定fixture
- 入力Toolごとの構造化監査イベント(秘密情報を含めない)
- WebRTC/DataChannelの状態を入力なしで確認するread-only health Tool
- Hermes Agent向けの秘密情報を設定ファイルへ直書きしない起動wrapper
- 画面内の命令を信頼できないデータとして扱うPrompt Injection対策
- 複数JetKVMを安全に分離するPC profile
明示的な非目標:
- Developer Mode、独自Firmware、Cloud/Remote Accessの利用
- JetKVM設定変更API、Terminal、Serial、仮想メディア、Wake on LANの公開
- 日本語IMEへの直接文字列注入、認証失敗時の自動リトライ
実機検証ログ
- 2026-08-18 STEP 1: 同一WebRTCセッションで
take_screenshot3回、move_mouse2回を実行。 (100,100)→ HID(1708,3037)、(1700,900)→ HID(29028,27331)。- 両方とも公式E2E HID hook、HID ready、RPC DataChannel open、WebRTC connectedを確認。
mouse-a.pngとmouse-b.pngでPC1カーソルが異なる2地点へ移動したことを確認。- click、double_click、scroll、press_key、hotkey、type_textの実機呼び出しは0回。
- 2026-08-18 STEP 2: 同一WebRTCセッションで
take_screenshot2回、move_mouse1回、leftclick1回を実行。 (960,540)→ HID(16392,16399)。move/clickとも公式E2E HID hook、HID ready、RPC DataChannel open、WebRTC connectedを確認。- 安全なロック画面背景をクリックしたため、カーソル移動以外のPC1 UI変化はなし。
- double_click、right click、scroll、press_key、hotkey、type_textのSTEP 2実機呼び出しは0回。
- 2026-08-18 STEP 3: 同一WebRTCセッションで
take_screenshot2回、press_key("Tab")1回(down/up各1回)を実行。 - Tabは公式
sendKeypressE2E HID hook(USB HID usage0x2b)経由。HID ready、RPC DataChannel open、WebRTC connectedを確認。 - before/after画像ではロック画面の明確なフォーカス変化を判定できなかった。Tab以外のキー、click、double_click、scroll、hotkey、type_textのSTEP 3実機呼び出しは0回。
- 2026-08-18 STEP 4: 同一WebRTCセッションで
take_screenshot3回、type_text("abc")1回、press_key("Backspace")3回を実行。 abcとBackspaceは公式sendKeypressE2E HID hook経由。HID ready、RPC DataChannel open、WebRTC connectedを全入力で確認。小文字入力のためShiftは0回、Enterは0回。- 入力後はパスワード欄に3文字分のマーカーが表示され、Backspace 3回後にすべて消えた。ロック画面からの遷移および追加操作はなし。
- 2026-08-18 STEP 5: 同一WebRTCセッションで
take_screenshot2回、move_mouse(1400,700)1回、leftdouble_click(1400,700)1回を実行。 - double-clickは公式
sendAbsMouseMoveE2E HID hook経由でleft button down/upを各2回送信。HID ready、RPC DataChannel open、WebRTC connectedを確認。 - ロック画面の何もない場所で実施し、画面状態の変更なし。単発clickおよびその他の追加入力は0回。
- 2026-08-18 STEP 6: 同一WebRTCセッションで
take_screenshot2回、Slackメッセージ本文領域へのmove_mouse(1150,540)1回、scroll(0,500)1回を実行。 - scrollは公式video wheel listenerからJetKVMのwheel RPC経路へ送信され、正規化wheel値は
(0,-5)。HID ready、RPC DataChannel open、WebRTC connectedを確認。 - before/afterでSlackメッセージ本文が上方向へ移動したことを確認。click、double_click、keyboard系Toolおよびその他の追加入力は0回。
- 2026-08-18 STEP 7初回:
hotkey(["SHIFT","TAB"])は大文字TABの正規化エラーでHID dispatch前に停止。スクリーンショット2回、PC1へのHID入力0回、画面変化なし。 TABaliasをTabに正規化する修正とunit testを追加済み。安全条件に従い、この回では実機再試行を行っていない。- 2026-08-18 STEP 7再試行: 同一WebRTCセッションで
take_screenshot2回、hotkey(["SHIFT","TAB"])1回を実行。 - 公式
sendKeypressE2E HID hookからShiftLeft down (0xe1)、Tab down (0x2b)、Tab up、ShiftLeft upの順に送信。HID ready、RPC DataChannel open、WebRTC connectedを確認。 - PC1は壁紙のみの表示からロック画面表示へ変化。その他の入力Toolと追加実機入力は0回。
- 2026-08-19統合検証: Hermesから
take_screenshotとrisk gate付きexecute_actionを呼び出し、同一WebRTC sessionの維持とbefore/after差分確認を実証。 - ローカルVisionはFinderをactive applicationとして識別し、壁紙上の低confidenceな単独文字をclick候補から除外。改善したmacOS lock判定で
ensure_unlockedを1回だけ実行し、再試行なしで解除を確認。 - SlackはABC入力ソースで安全な下書きを入力後に消去し、送信操作は未実施。VPN接続後のJira、保護されたGoogle Sheets、Figmaはread-only表示を確認。
- VS CodeはDockの右クリックメニューから
workspaceを選択して開き、Copilot Chatへファイル変更を禁止した確認文だけを送って応答を確認。
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.