trackmcp
Back to directory

A Model Context Protocol (MCP) server that enables AI assistants to interact with web browsers and mobile applications using WebDriverIO. Automate Chrome browsers, iOS apps, and Android apps—all through a unified interface.

37 stars TypeScriptOthers Updated Sep 4, 2026
mcpwebdriveriobrowsermobiletestingandroidappiumios

Documentation

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):

json
{
  "mcpServers": {
    "wdio-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "@wdio/mcp@latest"
      ]
    }
  }
}
Install in VS Code
Install in VS Code Insiders

[](https://cursor.com/en/install-mcp?name=WebDriverIO&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkB3ZGlvL21jcEBsYXRlc3QiXX0%3D)

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):

json
{
  "mcpServers": {
    "wdio-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "@wdio/mcp@latest"
      ]
    }
  }
}

Claude Code

bash
claude mcp add wdio-mcp -- npx -y @wdio/mcp@latest

Cline

Add to your VS Code `settings.json` or `cline_mcp_settings.json` file:

json
{
  "mcpServers": {
    "wdio-mcp": {
      "type": "stdio",
      "command": "npx",
      "args": [
        "-y",
        "@wdio/mcp@latest"
      ]
    }
  }
}

Cursor

Go to `Cursor Settings` → `MCP` → `Add new MCP Server`, or create `.cursor/mcp.json`:

json
{
  "mcpServers": {
    "wdio-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "@wdio/mcp@latest"
      ]
    }
  }
}

Codex

Use the Codex CLI:

bash
codex mcp add wdio-mcp npx "@wdio/mcp@latest"

Or edit `~/.codex/config.toml`:

toml
[mcp_servers.wdio-mcp]
command = "npx"
args = ["@wdio/mcp@latest"]

Goose

Go to `Advanced settings` → `Extensions` → `Add custom extension`, or run:

bash
goose configure

Or edit `~/.config/goose/config.yaml`:

yaml
extensions:
  wdio-mcp:
    name: WebDriverIO MCP
    cmd: npx
    args: [ -y, "@wdio/mcp@latest" ]
    enabled: true
    type: stdio

Windsurf

Edit `~/.codeium/windsurf/mcp_config.json`:

json
{
  "mcpServers": {
    "wdio-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "@wdio/mcp@latest"
      ]
    }
  }
}

Zed

Edit Zed settings (`~/.config/zed/settings.json`):

json
{
  "context_servers": {
    "wdio-mcp": {
      "source": "custom",
      "command": "npx",
      "args": [
        "-y",
        "@wdio/mcp@latest"
      ]
    }
  }
}

VS Code (Copilot)

bash
code --add-mcp '{"name":"wdio-mcp","command":"npx","args":["-y","@wdio/mcp@latest"]}'

> ⚠️ 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:

bash
npm install -g @wdio/mcp

Then use `wdio-mcp` as the command:

json
{
  "mcpServers": {
    "wdio-mcp": {
      "command": "wdio-mcp"
    }
  }
}

📖 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:

