Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ settings.local.json
*.tgz

.mcp.json
.env
.env

.lambdatest
*.apk
32 changes: 24 additions & 8 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ Context for Claude Code when working with this repository.
## Commands

```bash
npm run bundle # Build: clean + tsup + make executable + create .tgz
npm test # Run unit tests (vitest + happy-dom)
npm run dev # Development server (tsx, no build)
npm start # Run built server from lib/server.js
npm run bundle # Build: clean + tsup + make executable + create .tgz
npm run lint # ESLint + TypeScript type-checking (tsc --noEmit)
npm test # Run unit tests (vitest + happy-dom)
npm run dev # Development server (tsx --watch) — picks up code changes automatically; rebundle only needed for npm package changes
npm run dev:http # Dev server with HTTP transport (for browser-based MCP clients)
npm start # Run built server from lib/server.js
npm run start:http # Built server with HTTP transport (for browser-based MCP clients)
```

## Architecture
Expand All @@ -22,7 +25,10 @@ src/
│ ├── local-browser.provider.ts # Chrome/Firefox/Edge/Safari
│ ├── local-appium.provider.ts # iOS/Android via Appium
│ └── cloud/
│ └── browserstack.provider.ts # BrowserStack (browser + App Automate)
│ ├── browserstack.provider.ts # BrowserStack (browser + App Automate)
│ ├── saucelabs.provider.ts # Sauce Labs (browser + App Storage)
│ └── testmu.provider.ts # TestMu / LambdaTest (browser + mobile)
├── trace/ # Playwright-compatible trace recording (recorder.ts, tool-mapping.ts, zip-writer.ts)
├── tools/ # One file per MCP tool (see Tool Pattern below)
├── resources/ # One file per MCP resource (see Recording below)
├── recording/ # step-recorder.ts (withRecording HOF) + code-generator.ts
Expand All @@ -45,7 +51,7 @@ export interface SessionMetadata {
type: 'browser' | 'ios' | 'android';
capabilities: Record<string, unknown>;
isAttached: boolean;
provider?: 'local' | 'browserstack'; // set at session start; used by lifecycle to call provider hooks
provider?: 'local' | 'browserstack' | 'saucelabs' | 'testmu'; // set at session start; used by lifecycle to call provider hooks
tunnelHandle?: unknown; // opaque handle returned by provider.startTunnel(), passed back to onSessionClose()
}
```
Expand Down Expand Up @@ -139,6 +145,10 @@ MCP resources expose live session data — all at fixed URIs discoverable via Li

## Gotchas

### Dev Reload vs Reconnect

`npm run dev` runs `tsx --watch` — code changes reload in-process. Only tool/resource **schema changes** (Zod definitions, new tools, parameter additions) require an MCP client reconnect to re-advertise capabilities. No need to rebundle or restart the dev server for implementation-only changes.

### Console Output

All console methods redirect to stderr via `console.error`. Chrome writes to stdout which corrupts MCP stdio protocol.
Expand Down Expand Up @@ -207,11 +217,17 @@ catch (e) {
|----------|-------------|
| `BROWSERSTACK_USERNAME` | BrowserStack sessions + tools |
| `BROWSERSTACK_ACCESS_KEY` | BrowserStack sessions + tools |
| `SAUCE_USERNAME` | Sauce Labs sessions + App Storage tools |
| `SAUCE_ACCESS_KEY` | Sauce Labs sessions + App Storage tools |
| `TESTMU_USERNAME` | TestMu / LambdaTest sessions + tools |
| `TESTMU_ACCESS_KEY` | TestMu / LambdaTest sessions + tools |

## Planned Improvements

See `docs/architecture/` for proposals:

- `session-configuration-proposal.md` — Cloud provider pattern (SauceLabs etc.) — BrowserStack already implemented; `providers/registry.ts` + `providers/cloud/` is the extension point
- `session-configuration-proposal.md` — Cloud provider pattern — BrowserStack, SauceLabs, and TestMu implemented; `providers/registry.ts` + `providers/cloud/` is the extension point for new providers
- `multi-session-proposal.md` — Parallel sessions for sub-agent coordination
- `interaction-sequencing-proposal.md` — Sequencing model for tool interactions
- `interaction-sequencing-proposal.md` — Sequencing model for tool interactions
- `trace-recording-and-replay.md` — Playwright-compatible trace recording (implemented in `src/trace/`)
- `trace-extraction-proposal.md` — Trace data extraction and analysis
198 changes: 159 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,31 +263,30 @@ appium
# Server runs at http://127.0.0.1:4723 by default
```

## BrowserStack
## Cloud Providers

