Electron Viewer

io.github.acertijosapps由 GitHub 账号 acertijosapps 发布v1.1.0更新于 Nov 7, 2026

Drive Electron apps over CDP: screenshots, clicks, drags, keys, JS in renderer and main, logs.

已验证STDIO仅桌面浏览器自动化开发者工具

概览

AI 生成的概览

通过 CDP 操控 Electron 桌面应用:截图、鼠标、触摸与键盘输入、在渲染进程和主进程执行 JavaScript,以及读取控制台日志。

可以做什么
  • 列出、启动并附加到 Electron 应用及其窗口
  • 截取窗口画面,并读取带可点击坐标的无障碍树
  • 点击、拖拽、悬停、滚动、输入文本和带修饰键的按键
  • 轻点、滑动、长按和双指缩放,适用于触屏优先的应用
  • 调整或重载窗口,并在页面或主进程中执行 JavaScript
  • 读取控制台消息与异常,或已启动应用的原始标准输出
适用场景
适合需要检查、测试或操作本地 Electron 应用的场景,例如复现界面缺陷、端到端验证某个流程,或驱动自助终端与触屏应用。也可让人在浏览器面板中实时观看智能体正在操作的同一窗口。
运行要求
通过 npx 以 stdio 在本地运行,需要 Node.js 18 或更高版本。启动应用需要项目中 node_modules 已安装 electron,或应用已带远程调试端口启动。可选环境变量:ELECTRON_VIEWER_PROJECT、ELECTRON_VIEWER_URL、ELECTRON_VIEWER_LAUNCH_CMD。仅限桌面,无身份验证。
安装前请注意
该服务器可在应用的渲染进程和主进程中执行任意 JavaScript,因此能读取或修改应用可触及的任何内容。它会启动进程并发送真实输入,可能在应用中写入或删除数据。应固定具体版本号,不要让 npx 拉取最新版本。标题栏、菜单、对话框等原生界面不会被截取或点击。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Electron Viewer,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

@acertijosapps/electron-viewer

Live viewer for Electron apps in the browser, plus an MCP server so an AI agent can drive them. Built for the browser pane of AI coding tools (such as Claude Code): you and the agent watch and control the same app window, in real time.

[Electron Viewer showing the example app: window selector and launch button on top, the live window in the middle, and the reload, DevTools, window size, touch and screenshot controls at the bottom]
  • Live video: Page.startScreencast from the Chrome DevTools Protocol (CDP) over WebSocket. It keeps streaming while the app window is covered or minimized.
  • Real input: mouse move and hover, click, double click, right click, drag, wheel and full keyboard passthrough (including ⌘C/⌘V/⌘A on macOS, IME and dictation).
  • Touch: a touch mode in the viewer turns the pointer into a finger, and the agent can tap, swipe, long press and pinch, for touch-first and kiosk apps.
  • Every app and window: launch your project with electron . from the viewer, or attach to any app started with --remote-debugging-port. Apps on ports 9222–9231 are found automatically.
  • Main process too: launched apps also expose their main process (Node inspector), so the agent can run code there, resize the real window and open DevTools.
  • MCP server: the same actions as tools, plus JavaScript in the renderer and main process, the accessibility tree with click-ready coordinates, and console logs.
  • Nothing to add to your app: no preload, no dependency, no code changes.

Requirements

  • Node.js 18 or later.
  • An Electron app. To launch it from the viewer, the project must have electron in its node_modules (npm install --save-dev electron); if its binary hasn't been downloaded yet, the viewer downloads it on the first launch.
  • Works on macOS, Linux and Windows.

Quick start

From your Electron project folder:

sh
npx @acertijosapps/[email protected]

Open http://localhost:8094 and click Launch. The viewer runs electron . on free debugging ports (CDP from 9222, Node inspector from 9229). Deep link to a window: ?window=<target id>.

If your app is started by another tool (electron-vite, Electron Forge, a custom script), give the viewer your own start command. {cdpPort} and {inspectPort} are replaced with free debugging ports, and the Launch button (and launch_app) runs it in the project folder:

sh
npx @acertijosapps/[email protected] --launch-cmd "yarn dev --remoteDebuggingPort {cdpPort} --inspect {inspectPort}"

That line is for electron-vite; for an npm script that ends in electron ., pass the flags after --: --launch-cmd "npm start -- --remote-debugging-port={cdpPort} --inspect={inspectPort}". The command is only read from the command line or ELECTRON_VIEWER_LAUNCH_CMD, never from the HTTP API or the MCP.

