@browserstack/mcp-server 1.2.3-beta.1 → 1.2.4

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.
Files changed (126) hide show
  1. package/README.md +88 -2
  2. package/dist/lib/device-cache.js +20 -17
  3. package/dist/lib/inmemory-store.d.ts +1 -0
  4. package/dist/lib/inmemory-store.js +1 -0
  5. package/dist/lib/utils.d.ts +5 -0
  6. package/dist/lib/utils.js +27 -0
  7. package/dist/server-factory.js +6 -0
  8. package/dist/tools/add-percy-snapshots.d.ts +5 -0
  9. package/dist/tools/add-percy-snapshots.js +17 -0
  10. package/dist/tools/appautomate-utils/appium-sdk/types.d.ts +2 -2
  11. package/dist/tools/appautomate-utils/appium-sdk/types.js +2 -9
  12. package/dist/tools/appautomate-utils/appium-sdk/utils.js +3 -0
  13. package/dist/tools/bstack-sdk.d.ts +2 -15
  14. package/dist/tools/bstack-sdk.js +10 -119
  15. package/dist/tools/build-insights.d.ts +7 -0
  16. package/dist/tools/build-insights.js +67 -0
  17. package/dist/tools/list-test-files.d.ts +2 -0
  18. package/dist/tools/list-test-files.js +36 -0
  19. package/dist/tools/percy-sdk.d.ts +4 -0
  20. package/dist/tools/percy-sdk.js +71 -0
  21. package/dist/tools/percy-snapshot-utils/constants.d.ts +16 -0
  22. package/dist/tools/percy-snapshot-utils/constants.js +500 -0
  23. package/dist/tools/percy-snapshot-utils/detect-test-files.d.ts +10 -0
  24. package/dist/tools/percy-snapshot-utils/detect-test-files.js +175 -0
  25. package/dist/tools/percy-snapshot-utils/types.d.ts +15 -0
  26. package/dist/tools/percy-snapshot-utils/utils.d.ts +4 -0
  27. package/dist/tools/percy-snapshot-utils/utils.js +30 -0
  28. package/dist/tools/rca-agent-utils/constants.d.ts +13 -0
  29. package/dist/tools/rca-agent-utils/constants.js +24 -0
  30. package/dist/tools/rca-agent-utils/format-rca.d.ts +1 -0
  31. package/dist/tools/rca-agent-utils/format-rca.js +37 -0
  32. package/dist/tools/rca-agent-utils/get-build-id.d.ts +1 -0
  33. package/dist/tools/rca-agent-utils/get-build-id.js +18 -0
  34. package/dist/tools/rca-agent-utils/get-failed-test-id.d.ts +2 -0
  35. package/dist/tools/rca-agent-utils/get-failed-test-id.js +69 -0
  36. package/dist/tools/rca-agent-utils/rca-data.d.ts +9 -0
  37. package/dist/tools/rca-agent-utils/rca-data.js +196 -0
  38. package/dist/tools/rca-agent-utils/types.d.ts +48 -0
  39. package/dist/tools/rca-agent-utils/types.js +20 -0
  40. package/dist/tools/rca-agent.d.ts +14 -0
  41. package/dist/tools/rca-agent.js +119 -0
  42. package/dist/tools/review-agent-utils/build-counts.d.ts +7 -0
  43. package/dist/tools/review-agent-utils/build-counts.js +44 -0
  44. package/dist/tools/review-agent-utils/percy-approve-reject.d.ts +6 -0
  45. package/dist/tools/review-agent-utils/percy-approve-reject.js +39 -0
  46. package/dist/tools/review-agent-utils/percy-diffs.d.ts +9 -0
  47. package/dist/tools/review-agent-utils/percy-diffs.js +35 -0
  48. package/dist/tools/review-agent-utils/percy-snapshots.d.ts +11 -0
  49. package/dist/tools/review-agent-utils/percy-snapshots.js +58 -0
  50. package/dist/tools/review-agent.d.ts +5 -0
  51. package/dist/tools/review-agent.js +56 -0
  52. package/dist/tools/run-percy-scan.d.ts +8 -0
  53. package/dist/tools/run-percy-scan.js +37 -0
  54. package/dist/tools/sdk-utils/{commands.d.ts → bstack/commands.d.ts} +1 -1
  55. package/dist/tools/sdk-utils/bstack/commands.js +88 -0
  56. package/dist/tools/sdk-utils/bstack/configUtils.d.ts +4 -0
  57. package/dist/tools/sdk-utils/bstack/configUtils.js +66 -0
  58. package/dist/tools/sdk-utils/bstack/constants.d.ts +58 -0
  59. package/dist/tools/sdk-utils/{constants.js → bstack/constants.js} +117 -78
  60. package/dist/tools/sdk-utils/{constants.d.ts → bstack/frameworks.d.ts} +1 -1
  61. package/dist/tools/sdk-utils/bstack/frameworks.js +57 -0
  62. package/dist/tools/sdk-utils/bstack/index.d.ts +4 -0
  63. package/dist/tools/sdk-utils/bstack/index.js +5 -0
  64. package/dist/tools/sdk-utils/bstack/sdkHandler.d.ts +4 -0
  65. package/dist/tools/sdk-utils/bstack/sdkHandler.js +74 -0
  66. package/dist/tools/sdk-utils/common/constants.d.ts +10 -0
  67. package/dist/tools/sdk-utils/common/constants.js +86 -0
  68. package/dist/tools/sdk-utils/common/formatUtils.d.ts +5 -0
  69. package/dist/tools/sdk-utils/common/formatUtils.js +27 -0
  70. package/dist/tools/sdk-utils/common/index.d.ts +3 -0
  71. package/dist/tools/sdk-utils/common/index.js +4 -0
  72. package/dist/tools/sdk-utils/common/instructionUtils.d.ts +8 -0
  73. package/dist/tools/sdk-utils/common/instructionUtils.js +20 -0
  74. package/dist/tools/sdk-utils/common/schema.d.ts +73 -0
  75. package/dist/tools/sdk-utils/common/schema.js +51 -0
  76. package/dist/tools/sdk-utils/common/types.d.ts +66 -0
  77. package/dist/tools/sdk-utils/{types.js → common/types.js} +15 -2
  78. package/dist/tools/sdk-utils/common/utils.d.ts +25 -0
  79. package/dist/tools/sdk-utils/common/utils.js +90 -0
  80. package/dist/tools/sdk-utils/handler.d.ts +4 -0
  81. package/dist/tools/sdk-utils/handler.js +119 -0
  82. package/dist/tools/sdk-utils/percy-automate/constants.d.ts +11 -0
  83. package/dist/tools/sdk-utils/percy-automate/constants.js +338 -0
  84. package/dist/tools/sdk-utils/percy-automate/frameworks.d.ts +8 -0
  85. package/dist/tools/sdk-utils/percy-automate/frameworks.js +50 -0
  86. package/dist/tools/sdk-utils/percy-automate/handler.d.ts +3 -0
  87. package/dist/tools/sdk-utils/percy-automate/handler.js +30 -0
  88. package/dist/tools/sdk-utils/percy-automate/index.d.ts +1 -0
  89. package/dist/tools/sdk-utils/percy-automate/index.js +2 -0
  90. package/dist/tools/sdk-utils/percy-automate/types.d.ts +13 -0
  91. package/dist/tools/sdk-utils/percy-automate/types.js +1 -0
  92. package/dist/tools/sdk-utils/percy-bstack/constants.d.ts +4 -0
  93. package/dist/tools/sdk-utils/{percy → percy-bstack}/constants.js +13 -39
  94. package/dist/tools/sdk-utils/percy-bstack/frameworks.d.ts +2 -0
  95. package/dist/tools/sdk-utils/percy-bstack/frameworks.js +27 -0
  96. package/dist/tools/sdk-utils/percy-bstack/handler.d.ts +4 -0
  97. package/dist/tools/sdk-utils/percy-bstack/handler.js +99 -0
  98. package/dist/tools/sdk-utils/percy-bstack/index.d.ts +4 -0
  99. package/dist/tools/sdk-utils/percy-bstack/index.js +4 -0
  100. package/dist/tools/sdk-utils/percy-bstack/instructions.d.ts +7 -0
  101. package/dist/tools/sdk-utils/{percy → percy-bstack}/instructions.js +5 -9
  102. package/dist/tools/sdk-utils/percy-bstack/types.d.ts +13 -0
  103. package/dist/tools/sdk-utils/percy-bstack/types.js +5 -0
  104. package/dist/tools/sdk-utils/percy-web/constants.d.ts +41 -0
  105. package/dist/tools/sdk-utils/percy-web/constants.js +883 -0
  106. package/dist/tools/sdk-utils/percy-web/fetchPercyToken.d.ts +4 -0
  107. package/dist/tools/sdk-utils/percy-web/fetchPercyToken.js +32 -0
  108. package/dist/tools/sdk-utils/percy-web/frameworks.d.ts +7 -0
  109. package/dist/tools/sdk-utils/percy-web/frameworks.js +103 -0
  110. package/dist/tools/sdk-utils/percy-web/handler.d.ts +4 -0
  111. package/dist/tools/sdk-utils/percy-web/handler.js +27 -0
  112. package/dist/tools/sdk-utils/percy-web/index.d.ts +4 -0
  113. package/dist/tools/sdk-utils/percy-web/index.js +4 -0
  114. package/dist/tools/sdk-utils/percy-web/types.d.ts +12 -0
  115. package/dist/tools/sdk-utils/percy-web/types.js +1 -0
  116. package/dist/tools/testmanagement-utils/create-testrun.d.ts +4 -4
  117. package/dist/tools/testmanagement-utils/update-testrun.d.ts +4 -4
  118. package/package.json +2 -1
  119. package/dist/tools/sdk-utils/commands.js +0 -65
  120. package/dist/tools/sdk-utils/instructions.d.ts +0 -6
  121. package/dist/tools/sdk-utils/instructions.js +0 -99
  122. package/dist/tools/sdk-utils/percy/constants.d.ts +0 -3
  123. package/dist/tools/sdk-utils/percy/instructions.d.ts +0 -10
  124. package/dist/tools/sdk-utils/percy/types.d.ts +0 -5
  125. package/dist/tools/sdk-utils/types.d.ts +0 -40
  126. /package/dist/tools/{sdk-utils/percy → percy-snapshot-utils}/types.js +0 -0
