@browserstack/mcp-server 2.2.5-beta.1 → 2.2.5

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.
@@ -162,12 +162,18 @@ const PROCESSING_STATUSES = new Set([
162
162
  "processing",
163
163
  "finalizing",
164
164
  ]);
165
+ /**
166
+ * Of those, the one worth waiting on: the advanced-issue processing after the run, which
167
+ * ends within minutes. A report whose scan or tests are still running is read as it is,
168
+ * with a note, since that can last as long as the suite does.
169
+ */
170
+ const WAIT_STATUSES = new Set(["finalizing"]);
165
171
  /**
166
172
  * Where a report is in its processing before its issues are read. Issues keep being added
167
- * until it completes (an advanced scan adds its advanced-rule issues while "finalizing", for
168
- * minutes after the tests end), so a first read waits for it, reporting progress, for up to
169
- * REPORT_STATUS_WAIT_MS. A later page does not wait again, and a status that cannot be read
170
- * is not waited on.
173
+ * until it completes; an advanced scan adds its advanced-rule issues while "finalizing", for
174
+ * minutes after the tests end, so a first read waits for that, reporting progress, for up to
175
+ * REPORT_STATUS_WAIT_MS. A run still in progress is not waited on (it can take as long as
176
+ * the suite), nor is a later page, nor a status that cannot be read.
171
177
  */
172
178
  async function waitForProcessing(reportFetcher, source, wait, context) {
173
179
  const started = Date.now();
@@ -175,7 +181,7 @@ async function waitForProcessing(reportFetcher, source, wait, context) {
175
181
  let status = firstStatus;
176
182
  while (wait &&
177
183
  status &&
178
- PROCESSING_STATUSES.has(status) &&
184
+ WAIT_STATUSES.has(status) &&
179
185
  Date.now() - started < REPORT_STATUS_WAIT_MS) {
180
186
  const waited = Date.now() - started;
181
187
  if (context) {
@@ -466,28 +472,29 @@ export default function addAccessibilityTools(server, config) {
466
472
  }, async (args) => {
467
473
  return await executeGetAuthConfig(args, server, config);
468
474
  });
469
- tools.fetchAccessibilityIssues = server.tool("fetchAccessibilityIssues", "Fetch accessibility issues with pagination support, from exactly one source: a completed Website Scanner run (scanId with scanRunId), an Automated Tests build from a BrowserStack SDK test run (buildId), a Workflow Analyzer report (workflowReportId), or a Website Scanner or Automated Tests report's dashboard link (reportUrl). Also returns a link to download the full report as a CSV. Use cursor parameter to get subsequent pages of results." +
475
+ tools.fetchAccessibilityIssues = server.tool("fetchAccessibilityIssues", "Fetch accessibility issues (paged by cursor) from one source: scanId+scanRunId, buildId, workflowReportId or reportUrl." +
470
476
  NEEDS_A11Y_SCAN_ID, {
471
477
  scanId: z
472
478
  .string()
473
479
  .optional()
474
- .describe("Website Scanner: the scan ID from a completed accessibility scan"),
480
+ .describe("Website Scanner: the scan id from startAccessibilityScan."),
475
481
  scanRunId: z
476
482
  .string()
477
483
  .optional()
478
- .describe("Website Scanner: the scan run ID from a completed accessibility scan"),
484
+ .describe("Website Scanner: the scan run id from startAccessibilityScan."),
479
485
  buildId: z
480
486
  .string()
487
+ .regex(/^[a-z0-9]{40}$/, "40 lowercase letters and digits")
481
488
  .optional()
482
- .describe("Automated Tests: the Accessibility build id, shown as thBuildId in the build's accessibility report link (40 lowercase letters and digits). Not the Automate build id or the build name."),
489
+ .describe("Automated Tests: the thBuildId in the build's accessibility report link (not the Automate build id)."),
483
490
  workflowReportId: z
484
491
  .string()
485
492
  .optional()
486
- .describe("Workflow Analyzer: the report's numeric id, e.g. 123456"),
493
+ .describe("Workflow Analyzer: the report's numeric id."),
487
494
  reportUrl: z
488
495
  .string()
489
496
  .optional()
490
- .describe("A report's Accessibility dashboard link, as the user pasted it: a Website Scanner or Automated Tests report link carrying thBuildId. A Website Scanner link resolves only to its scan's latest run. Workflow Analyzer links are not supported; use workflowReportId."),
497
+ .describe("A Website Scanner or Automated Tests report link from the Accessibility dashboard (carries thBuildId)."),
491
498
  cursor: z
492
499
  .number()
493
500
  .optional()
@@ -109,10 +109,14 @@ export class AccessibilityReportFetcher {
109
109
  const data = resp.data?.data;
110
110
  for (const scan of data?.scans ?? []) {
111
111
  if (thBuildIdOf(scan.scanUrl) === thBuildId) {
112
+ const scanRunId = scan.lastScanDetails?.scanRunId;
113
+ if (scanRunId === undefined) {
114
+ throw new Error(`Website Scanner scan ${scan.id} has no completed run to read yet; wait for it to finish, or pass scanId and scanRunId`);
115
+ }
112
116
  return {
113
117
  kind: "scan",
114
118
  scanId: String(scan.id),
115
- scanRunId: String(scan.lastScanDetails?.scanRunId),
119
+ scanRunId: String(scanRunId),
116
120
  };
117
121
  }
118
122
  if (sameName === undefined && scan.name === scanName) {
@@ -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({
@@ -65,8 +65,8 @@ export declare const NEEDS_SESSION_ID: string;
65
65
  * A scan run's ids come from startAccessibilityScan. Nothing here lists builds or reports,
66
66
  * but the tool takes a report's dashboard link and looks the ids up itself.
67
67
  */
68
- export declare const NEEDS_A11Y_SCAN_ID: string;
68
+ export declare const NEEDS_A11Y_SCAN_ID = " Ids: startAccessibilityScan or a dashboard link; nothing here lists reports.";
69
69
  /** Auth-config ids are not listable by any tool here. */
70
- export declare const NEEDS_A11Y_CONFIG_ID: string;
70
+ export declare const NEEDS_A11Y_CONFIG_ID = " Requires the id returned by createAccessibilityAuthConfig; nothing here lists configs.";
71
71
  /** Test ids come from listTestIds, which itself needs a build id. */
72
72
  export declare const NEEDS_TEST_IDS: string;
@@ -74,14 +74,8 @@ export const NEEDS_SESSION_ID = " Requires a session id. listSessions lists them
74
74
  * A scan run's ids come from startAccessibilityScan. Nothing here lists builds or reports,
75
75
  * but the tool takes a report's dashboard link and looks the ids up itself.
76
76
  */
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.";
77
+ export const NEEDS_A11Y_SCAN_ID = " Ids: startAccessibilityScan or a dashboard link; nothing here lists reports.";
83
78
  /** 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.";
79
+ export const NEEDS_A11Y_CONFIG_ID = " Requires the id returned by createAccessibilityAuthConfig; nothing here lists configs.";
86
80
  /** Test ids come from listTestIds, which itself needs a build id. */
87
81
  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.1",
3
+ "version": "2.2.5",
4
4
  "description": "BrowserStack's Official MCP Server",
5
5
  "mcpName": "io.github.browserstack/mcp-server",
6
6
  "main": "dist/index.js",