@browserstack/mcp-server 1.2.21-beta.2 → 1.2.22-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.
- package/README.md +2 -0
- package/dist/tools/rca-agent-utils/format-rca.js +11 -1
- package/dist/tools/rca-agent.js +1 -1
- package/dist/tools/testmanagement-utils/create-testcase.d.ts +6 -2
- package/dist/tools/testmanagement-utils/create-testcase.js +33 -14
- package/dist/tools/testmanagement-utils/update-testcase.d.ts +2 -2
- package/dist/tools/testmanagement-utils/update-testcase.js +7 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -85,6 +85,8 @@ Similar to the app testing, you can use the following prompts to test your **web
|
|
|
85
85
|
Auto-analyze, diagnose, and even fix broken test scripts right in your IDE or LLM. Instantly fetch logs, identify root causes, and apply context-aware fixes. No more debugging loops.
|
|
86
86
|
Below are few example prompts to run/debug/fix your automated tests on BrowserStack's [Test Platform](https://www.browserstack.com/test-platform).
|
|
87
87
|
|
|
88
|
+
> **Note:** When fetching Root Cause Analysis (RCA) for a test, the server returns the suggested fix as a proposal only. It never applies code changes automatically — your assistant must present the suggestion and wait for your explicit approval before editing any files.
|
|
89
|
+
|
|
88
90
|
```bash
|
|
89
91
|
#Port test suite to BrowserStack
|
|
90
92
|
"Setup test suite to run on BrowserStack infra"
|
|
@@ -4,6 +4,9 @@ export function formatRCAData(rcaData) {
|
|
|
4
4
|
return "No RCA data available.";
|
|
5
5
|
}
|
|
6
6
|
let output = "## Root Cause Analysis Report\n\n";
|
|
7
|
+
// Track whether any test case carries a fix suggestion so we can append the
|
|
8
|
+
// approval-gate directive only when it is relevant.
|
|
9
|
+
let hasFixSuggestion = false;
|
|
7
10
|
rcaData.testCases.forEach((testCase, index) => {
|
|
8
11
|
// Show test case ID with smaller heading
|
|
9
12
|
output += `### Test Case ${index + 1}\n`;
|
|
@@ -22,7 +25,8 @@ export function formatRCAData(rcaData) {
|
|
|
22
25
|
output += `**Detailed Analysis:**\n${rca.description}\n\n`;
|
|
23
26
|
}
|
|
24
27
|
if (rca.possible_fix) {
|
|
25
|
-
|
|
28
|
+
hasFixSuggestion = true;
|
|
29
|
+
output += `**Suggested Fix (proposal only — do not apply without explicit user approval):**\n${rca.possible_fix}\n\n`;
|
|
26
30
|
}
|
|
27
31
|
}
|
|
28
32
|
else if (testCase.rcaData?.error) {
|
|
@@ -33,5 +37,11 @@ export function formatRCAData(rcaData) {
|
|
|
33
37
|
}
|
|
34
38
|
output += "---\n\n";
|
|
35
39
|
});
|
|
40
|
+
if (hasFixSuggestion) {
|
|
41
|
+
output +=
|
|
42
|
+
"> **Action required:** The fixes above are suggestions only. " +
|
|
43
|
+
"Present them to the user and apply code changes ONLY after the user " +
|
|
44
|
+
"explicitly approves. Do not modify any files automatically.\n";
|
|
45
|
+
}
|
|
36
46
|
return output;
|
|
37
47
|
}
|
package/dist/tools/rca-agent.js
CHANGED
|
@@ -99,7 +99,7 @@ export async function listTestIdsTool(args, config) {
|
|
|
99
99
|
}
|
|
100
100
|
export default function addRCATools(server, config) {
|
|
101
101
|
const tools = {};
|
|
102
|
-
tools.fetchRCA = server.tool("fetchRCA", "
|
|
102
|
+
tools.fetchRCA = server.tool("fetchRCA", "Fetch AI Root Cause Analysis for failed BrowserStack Automate/App-Automate tests. Suggests fixes only; never auto-apply, require explicit user approval.", FETCH_RCA_PARAMS, async (args) => {
|
|
103
103
|
try {
|
|
104
104
|
trackMCP("fetchRCA", server.server.getClientVersion(), undefined, config);
|
|
105
105
|
return await fetchRCADataTool(args, config);
|
|
@@ -9,6 +9,7 @@ interface IssueTracker {
|
|
|
9
9
|
name: string;
|
|
10
10
|
host: string;
|
|
11
11
|
}
|
|
12
|
+
export type CustomFieldValue = string | number | boolean | Array<string | number>;
|
|
12
13
|
export interface TestCaseCreateRequest {
|
|
13
14
|
project_identifier: string;
|
|
14
15
|
folder_id: string;
|
|
@@ -20,9 +21,10 @@ export interface TestCaseCreateRequest {
|
|
|
20
21
|
issues?: string[];
|
|
21
22
|
issue_tracker?: IssueTracker;
|
|
22
23
|
tags?: string[];
|
|
23
|
-
custom_fields?: Record<string,
|
|
24
|
+
custom_fields?: Record<string, CustomFieldValue>;
|
|
24
25
|
automation_status?: string;
|
|
25
26
|
priority?: string;
|
|
27
|
+
template?: string;
|
|
26
28
|
}
|
|
27
29
|
export interface TestCaseResponse {
|
|
28
30
|
data: {
|
|
@@ -52,6 +54,7 @@ export interface TestCaseResponse {
|
|
|
52
54
|
};
|
|
53
55
|
};
|
|
54
56
|
}
|
|
57
|
+
export declare const customFieldValueSchema: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>]>;
|
|
55
58
|
export declare const CreateTestCaseSchema: z.ZodObject<{
|
|
56
59
|
project_identifier: z.ZodString;
|
|
57
60
|
folder_id: z.ZodString;
|
|
@@ -69,9 +72,10 @@ export declare const CreateTestCaseSchema: z.ZodObject<{
|
|
|
69
72
|
host: z.ZodString;
|
|
70
73
|
}, z.core.$strip>>;
|
|
71
74
|
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
72
|
-
custom_fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString
|
|
75
|
+
custom_fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>]>>>;
|
|
73
76
|
automation_status: z.ZodOptional<z.ZodString>;
|
|
74
77
|
priority: z.ZodOptional<z.ZodString>;
|
|
78
|
+
template: z.ZodOptional<z.ZodString>;
|
|
75
79
|
}, z.core.$strip>;
|
|
76
80
|
export declare function sanitizeArgs(args: any): any;
|
|
77
81
|
export declare function createTestCase(params: TestCaseCreateRequest, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
@@ -4,6 +4,13 @@ import { formatAxiosError } from "../../lib/error.js";
|
|
|
4
4
|
import { fetchFormFields, normalizeDefaultFieldValue, projectIdentifierToId, } from "./TCG-utils/api.js";
|
|
5
5
|
import { getTMBaseURL } from "../../lib/tm-base-url.js";
|
|
6
6
|
import logger from "../../logger.js";
|
|
7
|
+
// Scalar value, or an array of values for multi-select custom fields.
|
|
8
|
+
export const customFieldValueSchema = z.union([
|
|
9
|
+
z.string(),
|
|
10
|
+
z.number(),
|
|
11
|
+
z.boolean(),
|
|
12
|
+
z.array(z.union([z.string(), z.number()])),
|
|
13
|
+
]);
|
|
7
14
|
export const CreateTestCaseSchema = z.object({
|
|
8
15
|
project_identifier: z
|
|
9
16
|
.string()
|
|
@@ -48,9 +55,9 @@ export const CreateTestCaseSchema = z.object({
|
|
|
48
55
|
.optional()
|
|
49
56
|
.describe("Tags to attach to the test case. This should be strictly in array format not the string of json"),
|
|
50
57
|
custom_fields: z
|
|
51
|
-
.record(z.string(),
|
|
58
|
+
.record(z.string(), customFieldValueSchema)
|
|
52
59
|
.optional()
|
|
53
|
-
.describe("Map of custom field
|
|
60
|
+
.describe("Map of custom field NAME to value; use an array for multi-select fields."),
|
|
54
61
|
automation_status: z
|
|
55
62
|
.string()
|
|
56
63
|
.optional()
|
|
@@ -59,6 +66,10 @@ export const CreateTestCaseSchema = z.object({
|
|
|
59
66
|
.string()
|
|
60
67
|
.optional()
|
|
61
68
|
.describe("Priority of the test case. Accepts either display name (e.g. 'Critical', 'High', 'Medium', 'Low') or internal name (e.g. 'medium'). If omitted, the project default (usually 'Medium') is applied. Valid values are per-project and discoverable via the form-fields endpoint."),
|
|
69
|
+
template: z
|
|
70
|
+
.string()
|
|
71
|
+
.optional()
|
|
72
|
+
.describe("Template internal slug, e.g. 'test_case_steps' or 'test_case_bdd'. Use the slug, not the display name."),
|
|
62
73
|
});
|
|
63
74
|
export function sanitizeArgs(args) {
|
|
64
75
|
const cleaned = { ...args };
|
|
@@ -70,6 +81,8 @@ export function sanitizeArgs(args) {
|
|
|
70
81
|
delete cleaned.preconditions;
|
|
71
82
|
if (cleaned.automation_status === null)
|
|
72
83
|
delete cleaned.automation_status;
|
|
84
|
+
if (cleaned.template === null)
|
|
85
|
+
delete cleaned.template;
|
|
73
86
|
if (cleaned.issue_tracker) {
|
|
74
87
|
if (cleaned.issue_tracker.name === undefined ||
|
|
75
88
|
cleaned.issue_tracker.host === undefined) {
|
|
@@ -126,22 +139,28 @@ export async function createTestCase(params, config) {
|
|
|
126
139
|
}
|
|
127
140
|
const tc = data.test_case;
|
|
128
141
|
const projectId = await projectIdentifierToId(params.project_identifier, config);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
142
|
+
const content = [];
|
|
143
|
+
// The TM API silently ignores an unrecognized template slug and falls back
|
|
144
|
+
// to the default. Surface that instead of letting it pass as success.
|
|
145
|
+
if (params.template &&
|
|
146
|
+
tc.template &&
|
|
147
|
+
String(tc.template).toLowerCase() !==
|
|
148
|
+
String(params.template).toLowerCase()) {
|
|
149
|
+
content.push({
|
|
150
|
+
type: "text",
|
|
151
|
+
text: `Warning: requested template "${params.template}" was not applied — the test case was created with "${tc.template}". BrowserStack expects the template's internal slug (e.g. "test_case_steps", "test_case_bdd") and silently uses the default for unrecognized values.`,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
content.push({
|
|
155
|
+
type: "text",
|
|
156
|
+
text: `Test case successfully created:
|
|
134
157
|
- Identifier: ${tc.identifier}
|
|
135
158
|
- Title: ${tc.title}
|
|
136
159
|
|
|
137
160
|
You can view it here: ${tmBaseUrl}/projects/${projectId}/folder/search?q=${tc.identifier}`,
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
text: JSON.stringify(tc, null, 2),
|
|
142
|
-
},
|
|
143
|
-
],
|
|
144
|
-
};
|
|
161
|
+
});
|
|
162
|
+
content.push({ type: "text", text: JSON.stringify(tc, null, 2) });
|
|
163
|
+
return { content };
|
|
145
164
|
}
|
|
146
165
|
catch (err) {
|
|
147
166
|
// Delegate to our centralized Axios error formatter
|
|
@@ -18,7 +18,7 @@ export interface TestCaseUpdateRequest {
|
|
|
18
18
|
status?: string;
|
|
19
19
|
tags?: string[];
|
|
20
20
|
issues?: string[];
|
|
21
|
-
custom_fields?: Record<string, string | number | boolean
|
|
21
|
+
custom_fields?: Record<string, string | number | boolean | Array<string | number>>;
|
|
22
22
|
}
|
|
23
23
|
export declare const UpdateTestCaseSchema: z.ZodObject<{
|
|
24
24
|
project_identifier: z.ZodString;
|
|
@@ -37,7 +37,7 @@ export declare const UpdateTestCaseSchema: z.ZodObject<{
|
|
|
37
37
|
status: z.ZodOptional<z.ZodString>;
|
|
38
38
|
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
39
39
|
issues: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
40
|
-
custom_fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
40
|
+
custom_fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>]>>>;
|
|
41
41
|
}, z.core.$strip>;
|
|
42
42
|
/**
|
|
43
43
|
* Updates an existing test case in BrowserStack Test Management.
|
|
@@ -58,9 +58,14 @@ export const UpdateTestCaseSchema = z.object({
|
|
|
58
58
|
.optional()
|
|
59
59
|
.describe("Replacement list of linked Jira/Asana/Azure issue IDs for the test case."),
|
|
60
60
|
custom_fields: z
|
|
61
|
-
.record(z.string(), z.union([
|
|
61
|
+
.record(z.string(), z.union([
|
|
62
|
+
z.string(),
|
|
63
|
+
z.number(),
|
|
64
|
+
z.boolean(),
|
|
65
|
+
z.array(z.union([z.string(), z.number()])),
|
|
66
|
+
]))
|
|
62
67
|
.optional()
|
|
63
|
-
.describe("Map of custom field
|
|
68
|
+
.describe("Map of custom field NAME to value; use an array for multi-select fields."),
|
|
64
69
|
});
|
|
65
70
|
/**
|
|
66
71
|
* Normalise default-field inputs (priority/case_type/automation_status) to
|