package/README.md CHANGED
@@ -38,7 +38,15 @@ Stay in flow—keep all project context in one place and trigger actions directl
38
38
  ## ⚡️ One Click MCP Setup
39
39
 
40
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
-
41
+ #### Note : Ensure you are using Node version >= `18.0`
42
+ - Check your node version using `node --version`. Recommended version: `v22.15.0` (LTS)
43
+ - To Upgrade Node :
44
+ - 1. On macOS `(Homebrew) - brew update && brew upgrade node or if using (nvm) - nvm install 22.15.0 && nvm use 22.15.0 && nvm alias default 22.15.0`
45
+ - 2. On Windows `(nvm-windows) : nvm install 22.15.0 && nvm use 22.15.0`
46
+ - 👉 <a href="https://nodejs.org/en/download" target="_blank">Or directly download the Node.js LTS Installer</a>
47
+
48
+ .
49
+
42
50
  ## 💡 Usage Examples
43
51
 
44
52
  ### 📱 Manual App Testing
@@ -142,6 +150,10 @@ Generate test cases from PRDs, convert manual tests to low-code automation, and
142
150
 
143
151
  ## 🛠️ Installation
144
152
 
153
+ ### 📋 Prerequisites for MCP Setup
154
+ #### Note : Ensure you are using Node version >= `18.0`
155
+ - Check your node version using `node --version`. Recommended version: `v22.15.0` (LTS)
156
+
145
157
  ### **One Click MCP Setup**