Run browser and mobile app tests on [BrowserStack](https://www.browserstack.com/) real devices and browsers without any
local setup.
Run browser and mobile app tests on cloud real devices and browsers without any local setup. Currently supports
[BrowserStack](https://www.browserstack.com/), [Sauce Labs](https://saucelabs.com/), and
[LambdaTest](https://www.lambdatest.com/).

### Prerequisites

Set your credentials as environment variables:
Set your provider credentials as environment variables or in your MCP client config:

<details>
<summary>BrowserStack</summary>

```bash
export BROWSERSTACK_USERNAME=your_username
export BROWSERSTACK_ACCESS_KEY=your_access_key
```

Or add them to your MCP client config:

```json
{
"mcpServers": {
"wdio-mcp": {
"command": "npx",
"args": [
"-y",
"@wdio/mcp@latest"
],
"args": ["-y", "@wdio/mcp@latest"],
"env": {
"BROWSERSTACK_USERNAME": "your_username",
"BROWSERSTACK_ACCESS_KEY": "your_access_key"
Expand All @@ -297,11 +296,72 @@ Or add them to your MCP client config:
}
```

</details>

<details>
<summary>Sauce Labs</summary>

```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`).

</details>

<details>
<summary>LambdaTest (TestMu)</summary>

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

</details>

### Browser Sessions

Run a browser on a specific OS/version combination:

```javascript
// BrowserStack
start_session({
provider: 'browserstack',
platform: 'browser',
Expand All @@ -315,69 +375,127 @@ start_session({
session: 'Login flow'
}
})

// Sauce Labs
start_session({
provider: 'saucelabs',
platform: 'browser',
browser: 'chrome',
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: 'Linux', // default: Linux
osVersion: '11',
reporting: {
project: 'My Project',
build: 'v1.2.0',
session: 'Login flow'
}
})
```

### Mobile App Sessions

Test on BrowserStack real devices. First upload your app (or use an existing `bs://` URL):
Test on cloud real devices. First upload your app (or use an existing app URL):

```javascript
// Upload a local .apk or .ipa (returns a bs:// URL)
upload_app({path: '/path/to/app.apk'})
// 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' })

// Start a session with the returned URL
// LambdaTest: returns lt:// URL
upload_app({ provider: 'testmu', path: '/path/to/app.apk' })

// Start a session
start_session({
provider: 'browserstack',
platform: 'android', // android | ios
app: 'bs://abc123...', // bs:// URL or custom_id from upload
platform: 'android',
app: 'bs://abc123...',
deviceName: 'Samsung Galaxy S23',
platformVersion: '13.0',
reporting: {
project: 'My Project',
build: 'v1.2.0',
session: 'Checkout flow'
}
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'
})
```

Use `list_apps` to see previously uploaded apps:

```javascript
list_apps() // own uploads, sorted by date
list_apps({sortBy: 'app_name'})
list_apps({organizationWide: true}) // all uploads in your org
list_apps({ provider: 'browserstack' })
list_apps({ provider: 'saucelabs', sortBy: 'app_name' })
list_apps({ provider: 'testmu' })
list_apps({ provider: 'browserstack', organizationWide: true })
```

### BrowserStack Local
### Local Tunnel

To test against URLs that are only accessible on your local machine or internal network, enable the BrowserStack 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: 'browserstack',
provider: 'saucelabs',
platform: 'browser',
browser: 'chrome',
browserstackLocal: true // starts tunnel automatically
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**: Sauce Connect and TestMu Tunnel require their respective binaries. The `wdio://saucelabs/local-binary` and `wdio://testmu/local-binary` resources provide platform-specific download URLs and setup instructions.

### Reporting Labels

All session types support `reporting` labels that appear in the BrowserStack Automate dashboard:
All session types support `reporting` labels that appear in the provider dashboard:

| Field | Description |
|---------------------|-----------------------------------------|
| `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 |

### BrowserStack Tools
### Cloud Provider Tools

| Tool | Description |
|--------------|-------------------------------------------------------------------------------|
| `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 |

| Tool | Description |
|--------------|------------------------------------------------------------------------|
| `upload_app` | Upload a local `.apk` or `.ipa` to BrowserStack; returns a `bs://` URL |
| `list_apps` | List apps previously uploaded to your BrowserStack account |
Both tools require a `provider` parameter (`'browserstack'`, `'saucelabs'`, or `'testmu'`).

## Features

Expand Down Expand Up @@ -487,6 +605,8 @@ All session types support `reporting` labels that appear in the BrowserStack Aut
| `wdio://session/current/geolocation` | Device geolocation |
| `wdio://session/current/capabilities` | Resolved WebDriver capabilities for the active session |
| `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 |

## Usage Examples

Expand Down Expand Up @@ -794,8 +914,8 @@ MCP resources — no extra tool calls needed:
- `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 BrowserStack sessions it includes the full try/catch/finally
with automatic session result marking.
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

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"test": "vitest run"
},
"dependencies": {
"@lambdatest/node-tunnel": "^4.0.11",
"@modelcontextprotocol/sdk": "^1.27.1",
"@toon-format/toon": "^2.1.0",
"@wdio/protocols": "^9.27.0",
Expand Down
Loading
Loading