@browserstack/mcp-server 1.2.1 → 1.2.2-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,6 +35,10 @@ Manage, execute, debug tests, and even fix code using plain English prompts.
35
35
  #### Reduced context switching:
36
36
  Stay in flow—keep all project context in one place and trigger actions directly from your IDE or LLM.
37
37
 
38
+ ## ⚡️ One Click MCP Setup
39
+
40
+ [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](http://mcp.browserstack.com/one-click-setup?client=vscode)   [![Install in Cursor](https://img.shields.io/badge/Cursor-Install_Server-24bfa5?style=flat-square&color=000000&logo=visualstudiocode&logoColor=white)](http://mcp.browserstack.com/one-click-setup?client=cursor)
41
+
38
42
  ## 💡 Usage Examples
39
43
 
40
44
  ### 📱 Manual App Testing
@@ -138,6 +142,13 @@ Generate test cases from PRDs, convert manual tests to low-code automation, and
138
142
 
139
143
  ## 🛠️ Installation
140
144
 
145
+ ### **One Click MCP Setup**
146
+
147
+ [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](http://mcp.browserstack.com/one-click-setup?client=vscode)   [![Install in Cursor](https://img.shields.io/badge/Cursor-Install_Server-24bfa5?style=flat-square&color=000000&logo=visualstudiocode&logoColor=white)](http://mcp.browserstack.com/one-click-setup?client=cursor)
148
+
149
+
150
+ ### **Alternate ways to Setup MCP server**
151
+
141
152
  1. **Create a BrowserStack Account**
142
153
 
143
154
  - Sign up for [BrowserStack](https://www.browserstack.com/users/sign_up) if you don't have an account already.
@@ -148,9 +159,21 @@ Generate test cases from PRDs, convert manual tests to low-code automation, and
148
159
  - Once you have an account (and purchased appropriate plan), note down your `username` and `access_key` from [Account Settings](https://www.browserstack.com/accounts/profile/details).
149
160
 
150
161
  2. Ensure you are using Node version >= `18.0`. Check your node version using `node --version`. Recommended version: `v22.15.0` (LTS)
162
+
151
163
  3. **Install the MCP Server**
152
164
 
153
165
  - VSCode (Copilot - Agent Mode): `.vscode/mcp.json`:
166
+
167
+ - Locate or Create the Configuration File:
168
+ In the root directory of your project, look for a folder named .vscode. This folder is usually hidden so you will need to find it as mentioned in the expand.
169
+
170
+ - If this folder doesn't exist, create it.
171
+
172
+ - Inside the .vscode folder, create a new file named mcp.json
173
+
174
+ - Add the Configuration: Open the mcp.json file and then add the following JSON content.
175
+
176
+ - Replace the username and <access_key> with your BrowserStack credentials.
154
177
 
155
178
  ```json
156
179
  {
@@ -170,6 +193,30 @@ Generate test cases from PRDs, convert manual tests to low-code automation, and
170
193
  - In VSCode, make sure to click on `Start` button in the MCP Server to start the server.
171
194
  ![Start MCP Server](assets/vscode_install.png)
172
195
 
196
+
197
+ #### ** Alternate way to setup MCP on VSCode Copilot
198
+
199
+ 1.Click on the gear icon to Select Tools
200
+ <div align="center">
201
+ <img src="assets/select_tools.png" alt="Select Tools" height="100">
202
+ </div>
203
+ 2. A tool menu would appear at the top-centre, scroll down on the menu at the top and then Click on Add MCP Server
204
+ <div align="center">
205
+ <img src="assets/add_mcp_server.png" alt="Add MCP Server" height="100">
206
+ </div>
207
+ 3. Select NPM package option (Install fron an NPM package) - 3rd in the list
208
+ <div align="center">
209
+ <img src="assets/select_npm_package.png" alt="Select NPM Package" height="100">
210
+ </div>
211
+ 4. Enter NPM Package Name (@browserstack/mcp-server)
212
+ <div align="center">
213
+ <img src="assets/enter_npm_package.png" alt="Enter NPM Package" height="100">
214
+ </div>
215
+ 5. Enter browserstack user name and access key
216
+
217
+
218
+
219
+
173
220
  * For Cursor: `.cursor/mcp.json`:
174
221
 
175
222
  ```json
@@ -231,6 +278,186 @@ To install BrowserStack Test Platform Server for Claude Desktop automatically vi
231
278
  npx -y @smithery/cli install @browserstack/mcp-server --client claude
232
279
  ```
233
280
 
281
+
282
+ ### 💡 List of BrowserStack MCP Tools
283
+
284
+ As of now we support 20 tools.
285
+
286
+
287
+ ---
288
+
289
+ ## 🧾 Test Management
290
+
291
+ 1. `createProjectOrFolder` — Create a Test Management project and/or folders to organize test cases. Returns with Folder ID, Project ID and Test Management Link to access the TM Project Dashboard.
292
+ **Prompt example**
293
+
294
+ ```text
295
+ Create a new Test Management project named 'Shopping App' with two folders - Login and Checkout
296
+ ```
297
+
298
+
299
+ 2. `createTestCase` — Add a manual test case under a specific project/folder (uses project identifier like PR-xxxxx and a folder ID).
300
+ **Prompt example**
301
+
302
+ ```text
303
+ Add a test case named 'Invalid Login Scenario' to the Login folder in the 'Shopping App' project with PR-53617, Folder ID: 117869
304
+ ```
305
+
306
+ 3. `listTestCases` — List test cases for a project (supports filters like priority, status, tags).
307
+ **Prompt example**
308
+
309
+ ```text
310
+ List all high-priority test cases in the 'Shopping App' project with project_identifier: PR-59457
311
+ ```
312
+
313
+ 4. `createTestRun` — Create a test run (suite) for selected test cases in a project.
314
+ **Prompt example**
315
+
316
+ ```text
317
+ Create a test run for the Login folder in the 'Shopping App' project and name it 'Release v1.0 Login Flow'
318
+ ```
319
+
320
+ 5. `listTestRuns` — List test runs for a project (filter by dates, assignee, state).
321
+ **Prompt example**
322
+
323
+ ```text
324
+ List all test runs from the 'Shopping App' project that were executed last week and are currently marked in-progress
325
+ ```
326
+
327
+ 6. `updateTestRun` — Partially update a test run (status, tags, notes, associated test cases).
328
+ **Prompt example**
329
+
330
+ ```text
331
+ Update test run ID 1043 in the 'Shopping App' project and mark it as complete with the note 'Regression cycle done'
332
+ ```
333
+
334
+ 7. `addTestResult` — Add a manual execution result (passed/failed/blocked/skipped) for a test case within a run.
335
+ **Prompt example**
336
+
337
+ ```text
338
+ Mark the test case 'Invalid Login Scenario' as passed in test run ID 1043 of the 'Shopping App' project
339
+ ```
340
+
341
+ 8. `createTestCasesFromFile` — Bulk-create test cases from an uploaded file (e.g., PDF).
342
+ **Prompt example**
343
+
344
+ ```text
345
+ Upload test cases from '/Users/xyz/testcases.pdf' to the 'Shopping App' project in Test Management
346
+ ```
347
+
348
+ ---
349
+
350
+ ## ⚙️ BrowserStack SDK Setup / Automate Test
351
+
352
+ 9. `setupBrowserStackAutomateTests` — Integrate BrowserStack SDK and run web tests on BrowserStack (optionally enable Percy).
353
+ **Prompt example**
354
+
355
+ ```text
356
+ Run my Selenium-JUnit5 tests written in Java on Chrome and Firefox. Enable Percy for visual testing.
357
+ ```
358
+
359
+ 10. `fetchAutomationScreenshots` — Fetch screenshots captured during a given Automate/App Automate session.
360
+ **Prompt example**
361
+
362
+ ```text
363
+ Get screenshots from Automate session ID abc123xyz for my desktop test run
364
+ ```
365
+
366
+ ---
367
+
368
+ ## 🔍 Observability
369
+
370
+ 11. `getFailureLogs` — Retrieve error logs for Automate/App Automate sessions (optionally by Build ID for App Automate).
371
+ **Prompt example**
372
+
373
+ ```text
374
+ Get the error logs from the session ID: 21a864032a7459f1e7634222249b316759d6827f, Build ID: dt7ung4wmjittzff8kksrjadjax9gzvbscoyf9qn of App Automate test session
375
+ ```
376
+
377
+ ---
378
+
379
+ ## 📱 App Live
380
+
381
+ 12. `runAppLiveSession` — Start a manual app testing session on a real device in the cloud.
382
+ **Prompt example**
383
+
384
+ ```text
385
+ Open my app on iPhone 15 Pro Max with iOS 17. App path is /Users/xyz/app.ipa
386
+ ```
387
+
388
+ ---
389
+
390
+ ## 💻 Live
391
+
392
+ 13. `runBrowserLiveSession` — Start a Live session for website testing on desktop or mobile browsers.
393
+ **Prompt example**
394
+
395
+ ```text
396
+ Open www.google.com on the latest version of Microsoft Edge on Windows 11
397
+ ```
398
+
399
+ ---
400
+
401
+ ## 📲 App Automate
402
+
403
+ 14. `takeAppScreenshot` — Launch the app on a specified device and captures a quick verification screenshot. This tool is just to verify whether your app has been launched.
404
+ **Prompt example**
405
+
406
+ ```text
407
+ Take a screenshot of my app on Google Pixel 6 with Android 14 while testing on App Automate. App file path: /Users/xyz/app-debug.apk
408
+ ```
409
+
410
+ 15. `runAppTestsOnBrowserStack` — Run automated mobile tests (Espresso/XCUITest, etc.) on real devices.
411
+ **Prompt example**
412
+
413
+ ```text
414
+ Run Espresso tests from /tests/checkout.zip on Galaxy S21 and Pixel 6 with Android 14. App path is /apps/beta-release.apk under project 'Checkout Flow'
415
+ ```
416
+
417
+ ---
418
+
419
+ ## ♿ Accessibility
420
+
421
+ 16. `accessibilityExpert` — Ask A11y Expert (WCAG 2.0/2.1/2.2, mobile/web usability, best practices).
422
+ **Prompt example**
423
+
424
+ ```text
425
+ What WCAG guidelines apply to form field error messages on mobile web?
426
+ ```
427
+
428
+ 17. `startAccessibilityScan` — Start a web accessibility scan and return the result link.
429
+ **Prompt example**
430
+
431
+ ```text
432
+ Run accessibility scan for "www.example.com"
433
+ ```
434
+
435
+ ---
436
+
437
+ ## 🤖 BrowserStack AI Agents
438
+
439
+ 18. `fetchSelfHealedSelectors` — Retrieve AI self-healed selectors to fix flaky tests due to DOM changes.
440
+ **Prompt example**
441
+
442
+ ```text
443
+ Fetch and fix flaky test selectors in Automate session ID session_9482 using MCP
444
+ ```
445
+
446
+ 19. `createLCASteps` — Generate Low Code Automation steps from a manual test case in Test Management.
447
+ **Prompt example**
448
+
449
+ ```text
450
+ Convert the manual test case 'Add to Cart' in the 'Shopping App' project into LCA steps
451
+ ```
452
+
453
+ 20. `uploadProductRequirementFile` — Upload a PRD/screenshot/PDF and get a file mapping ID (used with `createTestCasesFromFile`).
454
+ **Prompt example**
455
+
456
+ ```text
457
+ Upload PRD from /Users/xyz/Desktop/login-flow.pdf and use BrowserStack AI to generate test cases
458
+ ```
459
+
460
+
234
461
  ## 🤝 Recommended MCP Clients
235
462
 
236
463
  - We recommend using **Github Copilot or Cursor** for automated testing + debugging use cases.
package/dist/index.d.ts CHANGED
@@ -2,3 +2,4 @@
2
2
  import "dotenv/config";
3
3
  export { setLogger } from "./logger.js";
4
4
  export { BrowserStackMcpServer } from "./server-factory.js";
5
+ export { trackMCP } from "./lib/instrumentation.js";
package/dist/index.js CHANGED
@@ -35,3 +35,4 @@ process.on("exit", () => {
35
35
  });
36
36
  export { setLogger } from "./logger.js";
37
37
  export { BrowserStackMcpServer } from "./server-factory.js";
38
+ export { trackMCP } from "./lib/instrumentation.js";
@@ -7,3 +7,4 @@ export declare enum BrowserStackProducts {
7
7
  * Fetches and caches BrowserStack datasets (live + app_live + app_automate) with a shared TTL.
8
8
  */
9
9
  export declare function getDevicesAndBrowsers(type: BrowserStackProducts): Promise<any>;
10
+ export declare function shouldSendStartedEvent(): boolean;
@@ -2,9 +2,11 @@ import fs from "fs";
2
2
  import os from "os";
3
3
  import path from "path";
4
4
  import { apiClient } from "./apiClient.js";
5
+ import config from "../config.js";
5
6
  const CACHE_DIR = path.join(os.homedir(), ".browserstack", "combined_cache");
6
7
  const CACHE_FILE = path.join(CACHE_DIR, "data.json");
7
8
  const TTL_MS = 24 * 60 * 60 * 1000; // 1 day
9
+ const TTL_STARTED_MS = 3 * 60 * 60 * 1000; // 3 Hours
8
10
  export var BrowserStackProducts;
9
11
  (function (BrowserStackProducts) {
10
12
  BrowserStackProducts["LIVE"] = "live";
@@ -49,3 +51,29 @@ export async function getDevicesAndBrowsers(type) {
49
51
  fs.writeFileSync(CACHE_FILE, JSON.stringify(cache), "utf8");
50
52
  return cache[type];
51
53
  }
54
+ // Rate limiter for started event (3H)
55
+ export function shouldSendStartedEvent() {
56
+ try {
57
+ if (config && config.REMOTE_MCP) {
58
+ return false;
59
+ }
60
+ if (!fs.existsSync(CACHE_DIR)) {
61
+ fs.mkdirSync(CACHE_DIR, { recursive: true });
62
+ }
63
+ let cache = {};
64
+ if (fs.existsSync(CACHE_FILE)) {
65
+ const raw = fs.readFileSync(CACHE_FILE, "utf8");
66
+ cache = JSON.parse(raw || "{}");
67
+ const last = parseInt(cache.lastStartedEvent, 10);
68
+ if (!isNaN(last) && Date.now() - last < TTL_STARTED_MS) {
69
+ return false;
70
+ }
71
+ }
72
+ cache.lastStartedEvent = Date.now();
73
+ fs.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2), "utf8");
74
+ return true;
75
+ }
76
+ catch {
77
+ return true;
78
+ }
79
+ }
@@ -1,4 +1,5 @@
1
1
  import { trackMCP } from "./lib/instrumentation.js";
2
+ import { shouldSendStartedEvent } from "./lib/device-cache.js";
2
3
  export function setupOnInitialized(server, config) {
3
4
  const nodeVersion = process.versions.node;
4
5
  // Check for Node.js version
@@ -6,6 +7,8 @@ export function setupOnInitialized(server, config) {
6
7
  throw new Error("Node version is not supported. Please upgrade to 18.0.0 or later.");
7
8
  }
8
9
  server.server.oninitialized = () => {
9
- trackMCP("started", server.server.getClientVersion(), undefined, config);
10
+ if (shouldSendStartedEvent()) {
11
+ trackMCP("started", server.server.getClientVersion(), undefined, config);
12
+ }
10
13
  };
11
14
  }
@@ -8,7 +8,7 @@ import addBrowserLiveTools from "./tools/live.js";
8
8
  import addAccessibilityTools from "./tools/accessibility.js";
9
9
  import addTestManagementTools from "./tools/testmanagement.js";
10
10
  import addAppAutomationTools from "./tools/appautomate.js";
11
- import addFailureLogsTools from "./tools/getFailureLogs.js";
11
+ import addFailureLogsTools from "./tools/get-failure-logs.js";
12
12
  import addAutomateTools from "./tools/automate.js";
13
13
  import addSelfHealTools from "./tools/selfheal.js";
14
14
  import addAppLiveTools from "./tools/applive.js";
@@ -26,8 +26,9 @@ export declare function resolveVersion(versions: string[], requestedVersion: str
26
26
  export declare function validateArgs(args: {
27
27
  desiredPlatform: string;
28
28
  desiredPlatformVersion: string;
29
- appPath: string;
29
+ appPath?: string;
30
30
  desiredPhone: string;
31
+ browserstackAppUrl?: string;
31
32
  }): void;
32
33
  /**
33
34
  * Uploads an application file to AppAutomate and returns the app URL
@@ -49,21 +49,24 @@ export function resolveVersion(versions, requestedVersion) {
49
49
  * Checks for presence and correctness of platform, device, and file types.
50
50
  */
51
51
  export function validateArgs(args) {
52
- const { desiredPlatform, desiredPlatformVersion, appPath, desiredPhone } = args;
52
+ const { desiredPlatform, desiredPlatformVersion, appPath, desiredPhone, browserstackAppUrl, } = args;
53
53
  if (!desiredPlatform || !desiredPhone) {
54
54
  throw new Error("Missing required arguments: desiredPlatform and desiredPhone are required");
55
55
  }
56
56
  if (!desiredPlatformVersion) {
57
57
  throw new Error("Missing required arguments: desiredPlatformVersion is required");
58
58
  }
59
- if (!appPath) {
60
- throw new Error("You must provide an appPath.");
61
- }
62
- if (desiredPlatform === "android" && !appPath.endsWith(".apk")) {
63
- throw new Error("You must provide a valid Android app path (.apk).");
64
- }
65
- if (desiredPlatform === "ios" && !appPath.endsWith(".ipa")) {
66
- throw new Error("You must provide a valid iOS app path (.ipa).");
59
+ if (!appPath && !browserstackAppUrl) {
60
+ throw new Error("Either appPath or browserstackAppUrl must be provided");
61
+ }
62
+ // Only validate app path format if appPath is provided
63
+ if (appPath) {
64
+ if (desiredPlatform === "android" && !appPath.endsWith(".apk")) {
65
+ throw new Error("You must provide a valid Android app path (.apk).");
66
+ }
67
+ if (desiredPlatform === "ios" && !appPath.endsWith(".ipa")) {
68
+ throw new Error("You must provide a valid iOS app path (.ipa).");
69
+ }
67
70
  }
68
71
  }
69
72
  /**
@@ -19,7 +19,7 @@ async function takeAppScreenshot(args) {
19
19
  let driver;
20
20
  try {
21
21
  validateArgs(args);
22
- const { desiredPlatform, desiredPhone, appPath, config } = args;
22
+ const { desiredPlatform, desiredPhone, appPath, browserstackAppUrl, config, } = args;
23
23
  let { desiredPlatformVersion } = args;
24
24
  const platforms = (await getDevicesAndBrowsers(BrowserStackProducts.APP_AUTOMATE)).mobile;
25
25
  const platformData = platforms.find((p) => p.os === desiredPlatform.toLowerCase());
@@ -35,8 +35,18 @@ async function takeAppScreenshot(args) {
35
35
  }
36
36
  const authString = getBrowserStackAuth(config);
37
37
  const [username, password] = authString.split(":");
38
- const app_url = await uploadApp(appPath, username, password);
39
- logger.info(`App uploaded. URL: ${app_url}`);
38
+ let app_url;
39
+ if (browserstackAppUrl) {
40
+ app_url = browserstackAppUrl;
41
+ logger.info(`Using provided BrowserStack app URL: ${app_url}`);
42
+ }
43
+ else {
44
+ if (!appPath) {
45
+ throw new Error("appPath is required when browserstackAppUrl is not provided");
46
+ }
47
+ app_url = await uploadApp(appPath, username, password);
48
+ logger.info(`App uploaded. URL: ${app_url}`);
49
+ }
40
50
  const capabilities = {
41
51
  platformName: desiredPlatform,
42
52
  "appium:platformVersion": selectedDevice.os_version,
@@ -89,11 +99,34 @@ async function takeAppScreenshot(args) {
89
99
  }
90
100
  //Runs AppAutomate tests on BrowserStack by uploading app and test suite, then triggering a test run.
91
101
  async function runAppTestsOnBrowserStack(args, config) {
102
+ // Validate that either paths or URLs are provided for both app and test suite
103
+ if (!args.browserstackAppUrl && !args.appPath) {
104
+ throw new Error("appPath is required when browserstackAppUrl is not provided");
105
+ }
106
+ if (!args.browserstackTestSuiteUrl && !args.testSuitePath) {
107
+ throw new Error("testSuitePath is required when browserstackTestSuiteUrl is not provided");
108
+ }
92
109
  switch (args.detectedAutomationFramework) {
93
110
  case AppTestPlatform.ESPRESSO: {
94
111
  try {
95
- const app_url = await uploadEspressoApp(args.appPath, config);
96
- const test_suite_url = await uploadEspressoTestSuite(args.testSuitePath, config);
112
+ let app_url;
113
+ if (args.browserstackAppUrl) {
114
+ app_url = args.browserstackAppUrl;
115
+ logger.info(`Using provided BrowserStack app URL: ${app_url}`);
116
+ }
117
+ else {
118
+ app_url = await uploadEspressoApp(args.appPath, config);
119
+ logger.info(`App uploaded. URL: ${app_url}`);
120
+ }
121
+ let test_suite_url;
122
+ if (args.browserstackTestSuiteUrl) {
123
+ test_suite_url = args.browserstackTestSuiteUrl;
124
+ logger.info(`Using provided BrowserStack test suite URL: ${test_suite_url}`);
125
+ }
126
+ else {
127
+ test_suite_url = await uploadEspressoTestSuite(args.testSuitePath, config);
128
+ logger.info(`Test suite uploaded. URL: ${test_suite_url}`);
129
+ }
97
130
  const build_id = await triggerEspressoBuild(app_url, test_suite_url, args.devices, args.project);
98
131
  return {
99
132
  content: [
@@ -111,8 +144,24 @@ async function runAppTestsOnBrowserStack(args, config) {
111
144
  }
112
145
  case AppTestPlatform.XCUITEST: {
113
146
  try {
114
- const app_url = await uploadXcuiApp(args.appPath, config);
115
- const test_suite_url = await uploadXcuiTestSuite(args.testSuitePath, config);
147
+ let app_url;
148
+ if (args.browserstackAppUrl) {
149
+ app_url = args.browserstackAppUrl;
150
+ logger.info(`Using provided BrowserStack app URL: ${app_url}`);
151
+ }
152
+ else {
153
+ app_url = await uploadXcuiApp(args.appPath, config);
154
+ logger.info(`App uploaded. URL: ${app_url}`);
155
+ }
156
+ let test_suite_url;
157
+ if (args.browserstackTestSuiteUrl) {
158
+ test_suite_url = args.browserstackTestSuiteUrl;
159
+ logger.info(`Using provided BrowserStack test suite URL: ${test_suite_url}`);
160
+ }
161
+ else {
162
+ test_suite_url = await uploadXcuiTestSuite(args.testSuitePath, config);
163
+ logger.info(`Test suite uploaded. URL: ${test_suite_url}`);
164
+ }
116
165
  const build_id = await triggerXcuiBuild(app_url, test_suite_url, args.devices, args.project, config);
117
166
  return {
118
167
  content: [
@@ -1,9 +1,10 @@
1
1
  import { BrowserStackConfig } from "../../lib/types.js";
2
2
  interface StartSessionArgs {
3
- appPath: string;
3
+ appPath?: string;
4
4
  desiredPlatform: "android" | "ios";
5
5
  desiredPhone: string;
6
6
  desiredPlatformVersion: string;
7
+ browserstackAppUrl?: string;
7
8
  }
8
9
  interface StartSessionOptions {
9
10
  config: BrowserStackConfig;
@@ -11,7 +11,7 @@ import envConfig from "../../config.js";
11
11
  * Start an App Live session: filter, select, upload, and open.
12
12
  */
13
13
  export async function startSession(args, options) {
14
- const { appPath, desiredPlatform, desiredPhone, desiredPlatformVersion } = args;
14
+ const { appPath, desiredPlatform, desiredPhone, desiredPlatformVersion, browserstackAppUrl, } = args;
15
15
  const { config } = options;
16
16
  // 1) Fetch devices for APP_LIVE
17
17
  const data = await getDevicesAndBrowsers(BrowserStackProducts.APP_LIVE);
@@ -38,11 +38,22 @@ export async function startSession(args, options) {
38
38
  desiredPlatformVersion !== "oldest") {
39
39
  note = `\n Note: The requested version "${desiredPlatformVersion}" is not available. Using "${version}" instead.`;
40
40
  }
41
- // 6) Upload app
42
- const authString = getBrowserStackAuth(config);
43
- const [username, password] = authString.split(":");
44
- const { app_url } = await uploadApp(appPath, username, password);
45
- logger.info(`App uploaded: ${app_url}`);
41
+ // 6) Upload app or use provided URL
42
+ let app_url;
43
+ if (browserstackAppUrl) {
44
+ app_url = browserstackAppUrl;
45
+ logger.info(`Using provided BrowserStack app URL: ${app_url}`);
46
+ }
47
+ else {
48
+ if (!appPath) {
49
+ throw new Error("appPath is required when browserstackAppUrl is not provided");
50
+ }
51
+ const authString = getBrowserStackAuth(config);
52
+ const [username, password] = authString.split(":");
53
+ const result = await uploadApp(appPath, username, password);
54
+ app_url = result.app_url;
55
+ logger.info(`App uploaded: ${app_url}`);
56
+ }
46
57
  if (!app_url) {
47
58
  throw new Error("Failed to upload app. Please try again.");
48
59
  }
@@ -7,7 +7,8 @@ import { BrowserStackConfig } from "../lib/types.js";
7
7
  export declare function startAppLiveSession(args: {
8
8
  desiredPlatform: string;
9
9
  desiredPlatformVersion: string;
10
- appPath: string;
10
+ appPath?: string;
11
11
  desiredPhone: string;
12
+ browserstackAppUrl?: string;
12
13
  }, config: BrowserStackConfig): Promise<CallToolResult>;
13
14
  export default function addAppLiveTools(server: McpServer, config: BrowserStackConfig): Record<string, any>;
@@ -10,34 +10,38 @@ export async function startAppLiveSession(args, config) {
10
10
  if (!args.desiredPlatform) {
11
11
  throw new Error("You must provide a desiredPlatform.");
12
12
  }
13
- if (!args.appPath) {
14
- throw new Error("You must provide a appPath.");
13
+ if (!args.appPath && !args.browserstackAppUrl) {
14
+ throw new Error("You must provide either appPath or browserstackAppUrl.");
15
15
  }
16
16
  if (!args.desiredPhone) {
17
17
  throw new Error("You must provide a desiredPhone.");
18
18
  }
19
- if (args.desiredPlatform === "android" && !args.appPath.endsWith(".apk")) {
20
- throw new Error("You must provide a valid Android app path.");
21
- }
22
- if (args.desiredPlatform === "ios" && !args.appPath.endsWith(".ipa")) {
23
- throw new Error("You must provide a valid iOS app path.");
24
- }
25
- // check if the app path exists && is readable
26
- try {
27
- if (!fs.existsSync(args.appPath)) {
28
- throw new Error("The app path does not exist.");
19
+ // Only validate app path if it's provided (not using browserstackAppUrl)
20
+ if (args.appPath) {
21
+ if (args.desiredPlatform === "android" && !args.appPath.endsWith(".apk")) {
22
+ throw new Error("You must provide a valid Android app path.");
23
+ }
24
+ if (args.desiredPlatform === "ios" && !args.appPath.endsWith(".ipa")) {
25
+ throw new Error("You must provide a valid iOS app path.");
26
+ }
27
+ // check if the app path exists && is readable
28
+ try {
29
+ if (!fs.existsSync(args.appPath)) {
30
+ throw new Error("The app path does not exist.");
31
+ }
32
+ fs.accessSync(args.appPath, fs.constants.R_OK);
33
+ }
34
+ catch (error) {
35
+ logger.error("The app path does not exist or is not readable: %s", error);
36
+ throw new Error("The app path does not exist or is not readable.");
29
37
  }
30
- fs.accessSync(args.appPath, fs.constants.R_OK);
31
- }
32
- catch (error) {
33
- logger.error("The app path does not exist or is not readable: %s", error);
34
- throw new Error("The app path does not exist or is not readable.");
35
38
  }
36
39
  const launchUrl = await startSession({
37
40
  appPath: args.appPath,
38
41
  desiredPlatform: args.desiredPlatform,
39
42
  desiredPhone: args.desiredPhone,
40
43
  desiredPlatformVersion: args.desiredPlatformVersion,
44
+ browserstackAppUrl: args.browserstackAppUrl,
41
45
  }, { config });
42
46
  return {
43
47
  content: [
@@ -311,6 +311,12 @@ Add new scripts to package.json for running tests on BrowserStack:
311
311
  }
312
312
  \`\`\`
313
313
 
314
+ Example :
315
+ \`\`\`json
316
+ "scripts": {
317
+ "test:browserstack": "npx browserstack-node-sdk playwright test"
318
+ }
319
+ \`\`\`
314
320
  ---STEP---
315
321
 
316
322
  Export BrowserStack credentials as environment variables:
@@ -319,6 +325,10 @@ Set the following environment variables before running tests.
319
325
  export BROWSERSTACK_USERNAME=${username}
320
326
  export BROWSERSTACK_ACCESS_KEY=${accessKey}
321
327
  \`\`\`
328
+
329
+ ---STEP---
330
+ Run your tests:
331
+ You can now run your tests on BrowserStack using your standard command or Use the commands defined in your package.json file to run the tests.
322
332
  `;
323
333
  /**
324
334
  * ---------- EXPORT CONFIG ----------
@@ -426,7 +436,7 @@ exports.config.capabilities.forEach(function (caps) {
426
436
  ---STEP---
427
437
 
428
438
  Run your tests:
429
- You can now run your tests on BrowserStack using your standard WebdriverIO command.
439
+ You can now run your tests on BrowserStack using your standard WebdriverIO command or Use the commands defined in your package.json file to run the tests.
430
440
  `;
431
441
  const cypressInstructions = (username, accessKey) => `
432
442
  ---STEP---
@@ -555,5 +565,8 @@ export const SUPPORTED_CONFIGURATIONS = {
555
565
  cypress: {
556
566
  cypress: { instructions: cypressInstructions },
557
567
  },
568
+ webdriverio: {
569
+ mocha: { instructions: webdriverioInstructions },
570
+ },
558
571
  },
559
572
  };
@@ -8,7 +8,8 @@ export type SDKSupportedLanguage = keyof typeof SDKSupportedLanguageEnum;
8
8
  export declare enum SDKSupportedBrowserAutomationFrameworkEnum {
9
9
  playwright = "playwright",
10
10
  selenium = "selenium",
11
- cypress = "cypress"
11
+ cypress = "cypress",
12
+ webdriverio = "webdriverio"
12
13
  }
13
14
  export type SDKSupportedBrowserAutomationFramework = keyof typeof SDKSupportedBrowserAutomationFrameworkEnum;
14
15
  export declare enum SDKSupportedTestingFrameworkEnum {
@@ -10,6 +10,7 @@ export var SDKSupportedBrowserAutomationFrameworkEnum;
10
10
  SDKSupportedBrowserAutomationFrameworkEnum["playwright"] = "playwright";
11
11
  SDKSupportedBrowserAutomationFrameworkEnum["selenium"] = "selenium";
12
12
  SDKSupportedBrowserAutomationFrameworkEnum["cypress"] = "cypress";
13
+ SDKSupportedBrowserAutomationFrameworkEnum["webdriverio"] = "webdriverio";
13
14
  })(SDKSupportedBrowserAutomationFrameworkEnum || (SDKSupportedBrowserAutomationFrameworkEnum = {}));
14
15
  export var SDKSupportedTestingFrameworkEnum;
15
16
  (function (SDKSupportedTestingFrameworkEnum) {
@@ -21,6 +21,7 @@ export interface TestCaseCreateRequest {
21
21
  issue_tracker?: IssueTracker;
22
22
  tags?: string[];
23
23
  custom_fields?: Record<string, string>;
24
+ automation_status?: string;
24
25
  }
25
26
  export interface TestCaseResponse {
26
27
  data: {
@@ -80,6 +81,7 @@ export declare const CreateTestCaseSchema: z.ZodObject<{
80
81
  }>>;
81
82
  tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
82
83
  custom_fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
84
+ automation_status: z.ZodOptional<z.ZodString>;
83
85
  }, "strip", z.ZodTypeAny, {
84
86
  name: string;
85
87
  test_case_steps: {
@@ -90,6 +92,7 @@ export declare const CreateTestCaseSchema: z.ZodObject<{
90
92
  folder_id: string;
91
93
  issues?: string[] | undefined;
92
94
  description?: string | undefined;
95
+ automation_status?: string | undefined;
93
96
  tags?: string[] | undefined;
94
97
  custom_fields?: Record<string, string> | undefined;
95
98
  preconditions?: string | undefined;
@@ -108,6 +111,7 @@ export declare const CreateTestCaseSchema: z.ZodObject<{
108
111
  folder_id: string;
109
112
  issues?: string[] | undefined;
110
113
  description?: string | undefined;
114
+ automation_status?: string | undefined;
111
115
  tags?: string[] | undefined;
112
116
  custom_fields?: Record<string, string> | undefined;
113
117
  preconditions?: string | undefined;
@@ -49,6 +49,10 @@ export const CreateTestCaseSchema = z.object({
49
49
  .record(z.string(), z.string())
50
50
  .optional()
51
51
  .describe("Map of custom field names to values."),
52
+ automation_status: z
53
+ .string()
54
+ .optional()
55
+ .describe("Automation status of the test case. Common values include 'not_automated', 'automated', 'automation_not_required'."),
52
56
  });
53
57
  export function sanitizeArgs(args) {
54
58
  const cleaned = { ...args };
@@ -58,6 +62,8 @@ export function sanitizeArgs(args) {
58
62
  delete cleaned.owner;
59
63
  if (cleaned.preconditions === null)
60
64
  delete cleaned.preconditions;
65
+ if (cleaned.automation_status === null)
66
+ delete cleaned.automation_status;
61
67
  if (cleaned.issue_tracker) {
62
68
  if (cleaned.issue_tracker.name === undefined ||
63
69
  cleaned.issue_tracker.host === undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@browserstack/mcp-server",
3
- "version": "1.2.1",
3
+ "version": "1.2.2-beta",
4
4
  "description": "BrowserStack's Official MCP Server",
5
5
  "main": "dist/index.js",
6
6
  "repository": {