146
158
 
147
159
  [![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) &nbsp; [![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)
@@ -158,7 +170,9 @@ Generate test cases from PRDs, convert manual tests to low-code automation, and
158
170
 
159
171
  - 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).
160
172
 
161
- 2. Ensure you are using Node version >= `18.0`. Check your node version using `node --version`. Recommended version: `v22.15.0` (LTS)
173
+ 2. #### Note : Ensure you are using Node version >= `18.0`
174
+ - Check your node version using `node --version`. Recommended version: `v22.15.0` (LTS)
175
+
162
176
 
163
177
  3. **Install the MCP Server**
164
178
 
@@ -456,7 +470,79 @@ As of now we support 20 tools.
456
470
  ```text
457
471
  Upload PRD from /Users/xyz/Desktop/login-flow.pdf and use BrowserStack AI to generate test cases
458
472
  ```
473
+ ## 🚀 Remote MCP Server
474
+
475
+ Remote MCP comes with all the functionalities of an MCP server without the hassles of complex setup or local installation.
476
+
477
+ ### Key benefits:
478
+
479
+ - ✅ Works seamlessly in enterprise networks without worrying about firewalls or binaries or where local installation is not allowed.
480
+
481
+ - ✅ Secure OAuth integration – no password sharing or manual credential handling.
482
+
483
+ ### Limitations:
484
+
485
+ - ❌ No Local Testing support (cannot test apps behind VPNs, firewalls, or localhost). If you have to do Local Testing, you would have to use a BrowserStack Local MCP server.
486
+ - ❌ Latency can be slightly higher, but nothing considerable — you generally won’t notice it in normal use.
487
+
488
+ ### Installation Steps:
459
489
 
490
+ - On VSCode (Copilot - Agent Mode): `.vscode/mcp.json`:
491
+
492
+ - Locate or Create the Configuration File:
493
+ - 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.
494
+ - If this folder doesn't exist, create it.
495
+ - Inside the .vscode folder, create a new file named mcp.json
496
+ - To setup Remote BrowserStack MCP instead of local BrowserStack MCP you can add the following JSON content :
497
+ <div align="center">
498
+ <img src="assets/remotemcp_json_file.png" alt="Remote MCP JSON file" height="300" width="300">
499
+ </div>
500
+
501
+ ### Alternative way to Setup Remote MCP
502
+
503
+ - Step 1.Click on the gear icon to Select Tools
504
+
505
+ <div align="center">
506
+ <img src="assets/select_tools.png" alt="Select Tools" height="300" width="300">
507
+ </div>
508
+
509
+ - Step 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
510
+
511
+ <div align="center">
512
+ <img src="assets/add_mcp_server.png" alt="Add MCP Server" height="300" width="300">
513
+ </div>
514
+
515
+ - Step 3. Click on HTTP option
516
+ <div align="center">
517
+ <img src="assets/http_option.png" alt="HTTP Option" height="300" width="300">
518
+ </div>
519
+
520
+ - Step 4. Paste Remote MCP Server URL : https://mcp.browserstack.com/mcp
521
+ <div align="center">
522
+ <img src="assets/server_url.png" alt="Remote MCP Server URL" height="300" width="300">
523
+ </div>
524
+
525
+ - Step 5. Give server id as : browserstack
526
+
527
+ <div align="center">
528
+ <img src="assets/server_id.png" alt="Remote MCP Server ID" height="300" width="300">
529
+ </div>
530
+
531
+ - Step 6. In VSCode Click on start MCP Server and then click on "Allow"
532
+
533
+ <div align="center">
534
+ <img src="assets/authentication1.png" alt="authentication1" height="300" width="300">
535
+ </div>
536
+
537
+ <div align="center">
538
+ <img src="assets/authentication2.png" alt="authentication2" height="300" width="300">
539
+ </div>
540
+
541
+ <div align="center">
542
+ <img src="assets/signin_success.png" alt="Sign_in_success" height="300" width="300">
543
+ </div>
544
+
545
+
460
546
 
461
547
  ## 🤝 Recommended MCP Clients
462
548
 
@@ -26,30 +26,33 @@ export async function getDevicesAndBrowsers(type) {
26
26
  fs.mkdirSync(CACHE_DIR, { recursive: true });
27
27
  }
28
28
  let cache = {};
29
+ // Load existing cache
29
30
  if (fs.existsSync(CACHE_FILE)) {
30
- const stats = fs.statSync(CACHE_FILE);
31
- if (Date.now() - stats.mtimeMs < TTL_MS) {
32
- try {
33
- cache = JSON.parse(fs.readFileSync(CACHE_FILE, "utf8"));
34
- if (cache[type]) {
35
- return cache[type];
36
- }
37
- }
38
- catch (error) {
39
- console.error("Error parsing cache file:", error);
40
- // Continue with fetching fresh data
41
- }
31
+ try {
32
+ cache = JSON.parse(fs.readFileSync(CACHE_FILE, "utf8"));
33
+ }
34
+ catch (err) {
35
+ console.error("Error parsing cache file:", err);
36
+ cache = {};
37
+ }
38
+ // Check per-product TTL
39
+ const cachedEntry = cache[type];
40
+ if (cachedEntry?.timestamp && Date.now() - cachedEntry.timestamp < TTL_MS) {
41
+ return cachedEntry.data;
42
42
  }
43
43
  }
44
+ // Fetch fresh data from BrowserStack
44
45
  const liveRes = await apiClient.get({ url: URLS[type], raise_error: false });
45
46
  if (!liveRes.ok) {
46
- throw new Error(`Failed to fetch configuration from BrowserStack : ${type}=${liveRes.statusText}`);
47
+ throw new Error(`Failed to fetch configuration from BrowserStack: ${type} = ${liveRes.statusText}`);
47
48
  }
48
- cache = {
49
- [type]: liveRes.data,
49
+ // Save to cache with timestamp and data directly under product key
50
+ cache[type] = {
51
+ timestamp: Date.now(),
52
+ data: liveRes.data,
50
53
  };
51
- fs.writeFileSync(CACHE_FILE, JSON.stringify(cache), "utf8");
52
- return cache[type];
54
+ fs.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2), "utf8");
55
+ return liveRes.data;
53
56
  }
