attach_session
Attaches to an existing remote WebDriver or Appium session by ID without creating a new session. Only one session can be active at a time. The externally managed session is detached, not terminated, by default on close.
Drive real browsers and real mobile apps from an agent — one session at a time, with the page's elements and accessibility tree handed back as selectors.
LABSCO SUMMARY
The element and accessibility-tree tools are what make this workable for a model: it gets selectors and bounding boxes instead of guessing coordinates from a screenshot. The detail worth planning around is the single active session — this drives one target at a time, and attaching to an existing session by id is how you pick up something already running rather than starting over.
WebdriverIO's own automation server covering Chrome, Firefox, Edge and Safari plus iOS and Android through Appium, locally or on a cloud device provider.
Runs from npm as @wdio/mcp over stdio. Browser automation needs the browser installed. Mobile work needs Appium — installed globally and running before a session starts — plus the platform SDK, or credentials for a cloud provider when the device lives there. Only one session is active at a time: starting another replaces it.
One command — npx -y @wdio/mcp@latest
USE CASES
Start the session, navigate, list the interactable elements, then click and type your way through.
Start a device session through Appium, switch between native and webview contexts, and use taps and swipes.
Navigate to the domain, set the session cookie, then continue the flow.
Emulate the device viewport and user agent, or override the geolocation, then reload.
Read the accessibility tree with roles and names, and take a screenshot to compare.
Upload the .apk or .ipa to the provider, confirm it is listed, and start the session against it.
Counted once with only startup setup. Other versions, modes or optional credentials may expose a different list.
attach_sessionAttaches to an existing remote WebDriver or Appium session by ID without creating a new session. Only one session can be active at a time. The externally managed session is detached, not terminated, by default on close.
click_elementWaits for an element, scrolls it into view, and fires element.click(). May trigger navigation, form submission, or modals. Browser sessions only — on iOS element.click() is silently ignored; use tap_element instead. Default timeout: 3000ms.
close_sessionCloses the current session or detaches without terminating. Detach preserves app state on the Appium server — sessions with noReset: true auto-detach by default. Closing a browser attach session terminates chromedriver but the Chrome process spawned by launch_chrome remains running.
delete_cookiesDeletes all cookies or a single cookie by name from the current browser session. Irreversible — deleted cookies cannot be recovered.
drag_and_dropDrags an element to another element or to relative x/y offsets. x and y are offsets from the source element, not absolute screen coordinates (unlike tap_element). Provide targetSelector OR both x and y. Mobile-only.
emulate_deviceEmulates a mobile or tablet device in the current browser session by setting viewport, DPR, user-agent, and touch events. Requires a BiDi-enabled session (start_session with capabilities: { webSocketUrl: true }). Omit device to list available presets. Pass "reset" to restore desktop defaults. Changes persist for all subsequent tool calls until reset or session close. Browser-only.
execute_scriptExecutes arbitrary JavaScript in browser page context or Appium mobile: commands. Can read/modify DOM, trigger events, terminate apps, or run Android shell commands — use only when no dedicated tool covers the action. Browser: pass JS in script, use 'return' for values, string args matching selectors auto-resolve to elements. Mobile: use 'mobile: <command>' syntax in script with args array (e.g. "mobile: pressKey", "mobile: activateApp"). Prefer click_element/set_value/get_elements for standard interactions.
get_accessibility_treeReturns the page accessibility tree with roles, names, and selectors. Browser-only. Supports filtering by ARIA roles and pagination via limit/offset.
get_app_stateReturns the current state of a mobile app: not installed, not running, background, or foreground. Mobile-only.
get_contextsReturns available automation contexts and the currently active one. Use before switch_context to discover NATIVE_APP and WEBVIEW_* targets. Mobile-only.
get_cookiesReturns all cookies for the current session, or a single cookie by name. Use to verify auth state, session tokens, or feature flags after login flows.
get_elementsReturns interactable elements on the current page with selectors, text, and bounding boxes. Supports filtering by element type, viewport visibility, and pagination. Use when the wdio://session/current/elements resource does not return desired elements.
get_screenshotTakes a screenshot of the current page or screen and returns a base64-encoded image, resized and compressed for model context limits.
get_tabsLists all browser tabs with handle, title, URL, and which is active. Use before switch_tab to find the target handle or index. Browser-only.
hide_keyboardDismisses the on-screen keyboard on mobile. Call after text entry when the keyboard obscures elements. No-op if already hidden. Mobile-only.
launch_chromeLaunches Chrome with remote debugging enabled. Wipes and recreates a temporary profile directory on each call. Mode "newInstance" (default) runs alongside existing Chrome; "freshSession" starts with an empty profile. Set copyProfileFiles to copy cookies/logins from your Default profile — changes do not sync back. After launch, call start_session with attach: true to connect. Spawns a detached Chrome process that persists if the server exits.
list_appsList apps uploaded to a cloud provider (BrowserStack App Automate, Sauce Labs App Storage, TestMu Real Device Cloud, TestingBot Storage, or Digital.ai Applications). Reads provider-specific credentials from environment.
navigateLoads a URL in the current tab and waits for the page load event. Resets page state — DOM, JS runtime, timers, and frame context are destroyed. Use instead of clicking links when the target URL is known.
open_web_extensionInstalls a web extension through WebDriver BiDi and opens one of its extension pages so existing MCP tools can inspect and drive its UI. Requires a BiDi-enabled browser session. Use base64 for cloud/remote sessions where the browser driver cannot read the MCP server filesystem.
rotate_deviceRotates a mobile device to portrait or landscape orientation. Waits for the OS rotation animation to complete. Use to test orientation-dependent layouts. Mobile-only; no effect in browser sessions.
scrollScrolls the page vertically by a pixel amount. Browser-only — for mobile scrolling use swipe. Only supports up/down; no horizontal scrolling.
set_cookieSets a browser cookie on the active session. The browser must already be on the target domain — cookies cannot be set cross-domain. Use to inject session tokens or feature flags without login flows.
set_geolocationOverrides GPS coordinates for the session. Affects navigator.geolocation in browsers and location services on mobile. Location permissions must already be granted to the app.
set_valueClears an input or textarea then types the given text character by character. Always replaces existing content — clearValue() runs first. Triggers input, change, and key events which may fire validation or autocomplete. Scrolls into view by default.
start_sessionStarts a new browser or mobile automation session. Only one active session at a time — starting another session closes or detaches from the existing session first. Use attach: true to connect to a running Chrome via CDP.
swipePerforms a full-screen swipe gesture. Direction is content movement — "up" scrolls content upward (finger moves down). For browser scrolling use scroll; for dragging a specific element use drag_and_drop. No error if content cannot scroll further. Mobile-only.
switch_contextSwitches between native and webview automation contexts in a hybrid mobile app. In NATIVE_APP context, use accessibility IDs; in WEBVIEW_* context, use CSS/XPath. Changes persist for all subsequent commands. Accepts context name or 1-based index. Use get_contexts to discover available targets. Mobile-only.
switch_frameSwitches WebDriver frame context into an iframe by CSS/XPath selector, or back to top-level if selector is omitted. Changes persist — all subsequent click_element, set_value, get_elements calls operate within the switched frame until you switch back. Waits up to 5s for the iframe. Browser-only.
switch_tabFocuses a browser tab by window handle or 0-based index. All subsequent tool calls operate on the active tab. Provide handle OR index — use get_tabs to find them. Browser-only; use switch_context for mobile webviews.
tap_elementTaps a matched element via element.tap() or at absolute screen coordinates (x, y). No scroll-into-view or wait — element must already be visible on screen. Use instead of click_element on iOS where element.click() is ignored. Provide selector OR both x and y. Mobile-only.
upload_appUpload a local .apk or .ipa to a cloud provider (BrowserStack, Sauce Labs, TestMu, TestingBot, or Digital.ai). Returns the app URL for use in start_session.
BEFORE YOU INSTALL
31 tools when we connected. upload_app and list_apps require a cloud provider parameter. The wdio:// entries are resources, not tools.
npx on PATH
CONTEXT COST
Rated by the size of the 31 tool definitions that ride along on every turn, not by how many there are
INSTALLATION
npx -y @wdio/mcp@latest{
"mcpServers": {
"wdio-mcp": {
"command": "npx",
"args": [
"-y",
"@wdio/mcp@latest"
]
}
}
}claude mcp add wdio-mcp -- npx -y @wdio/mcp@latest{
"mcpServers": {
"wdio-mcp": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@wdio/mcp@latest"
]
}
}
}[mcp_servers.wdio-mcp]
command = "npx"
args = ["@wdio/mcp@latest"]~/.codex/config.toml
extensions:
wdio-mcp:
name: WebDriverIO MCP
cmd: npx
args: [ -y, "@wdio/mcp@latest" ]
enabled: true
type: stdio~/.config/goose/config.yaml
{
"context_servers": {
"wdio-mcp": {
"source": "custom",
"command": "npx",
"args": [
"-y",
"@wdio/mcp@latest"
]
}
}
}code --add-mcp '{"name":"wdio-mcp","command":"npx","args":["-y","@wdio/mcp@latest"]}'npm install -g @wdio/mcpThen use wdio-mcp as the command:
wdio-mcp{
"mcpServers": {
"wdio-mcp": {
"command": "wdio-mcp"
}
}
}For clients that cannot launch subprocesses, enable HTTP transport:
npx @wdio/mcp --http --port 3000Then point your MCP client at http://localhost:3000/mcp.
{
"mcpServers": {
"wdio-mcp": {
"command": "npx",
"args": [
"-y",
"@wdio/mcp@latest"
],
"env": {
"BROWSERSTACK_USERNAME": "<YOUR_BROWSERSTACK_USERNAME>",
"BROWSERSTACK_ACCESS_KEY": "<YOUR_BROWSERSTACK_ACCESS_KEY>"
}
}
}
}{
"mcpServers": {
"wdio-mcp": {
"command": "npx",
"args": [
"-y",
"@wdio/mcp@latest"
],
"env": {
"SAUCE_USERNAME": "<YOUR_SAUCE_USERNAME>",
"SAUCE_ACCESS_KEY": "<YOUR_SAUCE_ACCESS_KEY>"
}
}
}
}