@browserstack/mcp-server 1.2.0 → 1.2.2-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +231 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -4
- package/dist/lib/device-cache.d.ts +1 -0
- package/dist/lib/device-cache.js +28 -0
- package/dist/oninitialized.js +4 -1
- package/dist/server-factory.d.ts +24 -2
- package/dist/server-factory.js +56 -23
- package/dist/tools/accessibility.d.ts +1 -1
- package/dist/tools/accessibility.js +4 -2
- package/dist/tools/appautomate-utils/appautomate.d.ts +2 -1
- package/dist/tools/appautomate-utils/appautomate.js +12 -9
- package/dist/tools/appautomate.d.ts +1 -1
- package/dist/tools/appautomate.js +60 -9
- package/dist/tools/applive-utils/start-session.d.ts +2 -1
- package/dist/tools/applive-utils/start-session.js +17 -6
- package/dist/tools/applive.d.ts +3 -2
- package/dist/tools/applive.js +24 -18
- package/dist/tools/automate.d.ts +1 -1
- package/dist/tools/automate.js +3 -1
- package/dist/tools/bstack-sdk.d.ts +1 -1
- package/dist/tools/bstack-sdk.js +3 -1
- package/dist/tools/{getFailureLogs.d.ts → get-failure-logs.d.ts} +1 -1
- package/dist/tools/{getFailureLogs.js → get-failure-logs.js} +4 -5
- package/dist/tools/live.d.ts +1 -1
- package/dist/tools/live.js +17 -5
- package/dist/tools/sdk-utils/constants.js +14 -1
- package/dist/tools/sdk-utils/types.d.ts +2 -1
- package/dist/tools/sdk-utils/types.js +1 -0
- package/dist/tools/selfheal.d.ts +1 -1
- package/dist/tools/selfheal.js +3 -1
- package/dist/tools/testmanagement-utils/create-testcase.d.ts +4 -0
- package/dist/tools/testmanagement-utils/create-testcase.js +6 -0
- package/dist/tools/testmanagement.d.ts +1 -1
- package/dist/tools/testmanagement.js +12 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,13 +25,20 @@
|
|
|
25
25
|
<img src="assets/thumbnail.webp">
|
|
26
26
|
</a>
|
|
27
27
|
</div>
|
|
28
|
+
|
|
28
29
|
|
|
29
30
|
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
31
|
#### Test from anywhere:
|
|
31
32
|
Easily connect the BrowserStack Test Platform to your favourite AI tools, such as IDEs, LLMs, or agentic workflows.
|
|
33
|
+
#### Test with natural language:
|
|
34
|
+
Manage, execute, debug tests, and even fix code using plain English prompts.
|
|
32
35
|
#### Reduced context switching:
|
|
33
36
|
Stay in flow—keep all project context in one place and trigger actions directly from your IDE or LLM.
|
|
34
37
|
|
|
38
|
+
## ⚡️ One Click MCP Setup
|
|
39
|
+
|
|
40
|
+
[](http://mcp.browserstack.com/one-click-setup?client=vscode) [](http://mcp.browserstack.com/one-click-setup?client=cursor)
|
|
41
|
+
|
|
35
42
|
## 💡 Usage Examples
|
|
36
43
|
|
|
37
44
|
### 📱 Manual App Testing
|
|
@@ -115,7 +122,7 @@ Create and manage test cases, create test plans and trigger test runs using natu
|
|
|
115
122
|
"update test results as passed for Login tests test run from My Demo Project"
|
|
116
123
|
```
|
|
117
124
|
|
|
118
|
-
### 🧪 Access BrowserStack AI
|
|
125
|
+
### 🧪 Access BrowserStack AI agents
|
|
119
126
|
|
|
120
127
|
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
128
|
|
|
@@ -135,6 +142,13 @@ Generate test cases from PRDs, convert manual tests to low-code automation, and
|
|
|
135
142
|
|
|
136
143
|
## 🛠️ Installation
|
|
137
144
|
|
|
145
|
+
### **One Click MCP Setup**
|
|
146
|
+
|
|
147
|
+
[](http://mcp.browserstack.com/one-click-setup?client=vscode) [](http://mcp.browserstack.com/one-click-setup?client=cursor)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
### **Alternate ways to Setup MCP server**
|
|
151
|
+
|
|
138
152
|
1. **Create a BrowserStack Account**
|
|
139
153
|
|
|
140
154
|
- Sign up for [BrowserStack](https://www.browserstack.com/users/sign_up) if you don't have an account already.
|
|
@@ -145,9 +159,21 @@ Generate test cases from PRDs, convert manual tests to low-code automation, and
|
|
|
145
159
|
- Once you have an account (and purchased appropriate plan), note down your `username` and `access_key` from [Account Settings](https://www.browserstack.com/accounts/profile/details).
|
|
146
160
|
|
|
147
161
|
2. Ensure you are using Node version >= `18.0`. Check your node version using `node --version`. Recommended version: `v22.15.0` (LTS)
|
|
162
|
+
|
|
148
163
|
3. **Install the MCP Server**
|
|
149
164
|
|
|
150
165
|
- VSCode (Copilot - Agent Mode): `.vscode/mcp.json`:
|
|
166
|
+
|
|
167
|
+
- Locate or Create the Configuration File:
|
|
168
|
+
In the root directory of your project, look for a folder named .vscode. This folder is usually hidden so you will need to find it as mentioned in the expand.
|
|
169
|
+
|
|
170
|
+
- If this folder doesn't exist, create it.
|
|
171
|
+
|
|
172
|
+
- Inside the .vscode folder, create a new file named mcp.json
|
|
173
|
+
|
|
174
|
+
- Add the Configuration: Open the mcp.json file and then add the following JSON content.
|
|
175
|
+
|
|
176
|
+
- Replace the username and <access_key> with your BrowserStack credentials.
|
|
151
177
|
|
|
152
178
|
```json
|
|
153
179
|
{
|
|
@@ -167,6 +193,30 @@ Generate test cases from PRDs, convert manual tests to low-code automation, and
|
|
|
167
193
|
- In VSCode, make sure to click on `Start` button in the MCP Server to start the server.
|
|
168
194
|

|
|
169
195
|
|
|
196
|
+
|
|
197
|
+
#### ** Alternate way to setup MCP on VSCode Copilot
|
|
198
|
+
|
|
199
|
+
1.Click on the gear icon to Select Tools
|
|
200
|
+
<div align="center">
|
|
201
|
+
<img src="assets/select_tools.png" alt="Select Tools" height="100">
|
|
202
|
+
</div>
|
|
203
|
+
2. A tool menu would appear at the top-centre, scroll down on the menu at the top and then Click on Add MCP Server
|
|
204
|
+
<div align="center">
|
|
205
|
+
<img src="assets/add_mcp_server.png" alt="Add MCP Server" height="100">
|
|
206
|
+
</div>
|
|
207
|
+
3. Select NPM package option (Install fron an NPM package) - 3rd in the list
|
|
208
|
+
<div align="center">
|
|
209
|
+
<img src="assets/select_npm_package.png" alt="Select NPM Package" height="100">
|
|
210
|
+
</div>
|
|
211
|
+
4. Enter NPM Package Name (@browserstack/mcp-server)
|
|
212
|
+
<div align="center">
|
|
213
|
+
<img src="assets/enter_npm_package.png" alt="Enter NPM Package" height="100">
|
|
214
|
+
</div>
|
|
215
|
+
5. Enter browserstack user name and access key
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
|
|
170
220
|
* For Cursor: `.cursor/mcp.json`:
|
|
171
221
|
|
|
172
222
|
```json
|
|
@@ -228,6 +278,186 @@ To install BrowserStack Test Platform Server for Claude Desktop automatically vi
|
|
|
228
278
|
npx -y @smithery/cli install @browserstack/mcp-server --client claude
|
|
229
279
|
```
|
|
230
280
|
|
|
281
|
+
|
|
282
|
+
### 💡 List of BrowserStack MCP Tools
|
|
283
|
+
|
|
284
|
+
As of now we support 20 tools.
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## 🧾 Test Management
|
|
290
|
+
|
|
291
|
+
1. `createProjectOrFolder` — Create a Test Management project and/or folders to organize test cases. Returns with Folder ID, Project ID and Test Management Link to access the TM Project Dashboard.
|
|
292
|
+
**Prompt example**
|
|
293
|
+
|
|
294
|
+
```text
|
|
295
|
+
Create a new Test Management project named 'Shopping App' with two folders - Login and Checkout
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
2. `createTestCase` — Add a manual test case under a specific project/folder (uses project identifier like PR-xxxxx and a folder ID).
|
|
300
|
+
**Prompt example**
|
|
301
|
+
|
|
302
|
+
```text
|
|
303
|
+
Add a test case named 'Invalid Login Scenario' to the Login folder in the 'Shopping App' project with PR-53617, Folder ID: 117869
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
3. `listTestCases` — List test cases for a project (supports filters like priority, status, tags).
|
|
307
|
+
**Prompt example**
|
|
308
|
+
|
|
309
|
+
```text
|
|
310
|
+
List all high-priority test cases in the 'Shopping App' project with project_identifier: PR-59457
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
4. `createTestRun` — Create a test run (suite) for selected test cases in a project.
|
|
314
|
+
**Prompt example**
|
|
315
|
+
|
|
316
|
+
```text
|
|
317
|
+
Create a test run for the Login folder in the 'Shopping App' project and name it 'Release v1.0 Login Flow'
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
5. `listTestRuns` — List test runs for a project (filter by dates, assignee, state).
|
|
321
|
+
**Prompt example**
|
|
322
|
+
|
|
323
|
+
```text
|
|
324
|
+
List all test runs from the 'Shopping App' project that were executed last week and are currently marked in-progress
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
6. `updateTestRun` — Partially update a test run (status, tags, notes, associated test cases).
|
|
328
|
+
**Prompt example**
|
|
329
|
+
|
|
330
|
+
```text
|
|
331
|
+
Update test run ID 1043 in the 'Shopping App' project and mark it as complete with the note 'Regression cycle done'
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
7. `addTestResult` — Add a manual execution result (passed/failed/blocked/skipped) for a test case within a run.
|
|
335
|
+
**Prompt example**
|
|
336
|
+
|
|
337
|
+
```text
|
|
338
|
+
Mark the test case 'Invalid Login Scenario' as passed in test run ID 1043 of the 'Shopping App' project
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
8. `createTestCasesFromFile` — Bulk-create test cases from an uploaded file (e.g., PDF).
|
|
342
|
+
**Prompt example**
|
|
343
|
+
|
|
344
|
+
```text
|
|
345
|
+
Upload test cases from '/Users/xyz/testcases.pdf' to the 'Shopping App' project in Test Management
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## ⚙️ BrowserStack SDK Setup / Automate Test
|
|
351
|
+
|
|
352
|
+
9. `setupBrowserStackAutomateTests` — Integrate BrowserStack SDK and run web tests on BrowserStack (optionally enable Percy).
|
|
353
|
+
**Prompt example**
|
|
354
|
+
|
|
355
|
+
```text
|
|
356
|
+
Run my Selenium-JUnit5 tests written in Java on Chrome and Firefox. Enable Percy for visual testing.
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
10. `fetchAutomationScreenshots` — Fetch screenshots captured during a given Automate/App Automate session.
|
|
360
|
+
**Prompt example**
|
|
361
|
+
|
|
362
|
+
```text
|
|
363
|
+
Get screenshots from Automate session ID abc123xyz for my desktop test run
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## 🔍 Observability
|
|
369
|
+
|
|
370
|
+
11. `getFailureLogs` — Retrieve error logs for Automate/App Automate sessions (optionally by Build ID for App Automate).
|
|
371
|
+
**Prompt example**
|
|
372
|
+
|
|
373
|
+
```text
|
|
374
|
+
Get the error logs from the session ID: 21a864032a7459f1e7634222249b316759d6827f, Build ID: dt7ung4wmjittzff8kksrjadjax9gzvbscoyf9qn of App Automate test session
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## 📱 App Live
|
|
380
|
+
|
|
381
|
+
12. `runAppLiveSession` — Start a manual app testing session on a real device in the cloud.
|
|
382
|
+
**Prompt example**
|
|
383
|
+
|
|
384
|
+
```text
|
|
385
|
+
Open my app on iPhone 15 Pro Max with iOS 17. App path is /Users/xyz/app.ipa
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## 💻 Live
|
|
391
|
+
|
|
392
|
+
13. `runBrowserLiveSession` — Start a Live session for website testing on desktop or mobile browsers.
|
|
393
|
+
**Prompt example**
|
|
394
|
+
|
|
395
|
+
```text
|
|
396
|
+
Open www.google.com on the latest version of Microsoft Edge on Windows 11
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## 📲 App Automate
|
|
402
|
+
|
|
403
|
+
14. `takeAppScreenshot` — Launch the app on a specified device and captures a quick verification screenshot. This tool is just to verify whether your app has been launched.
|
|
404
|
+
**Prompt example**
|
|
405
|
+
|
|
406
|
+
```text
|
|
407
|
+
Take a screenshot of my app on Google Pixel 6 with Android 14 while testing on App Automate. App file path: /Users/xyz/app-debug.apk
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
15. `runAppTestsOnBrowserStack` — Run automated mobile tests (Espresso/XCUITest, etc.) on real devices.
|
|
411
|
+
**Prompt example**
|
|
412
|
+
|
|
413
|
+
```text
|
|
414
|
+
Run Espresso tests from /tests/checkout.zip on Galaxy S21 and Pixel 6 with Android 14. App path is /apps/beta-release.apk under project 'Checkout Flow'
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## ♿ Accessibility
|
|
420
|
+
|
|
421
|
+
16. `accessibilityExpert` — Ask A11y Expert (WCAG 2.0/2.1/2.2, mobile/web usability, best practices).
|
|
422
|
+
**Prompt example**
|
|
423
|
+
|
|
424
|
+
```text
|
|
425
|
+
What WCAG guidelines apply to form field error messages on mobile web?
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
17. `startAccessibilityScan` — Start a web accessibility scan and return the result link.
|
|
429
|
+
**Prompt example**
|
|
430
|
+
|
|
431
|
+
```text
|
|
432
|
+
Run accessibility scan for "www.example.com"
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## 🤖 BrowserStack AI Agents
|
|
438
|
+
|
|
439
|
+
18. `fetchSelfHealedSelectors` — Retrieve AI self-healed selectors to fix flaky tests due to DOM changes.
|
|
440
|
+
**Prompt example**
|
|
441
|
+
|
|
442
|
+
```text
|
|
443
|
+
Fetch and fix flaky test selectors in Automate session ID session_9482 using MCP
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
19. `createLCASteps` — Generate Low Code Automation steps from a manual test case in Test Management.
|
|
447
|
+
**Prompt example**
|
|
448
|
+
|
|
449
|
+
```text
|
|
450
|
+
Convert the manual test case 'Add to Cart' in the 'Shopping App' project into LCA steps
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
20. `uploadProductRequirementFile` — Upload a PRD/screenshot/PDF and get a file mapping ID (used with `createTestCasesFromFile`).
|
|
454
|
+
**Prompt example**
|
|
455
|
+
|
|
456
|
+
```text
|
|
457
|
+
Upload PRD from /Users/xyz/Desktop/login-flow.pdf and use BrowserStack AI to generate test cases
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
|
|
231
461
|
## 🤝 Recommended MCP Clients
|
|
232
462
|
|
|
233
463
|
- We recommend using **Github Copilot or Cursor** for automated testing + debugging use cases.
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ const require = createRequire(import.meta.url);
|
|
|
5
5
|
const packageJson = require("../package.json");
|
|
6
6
|
import "dotenv/config";
|
|
7
7
|
import logger from "./logger.js";
|
|
8
|
-
import {
|
|
8
|
+
import { BrowserStackMcpServer } from "./server-factory.js";
|
|
9
9
|
async function main() {
|
|
10
10
|
logger.info("Launching BrowserStack MCP server, version %s", packageJson.version);
|
|
11
11
|
const remoteMCP = process.env.REMOTE_MCP === "true";
|
|
@@ -22,16 +22,17 @@ async function main() {
|
|
|
22
22
|
throw new Error("BROWSERSTACK_ACCESS_KEY environment variable is required");
|
|
23
23
|
}
|
|
24
24
|
const transport = new StdioServerTransport();
|
|
25
|
-
const
|
|
25
|
+
const mcpServer = new BrowserStackMcpServer({
|
|
26
26
|
"browserstack-username": username,
|
|
27
27
|
"browserstack-access-key": accessKey,
|
|
28
28
|
});
|
|
29
|
-
await
|
|
29
|
+
await mcpServer.getInstance().connect(transport);
|
|
30
30
|
}
|
|
31
31
|
main().catch(console.error);
|
|
32
32
|
// Ensure logs are flushed before exit
|
|
33
33
|
process.on("exit", () => {
|
|
34
34
|
logger.flush();
|
|
35
35
|
});
|
|
36
|
-
export { createMcpServer } from "./server-factory.js";
|
|
37
36
|
export { setLogger } from "./logger.js";
|
|
37
|
+
export { BrowserStackMcpServer } from "./server-factory.js";
|
|
38
|
+
export { trackMCP } from "./lib/instrumentation.js";
|
|
@@ -7,3 +7,4 @@ export declare enum BrowserStackProducts {
|
|
|
7
7
|
* Fetches and caches BrowserStack datasets (live + app_live + app_automate) with a shared TTL.
|
|
8
8
|
*/
|
|
9
9
|
export declare function getDevicesAndBrowsers(type: BrowserStackProducts): Promise<any>;
|
|
10
|
+
export declare function shouldSendStartedEvent(): boolean;
|
package/dist/lib/device-cache.js
CHANGED
|
@@ -2,9 +2,11 @@ import fs from "fs";
|
|
|
2
2
|
import os from "os";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { apiClient } from "./apiClient.js";
|
|
5
|
+
import config from "../config.js";
|
|
5
6
|
const CACHE_DIR = path.join(os.homedir(), ".browserstack", "combined_cache");
|
|
6
7
|
const CACHE_FILE = path.join(CACHE_DIR, "data.json");
|
|
7
8
|
const TTL_MS = 24 * 60 * 60 * 1000; // 1 day
|
|
9
|
+
const TTL_STARTED_MS = 3 * 60 * 60 * 1000; // 3 Hours
|
|
8
10
|
export var BrowserStackProducts;
|
|
9
11
|
(function (BrowserStackProducts) {
|
|
10
12
|
BrowserStackProducts["LIVE"] = "live";
|
|
@@ -49,3 +51,29 @@ export async function getDevicesAndBrowsers(type) {
|
|
|
49
51
|
fs.writeFileSync(CACHE_FILE, JSON.stringify(cache), "utf8");
|
|
50
52
|
return cache[type];
|
|
51
53
|
}
|
|
54
|
+
// Rate limiter for started event (3H)
|
|
55
|
+
export function shouldSendStartedEvent() {
|
|
56
|
+
try {
|
|
57
|
+
if (config && config.REMOTE_MCP) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (!fs.existsSync(CACHE_DIR)) {
|
|
61
|
+
fs.mkdirSync(CACHE_DIR, { recursive: true });
|
|
62
|
+
}
|
|
63
|
+
let cache = {};
|
|
64
|
+
if (fs.existsSync(CACHE_FILE)) {
|
|
65
|
+
const raw = fs.readFileSync(CACHE_FILE, "utf8");
|
|
66
|
+
cache = JSON.parse(raw || "{}");
|
|
67
|
+
const last = parseInt(cache.lastStartedEvent, 10);
|
|
68
|
+
if (!isNaN(last) && Date.now() - last < TTL_STARTED_MS) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
cache.lastStartedEvent = Date.now();
|
|
73
|
+
fs.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2), "utf8");
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
package/dist/oninitialized.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { trackMCP } from "./lib/instrumentation.js";
|
|
2
|
+
import { shouldSendStartedEvent } from "./lib/device-cache.js";
|
|
2
3
|
export function setupOnInitialized(server, config) {
|
|
3
4
|
const nodeVersion = process.versions.node;
|
|
4
5
|
// Check for Node.js version
|
|
@@ -6,6 +7,8 @@ export function setupOnInitialized(server, config) {
|
|
|
6
7
|
throw new Error("Node version is not supported. Please upgrade to 18.0.0 or later.");
|
|
7
8
|
}
|
|
8
9
|
server.server.oninitialized = () => {
|
|
9
|
-
|
|
10
|
+
if (shouldSendStartedEvent()) {
|
|
11
|
+
trackMCP("started", server.server.getClientVersion(), undefined, config);
|
|
12
|
+
}
|
|
10
13
|
};
|
|
11
14
|
}
|
package/dist/server-factory.d.ts
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1
|
+
import { McpServer, RegisteredTool } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { BrowserStackConfig } from "./lib/types.js";
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Wrapper class for BrowserStack MCP Server
|
|
5
|
+
* Stores a map of registered tools by name
|
|
6
|
+
*/
|
|
7
|
+
export declare class BrowserStackMcpServer {
|
|
8
|
+
private config;
|
|
9
|
+
server: McpServer;
|
|
10
|
+
tools: Record<string, RegisteredTool>;
|
|
11
|
+
constructor(config: BrowserStackConfig);
|
|
12
|
+
/**
|
|
13
|
+
* Calls each tool-adder function and collects their returned tools
|
|
14
|
+
*/
|
|
15
|
+
private registerTools;
|
|
16
|
+
/**
|
|
17
|
+
* Expose the underlying MCP server instance
|
|
18
|
+
*/
|
|
19
|
+
getInstance(): McpServer;
|
|
20
|
+
/**
|
|
21
|
+
* Get all registered tools
|
|
22
|
+
*/
|
|
23
|
+
getTools(): Record<string, RegisteredTool>;
|
|
24
|
+
getTool(name: string): RegisteredTool | undefined;
|
|
25
|
+
}
|
package/dist/server-factory.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1
|
+
import { McpServer, } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { createRequire } from "module";
|
|
3
3
|
const require = createRequire(import.meta.url);
|
|
4
4
|
const packageJson = require("../package.json");
|
|
@@ -8,30 +8,63 @@ import addBrowserLiveTools from "./tools/live.js";
|
|
|
8
8
|
import addAccessibilityTools from "./tools/accessibility.js";
|
|
9
9
|
import addTestManagementTools from "./tools/testmanagement.js";
|
|
10
10
|
import addAppAutomationTools from "./tools/appautomate.js";
|
|
11
|
-
import addFailureLogsTools from "./tools/
|
|
11
|
+
import addFailureLogsTools from "./tools/get-failure-logs.js";
|
|
12
12
|
import addAutomateTools from "./tools/automate.js";
|
|
13
13
|
import addSelfHealTools from "./tools/selfheal.js";
|
|
14
14
|
import addAppLiveTools from "./tools/applive.js";
|
|
15
15
|
import { setupOnInitialized } from "./oninitialized.js";
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Wrapper class for BrowserStack MCP Server
|
|
18
|
+
* Stores a map of registered tools by name
|
|
19
|
+
*/
|
|
20
|
+
export class BrowserStackMcpServer {
|
|
21
|
+
config;
|
|
22
|
+
server;
|
|
23
|
+
tools = {};
|
|
24
|
+
constructor(config) {
|
|
25
|
+
this.config = config;
|
|
26
|
+
logger.info("Creating BrowserStack MCP Server, version %s", packageJson.version);
|
|
27
|
+
this.server = new McpServer({
|
|
28
|
+
name: "BrowserStack MCP Server",
|
|
29
|
+
version: packageJson.version,
|
|
30
|
+
});
|
|
31
|
+
setupOnInitialized(this.server, this.config);
|
|
32
|
+
this.registerTools();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Calls each tool-adder function and collects their returned tools
|
|
36
|
+
*/
|
|
37
|
+
registerTools() {
|
|
38
|
+
const toolAdders = [
|
|
39
|
+
addAccessibilityTools,
|
|
40
|
+
addSDKTools,
|
|
41
|
+
addAppLiveTools,
|
|
42
|
+
addBrowserLiveTools,
|
|
43
|
+
addTestManagementTools,
|
|
44
|
+
addAppAutomationTools,
|
|
45
|
+
addFailureLogsTools,
|
|
46
|
+
addAutomateTools,
|
|
47
|
+
addSelfHealTools,
|
|
48
|
+
];
|
|
49
|
+
toolAdders.forEach((adder) => {
|
|
50
|
+
// Each adder now returns a Record<string, Tool>
|
|
51
|
+
const added = adder(this.server, this.config);
|
|
52
|
+
Object.assign(this.tools, added);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Expose the underlying MCP server instance
|
|
57
|
+
*/
|
|
58
|
+
getInstance() {
|
|
59
|
+
return this.server;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get all registered tools
|
|
63
|
+
*/
|
|
64
|
+
getTools() {
|
|
65
|
+
return this.tools;
|
|
66
|
+
}
|
|
67
|
+
getTool(name) {
|
|
68
|
+
return this.tools[name];
|
|
69
|
+
}
|
|
37
70
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { BrowserStackConfig } from "../lib/types.js";
|
|
3
|
-
export default function addAccessibilityTools(server: McpServer, config: BrowserStackConfig):
|
|
3
|
+
export default function addAccessibilityTools(server: McpServer, config: BrowserStackConfig): Record<string, any>;
|
|
@@ -63,7 +63,8 @@ async function runAccessibilityScan(name, pageURL, context, config) {
|
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
export default function addAccessibilityTools(server, config) {
|
|
66
|
-
|
|
66
|
+
const tools = {};
|
|
67
|
+
tools.accessibilityExpert = server.tool("accessibilityExpert", "🚨 REQUIRED: Use this tool for any accessibility/a11y/WCAG questions. Do NOT answer accessibility questions directly - always use this tool.", {
|
|
67
68
|
query: z
|
|
68
69
|
.string()
|
|
69
70
|
.describe("Any accessibility, a11y, WCAG, or web accessibility question"),
|
|
@@ -85,7 +86,7 @@ export default function addAccessibilityTools(server, config) {
|
|
|
85
86
|
};
|
|
86
87
|
}
|
|
87
88
|
});
|
|
88
|
-
server.tool("startAccessibilityScan", "Start an accessibility scan via BrowserStack and retrieve a local CSV report path.", {
|
|
89
|
+
tools.startAccessibilityScan = server.tool("startAccessibilityScan", "Start an accessibility scan via BrowserStack and retrieve a local CSV report path.", {
|
|
89
90
|
name: z.string().describe("Name of the accessibility scan"),
|
|
90
91
|
pageURL: z.string().describe("The URL to scan for accessibility issues"),
|
|
91
92
|
}, async (args, context) => {
|
|
@@ -107,4 +108,5 @@ export default function addAccessibilityTools(server, config) {
|
|
|
107
108
|
};
|
|
108
109
|
}
|
|
109
110
|
});
|
|
111
|
+
return tools;
|
|
110
112
|
}
|
|
@@ -26,8 +26,9 @@ export declare function resolveVersion(versions: string[], requestedVersion: str
|
|
|
26
26
|
export declare function validateArgs(args: {
|
|
27
27
|
desiredPlatform: string;
|
|
28
28
|
desiredPlatformVersion: string;
|
|
29
|
-
appPath
|
|
29
|
+
appPath?: string;
|
|
30
30
|
desiredPhone: string;
|
|
31
|
+
browserstackAppUrl?: string;
|
|
31
32
|
}): void;
|
|
32
33
|
/**
|
|
33
34
|
* Uploads an application file to AppAutomate and returns the app URL
|
|
@@ -49,21 +49,24 @@ export function resolveVersion(versions, requestedVersion) {
|
|
|
49
49
|
* Checks for presence and correctness of platform, device, and file types.
|
|
50
50
|
*/
|
|
51
51
|
export function validateArgs(args) {
|
|
52
|
-
const { desiredPlatform, desiredPlatformVersion, appPath, desiredPhone } = args;
|
|
52
|
+
const { desiredPlatform, desiredPlatformVersion, appPath, desiredPhone, browserstackAppUrl, } = args;
|
|
53
53
|
if (!desiredPlatform || !desiredPhone) {
|
|
54
54
|
throw new Error("Missing required arguments: desiredPlatform and desiredPhone are required");
|
|
55
55
|
}
|
|
56
56
|
if (!desiredPlatformVersion) {
|
|
57
57
|
throw new Error("Missing required arguments: desiredPlatformVersion is required");
|
|
58
58
|
}
|
|
59
|
-
if (!appPath) {
|
|
60
|
-
throw new Error("
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
if (!appPath && !browserstackAppUrl) {
|
|
60
|
+
throw new Error("Either appPath or browserstackAppUrl must be provided");
|
|
61
|
+
}
|
|
62
|
+
// Only validate app path format if appPath is provided
|
|
63
|
+
if (appPath) {
|
|
64
|
+
if (desiredPlatform === "android" && !appPath.endsWith(".apk")) {
|
|
65
|
+
throw new Error("You must provide a valid Android app path (.apk).");
|
|
66
|
+
}
|
|
67
|
+
if (desiredPlatform === "ios" && !appPath.endsWith(".ipa")) {
|
|
68
|
+
throw new Error("You must provide a valid iOS app path (.ipa).");
|
|
69
|
+
}
|
|
67
70
|
}
|
|
68
71
|
}
|
|
69
72
|
/**
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { BrowserStackConfig } from "../lib/types.js";
|
|
3
|
-
export default function addAppAutomationTools(server: McpServer, config: BrowserStackConfig):
|
|
3
|
+
export default function addAppAutomationTools(server: McpServer, config: BrowserStackConfig): Record<string, any>;
|
|
@@ -19,7 +19,7 @@ async function takeAppScreenshot(args) {
|
|
|
19
19
|
let driver;
|
|
20
20
|
try {
|
|
21
21
|
validateArgs(args);
|
|
22
|
-
const { desiredPlatform, desiredPhone, appPath, config } = args;
|
|
22
|
+
const { desiredPlatform, desiredPhone, appPath, browserstackAppUrl, config, } = args;
|
|
23
23
|
let { desiredPlatformVersion } = args;
|
|
24
24
|
const platforms = (await getDevicesAndBrowsers(BrowserStackProducts.APP_AUTOMATE)).mobile;
|
|
25
25
|
const platformData = platforms.find((p) => p.os === desiredPlatform.toLowerCase());
|
|
@@ -35,8 +35,18 @@ async function takeAppScreenshot(args) {
|
|
|
35
35
|
}
|
|
36
36
|
const authString = getBrowserStackAuth(config);
|
|
37
37
|
const [username, password] = authString.split(":");
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
let app_url;
|
|
39
|
+
if (browserstackAppUrl) {
|
|
40
|
+
app_url = browserstackAppUrl;
|
|
41
|
+
logger.info(`Using provided BrowserStack app URL: ${app_url}`);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
if (!appPath) {
|
|
45
|
+
throw new Error("appPath is required when browserstackAppUrl is not provided");
|
|
46
|
+
}
|
|
47
|
+
app_url = await uploadApp(appPath, username, password);
|
|
48
|
+
logger.info(`App uploaded. URL: ${app_url}`);
|
|
49
|
+
}
|
|
40
50
|
const capabilities = {
|
|
41
51
|
platformName: desiredPlatform,
|
|
42
52
|
"appium:platformVersion": selectedDevice.os_version,
|
|
@@ -89,11 +99,34 @@ async function takeAppScreenshot(args) {
|
|
|
89
99
|
}
|
|
90
100
|
//Runs AppAutomate tests on BrowserStack by uploading app and test suite, then triggering a test run.
|
|
91
101
|
async function runAppTestsOnBrowserStack(args, config) {
|
|
102
|
+
// Validate that either paths or URLs are provided for both app and test suite
|
|
103
|
+
if (!args.browserstackAppUrl && !args.appPath) {
|
|
104
|
+
throw new Error("appPath is required when browserstackAppUrl is not provided");
|
|
105
|
+
}
|
|
106
|
+
if (!args.browserstackTestSuiteUrl && !args.testSuitePath) {
|
|
107
|
+
throw new Error("testSuitePath is required when browserstackTestSuiteUrl is not provided");
|
|
108
|
+
}
|
|
92
109
|
switch (args.detectedAutomationFramework) {
|
|
93
110
|
case AppTestPlatform.ESPRESSO: {
|
|
94
111
|
try {
|
|
95
|
-
|
|
96
|
-
|
|
112
|
+
let app_url;
|
|
113
|
+
if (args.browserstackAppUrl) {
|
|
114
|
+
app_url = args.browserstackAppUrl;
|
|
115
|
+
logger.info(`Using provided BrowserStack app URL: ${app_url}`);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
app_url = await uploadEspressoApp(args.appPath, config);
|
|
119
|
+
logger.info(`App uploaded. URL: ${app_url}`);
|
|
120
|
+
}
|
|
121
|
+
let test_suite_url;
|
|
122
|
+
if (args.browserstackTestSuiteUrl) {
|
|
123
|
+
test_suite_url = args.browserstackTestSuiteUrl;
|
|
124
|
+
logger.info(`Using provided BrowserStack test suite URL: ${test_suite_url}`);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
test_suite_url = await uploadEspressoTestSuite(args.testSuitePath, config);
|
|
128
|
+
logger.info(`Test suite uploaded. URL: ${test_suite_url}`);
|
|
129
|
+
}
|
|
97
130
|
const build_id = await triggerEspressoBuild(app_url, test_suite_url, args.devices, args.project);
|
|
98
131
|
return {
|
|
99
132
|
content: [
|
|
@@ -111,8 +144,24 @@ async function runAppTestsOnBrowserStack(args, config) {
|
|
|
111
144
|
}
|
|
112
145
|
case AppTestPlatform.XCUITEST: {
|
|
113
146
|
try {
|
|
114
|
-
|
|
115
|
-
|
|
147
|
+
let app_url;
|
|
148
|
+
if (args.browserstackAppUrl) {
|
|
149
|
+
app_url = args.browserstackAppUrl;
|
|
150
|
+
logger.info(`Using provided BrowserStack app URL: ${app_url}`);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
app_url = await uploadXcuiApp(args.appPath, config);
|
|
154
|
+
logger.info(`App uploaded. URL: ${app_url}`);
|
|
155
|
+
}
|
|
156
|
+
let test_suite_url;
|
|
157
|
+
if (args.browserstackTestSuiteUrl) {
|
|
158
|
+
test_suite_url = args.browserstackTestSuiteUrl;
|
|
159
|
+
logger.info(`Using provided BrowserStack test suite URL: ${test_suite_url}`);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
test_suite_url = await uploadXcuiTestSuite(args.testSuitePath, config);
|
|
163
|
+
logger.info(`Test suite uploaded. URL: ${test_suite_url}`);
|
|
164
|
+
}
|
|
116
165
|
const build_id = await triggerXcuiBuild(app_url, test_suite_url, args.devices, args.project, config);
|
|
117
166
|
return {
|
|
118
167
|
content: [
|
|
@@ -133,7 +182,8 @@ async function runAppTestsOnBrowserStack(args, config) {
|
|
|
133
182
|
}
|
|
134
183
|
}
|
|
135
184
|
export default function addAppAutomationTools(server, config) {
|
|
136
|
-
|
|
185
|
+
const tools = {};
|
|
186
|
+
tools.takeAppScreenshot = server.tool("takeAppScreenshot", "Use this tool to take a screenshot of an app running on a BrowserStack device. This is useful for visual testing and debugging.", {
|
|
137
187
|
desiredPhone: z
|
|
138
188
|
.string()
|
|
139
189
|
.describe("The full name of the device to run the app on. Example: 'iPhone 12 Pro' or 'Samsung Galaxy S20'. Always ask the user for the device they want to use."),
|
|
@@ -164,7 +214,7 @@ export default function addAppAutomationTools(server, config) {
|
|
|
164
214
|
};
|
|
165
215
|
}
|
|
166
216
|
});
|
|
167
|
-
server.tool("runAppTestsOnBrowserStack", "Run AppAutomate tests on BrowserStack by uploading app and test suite. If running from Android Studio or Xcode, the tool will help export app and test files automatically. For other environments, you'll need to provide the paths to your pre-built app and test files.", {
|
|
217
|
+
tools.runAppTestsOnBrowserStack = server.tool("runAppTestsOnBrowserStack", "Run AppAutomate tests on BrowserStack by uploading app and test suite. If running from Android Studio or Xcode, the tool will help export app and test files automatically. For other environments, you'll need to provide the paths to your pre-built app and test files.", {
|
|
168
218
|
appPath: z
|
|
169
219
|
.string()
|
|
170
220
|
.describe("Path to your application file:\n" +
|
|
@@ -215,4 +265,5 @@ export default function addAppAutomationTools(server, config) {
|
|
|
215
265
|
};
|
|
216
266
|
}
|
|
217
267
|
});
|
|
268
|
+
return tools;
|
|
218
269
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { BrowserStackConfig } from "../../lib/types.js";
|
|
2
2
|
interface StartSessionArgs {
|
|
3
|
-
appPath
|
|
3
|
+
appPath?: string;
|
|
4
4
|
desiredPlatform: "android" | "ios";
|
|
5
5
|
desiredPhone: string;
|
|
6
6
|
desiredPlatformVersion: string;
|
|
7
|
+
browserstackAppUrl?: string;
|
|
7
8
|
}
|
|
8
9
|
interface StartSessionOptions {
|
|
9
10
|
config: BrowserStackConfig;
|
|
@@ -11,7 +11,7 @@ import envConfig from "../../config.js";
|
|
|
11
11
|
* Start an App Live session: filter, select, upload, and open.
|
|
12
12
|
*/
|
|
13
13
|
export async function startSession(args, options) {
|
|
14
|
-
const { appPath, desiredPlatform, desiredPhone, desiredPlatformVersion } = args;
|
|
14
|
+
const { appPath, desiredPlatform, desiredPhone, desiredPlatformVersion, browserstackAppUrl, } = args;
|
|
15
15
|
const { config } = options;
|
|
16
16
|
// 1) Fetch devices for APP_LIVE
|
|
17
17
|
const data = await getDevicesAndBrowsers(BrowserStackProducts.APP_LIVE);
|
|
@@ -38,11 +38,22 @@ export async function startSession(args, options) {
|
|
|
38
38
|
desiredPlatformVersion !== "oldest") {
|
|
39
39
|
note = `\n Note: The requested version "${desiredPlatformVersion}" is not available. Using "${version}" instead.`;
|
|
40
40
|
}
|
|
41
|
-
// 6) Upload app
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
// 6) Upload app or use provided URL
|
|
42
|
+
let app_url;
|
|
43
|
+
if (browserstackAppUrl) {
|
|
44
|
+
app_url = browserstackAppUrl;
|
|
45
|
+
logger.info(`Using provided BrowserStack app URL: ${app_url}`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
if (!appPath) {
|
|
49
|
+
throw new Error("appPath is required when browserstackAppUrl is not provided");
|
|
50
|
+
}
|
|
51
|
+
const authString = getBrowserStackAuth(config);
|
|
52
|
+
const [username, password] = authString.split(":");
|
|
53
|
+
const result = await uploadApp(appPath, username, password);
|
|
54
|
+
app_url = result.app_url;
|
|
55
|
+
logger.info(`App uploaded: ${app_url}`);
|
|
56
|
+
}
|
|
46
57
|
if (!app_url) {
|
|
47
58
|
throw new Error("Failed to upload app. Please try again.");
|
|
48
59
|
}
|
package/dist/tools/applive.d.ts
CHANGED
|
@@ -7,7 +7,8 @@ import { BrowserStackConfig } from "../lib/types.js";
|
|
|
7
7
|
export declare function startAppLiveSession(args: {
|
|
8
8
|
desiredPlatform: string;
|
|
9
9
|
desiredPlatformVersion: string;
|
|
10
|
-
appPath
|
|
10
|
+
appPath?: string;
|
|
11
11
|
desiredPhone: string;
|
|
12
|
+
browserstackAppUrl?: string;
|
|
12
13
|
}, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
13
|
-
export default function addAppLiveTools(server: McpServer, config: BrowserStackConfig):
|
|
14
|
+
export default function addAppLiveTools(server: McpServer, config: BrowserStackConfig): Record<string, any>;
|
package/dist/tools/applive.js
CHANGED
|
@@ -10,34 +10,38 @@ export async function startAppLiveSession(args, config) {
|
|
|
10
10
|
if (!args.desiredPlatform) {
|
|
11
11
|
throw new Error("You must provide a desiredPlatform.");
|
|
12
12
|
}
|
|
13
|
-
if (!args.appPath) {
|
|
14
|
-
throw new Error("You must provide
|
|
13
|
+
if (!args.appPath && !args.browserstackAppUrl) {
|
|
14
|
+
throw new Error("You must provide either appPath or browserstackAppUrl.");
|
|
15
15
|
}
|
|
16
16
|
if (!args.desiredPhone) {
|
|
17
17
|
throw new Error("You must provide a desiredPhone.");
|
|
18
18
|
}
|
|
19
|
-
if
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if
|
|
28
|
-
|
|
19
|
+
// Only validate app path if it's provided (not using browserstackAppUrl)
|
|
20
|
+
if (args.appPath) {
|
|
21
|
+
if (args.desiredPlatform === "android" && !args.appPath.endsWith(".apk")) {
|
|
22
|
+
throw new Error("You must provide a valid Android app path.");
|
|
23
|
+
}
|
|
24
|
+
if (args.desiredPlatform === "ios" && !args.appPath.endsWith(".ipa")) {
|
|
25
|
+
throw new Error("You must provide a valid iOS app path.");
|
|
26
|
+
}
|
|
27
|
+
// check if the app path exists && is readable
|
|
28
|
+
try {
|
|
29
|
+
if (!fs.existsSync(args.appPath)) {
|
|
30
|
+
throw new Error("The app path does not exist.");
|
|
31
|
+
}
|
|
32
|
+
fs.accessSync(args.appPath, fs.constants.R_OK);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
logger.error("The app path does not exist or is not readable: %s", error);
|
|
36
|
+
throw new Error("The app path does not exist or is not readable.");
|
|
29
37
|
}
|
|
30
|
-
fs.accessSync(args.appPath, fs.constants.R_OK);
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
logger.error("The app path does not exist or is not readable: %s", error);
|
|
34
|
-
throw new Error("The app path does not exist or is not readable.");
|
|
35
38
|
}
|
|
36
39
|
const launchUrl = await startSession({
|
|
37
40
|
appPath: args.appPath,
|
|
38
41
|
desiredPlatform: args.desiredPlatform,
|
|
39
42
|
desiredPhone: args.desiredPhone,
|
|
40
43
|
desiredPlatformVersion: args.desiredPlatformVersion,
|
|
44
|
+
browserstackAppUrl: args.browserstackAppUrl,
|
|
41
45
|
}, { config });
|
|
42
46
|
return {
|
|
43
47
|
content: [
|
|
@@ -49,7 +53,8 @@ export async function startAppLiveSession(args, config) {
|
|
|
49
53
|
};
|
|
50
54
|
}
|
|
51
55
|
export default function addAppLiveTools(server, config) {
|
|
52
|
-
|
|
56
|
+
const tools = {};
|
|
57
|
+
tools.runAppLiveSession = server.tool("runAppLiveSession", "Use this tool when user wants to manually check their app on a particular mobile device using BrowserStack's cloud infrastructure. Can be used to debug crashes, slow performance, etc.", {
|
|
53
58
|
desiredPhone: z
|
|
54
59
|
.string()
|
|
55
60
|
.describe("The full name of the device to run the app on. Example: 'iPhone 12 Pro' or 'Samsung Galaxy S20' or 'Google Pixel 6'. Always ask the user for the device they want to use, do not assume it. "),
|
|
@@ -82,4 +87,5 @@ export default function addAppLiveTools(server, config) {
|
|
|
82
87
|
};
|
|
83
88
|
}
|
|
84
89
|
});
|
|
90
|
+
return tools;
|
|
85
91
|
}
|
package/dist/tools/automate.d.ts
CHANGED
|
@@ -6,4 +6,4 @@ export declare function fetchAutomationScreenshotsTool(args: {
|
|
|
6
6
|
sessionId: string;
|
|
7
7
|
sessionType: SessionType;
|
|
8
8
|
}, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
9
|
-
export default function addAutomationTools(server: McpServer, config: BrowserStackConfig):
|
|
9
|
+
export default function addAutomationTools(server: McpServer, config: BrowserStackConfig): Record<string, any>;
|
package/dist/tools/automate.js
CHANGED
|
@@ -42,7 +42,8 @@ export async function fetchAutomationScreenshotsTool(args, config) {
|
|
|
42
42
|
}
|
|
43
43
|
//Registers the fetchAutomationScreenshots tool with the MCP server
|
|
44
44
|
export default function addAutomationTools(server, config) {
|
|
45
|
-
|
|
45
|
+
const tools = {};
|
|
46
|
+
tools.fetchAutomationScreenshots = server.tool("fetchAutomationScreenshots", "Fetch and process screenshots from a BrowserStack Automate session", {
|
|
46
47
|
sessionId: z
|
|
47
48
|
.string()
|
|
48
49
|
.describe("The BrowserStack session ID to fetch screenshots from"),
|
|
@@ -67,4 +68,5 @@ export default function addAutomationTools(server, config) {
|
|
|
67
68
|
};
|
|
68
69
|
}
|
|
69
70
|
});
|
|
71
|
+
return tools;
|
|
70
72
|
}
|
|
@@ -14,4 +14,4 @@ export declare function bootstrapProjectWithSDK({ detectedBrowserAutomationFrame
|
|
|
14
14
|
enablePercy: boolean;
|
|
15
15
|
config: BrowserStackConfig;
|
|
16
16
|
}): Promise<CallToolResult>;
|
|
17
|
-
export default function addSDKTools(server: McpServer, config: BrowserStackConfig):
|
|
17
|
+
export default function addSDKTools(server: McpServer, config: BrowserStackConfig): Record<string, any>;
|
package/dist/tools/bstack-sdk.js
CHANGED
|
@@ -79,7 +79,8 @@ function formatFinalInstructions(combinedInstructions) {
|
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
export default function addSDKTools(server, config) {
|
|
82
|
-
|
|
82
|
+
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", {
|
|
83
84
|
detectedBrowserAutomationFramework: z
|
|
84
85
|
.nativeEnum(SDKSupportedBrowserAutomationFrameworkEnum)
|
|
85
86
|
.describe("The automation framework configured in the project. Example: 'playwright', 'selenium'"),
|
|
@@ -123,4 +124,5 @@ export default function addSDKTools(server, config) {
|
|
|
123
124
|
};
|
|
124
125
|
}
|
|
125
126
|
});
|
|
127
|
+
return tools;
|
|
126
128
|
}
|
|
@@ -10,5 +10,5 @@ export declare function getFailureLogs(args: {
|
|
|
10
10
|
logTypes: LogType[];
|
|
11
11
|
sessionType: SessionTypeValues;
|
|
12
12
|
}, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
13
|
-
export default function registerGetFailureLogs(server: McpServer, config: BrowserStackConfig):
|
|
13
|
+
export default function registerGetFailureLogs(server: McpServer, config: BrowserStackConfig): Record<string, any>;
|
|
14
14
|
export {};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import logger from "../logger.js";
|
|
3
2
|
import { trackMCP } from "../lib/instrumentation.js";
|
|
4
3
|
import { retrieveNetworkFailures, retrieveSessionFailures, retrieveConsoleFailures, } from "./failurelogs-utils/automate.js";
|
|
5
4
|
import { retrieveDeviceLogs, retrieveAppiumLogs, retrieveCrashLogs, } from "./failurelogs-utils/app-automate.js";
|
|
@@ -99,7 +98,8 @@ export async function getFailureLogs(args, config) {
|
|
|
99
98
|
}
|
|
100
99
|
// Register tool with the MCP server
|
|
101
100
|
export default function registerGetFailureLogs(server, config) {
|
|
102
|
-
|
|
101
|
+
const tools = {};
|
|
102
|
+
tools.getFailureLogs = server.tool("getFailureLogs", "Fetch various types of logs from a BrowserStack session. Supports both automate and app-automate sessions.", {
|
|
103
103
|
sessionType: z
|
|
104
104
|
.enum([SessionType.Automate, SessionType.AppAutomate])
|
|
105
105
|
.describe("Type of BrowserStack session. Must be explicitly provided by the user."),
|
|
@@ -126,14 +126,12 @@ export default function registerGetFailureLogs(server, config) {
|
|
|
126
126
|
return await getFailureLogs(args, config);
|
|
127
127
|
}
|
|
128
128
|
catch (error) {
|
|
129
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
130
129
|
trackMCP("getFailureLogs", server.server.getClientVersion(), error, config);
|
|
131
|
-
logger.error("Failed to fetch logs: %s", message);
|
|
132
130
|
return {
|
|
133
131
|
content: [
|
|
134
132
|
{
|
|
135
133
|
type: "text",
|
|
136
|
-
text: `Failed to fetch logs: ${message}`,
|
|
134
|
+
text: `Failed to fetch failure logs: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
137
135
|
isError: true,
|
|
138
136
|
},
|
|
139
137
|
],
|
|
@@ -141,4 +139,5 @@ export default function registerGetFailureLogs(server, config) {
|
|
|
141
139
|
};
|
|
142
140
|
}
|
|
143
141
|
});
|
|
142
|
+
return tools;
|
|
144
143
|
}
|
package/dist/tools/live.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { BrowserStackConfig } from "../lib/types.js";
|
|
3
|
-
export default function addBrowserLiveTools(server: McpServer, config: BrowserStackConfig):
|
|
3
|
+
export default function addBrowserLiveTools(server: McpServer, config: BrowserStackConfig): Record<string, any>;
|
package/dist/tools/live.js
CHANGED
|
@@ -3,6 +3,7 @@ import logger from "../logger.js";
|
|
|
3
3
|
import { startBrowserSession } from "./live-utils/start-session.js";
|
|
4
4
|
import { PlatformType } from "./live-utils/types.js";
|
|
5
5
|
import { trackMCP } from "../lib/instrumentation.js";
|
|
6
|
+
import globalConfig from "../config.js";
|
|
6
7
|
// Define the schema shape
|
|
7
8
|
const LiveArgsShape = {
|
|
8
9
|
platformType: z
|
|
@@ -67,17 +68,27 @@ async function runBrowserSession(rawArgs, config) {
|
|
|
67
68
|
const launchUrl = args.platformType === PlatformType.DESKTOP
|
|
68
69
|
? await launchDesktopSession(args, config)
|
|
69
70
|
: await launchMobileSession(args, config);
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
let response = [
|
|
72
|
+
{
|
|
73
|
+
type: "text",
|
|
74
|
+
text: `✅ Session started. If it didn't open automatically, visit:\n${launchUrl}`,
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
if (globalConfig.REMOTE_MCP) {
|
|
78
|
+
response = [
|
|
72
79
|
{
|
|
73
80
|
type: "text",
|
|
74
|
-
text: `✅
|
|
81
|
+
text: `✅ To start the session. Click on ${launchUrl}`,
|
|
75
82
|
},
|
|
76
|
-
]
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
content: response,
|
|
77
87
|
};
|
|
78
88
|
}
|
|
79
89
|
export default function addBrowserLiveTools(server, config) {
|
|
80
|
-
|
|
90
|
+
const tools = {};
|
|
91
|
+
tools.runBrowserLiveSession = server.tool("runBrowserLiveSession", "Launch a BrowserStack Live session (desktop or mobile).", LiveArgsShape, async (args) => {
|
|
81
92
|
try {
|
|
82
93
|
trackMCP("runBrowserLiveSession", server.server.getClientVersion(), undefined, config);
|
|
83
94
|
return await runBrowserSession(args, config);
|
|
@@ -97,4 +108,5 @@ export default function addBrowserLiveTools(server, config) {
|
|
|
97
108
|
};
|
|
98
109
|
}
|
|
99
110
|
});
|
|
111
|
+
return tools;
|
|
100
112
|
}
|
|
@@ -311,6 +311,12 @@ Add new scripts to package.json for running tests on BrowserStack:
|
|
|
311
311
|
}
|
|
312
312
|
\`\`\`
|
|
313
313
|
|
|
314
|
+
Example :
|
|
315
|
+
\`\`\`json
|
|
316
|
+
"scripts": {
|
|
317
|
+
"test:browserstack": "npx browserstack-node-sdk playwright test"
|
|
318
|
+
}
|
|
319
|
+
\`\`\`
|
|
314
320
|
---STEP---
|
|
315
321
|
|
|
316
322
|
Export BrowserStack credentials as environment variables:
|
|
@@ -319,6 +325,10 @@ Set the following environment variables before running tests.
|
|
|
319
325
|
export BROWSERSTACK_USERNAME=${username}
|
|
320
326
|
export BROWSERSTACK_ACCESS_KEY=${accessKey}
|
|
321
327
|
\`\`\`
|
|
328
|
+
|
|
329
|
+
---STEP---
|
|
330
|
+
Run your tests:
|
|
331
|
+
You can now run your tests on BrowserStack using your standard command or Use the commands defined in your package.json file to run the tests.
|
|
322
332
|
`;
|
|
323
333
|
/**
|
|
324
334
|
* ---------- EXPORT CONFIG ----------
|
|
@@ -426,7 +436,7 @@ exports.config.capabilities.forEach(function (caps) {
|
|
|
426
436
|
---STEP---
|
|
427
437
|
|
|
428
438
|
Run your tests:
|
|
429
|
-
You can now run your tests on BrowserStack using your standard WebdriverIO command.
|
|
439
|
+
You can now run your tests on BrowserStack using your standard WebdriverIO command or Use the commands defined in your package.json file to run the tests.
|
|
430
440
|
`;
|
|
431
441
|
const cypressInstructions = (username, accessKey) => `
|
|
432
442
|
---STEP---
|
|
@@ -555,5 +565,8 @@ export const SUPPORTED_CONFIGURATIONS = {
|
|
|
555
565
|
cypress: {
|
|
556
566
|
cypress: { instructions: cypressInstructions },
|
|
557
567
|
},
|
|
568
|
+
webdriverio: {
|
|
569
|
+
mocha: { instructions: webdriverioInstructions },
|
|
570
|
+
},
|
|
558
571
|
},
|
|
559
572
|
};
|
|
@@ -8,7 +8,8 @@ export type SDKSupportedLanguage = keyof typeof SDKSupportedLanguageEnum;
|
|
|
8
8
|
export declare enum SDKSupportedBrowserAutomationFrameworkEnum {
|
|
9
9
|
playwright = "playwright",
|
|
10
10
|
selenium = "selenium",
|
|
11
|
-
cypress = "cypress"
|
|
11
|
+
cypress = "cypress",
|
|
12
|
+
webdriverio = "webdriverio"
|
|
12
13
|
}
|
|
13
14
|
export type SDKSupportedBrowserAutomationFramework = keyof typeof SDKSupportedBrowserAutomationFrameworkEnum;
|
|
14
15
|
export declare enum SDKSupportedTestingFrameworkEnum {
|
|
@@ -10,6 +10,7 @@ export var SDKSupportedBrowserAutomationFrameworkEnum;
|
|
|
10
10
|
SDKSupportedBrowserAutomationFrameworkEnum["playwright"] = "playwright";
|
|
11
11
|
SDKSupportedBrowserAutomationFrameworkEnum["selenium"] = "selenium";
|
|
12
12
|
SDKSupportedBrowserAutomationFrameworkEnum["cypress"] = "cypress";
|
|
13
|
+
SDKSupportedBrowserAutomationFrameworkEnum["webdriverio"] = "webdriverio";
|
|
13
14
|
})(SDKSupportedBrowserAutomationFrameworkEnum || (SDKSupportedBrowserAutomationFrameworkEnum = {}));
|
|
14
15
|
export var SDKSupportedTestingFrameworkEnum;
|
|
15
16
|
(function (SDKSupportedTestingFrameworkEnum) {
|
package/dist/tools/selfheal.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ import { BrowserStackConfig } from "../lib/types.js";
|
|
|
4
4
|
export declare function fetchSelfHealSelectorTool(args: {
|
|
5
5
|
sessionId: string;
|
|
6
6
|
}, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
7
|
-
export default function addSelfHealTools(server: McpServer, config: BrowserStackConfig):
|
|
7
|
+
export default function addSelfHealTools(server: McpServer, config: BrowserStackConfig): Record<string, any>;
|
package/dist/tools/selfheal.js
CHANGED
|
@@ -23,7 +23,8 @@ export async function fetchSelfHealSelectorTool(args, config) {
|
|
|
23
23
|
}
|
|
24
24
|
// Registers the fetchSelfHealSelector tool with the MCP server
|
|
25
25
|
export default function addSelfHealTools(server, config) {
|
|
26
|
-
|
|
26
|
+
const tools = {};
|
|
27
|
+
tools.fetchSelfHealedSelectors = server.tool("fetchSelfHealedSelectors", "Retrieves AI-generated, self-healed selectors for a BrowserStack Automate session to resolve flaky tests caused by dynamic DOM changes.", {
|
|
27
28
|
sessionId: z.string().describe("The session ID of the test run"),
|
|
28
29
|
}, async (args) => {
|
|
29
30
|
try {
|
|
@@ -43,4 +44,5 @@ export default function addSelfHealTools(server, config) {
|
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
46
|
});
|
|
47
|
+
return tools;
|
|
46
48
|
}
|
|
@@ -21,6 +21,7 @@ export interface TestCaseCreateRequest {
|
|
|
21
21
|
issue_tracker?: IssueTracker;
|
|
22
22
|
tags?: string[];
|
|
23
23
|
custom_fields?: Record<string, string>;
|
|
24
|
+
automation_status?: string;
|
|
24
25
|
}
|
|
25
26
|
export interface TestCaseResponse {
|
|
26
27
|
data: {
|
|
@@ -80,6 +81,7 @@ export declare const CreateTestCaseSchema: z.ZodObject<{
|
|
|
80
81
|
}>>;
|
|
81
82
|
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
82
83
|
custom_fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
84
|
+
automation_status: z.ZodOptional<z.ZodString>;
|
|
83
85
|
}, "strip", z.ZodTypeAny, {
|
|
84
86
|
name: string;
|
|
85
87
|
test_case_steps: {
|
|
@@ -90,6 +92,7 @@ export declare const CreateTestCaseSchema: z.ZodObject<{
|
|
|
90
92
|
folder_id: string;
|
|
91
93
|
issues?: string[] | undefined;
|
|
92
94
|
description?: string | undefined;
|
|
95
|
+
automation_status?: string | undefined;
|
|
93
96
|
tags?: string[] | undefined;
|
|
94
97
|
custom_fields?: Record<string, string> | undefined;
|
|
95
98
|
preconditions?: string | undefined;
|
|
@@ -108,6 +111,7 @@ export declare const CreateTestCaseSchema: z.ZodObject<{
|
|
|
108
111
|
folder_id: string;
|
|
109
112
|
issues?: string[] | undefined;
|
|
110
113
|
description?: string | undefined;
|
|
114
|
+
automation_status?: string | undefined;
|
|
111
115
|
tags?: string[] | undefined;
|
|
112
116
|
custom_fields?: Record<string, string> | undefined;
|
|
113
117
|
preconditions?: string | undefined;
|
|
@@ -49,6 +49,10 @@ export const CreateTestCaseSchema = z.object({
|
|
|
49
49
|
.record(z.string(), z.string())
|
|
50
50
|
.optional()
|
|
51
51
|
.describe("Map of custom field names to values."),
|
|
52
|
+
automation_status: z
|
|
53
|
+
.string()
|
|
54
|
+
.optional()
|
|
55
|
+
.describe("Automation status of the test case. Common values include 'not_automated', 'automated', 'automation_not_required'."),
|
|
52
56
|
});
|
|
53
57
|
export function sanitizeArgs(args) {
|
|
54
58
|
const cleaned = { ...args };
|
|
@@ -58,6 +62,8 @@ export function sanitizeArgs(args) {
|
|
|
58
62
|
delete cleaned.owner;
|
|
59
63
|
if (cleaned.preconditions === null)
|
|
60
64
|
delete cleaned.preconditions;
|
|
65
|
+
if (cleaned.automation_status === null)
|
|
66
|
+
delete cleaned.automation_status;
|
|
61
67
|
if (cleaned.issue_tracker) {
|
|
62
68
|
if (cleaned.issue_tracker.name === undefined ||
|
|
63
69
|
cleaned.issue_tracker.host === undefined) {
|
|
@@ -57,4 +57,4 @@ export declare function createLCAStepsTool(args: z.infer<typeof CreateLCAStepsSc
|
|
|
57
57
|
/**
|
|
58
58
|
* Registers both project/folder and test-case tools.
|
|
59
59
|
*/
|
|
60
|
-
export default function addTestManagementTools(server: McpServer, config: BrowserStackConfig):
|
|
60
|
+
export default function addTestManagementTools(server: McpServer, config: BrowserStackConfig): Record<string, any>;
|
|
@@ -242,14 +242,16 @@ export async function createLCAStepsTool(args, context, config, server) {
|
|
|
242
242
|
* Registers both project/folder and test-case tools.
|
|
243
243
|
*/
|
|
244
244
|
export default function addTestManagementTools(server, config) {
|
|
245
|
-
|
|
246
|
-
server.tool("
|
|
247
|
-
server.tool("
|
|
248
|
-
server.tool("
|
|
249
|
-
server.tool("
|
|
250
|
-
server.tool("
|
|
251
|
-
server.tool("
|
|
252
|
-
server.tool("
|
|
253
|
-
server.tool("
|
|
254
|
-
server.tool("
|
|
245
|
+
const tools = {};
|
|
246
|
+
tools.createProjectOrFolder = server.tool("createProjectOrFolder", "Create a project and/or folder in BrowserStack Test Management.", CreateProjFoldSchema.shape, (args) => createProjectOrFolderTool(args, config, server));
|
|
247
|
+
tools.createTestCase = server.tool("createTestCase", "Use this tool to create a test case in BrowserStack Test Management.", CreateTestCaseSchema.shape, (args) => createTestCaseTool(args, config, server));
|
|
248
|
+
tools.listTestCases = server.tool("listTestCases", "List test cases in a project with optional filters (status, priority, custom fields, etc.)", ListTestCasesSchema.shape, (args) => listTestCasesTool(args, config, server));
|
|
249
|
+
tools.createTestRun = server.tool("createTestRun", "Create a test run in BrowserStack Test Management.", CreateTestRunSchema.shape, (args) => createTestRunTool(args, config, server));
|
|
250
|
+
tools.listTestRuns = server.tool("listTestRuns", "List test runs in a project with optional filters (date ranges, assignee, state, etc.)", ListTestRunsSchema.shape, (args) => listTestRunsTool(args, config, server));
|
|
251
|
+
tools.updateTestRun = server.tool("updateTestRun", "Update a test run in BrowserStack Test Management.", UpdateTestRunSchema.shape, (args) => updateTestRunTool(args, config, server));
|
|
252
|
+
tools.addTestResult = server.tool("addTestResult", "Add a test result to a specific test run via BrowserStack Test Management API.", AddTestResultSchema.shape, (args) => addTestResultTool(args, config, server));
|
|
253
|
+
tools.uploadProductRequirementFile = server.tool("uploadProductRequirementFile", "Upload files (e.g., PDRs, PDFs) to BrowserStack Test Management and retrieve a file mapping ID. This is utilized for generating test cases from files and is part of the Test Case Generator AI Agent in BrowserStack.", UploadFileSchema.shape, (args) => uploadProductRequirementFileTool(args, config, server));
|
|
254
|
+
tools.createTestCasesFromFile = server.tool("createTestCasesFromFile", "Generate test cases from a file in BrowserStack Test Management using the Test Case Generator AI Agent.", CreateTestCasesFromFileSchema.shape, (args, context) => createTestCasesFromFileTool(args, context, config, server));
|
|
255
|
+
tools.createLCASteps = server.tool("createLCASteps", "Generate Low Code Automation (LCA) steps for a test case in BrowserStack Test Management using the Low Code Automation Agent.", CreateLCAStepsSchema.shape, (args, context) => createLCAStepsTool(args, context, config, server));
|
|
256
|
+
return tools;
|
|
255
257
|
}
|