Skip to content

Commit 6901b3c

Browse files
Merge branch 'browserstack:main' into main
2 parents 9883262 + 8935cee commit 6901b3c

File tree

9 files changed

+240
-38
lines changed

9 files changed

+240
-38
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,28 @@ Use the following prompts to run/debug/fix your **automated tests** on BrowserSt
8484
- Run tests written in Jest, Playwright, Selenium, and more on BrowserStack's [Test Platform](https://www.browserstack.com/test-platform)
8585
- **Accessibility Testing**: Ensure WCAG and ADA compliance with our [Accessibility Testing](https://www.browserstack.com/accessibility-testing) tool
8686

87+
88+
### 📋 Test Management
89+
90+
Use the following prompts to utilise capabilities of BrowserStack's [Test Management](https://www.browserstack.com/test-management) with MCP server.
91+
92+
```bash
93+
# Create project & folder structure
94+
"create new Test management project named My Demo Project with two sub folders - Login & Checkout"
95+
96+
# Add test cases
97+
"add invalid login test case in Test Management project named My Demo Project"
98+
99+
# List added test cases
100+
"list high priority Login test cases from Test Management project - My Demo Project"
101+
102+
# Create test run
103+
"create a test run for Login tests from Test Management project - My Demo Project"
104+
105+
# Update test results
106+
"update test results as passed for Login tests test run from My Demo Project"
107+
```
108+
87109
## 🛠️ Installation
88110

89111
1. **Create a BrowserStack Account**

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@browserstack/mcp-server",
3-
"version": "1.0.12",
3+
"version": "1.0.13",
44
"description": "BrowserStack's Official MCP Server",
55
"main": "dist/index.js",
66
"repository": {

src/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export class Config {
1818
const config = new Config(
1919
process.env.BROWSERSTACK_USERNAME!,
2020
process.env.BROWSERSTACK_ACCESS_KEY!,
21-
process.env.DEV_MODE === "true"
21+
process.env.DEV_MODE === "true",
2222
);
2323

2424
export default config;

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import addBrowserLiveTools from "./tools/live";
1212
import addAccessibilityTools from "./tools/accessibility";
1313
import addAutomateTools from "./tools/automate";
1414
import addTestManagementTools from "./tools/testmanagement";
15+
import { trackMCP } from "./lib/instrumentation";
1516

1617
function registerTools(server: McpServer) {
1718
addSDKTools(server);
@@ -42,6 +43,7 @@ async function main() {
4243
await server.connect(transport);
4344

4445
logger.info("MCP server started successfully");
46+
trackMCP("started", server.server.getClientVersion()!);
4547
}
4648

4749
main().catch(console.error);

src/lib/instrumentation.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ export function trackMCP(
2020
clientInfo: { name?: string; version?: string },
2121
error?: unknown,
2222
): void {
23-
2423
if (config.DEV_MODE) {
2524
logger.info("Tracking MCP is disabled in dev mode");
2625
return;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import axios from "axios";
2+
import config from "../../config";
3+
import { z } from "zod";
4+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
5+
import { formatAxiosError } from "../../lib/error";
6+
7+
/**
8+
* Schema for adding a test result to a test run.
9+
*/
10+
export const AddTestResultSchema = z.object({
11+
project_identifier: z
12+
.string()
13+
.describe("Identifier of the project (e.g., PR-12345)"),
14+
test_run_id: z.string().describe("Identifier of the test run (e.g., TR-678)"),
15+
test_result: z.object({
16+
status: z
17+
.string()
18+
.describe("Status of the test result, e.g., 'passed', 'failed'."),
19+
description: z
20+
.string()
21+
.optional()
22+
.describe("Optional description of the test result."),
23+
}),
24+
test_case_id: z
25+
.string()
26+
.describe("Identifier of the test case, e.g., 'TC-13'."),
27+
});
28+
29+
export type AddTestResultArgs = z.infer<typeof AddTestResultSchema>;
30+
31+
/**
32+
* Adds a test result to a specific test run via BrowserStack Test Management API.
33+
*/
34+
export async function addTestResult(
35+
rawArgs: AddTestResultArgs,
36+
): Promise<CallToolResult> {
37+
try {
38+
const args = AddTestResultSchema.parse(rawArgs);
39+
const url = `https://test-management.browserstack.com/api/v2/projects/${encodeURIComponent(
40+
args.project_identifier,
41+
)}/test-runs/${encodeURIComponent(args.test_run_id)}/results`;
42+
43+
const body = {
44+
test_result: args.test_result,
45+
test_case_id: args.test_case_id,
46+
} as any;
47+
48+
const response = await axios.post(url, body, {
49+
auth: {
50+
username: config.browserstackUsername,
51+
password: config.browserstackAccessKey,
52+
},
53+
headers: { "Content-Type": "application/json" },
54+
});
55+
56+
const data = response.data;
57+
if (!data.success) {
58+
throw new Error(
59+
`API returned unsuccessful response: ${JSON.stringify(data)}`,
60+
);
61+
}
62+
63+
return {
64+
content: [
65+
{
66+
type: "text",
67+
text: `Successfully added test result with ID=${data["test-result"].id}`,
68+
},
69+
{ type: "text", text: JSON.stringify(data["test-result"], null, 2) },
70+
],
71+
};
72+
} catch (err: any) {
73+
return formatAxiosError(err, "Failed to add test result to test run");
74+
}
75+
}

src/tools/testmanagement.ts

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
CreateTestCaseSchema,
1515
} from "./testmanagement-utils/create-testcase";
1616

17-
1817
let serverInstance: McpServer;
1918

2019
import {
@@ -37,8 +36,10 @@ import {
3736
updateTestRun,
3837
} from "./testmanagement-utils/update-testrun";
3938

40-
41-
//TODO: Moving the traceMCP and catch block to the parent(server) function
39+
import {
40+
addTestResult,
41+
AddTestResultSchema,
42+
} from "./testmanagement-utils/add-test-result";
4243

4344
/**
4445
* Wrapper to call createProjectOrFolder util.
@@ -111,8 +112,10 @@ export async function listTestCasesTool(
111112
args: z.infer<typeof ListTestCasesSchema>,
112113
): Promise<CallToolResult> {
113114
try {
115+
trackMCP("listTestCases", serverInstance.server.getClientVersion()!);
114116
return await listTestCases(args);
115117
} catch (err) {
118+
trackMCP("listTestCases", serverInstance.server.getClientVersion()!, err);
116119
return {
117120
content: [
118121
{
@@ -135,8 +138,10 @@ export async function createTestRunTool(
135138
args: z.infer<typeof CreateTestRunSchema>,
136139
): Promise<CallToolResult> {
137140
try {
141+
trackMCP("createTestRun", serverInstance.server.getClientVersion()!);
138142
return await createTestRun(args);
139143
} catch (err) {
144+
trackMCP("createTestRun", serverInstance.server.getClientVersion()!, err);
140145
return {
141146
content: [
142147
{
@@ -159,8 +164,10 @@ export async function listTestRunsTool(
159164
args: z.infer<typeof ListTestRunsSchema>,
160165
): Promise<CallToolResult> {
161166
try {
167+
trackMCP("listTestRuns", serverInstance.server.getClientVersion()!);
162168
return await listTestRuns(args);
163169
} catch (err) {
170+
trackMCP("listTestRuns", serverInstance.server.getClientVersion()!, err);
164171
return {
165172
content: [
166173
{
@@ -185,8 +192,10 @@ export async function updateTestRunTool(
185192
args: z.infer<typeof UpdateTestRunSchema>,
186193
): Promise<CallToolResult> {
187194
try {
195+
trackMCP("updateTestRun", serverInstance.server.getClientVersion()!);
188196
return await updateTestRun(args);
189197
} catch (err) {
198+
trackMCP("updateTestRun", serverInstance.server.getClientVersion()!, err);
190199
return {
191200
content: [
192201
{
@@ -202,6 +211,32 @@ export async function updateTestRunTool(
202211
}
203212
}
204213

214+
/**
215+
* Adds a test result to a specific test run via BrowserStack Test Management API.
216+
*/
217+
export async function addTestResultTool(
218+
args: z.infer<typeof AddTestResultSchema>,
219+
): Promise<CallToolResult> {
220+
try {
221+
trackMCP("addTestResult", serverInstance.server.getClientVersion()!);
222+
return await addTestResult(args);
223+
} catch (err) {
224+
trackMCP("addTestResult", serverInstance.server.getClientVersion()!, err);
225+
return {
226+
content: [
227+
{
228+
type: "text",
229+
text: `Failed to add test result: ${
230+
err instanceof Error ? err.message : "Unknown error"
231+
}. Please open an issue on GitHub if the problem persists`,
232+
isError: true,
233+
},
234+
],
235+
isError: true,
236+
};
237+
}
238+
}
239+
205240
/**
206241
* Registers both project/folder and test-case tools.
207242
*/
@@ -247,4 +282,10 @@ export default function addTestManagementTools(server: McpServer) {
247282
UpdateTestRunSchema.shape,
248283
updateTestRunTool,
249284
);
285+
server.tool(
286+
"addTestResult",
287+
"Add a test result to a specific test run via BrowserStack Test Management API.",
288+
AddTestResultSchema.shape,
289+
addTestResultTool,
290+
);
250291
}

0 commit comments

Comments
 (0)