@browserstack/mcp-server 2.2.5-beta.2 → 2.2.5-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/lib/accessibility-routing.d.ts +5 -0
  2. package/dist/lib/accessibility-routing.js +5 -0
  3. package/dist/server-factory.js +6 -1
  4. package/dist/tools/accessibility.js +588 -67
  5. package/dist/tools/accessiblity-utils/app-report-fetcher.d.ts +341 -0
  6. package/dist/tools/accessiblity-utils/app-report-fetcher.js +731 -0
  7. package/dist/tools/accessiblity-utils/paginate.d.ts +21 -0
  8. package/dist/tools/accessiblity-utils/paginate.js +44 -0
  9. package/dist/tools/accessiblity-utils/report-fetcher.d.ts +116 -7
  10. package/dist/tools/accessiblity-utils/report-fetcher.js +273 -30
  11. package/dist/tools/accessiblity-utils/report-parser.d.ts +41 -1
  12. package/dist/tools/accessiblity-utils/report-parser.js +142 -43
  13. package/dist/tools/appautomate-utils/appium-sdk/config-generator.d.ts +2 -0
  14. package/dist/tools/appautomate-utils/appium-sdk/config-generator.js +15 -5
  15. package/dist/tools/appautomate-utils/appium-sdk/constants.d.ts +2 -1
  16. package/dist/tools/appautomate-utils/appium-sdk/constants.js +6 -1
  17. package/dist/tools/appautomate-utils/appium-sdk/handler.js +11 -1
  18. package/dist/tools/appautomate-utils/appium-sdk/instructions.d.ts +3 -1
  19. package/dist/tools/appautomate-utils/appium-sdk/instructions.js +2 -2
  20. package/dist/tools/appautomate-utils/appium-sdk/languages/accessibility.d.ts +2 -0
  21. package/dist/tools/appautomate-utils/appium-sdk/languages/accessibility.js +32 -0
  22. package/dist/tools/appautomate-utils/appium-sdk/languages/nodejs.d.ts +3 -1
  23. package/dist/tools/appautomate-utils/appium-sdk/languages/nodejs.js +19 -6
  24. package/dist/tools/appautomate-utils/native-execution/appautomate.d.ts +7 -2
  25. package/dist/tools/appautomate-utils/native-execution/appautomate.js +15 -12
  26. package/dist/tools/appautomate-utils/native-execution/constants.d.ts +2 -1
  27. package/dist/tools/appautomate-utils/native-execution/constants.js +6 -1
  28. package/dist/tools/appautomate.js +29 -16
  29. package/dist/tools/failurelogs-utils/app-automate.js +1 -1
  30. package/dist/tools/testmanagement-utils/create-testrun.d.ts +1 -1
  31. package/dist/tools/testmanagement-utils/update-testrun.d.ts +1 -1
  32. package/dist/tools/tool-handoff.d.ts +5 -4
  33. package/dist/tools/tool-handoff.js +5 -10
  34. package/package.json +1 -1
  35. package/dist/tools/accessiblity-utils/accessibility-rag.d.ts +0 -12
  36. package/dist/tools/accessiblity-utils/accessibility-rag.js +0 -59
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { AppTestPlatform } from "./types.js";
3
- export declare const RUN_APP_AUTOMATE_DESCRIPTION = "Execute pre-built native mobile test suites (Espresso for Android, XCUITest for iOS) by direct upload to BrowserStack. ONLY for compiled .apk/.ipa test files. This is NOT for SDK integration or Appium tests. For Appium-based testing with SDK setup, use 'setupBrowserStackAppAutomateTests' instead.";
3
+ export declare const RUN_APP_AUTOMATE_DESCRIPTION = "Execute pre-built native mobile test suites (Espresso for Android, XCUITest for iOS) by direct upload to BrowserStack. ONLY for compiled .apk/.ipa test files. This is NOT for SDK integration or Appium tests. For Appium-based testing with SDK setup, use 'setupBrowserStackAppAutomateTests' instead. Set accessibility: true to also run App Accessibility (WCAG) checks during the tests; the issues are then read with fetchAccessibilityIssues.";
4
4
  export declare const MobileDeviceSchema: z.ZodObject<{
5
5
  platform: z.ZodEnum<{
6
6
  android: "android";
@@ -22,4 +22,5 @@ export declare const RUN_APP_AUTOMATE_SCHEMA: {
22
22
  }, z.core.$strip>>>;
23
23
  project: z.ZodDefault<z.ZodOptional<z.ZodString>>;
24
24
  detectedAutomationFramework: z.ZodEnum<typeof AppTestPlatform>;
25
+ accessibility: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
25
26
  };
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { AppTestPlatform } from "./types.js";
3
- export const RUN_APP_AUTOMATE_DESCRIPTION = `Execute pre-built native mobile test suites (Espresso for Android, XCUITest for iOS) by direct upload to BrowserStack. ONLY for compiled .apk/.ipa test files. This is NOT for SDK integration or Appium tests. For Appium-based testing with SDK setup, use 'setupBrowserStackAppAutomateTests' instead.`;
3
+ export const RUN_APP_AUTOMATE_DESCRIPTION = `Execute pre-built native mobile test suites (Espresso for Android, XCUITest for iOS) by direct upload to BrowserStack. ONLY for compiled .apk/.ipa test files. This is NOT for SDK integration or Appium tests. For Appium-based testing with SDK setup, use 'setupBrowserStackAppAutomateTests' instead. Set accessibility: true to also run App Accessibility (WCAG) checks during the tests; the issues are then read with fetchAccessibilityIssues.`;
4
4
  export const MobileDeviceSchema = z.object({
5
5
  platform: z
6
6
  .enum(["android", "ios"])
@@ -46,4 +46,9 @@ export const RUN_APP_AUTOMATE_SCHEMA = {
46
46
  detectedAutomationFramework: z
47
47
  .nativeEnum(AppTestPlatform)
48
48
  .describe("The automation framework used in the project, such as 'espresso' (Android) or 'xcuitest' (iOS)."),
49
+ accessibility: z
50
+ .boolean()
51
+ .optional()
52
+ .default(false)
53
+ .describe("Run App Accessibility (WCAG) checks on the screens the tests touch; Android 11+ or iOS 14+."),
49
54
  };
@@ -96,8 +96,26 @@ async function takeAppScreenshot(args) {
96
96
  }
97
97
  }
98
98
  }
99
+ /**
100
+ * What a native suite needs for App Accessibility to scan it. The build request's flag
101
+ * turns the checks on, but only a suite built with BrowserStack's instrumentation records
102
+ * scans; without it the run finishes with none, and nothing says why.
103
+ */
104
+ const NATIVE_ACCESSIBILITY_SETUP = {
105
+ Espresso: "The test suite must be built with BrowserStack's Gradle plugin and SDK, or the run records no scans: classpath 'com.browserstack:gradle-tool:latest.release' in the root build.gradle, id 'com.browserstack.gradle-tool' in the app's plugins, and androidTestImplementation 'com.browserstack:browserstack-java-sdk:latest.release' (1.31.0 or newer). The app also needs internet permission.",
106
+ XCUITest: "The UI test target must embed and initialize the BrowserStack Swift SDK (BrowserstackSwiftSDK.xcframework, or the Swift package), or the run records no scans: set NSPrincipalClass to BrowserstackSwiftSDK.BrowserstackSwiftSDK in the test target's Info.plist, or call BrowserstackSwiftSDK.init() in the test class's init(). Swift 5.4+ and iOS 14+.",
107
+ };
108
+ /** The run's build and, with accessibility on, where its issues are read once it finishes. */
109
+ function runStartedMessage(framework, buildId, accessibility) {
110
+ const link = `https://app-automate.browserstack.com/builds/${buildId}`;
111
+ const a11y = accessibility
112
+ ? `\n\n♿ App Accessibility checks are on. ${NATIVE_ACCESSIBILITY_SETUP[framework] ?? ""}\nWhen the build finishes (typically 10–20 minutes), read the issues with fetchAccessibilityIssues, reportUrl: ${link} — it maps the build to its accessibility report and waits while the report is still processing.`
113
+ : "";
114
+ return `✅ ${framework} run started successfully!\n\n🔧 Build ID: ${buildId}\n🔗 View your build: ${link}${a11y}`;
115
+ }
99
116
  //Runs AppAutomate tests on BrowserStack by uploading app and test suite, then triggering a test run.
100
117
  async function runAppTestsOnBrowserStack(args, config) {
118
+ const accessibility = args.accessibility === true;
101
119
  // Validate that either paths or URLs are provided for both app and test suite
102
120
  if (!args.browserstackAppUrl && !args.appPath) {
103
121
  throw new Error("appPath is required when browserstackAppUrl is not provided");
@@ -105,8 +123,13 @@ async function runAppTestsOnBrowserStack(args, config) {
105
123
  if (!args.browserstackTestSuiteUrl && !args.testSuitePath) {
106
124
  throw new Error("testSuitePath is required when browserstackTestSuiteUrl is not provided");
107
125
  }
108
- // Validate devices against real BrowserStack device data
109
- await validateAppAutomateDevices(args.devices);
126
+ // Validate devices against real BrowserStack device data, and ask for them as App Automate
127
+ // lists them: a version given as "14" is "14.0" there, and a run asking for "-14" waits for
128
+ // a device that never matches.
129
+ const validated = await validateAppAutomateDevices(args.devices);
130
+ const deviceStrings = args.devices.length > 0
131
+ ? validated.map((device) => `${device.deviceName}-${device.osVersion}`)
132
+ : [];
110
133
  switch (args.detectedAutomationFramework) {
111
134
  case AppTestPlatform.ESPRESSO: {
112
135
  try {
@@ -128,17 +151,12 @@ async function runAppTestsOnBrowserStack(args, config) {
128
151
  test_suite_url = await uploadEspressoTestSuite(args.testSuitePath, config);
129
152
  logger.info(`Test suite uploaded. URL: ${test_suite_url}`);
130
153
  }
131
- // Convert array format to string format for Espresso
132
- const deviceStrings = args.devices.map((device) => {
133
- const [, deviceName, osVersion] = device;
134
- return `${deviceName}-${osVersion}`;
135
- });
136
- const build_id = await triggerEspressoBuild(app_url, test_suite_url, deviceStrings, args.project, config);
154
+ const build_id = await triggerEspressoBuild(app_url, test_suite_url, deviceStrings, args.project, config, { accessibility });
137
155
  return {
138
156
  content: [
139
157
  {
140
158
  type: "text",
141
- text: `✅ Espresso run started successfully!\n\n🔧 Build ID: ${build_id}\n🔗 View your build: https://app-automate.browserstack.com/builds/${build_id}`,
159
+ text: runStartedMessage("Espresso", build_id, accessibility),
142
160
  },
143
161
  ],
144
162
  };
@@ -168,17 +186,12 @@ async function runAppTestsOnBrowserStack(args, config) {
168
186
  test_suite_url = await uploadXcuiTestSuite(args.testSuitePath, config);
169
187
  logger.info(`Test suite uploaded. URL: ${test_suite_url}`);
170
188
  }
171
- // Convert array format to string format for XCUITest
172
- const deviceStrings = args.devices.map((device) => {
173
- const [, deviceName, osVersion] = device;
174
- return `${deviceName}-${osVersion}`;
175
- });
176
- const build_id = await triggerXcuiBuild(app_url, test_suite_url, deviceStrings, args.project, config);
189
+ const build_id = await triggerXcuiBuild(app_url, test_suite_url, deviceStrings, args.project, config, { accessibility });
177
190
  return {
178
191
  content: [
179
192
  {
180
193
  type: "text",
181
- text: `✅ XCUITest run started successfully!\n\n🔧 Build ID: ${build_id}\n🔗 View your build: https://app-automate.browserstack.com/builds/${build_id}`,
194
+ text: runStartedMessage("XCUITest", build_id, accessibility),
182
195
  },
183
196
  ],
184
197
  };
@@ -4,7 +4,7 @@ import { apiClient } from "../../lib/apiClient.js";
4
4
  import { wrapUntrusted } from "../../lib/untrusted-content.js";
5
5
  // DEVICE LOGS
6
6
  export async function retrieveDeviceLogs(sessionId, buildId, config) {
7
- const url = `https://api.browserstack.com/app-automate/builds/${buildId}/sessions/${sessionId}/deviceLogs`;
7
+ const url = `https://api.browserstack.com/app-automate/builds/${buildId}/sessions/${sessionId}/devicelogs`;
8
8
  const authString = getBrowserStackAuth(config);
9
9
  const auth = Buffer.from(authString).toString("base64");
10
10
  const response = await apiClient.get({
@@ -11,8 +11,8 @@ export declare const CreateTestRunSchema: z.ZodObject<{
11
11
  description: z.ZodOptional<z.ZodString>;
12
12
  run_state: z.ZodOptional<z.ZodEnum<{
13
13
  done: "done";
14
- new_run: "new_run";
15
14
  in_progress: "in_progress";
15
+ new_run: "new_run";
16
16
  under_review: "under_review";
17
17
  rejected: "rejected";
18
18
  closed: "closed";
@@ -11,8 +11,8 @@ export declare const UpdateTestRunSchema: z.ZodObject<{
11
11
  name: z.ZodOptional<z.ZodString>;
12
12
  run_state: z.ZodOptional<z.ZodEnum<{
13
13
  done: "done";
14
- new_run: "new_run";
15
14
  in_progress: "in_progress";
15
+ new_run: "new_run";
16
16
  under_review: "under_review";
17
17
  rejected: "rejected";
18
18
  closed: "closed";
@@ -62,11 +62,12 @@ export declare const NEEDS_BUILD_ID: string;
62
62
  */
63
63
  export declare const NEEDS_SESSION_ID: string;
64
64
  /**
65
- * A scan run's ids come from startAccessibilityScan. Nothing here lists builds or reports,
66
- * but the tool takes a report's dashboard link and looks the ids up itself.
65
+ * A scan run's ids come from startAccessibilityScan; a build's id is printed by the SDK at
66
+ * the end of its run. Nothing here lists builds or reports, but the tool takes a report's
67
+ * dashboard link, or an Automate link, and looks the ids up itself.
67
68
  */
68
- export declare const NEEDS_A11Y_SCAN_ID: string;
69
+ export declare const NEEDS_A11Y_SCAN_ID = " Ids: startAccessibilityScan or a dashboard link; nothing here lists reports.";
69
70
  /** Auth-config ids are not listable by any tool here. */
70
- export declare const NEEDS_A11Y_CONFIG_ID: string;
71
+ export declare const NEEDS_A11Y_CONFIG_ID = " Requires the id returned by createAccessibilityAuthConfig; nothing here lists configs.";
71
72
  /** Test ids come from listTestIds, which itself needs a build id. */
72
73
  export declare const NEEDS_TEST_IDS: string;
@@ -71,17 +71,12 @@ export const NEEDS_BUILD_ID = needsIdFrom("a BrowserStack build id", "getBuildId
71
71
  export const NEEDS_SESSION_ID = " Requires a session id. listSessions lists them for a build; if you do not have the " +
72
72
  "build either, getBuildId or listBuildId resolves one from a project and build name.";
73
73
  /**
74
- * A scan run's ids come from startAccessibilityScan. Nothing here lists builds or reports,
75
- * but the tool takes a report's dashboard link and looks the ids up itself.
74
+ * A scan run's ids come from startAccessibilityScan; a build's id is printed by the SDK at
75
+ * the end of its run. Nothing here lists builds or reports, but the tool takes a report's
76
+ * dashboard link, or an Automate link, and looks the ids up itself.
76
77
  */
77
- export const NEEDS_A11Y_SCAN_ID = " Requires a completed source: a Website Scanner run's ids, returned by " +
78
- "startAccessibilityScan; an Automated Tests build's id, the thBuildId in its accessibility " +
79
- "report link; a Workflow Analyzer report's numeric id; or a Website Scanner or Automated " +
80
- "Tests report's dashboard link, passed as reportUrl. No tool here lists past scans, builds " +
81
- "or reports, so if you have none of these, ask the user for the report link, or start a " +
82
- "new scan, rather than guessing.";
78
+ export const NEEDS_A11Y_SCAN_ID = " Ids: startAccessibilityScan or a dashboard link; nothing here lists reports.";
83
79
  /** Auth-config ids are not listable by any tool here. */
84
- export const NEEDS_A11Y_CONFIG_ID = " Requires the numeric id returned by createAccessibilityAuthConfig. No tool here lists " +
85
- "existing configurations, so if you do not have the id, create one rather than guessing.";
80
+ export const NEEDS_A11Y_CONFIG_ID = " Requires the id returned by createAccessibilityAuthConfig; nothing here lists configs.";
86
81
  /** Test ids come from listTestIds, which itself needs a build id. */
87
82
  export const NEEDS_TEST_IDS = needsIdFrom("test ids", "listTestIds");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@browserstack/mcp-server",
3
- "version": "2.2.5-beta.2",
3
+ "version": "2.2.5-beta.3",
4
4
  "description": "BrowserStack's Official MCP Server",
5
5
  "mcpName": "io.github.browserstack/mcp-server",
6
6
  "main": "dist/index.js",
@@ -1,12 +0,0 @@
1
- export interface RAGChunk {
2
- url: string;
3
- content: string;
4
- }
5
- import { BrowserStackConfig } from "../../lib/types.js";
6
- export interface AccessibilityRAGResponse {
7
- content: Array<{
8
- type: "text";
9
- text: string;
10
- }>;
11
- }
12
- export declare function queryAccessibilityRAG(userQuery: string, config: BrowserStackConfig): Promise<any>;
@@ -1,59 +0,0 @@
1
- import { apiClient } from "../../lib/apiClient.js";
2
- import { wrapUntrusted } from "../../lib/untrusted-content.js";
3
- import { getBrowserStackAuth } from "../../lib/get-auth.js";
4
- export async function queryAccessibilityRAG(userQuery, config) {
5
- const url = "https://accessibility.browserstack.com/api/tcg-proxy/search";
6
- const authString = getBrowserStackAuth(config);
7
- const auth = Buffer.from(authString).toString("base64");
8
- const response = await apiClient.post({
9
- url,
10
- headers: {
11
- "Content-Type": "application/json",
12
- Authorization: `Basic ${auth}`,
13
- },
14
- body: {
15
- query: userQuery,
16
- },
17
- raise_error: false,
18
- });
19
- if (!response.ok) {
20
- const errorText = typeof response.data === "string"
21
- ? response.data
22
- : JSON.stringify(response.data);
23
- throw new Error(`RAG endpoint error: ${response.status} ${errorText}`);
24
- }
25
- const responseData = response.data;
26
- if (!responseData.success) {
27
- throw new Error("Something went wrong: " + responseData.message);
28
- }
29
- // Parse the stringified JSON data
30
- let parsedData;
31
- try {
32
- parsedData = JSON.parse(responseData.data);
33
- }
34
- catch (err) {
35
- throw new Error("Failed to parse RAG response data as JSON: " +
36
- (err instanceof Error ? err.message : String(err)));
37
- }
38
- if (!parsedData ||
39
- !parsedData.data ||
40
- !Array.isArray(parsedData.data.chunks)) {
41
- throw new Error("RAG response data is missing expected 'data.chunks' array");
42
- }
43
- const chunks = parsedData.data.chunks;
44
- // Format the response properly
45
- const instruction = "IMPORTANT: Use ONLY the data provided below to answer the user's accessibility question. Do not use any external knowledge. When answering, you MUST include the relevant BrowserStack documentation links provided in the sources for personalization and further reference.\n\n";
46
- const formattedChunks = chunks
47
- .map((chunk, index) => `${index + 1}: Source: ${chunk.url}\n\n${chunk.content}`)
48
- .join("\n\n---\n\n");
49
- const formattedResponse = instruction +
50
- wrapUntrusted("BrowserStack accessibility documentation", formattedChunks);
51
- return {
52
- content: [
53
- {
54
- type: "text",
55
- text: formattedResponse,
56
- },
57
- ],
58
- };
59
- }