@browserstack/mcp-server 2.2.0-beta.2 → 2.2.1-beta.1

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.
@@ -6,6 +6,7 @@ type RequestOptions = {
6
6
  body?: any;
7
7
  timeout?: number;
8
8
  responseType?: AxiosRequestConfig["responseType"];
9
+ maxContentLength?: number;
9
10
  raise_error?: boolean;
10
11
  };
11
12
  declare class ApiResponse<T = any> {
@@ -24,7 +25,7 @@ declare class ApiClient {
24
25
  private get axiosAgent();
25
26
  private validateUrl;
26
27
  private requestWrapper;
27
- get<T = any>({ url, headers, params, timeout, responseType, raise_error, }: RequestOptions): Promise<ApiResponse<T>>;
28
+ get<T = any>({ url, headers, params, timeout, responseType, maxContentLength, raise_error, }: RequestOptions): Promise<ApiResponse<T>>;
28
29
  post<T = any>({ url, headers, body, timeout, raise_error, }: RequestOptions): Promise<ApiResponse<T>>;
29
30
  put<T = any>({ url, headers, body, timeout, raise_error, }: RequestOptions): Promise<ApiResponse<T>>;
30
31
  patch<T = any>({ url, headers, body, timeout, raise_error, }: RequestOptions): Promise<ApiResponse<T>>;
@@ -115,15 +115,22 @@ class ApiClient {
115
115
  if (error.response && !raise_error) {
116
116
  return new ApiResponse(error.response);
117
117
  }
118
+ if (!raise_error && /maxContentLength/.test(error.message ?? "")) {
119
+ return new ApiResponse({
120
+ status: 413,
121
+ statusText: "Payload Too Large",
122
+ });
123
+ }
118
124
  throw error;
119
125
  }
120
126
  }
121
- async get({ url, headers, params, timeout, responseType, raise_error = true, }) {
127
+ async get({ url, headers, params, timeout, responseType, maxContentLength, raise_error = true, }) {
122
128
  const config = {
123
129
  headers,
124
130
  params,
125
131
  timeout,
126
132
  responseType,
133
+ maxContentLength,
127
134
  httpsAgent: this.axiosAgent,
128
135
  };
129
136
  return this.requestWrapper(() => this.instance.get(url, config), url, config, raise_error);
@@ -1,5 +1,5 @@
1
1
  import { getBrowserStackAuth } from "../../lib/get-auth.js";
2
- import { filterLinesByKeywords, validateLogResponse } from "./utils.js";
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
5
  // DEVICE LOGS
@@ -13,6 +13,7 @@ export async function retrieveDeviceLogs(sessionId, buildId, config) {
13
13
  "Content-Type": "application/json",
14
14
  Authorization: `Basic ${auth}`,
15
15
  },
16
+ maxContentLength: MAX_LOG_BYTES,
16
17
  raise_error: false,
17
18
  });
18
19
  const validationError = validateLogResponse(response, "device logs");
@@ -37,6 +38,7 @@ export async function retrieveAppiumLogs(sessionId, buildId, config) {
37
38
  "Content-Type": "application/json",
38
39
  Authorization: `Basic ${auth}`,
39
40
  },
41
+ maxContentLength: MAX_LOG_BYTES,
40
42
  raise_error: false,
41
43
  });
42
44
  const validationError = validateLogResponse(response, "Appium logs");
@@ -61,6 +63,7 @@ export async function retrieveCrashLogs(sessionId, buildId, config) {
61
63
  "Content-Type": "application/json",
62
64
  Authorization: `Basic ${auth}`,
63
65
  },
66
+ maxContentLength: MAX_LOG_BYTES,
64
67
  raise_error: false,
65
68
  });
66
69
  const validationError = validateLogResponse(response, "crash logs");
@@ -1,6 +1,6 @@
1
1
  import { getBrowserStackAuth } from "../../lib/get-auth.js";
2
2
  import { wrapUntrusted } from "../../lib/untrusted-content.js";
3
- import { filterLinesByKeywords, validateLogResponse, } from "./utils.js";
3
+ import { filterLinesByKeywords, MAX_LOG_BYTES, validateLogResponse, } from "./utils.js";
4
4
  import { apiClient } from "../../lib/apiClient.js";
5
5
  // NETWORK LOGS
6
6
  export async function retrieveNetworkFailures(sessionId, config) {
@@ -13,13 +13,14 @@ export async function retrieveNetworkFailures(sessionId, config) {
13
13
  "Content-Type": "application/json",
14
14
  Authorization: `Basic ${auth}`,
15
15
  },
16
+ maxContentLength: MAX_LOG_BYTES,
16
17
  raise_error: false,
17
18
  });
18
19
  const validationError = validateLogResponse(response, "network logs");
19
20
  if (validationError)
20
21
  return validationError.message;
21
22
  const networklogs = response.data;
22
- const failureEntries = networklogs.log.entries.filter((entry) => entry.response.status === 0 ||
23
+ const failureEntries = (networklogs?.log?.entries ?? []).filter((entry) => entry.response.status === 0 ||
23
24
  entry.response.status >= 400 ||
24
25
  entry.response._error !== undefined);
25
26
  return failureEntries.length > 0
@@ -51,6 +52,7 @@ export async function retrieveSessionFailures(sessionId, config) {
51
52
  "Content-Type": "application/json",
52
53
  Authorization: `Basic ${auth}`,
53
54
  },
55
+ maxContentLength: MAX_LOG_BYTES,
54
56
  raise_error: false,
55
57
  });
56
58
  const validationError = validateLogResponse(response, "session logs");
@@ -75,6 +77,7 @@ export async function retrieveConsoleFailures(sessionId, config) {
75
77
  "Content-Type": "application/json",
76
78
  Authorization: `Basic ${auth}`,
77
79
  },
80
+ maxContentLength: MAX_LOG_BYTES,
78
81
  raise_error: false,
79
82
  });
80
83
  const validationError = validateLogResponse(response, "console logs");
@@ -1,4 +1,5 @@
1
1
  import type { ApiResponse } from "../../lib/apiClient.js";
2
+ export declare const MAX_LOG_BYTES: number | undefined;
2
3
  export interface LogResponse {
3
4
  logs?: any[];
4
5
  message?: string;
@@ -1,5 +1,12 @@
1
+ import appConfig from "../../config.js";
2
+ export const MAX_LOG_BYTES = appConfig.REMOTE_MCP ? 5 * 1024 * 1024 : undefined;
1
3
  export function validateLogResponse(response, logType) {
2
4
  if (!response.ok) {
5
+ if (response.status === 413) {
6
+ return {
7
+ message: `The ${logType} for this session are too large (over 5 MB) to analyze here. Open the session in the BrowserStack dashboard to view them.`,
8
+ };
9
+ }
3
10
  if (response.status === 404) {
4
11
  return { message: `No ${logType} available for this session` };
5
12
  }
@@ -38,7 +38,7 @@ export function createTestCasePayload(tc, scenarioId, folderId, fieldMaps, docum
38
38
  template: "test_case_steps",
39
39
  metadata: JSON.stringify({
40
40
  ai_prompt: {
41
- attachment_id: documentId,
41
+ attachment_ids: [documentId],
42
42
  rich_text_id: null,
43
43
  scenario: scenarioId,
44
44
  test_case_count: tc.test_case_count || 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@browserstack/mcp-server",
3
- "version": "2.2.0-beta.2",
3
+ "version": "2.2.1-beta.1",
4
4
  "description": "BrowserStack's Official MCP Server",
5
5
  "mcpName": "io.github.browserstack/mcp-server",
6
6
  "main": "dist/index.js",
@@ -16,7 +16,7 @@
16
16
  "test": "vitest run",
17
17
  "lint": "eslint . --ext .ts",
18
18
  "format": "prettier --write \"src/**/*.ts\"",
19
- "check:contract": "python3 scripts/check-contract.py capability/tm.capability-index.json --baseline scripts/contract-baseline/tm.json --quiet",
19
+ "check:contract": "python3 scripts/check-contract.py capability/tm.capability-index.json --baseline scripts/contract-baseline/tm.json --quiet && python3 scripts/check-contract.py capability/loadtesting.capability-index.json --baseline scripts/contract-baseline/loadtesting.json --quiet",
20
20
  "eval:index": "tsx scripts/eval-index.mts",
21
21
  "seed:live": "tsx scripts/seed-live.mts",
22
22
  "seed:live:tra": "tsx scripts/seed-live-tra.mts",
@@ -40,7 +40,7 @@
40
40
  "node": ">=20.9.0"
41
41
  },
42
42
  "author": "",
43
- "license": "ISC",
43
+ "license": "AGPL-3.0-only",
44
44
  "dependencies": {
45
45
  "@modelcontextprotocol/sdk": "^1.29.0",
46
46
  "@types/form-data": "^2.5.2",