@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.
- package/README.md +71 -35
- package/dist/config.d.ts +13 -0
- package/dist/config.js +10 -6
- package/dist/index.d.ts +4 -0
- package/dist/index.js +20 -30
- package/dist/lib/api.d.ts +2 -0
- package/dist/lib/api.js +10 -5
- package/dist/lib/apiClient.d.ts +31 -0
- package/dist/lib/apiClient.js +108 -0
- package/dist/lib/constants.d.ts +17 -0
- package/dist/lib/device-cache.d.ts +9 -0
- package/dist/lib/device-cache.js +3 -3
- package/dist/lib/error.d.ts +7 -0
- package/dist/lib/fuzzy.d.ts +1 -0
- package/dist/lib/get-auth.d.ts +2 -0
- package/dist/lib/get-auth.js +8 -0
- package/dist/lib/inmemory-store.d.ts +1 -0
- package/dist/lib/instrumentation.d.ts +4 -0
- package/dist/lib/instrumentation.js +8 -7
- package/dist/lib/local.d.ts +3 -0
- package/dist/lib/local.js +12 -3
- package/dist/lib/types.d.ts +4 -0
- package/dist/lib/types.js +1 -0
- package/dist/lib/utils.d.ts +4 -0
- package/dist/lib/version-resolver.d.ts +6 -0
- package/dist/logger.d.ts +3 -0
- package/dist/logger.js +20 -4
- package/dist/oninitialized.d.ts +2 -0
- package/dist/oninitialized.js +2 -7
- package/dist/server-factory.d.ts +3 -0
- package/dist/server-factory.js +37 -0
- package/dist/tools/accessibility.d.ts +3 -0
- package/dist/tools/accessibility.js +17 -10
- package/dist/tools/accessiblity-utils/accessibility-rag.d.ts +12 -0
- package/dist/tools/accessiblity-utils/accessibility-rag.js +22 -12
- package/dist/tools/accessiblity-utils/report-fetcher.d.ts +8 -0
- package/dist/tools/accessiblity-utils/report-fetcher.js +26 -16
- package/dist/tools/accessiblity-utils/report-parser.d.ts +23 -0
- package/dist/tools/accessiblity-utils/report-parser.js +3 -3
- package/dist/tools/accessiblity-utils/scanner.d.ts +25 -0
- package/dist/tools/accessiblity-utils/scanner.js +46 -24
- package/dist/tools/appautomate-utils/appautomate.d.ts +42 -0
- package/dist/tools/appautomate-utils/appautomate.js +95 -9
- package/dist/tools/appautomate-utils/types.d.ts +5 -0
- package/dist/tools/appautomate-utils/types.js +6 -0
- package/dist/tools/appautomate.d.ts +3 -0
- package/dist/tools/appautomate.js +109 -13
- package/dist/tools/applive-utils/device-search.d.ts +6 -0
- package/dist/tools/applive-utils/start-session.d.ts +15 -0
- package/dist/tools/applive-utils/start-session.js +11 -4
- package/dist/tools/applive-utils/types.d.ts +7 -0
- package/dist/tools/applive-utils/upload-app.d.ts +5 -0
- package/dist/tools/applive-utils/upload-app.js +8 -12
- package/dist/tools/applive-utils/version-utils.d.ts +4 -0
- package/dist/tools/applive.d.ts +13 -0
- package/dist/tools/applive.js +6 -6
- package/dist/tools/automate-utils/fetch-screenshots.d.ts +6 -0
- package/dist/tools/automate-utils/fetch-screenshots.js +16 -12
- package/dist/tools/automate.d.ts +9 -0
- package/dist/tools/automate.js +6 -6
- package/dist/tools/bstack-sdk.d.ts +17 -0
- package/dist/tools/bstack-sdk.js +47 -20
- package/dist/tools/failurelogs-utils/app-automate.d.ts +7 -0
- package/dist/tools/failurelogs-utils/app-automate.js +29 -11
- package/dist/tools/failurelogs-utils/automate.d.ts +6 -0
- package/dist/tools/failurelogs-utils/automate.js +27 -12
- package/dist/tools/failurelogs-utils/utils.d.ts +30 -0
- package/dist/tools/getFailureLogs.d.ts +14 -0
- package/dist/tools/getFailureLogs.js +11 -11
- package/dist/tools/live-utils/desktop-filter.d.ts +2 -0
- package/dist/tools/live-utils/mobile-filter.d.ts +2 -0
- package/dist/tools/live-utils/start-session.d.ts +6 -0
- package/dist/tools/live-utils/start-session.js +18 -5
- package/dist/tools/live-utils/types.d.ts +33 -0
- package/dist/tools/live.d.ts +3 -0
- package/dist/tools/live.js +11 -11
- package/dist/tools/observability.d.ts +5 -0
- package/dist/tools/observability.js +14 -11
- package/dist/tools/sdk-utils/commands.d.ts +3 -0
- package/dist/tools/sdk-utils/commands.js +20 -5
- package/dist/tools/sdk-utils/constants.d.ts +2 -0
- package/dist/tools/sdk-utils/constants.js +284 -160
- package/dist/tools/sdk-utils/instructions.d.ts +6 -0
- package/dist/tools/sdk-utils/instructions.js +28 -6
- package/dist/tools/sdk-utils/percy/constants.d.ts +3 -0
- package/dist/tools/sdk-utils/percy/constants.js +36 -25
- package/dist/tools/sdk-utils/percy/instructions.d.ts +10 -0
- package/dist/tools/sdk-utils/percy/types.d.ts +5 -0
- package/dist/tools/sdk-utils/types.d.ts +39 -0
- package/dist/tools/sdk-utils/types.js +3 -0
- package/dist/tools/selfheal-utils/selfheal.d.ts +11 -0
- package/dist/tools/selfheal-utils/selfheal.js +10 -6
- package/dist/tools/selfheal.d.ts +7 -0
- package/dist/tools/selfheal.js +6 -6
- package/dist/tools/testmanagement-utils/TCG-utils/api.d.ts +34 -0
- package/dist/tools/testmanagement-utils/TCG-utils/api.js +57 -44
- package/dist/tools/testmanagement-utils/TCG-utils/config.d.ts +5 -0
- package/dist/tools/testmanagement-utils/TCG-utils/helpers.d.ts +13 -0
- package/dist/tools/testmanagement-utils/TCG-utils/helpers.js +2 -1
- package/dist/tools/testmanagement-utils/TCG-utils/types.d.ts +26 -0
- package/dist/tools/testmanagement-utils/add-test-result.d.ts +42 -0
- package/dist/tools/testmanagement-utils/add-test-result.js +23 -10
- package/dist/tools/testmanagement-utils/create-lca-steps.d.ts +100 -0
- package/dist/tools/testmanagement-utils/create-lca-steps.js +64 -55
- package/dist/tools/testmanagement-utils/create-project-folder.d.ts +31 -0
- package/dist/tools/testmanagement-utils/create-project-folder.js +31 -21
- package/dist/tools/testmanagement-utils/create-testcase.d.ts +122 -0
- package/dist/tools/testmanagement-utils/create-testcase.js +13 -10
- package/dist/tools/testmanagement-utils/create-testrun.d.ts +82 -0
- package/dist/tools/testmanagement-utils/create-testrun.js +11 -8
- package/dist/tools/testmanagement-utils/list-testcases.d.ts +30 -0
- package/dist/tools/testmanagement-utils/list-testcases.js +9 -7
- package/dist/tools/testmanagement-utils/list-testruns.d.ts +22 -0
- package/dist/tools/testmanagement-utils/list-testruns.js +9 -7
- package/dist/tools/testmanagement-utils/poll-lca-status.d.ts +11 -0
- package/dist/tools/testmanagement-utils/poll-lca-status.js +12 -8
- package/dist/tools/testmanagement-utils/testcase-from-file.d.ts +4 -0
- package/dist/tools/testmanagement-utils/testcase-from-file.js +6 -6
- package/dist/tools/testmanagement-utils/update-testrun.d.ts +40 -0
- package/dist/tools/testmanagement-utils/update-testrun.js +11 -7
- package/dist/tools/testmanagement-utils/upload-file.d.ts +20 -0
- package/dist/tools/testmanagement-utils/upload-file.js +8 -6
- package/dist/tools/testmanagement.d.ts +60 -0
- package/dist/tools/testmanagement.js +51 -53
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
</div>
|
|
14
14
|
|
|
15
|
-
<p align="center">
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
#
|
|
74
|
-
"
|
|
71
|
+
#Port test suite to BrowserStack
|
|
72
|
+
"Setup test suite to run on BrowserStack infra"
|
|
75
73
|
|
|
76
|
-
#
|
|
77
|
-
|
|
74
|
+
#Run tests on BrowserStack
|
|
75
|
+
“Run my tests on BrowserStack”
|
|
78
76
|
|
|
79
|
-
#
|
|
80
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
package/dist/config.d.ts
ADDED
|
@@ -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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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.
|
|
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;
|
package/dist/index.d.ts
ADDED
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
|
|
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
|
-
|
|
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";
|
package/dist/lib/api.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
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
|
|
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 ${
|
|
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
|
|
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>;
|
package/dist/lib/device-cache.js
CHANGED
|
@@ -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
|
|
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,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>;
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import logger from "../logger.js";
|
|
2
|
-
import
|
|
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:
|
|
43
|
+
...(authHeader ? { Authorization: authHeader } : {}),
|
|
43
44
|
},
|
|
44
45
|
timeout: 2000,
|
|
45
46
|
})
|
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:
|
|
76
|
-
username
|
|
84
|
+
key: password,
|
|
85
|
+
username,
|
|
77
86
|
};
|
|
78
87
|
if (localIdentifier) {
|
|
79
88
|
bsLocalArgs.localIdentifier = localIdentifier;
|
|
@@ -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>;
|