@browserstack/mcp-server 1.1.8 → 1.2.0

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 (125) hide show
  1. package/README.md +71 -35
  2. package/dist/config.d.ts +13 -0
  3. package/dist/config.js +10 -6
  4. package/dist/index.d.ts +4 -0
  5. package/dist/index.js +20 -30
  6. package/dist/lib/api.d.ts +2 -0
  7. package/dist/lib/api.js +10 -5
  8. package/dist/lib/apiClient.d.ts +31 -0
  9. package/dist/lib/apiClient.js +108 -0
  10. package/dist/lib/constants.d.ts +17 -0
  11. package/dist/lib/device-cache.d.ts +9 -0
  12. package/dist/lib/device-cache.js +3 -3
  13. package/dist/lib/error.d.ts +7 -0
  14. package/dist/lib/fuzzy.d.ts +1 -0
  15. package/dist/lib/get-auth.d.ts +2 -0
  16. package/dist/lib/get-auth.js +8 -0
  17. package/dist/lib/inmemory-store.d.ts +1 -0
  18. package/dist/lib/instrumentation.d.ts +4 -0
  19. package/dist/lib/instrumentation.js +8 -7
  20. package/dist/lib/local.d.ts +3 -0
  21. package/dist/lib/local.js +12 -3
  22. package/dist/lib/types.d.ts +4 -0
  23. package/dist/lib/types.js +1 -0
  24. package/dist/lib/utils.d.ts +4 -0
  25. package/dist/lib/version-resolver.d.ts +6 -0
  26. package/dist/logger.d.ts +3 -0
  27. package/dist/logger.js +20 -4
  28. package/dist/oninitialized.d.ts +2 -0
  29. package/dist/oninitialized.js +2 -7
  30. package/dist/server-factory.d.ts +3 -0
  31. package/dist/server-factory.js +37 -0
  32. package/dist/tools/accessibility.d.ts +3 -0
  33. package/dist/tools/accessibility.js +17 -10
  34. package/dist/tools/accessiblity-utils/accessibility-rag.d.ts +12 -0
  35. package/dist/tools/accessiblity-utils/accessibility-rag.js +22 -12
  36. package/dist/tools/accessiblity-utils/report-fetcher.d.ts +8 -0
  37. package/dist/tools/accessiblity-utils/report-fetcher.js +26 -16
  38. package/dist/tools/accessiblity-utils/report-parser.d.ts +23 -0
  39. package/dist/tools/accessiblity-utils/report-parser.js +3 -3
  40. package/dist/tools/accessiblity-utils/scanner.d.ts +25 -0
  41. package/dist/tools/accessiblity-utils/scanner.js +46 -24
  42. package/dist/tools/appautomate-utils/appautomate.d.ts +42 -0
  43. package/dist/tools/appautomate-utils/appautomate.js +95 -9
  44. package/dist/tools/appautomate-utils/types.d.ts +5 -0
  45. package/dist/tools/appautomate-utils/types.js +6 -0
  46. package/dist/tools/appautomate.d.ts +3 -0
  47. package/dist/tools/appautomate.js +109 -13
  48. package/dist/tools/applive-utils/device-search.d.ts +6 -0
  49. package/dist/tools/applive-utils/start-session.d.ts +15 -0
  50. package/dist/tools/applive-utils/start-session.js +11 -4
  51. package/dist/tools/applive-utils/types.d.ts +7 -0
  52. package/dist/tools/applive-utils/upload-app.d.ts +5 -0
  53. package/dist/tools/applive-utils/upload-app.js +8 -12
  54. package/dist/tools/applive-utils/version-utils.d.ts +4 -0
  55. package/dist/tools/applive.d.ts +13 -0
  56. package/dist/tools/applive.js +6 -6
  57. package/dist/tools/automate-utils/fetch-screenshots.d.ts +6 -0
  58. package/dist/tools/automate-utils/fetch-screenshots.js +16 -12
  59. package/dist/tools/automate.d.ts +9 -0
  60. package/dist/tools/automate.js +6 -6
  61. package/dist/tools/bstack-sdk.d.ts +17 -0
  62. package/dist/tools/bstack-sdk.js +47 -20
  63. package/dist/tools/failurelogs-utils/app-automate.d.ts +7 -0
  64. package/dist/tools/failurelogs-utils/app-automate.js +29 -11
  65. package/dist/tools/failurelogs-utils/automate.d.ts +6 -0
  66. package/dist/tools/failurelogs-utils/automate.js +27 -12
  67. package/dist/tools/failurelogs-utils/utils.d.ts +30 -0
  68. package/dist/tools/getFailureLogs.d.ts +14 -0
  69. package/dist/tools/getFailureLogs.js +11 -11
  70. package/dist/tools/live-utils/desktop-filter.d.ts +2 -0
  71. package/dist/tools/live-utils/mobile-filter.d.ts +2 -0
  72. package/dist/tools/live-utils/start-session.d.ts +6 -0
  73. package/dist/tools/live-utils/start-session.js +18 -5
  74. package/dist/tools/live-utils/types.d.ts +33 -0
  75. package/dist/tools/live.d.ts +3 -0
  76. package/dist/tools/live.js +11 -11
  77. package/dist/tools/observability.d.ts +5 -0
  78. package/dist/tools/observability.js +14 -11
  79. package/dist/tools/sdk-utils/commands.d.ts +3 -0
  80. package/dist/tools/sdk-utils/commands.js +20 -5
  81. package/dist/tools/sdk-utils/constants.d.ts +2 -0
  82. package/dist/tools/sdk-utils/constants.js +284 -160
  83. package/dist/tools/sdk-utils/instructions.d.ts +6 -0
  84. package/dist/tools/sdk-utils/instructions.js +28 -6
  85. package/dist/tools/sdk-utils/percy/constants.d.ts +3 -0
  86. package/dist/tools/sdk-utils/percy/constants.js +36 -25
  87. package/dist/tools/sdk-utils/percy/instructions.d.ts +10 -0
  88. package/dist/tools/sdk-utils/percy/types.d.ts +5 -0
  89. package/dist/tools/sdk-utils/types.d.ts +39 -0
  90. package/dist/tools/sdk-utils/types.js +3 -0
  91. package/dist/tools/selfheal-utils/selfheal.d.ts +11 -0
  92. package/dist/tools/selfheal-utils/selfheal.js +10 -6
  93. package/dist/tools/selfheal.d.ts +7 -0
  94. package/dist/tools/selfheal.js +6 -6
  95. package/dist/tools/testmanagement-utils/TCG-utils/api.d.ts +34 -0
  96. package/dist/tools/testmanagement-utils/TCG-utils/api.js +57 -44
  97. package/dist/tools/testmanagement-utils/TCG-utils/config.d.ts +5 -0
  98. package/dist/tools/testmanagement-utils/TCG-utils/helpers.d.ts +13 -0
  99. package/dist/tools/testmanagement-utils/TCG-utils/helpers.js +2 -1
  100. package/dist/tools/testmanagement-utils/TCG-utils/types.d.ts +26 -0
  101. package/dist/tools/testmanagement-utils/add-test-result.d.ts +42 -0
  102. package/dist/tools/testmanagement-utils/add-test-result.js +23 -10
  103. package/dist/tools/testmanagement-utils/create-lca-steps.d.ts +100 -0
  104. package/dist/tools/testmanagement-utils/create-lca-steps.js +64 -55
  105. package/dist/tools/testmanagement-utils/create-project-folder.d.ts +31 -0
  106. package/dist/tools/testmanagement-utils/create-project-folder.js +31 -21
  107. package/dist/tools/testmanagement-utils/create-testcase.d.ts +122 -0
  108. package/dist/tools/testmanagement-utils/create-testcase.js +13 -10
  109. package/dist/tools/testmanagement-utils/create-testrun.d.ts +82 -0
  110. package/dist/tools/testmanagement-utils/create-testrun.js +11 -8
  111. package/dist/tools/testmanagement-utils/list-testcases.d.ts +30 -0
  112. package/dist/tools/testmanagement-utils/list-testcases.js +9 -7
  113. package/dist/tools/testmanagement-utils/list-testruns.d.ts +22 -0
  114. package/dist/tools/testmanagement-utils/list-testruns.js +9 -7
  115. package/dist/tools/testmanagement-utils/poll-lca-status.d.ts +11 -0
  116. package/dist/tools/testmanagement-utils/poll-lca-status.js +12 -8
  117. package/dist/tools/testmanagement-utils/testcase-from-file.d.ts +4 -0
  118. package/dist/tools/testmanagement-utils/testcase-from-file.js +6 -6
  119. package/dist/tools/testmanagement-utils/update-testrun.d.ts +40 -0
  120. package/dist/tools/testmanagement-utils/update-testrun.js +11 -7
  121. package/dist/tools/testmanagement-utils/upload-file.d.ts +20 -0
  122. package/dist/tools/testmanagement-utils/upload-file.js +8 -6
  123. package/dist/tools/testmanagement.d.ts +60 -0
  124. package/dist/tools/testmanagement.js +51 -53
  125. package/package.json +1 -1
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  </div>
14
14
 