Or start the app yourself with the flags and the viewer finds it:

sh
electron --remote-debugging-port=9222 --inspect=9229 .

Apps on CDP ports 9222–9231 are found automatically, and so is their main process when its inspector is on 9229–9238. --inspect is optional; without it you get everything except the main-process features (evaluate_main, real window resizing and in-app DevTools). To attach on other ports, use the form in the viewer or --attach and --inspect.

text
electron-viewer [options]        Start the viewerelectron-viewer mcp [options]    Start the MCP server on stdio
      --project <dir>      Project to launch with "electron ." (default: current folder)      --launch-cmd <cmd>   Launch with this shell command instead; {cdpPort} and {inspectPort}                           are replaced with the debugging ports  -p, --port <port>        Viewer port (default 8094, env PORT)      --host <host>        Interface to bind (default 127.0.0.1, env HOST)      --attach <port>      Attach at startup to an app on this CDP port      --inspect <port>     With --attach: the app's main process inspector port      --viewer-url <url>   Viewer the MCP talks to (default http://localhost:8094)

Other environment variables: MAX_DIM (longest side of the streamed frames, default 1920), QUALITY (JPEG quality, default 80), SCREENSHOT_MAX (longest side of MCP screenshots, default 1024), CDP_PORT / INSPECT_PORT (first ports to scan and launch on, default 9222 / 9229) and ELECTRON_VIEWER_STATE_DIR (where launched and attached apps are remembered, default ~/.cache/electron-viewer).

Use it with an AI agent (MCP)

Claude Code:

sh
claude mcp add electron-viewer -- npx -y @acertijosapps/[email protected] mcp

Any other MCP client:

json
{  "mcpServers": {    "electron-viewer": {      "command": "npx",      "args": ["-y", "@acertijosapps/[email protected]", "mcp"]    }  }}

Also listed in the official MCP Registry as io.github.acertijosapps/electron-viewer.

The examples pin an exact version. The viewer and the MCP can run code in your app, so pin the version you reviewed instead of letting npx fetch whatever was published last, and update it on purpose.

Keep the viewer running (npx @acertijosapps/[email protected]) to watch what the agent does. Without it, the MCP drives the app over CDP directly. Apps launched by either one are visible to the other.

ToolWhat it does
list_windowsRunning apps (with main when the main process is reachable) and their windows
launch_appRun electron . in the project on free debugging ports and wait for the first window
attachAttach to an app on a CDP port, optionally with its inspector port
screenshotPNG of the window content, longest side ≤ 1024 px
clickLeft, right or middle click; count: 2 for a double click; optional modifiers
dragPress, move in steps and release: a real drag
hoverMove the mouse without clicking
tapOne-finger tap (touch events, pointerType: "touch")
swipeOne-finger swipe: scrolls touch lists and drives swipe gestures
long_pressHold one finger down for a while, 800 ms by default
pinchTwo-finger pinch around a point; scale > 1 zooms in, < 1 zooms out
scrollMouse wheel at a point, deltas in CSS px
type_textInsert text into the focused element (any Unicode)
press_keyEnter, Tab, Escape, arrows, F1–F12… or any character, with modifiers
resize_windowResize the content area: the real window with the main process, an emulated viewport when the window can't reach the size (or with emulate: true)
reloadReload the window's page
evaluateRun JavaScript in the window's page and return the value
evaluate_mainRun JavaScript in the main process, with require('electron') available
get_accessibility_treeRoles, names, values and states; interactive elements carry their normalized center
get_console_logsConsole messages and exceptions from renderer windows and the main process, or the raw stdout

Coordinates are normalized to 0–1 over the content screenshot (x = pixel_x / image_width), so they hold at any window size. window is optional when only one window is open; it takes the target id from list_windows or part of the window title.

In the Claude Code browser pane

Add the viewer to .claude/launch.json and open it with preview_start electron-viewer:

json
{  "version": "0.0.1",  "configurations": [    {      "name": "electron-viewer",      "runtimeExecutable": "npx",      "runtimeArgs": ["-y", "@acertijosapps/[email protected]"],      "port": 8094,      "autoPort": false    }  ]}

HTTP API

