electron-mcp
# @iamvinitk/electron-mcp
MCP server for interacting with and debugging an Electron app.
Spawns (or attaches to) an Electron process with the Chrome
DevTools Protocol on the renderer and the Node inspector on the
main process, then exposes ~20 tools for driving / inspecting both
sides.
Works with any Electron or Electron Forge project.
## Install
Run it on demand with `npx` (no install needed):
```sh
npx -y @iamvinitk/electron-mcp
```
…or install the `electron-mcp` binary globally:
```sh
npm i -g @iamvinitk/electron-mcp
electron-mcp
```
## Use it from an MCP client
The server speaks MCP over stdio — point any MCP client at it. For a
typical `mcpServers` config:
```json
{
"mcpServers": {
"electron": {
"command": "npx",
"args": ["-y", "@iamvinitk/electron-mcp"]
}
}
}
```
(To run from a local clone instead, `npm install && npm run build`, then
point your client's `command`/`args` at `node /path/to/build/index.js`.)
## Tools
### Lifecycle
| Tool | Purpose |
|---|---|
| `launch_via_npm` | Spawn via `npm run <script>` (electron-forge dev). |
| `launch_app` | Spawn the `electron` binary directly (for packaged/test builds). |
| `attach_app` | Register an already-running Electron instance. |
| `stop_app` | Close CDP clients, kill spawned process, drop the session. |
| `list_apps` | Snapshot every active session (pid, ports, status, uptime). |
| `ping_inspector` | Zero-cost sanity check that the Node inspector is reachable. |
### Window automation
| Tool | Purpose |
|---|---|
| `list_windows` | Enumerate renderer CDP targets. Skips `devtools://` pages. |
| `evaluate` | Run JS in the renderer via `Runtime.evaluate`. |
| `navigate` | Page-navigate the renderer. |
| `screenshot` | Capture a PNG/JPEG via `Page.captureScreenshot`. |
### Logs & network
| Tool | Purpose |
|---|---|
| `get_main_logs` | Read captured stdout/stderr from the spawned process. |
| `get_console_messages` | Read renderer `console.*` output and exceptions. |
| `get_network_requests` | Read renderer fetch/XHR activity (e.g. the app's API calls). |
| `clear_logs` | Wipe one or more ring buffers — useful before recording. |
### IPC
| Tool | Purpose |
|---|---|
| `list_electron_api_methods` | `Object.keys(window.electronAPI)`. |
| `invoke_electron_api` | Call any preload-exposed IPC method directly. |
| `enable_ipc_logging` | Inject a proxy that captures every IPC call the renderer makes. |
| `get_ipc_log` | Drain + read the captured IPC events. |
### Main-process state
| Tool | Purpose |
|---|---|
| `get_app_paths` | `app.getPath(...)`, `app.isPackaged`, `process.resourcesPath`, versions. |
| `read_user_data_file` | Path-traversal-guarded read under `app.getPath('userData')`. |
### Resources
| URI | Purpose |
|---|---|
| `electron://sessions` | JSON list of every tracked session. |
| `electron://session/{id}` | Detailed snapshot of one session (windows, recent logs, buffer sizes). |
## Typical debug flow
```
1. launch_via_npm → id=electron-abcd1234
2. list_windows id=... → pick the target id
3. evaluate id=... expr=… → probe renderer state
4. invoke_electron_api → test an IPC handler
5. get_main_logs / get_console_messages / get_network_requests
6. stop_app → tear down
```
## How it works
- **Launch modes.** `launch_app` invokes the `electron` binary with
`--remote-debugging-port=<N> --inspect=<N+?> <appPath>`.
`launch_via_npm` wraps `npm run <script>` and smuggles
`--inspect-electron` through forge's flag plumbing (double `--`
separators), because forge only exposes main-process inspection
that way.
- **Two debug channels.** Electron renderers are Chromium pages, so
they speak CDP on `--remote-debugging-port`. The main process is
Node, which speaks the (near-identical) Node inspector protocol on
`--inspect`. Both use `chrome-remote-interface`; we keep per-session
client caches so repeated tool calls don't reconnect.
- **Log capture.** Main-process stdout/stderr flow into a ring
buffer on spawn. Renderer console, exceptions, and network events
flow into separate ring buffers via CDP event subscribers set up
the first time each target's client opens. `enable_ipc_logging`
installs a `Proxy` over `window.electronAPI` so every IPC call the
renderer makes (regardless of origin) is observable; re-injected
on `Page.frameNavigated`.
- **Stdin stays open.** Electron-forge's `start` command interprets
an EOF on its stdin as "the user left the interactive REPL" and
shuts down the child Electron. We hold stdin open (never write to
it) so forge keeps running as long as we want.
## Limitations
- **Packaged builds.** If the app's Electron Forge config disables the
`EnableNodeCliInspectArguments` fuse, that blocks `--inspect` at the
fuse layer in packaged builds. Window automation and
renderer-side tools still work against a packaged build if you pass
`--remote-debugging-port`, but main-process tools (`get_app_paths`,
`read_user_data_file`) need a non-fused dev build.
- **Single-process model.** One MCP server → any number of tracked
sessions. Sessions don't persist across MCP restarts; restart the
MCP server to refresh.
- **macOS stdin quirk.** Some terminal emulators (rare) block SIGINT
delivery through `npm`'s stdin pipe. If `stop_app` leaves a zombie
forge process, `pkill -f electron-forge` is the backup.
## Development
```sh
npm run watch # tsc --watch
npm run typecheck # tsc --noEmit
```
There are no unit tests in this package. Verification is
end-to-end against a running Electron app.
TDQS
Scored across 20 tools
Each tool targets a distinct aspect of Electron debugging (launching, attaching, logging, inspection, etc.), with clear boundaries even among similar logging tools. No two tools appear to overlap in purpose.
All tool names use lowercase snake_case with a consistent verb_noun pattern (e.g., list_windows, get_app_paths, launch_app). There is no mixing of conventions, making the set predictable.
20 tools is on the higher side, but each tool serves a specific need for a complex domain. Some tools like clear_logs and get_main_logs could arguably be combined, but the count is still reasonable.
The tools cover the full lifecycle of debugging an Electron app: launch/attach, inspect (renderer and main process), capture logs (console, IPC, network), navigate, screenshot, and stop. No obvious gaps for common tasks.