@browserstack/mcp-server 1.2.25 → 1.2.27-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.
package/README.md
CHANGED
|
@@ -334,7 +334,7 @@ As of now we support 20 tools.
|
|
|
334
334
|
List all test runs from the 'Shopping App' project that were executed last week and are currently marked in-progress
|
|
335
335
|
```
|
|
336
336
|
|
|
337
|
-
6. `updateTestRun` —
|
|
337
|
+
6. `updateTestRun` — Update a test run's name/state and/or add test cases to it.
|
|
338
338
|
**Prompt example**
|
|
339
339
|
|
|
340
340
|
```text
|
|
@@ -65,7 +65,13 @@ export async function listTestCases(args, config) {
|
|
|
65
65
|
const count = info?.count ?? test_cases.length;
|
|
66
66
|
// Summary for more focused output
|
|
67
67
|
const summary = test_cases
|
|
68
|
-
.map((tc) =>
|
|
68
|
+
.map((tc) => {
|
|
69
|
+
const links = (tc.issues ?? [])
|
|
70
|
+
.filter((i) => i?.issue_type && i?.jira_id)
|
|
71
|
+
.map((i) => `${i.issue_type}:${i.jira_id}`)
|
|
72
|
+
.join(", ");
|
|
73
|
+
return `• ${tc.identifier}: ${tc.title} [${tc.case_type} | ${tc.priority}]${links ? ` {linked: ${links}}` : ""}`;
|
|
74
|
+
})
|
|
69
75
|
.join("\n");
|
|
70
76
|
return {
|
|
71
77
|
content: [
|
|
@@ -17,11 +17,26 @@ export declare const UpdateTestRunSchema: z.ZodObject<{
|
|
|
17
17
|
rejected: "rejected";
|
|
18
18
|
closed: "closed";
|
|
19
19
|
}>>;
|
|
20
|
+
add_test_cases: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
21
|
+
test_case_ids: z.ZodArray<z.ZodString>;
|
|
22
|
+
configuration_ids: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
23
|
+
}, z.core.$strip>>>;
|
|
24
|
+
preserve_existing_results: z.ZodOptional<z.ZodBoolean>;
|
|
20
25
|
}, z.core.$strip>;
|
|
21
26
|
}, z.core.$strip>;
|
|
22
27
|
type UpdateTestRunArgs = z.infer<typeof UpdateTestRunSchema>;
|
|
23
28
|
/**
|
|
24
29
|
* Partially updates an existing test run.
|
|
30
|
+
*
|
|
31
|
+
* Dispatches to one of two BrowserStack endpoints based on the fields provided,
|
|
32
|
+
* mirroring how the platform splits these concerns across two endpoints:
|
|
33
|
+
* - metadata (name / run_state) -> PATCH .../test-runs/{id}/update
|
|
34
|
+
* - adding test cases -> PATCH .../test-runs/{id}/test-cases
|
|
35
|
+
*
|
|
36
|
+
* Either or both may be supplied in one call; each provided concern hits its
|
|
37
|
+
* own endpoint and both outcomes are reported. At least one must be provided.
|
|
38
|
+
* Removing test cases is intentionally not exposed — this tool is
|
|
39
|
+
* non-destructive.
|
|
25
40
|
*/
|
|
26
41
|
export declare function updateTestRun(args: UpdateTestRunArgs, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
27
42
|
export {};
|
|
@@ -3,6 +3,19 @@ import { getBrowserStackAuth } from "../../lib/get-auth.js";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { formatAxiosError } from "../../lib/error.js";
|
|
5
5
|
import { getTMBaseURL } from "../../lib/tm-base-url.js";
|
|
6
|
+
/**
|
|
7
|
+
* Selection of test cases (with optional configurations) to add.
|
|
8
|
+
*/
|
|
9
|
+
const TestCaseSelectionSchema = z.object({
|
|
10
|
+
test_case_ids: z
|
|
11
|
+
.array(z.string())
|
|
12
|
+
.min(1)
|
|
13
|
+
.describe("Test case IDs, e.g. TC-123"),
|
|
14
|
+
configuration_ids: z
|
|
15
|
+
.array(z.number())
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("Configuration IDs to apply"),
|
|
18
|
+
});
|
|
6
19
|
/**
|
|
7
20
|
* Schema for updating a test run with partial fields.
|
|
8
21
|
*/
|
|
@@ -24,22 +37,82 @@ export const UpdateTestRunSchema = z.object({
|
|
|
24
37
|
])
|
|
25
38
|
.optional()
|
|
26
39
|
.describe("Updated state of the test run"),
|
|
40
|
+
add_test_cases: z
|
|
41
|
+
.array(TestCaseSelectionSchema)
|
|
42
|
+
.optional()
|
|
43
|
+
.describe("Test cases to add to the run"),
|
|
44
|
+
preserve_existing_results: z
|
|
45
|
+
.boolean()
|
|
46
|
+
.optional()
|
|
47
|
+
.describe("Keep existing results when adding cases (default true)"),
|
|
27
48
|
}),
|
|
28
49
|
});
|
|
50
|
+
/**
|
|
51
|
+
* Builds the HTTP Basic auth header from per-request config credentials.
|
|
52
|
+
*/
|
|
53
|
+
function buildAuthHeader(config) {
|
|
54
|
+
return "Basic " + Buffer.from(getBrowserStackAuth(config)).toString("base64");
|
|
55
|
+
}
|
|
29
56
|
/**
|
|
30
57
|
* Partially updates an existing test run.
|
|
58
|
+
*
|
|
59
|
+
* Dispatches to one of two BrowserStack endpoints based on the fields provided,
|
|
60
|
+
* mirroring how the platform splits these concerns across two endpoints:
|
|
61
|
+
* - metadata (name / run_state) -> PATCH .../test-runs/{id}/update
|
|
62
|
+
* - adding test cases -> PATCH .../test-runs/{id}/test-cases
|
|
63
|
+
*
|
|
64
|
+
* Either or both may be supplied in one call; each provided concern hits its
|
|
65
|
+
* own endpoint and both outcomes are reported. At least one must be provided.
|
|
66
|
+
* Removing test cases is intentionally not exposed — this tool is
|
|
67
|
+
* non-destructive.
|
|
31
68
|
*/
|
|
32
69
|
export async function updateTestRun(args, config) {
|
|
70
|
+
const { name, run_state, add_test_cases } = args.test_run;
|
|
71
|
+
const addIds = add_test_cases?.flatMap((s) => s.test_case_ids) ?? [];
|
|
72
|
+
const hasTestCases = addIds.length > 0;
|
|
73
|
+
const hasMetadata = name !== undefined || run_state !== undefined;
|
|
74
|
+
if (!hasTestCases && !hasMetadata) {
|
|
75
|
+
return {
|
|
76
|
+
content: [
|
|
77
|
+
{
|
|
78
|
+
type: "text",
|
|
79
|
+
text: "Nothing to update: provide name/run_state and/or add_test_cases.",
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
isError: true,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const tmBaseUrl = await getTMBaseURL(config);
|
|
86
|
+
const authHeader = buildAuthHeader(config);
|
|
87
|
+
const tasks = [];
|
|
88
|
+
if (hasMetadata) {
|
|
89
|
+
tasks.push(updateTestRunMetadata(args, tmBaseUrl, authHeader));
|
|
90
|
+
}
|
|
91
|
+
if (hasTestCases) {
|
|
92
|
+
tasks.push(updateTestRunTestCases(args, tmBaseUrl, authHeader));
|
|
93
|
+
}
|
|
94
|
+
const results = await Promise.all(tasks);
|
|
95
|
+
if (results.length === 1) {
|
|
96
|
+
return results[0];
|
|
97
|
+
}
|
|
98
|
+
// Both concerns updated: aggregate outcomes; surface an error if either failed.
|
|
99
|
+
return {
|
|
100
|
+
content: results.flatMap((r) => r.content),
|
|
101
|
+
isError: results.some((r) => r.isError),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Updates test run metadata (name / run_state) via the /update endpoint.
|
|
106
|
+
*/
|
|
107
|
+
async function updateTestRunMetadata(args, baseUrl, authHeader) {
|
|
33
108
|
try {
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const url = `${
|
|
37
|
-
const authString = getBrowserStackAuth(config);
|
|
38
|
-
const [username, password] = authString.split(":");
|
|
109
|
+
const { name, run_state } = args.test_run;
|
|
110
|
+
const body = { test_run: { name, run_state } };
|
|
111
|
+
const url = `${baseUrl}/api/v2/projects/${encodeURIComponent(args.project_identifier)}/test-runs/${encodeURIComponent(args.test_run_id)}/update`;
|
|
39
112
|
const resp = await apiClient.patch({
|
|
40
113
|
url,
|
|
41
114
|
headers: {
|
|
42
|
-
Authorization:
|
|
115
|
+
Authorization: authHeader,
|
|
43
116
|
"Content-Type": "application/json",
|
|
44
117
|
},
|
|
45
118
|
body,
|
|
@@ -70,3 +143,52 @@ export async function updateTestRun(args, config) {
|
|
|
70
143
|
return formatAxiosError(err, "Failed to update test run");
|
|
71
144
|
}
|
|
72
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Adds test cases to a run via the /test-cases endpoint.
|
|
148
|
+
* This call is applied asynchronously by the backend.
|
|
149
|
+
*/
|
|
150
|
+
async function updateTestRunTestCases(args, baseUrl, authHeader) {
|
|
151
|
+
try {
|
|
152
|
+
const { add_test_cases, preserve_existing_results } = args.test_run;
|
|
153
|
+
const body = {
|
|
154
|
+
test_run: {
|
|
155
|
+
add_test_cases,
|
|
156
|
+
preserve_existing_results: preserve_existing_results ?? true,
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
const url = `${baseUrl}/api/v2/projects/${encodeURIComponent(args.project_identifier)}/test-runs/${encodeURIComponent(args.test_run_id)}/test-cases`;
|
|
160
|
+
const resp = await apiClient.patch({
|
|
161
|
+
url,
|
|
162
|
+
headers: {
|
|
163
|
+
Authorization: authHeader,
|
|
164
|
+
"Content-Type": "application/json",
|
|
165
|
+
},
|
|
166
|
+
body,
|
|
167
|
+
});
|
|
168
|
+
const data = resp.data;
|
|
169
|
+
if (!data.success) {
|
|
170
|
+
return {
|
|
171
|
+
content: [
|
|
172
|
+
{
|
|
173
|
+
type: "text",
|
|
174
|
+
text: `Failed to update test run test cases: ${JSON.stringify(data)}`,
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
isError: true,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const added = add_test_cases?.flatMap((s) => s.test_case_ids) ?? [];
|
|
181
|
+
return {
|
|
182
|
+
content: [
|
|
183
|
+
{
|
|
184
|
+
type: "text",
|
|
185
|
+
text: `Queued test-case update for ${args.test_run_id} (added ${added.length}); changes apply asynchronously.`,
|
|
186
|
+
},
|
|
187
|
+
{ type: "text", text: JSON.stringify(data, null, 2) },
|
|
188
|
+
],
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
return formatAxiosError(err, "Failed to update test run test cases");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -400,7 +400,7 @@ export default function addTestManagementTools(server, config) {
|
|
|
400
400
|
tools.listTestCaseTemplates = server.tool("listTestCaseTemplates", "List test-case templates with their numeric template_id. Use the id with createTestCase to apply a custom template (the 'template' slug only selects system templates).", ListTemplatesSchema.shape, (args) => listTemplatesTool(args, config, server));
|
|
401
401
|
tools.createTestRun = server.tool("createTestRun", "Create a test run in BrowserStack Test Management.", CreateTestRunSchema.shape, (args) => createTestRunTool(args, config, server));
|
|
402
402
|
tools.listTestRuns = server.tool("listTestRuns", "List test runs in a project with optional filters (date ranges, assignee, state, etc.)", ListTestRunsSchema.shape, (args) => listTestRunsTool(args, config, server));
|
|
403
|
-
tools.updateTestRun = server.tool("updateTestRun", "Update a test run
|
|
403
|
+
tools.updateTestRun = server.tool("updateTestRun", "Update a test run's metadata and/or add test cases to it.", UpdateTestRunSchema.shape, (args) => updateTestRunTool(args, config, server));
|
|
404
404
|
tools.addTestResult = server.tool("addTestResult", "Add a test result to a specific test run via BrowserStack Test Management API.", AddTestResultSchema.shape, (args) => addTestResultTool(args, config, server));
|
|
405
405
|
tools.uploadProductRequirementFile = server.tool("uploadProductRequirementFile", "Upload files (e.g., PDRs, PDFs) to BrowserStack Test Management and retrieve a file mapping ID. This is utilized for generating test cases from files and is part of the Test Case Generator AI Agent in BrowserStack.", UploadFileSchema.shape, (args) => uploadProductRequirementFileTool(args, config, server));
|
|
406
406
|
tools.createTestCasesFromFile = server.tool("createTestCasesFromFile", "Generate test cases from a file in BrowserStack Test Management using the Test Case Generator AI Agent.", CreateTestCasesFromFileSchema.shape, (args, context) => createTestCasesFromFileTool(args, context, config, server));
|