Skip to main content
Glama
electron-test-mcp

electron-test-mcp

Electron Test MCP

npm version npm downloads GitHub stars License: MIT

한국어 버전 (Korean)

MCP (Model Context Protocol) server for testing Electron applications using Playwright. Enables AI models like Claude to interact with and test your Electron apps.

🚀 Quick Start

# Run directly with npx
npx electron-test-mcp

# Or install globally
npm install -g electron-test-mcp
electron-test-mcp

Related MCP server: electron-mcp-server

Features

  • Two Connection Modes

    • CDP Mode: Connect to a running Electron app via Chrome DevTools Protocol

    • Launch Mode: Launch a fresh Electron app instance for testing

  • Full Playwright API: screenshot, click, fill, type, hover, press, wait, evaluate, and more

  • Accessibility Snapshots: Get the accessibility tree for element discovery

  • Main Process Access: Execute code in Electron's main process (launch mode only)

How It Works

User <--> AI Model (Claude) <--> MCP Protocol <--> electron-test-mcp <--> Electron App
  1. User: "Click the login button and fill in the email field"

  2. AI Model: Determines which MCP tools to use

  3. MCP Protocol: Standardized communication

  4. electron-test-mcp: Executes Playwright commands on the Electron app

  5. Electron App: Actions are performed in the actual application

Configuration

Claude Desktop / MCP Clients

Add to your MCP configuration file:

{
  "mcpServers": {
    "electron-test": {
      "command": "npx",
      "args": ["electron-test-mcp"]
    }
  }
}

OpenCode

{
  "mcp": {
    "electron-test": {
      "type": "local",
      "command": ["npx", "electron-test-mcp"],
      "enabled": true
    }
  }
}

Connection Modes

Connect to an already running Electron app with remote debugging enabled:

# Start your Electron app with debugging port
electron your-app --remote-debugging-port=9222

# Or with electron-vite
electron-vite dev -- --remote-debugging-port=9222

Then use the connect tool:

connect({ port: 9222 })

Advantages:

  • Works with your existing dev workflow

  • App state preserved between tests

  • Hot reload still works

Launch Mode

Launch a fresh Electron app instance:

launch({ appPath: "./out/main/index.js" })

# With headless mode for CI
launch({ appPath: "./out/main/index.js", headless: true })

Advantages:

  • Clean state for each test

  • Access to main process via evaluateMain

  • Can pass custom environment variables

  • Supports headless mode for CI/automation

Headless Mode (CI/Automation)

Launch Mode

Pass headless: true to run without a visible window:

launch({ appPath: "./out/main/index.js", headless: true })

CDP Mode

Start your Electron app with headless flags before connecting:

# Option 1: Electron headless flag (Electron 28+)
electron your-app --headless=new --remote-debugging-port=9222

# Option 2: xvfb (Linux) - virtual framebuffer
xvfb-run electron your-app --remote-debugging-port=9222

# Option 3: xvfb with specific display (CI environments)
Xvfb :99 -screen 0 1920x1080x24 &
DISPLAY=:99 electron your-app --remote-debugging-port=9222

Then connect normally:

connect({ port: 9222 })

Available Tools

Connection

Tool

Description

connect

Connect to running app via CDP

disconnect

Disconnect from CDP (app keeps running)

launch

Launch new Electron app instance

close

Close launched app

Interaction

Tool

Description

click

Click an element

fill

Fill text into input (clears first)

type

Type text character by character

hover

Hover over an element

press

Press keyboard key

drag

Drag and drop

selectOption

Select from dropdown

Inspection

Tool

Description

screenshot

Take screenshot (returns base64 image)

snapshot

Get accessibility tree

getText

Get element text content

getAttribute

Get element attribute

isVisible

Check if element is visible

count

Count matching elements

Advanced

Tool

Description

wait

Wait for element state

evaluate

Run JS in renderer process

evaluateMain

Run code in main process (launch mode only)

Selectors

Supports all Playwright selectors:

# CSS selectors
[data-testid="submit-btn"]
.my-class
#my-id

# Text selectors
text=Submit
text="Exact Match"

# Role selectors
role=button[name="Submit"]

# Combining
.form >> text=Submit

Usage Examples

Basic Test Flow

1. connect({ port: 9222 })
2. snapshot()  // See the page structure
3. click('[data-testid="login-btn"]')
4. fill('[data-testid="email"]', 'test@example.com')
5. fill('[data-testid="password"]', 'password123')
6. click('text=Sign In')
7. wait({ selector: '[data-testid="dashboard"]' })
8. screenshot()

Main Process Access (Launch Mode)

