@browserstack/mcp-server 1.0.13 → 1.0.14

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 (52) hide show
  1. package/README.md +22 -0
  2. package/dist/config.js +2 -6
  3. package/dist/index.js +31 -32
  4. package/dist/lib/api.js +3 -57
  5. package/dist/lib/constants.js +14 -0
  6. package/dist/lib/device-cache.js +32 -33
  7. package/dist/lib/error.js +3 -6
  8. package/dist/lib/fuzzy.js +1 -4
  9. package/dist/lib/inmemory-store.js +1 -0
  10. package/dist/lib/instrumentation.js +12 -18
  11. package/dist/lib/local.js +27 -35
  12. package/dist/lib/utils.js +29 -4
  13. package/dist/logger.js +4 -9
  14. package/dist/tools/accessibility.js +9 -12
  15. package/dist/tools/accessiblity-utils/accessibility.js +14 -22
  16. package/dist/tools/appautomate-utils/appautomate.js +95 -0
  17. package/dist/tools/appautomate.js +116 -0
  18. package/dist/tools/applive-utils/fuzzy-search.js +3 -6
  19. package/dist/tools/applive-utils/start-session.js +14 -20
  20. package/dist/tools/applive-utils/upload-app.js +12 -51
  21. package/dist/tools/applive.js +18 -25
  22. package/dist/tools/automate-utils/fetch-screenshots.js +59 -0
  23. package/dist/tools/automate.js +44 -37
  24. package/dist/tools/bstack-sdk.js +14 -18
  25. package/dist/tools/failurelogs-utils/app-automate.js +88 -0
  26. package/dist/tools/failurelogs-utils/automate.js +97 -0
  27. package/dist/tools/getFailureLogs.js +173 -0
  28. package/dist/tools/live-utils/desktop-filter.js +8 -11
  29. package/dist/tools/live-utils/mobile-filter.js +7 -10
  30. package/dist/tools/live-utils/start-session.js +17 -23
  31. package/dist/tools/live-utils/types.js +2 -5
  32. package/dist/tools/live-utils/version-resolver.js +1 -4
  33. package/dist/tools/live.js +23 -29
  34. package/dist/tools/observability.js +12 -19
  35. package/dist/tools/sdk-utils/constants.js +3 -9
  36. package/dist/tools/sdk-utils/instructions.js +4 -9
  37. package/dist/tools/sdk-utils/types.js +1 -2
  38. package/dist/tools/testmanagement-utils/TCG-utils/api.js +259 -0
  39. package/dist/tools/testmanagement-utils/TCG-utils/config.js +5 -0
  40. package/dist/tools/testmanagement-utils/TCG-utils/helpers.js +53 -0
  41. package/dist/tools/testmanagement-utils/TCG-utils/types.js +8 -0
  42. package/dist/tools/testmanagement-utils/add-test-result.js +18 -25
  43. package/dist/tools/testmanagement-utils/create-project-folder.js +21 -28
  44. package/dist/tools/testmanagement-utils/create-testcase.js +30 -38
  45. package/dist/tools/testmanagement-utils/create-testrun.js +23 -30
  46. package/dist/tools/testmanagement-utils/list-testcases.js +16 -23
  47. package/dist/tools/testmanagement-utils/list-testruns.js +13 -20
  48. package/dist/tools/testmanagement-utils/testcase-from-file.js +42 -0
  49. package/dist/tools/testmanagement-utils/update-testrun.js +16 -23
  50. package/dist/tools/testmanagement-utils/upload-file.js +101 -0
  51. package/dist/tools/testmanagement.js +98 -61
  52. package/package.json +10 -6
