MCP server that lets AI agents drive remote browsers and real iOS / Android devices on TestingBot's grid. Real Safari on macOS, Edge on Windows, Firefox, Chrome on any supported OS, plus physical Appium devices — all from one install, no local browser, simulator, or Appium server required. Every session includes a live-view URL so a human can watch the agent drive in real time.
Status: alpha (
0.1.0). One install gets you both browser and mobile automation. Mobile is powered by the officialappium-mcp, spawned as a child process and proxied through this server — you never need to install or configure it yourself.
| Tool | What it does |
|---|---|
tb_openBrowser |
Start a remote browser session on TestingBot via WebDriver. Returns a sessionId and a liveViewUrl. |
tb_navigate |
Navigate to a URL. |
tb_snapshot |
Structured page summary (title, headings, actionable elements, body text). |
tb_screenshot |
PNG of the current page as a base64 image. |
tb_click |
Click by CSS / xpath / id / name / tag / class / linkText / partialLinkText. |
tb_type |
Type into an input (with optional clear / press-Enter). Same by strategies. |
tb_getText |
Read the visible text of one element (cheap probe). |
tb_getAttribute |
Read an attribute (href, value, aria-label, data-*, …). |
tb_executeScript |
Run a JS snippet in the page. Escape hatch for anything not covered above. |
tb_pressKey |
Press a key/chord on the focused element (Enter, Tab, Control+A, Meta+Shift+Z, …). |
tb_closeBrowser |
Close a browser session. |
tb_listSessions |
List active browser sessions managed here. |
Every active browser session has a built-in idle reaper (4 min default) and a session cap (5 default). Both are configurable via AutomationOptions.
The full appium-mcp tool surface — ~30 appium_* tools including appium_session_management, appium_gesture, appium_set_value, appium_screenshot, appium_find_element, appium_app_lifecycle, appium_perform_actions, and more — is proxied straight through. The agent calls them by name; we forward each call to a bundled appium-mcp child process over MCP.
We pre-inject two things for every session-create call:
remoteServerUrl= the credentialed TestingBot Appium hub URL (https://<key>:<secret>@hub.testingbot.com/wd/hub)REMOTE_SERVER_URL_ALLOW_REGEX= a strict regex limiting connections to TestingBot's hub only
So the agent only needs to call appium_session_management(action: "create", capabilities: { platformName: "iOS", ... }). No URL, no credentials, no setup.
Mobile tools won't appear if the bundled
appium-mcppackage fails to start (e.g. its native deps can't be loaded). Browser tools work independently.
One MCP, two surfaces:
npx @testingbot/automation-mcpExample Claude Desktop config:
That's it. No second MCP, no extra env vars.
Heads-up on install size. Bundling
appium-mcpbrings in its full mobile-driver stack — XCUITest, UiAutomator2, langchain, and a few hundred MB of dependencies. Firstnpm installwill take longer than a typical MCP server. The cost is paid once.
If you're already running @testingbot/mcp-server for resource management, the automation tools compose into the same server instance — no second MCP entry, no extra process. @testingbot/mcp-server does this automatically; the example below is for any other host MCP:
import { addAutomationTools } from "@testingbot/automation-mcp";
const handle = await addAutomationTools(server, testingBotApi, config, {
idleTimeoutMs: 4 * 60 * 1000, // optional, default
maxSessions: 5, // optional, default
});
// On shutdown, close any live sessions AND the bundled appium-mcp child:
await handle.shutdown();addAutomationTools is async because it spawns and discovers tools from the bundled appium-mcp child. The host MUST await it before serving its first tools/list response, otherwise mobile tools won't be visible to the client.
The library entry expects the same server.tool(name, desc, schema, handler) duck-type used by @testingbot/mcp-server's other tool families.
Every tb_openBrowser response includes:
- **Live view** (open in a browser tab to watch): https://testingbot.com/tests/<sessionId>/live?auth=<hash>
Click that URL → you see the AI driving in real time. Works for any TestingBot session — Safari, Chrome, iOS, Android, etc. — same URL pattern: https://testingbot.com/tests/<sessionId>/live?auth=<md5(key:secret:sessionId)>.
For mobile sessions, ask the agent to surface the live-view URL after appium_session_management returns the sessionId.
Browser:
Open Safari 17 on macOS Sonoma on TestingBot. Go to news.ycombinator.com, get an ARIA snapshot, summarize the top 5 stories, then close the session.
Mobile (iOS simulator app):
Open an iPhone 15 Pro with iOS 17 on TestingBot, install the app at
tb://abc123, tap the "Sign in" button, take a screenshot, then close the session.
Mobile (Android web):
Open Chrome on a Pixel 8 with Android 14 on TestingBot, browse to wikipedia.org, search for "Appium", take a screenshot, then close.
Behind the scenes for mobile, the agent:
- Calls
appium_session_management({ action: "create", capabilities: { platformName: "iOS", "appium:deviceName": "iPhone 15 Pro", ... } }). We inject the credentialedremoteServerUrlautomatically. - Uses
appium_gesture/appium_set_value/appium_screenshot/appium_find_element/ etc. to drive the device. - Closes via
appium_session_management({ action: "delete", sessionId }).
- Sessions cost money. Every minute a remote browser or device is alive consumes TestingBot test minutes. The idle reaper closes browser sessions at 4 min by default; for mobile, ask the agent to be explicit about closing.
- TestingBot kills sessions at ~5 min idle. Our reaper closes browser sessions at 4 min so the close is clean instead of disconnect-on-next-call.
- WebDriver throughout. Browser sessions use the same protocol TestingBot is native to, so Chrome, Edge, Safari, and Firefox all work without a per-browser code path. Mobile uses WebDriver too via Appium.
- Live view URL leaks an MD5 hash. Don't paste it in public places — anyone with the URL can watch your session.
- Mobile traffic is locked to TestingBot's hub. We set
REMOTE_SERVER_URL_ALLOW_REGEXin the appium-mcp child's env to a strict regex; the child will refuse anyremoteServerUrlpointing elsewhere, even if the agent supplies one.
npm ci
npm run dev # watch mode
npm test # vitest
npm run lint
npm run build # lint + format:check + test + tscMIT — see LICENSE.
{ "mcpServers": { "testingbot-automation": { "command": "npx", "args": ["-y", "@testingbot/automation-mcp"], "env": { "TESTINGBOT_KEY": "your-api-key", "TESTINGBOT_SECRET": "your-api-secret" } } } }