// Get app version
evaluateMain({
  script: "({ app }) => app.getVersion()",
});

// Show dialog
evaluateMain({
  script: "({ dialog }) => dialog.showMessageBox({ message: 'Hello!' })",
});

With AI Assistant

You can ask Claude or other AI assistants to test your Electron app:

Connect to my Electron app running on port 9222 and:
1. Take a screenshot of the current state
2. Click the "Settings" button in the sidebar
3. Change the theme to dark mode
4. Verify the theme changed by checking the background color

Tips for Testable Electron Apps

  1. Add data-testid attributes to important elements

  2. Enable remote debugging in development: --remote-debugging-port=9222

  3. Use semantic HTML for better accessibility snapshots

  4. Keep selectors stable - prefer data-testid over classes

Development

# Clone repository
git clone https://github.com/lazy-dinosaur/electron-test-mcp.git
cd electron-test-mcp

# Install dependencies
npm install

# Build
npm run build

# Run locally
node dist/index.js

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📄 License

Distributed under the MIT License. See LICENSE file for more information.

❤️ Support

If you find this project useful, please consider giving it a ⭐️ on GitHub!

Available Tools

28 tools
clickC

Click an element

Inputs: selector (required).

closeA

Close the Electron app (only works with launch, not connect)

Inputs: no named parameters.

connectB

Connect to already running Electron app via CDP (e.g., bun run dev:local with --remote-debugging-port)

Inputs: port.

countC

Count elements matching selector

Inputs: selector (required).

dblclickC

Double-click an element

Inputs: selector (required).

disconnectA

Disconnect from CDP (does not close the app)

Inputs: no named parameters.

dragC

Drag and drop from source to target element

Inputs: source (required), target (required).

evaluateC

Execute JavaScript in the renderer process

Inputs: script (required).

evaluateMainC

Execute code in the Electron main process

Inputs: script (required).

fillC

Fill text into an input field

Inputs: text (required), selector (required).

getAttributeC

Get attribute value of an element

Inputs: selector (required), attribute (required).

getTextC

Get text content of an element

Inputs: selector (required).

hoverC

Hover over an element

Inputs: selector (required).

isVisibleC

Check if element is visible

Inputs: selector (required).

launchB

Launch Electron app for testing (builds and runs fresh instance)

Inputs: env, appPath, headless.

mouseClickC

Click at specific coordinates (without selector)

Inputs: x (required), y (required), button, clickCount.

mouseDownC

Press mouse button down

Inputs: button.

mouseMoveC

Move mouse cursor to specific coordinates

Inputs: x (required), y (required), steps.

mouseUpC

Release mouse button

Inputs: button.

pressC

Press a keyboard key

Inputs: key (required).

screenshotC

Take a screenshot of the current window

Inputs: fullPage.

scrollC

Scroll the page using mouse wheel

Inputs: deltaX, deltaY.

scrollIntoViewC

Scroll element into view

Inputs: selector (required).

scrollToC

Scroll to absolute position on the page

Inputs: x, y, behavior.

selectOptionC

Select option(s) from a dropdown

Inputs: value (required), selector (required).

snapshotB

Get accessibility tree snapshot of the page (for finding elements)

Inputs: no named parameters.

typeA

Type text character by character (triggers key events)

Inputs: text (required), selector (required).

waitC

Wait for an element or condition

Inputs: selector (required), state, timeout.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    C2.9/5.0

    Scored across 28 tools

    Disambiguation4/5

    Most tools target clearly distinct actions (click vs fill vs hover vs drag), and the lifecycle tools connect/launch/close/disconnect are well separated. However, there is real overlap among scroll, scrollTo, and scrollIntoView, and click vs mouseClick vs dblclick, which could cause misselection without careful reading.

    Naming Consistency2/5

    The set mixes snake_case-adjacent single words (connect, launch, screenshot, click, fill) with camelCase (getText, selectOption, getAttribute, isVisible, scrollIntoView, mouseMove, mouseClick) and an odd unseparated 'dblclick'. No single predictable verb_noun or verbNoun convention holds throughout.

    Tool Count3/5

    28 tools is heavy for the scope and includes highly granular primitives (mouseMove, mouseDown, mouseUp, mouseClick, scroll/scrollTo/scrollIntoView) that could be consolidated. It is defensible for a full automation harness but sits at the cumbersome end.

    Completeness4/5

    Covers the lifecycle (connect/launch/close/disconnect), inspection (screenshot, snapshot, getText, getAttribute, isVisible, count), and rich interaction plus JS evaluation in both renderer and main process. Minor gaps like file upload, navigation/wait-for-navigation, and iframe handling exist but are workaroundable.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers