@browserstack/mcp-server 2.2.3-beta.1 → 2.2.3-beta.2

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 (31) hide show
  1. package/dist/config.d.ts +4 -1
  2. package/dist/config.js +15 -2
  3. package/dist/lib/api.js +3 -1
  4. package/dist/lib/regional-host.d.ts +40 -0
  5. package/dist/lib/regional-host.js +51 -0
  6. package/dist/lib/tm-base-url.js +5 -0
  7. package/dist/tools/accessiblity-utils/accessibility-rag.js +2 -1
  8. package/dist/tools/accessiblity-utils/auth-config.js +4 -3
  9. package/dist/tools/accessiblity-utils/report-fetcher.js +3 -2
  10. package/dist/tools/accessiblity-utils/scanner.js +3 -2
  11. package/dist/tools/appautomate-utils/native-execution/appautomate.js +8 -7
  12. package/dist/tools/appautomate.js +2 -1
  13. package/dist/tools/automate-utils/fetch-screenshots.js +4 -1
  14. package/dist/tools/automate-utils/list-session-ids.d.ts +1 -1
  15. package/dist/tools/automate-utils/list-session-ids.js +7 -4
  16. package/dist/tools/automate-utils/resolve-hashed-build-id.d.ts +1 -1
  17. package/dist/tools/automate-utils/resolve-hashed-build-id.js +6 -6
  18. package/dist/tools/build-insights.js +4 -2
  19. package/dist/tools/failurelogs-utils/app-automate.js +4 -3
  20. package/dist/tools/failurelogs-utils/automate.js +4 -3
  21. package/dist/tools/failurelogs-utils/video.js +3 -1
  22. package/dist/tools/rca-agent-utils/get-build-id.js +3 -1
  23. package/dist/tools/rca-agent-utils/get-failed-test-id.d.ts +1 -1
  24. package/dist/tools/rca-agent-utils/get-failed-test-id.js +2 -2
  25. package/dist/tools/rca-agent-utils/list-build-id.js +3 -1
  26. package/dist/tools/rca-agent-utils/rca-data.d.ts +1 -1
  27. package/dist/tools/rca-agent-utils/rca-data.js +2 -2
  28. package/dist/tools/rca-agent.js +3 -2
  29. package/dist/tools/selfheal-utils/fetch-test-code.js +2 -2
  30. package/dist/tools/selfheal-utils/selfheal.js +4 -3
  31. package/package.json +1 -1
package/dist/config.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ declare const GRR_REGIONS: readonly ["us", "eu", "in"];
2
+ export type GrrRegion = (typeof GRR_REGIONS)[number];
1
3
  /**
2
4
  * USE_OWN_LOCAL_BINARY_PROCESS:
3
5
  * If true, the system will not start a new local binary process, but will use the user's own process.
@@ -26,6 +28,7 @@ export declare class Config {
26
28
  * "deferred" | "elicit". Anything else is ignored.
27
29
  */
28
30
  readonly ASK_BROWSERSTACK_RELAY_TRANSPORT: string | undefined;
31
+ readonly BROWSERSTACK_GRR_REGION: GrrRegion;
29
32
  constructor(DEV_MODE: boolean, browserstackLocalOptions: Record<string, any>, USE_OWN_LOCAL_BINARY_PROCESS: boolean, REMOTE_MCP: boolean, UPLOAD_BASE_DIR: string | undefined, O11Y_TFA_RCA_BASE_URL: string, BROWSERSTACK_AUTOMATION_BASE_URL: string, BROWSERSTACK_O11Y_UI_BASE_URL: string, ASK_BROWSERSTACK_ATLAS_URL: string | undefined, ASK_BROWSERSTACK_AUTH_TOKEN_URL: string | undefined,
30
33
  /**
31
34
  * Which relay transport to use, overriding what the deployment implies.
@@ -39,7 +42,7 @@ export declare class Config {
39
42
  *
40
43
  * "deferred" | "elicit". Anything else is ignored.
41
44
  */
42
- ASK_BROWSERSTACK_RELAY_TRANSPORT: string | undefined);
45
+ ASK_BROWSERSTACK_RELAY_TRANSPORT: string | undefined, BROWSERSTACK_GRR_REGION?: GrrRegion);
43
46
  }
44
47
  declare const config: Config;
45
48
  export default config;
package/dist/config.js CHANGED
@@ -34,6 +34,17 @@ const DEFAULT_O11Y_TFA_RCA_BASE_URL = "https://api-automation.browserstack.com";
34
34
  const DEFAULT_BROWSERSTACK_AUTOMATION_BASE_URL = "https://api-automation.browserstack.com";
35
35
  // Overridable via BROWSERSTACK_O11Y_UI_BASE_URL to point at a staging UI.
36
36
  const DEFAULT_BROWSERSTACK_O11Y_UI_BASE_URL = "https://automation.browserstack.com";
37
+ // BROWSERSTACK_GRR_REGION: "us" (default), "eu" or "in". Invalid values fail at startup.
38
+ const GRR_REGIONS = ["us", "eu", "in"];
39
+ function parseGrrRegion(raw) {
40
+ const value = (raw || "").trim().toLowerCase();
41
+ if (!value)
42
+ return "us";
43
+ if (GRR_REGIONS.includes(value)) {
44
+ return value;
45
+ }
46
+ throw new Error(`Invalid BROWSERSTACK_GRR_REGION "${raw}". Use one of: ${GRR_REGIONS.join(", ")}.`);
47
+ }
37
48
  /**
38
49
  * USE_OWN_LOCAL_BINARY_PROCESS:
39
50
  * If true, the system will not start a new local binary process, but will use the user's own process.
@@ -50,6 +61,7 @@ export class Config {
50
61
  ASK_BROWSERSTACK_ATLAS_URL;
51
62
  ASK_BROWSERSTACK_AUTH_TOKEN_URL;
52
63
  ASK_BROWSERSTACK_RELAY_TRANSPORT;
64
+ BROWSERSTACK_GRR_REGION;
53
65
  constructor(DEV_MODE, browserstackLocalOptions, USE_OWN_LOCAL_BINARY_PROCESS, REMOTE_MCP, UPLOAD_BASE_DIR, O11Y_TFA_RCA_BASE_URL, BROWSERSTACK_AUTOMATION_BASE_URL, BROWSERSTACK_O11Y_UI_BASE_URL,
54
66
  // askBrowserStackAI's process-startup settings. Declared here rather than read from
55
67
  // process.env inside src/tools/, per rules/tool-design.md — and so the remote wrapper,
@@ -71,7 +83,7 @@ export class Config {
71
83
  *
72
84
  * "deferred" | "elicit". Anything else is ignored.
73
85
  */
74
- ASK_BROWSERSTACK_RELAY_TRANSPORT) {
86
+ ASK_BROWSERSTACK_RELAY_TRANSPORT, BROWSERSTACK_GRR_REGION = "us") {
75
87
  this.DEV_MODE = DEV_MODE;
76
88
  this.browserstackLocalOptions = browserstackLocalOptions;
77
89
  this.USE_OWN_LOCAL_BINARY_PROCESS = USE_OWN_LOCAL_BINARY_PROCESS;
@@ -83,6 +95,7 @@ export class Config {
83
95
  this.ASK_BROWSERSTACK_ATLAS_URL = ASK_BROWSERSTACK_ATLAS_URL;
84
96
  this.ASK_BROWSERSTACK_AUTH_TOKEN_URL = ASK_BROWSERSTACK_AUTH_TOKEN_URL;
85
97
  this.ASK_BROWSERSTACK_RELAY_TRANSPORT = ASK_BROWSERSTACK_RELAY_TRANSPORT;
98
+ this.BROWSERSTACK_GRR_REGION = BROWSERSTACK_GRR_REGION;
86
99
  }
87
100
  }
88
101
  const config = new Config(process.env.DEV_MODE === "true", browserstackLocalOptions, process.env.USE_OWN_LOCAL_BINARY_PROCESS === "true", process.env.REMOTE_MCP === "true", process.env.MCP_UPLOAD_BASE_DIR && process.env.MCP_UPLOAD_BASE_DIR.length > 0
@@ -103,5 +116,5 @@ const config = new Config(process.env.DEV_MODE === "true", browserstackLocalOpti
103
116
  process.env.ASK_BROWSERSTACK_AUTH_TOKEN_URL.trim().length > 0
104
117
  ? process.env.ASK_BROWSERSTACK_AUTH_TOKEN_URL
105
118
  : undefined, (process.env.ASK_BROWSERSTACK_RELAY_TRANSPORT || "").trim().toLowerCase() ||
106
- undefined);
119
+ undefined, parseGrrRegion(process.env.BROWSERSTACK_GRR_REGION));
107
120
  export default config;
package/dist/lib/api.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { getBrowserStackAuth } from "./get-auth.js";
2
2
  import { apiClient } from "./apiClient.js";
3
+ import { getRegionalHost } from "./regional-host.js";
3
4
  export async function getLatestO11YBuildInfo(buildName, projectName, config) {
4
- const buildsUrl = `https://api-observability.browserstack.com/ext/v1/builds/latest?build_name=${encodeURIComponent(buildName)}&project_name=${encodeURIComponent(projectName)}`;
5
+ const host = getRegionalHost("observabilityApi");
6
+ const buildsUrl = `${host}/ext/v1/builds/latest?build_name=${encodeURIComponent(buildName)}&project_name=${encodeURIComponent(projectName)}`;
5
7
  const authString = getBrowserStackAuth(config);
6
8
  const auth = Buffer.from(authString).toString("base64");
7
9
  const buildsResponse = await apiClient.get({
@@ -0,0 +1,40 @@
1
+ import { GrrRegion } from "../config.js";
2
+ type Hosts = Record<GrrRegion, string>;
3
+ declare const HOSTS: {
4
+ readonly automateApi: Hosts;
5
+ readonly appAutomateApi: Hosts;
6
+ readonly appAutomateUpload: {
7
+ readonly us: "https://api-cloud.browserstack.com";
8
+ readonly eu: string;
9
+ readonly in: string;
10
+ };
11
+ readonly appAutomateHub: {
12
+ readonly us: "hub.browserstack.com";
13
+ readonly eu: "hub-eu-only.browserstack.com";
14
+ readonly in: "hub-aps-only.browserstack.com";
15
+ };
16
+ readonly accessibilityApi: {
17
+ readonly us: "https://api-accessibility.browserstack.com";
18
+ readonly eu: "https://api-accessibility-eu.browserstack.com";
19
+ readonly in: "https://api-accessibility-in.browserstack.com";
20
+ };
21
+ readonly accessibilityDashboard: {
22
+ readonly us: "https://accessibility.browserstack.com";
23
+ readonly eu: "https://accessibility-eu.browserstack.com";
24
+ readonly in: "https://accessibility-in.browserstack.com";
25
+ };
26
+ readonly observabilityApi: {
27
+ readonly us: "https://api-observability.browserstack.com";
28
+ readonly eu: "https://api-observability-eu.browserstack.com";
29
+ readonly in: "https://api-observability-in.browserstack.com";
30
+ };
31
+ readonly automationApi: {
32
+ readonly us: "https://api-automation.browserstack.com";
33
+ readonly eu: "https://api-automation-eu.browserstack.com";
34
+ readonly in: "https://api-automation-in.browserstack.com";
35
+ };
36
+ };
37
+ export type RegionalHostKind = keyof typeof HOSTS;
38
+ export declare function hostFor(kind: RegionalHostKind, region: GrrRegion): string;
39
+ export declare function getRegionalHost(kind: RegionalHostKind): string;
40
+ export {};
@@ -0,0 +1,51 @@
1
+ import appConfig from "../config.js";
2
+ const AUTOMATE_API = {
3
+ us: "https://api.browserstack.com",
4
+ eu: "https://api-eu-only.browserstack.com",
5
+ in: "https://api-aps-only.browserstack.com",
6
+ };
7
+ const HOSTS = {
8
+ automateApi: AUTOMATE_API,
9
+ appAutomateApi: AUTOMATE_API,
10
+ appAutomateUpload: {
11
+ us: "https://api-cloud.browserstack.com",
12
+ eu: AUTOMATE_API.eu,
13
+ in: AUTOMATE_API.in,
14
+ },
15
+ appAutomateHub: {
16
+ us: "hub.browserstack.com",
17
+ eu: "hub-eu-only.browserstack.com",
18
+ in: "hub-aps-only.browserstack.com",
19
+ },
20
+ accessibilityApi: {
21
+ us: "https://api-accessibility.browserstack.com",
22
+ eu: "https://api-accessibility-eu.browserstack.com",
23
+ in: "https://api-accessibility-in.browserstack.com",
24
+ },
25
+ accessibilityDashboard: {
26
+ us: "https://accessibility.browserstack.com",
27
+ eu: "https://accessibility-eu.browserstack.com",
28
+ in: "https://accessibility-in.browserstack.com",
29
+ },
30
+ observabilityApi: {
31
+ us: "https://api-observability.browserstack.com",
32
+ eu: "https://api-observability-eu.browserstack.com",
33
+ in: "https://api-observability-in.browserstack.com",
34
+ },
35
+ automationApi: {
36
+ us: "https://api-automation.browserstack.com",
37
+ eu: "https://api-automation-eu.browserstack.com",
38
+ in: "https://api-automation-in.browserstack.com",
39
+ },
40
+ };
41
+ export function hostFor(kind, region) {
42
+ return HOSTS[kind][region];
43
+ }
44
+ export function getRegionalHost(kind) {
45
+ if (kind === "automationApi" &&
46
+ appConfig.BROWSERSTACK_AUTOMATION_BASE_URL &&
47
+ appConfig.BROWSERSTACK_AUTOMATION_BASE_URL !== HOSTS.automationApi.us) {
48
+ return appConfig.BROWSERSTACK_AUTOMATION_BASE_URL;
49
+ }
50
+ return hostFor(kind, appConfig.BROWSERSTACK_GRR_REGION ?? "us");
51
+ }
@@ -9,6 +9,11 @@ const TM_BASE_URLS = [
9
9
  ];
10
10
  let cachedBaseUrl = null;
11
11
  export async function getTMBaseURL(config) {
12
+ // Unset or "us" falls through to the probe below.
13
+ const grrRegion = appConfig.BROWSERSTACK_GRR_REGION;
14
+ if (grrRegion === "eu" || grrRegion === "in") {
15
+ return `https://test-management-${grrRegion}.browserstack.com`;
16
+ }
12
17
  // Skip the module-level cache in remote (multi-tenant) mode: it is process-shared,
13
18
  // so the first user's region would be served to every subsequent user — breaking
14
19
  // requests for users on a different region's BrowserStack account.
@@ -1,8 +1,9 @@
1
1
  import { apiClient } from "../../lib/apiClient.js";
2
2
  import { wrapUntrusted } from "../../lib/untrusted-content.js";
3
3
  import { getBrowserStackAuth } from "../../lib/get-auth.js";
4
+ import { getRegionalHost } from "../../lib/regional-host.js";
4
5
  export async function queryAccessibilityRAG(userQuery, config) {
5
- const url = "https://accessibility.browserstack.com/api/tcg-proxy/search";
6
+ const url = `${getRegionalHost("accessibilityDashboard")}/api/tcg-proxy/search`;
6
7
  const authString = getBrowserStackAuth(config);
7
8
  const auth = Buffer.from(authString).toString("base64");
8
9
  const response = await apiClient.post({
@@ -1,5 +1,6 @@
1
1
  import { apiClient } from "../../lib/apiClient.js";
2
2
  import logger from "../../logger.js";
3
+ import { getRegionalHost } from "../../lib/regional-host.js";
3
4
  /**
4
5
  * Allowlist of non-sensitive fields safe to return to the LLM/tool caller.
5
6
  * Everything else (site username/password, selectors, undeclared keys) is
@@ -52,7 +53,7 @@ export class AccessibilityAuthConfig {
52
53
  };
53
54
  try {
54
55
  const response = await apiClient.post({
55
- url: "https://api-accessibility.browserstack.com/api/website-scanner/v1/auth_configs",
56
+ url: `${getRegionalHost("accessibilityApi")}/api/website-scanner/v1/auth_configs`,
56
57
  headers: {
57
58
  Authorization: "Basic " +
58
59
  Buffer.from(`${this.auth.username}:${this.auth.password}`).toString("base64"),
@@ -91,7 +92,7 @@ export class AccessibilityAuthConfig {
91
92
  };
92
93
  try {
93
94
  const response = await apiClient.post({
94
- url: "https://api-accessibility.browserstack.com/api/website-scanner/v1/auth_configs",
95
+ url: `${getRegionalHost("accessibilityApi")}/api/website-scanner/v1/auth_configs`,
95
96
  headers: {
96
97
  Authorization: "Basic " +
97
98
  Buffer.from(`${this.auth.username}:${this.auth.password}`).toString("base64"),
@@ -120,7 +121,7 @@ export class AccessibilityAuthConfig {
120
121
  try {
121
122
  // No GET-by-id route exists; list and select.
122
123
  const response = await apiClient.get({
123
- url: "https://api-accessibility.browserstack.com/api/website-scanner/v1/auth_configs",
124
+ url: `${getRegionalHost("accessibilityApi")}/api/website-scanner/v1/auth_configs`,
124
125
  headers: {
125
126
  Authorization: "Basic " +
126
127
  Buffer.from(`${this.auth.username}:${this.auth.password}`).toString("base64"),
@@ -1,4 +1,5 @@
1
1
  import { apiClient } from "../../lib/apiClient.js";
2
+ import { getRegionalHost } from "../../lib/regional-host.js";
2
3
  export class AccessibilityReportFetcher {
3
4
  auth;
4
5
  setAuth(auth) {
@@ -6,7 +7,7 @@ export class AccessibilityReportFetcher {
6
7
  }
7
8
  async getReportLink(scanId, scanRunId) {
8
9
  // Initiate CSV link generation
9
- const initUrl = `https://api-accessibility.browserstack.com/api/website-scanner/v1/scans/${scanId}/scan_runs/issues?scan_run_id=${scanRunId}`;
10
+ const initUrl = `${getRegionalHost("accessibilityApi")}/api/website-scanner/v1/scans/${scanId}/scan_runs/issues?scan_run_id=${scanRunId}`;
10
11
  let basicAuthHeader = undefined;
11
12
  if (this.auth) {
12
13
  const { username, password } = this.auth;
@@ -23,7 +24,7 @@ export class AccessibilityReportFetcher {
23
24
  }
24
25
  const taskId = initData.data.task_id;
25
26
  // Poll for the generated CSV link (task is async, may take a few seconds)
26
- const reportUrl = `https://api-accessibility.browserstack.com/api/website-scanner/v1/scans/${scanId}/scan_runs/issues?task_id=${encodeURIComponent(taskId)}`;
27
+ const reportUrl = `${getRegionalHost("accessibilityApi")}/api/website-scanner/v1/scans/${scanId}/scan_runs/issues?task_id=${encodeURIComponent(taskId)}`;
27
28
  const maxAttempts = 3;
28
29
  const pollIntervalMs = 2000;
29
30
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
@@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto";
3
3
  import logger from "../../logger.js";
4
4
  import { isLocalURL, ensureLocalBinarySetup, killExistingBrowserStackLocalProcesses, } from "../../lib/local.js";
5
5
  import config from "../../config.js";
6
+ import { getRegionalHost } from "../../lib/regional-host.js";
6
7
  export class AccessibilityScanner {
7
8
  auth;
8
9
  setAuth(auth) {
@@ -64,7 +65,7 @@ export class AccessibilityScanner {
64
65
  }
65
66
  try {
66
67
  const response = await apiClient.post({
67
- url: "https://api-accessibility.browserstack.com/api/website-scanner/v1/scans",
68
+ url: `${getRegionalHost("accessibilityApi")}/api/website-scanner/v1/scans`,
68
69
  headers: {
69
70
  Authorization: "Basic " +
70
71
  Buffer.from(`${this.auth.username}:${this.auth.password}`).toString("base64"),
@@ -92,7 +93,7 @@ export class AccessibilityScanner {
92
93
  async pollStatus(scanId, scanRunId) {
93
94
  try {
94
95
  const response = await apiClient.get({
95
- url: `https://api-accessibility.browserstack.com/api/website-scanner/v1/scans/${scanId}/scan_runs/${scanRunId}/status`,
96
+ url: `${getRegionalHost("accessibilityApi")}/api/website-scanner/v1/scans/${scanId}/scan_runs/${scanRunId}/status`,
96
97
  headers: {
97
98
  Authorization: "Basic " +
98
99
  Buffer.from(`${this.auth?.username}:${this.auth?.password}`).toString("base64"),
@@ -5,6 +5,7 @@ import { customFuzzySearch } from "../../../lib/fuzzy.js";
5
5
  import { getBrowserStackAuth } from "../../../lib/get-auth.js";
6
6
  import { validateUploadPath, APP_BINARY_EXTENSIONS, MAX_APP_UPLOAD_BYTES, } from "../../../lib/upload-validator.js";
7
7
  import appConfig from "../../../config.js";
8
+ import { getRegionalHost } from "../../../lib/regional-host.js";
8
9
  /**
9
10
  * Finds devices that exactly match the provided display name.
10
11
  * Uses fuzzy search first, and then filters for exact case-insensitive match.
@@ -84,7 +85,7 @@ export async function uploadApp(appPath, username, password) {
84
85
  const formData = new FormData();
85
86
  formData.append("file", fs.createReadStream(safePath));
86
87
  const response = await apiClient.post({
87
- url: "https://api-cloud.browserstack.com/app-automate/upload",
88
+ url: `${getRegionalHost("appAutomateUpload")}/app-automate/upload`,
88
89
  headers: {
89
90
  ...formData.getHeaders(),
90
91
  Authorization: "Basic " + Buffer.from(`${username}:${password}`).toString("base64"),
@@ -124,25 +125,25 @@ async function uploadFileToBrowserStack(filePath, endpoint, responseKey, config,
124
125
  }
125
126
  //Uploads an Android app (.apk or .aab) to BrowserStack Espresso endpoint and returns the app_url
126
127
  export async function uploadEspressoApp(appPath, config) {
127
- return uploadFileToBrowserStack(appPath, "https://api-cloud.browserstack.com/app-automate/espresso/v2/app", "app_url", config, { allowedExtensions: [".apk", ".aab"] });
128
+ return uploadFileToBrowserStack(appPath, `${getRegionalHost("appAutomateUpload")}/app-automate/espresso/v2/app`, "app_url", config, { allowedExtensions: [".apk", ".aab"] });
128
129
  }
129
130
  //Uploads an Espresso test suite (.apk) to BrowserStack and returns the test_suite_url
130
131
  export async function uploadEspressoTestSuite(testSuitePath, config) {
131
- return uploadFileToBrowserStack(testSuitePath, "https://api-cloud.browserstack.com/app-automate/espresso/v2/test-suite", "test_suite_url", config, { allowedExtensions: [".apk"] });
132
+ return uploadFileToBrowserStack(testSuitePath, `${getRegionalHost("appAutomateUpload")}/app-automate/espresso/v2/test-suite`, "test_suite_url", config, { allowedExtensions: [".apk"] });
132
133
  }
133
134
  //Uploads an iOS app (.ipa) to BrowserStack XCUITest endpoint and returns the app_url
134
135
  export async function uploadXcuiApp(appPath, config) {
135
- return uploadFileToBrowserStack(appPath, "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/app", "app_url", config, { allowedExtensions: [".ipa"] });
136
+ return uploadFileToBrowserStack(appPath, `${getRegionalHost("appAutomateUpload")}/app-automate/xcuitest/v2/app`, "app_url", config, { allowedExtensions: [".ipa"] });
136
137
  }
137
138
  //Uploads an XCUITest test suite (.zip) to BrowserStack and returns the test_suite_url
138
139
  export async function uploadXcuiTestSuite(testSuitePath, config) {
139
- return uploadFileToBrowserStack(testSuitePath, "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/test-suite", "test_suite_url", config, { allowedExtensions: [".zip"] });
140
+ return uploadFileToBrowserStack(testSuitePath, `${getRegionalHost("appAutomateUpload")}/app-automate/xcuitest/v2/test-suite`, "test_suite_url", config, { allowedExtensions: [".zip"] });
140
141
  }
141
142
  // Triggers an Espresso test run on BrowserStack and returns the build_id
142
143
  export async function triggerEspressoBuild(app_url, test_suite_url, devices, project, config) {
143
144
  const authHeader = "Basic " + Buffer.from(getBrowserStackAuth(config)).toString("base64");
144
145
  const response = await apiClient.post({
145
- url: "https://api-cloud.browserstack.com/app-automate/espresso/v2/build",
146
+ url: `${getRegionalHost("appAutomateUpload")}/app-automate/espresso/v2/build`,
146
147
  headers: {
147
148
  Authorization: authHeader,
148
149
  "Content-Type": "application/json",
@@ -166,7 +167,7 @@ export async function triggerXcuiBuild(app_url, test_suite_url, devices, project
166
167
  password: config["browserstack-access-key"],
167
168
  };
168
169
  const response = await apiClient.post({
169
- url: "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/build",
170
+ url: `${getRegionalHost("appAutomateUpload")}/app-automate/xcuitest/v2/build`,
170
171
  headers: {
171
172
  Authorization: "Basic " +
172
173
  Buffer.from(`${auth.username}:${auth.password}`).toString("base64"),
@@ -12,6 +12,7 @@ import { Platform, } from "./appautomate-utils/native-execution/types.js";
12
12
  import { getDevicesAndBrowsers, BrowserStackProducts, } from "../lib/device-cache.js";
13
13
  import { findMatchingDevice, getDeviceVersions, resolveVersion, validateArgs, uploadApp, uploadEspressoApp, uploadEspressoTestSuite, triggerEspressoBuild, uploadXcuiApp, uploadXcuiTestSuite, triggerXcuiBuild, } from "./appautomate-utils/native-execution/appautomate.js";
14
14
  import { RUN_APP_AUTOMATE_DESCRIPTION, RUN_APP_AUTOMATE_SCHEMA, } from "./appautomate-utils/native-execution/constants.js";
15
+ import { getRegionalHost } from "../lib/regional-host.js";
15
16
  /**
16
17
  * Launches an app on a selected BrowserStack device and takes a screenshot.
17
18
  */
@@ -63,7 +64,7 @@ async function takeAppScreenshot(args) {
63
64
  try {
64
65
  driver = await remote({
65
66
  protocol: "https",
66
- hostname: "hub.browserstack.com",
67
+ hostname: getRegionalHost("appAutomateHub"),
67
68
  port: 443,
68
69
  path: "/wd/hub",
69
70
  capabilities,
@@ -3,6 +3,7 @@ import { SessionType } from "../../lib/constants.js";
3
3
  import { getBrowserStackAuth } from "../../lib/get-auth.js";
4
4
  import { apiClient } from "../../lib/apiClient.js";
5
5
  import globalConfig from "../../config.js";
6
+ import { getRegionalHost } from "../../lib/regional-host.js";
6
7
  import { MAX_LOG_BYTES } from "../failurelogs-utils/utils.js";
7
8
  export const MAX_SCREENSHOT_BYTES = globalConfig.REMOTE_MCP
8
9
  ? 10 * 1024 * 1024
@@ -10,7 +11,9 @@ export const MAX_SCREENSHOT_BYTES = globalConfig.REMOTE_MCP
10
11
  async function extractScreenshotUrls(sessionId, sessionType, config) {
11
12
  const authString = getBrowserStackAuth(config);
12
13
  const auth = Buffer.from(authString).toString("base64");
13
- const baseUrl = `https://api.browserstack.com/${sessionType === SessionType.Automate ? "automate" : "app-automate"}`;
14
+ const isAutomate = sessionType === SessionType.Automate;
15
+ const host = getRegionalHost(isAutomate ? "automateApi" : "appAutomateApi");
16
+ const baseUrl = `${host}/${isAutomate ? "automate" : "app-automate"}`;
14
17
  const url = `${baseUrl}/sessions/${sessionId}/logs`;
15
18
  const response = await apiClient.get({
16
19
  url,
@@ -23,6 +23,6 @@ export interface SessionIdRecord {
23
23
  browserUrl?: string;
24
24
  videoUrl?: string;
25
25
  }
26
- export declare function sessionsListUrl(sessionType: SessionType, buildId: string): string;
26
+ export declare function sessionsListUrl(sessionType: SessionType, buildId: string, host?: string): string;
27
27
  export declare function mapSessionRecords(payload: unknown, statusFilter?: string): SessionIdRecord[];
28
28
  export declare function listSessionIds(args: ListSessionIdsArgs, config: BrowserStackConfig): Promise<SessionIdRecord[]>;
@@ -1,6 +1,7 @@
1
1
  import { SessionType } from "../../lib/constants.js";
2
2
  import { getBrowserStackAuth } from "../../lib/get-auth.js";
3
3
  import { apiClient } from "../../lib/apiClient.js";
4
+ import { getRegionalHost } from "../../lib/regional-host.js";
4
5
  export const DEFAULT_SESSION_LIST_LIMIT = 10;
5
6
  /** The REST session list returned 404: no Automate/App Automate build has this hashed id. */
6
7
  export class UnknownBuildError extends Error {
@@ -9,13 +10,13 @@ export class UnknownBuildError extends Error {
9
10
  this.name = "UnknownBuildError";
10
11
  }
11
12
  }
12
- export function sessionsListUrl(sessionType, buildId) {
13
+ export function sessionsListUrl(sessionType, buildId, host) {
13
14
  const encodedBuildId = encodeURIComponent(buildId);
14
15
  switch (sessionType) {
15
16
  case SessionType.Automate:
16
- return `https://api.browserstack.com/automate/builds/${encodedBuildId}/sessions.json`;
17
+ return `${host ?? "https://api.browserstack.com"}/automate/builds/${encodedBuildId}/sessions.json`;
17
18
  case SessionType.AppAutomate:
18
- return `https://api-cloud.browserstack.com/app-automate/builds/${encodedBuildId}/sessions.json`;
19
+ return `${host ?? "https://api-cloud.browserstack.com"}/app-automate/builds/${encodedBuildId}/sessions.json`;
19
20
  default: {
20
21
  const _exhaustive = sessionType;
21
22
  throw new Error(`Unsupported session type: ${_exhaustive}`);
@@ -66,7 +67,9 @@ export async function listSessionIds(args, config) {
66
67
  params.offset = args.offset;
67
68
  }
68
69
  const response = await apiClient.get({
69
- url: sessionsListUrl(args.sessionType, buildId),
70
+ url: sessionsListUrl(args.sessionType, buildId, getRegionalHost(args.sessionType === SessionType.Automate
71
+ ? "automateApi"
72
+ : "appAutomateUpload")),
70
73
  headers: {
71
74
  "Content-Type": "application/json",
72
75
  Authorization: `Basic ${auth}`,
@@ -2,7 +2,7 @@ import { SessionType } from "../../lib/constants.js";
2
2
  import { BrowserStackConfig } from "../../lib/types.js";
3
3
  export declare function isObservabilityBuildUuid(id: string): boolean;
4
4
  export declare function isHashedBuildId(id: string): boolean;
5
- export declare function sessionDetailsUrl(sessionType: SessionType, sessionId: string): string;
5
+ export declare function sessionDetailsUrl(sessionType: SessionType, sessionId: string, host?: string): string;
6
6
  /**
7
7
  * Resolve the hashed build id that a session belongs to via the Automate /
8
8
  * App Automate session detail endpoint. Returns undefined when the session
@@ -2,8 +2,8 @@ import { SessionType } from "../../lib/constants.js";
2
2
  import { getBrowserStackAuth } from "../../lib/get-auth.js";
3
3
  import { apiClient } from "../../lib/apiClient.js";
4
4
  import logger from "../../logger.js";
5
- import { getAutomationBaseUrl } from "../rca-agent-utils/constants.js";
6
5
  import { extractTestIds } from "../rca-agent-utils/get-failed-test-id.js";
6
+ import { getRegionalHost } from "../../lib/regional-host.js";
7
7
  // Observability (Test Reporting & Analytics) build ids are usually UUIDs but
8
8
  // some are 40-char hex, the same shape as Automate / App Automate "hashed ids".
9
9
  // The two are never interchangeable, and the observability build API does not
@@ -19,13 +19,13 @@ export function isObservabilityBuildUuid(id) {
19
19
  export function isHashedBuildId(id) {
20
20
  return HASHED_BUILD_ID_RE.test(id.trim());
21
21
  }
22
- export function sessionDetailsUrl(sessionType, sessionId) {
22
+ export function sessionDetailsUrl(sessionType, sessionId, host = "https://api.browserstack.com") {
23
23
  const encoded = encodeURIComponent(sessionId);
24
24
  switch (sessionType) {
25
25
  case SessionType.Automate:
26
- return `https://api.browserstack.com/automate/sessions/${encoded}.json`;
26
+ return `${host}/automate/sessions/${encoded}.json`;
27
27
  case SessionType.AppAutomate:
28
- return `https://api.browserstack.com/app-automate/sessions/${encoded}.json`;
28
+ return `${host}/app-automate/sessions/${encoded}.json`;
29
29
  default: {
30
30
  const _exhaustive = sessionType;
31
31
  throw new Error(`Unsupported session type: ${_exhaustive}`);
@@ -41,7 +41,7 @@ export async function resolveBuildIdFromSession(sessionId, sessionType, config)
41
41
  const authString = getBrowserStackAuth(config);
42
42
  const auth = Buffer.from(authString).toString("base64");
43
43
  const response = await apiClient.get({
44
- url: sessionDetailsUrl(sessionType, sessionId),
44
+ url: sessionDetailsUrl(sessionType, sessionId, getRegionalHost(sessionType === SessionType.Automate ? "automateApi" : "appAutomateApi")),
45
45
  headers: {
46
46
  "Content-Type": "application/json",
47
47
  Authorization: `Basic ${auth}`,
@@ -66,7 +66,7 @@ export async function resolveBuildIdFromSession(sessionId, sessionType, config)
66
66
  export async function findSessionIdForObservabilityBuild(observabilityBuildId, config) {
67
67
  const authString = getBrowserStackAuth(config);
68
68
  const auth = Buffer.from(authString).toString("base64");
69
- const baseUrl = `${getAutomationBaseUrl()}/ext/v1/builds/${encodeURIComponent(observabilityBuildId)}/testRuns`;
69
+ const baseUrl = `${getRegionalHost("automationApi")}/ext/v1/builds/${encodeURIComponent(observabilityBuildId)}/testRuns`;
70
70
  let nextPage;
71
71
  for (let page = 0; page < MAX_TEST_RUN_PAGES; page++) {
72
72
  const response = await apiClient.get({
@@ -3,11 +3,13 @@ import logger from "../logger.js";
3
3
  import { fetchFromBrowserStackAPI, handleMCPError } from "../lib/utils.js";
4
4
  import { trackMCP } from "../lib/instrumentation.js";
5
5
  import { resolveHashedBuildId } from "./automate-utils/resolve-hashed-build-id.js";
6
+ import { getRegionalHost } from "../lib/regional-host.js";
6
7
  // Tool function that fetches build insights from two APIs
7
8
  export async function fetchBuildInsightsTool(args, config) {
8
9
  try {
9
- const buildUrl = `https://api-automation.browserstack.com/ext/v1/builds/${args.buildId}`;
10
- const qualityGateUrl = `https://api-automation.browserstack.com/ext/v1/quality-gates/${args.buildId}`;
10
+ const host = getRegionalHost("automationApi");
11
+ const buildUrl = `${host}/ext/v1/builds/${args.buildId}`;
12
+ const qualityGateUrl = `${host}/ext/v1/quality-gates/${args.buildId}`;
11
13
  // Quality gate data is optional — a failure there should not block build insights
12
14
  const [buildData, qualityData] = await Promise.all([
13
15
  fetchFromBrowserStackAPI(buildUrl, config),
@@ -2,9 +2,10 @@ import { getBrowserStackAuth } from "../../lib/get-auth.js";
2
2
  import { filterLinesByKeywords, MAX_LOG_BYTES, validateLogResponse, } from "./utils.js";
3
3
  import { apiClient } from "../../lib/apiClient.js";
4
4
  import { wrapUntrusted } from "../../lib/untrusted-content.js";
5
+ import { getRegionalHost } from "../../lib/regional-host.js";
5
6
  // DEVICE LOGS
6
7
  export async function retrieveDeviceLogs(sessionId, buildId, config) {
7
- const url = `https://api.browserstack.com/app-automate/builds/${buildId}/sessions/${sessionId}/deviceLogs`;
8
+ const url = `${getRegionalHost("appAutomateApi")}/app-automate/builds/${buildId}/sessions/${sessionId}/deviceLogs`;
8
9
  const authString = getBrowserStackAuth(config);
9
10
  const auth = Buffer.from(authString).toString("base64");
10
11
  const response = await apiClient.get({
@@ -29,7 +30,7 @@ export async function retrieveDeviceLogs(sessionId, buildId, config) {
29
30
  }
30
31
  // APPIUM LOGS
31
32
  export async function retrieveAppiumLogs(sessionId, buildId, config) {
32
- const url = `https://api.browserstack.com/app-automate/builds/${buildId}/sessions/${sessionId}/appiumlogs`;
33
+ const url = `${getRegionalHost("appAutomateApi")}/app-automate/builds/${buildId}/sessions/${sessionId}/appiumlogs`;
33
34
  const authString = getBrowserStackAuth(config);
34
35
  const auth = Buffer.from(authString).toString("base64");
35
36
  const response = await apiClient.get({
@@ -54,7 +55,7 @@ export async function retrieveAppiumLogs(sessionId, buildId, config) {
54
55
  }
55
56
  // CRASH LOGS
56
57
  export async function retrieveCrashLogs(sessionId, buildId, config) {
57
- const url = `https://api.browserstack.com/app-automate/builds/${buildId}/sessions/${sessionId}/crashlogs`;
58
+ const url = `${getRegionalHost("appAutomateApi")}/app-automate/builds/${buildId}/sessions/${sessionId}/crashlogs`;
58
59
  const authString = getBrowserStackAuth(config);
59
60
  const auth = Buffer.from(authString).toString("base64");
60
61
  const response = await apiClient.get({
@@ -2,9 +2,10 @@ import { getBrowserStackAuth } from "../../lib/get-auth.js";
2
2
  import { wrapUntrusted } from "../../lib/untrusted-content.js";
3
3
  import { filterLinesByKeywords, MAX_LOG_BYTES, validateLogResponse, } from "./utils.js";
4
4
  import { apiClient } from "../../lib/apiClient.js";
5
+ import { getRegionalHost } from "../../lib/regional-host.js";
5
6
  // NETWORK LOGS
6
7
  export async function retrieveNetworkFailures(sessionId, config) {
7
- const url = `https://api.browserstack.com/automate/sessions/${sessionId}/networklogs`;
8
+ const url = `${getRegionalHost("automateApi")}/automate/sessions/${sessionId}/networklogs`;
8
9
  const authString = getBrowserStackAuth(config);
9
10
  const auth = Buffer.from(authString).toString("base64");
10
11
  const response = await apiClient.get({
@@ -43,7 +44,7 @@ export async function retrieveNetworkFailures(sessionId, config) {
43
44
  }
44
45
  // SESSION LOGS
45
46
  export async function retrieveSessionFailures(sessionId, config) {
46
- const url = `https://api.browserstack.com/automate/sessions/${sessionId}/logs`;
47
+ const url = `${getRegionalHost("automateApi")}/automate/sessions/${sessionId}/logs`;
47
48
  const authString = getBrowserStackAuth(config);
48
49
  const auth = Buffer.from(authString).toString("base64");
49
50
  const response = await apiClient.get({
@@ -68,7 +69,7 @@ export async function retrieveSessionFailures(sessionId, config) {
68
69
  }
69
70
  // CONSOLE LOGS
70
71
  export async function retrieveConsoleFailures(sessionId, config) {
71
- const url = `https://api.browserstack.com/automate/sessions/${sessionId}/consolelogs`;
72
+ const url = `${getRegionalHost("automateApi")}/automate/sessions/${sessionId}/consolelogs`;
72
73
  const authString = getBrowserStackAuth(config);
73
74
  const auth = Buffer.from(authString).toString("base64");
74
75
  const response = await apiClient.get({
@@ -2,9 +2,11 @@ import { getBrowserStackAuth } from "../../lib/get-auth.js";
2
2
  import { apiClient } from "../../lib/apiClient.js";
3
3
  import { SessionType } from "../../lib/constants.js";
4
4
  import { validateLogResponse } from "./utils.js";
5
+ import { getRegionalHost } from "../../lib/regional-host.js";
5
6
  export async function retrieveSessionVideo(sessionId, sessionType, config) {
6
7
  const product = sessionType === SessionType.AppAutomate ? "app-automate" : "automate";
7
- const url = `https://api.browserstack.com/${product}/sessions/${encodeURIComponent(sessionId)}.json`;
8
+ const host = getRegionalHost(sessionType === SessionType.AppAutomate ? "appAutomateApi" : "automateApi");
9
+ const url = `${host}/${product}/sessions/${encodeURIComponent(sessionId)}.json`;
8
10
  const authString = getBrowserStackAuth(config);
9
11
  const auth = Buffer.from(authString).toString("base64");
10
12
  const response = await apiClient.get({
@@ -1,5 +1,7 @@
1
+ import { getRegionalHost } from "../../lib/regional-host.js";
1
2
  export async function getBuildId(projectName, buildName, username, accessKey) {
2
- const url = new URL("https://api-automation.browserstack.com/ext/v1/builds/latest");
3
+ const host = getRegionalHost("automationApi");
4
+ const url = new URL(`${host}/ext/v1/builds/latest`);
3
5
  url.searchParams.append("project_name", projectName);
4
6
  url.searchParams.append("build_name", buildName);
5
7
  url.searchParams.append("user_name", username);
@@ -1,4 +1,4 @@
1
1
  import { TestStatus, FailedTestInfo, TestDetails } from "./types.js";
2
- export declare function getTestIds(buildId: string, authString: string, status?: TestStatus, includeFailureDetail?: boolean): Promise<FailedTestInfo[]>;
2
+ export declare function getTestIds(buildId: string, authString: string, status?: TestStatus, includeFailureDetail?: boolean, host?: string): Promise<FailedTestInfo[]>;
3
3
  export declare function extractTestIds(hierarchy: TestDetails[], status?: TestStatus, includeFailureDetail?: boolean): FailedTestInfo[];
4
4
  export declare const extractFailedTestIds: typeof extractTestIds;
@@ -5,9 +5,9 @@ import { TestStatus, } from "./types.js";
5
5
  const ERROR_SUMMARY_MAX = 200;
6
6
  // Safety bound on pagination; hitting it is logged, never silent.
7
7
  const MAX_PAGES = 100;
8
- export async function getTestIds(buildId, authString, status, includeFailureDetail = false) {
8
+ export async function getTestIds(buildId, authString, status, includeFailureDetail = false, host = getAutomationBaseUrl()) {
9
9
  // No `status` → returns ALL tests; a `status` narrows the query.
10
- const baseUrl = `${getAutomationBaseUrl()}/ext/v1/builds/${buildId}/testRuns`;
10
+ const baseUrl = `${host}/ext/v1/builds/${buildId}/testRuns`;
11
11
  let url = status ? `${baseUrl}?test_statuses=${status}` : baseUrl;
12
12
  let allTests = [];
13
13
  let requestNumber = 0;
@@ -1,11 +1,13 @@
1
1
  import { apiClient } from "../../lib/apiClient.js";
2
+ import { getRegionalHost } from "../../lib/regional-host.js";
2
3
  /**
3
4
  * Returns the latest build for a project + build name across all users (not just the caller's).
4
5
  */
5
6
  export async function listBuildId(projectName, buildName, username, accessKey) {
6
7
  const authHeader = "Basic " + Buffer.from(`${username}:${accessKey}`).toString("base64");
8
+ const host = getRegionalHost("automationApi");
7
9
  const response = await apiClient.get({
8
- url: "https://api-automation.browserstack.com/ext/v1/builds/latest",
10
+ url: `${host}/ext/v1/builds/latest`,
9
11
  headers: {
10
12
  Authorization: authHeader,
11
13
  "Content-Type": "application/json",
@@ -5,5 +5,5 @@ interface ScanProgressContext {
5
5
  progressToken?: string | number;
6
6
  };
7
7
  }
8
- export declare function getRCAData(testIds: number[], authString: string, context?: ScanProgressContext): Promise<RCAResponse>;
8
+ export declare function getRCAData(testIds: number[], authString: string, context?: ScanProgressContext, host?: string): Promise<RCAResponse>;
9
9
  export {};
@@ -177,11 +177,11 @@ async function pollRCAResults(testCases, headers, baseUrl, context, pollInterval
177
177
  }
178
178
  return { testCases };
179
179
  }
180
- export async function getRCAData(testIds, authString, context) {
180
+ export async function getRCAData(testIds, authString, context, host = "https://api-observability.browserstack.com") {
181
181
  const pollInterval = 5000;
182
182
  const timeout = 40000;
183
183
  const initialDelay = 20000;
184
- const baseUrl = "https://api-observability.browserstack.com/ext/v1/testRun/{testId}/testRca";
184
+ const baseUrl = `${host}/ext/v1/testRun/{testId}/testRca`;
185
185
  const headers = {
186
186
  Authorization: `Basic ${Buffer.from(authString).toString("base64")}`,
187
187
  "Content-Type": "application/json",
@@ -9,6 +9,7 @@ import { handleMCPError } from "../lib/utils.js";
9
9
  import { trackMCP } from "../index.js";
10
10
  import { NEEDS_BUILD_ID, NEEDS_TEST_IDS } from "./tool-handoff.js";
11
11
  import { FETCH_RCA_PARAMS, GET_BUILD_ID_PARAMS, LIST_TEST_IDS_PARAMS, } from "./rca-agent-utils/constants.js";
12
+ import { getRegionalHost } from "../lib/regional-host.js";
12
13
  // Tool function to fetch build ID
13
14
  export async function getBuildIdTool(args, config) {
14
15
  try {
@@ -75,7 +76,7 @@ export async function fetchRCADataTool(args, config) {
75
76
  const authString = getBrowserStackAuth(config);
76
77
  // Limit to first 3 test IDs for performance
77
78
  const testIds = args.testId;
78
- const rcaData = await getRCAData(testIds, authString);
79
+ const rcaData = await getRCAData(testIds, authString, undefined, getRegionalHost("observabilityApi"));
79
80
  const formattedData = formatRCAData(rcaData);
80
81
  return {
81
82
  content: [
@@ -105,7 +106,7 @@ export async function listTestIdsTool(args, config) {
105
106
  const { buildId, status, includeFailureDetail } = args;
106
107
  const authString = getBrowserStackAuth(config);
107
108
  // Get test IDs
108
- const testIds = await getTestIds(buildId, authString, status, includeFailureDetail);
109
+ const testIds = await getTestIds(buildId, authString, status, includeFailureDetail, getRegionalHost("automationApi"));
109
110
  return {
110
111
  content: [
111
112
  {
@@ -1,7 +1,7 @@
1
1
  import { getBrowserStackAuth } from "../../lib/get-auth.js";
2
2
  import { apiClient } from "../../lib/apiClient.js";
3
3
  import logger from "../../logger.js";
4
- const OBSERVABILITY_API_BASE = "https://api-automation.browserstack.com/ext/v1";
4
+ import { getRegionalHost } from "../../lib/regional-host.js";
5
5
  /**
6
6
  * Normalizes the testCode response. The Observability API used to return a
7
7
  * bare array of TestCodeEntry, but now wraps it as
@@ -43,7 +43,7 @@ export async function fetchTestCodeBySession(sessionId, config) {
43
43
  }
44
44
  const authString = getBrowserStackAuth(config);
45
45
  const auth = Buffer.from(authString).toString("base64");
46
- const url = `${OBSERVABILITY_API_BASE}/sessions/${encodeURIComponent(sessionId)}/testCode`;
46
+ const url = `${getRegionalHost("automationApi")}/ext/v1/sessions/${encodeURIComponent(sessionId)}/testCode`;
47
47
  try {
48
48
  const response = await apiClient.get({
49
49
  url,
@@ -1,7 +1,7 @@
1
1
  import { assertOkResponse } from "../../lib/utils.js";
2
2
  import { getBrowserStackAuth } from "../../lib/get-auth.js";
3
3
  import { apiClient } from "../../lib/apiClient.js";
4
- const SELF_HEAL_REPORT_BASE = "https://api-automation.browserstack.com/ext/v1/builds";
4
+ import { getRegionalHost } from "../../lib/regional-host.js";
5
5
  /**
6
6
  * Fetches the self-healing report for a build via the build-scoped API.
7
7
  *
@@ -19,7 +19,7 @@ export async function fetchSelfHealingReportByBuild(buildUuid, config) {
19
19
  const authString = getBrowserStackAuth(config);
20
20
  const auth = Buffer.from(authString).toString("base64");
21
21
  const presignedResp = await apiClient.get({
22
- url: `${SELF_HEAL_REPORT_BASE}/${encodeURIComponent(buildUuid)}/selfHealingReport`,
22
+ url: `${getRegionalHost("automationApi")}/ext/v1/builds/${encodeURIComponent(buildUuid)}/selfHealingReport`,
23
23
  headers: {
24
24
  "Content-Type": "application/json",
25
25
  Authorization: `Basic ${auth}`,
@@ -67,7 +67,8 @@ export async function getSelfHealSelectors(sessionId, config, sessionType = "aut
67
67
  const authString = getBrowserStackAuth(config);
68
68
  const auth = Buffer.from(authString).toString("base64");
69
69
  const productPath = sessionType === "app-automate" ? "app-automate" : "automate";
70
- const url = `https://api.browserstack.com/${productPath}/sessions/${sessionId}/logs`;
70
+ const host = getRegionalHost(sessionType === "app-automate" ? "appAutomateApi" : "automateApi");
71
+ const url = `${host}/${productPath}/sessions/${sessionId}/logs`;
71
72
  const response = await apiClient.get({
72
73
  url,
73
74
  headers: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@browserstack/mcp-server",
3
- "version": "2.2.3-beta.1",
3
+ "version": "2.2.3-beta.2",
4
4
  "description": "BrowserStack's Official MCP Server",
5
5
  "mcpName": "io.github.browserstack/mcp-server",
6
6
  "main": "dist/index.js",