@@ -0,0 +1,101 @@
1
+ import { z } from "zod";
2
+ import axios from "axios";
3
+ import FormData from "form-data";
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import { v4 as uuidv4 } from "uuid";
7
+ import config from "../../config.js";
8
+ import { signedUrlMap } from "../../lib/inmemory-store.js";
9
+ import { projectIdentifierToId } from "./TCG-utils/api.js";
10
+ /**
11
+ * Schema for the upload file tool
12
+ */
13
+ export const UploadFileSchema = z.object({
14
+ project_identifier: z
15
+ .string()
16
+ .describe("ID of the project where the file should be uploaded. Do not assume it, always ask user for it."),
17
+ file_path: z
18
+ .string()
19
+ .describe("Full path to the file that should be uploaded"),
20
+ });
21
+ /**
22
+ * Uploads a file to BrowserStack Test Management and returns the signed URL.
23
+ */
24
+ export async function uploadFile(args) {
25
+ const { project_identifier, file_path } = args;
26
+ try {
27
+ // Validate file exists
28
+ if (!fs.existsSync(file_path)) {
29
+ return {
30
+ content: [
31
+ {
32
+ type: "text",
33
+ text: `File ${file_path} does not exist.`,
34
+ isError: true,
35
+ },
36
+ ],
37
+ isError: true,
38
+ };
39
+ }
40
+ // Get the project ID
41
+ const projectIdResponse = await projectIdentifierToId(project_identifier);
42
+ const formData = new FormData();
43
+ formData.append("attachments[]", fs.createReadStream(file_path));
44
+ const uploadUrl = `https://test-management.browserstack.com/api/v1/projects/${projectIdResponse}/generic/attachments/ai_uploads`;
45
+ const response = await axios.post(uploadUrl, formData, {
46
+ headers: {
47
+ ...formData.getHeaders(),
48
+ "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
49
+ accept: "application/json, text/plain, */*",
50
+ },
51
+ });
52
+ if (response.status >= 200 &&
53
+ response.status < 300 &&
54
+ response.data.generic_attachment) {
55
+ const attachments = response.data.generic_attachment.map((attachment) => {
56
+ // Generate a unique ID for each attachment
57
+ const fileId = uuidv4();
58
+ // Store the download URL in the signedUrlMap
59
+ const data = {
60
+ fileId: attachment.id,
61
+ downloadUrl: attachment.download_url,
62
+ };
63
+ signedUrlMap.set(fileId, data);
64
+ return {
65
+ name: attachment.name,
66
+ documentID: fileId,
67
+ contentType: attachment.content_type,
68
+ size: attachment.size,
69
+ projectReferenceId: projectIdResponse,
70
+ };
71
+ });
72
+ return {
73
+ content: [
74
+ {
75
+ type: "text",
76
+ text: `Successfully uploaded ${path.basename(file_path)} to BrowserStack Test Management.`,
77
+ },
78
+ {
79
+ type: "text",
80
+ text: JSON.stringify(attachments, null, 2),
81
+ },
82
+ ],
83
+ };
84
+ }
85
+ else {
86
+ throw new Error(`Unexpected response: ${JSON.stringify(response.data)}`);
87
+ }
88
+ }
89
+ catch (error) {
90
+ return {
91
+ content: [
92
+ {
93
+ type: "text",
94
+ text: `Failed to upload file: ${error instanceof Error ? error.message : "Unknown error"}. Please check your credentials and try again.`,
95
+ isError: true,
96
+ },
97
+ ],
98
+ isError: true,
99
+ };
100
+ }
101
+ }
@@ -1,37 +1,28 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.createProjectOrFolderTool = createProjectOrFolderTool;
7
- exports.createTestCaseTool = createTestCaseTool;
8
- exports.listTestCasesTool = listTestCasesTool;
9
- exports.createTestRunTool = createTestRunTool;
10
- exports.listTestRunsTool = listTestRunsTool;
11
- exports.updateTestRunTool = updateTestRunTool;
12
- exports.addTestResultTool = addTestResultTool;
13
- exports.default = addTestManagementTools;
14
- const instrumentation_1 = require("../lib/instrumentation");
15
- const logger_1 = __importDefault(require("../logger"));
16
- const create_project_folder_1 = require("./testmanagement-utils/create-project-folder");
17
- const create_testcase_1 = require("./testmanagement-utils/create-testcase");
1
+ import { trackMCP } from "../lib/instrumentation.js";
2
+ import logger from "../logger.js";
3
+ import { createProjectOrFolder, CreateProjFoldSchema, } from "./testmanagement-utils/create-project-folder.js";
4
+ import { createTestCase as createTestCaseAPI, sanitizeArgs, CreateTestCaseSchema, } from "./testmanagement-utils/create-testcase.js";
18
5
  let serverInstance;
