WebDriverIO MCP Server
A Model Context Protocol (MCP) server that enables AI assistants to interact with web browsers, local Electron applications,
and mobile applications using WebdriverIO. Automate Chrome, Firefox, Edge, Safari, Electron, iOS, and Android through a unified interface.
Installation
[mcp MCP server]
Add the following configuration to your MCP client settings:
Standard config (works in most clients):
[Install in VS Code]
[Install in VS Code Insiders]
[Install in Cursor]
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS),
%APPDATA%\Claude\claude_desktop_config.json (Windows), or ~/.config/Claude/claude_desktop_config.json (Linux):
Claude Code
Cline
Add to your VS Code settings.json or cline_mcp_settings.json file:
Cursor
Go to Cursor Settings → MCP → Add new MCP Server, or create .cursor/mcp.json:
Codex
Use the Codex CLI:
Or edit ~/.codex/config.toml:
Goose
Go to Advanced settings → Extensions → Add custom extension, or run:
Or edit ~/.config/goose/config.yaml:
Windsurf
Edit ~/.codeium/windsurf/mcp_config.json:
Zed
Edit Zed settings (~/.config/zed/settings.json):
VS Code (Copilot)
⚠️ Restart Required: After adding the configuration, fully restart your MCP client to apply the changes.
Option 2: Global Installation
If you prefer to install globally:
Then use wdio-mcp as the command:
📖 Need help? Follow the MCP install guide.
HTTP Transport (for non-subprocess clients)
By default the server uses stdio (subprocess) transport. For clients that cannot launch subprocesses (e.g.
llama.cpp, OpenAI Codex secure mode), enable HTTP transport:
Then point your MCP client at http://localhost:3000/mcp.
Prerequisites For Mobile App Automation
- Appium Server: Install globally with
npm install -g appium
- Platform Drivers:
- iOS:
appium driver install xcuitest (requires Xcode on macOS)
- Android:
appium driver install uiautomator2 (requires Android Studio)
- Devices/Emulators:
- iOS Simulator (macOS) or physical device
- Android Emulator or physical device
- For iOS Real Devices: You'll need the device's UDID (Unique Device Identifier)
- Find UDID on macOS: Connect device → Open Finder → Select device → Click device name/model to reveal UDID
- Find UDID on Windows: Connect device → iTunes or Apple Devices app → Click device icon → Click "Serial Number"
to reveal UDID
- Xcode method: Window → Devices and Simulators → Select device → UDID shown as "Identifier"
Start the Appium server before using mobile features:
Cloud Providers
Run browser and mobile app tests on cloud real devices and browsers without any local setup. Currently supports
BrowserStack, Sauce Labs,
LambdaTest, TestingBot, and
Digital.ai Testing.
Prerequisites
Set your provider credentials as environment variables or in your MCP client config:
BrowserStack
Sauce Labs
| SAUCE_USERNAME | Sauce Labs username (required) |
| SAUCE_ACCESS_KEY | Sauce Labs access key (required) |
The data center is set per-session via the region parameter in start_session (defaults to eu-central-1).
LambdaTest (TestMu)
| TESTMU_USERNAME | LambdaTest username (required) |
| TESTMU_ACCESS_KEY | LambdaTest access key (required) |
TestingBot
| TESTINGBOT_KEY | TestingBot key (required) |
| TESTINGBOT_SECRET | TestingBot secret (required) |
Digital.ai Testing
| DIGITALAI_CLOUD_URL | Digital.ai cloud host, e.g. https://your-cloud.example.com (required) |
| DIGITALAI_ACCESS_KEY | Digital.ai access key (required) |
The access key is sent via the digitalai:options capability (mobile) or the flat digitalai:accessKey capability (web).
Report pass/fail status: WebdriverIO defaults to the BiDi protocol, over which Digital.ai's cloud cannot observe command failures — so reports default to "Passed". To make the cloud reflect actual pass/fail, opt in to classic WebDriver per session:
(Pure client-side assertion failures still report as "Passed" — only failures that reach the cloud as WebDriver command errors are detected.)
Mobile (Appium): configure your Digital.ai project for Appium-server execution and pick its default Appium version via the project's "Manage default Appium server version" setting — the version is chosen at the project level (and tracks the versions your cloud supports), so this MCP does not pin one. See Appium Server Test Execution.
Browser Sessions
Run a browser on a specific OS/version combination:
Provider-specific os / osVersion behavior:
- BrowserStack —
os and osVersion map to separate bstack:options.os / bstack:options.osVersion fields.
- Sauce Labs / LambdaTest / TestingBot —
os and osVersion are combined into the W3C platformName capability (e.g., os: 'Windows' + osVersion: '11' → platformName: 'Windows 11'). These providers use platformName values like "Windows 11", "MacOS Sequoia", or "Linux". TestingBot defaults to Windows 11 when os is omitted.
- Digital.ai —
os and osVersion are combined into the flat digitalai:osName capability (NOT platformName), e.g. os: 'Windows' + osVersion: '11' → digitalai:osName: 'Windows 11'.
Mobile App Sessions
Test on cloud real devices. First upload your app (or use an existing app URL):
Mobile Browser Sessions
Run a browser on a cloud mobile device — real device or emulator/simulator — without uploading an app:
Note: Mobile browser sessions do not require app, appPath, or noReset. The provider launches a browser directly on the selected device — real or emulator/simulator.
Use list_apps to see previously uploaded apps:
Local Tunnel
To test against URLs that are only accessible on your local machine or internal network, enable a local tunnel:
The tunnel parameter replaces the deprecated browserstackLocal, saucelabsLocal, and testmuLocal params. Set it to true to auto-start the tunnel (stopped automatically after the session), or 'external' to use a tunnel already running on your machine.
Note: With tunnel: true the provider downloads and manages the tunnel binary for you. For tunnel: 'external' you run it yourself — the wdio://saucelabs/local-binary, wdio://testmu/local-binary, and wdio://testingbot/local-binary resources provide download URLs and setup instructions. The TestingBot Tunnel is a single cross-platform Java JAR (requires Java 11+) rather than a per-platform binary.
Reporting Labels
All session types support reporting labels that appear in the provider dashboard:
Cloud Provider Tools
Both tools require a provider parameter ('browserstack', 'saucelabs', 'testmu', 'testingbot', or 'digitalai').
Features
Browser Automation
- Session Management: Start and close browser sessions (Chrome, Firefox, Edge, Safari) with headless/headed modes
- Navigation & Interaction: Navigate URLs, click elements, fill forms, and retrieve content
- Page Analysis: Get visible elements, accessibility trees, take screenshots
- Cookie Management: Get, set, and delete cookies
- Scrolling: Smooth scrolling with configurable distances
- Attach to running Chrome: Connect to an existing Chrome window via
--remote-debugging-port — ideal for testing
authenticated or pre-configured sessions
- Connect to existing WebDriver endpoints: Reuse an already-running Selenium-compatible WebDriver endpoint, such as
a framework-managed browser or a desktop webview automation bridge (like Tauri)
- Device emulation: Apply mobile/tablet presets (iPhone 15, Pixel 7, etc.) to simulate responsive layouts without a
physical device
- Session Recording: All tool calls are automatically recorded and exportable as runnable WebdriverIO JS
Mobile App Automation (iOS/Android)
- Native App Testing: Test iOS (.app/.ipa) and Android (.apk) apps via Appium
- Touch Gestures: Tap, swipe, long-press, drag-and-drop
- App Lifecycle: Launch, background, terminate, check app state
- Context Switching: Seamlessly switch between native and webview contexts for hybrid apps
- Device Control: Rotate, lock/unlock, geolocation, keyboard control, notifications
- Cross-Platform Selectors: Accessibility IDs, XPath, UiAutomator (Android), Predicates (iOS)
Available Tools
The server lists only the tools that fit the current session: browser sessions get the page tools below, mobile sessions the gestures and device tools, Electron sessions the Electron tools. Rarely used groups (mocks, cookies, device emulation, extensions, attaching, the raw element list, cloud app uploads) are listed after the agent calls enable_tools, so their definitions aren't sent with every request. With several live sessions, tools are listed for the union of their platforms. Set WDIO_MCP_TOOLS=all to list everything up front.
Set WDIO_MCP_NETWORK_LOGS to configure the default Chromium network/performance log capture mode for start_session: none (default, off), failures (Network.loadingFailed and Network.responseReceived entries with HTTP status >= 400), or all (every Chromium performance log entry). Individual sessions can override this default via start_session({ networkLogs: 'none' | 'failures' | 'all' }).
Session Management
Multiple sessions
By default the server keeps one session: starting a new one closes the previous one. To run sessions side by side, pass onExisting: "keep" and a name to start_session. The new session is not made the default, so the original keeps answering calls that omit a target.
Every session-bound tool accepts an optional session argument, either a name or a sessionId (sessionId is matched first). Omitting it uses the default session, and targeting a session never changes the default. When a call is targeted, or more than one session is live, the result starts with [session: name (id8)]. Calls to the same session queue and run in order; calls to different sessions run concurrently. An unknown target, or no default session while others are live, returns an error that lists the live sessions. Names are scoped to one server process. The wdio://session/current/* resources always refer to the default session.
Navigation & Page Interaction (Web & Mobile)
Element Interaction (Web & Mobile)
click_element, set_value, tap_element and perform_actions take a ref from snapshot (e12) as well as a selector, in browsers and apps, and each result lists what the action changed on the page or screen: new or changed elements with their refs, or the page it navigated to. An agent rarely needs a separate screenshot or snapshot to see the effect.
Recommended workflow (also sent to clients as server instructions):
snapshot, then act with refs.
- Several steps in a row (fill a form, then submit): one
perform_actions call.
- Refs from the latest result stay valid until the page changes.
Cookie Management (Web, after enable_tools)
Mobile Gestures (iOS/Android)
Context Switching (Hybrid Apps)
Device Control (iOS/Android)
Documentation Search
MCP Resources (read-only, no tool call needed)
Usage Examples
Real-World Test Cases
Example 1: Testing Demo Android App (Book Scanning)
Example 2: Testing World of Books E-commerce Site
Browser Automation
Basic web testing prompt:
Browser configuration options:
Mocking
Mocking is available through mock, get_mock_calls, and manage_mock (listed after enable_tools({ groups: ['mocks'] })), for both Electron main-process API functions and browser network requests.
mockType accepts 'electron' or 'browser'. Browser mocks are the default in WebDriver sessions when mockType is omitted. Electron sessions require an explicit selection because they can target both kinds of mocks. iOS/Android Appium sessions do not support mocking.
Electron behavior defaults to mockReturnValue; mockResolvedValue and mockRejectedValue support async APIs. Each has a Once variant for queued responses. Repeated configuration preserves the existing mock and call history. Values must be JSON; omit value for undefined. clear removes call history, reset also removes behavior and queued responses, and restore reinstates the original function. These tools support individual API functions; class mocks and arbitrary mock implementations are not exposed.
Browser mocks intercept network requests matching a url glob (for example **/api/todos) with an optional method filter. behavior is optional: omit it to passively record matching requests without altering them — the mock still continues every request. respond and respondOnce require value (the JSON response body) and accept optional statusCode and headers; abort/abortOnce fail the request, and redirect/redirectOnce send it to the URL in value. get_mock_calls returns { calls, callCount } for the intercepted requests, and manage_mock takes clear, reset, or restore.
Browser mocks require a BiDi-enabled session — start it with capabilities: { webSocketUrl: true }. Attached sessions default to BiDi off, so browser mocks do not work there unless the attached session negotiated BiDi; in Electron sessions browser mocks additionally require BiDi. Handles belong to the active browser session and cannot be reused after it closes or is replaced. All three tools participate in tracing and generated replay.
Electron applications
Electron support is local-only and uses the official @wdio/electron-service standalone lifecycle. It requires Node.js 22.23.4, 24.22.1, 26.12.1 or a later release of these lines, as the server does. Put service options such as appBinaryPath, appEntryPoint, and appArgs in capabilities['wdio:electronServiceOptions']; use top-level electronRootDir for the service's Electron Builder/Electron Forge discovery. When testing a binary outside the project, set browserVersion to the Electron version so the service can select a compatible Chromedriver.
Existing browser DOM tools work against the Electron renderer. close_session always tears down MCP-managed Electron sessions; detach: true is intentionally unsupported. Main/renderer log capture can be enabled with captureMainProcessLogs or captureRendererLogs plus logDir. To trigger an app deeplink, explicitly configure its URI scheme when starting the Electron session. The scheme has no colon and only URLs with that exact scheme can be dispatched:
Attach to a running Chrome instance:
Connect to an existing WebDriver endpoint:
Use provider: 'external' when another process already owns the browser or webview lifecycle and exposes a W3C
WebDriver endpoint. This is useful for Selenium Grid sessions, externally managed browser drivers, or desktop apps like Tauri apps that
embed a webview and expose WebDriver separately. The MCP server connects to the endpoint; it does not launch or stop the
target application, start tunnels, or manage framework-specific setup.
For desktop webview apps such as Tauri, first start the app and its WebDriver bridge outside of this MCP server, then
pass the endpoint and the required capabilities. For example, a Tauri WebDriver bridge may require
capabilities: {browserName: 'tauri'}.
Attach to an existing WebDriver or Appium session:
Use attach_session when the session has already been created by another process. The MCP reuses the selected provider's
endpoint and credentials, registers the appropriate browser/mobile command set locally, and does not issue a new-session
request. Attached sessions are externally managed: close_session() detaches by default, while
close_session({detach: false}) explicitly terminates the remote session.
An existing cloud session must keep using the tunnel it was created with. attach_session never starts or stops a tunnel,
so keep the original tunnel process alive for as long as the session needs it.
Device emulation (requires BiDi session):
Web extensions (requires BiDi session):
Mobile App Automation
Testing an iOS app on simulator:
Preserving app state between sessions:
Testing an iOS app on real device:
Testing an Android app:
Advanced element detection:
Hybrid app testing (switching contexts):
Important Notes
⚠️ Session Management:
- Only one session (browser OR app) can be active at a time
- Always close sessions when done to free system resources
- To switch between browser and mobile, close the current session first
- Use
close_session({ detach: true }) to disconnect without terminating the session on the Appium server
- State preservation can be controlled with
noReset and fullReset parameters during session creation
- Sessions created with
noReset: true or without appPath will automatically detach on close
- Sessions adopted with
attach_session always detach on close unless detach: false is explicitly requested
⚠️ Task Planning:
- Break complex automation into smaller, focused operations
- Claude may consume message limits quickly with extensive automation
⚠️ Mobile Automation:
- Appium server must be running before starting mobile sessions
- Ensure emulators/simulators are running and devices are connected
- iOS automation requires macOS with Xcode installed
- iOS Real Devices: Testing on physical iOS devices requires the device's UDID (40-character unique identifier). See
Prerequisites section for how to find your UDID
Selector Syntax Quick Reference
Web (CSS/XPath):
- CSS:
button.my-class, #element-id
- XPath:
//button[@class='my-class']
- Text:
button=Exact text, a*=Contains text
Mobile (Cross-Platform):
- Accessibility ID:
~loginButton (works on both iOS and Android)
- Android UiAutomator:
android=new UiSelector().text("Login")
- iOS Predicate:
-ios predicate string:label == "Login" AND visible == 1
- XPath:
//android.widget.Button[@text="Login"]
Advanced Features
App State Preservation
State Preservation with noReset/fullReset:
Control app state when creating new sessions using the noReset and fullReset parameters:
Example with state preservation:
Detach from Sessions:
The close_session tool supports a detach parameter that disconnects from the session without terminating it on the
Appium server:
Sessions created with noReset: true or without appPath will automatically detach on close.
Sessions adopted with attach_session are externally managed and also detach by default; pass detach: false only when the
MCP should deliberately terminate the existing remote session.
This is particularly useful when:
- Preserving app state for manual testing continuation
- Debugging multi-step workflows (leave session running between tool invocations)
- Testing scenarios where you want the app to remain installed and in current state
Smart Element Detection
- Platform-specific element classification: Automatically identifies interactable elements vs layout containers
- Android: Button, EditText, CheckBox vs ViewGroup, FrameLayout, ScrollView
- iOS: Button, TextField, Switch vs View, StackView, CollectionView
- Multiple locator strategies: Each element provides accessibility ID, resource ID, text, XPath, and
platform-specific selectors
- Viewport filtering: Control whether to get only visible elements or all elements including off-screen
- Layout debugging: Optionally include container elements to understand UI hierarchy
Automatic Permission & Alert Handling
Both iOS and Android sessions now support automatic handling of system permissions and alerts:
autoGrantPermissions (default: true): Automatically grants app permissions (camera, location, etc.)
autoAcceptAlerts (default: true): Automatically accepts system alerts and dialogs
autoDismissAlerts (optional): Set to true to dismiss alerts instead of accepting them
This eliminates the need to manually handle permission popups during automated testing.
Technical Details
- Built with: TypeScript, WebDriverIO, Appium
- Browser Support: Chrome, Firefox, Edge (headed/headless, automated driver management), Safari (headed only; macOS)
- Mobile Support: iOS (XCUITest) and Android (UiAutomator2/Espresso)
- Protocol: Model Context Protocol (MCP) for Claude Desktop integration
- Session Model: One default session, with optional named sessions running alongside (browser or mobile app)
- Data Format: TOON (Token-Oriented Object Notation) for efficient LLM communication
- Element Detection: XML-based page source parsing with intelligent filtering and multi-strategy locator generation
Session Recording & Code Export
Every tool call is automatically recorded to a session history. You can inspect sessions and export runnable code via
MCP resources — no extra tool calls needed:
wdio://sessions — lists all recorded sessions with type, timestamps, and step count
wdio://session/current/steps — step log for the default session
wdio://session/current/code — generated runnable WebdriverIO JS for the default session
wdio://session/{sessionId}/steps — step log for any past session by ID
wdio://session/{sessionId}/code — generated JS for any past session by ID
The generated script reconstructs the full session — including capabilities, navigation, clicks, and inputs — as a
standalone import { remote } from 'webdriverio' file. For cloud provider sessions it includes the full try/catch/finally
with automatic session result marking via the provider's REST API.
Trace Recording
Passing trace: true to start_session produces a Playwright-compatible .trace zip in the .trace/ directory when
the session closes. The zip is playable at player.vibium.dev and shows a filmstrip of
screenshots alongside the action timeline.
How screenshots are timed
Appium's takeScreenshot round-trip takes 700–1300 ms on a local emulator, which is long enough for the previous
action's animations to settle. We exploit this: each screenshot is captured before the next action fires, so what
the Appium server returns is already the settled result of the prior action.
The tricky part is making the trace player show that screenshot under the right action. The player associates a
screencast-frame event with whichever action's time window contains the frame's timestamp field. If the timestamp
is set to "now" (capture time), it falls before the current action's startTime and the player labels it as the
before state of the next action — one action out of sync.
The fix: stamp each screencast-frame with lastAfterEndTime — the endTime of the action that just completed. That
places the frame inside the previous action's window, so the player shows it as the result of that action, not the
precursor to the next one.
The final screenshot at session close is stamped with the last action's endTime, so it renders under that action
rather than appearing as an orphaned frame after the timeline ends.
Session Logs
The wdio://session/current/logs resource returns crash reports, console errors, and system logs for the current
session, auto-detecting the session type to fetch the right log buffer:
Note: Reading this resource clears the log buffer (per the WebDriver spec). Subsequent reads return only entries
accumulated since the last read. Browser logs require Chromium (Chrome/Edge) — Firefox and Safari do not support the
getLogs command.
The response is JSON with sessionType, logTypes (available log types), and entries — each entry includes level,
message, timestamp (Unix ms), and timestampISO.
Troubleshooting
Browser automation not working?
- Ensure Chrome, Firefox, Edge, or Safari is installed (Safari requires macOS)
- Try restarting Claude Desktop completely
- Check that no other WebDriver instances are running
Mobile automation not working?
- Verify Appium server is running:
appium
- Check device/emulator is running:
adb devices (Android) or Xcode Devices (iOS)
- Ensure correct platform drivers are installed
- Verify app path is correct and accessible
Found issues or have suggestions? Please share your feedback!