bash
npx @wdio/mcp --http --port 3000
FlagDefaultDescription
`--http`Enable HTTP transport mode
`--port``3000`Port to listen on
`--allowedHosts``localhost,127.0.0.1,::1`Allowed `Host` header values (DNS rebinding protection)
`--allowedOrigins`_(none — browser clients blocked)_Allowed `Origin` values for CORS. Use `*` to allow all.

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:
    • Devices/Emulators:
      • For iOS Real Devices: You'll need the device's UDID (Unique Device Identifier)

        to reveal UDID

          Start the Appium server before using mobile features:

          bash
          appium
          # Server runs at http://127.0.0.1:4723 by default

          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

          bash
          export BROWSERSTACK_USERNAME=your_username
          export BROWSERSTACK_ACCESS_KEY=your_access_key
          json
          {
            "mcpServers": {
              "wdio-mcp": {
                "command": "npx",
                "args": ["-y", "@wdio/mcp@latest"],
                "env": {
                  "BROWSERSTACK_USERNAME": "your_username",
                  "BROWSERSTACK_ACCESS_KEY": "your_access_key"
                }
              }
            }
          }

          Sauce Labs

          bash
          export SAUCE_USERNAME=your_username
          export SAUCE_ACCESS_KEY=your_access_key
          json
          {
            "mcpServers": {
              "wdio-mcp": {
                "command": "npx",
                "args": ["-y", "@wdio/mcp@latest"],
                "env": {
                  "SAUCE_USERNAME": "your_username",
                  "SAUCE_ACCESS_KEY": "your_access_key"
                }
              }
            }
          }
          `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)

          bash
          export TESTMU_USERNAME=your_username
          export TESTMU_ACCESS_KEY=your_access_key
          json
          {
            "mcpServers": {
              "wdio-mcp": {
                "command": "npx",
                "args": ["-y", "@wdio/mcp@latest"],
                "env": {
                  "TESTMU_USERNAME": "your_username",
                  "TESTMU_ACCESS_KEY": "your_access_key"
                }
              }
            }
          }
          `TESTMU_USERNAME`LambdaTest username (required)
          `TESTMU_ACCESS_KEY`LambdaTest access key (required)

          TestingBot

          bash
          export TESTINGBOT_KEY=your_key
          export TESTINGBOT_SECRET=your_secret
          json
          {
            "mcpServers": {
              "wdio-mcp": {
                "command": "npx",
                "args": ["-y", "@wdio/mcp@latest"],
                "env": {
                  "TESTINGBOT_KEY": "your_key",
                  "TESTINGBOT_SECRET": "your_secret"
                }
              }
            }
          }
          `TESTINGBOT_KEY`TestingBot key (required)
          `TESTINGBOT_SECRET`TestingBot secret (required)

          Digital.ai Testing

          bash
          export DIGITALAI_CLOUD_URL=https://your-cloud.example.com
          export DIGITALAI_ACCESS_KEY=your_access_key
          json
          {
            "mcpServers": {
              "wdio-mcp": {
                "command": "npx",
                "args": ["-y", "@wdio/mcp@latest"],
                "env": {
                  "DIGITALAI_CLOUD_URL": "https://your-cloud.example.com",
                  "DIGITALAI_ACCESS_KEY": "your_access_key"
                }
              }
            }
          }
          `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:

          js
          start_session({
            provider: 'digitalai', platform: 'browser', browser: 'chrome', os: 'Windows 10',
            capabilities: { 'wdio:enforceWebDriverClassic': true }
          })

          (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:

          javascript
          // BrowserStack
          start_session({
              provider: 'browserstack',
              platform: 'browser',
              browser: 'chrome',           // chrome | firefox | edge | safari
              browserVersion: 'latest',    // default: latest
              os: 'Windows',               // e.g. "Windows", "OS X"
              osVersion: '11',             // e.g. "11", "Sequoia"
              reporting: {
                  project: 'My Project',
                  build: 'v1.2.0',
                  session: 'Login flow'
              }
          })
          
          // Sauce Labs
          start_session({
              provider: 'saucelabs',
              platform: 'browser',
              browser: 'chrome',
              os: 'Windows',               // combined with osVersion → platformName
              osVersion: '11',             // e.g. "11", "15" (numbered Mac naming)
              region: 'eu-central-1',      // default: eu-central-1
              reporting: {
                  build: 'v1.2.0',
                  session: 'Login flow'
              }
          })
          
          // LambdaTest
          start_session({
              provider: 'testmu',
              platform: 'browser',
              browser: 'chrome',
              os: 'Windows',               // combined with osVersion → platformName
              osVersion: '11',             // e.g. "11", "Sequoia" (optional)
              reporting: {
                  project: 'My Project',
                  build: 'v1.2.0',
                  session: 'Login flow'
              }
          })
          
          // TestingBot
          start_session({
              provider: 'testingbot',
              platform: 'browser',
              browser: 'chrome',
              os: 'Windows',               // combined with osVersion → platformName (default: Windows 11)
              osVersion: '11',
              reporting: {
                  build: 'v1.2.0',
                  session: 'Login flow'
              }
          })
          
          // Digital.ai
          start_session({
              provider: 'digitalai',
              platform: 'browser',
              browser: 'chrome',
              os: 'Windows',               // combined with osVersion → digitalai:osName (optional)
              osVersion: '11',
              reporting: {
                  session: 'Login flow'    // → digitalai:testName (flat capability, not nested)
              }
          })

          > 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):

          javascript
          // BrowserStack: returns bs:// URL
          upload_app({ provider: 'browserstack', path: '/path/to/app.apk' })
          
          // Sauce Labs: returns storage:filename= reference
          upload_app({ provider: 'saucelabs', path: '/path/to/app.apk' })
          
          // LambdaTest: returns lt:// URL
          upload_app({ provider: 'testmu', path: '/path/to/app.apk' })
          
          // TestingBot: returns tb:// URL
          upload_app({ provider: 'testingbot', path: '/path/to/app.apk' })
          
          // Digital.ai: returns cloud: reference
          upload_app({ provider: 'digitalai', path: '/path/to/app.apk' })
          
          // Start a session
          start_session({
              provider: 'browserstack',
              platform: 'android',
              app: 'bs://abc123...',
              deviceName: 'Samsung Galaxy S23',
              platformVersion: '13.0'
          })
          
          // Sauce Labs native app
          start_session({
              provider: 'saucelabs',
              platform: 'android',
              app: 'storage:filename=myapp.apk',
              deviceName: 'Samsung.*',
              platformVersion: '16'
          })
          
          // LambdaTest native app
          start_session({
              provider: 'testmu',
              platform: 'android',
              app: 'lt://abc123...',
              deviceName: 'Pixel 7',
              platformVersion: '13'
          })
          
          // TestingBot native app
          start_session({
              provider: 'testingbot',
              platform: 'android',
              app: 'tb://abc123...',
              deviceName: 'Pixel 7',
              platformVersion: '13'
          })
          
          // Digital.ai native app — devices are selected via a deviceQuery
          start_session({
              provider: 'digitalai',
              platform: 'android',
              app: 'cloud:com.example.app',
              deviceQuery: "@os='android' and @version='14' and @name='.*Pixel.*'"
              // or omit deviceQuery and pass deviceName / platformVersion to build one
          })

          Mobile Browser Sessions

          Run a browser on a cloud mobile device — real device or emulator/simulator — without uploading an app:

          javascript
          // BrowserStack — Chrome on Android emulator
          start_session({
              provider: 'browserstack',
              platform: 'android',
              browser: 'chrome',
              deviceName: 'Google Pixel 7',
              platformVersion: '13'
          })
          
          // Sauce Labs — Safari on iOS simulator
          start_session({
              provider: 'saucelabs',
              platform: 'ios',
              browser: 'safari',
              deviceName: 'iPhone 15',
              platformVersion: '18',
              region: 'eu-central-1'
          })
          
          // LambdaTest — Chrome on Android emulator
          start_session({
              provider: 'testmu',
              platform: 'android',
              browser: 'chrome',
              deviceName: 'Pixel 7',
              platformVersion: '13'
          })
          
          // TestingBot — Chrome on Android emulator
          start_session({
              provider: 'testingbot',
              platform: 'android',
              browser: 'chrome',
              deviceName: 'Pixel 7',
              platformVersion: '13'
          })
          
          // Digital.ai — Chrome on an Android device (real or emulator; selected via a deviceQuery)
          start_session({
              provider: 'digitalai',
              platform: 'android',
              browser: 'chrome',
              deviceName: 'Pixel 7',
              platformVersion: '13'
              // or omit deviceName / platformVersion and pass deviceQuery directly, e.g.
              // deviceQuery: "@os='android' and @emulator='true'" to force an emulator
          })

          > 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:

          javascript
          list_apps({ provider: 'browserstack' })
          list_apps({ provider: 'saucelabs', sortBy: 'app_name' })
          list_apps({ provider: 'testmu' })
          list_apps({ provider: 'testingbot' })
          list_apps({ provider: 'digitalai' })
          list_apps({ provider: 'browserstack', organizationWide: true })

          Local Tunnel

          To test against URLs that are only accessible on your local machine or internal network, enable a local tunnel:

          javascript
          // Auto-start tunnel (provider manages lifecycle)
          start_session({
              provider: 'saucelabs',
              platform: 'browser',
              tunnel: true                  // auto-starts tunnel before session
          })
          
          // Use an already-running tunnel
          start_session({
              provider: 'saucelabs',
              platform: 'browser',
              tunnel: 'external'            // uses existing 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:

          FieldDescription
          `reporting.project`Group sessions under a project name
          `reporting.build`Tag sessions with a build/version label
          `reporting.session`Name for the individual test session

          Cloud Provider Tools

          ToolDescription
          `upload_app`Upload a local `.apk` or `.ipa` to the provider; returns an app URL/reference
          `list_apps`List apps previously uploaded to the provider's app storage

          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

          Session Management

          ToolDescription
          `start_session`Start a browser, local Electron application, or mobile app session; `attach: true` retains the existing Chrome CDP connection mode
          `attach_session`Attach to an existing remote WebDriver/Appium session by ID without creating a new session
          `launch_chrome`Launch a new Chrome instance with remote debugging enabled (for use with `start_session({ attach: true })`)
          `close_session`Close or detach from the current session (supports `detach: true` to disconnect without terminating)
          `emulate_device`Emulate a mobile/tablet device preset (viewport, DPR, UA, touch); requires BiDi session
          `open_web_extension`Install a web extension through WebDriver BiDi and open one of its extension pages so normal page tools can drive its UI
          ToolDescription
          `navigate`Navigate to a URL
          `get_elements`Get visible, interactable elements on the page. Supports `inViewportOnly` (default: true) to filter viewport elements, and `includeContainers` (default: false) to include layout containers on mobile
          `get_accessibility_tree`Get the page accessibility tree with roles, names, and selectors. Supports filtering by role and pagination. Browser-only.
          `get_screenshot`Take a screenshot of the current page or screen (base64-encoded, auto-resized to max 2000px / 1MB)
          `get_tabs`List all open browser tabs with handle, title, URL, and active status. Browser-only.
          `scroll`Scroll in a direction (up/down) by specified pixels. Browser-only.
          `execute_script`Execute arbitrary JavaScript in the browser, or Appium mobile commands on devices
          `execute_electron_script`Execute privileged JavaScript in the Electron main process (Electron sessions only)
          `trigger_electron_deeplink`Trigger an Electron deeplink whose scheme was explicitly configured at session start
          `switch_tab`Switch to a different browser tab by handle or 0-based index. Browser-only.
          `switch_frame`Switch into an iframe by CSS/XPath selector, or back to the top-level frame if no selector is given. Browser-only.

          Element Interaction (Web & Mobile)

          ToolDescription
          `click_element`Click an element
          `set_value`Type text into input fields
          ToolDescription
          `get_cookies`Get all cookies for the current session, or a single cookie by name
          `set_cookie`Set a cookie with name, value, and optional attributes
          `delete_cookies`Delete all cookies or a specific cookie

          Mobile Gestures (iOS/Android)

          ToolDescription
          `tap_element`Tap an element by selector or coordinates
          `swipe`Swipe in a direction (up/down/left/right)
          `drag_and_drop`Drag from one location to another

          Context Switching (Hybrid Apps)

          ToolDescription
          `get_contexts`List available automation contexts (NATIVE_APP, WEBVIEW_*) and the currently active one
          `switch_context`Switch between native and webview contexts

          Device Control (iOS/Android)

          ToolDescription
          `get_app_state`Get the current lifecycle state of a mobile app (not installed / not running / background / foreground)
          `rotate_device`Rotate to portrait or landscape
          `hide_keyboard`Hide on-screen keyboard
          `set_geolocation`Set device GPS location

          MCP Resources (read-only, no tool call needed)

          ResourceDescription
          `wdio://sessions`Index of all recorded sessions
          `wdio://session/current/steps`Step log for the active session
          `wdio://session/current/code`Generated runnable WebdriverIO JS for the active session
          `wdio://session/{id}/steps`Step log for any past session by ID
          `wdio://session/{id}/code`Generated JS for any past session by ID
          `wdio://session/current/elements`Interactable elements (viewport-only by default)
          `wdio://session/current/accessibility`Accessibility tree
          `wdio://session/current/screenshot`Screenshot (base64)
          `wdio://session/current/cookies`Browser cookies
          `wdio://session/current/tabs`Open browser tabs
          `wdio://session/current/contexts`Native/webview contexts (mobile)
          `wdio://session/current/context`Currently active context (mobile)
          `wdio://session/current/app-state/{bundleId}`Mobile app lifecycle state for a given bundle ID
          `wdio://session/current/geolocation`Device geolocation
          `wdio://session/current/capabilities`Resolved WebDriver capabilities for the active session
          `wdio://session/current/logs`Crash/console logs for the current session. Auto-detects session type — browser: console logs + JS exceptions; Android: logcat; iOS: crashlog + syslog
          `wdio://browserstack/local-binary`BrowserStack Local binary download URL and start command
          `wdio://saucelabs/local-binary`Sauce Connect binary download URL and start command
          `wdio://testmu/local-binary`TestMu Tunnel binary download URL and start command
          `wdio://testingbot/local-binary`TestingBot Tunnel JAR download URL and start command (Java 11+)

          Usage Examples

          Real-World Test Cases

          Example 1: Testing Demo Android App (Book Scanning)

          code
          Test the Demo Android app at C:\Users\demo-liveApiGbRegionNonMinifiedRelease-3018788.apk on emulator-5554:
          1. Start the app with auto-grant permissions
          2. Get visible elements on the onboarding screen
          3. Tap "Skip" to bypass onboarding
          4. Verify main screen loads
          5. Take a screenshot

          Example 2: Testing World of Books E-commerce Site

          code
          You are a Testing expert, and want to assess the basic workflows of worldofbooks.com:
          - Open World of Books (accept all cookies)
          - Get visible elements to see navigation structure
          - Search for a fiction book
          - Choose one and validate if there are NEW and used book options
          - Report your findings at the end

          Browser Automation

          Basic web testing prompt:

          code
          You are a Testing expert, and want to assess the basic workflows of a web application:
          - Open World of Books (accept all cookies)
          - Search for a fiction book
          - Choose one and validate if there are NEW and used book options
          - Report your findings at the end

          Browser configuration options:

          javascript
          // Default settings (headed mode, 1280x1080)
          start_session({platform: 'browser'})
          
          // Firefox
          start_session({platform: 'browser', browser: 'firefox'})
          
          // Edge
          start_session({platform: 'browser', browser: 'edge'})
          
          // Safari (headed only; requires macOS)
          start_session({platform: 'browser', browser: 'safari'})
          
          // Headless mode
          start_session({platform: 'browser', headless: true})
          
          // Custom dimensions
          start_session({platform: 'browser', windowWidth: 1920, windowHeight: 1080})
          
          // Pass custom capabilities (e.g. Chrome extensions, profile, prefs)
          start_session({
              platform: 'browser',
              headless: false,
              capabilities: {
                  'goog:chromeOptions': {
                      args: ['--user-data-dir=/tmp/wdio-mcp-profile', '--load-extension=/path/to/unpacked-extension']
                  }
              }
          })

          Electron applications

          Electron support is local-only and uses the official `@wdio/electron-service` standalone lifecycle. It requires Node.js 22.12 or newer. 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.

          javascript
          start_session({
            platform: 'electron',
            browserVersion: '33.2.1',
            capabilities: {
              'wdio:electronServiceOptions': {
                appBinaryPath: '/path/to/MyApp.app/Contents/MacOS/MyApp',
                appArgs: ['--disable-gpu']
              }
            }
          })
          
          // Privileged: this code runs in the Electron main process, not the renderer.
          execute_electron_script({ script: 'return electron.app.getName()' })

          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`. Electron mocks are not exposed yet.

          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:

          javascript
          start_session({
            platform: 'electron',
            electronDeeplinkScheme: 'myapp',
            capabilities: {
              'wdio:electronServiceOptions': {
                appBinaryPath: '/path/to/MyApp.app/Contents/MacOS/MyApp'
              }
            }
          })
          trigger_electron_deeplink({ url: 'myapp://open/item' })

          Attach to a running Chrome instance:

          code
          // First, launch Chrome with remote debugging enabled:
          //
          //   macOS (must quit Chrome first — open -a ignores args if Chrome is already running):
          //     pkill -x "Google Chrome" && sleep 1
          //     /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
          //       --remote-debugging-port=9222 \
          //       --user-data-dir=/tmp/chrome-debug &
          //
          //   Linux:
          //     google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug &
          //
          //   Verify it's ready: curl http://localhost:9222/json/version
          start_session({attach: true})
          start_session({attach: true, port: 9333})
          start_session({attach: true, port: 9222, navigationUrl: 'https://app.example.com'})

          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.

          javascript
          // Defaults to http://127.0.0.1:4445/ and browserName: 'chrome'
          start_session({provider: 'external', platform: 'browser'})
          
          // Custom WebDriver endpoint and capabilities
          start_session({
              provider: 'external',
              platform: 'browser',
              webdriverConfig: {
                  protocol: 'http',
                  hostname: '127.0.0.1',
                  port: 4445,
                  path: '/'
              },
              capabilities: {
                  browserName: 'tauri'
              }
          })

          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.

          javascript
          // Existing BrowserStack App Automate session
          attach_session({
              provider: 'browserstack',
              platform: 'ios',
              sessionId: 'existing-browserstack-session-id',
              capabilities: {
                  'appium:deviceName': 'iPhone 15',
                  'appium:automationName': 'XCUITest'
              }
          })
          
          // Existing session on a local Appium server
          attach_session({
              provider: 'local',
              platform: 'android',
              sessionId: 'existing-appium-session-id',
              appiumConfig: {
                  protocol: 'http',
                  host: '127.0.0.1',
                  port: 4723,
                  path: '/'
              }
          })
          
          // Existing mobile session on a custom W3C WebDriver endpoint
          attach_session({
              provider: 'external',
              platform: 'ios',
              sessionId: 'existing-grid-session-id',
              webdriverConfig: {
                  protocol: 'https',
                  hostname: 'grid.example.com',
                  port: 443,
                  path: '/wd/hub'
              }
          })

          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):

          code
          // Device emulation (requires BiDi session)
          start_session({capabilities: {webSocketUrl: true}})
          emulate_device()                         // list available presets
          emulate_device({device: 'iPhone 15'})    // activate emulation
          emulate_device({device: 'Pixel 7'})      // switch device
          emulate_device({device: 'reset'})        // restore desktop defaults

          Web extensions (requires BiDi session):

          javascript
          start_session({platform: 'browser', browser: 'chrome', capabilities: {webSocketUrl: true}})
          
          open_web_extension({
              extensionData: {type: 'path', path: '/path/to/unpacked-extension'},
              path: 'options.html'
          })
          
          // Drive the extension UI with the normal page tools.
          get_elements()
          click_element({selector: '#save'})
          
          // For remote/cloud sessions, send a packaged extension archive as base64.
          open_web_extension({
              extensionData: {type: 'base64', value: ''},
              path: 'options.html'
          })

          Mobile App Automation

          Testing an iOS app on simulator:

          code
          Test my iOS app located at /path/to/MyApp.app on iPhone 15 Pro simulator:
          1. Start the app session
          2. Tap the login button
          3. Enter "testuser" in the username field
          4. Take a screenshot of the home screen
          5. Close the session

          Preserving app state between sessions:

          code
          Test my Android app without resetting data:
          1. Start app session with noReset: true and fullReset: false
          2. App launches with existing login state and user data preserved
          3. Run test scenarios
          4. Close session (app remains installed with data intact)

          Testing an iOS app on real device:

          code
          Test my iOS app on my physical iPhone:
          1. Start app session with:
             - platform: iOS
             - appPath: /path/to/MyApp.ipa
             - deviceName: My iPhone
             - udid: 00008030-001234567890ABCD (your device's UDID)
             - platformVersion: 17.0
          2. Run your test scenario
          3. Close the session

          Testing an Android app:

          code
          Test my Android app /path/to/app.apk on the Pixel_6_API_34 emulator:
          1. Start the app with auto-grant permissions
          2. Get visible elements (use inViewportOnly: false to see all elements)
          3. Swipe up to scroll
          4. Tap on the "Settings" button using text matching
          5. Verify the settings screen is displayed

          Advanced element detection:

          code
          Test my app and debug layout issues:
          1. Start the app session
          2. Get visible elements with includeContainers: true to see the layout hierarchy
          3. Analyze ViewGroup, FrameLayout, and ScrollView containers
          4. Use inViewportOnly: false to find off-screen elements that need scrolling

          Hybrid app testing (switching contexts):

          code
          Test my hybrid app:
          1. Start the Android app session
          2. Tap "Open Web" button in native context
          3. List available contexts
          4. Switch to WEBVIEW context
          5. Click the login button using CSS selector
          6. Switch back to NATIVE_APP context
          7. Verify we're back on the home screen

          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:

          noResetfullResetBehavior
          `true``false`Preserve state: App stays installed, data preserved
          `false``false`Clear app data but keep app installed (default)
          `false``true`Full reset: Uninstall and reinstall app (clean slate)

          Example with state preservation:

          javascript
          // Preserve login state between test runs
          start_session({
              platform: 'android',
              appPath: '/path/to/app.apk',
              deviceName: 'emulator-5554',
              noReset: true,         // Don't reset app state
              fullReset: false,      // Don't uninstall
              autoGrantPermissions: true,
              capabilities: {
                  'appium:chromedriverExecutable': '/path/to/chromedriver',
                  'appium:autoWebview': true
              }
          })
          // App launches with existing user data, login tokens, preferences intact

          Detach from Sessions:

          The `close_session` tool supports a `detach` parameter that disconnects from the session without terminating it on the

          Appium server:

          javascript
          // Detach without killing the session
          close_session({detach: true})
          
          // Explicit session termination (closes the app and removes session)
          close_session({detach: false})

          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
            • 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: Single active session (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 active session
            • `wdio://session/current/code` — generated runnable WebdriverIO JS for the active 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.

            code
            Timeline (monotonic ms):
            
              prev.endTime ← frame timestamp stamped here
                    │
                    │   [screenshot captured here — shows settled state after prev action]
                    │
              curr.startTime
                    │
                    │   [action executes]
                    │
              curr.endTime ← next frame will be stamped here

            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:

            Session TypeLog SourcesContents
            Browser`getLogs('browser')`Console output + uncaught JS exceptions
            Android`getLogs('logcat')`System logs, crash dumps, fatal exceptions
            iOS`getLogs('crashlog')` + `getLogs('syslog')`Crash/panic reports + system diagnostics

            > 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!

            Frequently asked questions

            What is mcp?

            mcp is A Model Context Protocol (MCP) server that enables AI assistants to interact with web browsers and mobile applications using WebDriverIO. Automate Chrome browsers, iOS apps, and Android apps—all through a unified interface.

            How do I install mcp?

            Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.

            Is mcp open source?

            Yes — it is hosted on GitHub at https://github.com/webdriverio/mcp and has 37 stars.

            Related MCP tools

            Run your own MCP server? See who uses it and what to fix.

            Measure it with TrackMCP