Skip to content

Commit 32eead6

Browse files
Merge pull request #139 from tech-sushant/tra-insights
feat: Add build insights tool to fetch and display build Insights with QG
2 parents 058ad08 + 4fe3a2a commit 32eead6

File tree

3 files changed

+124
-0
lines changed

3 files changed

+124
-0
lines changed

src/lib/utils.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import sharp from "sharp";
22
import type { ApiResponse } from "./apiClient.js";
3+
import { BrowserStackConfig } from "./types.js";
4+
import { getBrowserStackAuth } from "./get-auth.js";
35

46
export function sanitizeUrlParam(param: string): string {
57
// Remove any characters that could be used for command injection
@@ -38,3 +40,25 @@ export async function assertOkResponse(
3840
);
3941
}
4042
}
43+
44+
export async function fetchFromBrowserStackAPI(
45+
url: string,
46+
config: BrowserStackConfig,
47+
): Promise<any> {
48+
const authString = getBrowserStackAuth(config);
49+
const auth = Buffer.from(authString).toString("base64");
50+
51+
const res = await fetch(url, {
52+
headers: {
53+
Authorization: `Basic ${auth}`,
54+
},
55+
});
56+
57+
if (!res.ok) {
58+
throw new Error(
59+
`Failed to fetch from ${url}: ${res.status} ${res.statusText}`,
60+
);
61+
}
62+
63+
return res.json();
64+
}

src/server-factory.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import addFailureLogsTools from "./tools/get-failure-logs.js";
1616
import addAutomateTools from "./tools/automate.js";
1717
import addSelfHealTools from "./tools/selfheal.js";
1818
import addAppLiveTools from "./tools/applive.js";
19+
import addBuildInsightsTools from "./tools/build-insights.js";
1920
import { setupOnInitialized } from "./oninitialized.js";
2021
import { BrowserStackConfig } from "./lib/types.js";
2122
import addRCATools from "./tools/rca-agent.js";
@@ -58,6 +59,7 @@ export class BrowserStackMcpServer {
5859
addFailureLogsTools,
5960
addAutomateTools,
6061
addSelfHealTools,
62+
addBuildInsightsTools,
6163
addRCATools,
6264
];
6365

src/tools/build-insights.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2+
import { z } from "zod";
3+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
4+
import logger from "../logger.js";
5+
import { BrowserStackConfig } from "../lib/types.js";
6+
import { fetchFromBrowserStackAPI } from "../lib/utils.js";
7+
8+
// Tool function that fetches build insights from two APIs
9+
export async function fetchBuildInsightsTool(
10+
args: { buildId: string },
11+
config: BrowserStackConfig,
12+
): Promise<CallToolResult> {
13+
try {
14+
const buildUrl = `https://api-automation.browserstack.com/ext/v1/builds/${args.buildId}`;
15+
const qualityGateUrl = `https://api-automation.browserstack.com/ext/v1/quality-gates/${args.buildId}`;
16+
17+
const [buildData, qualityData] = await Promise.all([
18+
fetchFromBrowserStackAPI(buildUrl, config),
19+
fetchFromBrowserStackAPI(qualityGateUrl, config),
20+
]);
21+
22+
// Select useful fields for users
23+
const insights = {
24+
name: buildData.name,
25+
status: buildData.status,
26+
duration: buildData.duration,
27+
user: buildData.user,
28+
tags: buildData.tags,
29+
alerts: buildData.alerts,
30+
status_stats: buildData.status_stats,
31+
failure_categories: buildData.failure_categories,
32+
smart_tags: buildData.smart_tags,
33+
unique_errors: buildData.unique_errors?.overview,
34+
observability_url: buildData?.observability_url,
35+
ci_build_url: buildData.ci_info?.build_url,
36+
quality_gate_result: qualityData.quality_gate_result,
37+
};
38+
39+
const qualityProfiles = qualityData.quality_profiles?.map(
40+
(profile: any) => ({
41+
name: profile.name,
42+
result: profile.result,
43+
}),
44+
);
45+
46+
const qualityProfilesText =
47+
qualityProfiles && qualityProfiles.length > 0
48+
? `Quality Gate Profiles (respond only if explicitly requested): ${JSON.stringify(qualityProfiles, null, 2)}`
49+
: "No Quality Gate Profiles available.";
50+
51+
return {
52+
content: [
53+
{
54+
type: "text",
55+
text: "Build insights:\n" + JSON.stringify(insights, null, 2),
56+
},
57+
{ type: "text", text: qualityProfilesText },
58+
],
59+
};
60+
} catch (error) {
61+
logger.error("Error fetching build insights", error);
62+
throw error;
63+
}
64+
}
65+
66+
// Registers the fetchBuildInsights tool with the MCP server
67+
export default function addBuildInsightsTools(
68+
server: McpServer,
69+
config: BrowserStackConfig,
70+
) {
71+
const tools: Record<string, any> = {};
72+
73+
tools.fetchBuildInsights = server.tool(
74+
"fetchBuildInsights",
75+
"Fetches insights about a BrowserStack build by combining build details and quality gate results.",
76+
{
77+
buildId: z.string().describe("The build UUID of the BrowserStack build"),
78+
},
79+
async (args) => {
80+
try {
81+
return await fetchBuildInsightsTool(args, config);
82+
} 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+
};
93+
}
94+
},
95+
);
96+
97+
return tools;
98+
}

0 commit comments

Comments
 (0)