Skip to content

Commit 3e45dbc

Browse files
Merge pull request #140 from tech-sushant/refactoring-bump
fix : Instrumentation, refactoring and bump version
2 parents ff65385 + 6532db2 commit 3e45dbc

File tree

18 files changed

+197
-201
lines changed

18 files changed

+197
-201
lines changed

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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"name": "@browserstack/mcp-server",
3-
"version": "1.2.3",
3+
"version": "1.2.4",
44
"description": "BrowserStack's Official MCP Server",
5+
"mcpName": "io.github.browserstack/mcp-server",
56
"main": "dist/index.js",
67
"repository": {
78
"type": "git",

src/lib/utils.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import sharp from "sharp";
22
import type { ApiResponse } from "./apiClient.js";
33
import { BrowserStackConfig } from "./types.js";
44
import { getBrowserStackAuth } from "./get-auth.js";
5+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
7+
import { trackMCP } from "../index.js";
58

69
export function sanitizeUrlParam(param: string): string {
710
// Remove any characters that could be used for command injection
@@ -62,3 +65,27 @@ export async function fetchFromBrowserStackAPI(
6265

6366
return res.json();
6467
}
68+
69+
function errorContent(message: string): CallToolResult {
70+
return {
71+
content: [{ type: "text", text: message }],
72+
isError: true,
73+
};
74+
}
75+
76+
export function handleMCPError(
77+
toolName: string,
78+
server: McpServer,
79+
config: BrowserStackConfig,
80+
error: unknown,
81+
) {
82+
trackMCP(toolName, server.server.getClientVersion()!, error, config);
83+
84+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
85+
86+
const readableToolName = toolName.replace(/([A-Z])/g, " $1").toLowerCase();
87+
88+
return errorContent(
89+
`Failed to ${readableToolName}: ${errorMessage}. Please open an issue on GitHub if the problem persists`,
90+
);
91+
}

src/tools/appautomate-utils/appium-sdk/types.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,8 @@ export interface AppSDKInstruction {
6060
export const SUPPORTED_CONFIGURATIONS = {
6161
appium: {
6262
ruby: ["cucumberRuby"],
63-
java: [
64-
"junit5",
65-
"junit4",
66-
"testng",
67-
"cucumberTestng",
68-
"selenide",
69-
"jbehave",
70-
],
71-
csharp: ["nunit", "xunit", "mstest", "specflow", "reqnroll"],
63+
java: [],
64+
csharp: [],
7265
python: ["pytest", "robot", "behave", "lettuce"],
7366
nodejs: ["jest", "mocha", "cucumberJs", "webdriverio", "nightwatch"],
7467
},

src/tools/appautomate-utils/appium-sdk/utils.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,15 @@ export function validateSupportforAppAutomate(
9696
);
9797
}
9898

99-
const testingFrameworks = SUPPORTED_CONFIGURATIONS[framework][language];
99+
const testingFrameworks = SUPPORTED_CONFIGURATIONS[framework][
100+
language
101+
] as string[];
102+
103+
if (testingFrameworks.length === 0) {
104+
throw new Error(
105+
`No testing frameworks are supported for language '${language}' and framework '${framework}'.`,
106+
);
107+
}
100108
if (!testingFrameworks.includes(testingFramework)) {
101109
throw new Error(
102110
`Unsupported testing framework '${testingFramework}' for language '${language}' and framework '${framework}'. Supported testing frameworks: ${testingFrameworks.join(", ")}`,

src/tools/bstack-sdk.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { BrowserStackConfig } from "../lib/types.js";
33
import { RunTestsOnBrowserStackParamsShape } from "./sdk-utils/common/schema.js";
44
import { runTestsOnBrowserStackHandler } from "./sdk-utils/handler.js";
55
import { RUN_ON_BROWSERSTACK_DESCRIPTION } from "./sdk-utils/common/constants.js";
6+
import { handleMCPError } from "../lib/utils.js";
7+
import { trackMCP } from "../lib/instrumentation.js";
68

79
export function registerRunBrowserStackTestsTool(
810
server: McpServer,
@@ -15,7 +17,16 @@ export function registerRunBrowserStackTestsTool(
1517
RUN_ON_BROWSERSTACK_DESCRIPTION,
1618
RunTestsOnBrowserStackParamsShape,
1719
async (args) => {
18-
return runTestsOnBrowserStackHandler(args, config);
20+
try {
21+
trackMCP(
22+
"runTestsOnBrowserStack",
23+
server.server.getClientVersion()!,
24+
config,
25+
);
26+
return await runTestsOnBrowserStackHandler(args, config);
27+
} catch (error) {
28+
return handleMCPError("runTestsOnBrowserStack", server, config, error);
29+
}
1930
},
2031
);
2132

src/tools/build-insights.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { z } from "zod";
33
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
44
import logger from "../logger.js";
55
import { BrowserStackConfig } from "../lib/types.js";
6-
import { fetchFromBrowserStackAPI } from "../lib/utils.js";
6+
import { fetchFromBrowserStackAPI, handleMCPError } from "../lib/utils.js";
7+
import { trackMCP } from "../lib/instrumentation.js";
78

89
// Tool function that fetches build insights from two APIs
910
export async function fetchBuildInsightsTool(
@@ -78,18 +79,14 @@ export default function addBuildInsightsTools(
7879
},
7980
async (args) => {
8081
try {
82+
trackMCP(
83+
"fetchBuildInsights",
84+
server.server.getClientVersion()!,
85+
config,
86+
);
8187
return await fetchBuildInsightsTool(args, config);
8288
} catch (error) {
83-
const errorMessage =
84-
error instanceof Error ? error.message : "Unknown error";
85-
return {
86-
content: [
87-
{
88-
type: "text",
89-
text: `Error during fetching build insights: ${errorMessage}`,
90-
},
91-
],
92-
};
89+
return handleMCPError("fetchBuildInsights", server, config, error);
9390
}
9491
},
9592
);

src/tools/list-test-files.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ export async function addListTestFiles(args: any): Promise<CallToolResult> {
77
const { dirs, language, framework } = args;
88
let testFiles: string[] = [];
99

10+
if (!dirs || dirs.length === 0) {
11+
throw new Error(
12+
"No directories provided to add the test files. Please provide test directories to add percy snapshot commands.",
13+
);
14+
}
15+
1016
for (const dir of dirs) {
1117
const files = await listTestFiles({
1218
language,

src/tools/percy-sdk.ts

Lines changed: 39 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { trackMCP } from "../index.js";
22
import { BrowserStackConfig } from "../lib/types.js";
3-
import { fetchPercyChanges } from "./percy-change.js";
3+
import { fetchPercyChanges } from "./review-agent.js";
44
import { addListTestFiles } from "./list-test-files.js";
55
import { runPercyScan } from "./run-percy-scan.js";
66
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -25,14 +25,14 @@ import {
2525
FetchPercyChangesParamsShape,
2626
ManagePercyBuildApprovalParamsShape,
2727
} from "./sdk-utils/common/schema.js";
28+
import { handleMCPError } from "../lib/utils.js";
2829

2930
export function registerPercyTools(
3031
server: McpServer,
3132
config: BrowserStackConfig,
3233
) {
3334
const tools: Record<string, any> = {};
3435

35-
// Register setupPercyVisualTesting
3636
tools.setupPercyVisualTesting = server.tool(
3737
"setupPercyVisualTesting",
3838
SETUP_PERCY_DESCRIPTION,
@@ -46,26 +46,11 @@ export function registerPercyTools(
4646
);
4747
return setUpPercyHandler(args, config);
4848
} catch (error) {
49-
trackMCP(
50-
"setupPercyVisualTesting",
51-
server.server.getClientVersion()!,
52-
error,
53-
config,
54-
);
55-
return {
56-
content: [
57-
{
58-
type: "text",
59-
text: error instanceof Error ? error.message : String(error),
60-
},
61-
],
62-
isError: true,
63-
};
49+
return handleMCPError("setupPercyVisualTesting", server, config, error);
6450
}
6551
},
6652
);
6753

68-
// Register addPercySnapshotCommands
6954
tools.addPercySnapshotCommands = server.tool(
7055
"addPercySnapshotCommands",
7156
PERCY_SNAPSHOT_COMMANDS_DESCRIPTION,
@@ -79,26 +64,16 @@ export function registerPercyTools(
7964
);
8065
return await updateTestsWithPercyCommands(args);
8166
} catch (error) {
82-
trackMCP(
67+
return handleMCPError(
8368
"addPercySnapshotCommands",
84-
server.server.getClientVersion()!,
85-
error,
69+
server,
8670
config,
71+
error,
8772
);
88-
return {
89-
content: [
90-
{
91-
type: "text",
92-
text: error instanceof Error ? error.message : String(error),
93-
},
94-
],
95-
isError: true,
96-
};
9773
}
9874
},
9975
);
10076

101-
// Register listTestFiles
10277
tools.listTestFiles = server.tool(
10378
"listTestFiles",
10479
LIST_TEST_FILES_DESCRIPTION,
@@ -108,21 +83,7 @@ export function registerPercyTools(
10883
trackMCP("listTestFiles", server.server.getClientVersion()!, config);
10984
return addListTestFiles(args);
11085
} catch (error) {
111-
trackMCP(
112-
"listTestFiles",
113-
server.server.getClientVersion()!,
114-
error,
115-
config,
116-
);
117-
return {
118-
content: [
119-
{
120-
type: "text",
121-
text: error instanceof Error ? error.message : String(error),
122-
},
123-
],
124-
isError: true,
125-
};
86+
return handleMCPError("listTestFiles", server, config, error);
12687
}
12788
},
12889
);
@@ -132,16 +93,30 @@ export function registerPercyTools(
13293
"Run a Percy visual test scan. Example prompts : Run this Percy build/scan. Never run percy scan/build without this tool",
13394
RunPercyScanParamsShape,
13495
async (args) => {
135-
return runPercyScan(args, config);
96+
try {
97+
trackMCP("runPercyScan", server.server.getClientVersion()!, config);
98+
return runPercyScan(args, config);
99+
} catch (error) {
100+
return handleMCPError("runPercyScan", server, config, error);
101+
}
136102
},
137103
);
138104

139105
tools.fetchPercyChanges = server.tool(
140106
"fetchPercyChanges",
141-
"Retrieves and summarizes all visual changes detected by Percy between the latest and previous builds, helping quickly review what has changed in your project.",
107+
"Retrieves and summarizes all visual changes detected by Percy AI between the latest and previous builds, helping quickly review what has changed in your project.",
142108
FetchPercyChangesParamsShape,
143109
async (args) => {
144-
return await fetchPercyChanges(args, config);
110+
try {
111+
trackMCP(
112+
"fetchPercyChanges",
113+
server.server.getClientVersion()!,
114+
config,
115+
);
116+
return await fetchPercyChanges(args, config);
117+
} catch (error) {
118+
return handleMCPError("fetchPercyChanges", server, config, error);
119+
}
145120
},
146121
);
147122

@@ -150,7 +125,21 @@ export function registerPercyTools(
150125
"Approve or reject a Percy build",
151126
ManagePercyBuildApprovalParamsShape,
152127
async (args) => {
153-
return await approveOrDeclinePercyBuild(args, config);
128+
try {
129+
trackMCP(
130+
"managePercyBuildApproval",
131+
server.server.getClientVersion()!,
132+
config,
133+
);
134+
return await approveOrDeclinePercyBuild(args, config);
135+
} catch (error) {
136+
return handleMCPError(
137+
"managePercyBuildApproval",
138+
server,
139+
config,
140+
error,
141+
);
142+
}
154143
},
155144
);
156145

0 commit comments

Comments
 (0)