Skip to main content
Glama

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診断保存

  • 出力先制限、ディレクトリ外を指すファイル名の拒否、資格情報のログ抑止

Related MCP server: site-motion-capture

調査に基づく方式

2026-08-18時点のJetKVM公式 jetkvm/kvm リポジトリ(dev、commit b3c29a44d9e2862b8ff7530830781803ce27b060)を確認しました。

  • ローカル認証UIは POST /auth/login-local を使い、成功時にHttpOnlyの authToken Cookieを設定します。

  • ローカルWebRTC signalingは認証保護された GET /webrtc/signaling/client を使います。

  • UIは RTCPeerConnection に recvonly video 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画面を開かないでください。

公式資料:

セットアップ

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.html

  • screenshots/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 で明示する必要があります。

公式資料:

まず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_screenshot 3回、move_mouse 2回を実行。

  • (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_screenshot 2回、move_mouse 1回、left click 1回を実行。

  • (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_screenshot 2回、press_key("Tab") 1回(down/up各1回)を実行。

  • Tabは公式 sendKeypress E2E HID hook(USB HID usage 0x2b)経由。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_screenshot 3回、type_text("abc") 1回、press_key("Backspace") 3回を実行。

  • abc とBackspaceは公式 sendKeypress E2E HID hook経由。HID ready、RPC DataChannel open、WebRTC connectedを全入力で確認。小文字入力のためShiftは0回、Enterは0回。

  • 入力後はパスワード欄に3文字分のマーカーが表示され、Backspace 3回後にすべて消えた。ロック画面からの遷移および追加操作はなし。

  • 2026-08-18 STEP 5: 同一WebRTCセッションで take_screenshot 2回、move_mouse(1400,700) 1回、left double_click(1400,700) 1回を実行。

  • double-clickは公式 sendAbsMouseMove E2E HID hook経由でleft button down/upを各2回送信。HID ready、RPC DataChannel open、WebRTC connectedを確認。

  • ロック画面の何もない場所で実施し、画面状態の変更なし。単発clickおよびその他の追加入力は0回。

  • 2026-08-18 STEP 6: 同一WebRTCセッションで take_screenshot 2回、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回、画面変化なし。

  • TAB aliasを Tab に正規化する修正とunit testを追加済み。安全条件に従い、この回では実機再試行を行っていない。

  • 2026-08-18 STEP 7再試行: 同一WebRTCセッションで take_screenshot 2回、hotkey(["SHIFT","TAB"]) 1回を実行。

  • 公式 sendKeypress E2E 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へファイル変更を禁止した確認文だけを送って応答を確認。

Related MCP Connectors

Related MCP Servers

  • A
    license
    C
    quality
    D
    maintenance
    Exposes a remote browser as MCP tools via Playwright, enabling AI agents to navigate and interact with web pages through DOM snapshots, clicks, typing, and form operations.
    40
    9 npm
    14
    Apache 2.0
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables recording live website behavior on a rented GPU VM via Playwright, capturing page load, scroll, and optional hover/click actions into WebM video with jank reports, then copying the artifacts locally.
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to control remote desktops through screen capture, mouse movement, and keyboard input.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to see and control an existing Linux X11 desktop over SSH by taking screenshots and performing mouse, scroll, typing, and keyboard-shortcut actions across browsers, terminals, file managers, and other apps.
    MIT