Skip to content

Commit 92100d4

Browse files
committed
Feat: Integrate MCP Instrumentation tracking across various tools
1 parent fc13c91 commit 92100d4

File tree

10 files changed

+106
-1
lines changed

10 files changed

+106
-1
lines changed

src/index.ts

Lines changed: 16 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 { createCustomInitializeHandler } from "./lib/utils";
1516

1617
function registerTools(server: McpServer) {
1718
addSDKTools(server);
@@ -31,6 +32,19 @@ const server: McpServer = new McpServer({
3132

3233
registerTools(server);
3334

35+
let clientName: string | undefined;
36+
37+
function setClientName(name: string) {
38+
clientName = name;
39+
}
40+
41+
const origInitializeHandler =
42+
server.server["_requestHandlers"].get("initialize");
43+
server.server["_requestHandlers"].set(
44+
"initialize",
45+
createCustomInitializeHandler(origInitializeHandler, logger, setClientName),
46+
);
47+
3448
async function main() {
3549
logger.info(
3650
"Launching BrowserStack MCP server, version %s",
@@ -50,3 +64,5 @@ main().catch(console.error);
5064
process.on("exit", () => {
5165
logger.flush();
5266
});
67+
68+
export { clientName };

src/lib/instrumentation.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import logger from "../logger";
2+
import config from "../config";
3+
import packageJson from "../../package.json";
4+
import axios from "axios";
5+
import { clientName } from "../index";
6+
7+
interface MCPEventPayload {
8+
event_type: string;
9+
event_properties: {
10+
mcp_version: string;
11+
tool_name: string;
12+
mcp_client: string;
13+
};
14+
}
15+
16+
export function trackMCPEvent(toolName: string): void {
17+
const instrumentationEndpoint = "https://api.browserstack.com/sdk/v1/event";
18+
const mcpClient = clientName || "unknown";
19+
20+
const event: MCPEventPayload = {
21+
event_type: "MCPInstrumentation",
22+
event_properties: {
23+
mcp_version: packageJson.version,
24+
tool_name: toolName,
25+
mcp_client: mcpClient,
26+
},
27+
};
28+
29+
axios
30+
.post(instrumentationEndpoint, event, {
31+
headers: {
32+
"Content-Type": "application/json",
33+
Authorization: `Basic ${Buffer.from(
34+
`${config.browserstackUsername}:${config.browserstackAccessKey}`,
35+
).toString("base64")}`,
36+
},
37+
timeout: 2000,
38+
})
39+
.then((response) => {
40+
logger.info("MCP event tracked successfully", {
41+
toolName,
42+
response,
43+
});
44+
})
45+
.catch((error: unknown) => {
46+
logger.warn(
47+
`Failed to track MCP event: ${error instanceof Error ? error.message : String(error)}`,
48+
{
49+
toolName,
50+
},
51+
);
52+
});
53+
}

src/lib/utils.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,22 @@ export interface HarEntry {
2424
serverIPAddress?: string;
2525
time?: number;
2626
}
27+
28+
export function createCustomInitializeHandler(
29+
origHandler: (request: any, extra: any) => Promise<any>,
30+
logger: any,
31+
setClientName: (name: string) => void,
32+
) {
33+
return async function (this: any, request: any, extra: any) {
34+
const clientInfo = request.params.clientInfo;
35+
if (clientInfo && clientInfo.name) {
36+
setClientName(clientInfo.name);
37+
logger.info(
38+
`Client connected: ${clientInfo.name} (version: ${clientInfo.version})`,
39+
);
40+
} else {
41+
logger.info("Client connected: unknown client");
42+
}
43+
return origHandler.call(this, request, extra);
44+
};
45+
}

src/tools/accessibility.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
startAccessibilityScan,
66
AccessibilityScanResponse,
77
} from "./accessiblity-utils/accessibility";
8+
import { trackMCPEvent } from "../lib/instrumentation";
89

910
async function runAccessibilityScan(
1011
name: string,
@@ -55,6 +56,7 @@ export default function addAccessibilityTools(server: McpServer) {
5556
pageURL: z.string().describe("The URL to scan for accessibility issues"),
5657
},
5758
async (args) => {
59+
trackMCPEvent("startAccessibilityScan")
5860
return runAccessibilityScan(args.name, args.pageURL);
5961
},
6062
);

src/tools/applive.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
44
import fs from "fs";
55
import { startSession } from "./applive-utils/start-session";
66
import logger from "../logger";
7+
import { trackMCPEvent } from "../lib/instrumentation";
78

89
/**
910
* Launches an App Live Session on BrowserStack.
@@ -89,6 +90,7 @@ export default function addAppLiveTools(server: McpServer) {
8990
},
9091
async (args) => {
9192
try {
93+
trackMCPEvent("runAppLiveSession");
9294
return startAppLiveSession(args);
9395
} catch (error) {
9496
return {

src/tools/automate.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { z } from "zod";
33
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
44
import logger from "../logger";
55
import { retrieveNetworkFailures } from "../lib/api";
6+
import { trackMCPEvent } from "../lib/instrumentation";
67

78
/**
89
* Fetches failed network requests from a BrowserStack Automate session.
@@ -57,6 +58,9 @@ export default function addAutomateTools(server: McpServer) {
5758
{
5859
sessionId: z.string().describe("The Automate session ID."),
5960
},
60-
getNetworkFailures,
61+
async (args) => {
62+
trackMCPEvent("getNetworkFailures");
63+
return getNetworkFailures(args);
64+
},
6165
);
6266
}

src/tools/bstack-sdk.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
generateBrowserStackYMLInstructions,
1111
getInstructionsForProjectConfiguration,
1212
} from "./sdk-utils/instructions";
13+
import { trackMCPEvent } from "../lib/instrumentation";
1314

1415
/**
1516
* BrowserStack SDK hooks into your test framework to seamlessly run tests on BrowserStack.
@@ -80,6 +81,7 @@ export default function addSDKTools(server: McpServer) {
8081
const desiredPlatforms = args.desiredPlatforms;
8182

8283
try {
84+
trackMCPEvent("runTestsOnBrowserStack");
8385
return bootstrapProjectWithSDK({
8486
detectedBrowserAutomationFramework,
8587
detectedTestingFramework,

src/tools/live.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { z } from "zod";
44
import logger from "../logger";
55
import { startBrowserSession } from "./live-utils/start-session";
66
import { PlatformType } from "./live-utils/types";
7+
import { trackMCPEvent } from "../lib/instrumentation";
78

89
// Define the schema shape
910
const LiveArgsShape = {
@@ -118,6 +119,7 @@ export default function addBrowserLiveTools(server: McpServer) {
118119
LiveArgsShape,
119120
async (args) => {
120121
try {
122+
trackMCPEvent("runBrowserLiveSession");
121123
const result = await runBrowserSession(args);
122124
return result;
123125
} catch (error: any) {

src/tools/observability.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
22
import { z } from "zod";
33
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
44
import { getLatestO11YBuildInfo } from "../lib/api";
5+
import { trackMCPEvent } from "../lib/instrumentation";
56

67
export async function getFailuresInLastRun(
78
buildName: string,
@@ -57,6 +58,7 @@ export default function addObservabilityTools(server: McpServer) {
5758
},
5859
async (args) => {
5960
try {
61+
trackMCPEvent("getFailuresInLastRun");
6062
return getFailuresInLastRun(args.buildName, args.projectName);
6163
} catch (error) {
6264
return {

src/tools/testmanagement.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
22
import { z } from "zod";
33
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
4+
import { trackMCPEvent } from "../lib/instrumentation";
45
import {
56
createProjectOrFolder,
67
CreateProjFoldSchema,
@@ -19,6 +20,7 @@ export async function createProjectOrFolderTool(
1920
args: z.infer<typeof CreateProjFoldSchema>,
2021
): Promise<CallToolResult> {
2122
try {
23+
trackMCPEvent("createProjectOrFolder");
2224
return await createProjectOrFolder(args);
2325
} catch (err) {
2426
return {
@@ -45,6 +47,7 @@ export async function createTestCaseTool(
4547
// Sanitize input arguments
4648
const cleanedArgs = sanitizeArgs(args);
4749
try {
50+
trackMCPEvent("createTestCase");
4851
return await createTestCaseAPI(cleanedArgs);
4952
} catch (err) {
5053
return {

0 commit comments

Comments
 (0)