The MCP uses the viewer's API; you can call it too. window (target id or part of the title) goes in the query string or the body and is optional with a single window. The server only listens on 127.0.0.1, only accepts a local Host and origin, and POST requests require content-type: application/json, so other websites can't drive your apps.

Endpoint
GET /api/windows[{ id, name, cdpPort, inspectPort, main, windows: [{ id, title, url }] }]
GET /api/projectThe project the viewer launches and whether it has Electron installed
GET /api/screenshot?max=<px>PNG; viewport size in X-Viewport-Width / X-Viewport-Height
GET /api/ax-tree?interactiveOnly=true{ tree }
GET /api/logs?level=&source=&limit={ logs } or { stdout }
POST /api/launch{ project? }
POST /api/attach{ port, inspectPort? }
POST /api/click{ x, y, button?, count?, modifiers? }
POST /api/drag{ x1, y1, x2, y2, ms? }
POST /api/hover{ x, y }
POST /api/tap{ x, y }
POST /api/swipe{ x1, y1, x2, y2, ms? }
POST /api/long-press{ x, y, ms? }
POST /api/pinch{ x?, y?, scale, ms? }
POST /api/scroll{ x, y, dx, dy }
POST /api/text{ text }
POST /api/key{ key, modifiers? }
POST /api/resize{ width, height, emulate? } (0 × 0 restores the window size)
POST /api/reload{ ignoreCache? }
POST /api/evaluate{ expression }
POST /api/evaluate-main{ expression, app? }
POST /api/devtoolsToggles DevTools; without the main process returns the DevTools url

How it works

  • Everything goes through CDP with ws: no Playwright, no Puppeteer, nothing injected into the app. The renderer is reached through --remote-debugging-port (one page target per window) and the main process through the Node inspector (--inspect), where the console API provides require.
  • One screencast session per window, shared by every viewer tab and stopped when the last one closes. The last frame is kept for late joiners. Focus and page lifecycle are emulated while someone is watching, so a covered or minimized window keeps painting.
  • Viewer input is queued in order and mouse moves are coalesced to one per animation frame. A hidden textarea holds the focus: keys are sent with Input.dispatchKeyEvent, and text that arrives without key events (IME, dictation) with Input.insertText. On macOS, ⌘A, ⌘C, ⌘V, ⌘X and ⌘Z are sent as editing commands, because the native menu that normally handles them never sees CDP events.
  • Touch goes through Input.dispatchTouchEvent. The first touch action on a window also turns on touch emulation (navigator.maxTouchPoints 5, pointer: coarse) until the viewer or MCP that sent it exits.
  • Each process keeps one connection per window and per main process, which also collects console messages and exceptions (the last 1000 per app).
  • For an app with no known inspector, each Node inspector on ports 9229–9238 is asked once, read only, whether it is Electron and which CDP port its app listens on. Any other Node process started with --inspect in that range gets one short connection (it logs Debugger attached.) and is left alone.
  • Launched and attached apps are remembered in ~/.cache/electron-viewer, so the viewer and the MCP see the same apps. Launched apps write their output to a log file there and keep running if the viewer or MCP exits.

Limitations

  • CDP only sees web content. Native UI (title bar, menus, dialogs, tray, notifications) isn't shown or clickable; use evaluate_main to drive it from the main process. Capturing it would mean capturing the screen, and with it any other window on top of the app, so the viewer doesn't.
  • Synchronous dialogs (dialog.showMessageBoxSync) block the main process: evaluate_main waits until the dialog is closed.
  • Each WebContentsView is a separate CDP target, so it is listed as its own window. <webview> tags are not listed.
  • A long press reaches the app as a held touch, so apps that time touchstart themselves see it, but Chromium on desktop doesn't turn it into a context menu. The viewer's touch mode is one finger; use pinch for two.
  • No audio.
  • A --launch-cmd must pass the ports on to Electron; how depends on the tool. Launched apps keep running when the viewer exits; close them from the app (or its terminal).

Contributing

Branching model (main · beta), Conventional Commits and releases are described in CONTRIBUTING.md; changes are listed in CHANGELOG.md. The example app in example/ is there to try changes.

Author

Made by AcertijosApps. See the rest of our libraries at acertijosapps.com/librerias.

Questions or proposals: [email protected] or open an issue.

License

MIT

来源:README.md,提交 980e6e6

工具

20

list_windows

List windows