19
- const list_testcases_1 = require("./testmanagement-utils/list-testcases");
20
- const create_testrun_1 = require("./testmanagement-utils/create-testrun");
21
- const list_testruns_1 = require("./testmanagement-utils/list-testruns");
22
- const update_testrun_1 = require("./testmanagement-utils/update-testrun");
23
- const add_test_result_1 = require("./testmanagement-utils/add-test-result");
6
+ import { listTestCases, ListTestCasesSchema, } from "./testmanagement-utils/list-testcases.js";
7
+ import { CreateTestRunSchema, createTestRun, } from "./testmanagement-utils/create-testrun.js";
8
+ import { ListTestRunsSchema, listTestRuns, } from "./testmanagement-utils/list-testruns.js";
9
+ import { UpdateTestRunSchema, updateTestRun, } from "./testmanagement-utils/update-testrun.js";
10
+ import { addTestResult, AddTestResultSchema, } from "./testmanagement-utils/add-test-result.js";
11
+ import { UploadFileSchema, uploadFile, } from "./testmanagement-utils/upload-file.js";
12
+ import { createTestCasesFromFile } from "./testmanagement-utils/testcase-from-file.js";
13
+ import { CreateTestCasesFromFileSchema } from "./testmanagement-utils/TCG-utils/types.js";
14
+ //TODO: Moving the traceMCP and catch block to the parent(server) function
24
15
  /**
25
16
  * Wrapper to call createProjectOrFolder util.
26
17
  */