15
- <p align="center">One Platform For All Your Testing Needs</p>
15
+ <p align="center">Comprehensive Test Platform</p>
16
16
 
17
17
  <div align="center">
18
18
  <a href="https://glama.ai/mcp/servers/@browserstack/mcp-server">
@@ -21,26 +21,23 @@
21
21
  </div>
22
22
 
23
23
  <div>
24
- <a href="https://www.youtube.com/watch?v=sLA7K9v7qZc">
24
+ <a href="https://www.youtube.com/watch?v=sLA7K9v7qZc&list=PL1vH6dHT3H7oy8w9CY6L_nxGxCc89VXMX&index=5">
25
25
  <img src="assets/thumbnail.webp">
26
26
  </a>
27
27
  </div>
28
28
 
29
- Enable every developer and tester in your team, whether they are testing manually, starting their automation journey, or scaling test automation.
30
- BrowserStack MCP Server allows you to use our cutting-edge [Test Platform](https://www.browserstack.com/test-platform) directly from your favourite AI tools.
31
-
32
- ### Why BrowserStack ?
33
-
34
- <p align="center">
35
- <img src="assets/overview.png" alt="overview">
36
- </p>
29
+ Manage test cases, execute manual or automated tests, debug issues, and even fix code—directly within tools like Cursor, Claude, or any MCP-enabled client, using plain English.
30
+ #### Test from anywhere:
31
+ Easily connect the BrowserStack Test Platform to your favourite AI tools, such as IDEs, LLMs, or agentic workflows.
32
+ #### Reduced context switching:
33
+ Stay in flow—keep all project context in one place and trigger actions directly from your IDE or LLM.
37
34
 
38
35
  ## 💡 Usage Examples
39
36
 
40
37
  ### 📱 Manual App Testing
41
38
 
42
- Use the following prompts to use your **mobile apps** on BrowserStack's extensive cloud of real devices. Stop using emulators!
43
-
39
+ Test mobile apps on real devices across the latest OS versions. Reproduce bugs and debug crashes without setup hassles.
40
+ Below are some sample prompts to use your mobile apps on BrowserStack's extensive cloud of real devices
44
41
  ```bash
45
42
  # Open app on specific device
46
43
  "open my app on a iPhone 15 Pro Max"
@@ -49,7 +46,6 @@ Use the following prompts to use your **mobile apps** on BrowserStack's extensiv
49
46
  "My app crashed on Android 14 device, can you help me debug?"
50
47
  ```
51
48
 
52
-
53
49
  - Unlike emulators, test your app's real-world performance on actual devices. With advanced [App-Profiling features](https://www.browserstack.com/docs/app-live/app-performance-testing), you can debug crashes and performance issues in real-time.
54
50
  - Access all major devices and OS versions from our [device grid](https://www.browserstack.com/list-of-browsers-and-platforms/app_live), We have strict SLAs to provision our global datacenters with newly released devices on [launch day](https://www.browserstack.com/blog/browserstack-launches-iphone-15-on-day-0-behind-the-scenes/).
55
51
 
@@ -58,8 +54,9 @@ Use the following prompts to use your **mobile apps** on BrowserStack's extensiv
58
54
  Similar to the app testing, you can use the following prompts to test your **websites** on BrowserStack's extensive cloud of real browsers and devices. Don't have Edge browser installed on your machine ? We've got you covered!
59
55
 
60
56
  ```bash
61
- # Test your local websites
57
+ # Test your websites
62
58
  "open my website hosted on localhost:3001 on Edge"
59
+ "open browserstack.com on latest version of Chrome"
63
60
  ```
64
61
 
65
62
  - Test websites across different browsers and devices. We support [every major browser](https://www.browserstack.com/list-of-browsers-and-platforms/live) across every major OS.
@@ -67,27 +64,39 @@ Similar to the app testing, you can use the following prompts to test your **web
67
64
 
68
65
  ### 🧪 Automated Testing (Playwright, Selenium, A11y and more..)
69
66
 
70
- Use the following prompts to run/debug/fix your **automated tests** on BrowserStack's [Test Platform](https://www.browserstack.com/test-platform).
67
+ Auto-analyze, diagnose, and even fix broken test scripts right in your IDE or LLM. Instantly fetch logs, identify root causes, and apply context-aware fixes. No more debugging loops.
68
+ Below are few example prompts to run/debug/fix your automated tests on BrowserStack's [Test Platform](https://www.browserstack.com/test-platform).
71
69
 
72
70
  ```bash
73
- # Port test suite to BrowserStack
74
- "run my test suite on BrowserStack infra"
71
+ #Port test suite to BrowserStack
72
+ "Setup test suite to run on BrowserStack infra"
75
73
 
76
- # Debug test failures
77
- "My test suite failed, can you help me fix the new failures?"
74
+ #Run tests on BrowserStack
75
+ “Run my tests on BrowserStack”
78
76
 
79
- # Accessibility testing
80
- "check for accessibility issues on my www.mywebsite.com"
81
- ```
77
+ #AI powered debugging of test failures
78
+ "My App Automate tests have failed, can you help me fix the new failures?"
82
79
 
80
+ ```
83
81
  - Fix test failures reported by your CI/CD pipeline by utilising our industry leading [Test Observability](https://www.browserstack.com/docs/test-observability) features. Find more info [here](https://www.browserstack.com/docs/test-observability/features/smart-tags).
84
82
  - Run tests written in Jest, Playwright, Selenium, and more on BrowserStack's [Test Platform](https://www.browserstack.com/test-platform)
85
- - **Accessibility Testing**: Ensure WCAG and ADA compliance with our [Accessibility Testing](https://www.browserstack.com/accessibility-testing) tool
86
83
 
84
+ ### 🌐 Accessibility
85
+
86
+ Catch accessibility issues early with automated, local a11y scans. Get one-click, AI-suggested fixes. No docs hunting, no CI surprises. Ensure WCAG and ADA compliance with our Accessibility Testing tool
87
+
88
+ ```bash
89
+ #Scan accessibility issues while development
90
+ "Scan & help fix accessibility issues for my website running locally on localhost:3000"
91
+
92
+ #Scan accessibility issues on production site
93
+ “Run accessibility scan & identify issues on my website - www.bstackdemo.com”
94
+
95
+ ```
87
96
 
88
97
  ### 📋 Test Management
89
98
 
90
- Use the following prompts to utilise capabilities of BrowserStack's [Test Management](https://www.browserstack.com/test-management) with MCP server.
99
+ Create and manage test cases, create test plans and trigger test runs using natural language. Below are a few example prompts to utilise capabilities of BrowserStack's [Test Management](https://www.browserstack.com/test-management) with MCP server.
91
100
 
92
101
  ```bash
93
102
  # Create project & folder structure
@@ -106,6 +115,24 @@ Use the following prompts to utilise capabilities of BrowserStack's [Test Manage
106
115
  "update test results as passed for Login tests test run from My Demo Project"
107
116
  ```
108
117
 
118
+ ### 🧪 Access BrowserStack AI agnets
119
+
120
+ Generate test cases from PRDs, convert manual tests to low-code automation, and auto-heal flaky scripts powered by BrowserStack’s AI agents, seamlessly integrated into your workflow. Below are few example prompts to access Browserstack AI agents
121
+
122
+ ```bash
123
+ #Test case generator agent
124
+ "With Browserstack AI, create relevant test cases for my PRD located at /usr/file/location"
125
+
126
+
127
+ #Low code authoring agent
128
+ “With Browserstack AI, automate my manual test case X, added in Test Management”
129
+
130
+
131
+ #Self healing agent
132
+ “Help fix flaky tests in my test script with Browserstack AI self healing”
133
+ ```
134
+
135
+
109
136
  ## 🛠️ Installation
110
137
 
111
138
  1. **Create a BrowserStack Account**
@@ -113,9 +140,7 @@ Use the following prompts to utilise capabilities of BrowserStack's [Test Manage
113
140
  - Sign up for [BrowserStack](https://www.browserstack.com/users/sign_up) if you don't have an account already.
114
141
 
115
142
  - ℹ️ If you have an open-source project, we'll be able to provide you with a [free plan](https://www.browserstack.com/open-source).
116
- <div align="center">
117
- <img src="assets/open-source-plan.png" alt="Open Source Plan">
118
- </div>
143
+
119
144
 
120
145
  - 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).
121
146
 
@@ -175,6 +200,25 @@ Use the following prompts to utilise capabilities of BrowserStack's [Test Manage
175
200
  }
176
201
  }
177
202
  ```
203
+ - Cline
204
+
205
+ Click the “MCP Servers” icon in the navigation bar
206
+ Select the “Installed” tab. Click the “Configure MCP Servers” button at the bottom of the pane.
207
+
208
+ ```json
209
+ {
210
+ "mcpServers": {
211
+ "browserstack": {
212
+ "command": "npx",
213
+ "args": ["-y", "@browserstack/mcp-server@latest"],
214
+ "env": {
215
+ "BROWSERSTACK_USERNAME": "<username>",
216
+ "BROWSERSTACK_ACCESS_KEY": "<access_key>"
217
+ }
218
+ }
219
+ }
220
+ }
221
+ ```
178
222
 
179
223
  ### Installing via Smithery
180
224
 
@@ -203,17 +247,9 @@ We welcome contributions! Please open an issue to discuss any changes you'd like
203
247
 
204
248
  For support, please:
205
249
 
206
- - Check our [documentation](https://www.browserstack.com/docs)
207
250
  - Open an issue in our [GitHub repository](https://github.com/browserstack/mcp-server) if you face any issues related to the MCP Server.
208
251
  - Contact our [support team](https://www.browserstack.com/contact) for any other queries.
209
252
 
210
253
  ## 🚀 More Features Coming Soon
211
254
 
212
255
  Stay tuned for exciting updates! Have any suggestions? Please open an issue to discuss.
213
-
214
- ## 🔗 Resources
215
-
216
- - [BrowserStack Test Platform](https://www.browserstack.com/test-platform)
217
- - [MCP Protocol Documentation](https://modelcontextprotocol.io)
218
- - [Device Grid](https://www.browserstack.com/list-of-browsers-and-platforms/app_live)
219
- - [Accessibility Testing](https://www.browserstack.com/accessibility-testing)
@@ -0,0 +1,13 @@
1
+ /**
2
+ * USE_OWN_LOCAL_BINARY_PROCESS:
3
+ * If true, the system will not start a new local binary process, but will use the user's own process.
4
+ */
5
+ export declare class Config {
6
+ readonly DEV_MODE: boolean;
7
+ readonly browserstackLocalOptions: Record<string, any>;
8
+ readonly USE_OWN_LOCAL_BINARY_PROCESS: boolean;
9
+ readonly REMOTE_MCP: boolean;
10
+ constructor(DEV_MODE: boolean, browserstackLocalOptions: Record<string, any>, USE_OWN_LOCAL_BINARY_PROCESS: boolean, REMOTE_MCP: boolean);
11
+ }
12
+ declare const config: Config;
13
+ export default config;
package/dist/config.js CHANGED
@@ -28,17 +28,21 @@ for (const key of BROWSERSTACK_LOCAL_OPTION_KEYS) {
28
28
  browserstackLocalOptions[key] = envVar;
29
29
  }
30
30
  }
31
+ /**
32
+ * USE_OWN_LOCAL_BINARY_PROCESS:
33
+ * If true, the system will not start a new local binary process, but will use the user's own process.
34
+ */
31
35
  export class Config {
32
- browserstackUsername;
33
- browserstackAccessKey;
34
36
  DEV_MODE;
35
37
  browserstackLocalOptions;
36
- constructor(browserstackUsername, browserstackAccessKey, DEV_MODE, browserstackLocalOptions) {
37
- this.browserstackUsername = browserstackUsername;
38
- this.browserstackAccessKey = browserstackAccessKey;
38
+ USE_OWN_LOCAL_BINARY_PROCESS;
39
+ REMOTE_MCP;
40
+ constructor(DEV_MODE, browserstackLocalOptions, USE_OWN_LOCAL_BINARY_PROCESS, REMOTE_MCP) {
39
41
  this.DEV_MODE = DEV_MODE;
40
42
  this.browserstackLocalOptions = browserstackLocalOptions;
43
+ this.USE_OWN_LOCAL_BINARY_PROCESS = USE_OWN_LOCAL_BINARY_PROCESS;
44
+ this.REMOTE_MCP = REMOTE_MCP;
41
45
  }
42
46
  }
43
- const config = new Config(process.env.BROWSERSTACK_USERNAME, process.env.BROWSERSTACK_ACCESS_KEY, process.env.DEV_MODE === "true", browserstackLocalOptions);
47
+ const config = new Config(process.env.DEV_MODE === "true", browserstackLocalOptions, process.env.USE_OWN_LOCAL_BINARY_PROCESS === "true", process.env.REMOTE_MCP === "true");
44
48
  export default config;
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import "dotenv/config";
3
+ export { createMcpServer } from "./server-factory.js";
4
+ export { setLogger } from "./logger.js";
package/dist/index.js CHANGED
@@ -1,43 +1,31 @@
1
1
  #!/usr/bin/env node
2
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
3
  import { createRequire } from "module";
5
4
  const require = createRequire(import.meta.url);
6
5
  const packageJson = require("../package.json");
7
6
  import "dotenv/config";
8
7
  import logger from "./logger.js";
9
- import addSDKTools from "./tools/bstack-sdk.js";
10
- import addAppLiveTools from "./tools/applive.js";
11
- import addBrowserLiveTools from "./tools/live.js";
12
- import addAccessibilityTools from "./tools/accessibility.js";
13
- import addTestManagementTools from "./tools/testmanagement.js";
14
- import addAppAutomationTools from "./tools/appautomate.js";
15
- import addFailureLogsTools from "./tools/getFailureLogs.js";
16
- import addAutomateTools from "./tools/automate.js";
17
- import addSelfHealTools from "./tools/selfheal.js";
18
- import { setupOnInitialized } from "./oninitialized.js";
19
- function registerTools(server) {
20
- addAccessibilityTools(server);
21
- addSDKTools(server);
22
- addAppLiveTools(server);
23
- addBrowserLiveTools(server);
24
- addTestManagementTools(server);
25
- addAppAutomationTools(server);
26
- addFailureLogsTools(server);
27
- addAutomateTools(server);
28
- addSelfHealTools(server);
29
- }
30
- // Create an MCP server
31
- const server = new McpServer({
32
- name: "BrowserStack MCP Server",
33
- version: packageJson.version,
34
- });
35
- setupOnInitialized(server);
36
- registerTools(server);
8
+ import { createMcpServer } from "./server-factory.js";
37
9
  async function main() {
38
10
  logger.info("Launching BrowserStack MCP server, version %s", packageJson.version);
39
- // Start receiving messages on stdin and sending messages on stdout
11
+ const remoteMCP = process.env.REMOTE_MCP === "true";
12
+ if (remoteMCP) {
13
+ logger.info("Running in remote MCP mode");
14
+ return;
15
+ }
16
+ const username = process.env.BROWSERSTACK_USERNAME;
17
+ const accessKey = process.env.BROWSERSTACK_ACCESS_KEY;
18
+ if (!username) {
19
+ throw new Error("BROWSERSTACK_USERNAME environment variable is required");
20
+ }
21
+ if (!accessKey) {
22
+ throw new Error("BROWSERSTACK_ACCESS_KEY environment variable is required");
23
+ }
40
24
  const transport = new StdioServerTransport();
25
+ const server = createMcpServer({
26
+ "browserstack-username": username,
27
+ "browserstack-access-key": accessKey,
28
+ });
41
29
  await server.connect(transport);
42
30
  }
43
31
  main().catch(console.error);
@@ -45,3 +33,5 @@ main().catch(console.error);
45
33
  process.on("exit", () => {
46
34
  logger.flush();
47
35
  });
36
+ export { createMcpServer } from "./server-factory.js";
37
+ export { setLogger } from "./logger.js";
@@ -0,0 +1,2 @@
1
+ import { BrowserStackConfig } from "../lib/types.js";
2
+ export declare function getLatestO11YBuildInfo(buildName: string, projectName: string, config: BrowserStackConfig): Promise<import("./apiClient.js").ApiResponse<any>>;
package/dist/lib/api.js CHANGED
@@ -1,10 +1,15 @@
1
- import config from "../config.js";
2
- export async function getLatestO11YBuildInfo(buildName, projectName) {
1
+ import { getBrowserStackAuth } from "./get-auth.js";
2
+ import { apiClient } from "./apiClient.js";
3
+ export async function getLatestO11YBuildInfo(buildName, projectName, config) {
3
4
  const buildsUrl = `https://api-observability.browserstack.com/ext/v1/builds/latest?build_name=${encodeURIComponent(buildName)}&project_name=${encodeURIComponent(projectName)}`;
4
- const buildsResponse = await fetch(buildsUrl, {
5
+ const authString = getBrowserStackAuth(config);
6
+ const auth = Buffer.from(authString).toString("base64");
7
+ const buildsResponse = await apiClient.get({
8
+ url: buildsUrl,
5
9
  headers: {
6
- Authorization: `Basic ${Buffer.from(`${config.browserstackUsername}:${config.browserstackAccessKey}`).toString("base64")}`,
10
+ Authorization: `Basic ${auth}`,
7
11
  },
12
+ raise_error: false,
8
13
  });
9
14
  if (!buildsResponse.ok) {
10
15
  if (buildsResponse.statusText === "Unauthorized") {
@@ -12,5 +17,5 @@ export async function getLatestO11YBuildInfo(buildName, projectName) {
12
17
  }
13
18
  throw new Error(`Failed to fetch builds: ${buildsResponse.statusText}`);
14
19
  }
15
- return buildsResponse.json();
20
+ return buildsResponse;
16
21
  }
@@ -0,0 +1,31 @@
1
+ import { AxiosRequestConfig, AxiosResponse } from "axios";
2
+ type RequestOptions = {
3
+ url: string;
4
+ headers?: Record<string, string>;
5
+ params?: Record<string, string | number>;
6
+ body?: any;
7
+ raise_error?: boolean;
8
+ };
9
+ declare class ApiResponse<T = any> {
10
+ private _response;
11
+ constructor(response: AxiosResponse<T>);
12
+ get data(): T;
13
+ get status(): number;
14
+ get statusText(): string;
15
+ get headers(): Record<string, string>;
16
+ get config(): AxiosRequestConfig;
17
+ get url(): string | undefined;
18
+ get ok(): boolean;
19
+ }
20
+ declare class ApiClient {
21
+ private instance;
22
+ private get axiosAgent();
23
+ private requestWrapper;
24
+ get<T = any>({ url, headers, params, raise_error, }: RequestOptions): Promise<ApiResponse<T>>;
25
+ post<T = any>({ url, headers, body, raise_error, }: RequestOptions): Promise<ApiResponse<T>>;
26
+ put<T = any>({ url, headers, body, raise_error, }: RequestOptions): Promise<ApiResponse<T>>;
27
+ patch<T = any>({ url, headers, body, raise_error, }: RequestOptions): Promise<ApiResponse<T>>;
28
+ delete<T = any>({ url, headers, params, raise_error, }: RequestOptions): Promise<ApiResponse<T>>;
29
+ }
30
+ export declare const apiClient: ApiClient;
31
+ export type { ApiResponse, RequestOptions };
@@ -0,0 +1,108 @@
1
+ import axios from "axios";
2
+ import httpsProxyAgentPkg from "https-proxy-agent";
3
+ const { HttpsProxyAgent } = httpsProxyAgentPkg;
4
+ import * as https from "https";
5
+ import * as fs from "fs";
6
+ import config from "../config.js";
7
+ class ApiResponse {
8
+ _response;
9
+ constructor(response) {
10
+ this._response = response;
11
+ }
12
+ get data() {
13
+ return this._response.data;
14
+ }
15
+ get status() {
16
+ return this._response.status;
17
+ }
18
+ get statusText() {
19
+ return this._response.statusText;
20
+ }
21
+ get headers() {
22
+ const raw = this._response.headers;
23
+ const sanitized = {};
24
+ for (const key in raw) {
25
+ const value = raw[key];
26
+ if (typeof value === "string") {
27
+ sanitized[key] = value;
28
+ }
29
+ }
30
+ return sanitized;
31
+ }
32
+ get config() {
33
+ return this._response.config;
34
+ }
35
+ get url() {
36
+ return this._response.config.url;
37
+ }
38
+ get ok() {
39
+ return this._response.status >= 200 && this._response.status < 300;
40
+ }
41
+ }
42
+ // Utility to create HTTPS agent if needed (proxy/CA)
43
+ function getAxiosAgent() {
44
+ const proxyHost = config.browserstackLocalOptions.proxyHost;
45
+ const proxyPort = config.browserstackLocalOptions.proxyPort;
46
+ const caCertPath = config.browserstackLocalOptions.useCaCertificate;
47
+ // If both proxy host and port are defined
48
+ if (proxyHost && proxyPort) {
49
+ const proxyUrl = `http://${proxyHost}:${proxyPort}`;
50
+ if (caCertPath && fs.existsSync(caCertPath)) {
51
+ // Proxy + CA cert
52
+ const ca = fs.readFileSync(caCertPath);
53
+ return new HttpsProxyAgent({
54
+ host: proxyHost,
55
+ port: Number(proxyPort),
56
+ ca,
57
+ rejectUnauthorized: false, // Set to true if you want strict SSL
58
+ });
59
+ }
60
+ else {
61
+ // Proxy only
62
+ return new HttpsProxyAgent(proxyUrl);
63
+ }
64
+ }
65
+ else if (caCertPath && fs.existsSync(caCertPath)) {
66
+ // CA only
67
+ return new https.Agent({
68
+ ca: fs.readFileSync(caCertPath),
69
+ rejectUnauthorized: false, // Set to true for strict SSL
70
+ });
71
+ }
72
+ // Default agent (no proxy, no CA)
73
+ return undefined;
74
+ }
75
+ class ApiClient {
76
+ instance = axios.create();
77
+ get axiosAgent() {
78
+ return getAxiosAgent();
79
+ }
80
+ async requestWrapper(fn, raise_error = true) {
81
+ try {
82
+ const res = await fn(this.axiosAgent);
83
+ return new ApiResponse(res);
84
+ }
85
+ catch (error) {
86
+ if (error.response && !raise_error) {
87
+ return new ApiResponse(error.response);
88
+ }
89
+ throw error;
90
+ }
91
+ }
92
+ async get({ url, headers, params, raise_error = true, }) {
93
+ return this.requestWrapper((agent) => this.instance.get(url, { headers, params, httpsAgent: agent }), raise_error);
94
+ }
95
+ async post({ url, headers, body, raise_error = true, }) {
96
+ return this.requestWrapper((agent) => this.instance.post(url, body, { headers, httpsAgent: agent }), raise_error);
97
+ }
98
+ async put({ url, headers, body, raise_error = true, }) {
99
+ return this.requestWrapper((agent) => this.instance.put(url, body, { headers, httpsAgent: agent }), raise_error);
100
+ }
101
+ async patch({ url, headers, body, raise_error = true, }) {
102
+ return this.requestWrapper((agent) => this.instance.patch(url, body, { headers, httpsAgent: agent }), raise_error);
103
+ }
104
+ async delete({ url, headers, params, raise_error = true, }) {
105
+ return this.requestWrapper((agent) => this.instance.delete(url, { headers, params, httpsAgent: agent }), raise_error);
106
+ }
107
+ }
108
+ export const apiClient = new ApiClient();
@@ -0,0 +1,17 @@
1
+ export declare const SessionType: {
2
+ readonly Automate: "automate";
3
+ readonly AppAutomate: "app-automate";
4
+ };
5
+ export declare const AutomateLogType: {
6
+ readonly NetworkLogs: "networkLogs";
7
+ readonly SessionLogs: "sessionLogs";
8
+ readonly ConsoleLogs: "consoleLogs";
9
+ };
10
+ export declare const AppAutomateLogType: {
11
+ readonly DeviceLogs: "deviceLogs";
12
+ readonly AppiumLogs: "appiumLogs";
13
+ readonly CrashLogs: "crashLogs";
14
+ };
15
+ export type SessionType = (typeof SessionType)[keyof typeof SessionType];
16
+ export type AutomateLogType = (typeof AutomateLogType)[keyof typeof AutomateLogType];
17
+ export type AppAutomateLogType = (typeof AppAutomateLogType)[keyof typeof AppAutomateLogType];
@@ -0,0 +1,9 @@
1
+ export declare enum BrowserStackProducts {
2
+ LIVE = "live",
3
+ APP_LIVE = "app_live",
4
+ APP_AUTOMATE = "app_automate"
5
+ }
6
+ /**
7
+ * Fetches and caches BrowserStack datasets (live + app_live + app_automate) with a shared TTL.
8
+ */
9
+ export declare function getDevicesAndBrowsers(type: BrowserStackProducts): Promise<any>;
@@ -1,6 +1,7 @@
1
1
  import fs from "fs";
2
2
  import os from "os";
3
3
  import path from "path";
4
+ import { apiClient } from "./apiClient.js";
4
5
  const CACHE_DIR = path.join(os.homedir(), ".browserstack", "combined_cache");
5
6
  const CACHE_FILE = path.join(CACHE_DIR, "data.json");
6
7
  const TTL_MS = 24 * 60 * 60 * 1000; // 1 day
@@ -38,13 +39,12 @@ export async function getDevicesAndBrowsers(type) {
38
39
  }
39
40
  }
40
41
  }
41
- const liveRes = await fetch(URLS[type]);
42
+ const liveRes = await apiClient.get({ url: URLS[type], raise_error: false });
42
43
  if (!liveRes.ok) {
43
44
  throw new Error(`Failed to fetch configuration from BrowserStack : ${type}=${liveRes.statusText}`);
44
45
  }
45
- const data = await liveRes.json();
46
46
  cache = {
47
- [type]: data,
47
+ [type]: liveRes.data,
48
48
  };
49
49
  fs.writeFileSync(CACHE_FILE, JSON.stringify(cache), "utf8");
50
50
  return cache[type];
@@ -0,0 +1,7 @@
1
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ /**
3
+ * Formats an AxiosError into a CallToolResult with an appropriate message.
4
+ * @param err - The error object to format
5
+ * @param defaultText - The fallback error message
6
+ */
7
+ export declare function formatAxiosError(err: unknown, defaultText: string): CallToolResult;
@@ -0,0 +1 @@
1
+ export declare function customFuzzySearch<T>(list: T[], keys: Array<keyof T | string>, query: string, limit?: number, maxDistance?: number): T[];
@@ -0,0 +1,2 @@
1
+ import { BrowserStackConfig } from "../lib/types.js";
2
+ export declare function getBrowserStackAuth(config: BrowserStackConfig): string;
@@ -0,0 +1,8 @@
1
+ export function getBrowserStackAuth(config) {
2
+ const username = config["browserstack-username"];
3
+ const accessKey = config["browserstack-access-key"];
4
+ if (!username || !accessKey) {
5
+ throw new Error("BrowserStack credentials not set on server.authHeaders");
6
+ }
7
+ return `${username}:${accessKey}`;
8
+ }
@@ -0,0 +1 @@
1
+ export declare const signedUrlMap: Map<string, object>;
@@ -0,0 +1,4 @@
1
+ export declare function trackMCP(toolName: string, clientInfo: {
2
+ name?: string;
3
+ version?: string;
4
+ }, error?: unknown, config?: any): void;
@@ -1,14 +1,10 @@
1
1
  import logger from "../logger.js";
2
- import config from "../config.js";
2
+ import { getBrowserStackAuth } from "./get-auth.js";
3
3
  import { createRequire } from "module";
4
4
  const require = createRequire(import.meta.url);
5
5
  const packageJson = require("../../package.json");
6
6
  import axios from "axios";
7
- export function trackMCP(toolName, clientInfo, error) {
8
- if (config.DEV_MODE) {
9
- logger.info("Tracking MCP is disabled in dev mode");
10
- return;
11
- }
7
+ export function trackMCP(toolName, clientInfo, error, config) {
12
8
  const instrumentationEndpoint = "https://api.browserstack.com/sdk/v1/event";
13
9
  const isSuccess = !error;
14
10
  const mcpClient = clientInfo?.name || "unknown";
@@ -35,11 +31,16 @@ export function trackMCP(toolName, clientInfo, error) {
35
31
  event.event_properties.error_type =
36
32
  error instanceof Error ? error.constructor.name : "Unknown";
37
33
  }
34
+ let authHeader = undefined;
35
+ if (config) {
36
+ const authString = getBrowserStackAuth(config);
37
+ authHeader = `Basic ${Buffer.from(authString).toString("base64")}`;
38
+ }
38
39
  axios
39
40
  .post(instrumentationEndpoint, event, {
40
41
  headers: {
41
42
  "Content-Type": "application/json",
42
- Authorization: `Basic ${Buffer.from(`${config.browserstackUsername}:${config.browserstackAccessKey}`).toString("base64")}`,
43
+ ...(authHeader ? { Authorization: authHeader } : {}),
43
44
  },
44
45
  timeout: 2000,
45
46
  })
@@ -0,0 +1,3 @@
1
+ export declare function killExistingBrowserStackLocalProcesses(): Promise<void>;
2
+ export declare function ensureLocalBinarySetup(username: string, password: string, localIdentifier?: string): Promise<void>;
3
+ export declare function isLocalURL(url: string): boolean;
package/dist/lib/local.js CHANGED
@@ -65,15 +65,24 @@ export async function killExistingBrowserStackLocalProcesses() {
65
65
  // Continue execution as there may not be any processes running
66
66
  }
67
67
  }
68
- export async function ensureLocalBinarySetup(localIdentifier) {
68
+ export async function ensureLocalBinarySetup(username, password, localIdentifier) {
69
69
  logger.info("Ensuring local binary setup as it is required for private URLs...");
70
+ if (config.USE_OWN_LOCAL_BINARY_PROCESS) {
71
+ logger.info("Using user's own BrowserStack Local binary process, checking if it's running...");
72
+ const isRunning = await isBrowserStackLocalRunning();
73
+ if (!isRunning) {
74
+ throw new Error("USE_OWN_LOCAL_BINARY_PROCESS is enabled but BrowserStack Local process is not running. Please start your BrowserStack Local binary process first.");
75
+ }
76
+ logger.info("BrowserStack Local process is running, proceeding with user's own process.");
77
+ return;
78
+ }
70
79
  const localBinary = new Local();
71
80
  await killExistingBrowserStackLocalProcesses();
72
81
  // Use a single options object from config and extend with required fields
73
82
  const bsLocalArgs = {
74
83
  ...(config.browserstackLocalOptions || {}),
75
- key: config.browserstackAccessKey,
76
- username: config.browserstackUsername,
84
+ key: password,
85
+ username,
77
86
  };
78
87
  if (localIdentifier) {
79
88
  bsLocalArgs.localIdentifier = localIdentifier;
@@ -0,0 +1,4 @@
1
+ export type BrowserStackConfig = {
2
+ "browserstack-username": string;
3
+ "browserstack-access-key": string;
4
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { ApiResponse } from "./apiClient.js";
2
+ export declare function sanitizeUrlParam(param: string): string;
3
+ export declare function maybeCompressBase64(base64: string): Promise<string>;
4
+ export declare function assertOkResponse(response: Response | ApiResponse, action: string): Promise<void>;