54
57
  // Rate limiter for started event (3H)
55
58
  export function shouldSendStartedEvent() {
@@ -1 +1,2 @@
1
1
  export declare const signedUrlMap: Map<string, object>;
2
+ export declare const testFilePathsMap: Map<string, string[]>;
@@ -1 +1,2 @@
1
1
  export const signedUrlMap = new Map();
2
+ export const testFilePathsMap = new Map();
@@ -1,4 +1,9 @@
1
1
  import type { ApiResponse } from "./apiClient.js";
2
+ import { BrowserStackConfig } from "./types.js";
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
5
  export declare function sanitizeUrlParam(param: string): string;
3
6
  export declare function maybeCompressBase64(base64: string): Promise<string>;
4
7
  export declare function assertOkResponse(response: Response | ApiResponse, action: string): Promise<void>;
8
+ export declare function fetchFromBrowserStackAPI(url: string, config: BrowserStackConfig): Promise<any>;
9
+ export declare function handleMCPError(toolName: string, server: McpServer, config: BrowserStackConfig, error: unknown): CallToolResult;
package/dist/lib/utils.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import sharp from "sharp";
2
+ import { getBrowserStackAuth } from "./get-auth.js";
3
+ import { trackMCP } from "../index.js";
2
4
  export function sanitizeUrlParam(param) {
3
5
  // Remove any characters that could be used for command injection
4
6
  return param.replace(/[;&|`$(){}[\]<>]/g, "");
@@ -24,3 +26,28 @@ export async function assertOkResponse(response, action) {
24
26
  throw new Error(`Failed to fetch logs for ${action}: ${response.statusText}`);
25
27
  }
26
28
  }
29
+ export async function fetchFromBrowserStackAPI(url, config) {
30
+ const authString = getBrowserStackAuth(config);
31
+ const auth = Buffer.from(authString).toString("base64");
32
+ const res = await fetch(url, {
33
+ headers: {
34
+ Authorization: `Basic ${auth}`,
35
+ },
36
+ });
37
+ if (!res.ok) {
38
+ throw new Error(`Failed to fetch from ${url}: ${res.status} ${res.statusText}`);
39
+ }
40
+ return res.json();
41
+ }
42
+ function errorContent(message) {
43
+ return {
44
+ content: [{ type: "text", text: message }],
45
+ isError: true,
46
+ };
47
+ }
48
+ export function handleMCPError(toolName, server, config, error) {
49
+ trackMCP(toolName, server.server.getClientVersion(), error, config);
50
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
51
+ const readableToolName = toolName.replace(/([A-Z])/g, " $1").toLowerCase();
52
+ return errorContent(`Failed to ${readableToolName}: ${errorMessage}. Please open an issue on GitHub if the problem persists`);
53
+ }
@@ -4,6 +4,7 @@ const require = createRequire(import.meta.url);
4
4
  const packageJson = require("../package.json");
5
5
  import logger from "./logger.js";
6
6
  import addSDKTools from "./tools/bstack-sdk.js";
7
+ import addPercyTools from "./tools/percy-sdk.js";
7
8
  import addBrowserLiveTools from "./tools/live.js";
8
9
  import addAccessibilityTools from "./tools/accessibility.js";
9
10
  import addTestManagementTools from "./tools/testmanagement.js";
@@ -12,7 +13,9 @@ import addFailureLogsTools from "./tools/get-failure-logs.js";
12
13
  import addAutomateTools from "./tools/automate.js";
13
14
  import addSelfHealTools from "./tools/selfheal.js";
14
15
  import addAppLiveTools from "./tools/applive.js";
16
+ import addBuildInsightsTools from "./tools/build-insights.js";
15
17
  import { setupOnInitialized } from "./oninitialized.js";
18
+ import addRCATools from "./tools/rca-agent.js";
16
19
  /**
17
20
  * Wrapper class for BrowserStack MCP Server
18
21
  * Stores a map of registered tools by name
@@ -38,6 +41,7 @@ export class BrowserStackMcpServer {
38
41
  const toolAdders = [
39
42
  addAccessibilityTools,
40
43
  addSDKTools,
44
+ addPercyTools,
41
45
  addAppLiveTools,
42
46
  addBrowserLiveTools,
43
47
  addTestManagementTools,
@@ -45,6 +49,8 @@ export class BrowserStackMcpServer {
45
49
  addFailureLogsTools,
46
50
  addAutomateTools,
47
51
  addSelfHealTools,
52
+ addBuildInsightsTools,
53
+ addRCATools,
48
54
  ];
49
55
  toolAdders.forEach((adder) => {
50
56
  // Each adder now returns a Record<string, Tool>
@@ -0,0 +1,5 @@
1
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ export declare function updateTestsWithPercyCommands(args: {
3
+ uuid: string;
4
+ index: number;
5
+ }): Promise<CallToolResult>;
@@ -0,0 +1,17 @@
1
+ import { testFilePathsMap } from "../lib/inmemory-store.js";
2
+ import { updateFileAndStep } from "./percy-snapshot-utils/utils.js";
3
+ import { percyWebSetupInstructions } from "../tools/sdk-utils/percy-web/handler.js";
4
+ export async function updateTestsWithPercyCommands(args) {
5
+ const { uuid, index } = args;
6
+ const filePaths = testFilePathsMap.get(uuid);
7
+ if (!filePaths) {
8
+ throw new Error(`No test files found in memory for UUID: ${uuid}`);
9
+ }
10
+ if (index < 0 || index >= filePaths.length) {
11
+ throw new Error(`Invalid index: ${index}. There are ${filePaths.length} files for UUID: ${uuid}`);
12
+ }
13
+ const result = await updateFileAndStep(filePaths[index], index, filePaths.length, percyWebSetupInstructions);
14
+ return {
15
+ content: result,
16
+ };
17
+ }
@@ -49,8 +49,8 @@ export interface AppSDKInstruction {
49
49
  export declare const SUPPORTED_CONFIGURATIONS: {
50
50
  appium: {
51
51
  ruby: string[];
52
- java: string[];
53
- csharp: string[];
52
+ java: never[];
53
+ csharp: never[];
54
54
  python: string[];
55
55
  nodejs: string[];
56
56
  };
@@ -46,15 +46,8 @@ export var AppSDKSupportedPlatformEnum;
46
46
  export const SUPPORTED_CONFIGURATIONS = {
47
47
  appium: {
48
48
  ruby: ["cucumberRuby"],
49
- java: [
50
- "junit5",
51
- "junit4",
52
- "testng",
53
- "cucumberTestng",
54
- "selenide",
55
- "jbehave",
56
- ],
57
- csharp: ["nunit", "xunit", "mstest", "specflow", "reqnroll"],
49
+ java: [],
50
+ csharp: [],
58
51
  python: ["pytest", "robot", "behave", "lettuce"],
59
52
  nodejs: ["jest", "mocha", "cucumberJs", "webdriverio", "nightwatch"],
60
53
  },
@@ -55,6 +55,9 @@ export function validateSupportforAppAutomate(framework, language, testingFramew
55
55
  throw new Error(`Unsupported language '${language}' for framework '${framework}'. Supported languages: ${languages.join(", ")}`);
56
56
  }
57
57
  const testingFrameworks = SUPPORTED_CONFIGURATIONS[framework][language];
58
+ if (testingFrameworks.length === 0) {
59
+ throw new Error(`No testing frameworks are supported for language '${language}' and framework '${framework}'.`);
60
+ }
58
61
  if (!testingFrameworks.includes(testingFramework)) {
59
62
  throw new Error(`Unsupported testing framework '${testingFramework}' for language '${language}' and framework '${framework}'. Supported testing frameworks: ${testingFrameworks.join(", ")}`);
60
63
  }
@@ -1,17 +1,4 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3
- import { SDKSupportedBrowserAutomationFramework, SDKSupportedLanguage, SDKSupportedTestingFramework } from "./sdk-utils/types.js";
4
2
  import { BrowserStackConfig } from "../lib/types.js";
5
- /**
6
- * BrowserStack SDK hooks into your test framework to seamlessly run tests on BrowserStack.
7
- * This tool gives instructions to setup a browserstack.yml file in the project root and installs the necessary dependencies.
8
- */
9
- export declare function bootstrapProjectWithSDK({ detectedBrowserAutomationFramework, detectedTestingFramework, detectedLanguage, desiredPlatforms, enablePercy, config, }: {
10
- detectedBrowserAutomationFramework: SDKSupportedBrowserAutomationFramework;
11
- detectedTestingFramework: SDKSupportedTestingFramework;
12
- detectedLanguage: SDKSupportedLanguage;
13
- desiredPlatforms: string[];
14
- enablePercy: boolean;
15
- config: BrowserStackConfig;
16
- }): Promise<CallToolResult>;
17
- export default function addSDKTools(server: McpServer, config: BrowserStackConfig): Record<string, any>;
3
+ export declare function registerRunBrowserStackTestsTool(server: McpServer, config: BrowserStackConfig): Record<string, any>;
4
+ export default registerRunBrowserStackTestsTool;
@@ -1,128 +1,19 @@
1
- import { z } from "zod";
1
+ import { RunTestsOnBrowserStackParamsShape } from "./sdk-utils/common/schema.js";
2
+ import { runTestsOnBrowserStackHandler } from "./sdk-utils/handler.js";
3
+ import { RUN_ON_BROWSERSTACK_DESCRIPTION } from "./sdk-utils/common/constants.js";
4
+ import { handleMCPError } from "../lib/utils.js";
2
5
  import { trackMCP } from "../lib/instrumentation.js";
3
- import { getSDKPrefixCommand } from "./sdk-utils/commands.js";
4
- import { SDKSupportedLanguageEnum, SDKSupportedBrowserAutomationFrameworkEnum, SDKSupportedTestingFrameworkEnum, } from "./sdk-utils/types.js";
5
- import { generateBrowserStackYMLInstructions, getInstructionsForProjectConfiguration, formatInstructionsWithNumbers, } from "./sdk-utils/instructions.js";
6
- import { formatPercyInstructions, getPercyInstructions, } from "./sdk-utils/percy/instructions.js";
7
- import { getBrowserStackAuth } from "../lib/get-auth.js";
8
- /**
9
- * BrowserStack SDK hooks into your test framework to seamlessly run tests on BrowserStack.
10
- * This tool gives instructions to setup a browserstack.yml file in the project root and installs the necessary dependencies.
11
- */
12
- export async function bootstrapProjectWithSDK({ detectedBrowserAutomationFramework, detectedTestingFramework, detectedLanguage, desiredPlatforms, enablePercy, config, }) {
13
- // Get credentials from config
14
- const authString = getBrowserStackAuth(config);
15
- const [username, accessKey] = authString.split(":");
16
- // Handle frameworks with unique setup instructions that don't use browserstack.yml
17
- if (detectedBrowserAutomationFramework === "cypress" ||
18
- detectedTestingFramework === "webdriverio") {
19
- let combinedInstructions = getInstructionsForProjectConfiguration(detectedBrowserAutomationFramework, detectedTestingFramework, detectedLanguage, username, accessKey);
20
- if (enablePercy) {
21
- const percyInstructions = getPercyInstructions(detectedLanguage, detectedBrowserAutomationFramework, detectedTestingFramework);
22
- if (percyInstructions) {
23
- combinedInstructions +=
24
- "\n\n" + formatPercyInstructions(percyInstructions);
25
- }
26
- else {
27
- throw new Error(`Percy is currently not supported through MCP for ${detectedLanguage} with ${detectedTestingFramework}. If you want to run the test cases without Percy, disable Percy and run it again.`);
28
- }
29
- }
30
- // Apply consistent formatting for all configurations
31
- return formatFinalInstructions(combinedInstructions);
32
- }
33
- // Handle default flow using browserstack.yml
34
- const sdkSetupCommand = getSDKPrefixCommand(detectedLanguage, detectedTestingFramework, username, accessKey);
35
- const ymlInstructions = generateBrowserStackYMLInstructions(desiredPlatforms, enablePercy);
36
- const instructionsForProjectConfiguration = getInstructionsForProjectConfiguration(detectedBrowserAutomationFramework, detectedTestingFramework, detectedLanguage, username, accessKey);
37
- let combinedInstructions = "";
38
- // Step 1: Add SDK setup command
39
- if (sdkSetupCommand) {
40
- combinedInstructions += sdkSetupCommand;
41
- }
42
- // Step 2: Add browserstack.yml setup
43
- if (ymlInstructions) {
44
- combinedInstructions += "\n\n---STEP---\n" + ymlInstructions;
45
- }
46
- // Step 3: Add language/framework-specific setup
47
- if (instructionsForProjectConfiguration) {
48
- combinedInstructions += "\n\n" + instructionsForProjectConfiguration;
49
- }
50
- // Step 4: Add Percy setup if applicable
51
- if (enablePercy) {
52
- const percyInstructions = getPercyInstructions(detectedLanguage, detectedBrowserAutomationFramework, detectedTestingFramework);
53
- if (percyInstructions) {
54
- combinedInstructions +=
55
- "\n\n" + formatPercyInstructions(percyInstructions);
56
- }
57
- else {
58
- throw new Error(`Percy is currently not supported through MCP for ${detectedLanguage} with ${detectedTestingFramework}. If you want to run the test cases without Percy, disable Percy and run it again.`);
59
- }
60
- }
61
- // Apply consistent formatting for all configurations
62
- return formatFinalInstructions(combinedInstructions);
63
- }
64
- // Helper function to apply consistent formatting to all instruction types
65
- function formatFinalInstructions(combinedInstructions) {
66
- const fullInstructions = `⚠️ IMPORTANT: DO NOT SKIP ANY STEP
67
- All the setup steps described in this file MUST be executed regardless of any existing configuration or setup.
68
- This ensures proper BrowserStack SDK setup.
69
-
70
- ${formatInstructionsWithNumbers(combinedInstructions)}`;
71
- return {
72
- content: [
73
- {
74
- type: "text",
75
- text: fullInstructions,
76
- isError: false,
77
- },
78
- ],
79
- };
80
- }
81
- export default function addSDKTools(server, config) {
6
+ export function registerRunBrowserStackTestsTool(server, config) {
82
7
  const tools = {};
83
- tools.setupBrowserStackAutomateTests = server.tool("setupBrowserStackAutomateTests", "Set up and run automated web-based tests on BrowserStack using the BrowserStack SDK. Use for functional or integration tests on BrowserStack, with optional Percy visual testing for supported frameworks. Example prompts: run this test on browserstack; run this test on browserstack with Percy; set up this project for browserstack with Percy. Integrate BrowserStack SDK into your project", {
84
- detectedBrowserAutomationFramework: z
85
- .nativeEnum(SDKSupportedBrowserAutomationFrameworkEnum)
86
- .describe("The automation framework configured in the project. Example: 'playwright', 'selenium'"),
87
- detectedTestingFramework: z
88
- .nativeEnum(SDKSupportedTestingFrameworkEnum)
89
- .describe("The testing framework used in the project. Be precise with framework selection Example: 'webdriverio', 'jest', 'pytest', 'junit4', 'junit5', 'mocha'"),
90
- detectedLanguage: z
91
- .nativeEnum(SDKSupportedLanguageEnum)
92
- .describe("The programming language used in the project. Example: 'nodejs', 'python', 'java', 'csharp'"),
93
- desiredPlatforms: z
94
- .array(z.enum(["windows", "macos", "android", "ios"]))
95
- .describe("The platforms the user wants to test on. Always ask this to the user, do not try to infer this."),
96
- enablePercy: z
97
- .boolean()
98
- .optional()
99
- .default(false)
100
- .describe("Set to true if the user wants to enable Percy for visual testing. Defaults to false."),
101
- }, async (args) => {
8
+ tools.setupBrowserStackAutomateTests = server.tool("setupBrowserStackAutomateTests", RUN_ON_BROWSERSTACK_DESCRIPTION, RunTestsOnBrowserStackParamsShape, async (args) => {
102
9
  try {
103
- trackMCP("runTestsOnBrowserStack", server.server.getClientVersion(), undefined, config);
104
- return await bootstrapProjectWithSDK({
105
- detectedBrowserAutomationFramework: args.detectedBrowserAutomationFramework,
106
- detectedTestingFramework: args.detectedTestingFramework,
107
- detectedLanguage: args.detectedLanguage,
108
- desiredPlatforms: args.desiredPlatforms,
109
- enablePercy: args.enablePercy,
110
- config,
111
- });
10
+ trackMCP("runTestsOnBrowserStack", server.server.getClientVersion(), config);
11
+ return await runTestsOnBrowserStackHandler(args, config);
112
12
  }
113
13
  catch (error) {
114
- trackMCP("runTestsOnBrowserStack", server.server.getClientVersion(), error, config);
115
- return {
116
- content: [
117
- {
118
- type: "text",
119
- text: `Failed to bootstrap project with BrowserStack SDK. Error: ${error}. Please open an issue on GitHub if the problem persists`,
120
- isError: true,
121
- },
122
- ],
123
- isError: true,
124
- };
14
+ return handleMCPError("runTestsOnBrowserStack", server, config, error);
125
15
  }
126
16
  });
127
17
  return tools;
128
18
  }
19
+ export default registerRunBrowserStackTestsTool;
@@ -0,0 +1,7 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3
+ import { BrowserStackConfig } from "../lib/types.js";
4
+ export declare function fetchBuildInsightsTool(args: {
5
+ buildId: string;
6
+ }, config: BrowserStackConfig): Promise<CallToolResult>;
7
+ export default function addBuildInsightsTools(server: McpServer, config: BrowserStackConfig): Record<string, any>;
@@ -0,0 +1,67 @@
1
+ import { z } from "zod";
2
+ import logger from "../logger.js";
3
+ import { fetchFromBrowserStackAPI, handleMCPError } from "../lib/utils.js";
4
+ import { trackMCP } from "../lib/instrumentation.js";
5
+ // Tool function that fetches build insights from two APIs
6
+ export async function fetchBuildInsightsTool(args, config) {
7
+ try {
8
+ const buildUrl = `https://api-automation.browserstack.com/ext/v1/builds/${args.buildId}`;
9
+ const qualityGateUrl = `https://api-automation.browserstack.com/ext/v1/quality-gates/${args.buildId}`;
10
+ const [buildData, qualityData] = await Promise.all([
11
+ fetchFromBrowserStackAPI(buildUrl, config),
12
+ fetchFromBrowserStackAPI(qualityGateUrl, config),
13
+ ]);
14
+ // Select useful fields for users
15
+ const insights = {
16
+ name: buildData.name,
17
+ status: buildData.status,
18
+ duration: buildData.duration,
19
+ user: buildData.user,
20
+ tags: buildData.tags,
21
+ alerts: buildData.alerts,
22
+ status_stats: buildData.status_stats,
23
+ failure_categories: buildData.failure_categories,
24
+ smart_tags: buildData.smart_tags,
25
+ unique_errors: buildData.unique_errors?.overview,
26
+ observability_url: buildData?.observability_url,
27
+ ci_build_url: buildData.ci_info?.build_url,
28
+ quality_gate_result: qualityData.quality_gate_result,
29
+ };
30
+ const qualityProfiles = qualityData.quality_profiles?.map((profile) => ({
31
+ name: profile.name,
32
+ result: profile.result,
33
+ }));
34
+ const qualityProfilesText = qualityProfiles && qualityProfiles.length > 0
35
+ ? `Quality Gate Profiles (respond only if explicitly requested): ${JSON.stringify(qualityProfiles, null, 2)}`
36
+ : "No Quality Gate Profiles available.";
37
+ return {
38
+ content: [
39
+ {
40
+ type: "text",
41
+ text: "Build insights:\n" + JSON.stringify(insights, null, 2),
42
+ },
43
+ { type: "text", text: qualityProfilesText },
44
+ ],
45
+ };
46
+ }
47
+ catch (error) {
48
+ logger.error("Error fetching build insights", error);
49
+ throw error;
50
+ }
51
+ }
52
+ // Registers the fetchBuildInsights tool with the MCP server
53
+ export default function addBuildInsightsTools(server, config) {
54
+ const tools = {};
55
+ tools.fetchBuildInsights = server.tool("fetchBuildInsights", "Fetches insights about a BrowserStack build by combining build details and quality gate results.", {
56
+ buildId: z.string().describe("The build UUID of the BrowserStack build"),
57
+ }, async (args) => {
58
+ try {
59
+ trackMCP("fetchBuildInsights", server.server.getClientVersion(), config);
60
+ return await fetchBuildInsightsTool(args, config);
61
+ }
62
+ catch (error) {
63
+ return handleMCPError("fetchBuildInsights", server, config, error);
64
+ }
65
+ });
66
+ return tools;
67
+ }
@@ -0,0 +1,2 @@
1
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ export declare function addListTestFiles(args: any): Promise<CallToolResult>;
@@ -0,0 +1,36 @@
1
+ import { listTestFiles } from "./percy-snapshot-utils/detect-test-files.js";
2
+ import { testFilePathsMap } from "../lib/inmemory-store.js";
3
+ import crypto from "crypto";
4
+ export async function addListTestFiles(args) {
5
+ const { dirs, language, framework } = args;
6
+ let testFiles = [];
7
+ if (!dirs || dirs.length === 0) {
8
+ throw new Error("No directories provided to add the test files. Please provide test directories to add percy snapshot commands.");
9
+ }
10
+ for (const dir of dirs) {
11
+ const files = await listTestFiles({
12
+ language,
13
+ framework,
14
+ baseDir: dir,
15
+ });
16
+ testFiles = testFiles.concat(files);
17
+ }
18
+ if (testFiles.length === 0) {
19
+ throw new Error("No test files found");
20
+ }
21
+ // Generate a UUID and store the test files in memory
22
+ const uuid = crypto.randomUUID();
23
+ testFilePathsMap.set(uuid, testFiles);
24
+ return {
25
+ content: [
26
+ {
27
+ type: "text",
28
+ text: `The Test files are stored in memory with id ${uuid} and the total number of tests files found is ${testFiles.length}. You can use this UUID to retrieve the tests file paths later.`,
29
+ },
30
+ {
31
+ type: "text",
32
+ text: `You can now use the tool addPercySnapshotCommands to update the test file with Percy commands for visual testing with the UUID ${uuid}`,
33
+ },
34
+ ],
35
+ };
36
+ }
@@ -0,0 +1,4 @@
1
+ import { BrowserStackConfig } from "../lib/types.js";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare function registerPercyTools(server: McpServer, config: BrowserStackConfig): Record<string, any>;
4
+ export default registerPercyTools;