27
- async function createProjectOrFolderTool(args) {
18
+ export async function createProjectOrFolderTool(args) {
28
19
  try {
29
- (0, instrumentation_1.trackMCP)("createProjectOrFolder", serverInstance.server.getClientVersion());
30
- return await (0, create_project_folder_1.createProjectOrFolder)(args);
20
+ trackMCP("createProjectOrFolder", serverInstance.server.getClientVersion());
21
+ return await createProjectOrFolder(args);
31
22
  }
32
23
  catch (err) {
33
- logger_1.default.error("Failed to create project/folder: %s", err);
34
- (0, instrumentation_1.trackMCP)("createProjectOrFolder", serverInstance.server.getClientVersion(), err);
24
+ logger.error("Failed to create project/folder: %s", err);
25
+ trackMCP("createProjectOrFolder", serverInstance.server.getClientVersion(), err);
35
26
  return {
36
27
  content: [
37
28
  {
@@ -47,16 +38,16 @@ async function createProjectOrFolderTool(args) {
47
38
  /**
48
39
  * Creates a test case in BrowserStack Test Management.
49
40
  */
50
- async function createTestCaseTool(args) {
41
+ export async function createTestCaseTool(args) {
51
42
  // Sanitize input arguments
52
- const cleanedArgs = (0, create_testcase_1.sanitizeArgs)(args);
43
+ const cleanedArgs = sanitizeArgs(args);
53
44
  try {
54
- (0, instrumentation_1.trackMCP)("createTestCase", serverInstance.server.getClientVersion());
55
- return await (0, create_testcase_1.createTestCase)(cleanedArgs);
45
+ trackMCP("createTestCase", serverInstance.server.getClientVersion());
46
+ return await createTestCaseAPI(cleanedArgs);
56
47
  }
57
48
  catch (err) {
58
- logger_1.default.error("Failed to create test case: %s", err);
59
- (0, instrumentation_1.trackMCP)("createTestCase", serverInstance.server.getClientVersion(), err);
49
+ logger.error("Failed to create test case: %s", err);
50
+ trackMCP("createTestCase", serverInstance.server.getClientVersion(), err);
60
51
  return {
61
52
  content: [
62
53
  {
@@ -72,13 +63,13 @@ async function createTestCaseTool(args) {
72
63
  /**
73
64
  * Lists test cases in a project with optional filters (status, priority, custom fields, etc.)
74
65
  */
75
- async function listTestCasesTool(args) {
66
+ export async function listTestCasesTool(args) {
76
67
  try {
77
- (0, instrumentation_1.trackMCP)("listTestCases", serverInstance.server.getClientVersion());
78
- return await (0, list_testcases_1.listTestCases)(args);
68
+ trackMCP("listTestCases", serverInstance.server.getClientVersion());
69
+ return await listTestCases(args);
79
70
  }
80
71
  catch (err) {
81
- (0, instrumentation_1.trackMCP)("listTestCases", serverInstance.server.getClientVersion(), err);
72
+ trackMCP("listTestCases", serverInstance.server.getClientVersion(), err);
82
73
  return {
83
74
  content: [
84
75
  {
@@ -94,13 +85,13 @@ async function listTestCasesTool(args) {
94
85
  /**
95
86
  * Creates a test run in BrowserStack Test Management.
96
87
  */
97
- async function createTestRunTool(args) {
88
+ export async function createTestRunTool(args) {
98
89
  try {
99
- (0, instrumentation_1.trackMCP)("createTestRun", serverInstance.server.getClientVersion());
100
- return await (0, create_testrun_1.createTestRun)(args);
90
+ trackMCP("createTestRun", serverInstance.server.getClientVersion());
91
+ return await createTestRun(args);
101
92
  }
102
93
  catch (err) {
103
- (0, instrumentation_1.trackMCP)("createTestRun", serverInstance.server.getClientVersion(), err);
94
+ trackMCP("createTestRun", serverInstance.server.getClientVersion(), err);
104
95
  return {
105
96
  content: [
106
97
  {
@@ -116,13 +107,13 @@ async function createTestRunTool(args) {
116
107
  /**
117
108
  * Lists test runs in a project with optional filters (date ranges, assignee, state, etc.)
118
109
  */
119
- async function listTestRunsTool(args) {
110
+ export async function listTestRunsTool(args) {
120
111
  try {
121
- (0, instrumentation_1.trackMCP)("listTestRuns", serverInstance.server.getClientVersion());
122
- return await (0, list_testruns_1.listTestRuns)(args);
112
+ trackMCP("listTestRuns", serverInstance.server.getClientVersion());
113
+ return await listTestRuns(args);
123
114
  }
124
115
  catch (err) {
125
- (0, instrumentation_1.trackMCP)("listTestRuns", serverInstance.server.getClientVersion(), err);
116
+ trackMCP("listTestRuns", serverInstance.server.getClientVersion(), err);
126
117
  return {
127
118
  content: [
128
119
  {
@@ -140,13 +131,13 @@ async function listTestRunsTool(args) {
140
131
  * This function allows for partial updates to an existing test run.
141
132
  * It takes the project identifier and test run ID as parameters.
142
133
  */
143
- async function updateTestRunTool(args) {
134
+ export async function updateTestRunTool(args) {
144
135
  try {
145
- (0, instrumentation_1.trackMCP)("updateTestRun", serverInstance.server.getClientVersion());
146
- return await (0, update_testrun_1.updateTestRun)(args);
136
+ trackMCP("updateTestRun", serverInstance.server.getClientVersion());
137
+ return await updateTestRun(args);
147
138
  }
148
139
  catch (err) {
149
- (0, instrumentation_1.trackMCP)("updateTestRun", serverInstance.server.getClientVersion(), err);
140
+ trackMCP("updateTestRun", serverInstance.server.getClientVersion(), err);
150
141
  return {
151
142
  content: [
152
143
  {
@@ -162,13 +153,13 @@ async function updateTestRunTool(args) {
162
153
  /**
163
154
  * Adds a test result to a specific test run via BrowserStack Test Management API.
164
155
  */
165
- async function addTestResultTool(args) {
156
+ export async function addTestResultTool(args) {
166
157
  try {
167
- (0, instrumentation_1.trackMCP)("addTestResult", serverInstance.server.getClientVersion());
168
- return await (0, add_test_result_1.addTestResult)(args);
158
+ trackMCP("addTestResult", serverInstance.server.getClientVersion());
159
+ return await addTestResult(args);
169
160
  }
170
161
  catch (err) {
171
- (0, instrumentation_1.trackMCP)("addTestResult", serverInstance.server.getClientVersion(), err);
162
+ trackMCP("addTestResult", serverInstance.server.getClientVersion(), err);
172
163
  return {
173
164
  content: [
174
165
  {
@@ -181,16 +172,62 @@ async function addTestResultTool(args) {
181
172
  };
182
173
  }
183
174
  }
175
+ /**
176
+ * Uploads files such as PDRs or screenshots to BrowserStack Test Management and get file mapping ID back.
177
+ */
178
+ export async function uploadFileTestManagementTool(args) {
179
+ try {
180
+ trackMCP("uploadFileTestManagement", serverInstance.server.getClientVersion());
181
+ return await uploadFile(args);
182
+ }
183
+ catch (err) {
184
+ trackMCP("uploadFileTestManagement", serverInstance.server.getClientVersion(), err);
185
+ return {
186
+ content: [
187
+ {
188
+ type: "text",
189
+ text: `Failed to upload file: ${err instanceof Error ? err.message : "Unknown error"}. Please open an issue on GitHub if the problem persists`,
190
+ isError: true,
191
+ },
192
+ ],
193
+ isError: true,
194
+ };
195
+ }
196
+ }
197
+ /**
198
+ * Creates test cases from a file in BrowserStack Test Management.
199
+ */
200
+ export async function createTestCasesFromFileTool(args, context) {
201
+ try {
202
+ trackMCP("createTestCasesFromFile", serverInstance.server.getClientVersion());
203
+ return await createTestCasesFromFile(args, context);
204
+ }
205
+ catch (err) {
206
+ trackMCP("createTestCasesFromFile", serverInstance.server.getClientVersion(), err);
207
+ return {
208
+ content: [
209
+ {
210
+ type: "text",
211
+ text: `Failed to create test cases from file: ${err instanceof Error ? err.message : "Unknown error"}. Please open an issue on GitHub if the problem persists`,
212
+ isError: true,
213
+ },
214
+ ],
215
+ isError: true,
216
+ };
217
+ }
218
+ }
184
219
  /**
185
220
  * Registers both project/folder and test-case tools.
186
221
  */
187
- function addTestManagementTools(server) {
222
+ export default function addTestManagementTools(server) {
188
223
  serverInstance = server;
189
- server.tool("createProjectOrFolder", "Create a project and/or folder in BrowserStack Test Management.", create_project_folder_1.CreateProjFoldSchema.shape, createProjectOrFolderTool);
190
- server.tool("createTestCase", "Use this tool to create a test case in BrowserStack Test Management.", create_testcase_1.CreateTestCaseSchema.shape, createTestCaseTool);
191
- server.tool("listTestCases", "List test cases in a project with optional filters (status, priority, custom fields, etc.)", list_testcases_1.ListTestCasesSchema.shape, listTestCasesTool);
192
- server.tool("createTestRun", "Create a test run in BrowserStack Test Management.", create_testrun_1.CreateTestRunSchema.shape, createTestRunTool);
193
- server.tool("listTestRuns", "List test runs in a project with optional filters (date ranges, assignee, state, etc.)", list_testruns_1.ListTestRunsSchema.shape, listTestRunsTool);
194
- server.tool("updateTestRun", "Update a test run in BrowserStack Test Management.", update_testrun_1.UpdateTestRunSchema.shape, updateTestRunTool);
195
- server.tool("addTestResult", "Add a test result to a specific test run via BrowserStack Test Management API.", add_test_result_1.AddTestResultSchema.shape, addTestResultTool);
224
+ server.tool("createProjectOrFolder", "Create a project and/or folder in BrowserStack Test Management.", CreateProjFoldSchema.shape, createProjectOrFolderTool);
225
+ server.tool("createTestCase", "Use this tool to create a test case in BrowserStack Test Management.", CreateTestCaseSchema.shape, createTestCaseTool);
226
+ server.tool("listTestCases", "List test cases in a project with optional filters (status, priority, custom fields, etc.)", ListTestCasesSchema.shape, listTestCasesTool);
227
+ server.tool("createTestRun", "Create a test run in BrowserStack Test Management.", CreateTestRunSchema.shape, createTestRunTool);
228
+ server.tool("listTestRuns", "List test runs in a project with optional filters (date ranges, assignee, state, etc.)", ListTestRunsSchema.shape, listTestRunsTool);
229
+ server.tool("updateTestRun", "Update a test run in BrowserStack Test Management.", UpdateTestRunSchema.shape, updateTestRunTool);
230
+ server.tool("addTestResult", "Add a test result to a specific test run via BrowserStack Test Management API.", AddTestResultSchema.shape, addTestResultTool);
231
+ server.tool("uploadFileTestManagement", "Upload files such as PDRs or PDFs to BrowserStack Test Management and get file mapping ID back. Its Used for generating test cases from file.", UploadFileSchema.shape, uploadFileTestManagementTool);
232
+ server.tool("createTestCasesFromFile", "Create test cases from a file in BrowserStack Test Management.", CreateTestCasesFromFileSchema.shape, createTestCasesFromFileTool);
196
233
  }
package/package.json CHANGED
@@ -1,17 +1,18 @@
1
1
  {
2
2
  "name": "@browserstack/mcp-server",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "BrowserStack's Official MCP Server",
5
5
  "main": "dist/index.js",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/browserstack/mcp-server.git"
9
9
  },
10
+ "type": "module",
10
11
  "scripts": {
11
12
  "build": "npm run lint && npm run format && npm test && tsc",
12
13
  "start": "node dist/index.js",
13
14
  "dev": "tsx watch --clear-screen=false src/index.ts",
14
- "test": "jest",
15
+ "test": "vitest run",
15
16
  "lint": "eslint . --ext .ts",
16
17
  "format": "prettier --write \"src/**/*.ts\""
17
18
  },
@@ -41,18 +42,21 @@
41
42
  "form-data": "^4.0.2",
42
43
  "pino": "^9.6.0",
43
44
  "pino-pretty": "^13.0.0",
45
+ "sharp": "^0.34.1",
46
+ "uuid": "^11.1.0",
47
+ "webdriverio": "^9.13.0",
44
48
  "zod": "^3.24.3"
45
49
  },
46
50
  "devDependencies": {
47
51
  "@eslint/js": "^9.25.0",
48
- "@types/jest": "^29.5.14",
49
52
  "@types/node": "^22.14.1",
53
+ "@types/uuid": "^10.0.0",
50
54
  "eslint": "^9.25.0",
51
- "jest": "^29.7.0",
52
55
  "prettier": "^3.5.3",
53
- "ts-jest": "^29.3.2",
54
56
  "tsx": "^4.19.3",
55
57
  "typescript": "^5.8.3",
56
- "typescript-eslint": "^8.30.1"
58
+ "typescript-eslint": "^8.30.1",
59
+ "vite": "^6.3.5",
60
+ "vitest": "^3.1.3"
57
61
  }
58
62
  }