Lists the running Electron apps (found on CDP ports 9222–9231, launched or attached) and their windows. main=true means the main process is reachable (evaluate_main, real window resizing). Includes the viewer URL to watch a window live in a browser pane.

无需输入参数。

launch_app

Launch app

Starts the Electron project on free debugging ports, so both the renderer (CDP) and the main process (Node inspector) are reachable, and waits for the first window. It runs `electron .` with the project's own Electron, or the launch command configured with --launch-cmd (e.g. a dev server).

输入参数project

attach

Attach to app

Attaches to an Electron app already running with --remote-debugging-port=<port> (and optionally --inspect=<inspect_port> for the main process). Apps on ports 9222–9231 are found without attaching.

输入参数port必填inspect_port

screenshot

Screenshot

Captures the window content (PNG). For click, drag, hover and scroll use normalized coordinates: x = pixel_x / image width, y = pixel_y / image height. Native UI (title bar, menus, dialogs) is not included.

输入参数window

click

Click

Clicks a point of the window content (Input.dispatchMouseEvent). count=2 is a double click.

输入参数x必填y必填countbuttonwindowmodifiers

drag

Drag

Presses the left button at one point, moves to another with intermediate steps and releases (a real drag).

输入参数x1必填x2必填y1必填y2必填windowmodifiersduration_ms

hover

Hover

Moves the mouse to a point without clicking (hover effects, tooltips).

输入参数x必填y必填window

tap

Tap

Taps a point with one finger (Input.dispatchTouchEvent): the page gets touch and pointer events with pointerType "touch". The first touch action turns on touch emulation for the window (maxTouchPoints, pointer: coarse).

输入参数x必填y必填window

swipe

Swipe

Swipes one finger from one point to another (touch events; use drag for the mouse). Scrolls touch lists and drives swipe gestures.

输入参数x1必填x2必填y1必填y2必填windowduration_ms

long_press

Long press

Holds one finger on a point and releases it. Apps that time touchstart themselves see a long press; Chromium on desktop doesn't turn it into a context menu.

输入参数x必填y必填windowduration_ms

pinch

Pinch

Two-finger pinch centered on a point: scale > 1 spreads the fingers (zoom in), scale < 1 brings them together.

输入参数xyscale必填windowduration_ms

scroll

Scroll

Scrolls with the mouse wheel at a point. Positive delta_y scrolls down; deltas are in CSS pixels.

输入参数xywindowdelta_xdelta_y

type_text

Type text

Types text into the focused element (Input.insertText; any Unicode). Click the field first; for Enter or shortcuts use press_key.

输入参数text必填window

press_key

Press key

Presses a key, optionally with modifiers (e.g. key "a" + ["meta"] selects all on macOS). Named keys: Enter, Tab, Backspace, Delete, Escape, Space, ArrowUp, ArrowDown, ArrowLeft, ArrowRight, Home, End, PageUp, PageDown, Insert, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12; or any single character.

输入参数key必填windowmodifiers

resize_window

Resize window

Sets the size of the window content area in CSS px. With the main process it resizes the real window; if the window cannot reach that size (smaller screen, kiosk, full screen), without the main process or with emulate=true, the page viewport is emulated at the exact size instead. Width and height 0 restore the window size.

输入参数width必填height必填windowemulate

reload

Reload

Reloads the page of the window (renderer only; the main process keeps running).

输入参数windowignore_cache

evaluate

Evaluate in renderer

Evaluates a JavaScript expression in the window page (main world) and returns its JSON value. Promises are awaited.

输入参数windowexpression必填

evaluate_main

Evaluate in main process

Evaluates a JavaScript expression in the app's main process (Node inspector) and returns its JSON value. `require` is available, e.g. require('electron').BrowserWindow.getAllWindows().map(w => w.getTitle()). Promises are awaited. Needs main=true in list_windows.

输入参数appexpression必填

get_accessibility_tree

Accessibility tree

Returns the accessibility tree of the window as indented text (role "name" value [state]). Interactive elements carry @(x, y): their center, normalized like click coordinates.

输入参数windowinteractive_only

get_console_logs

Console logs

Console messages and uncaught exceptions of the app: renderer windows and main process (up to the last 1000). source "stdout" returns the raw output of an app started with launch_app.

输入参数applevellimitsourcewindow

版本历史

2
  1. v1.1.0最新Nov 4, 2026
  2. v1.0.0Nov 3, 2026

评分

已满足适用检查项的 90%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分
  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具必需15 分
  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分