Skip to content

refactor: improve SDK setup instructions and bootstrap flow for clarity and consistency #80

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 58 additions & 13 deletions src/tools/bstack-sdk.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import { trackMCP } from "../lib/instrumentation.js";
import { getSDKPrefixCommand } from "./sdk-utils/commands.js";

import {
SDKSupportedBrowserAutomationFramework,
SDKSupportedLanguage,
Expand All @@ -9,16 +12,18 @@ import {
SDKSupportedBrowserAutomationFrameworkEnum,
SDKSupportedTestingFrameworkEnum,
} from "./sdk-utils/types.js";

import {
generateBrowserStackYMLInstructions,
getInstructionsForProjectConfiguration,
formatInstructionsWithNumbers,
} from "./sdk-utils/instructions.js";
import { trackMCP } from "../lib/instrumentation.js";

import {
formatPercyInstructions,
getPercyInstructions,
} from "./sdk-utils/percy/instructions.js";
import { getSDKPrefixCommand } from "./sdk-utils/commands.js";


/**
* BrowserStack SDK hooks into your test framework to seamlessly run tests on BrowserStack.
Expand All @@ -42,33 +47,35 @@ export async function bootstrapProjectWithSDK({
detectedBrowserAutomationFramework === "cypress" ||
detectedTestingFramework === "webdriverio"
) {
let instructions = getInstructionsForProjectConfiguration(
let combinedInstructions = getInstructionsForProjectConfiguration(
detectedBrowserAutomationFramework,
detectedTestingFramework,
detectedLanguage,
);

if (enablePercy) {
const percyInstructions = getPercyInstructions(
detectedLanguage,
detectedBrowserAutomationFramework,
detectedTestingFramework,
);

if (percyInstructions) {
instructions += formatPercyInstructions(percyInstructions);
combinedInstructions +=
"\n\n" + formatPercyInstructions(percyInstructions);
} else {
throw new Error(
`Percy is currently not supported through MCP for ${detectedLanguage} with ${detectedTestingFramework}. If you want to run the test cases without Percy, disable Percy and run it again.`,
);
}
}
return {
content: [{ type: "text", text: instructions, isError: false }],
};

// Apply consistent formatting for all configurations
return formatFinalInstructions(combinedInstructions);
}

let fullInstructions = "";
// Add language-dependent prefix command
fullInstructions += getSDKPrefixCommand(
// Handle default flow using browserstack.yml
const sdkSetupCommand = getSDKPrefixCommand(
detectedLanguage,
detectedTestingFramework,
);
Expand All @@ -77,7 +84,6 @@ export async function bootstrapProjectWithSDK({
desiredPlatforms,
enablePercy,
);
fullInstructions += `${ymlInstructions}`;

const instructionsForProjectConfiguration =
getInstructionsForProjectConfiguration(
Expand All @@ -86,22 +92,52 @@ export async function bootstrapProjectWithSDK({
detectedLanguage,
);

let combinedInstructions = "";

// Step 1: Add SDK setup command
if (sdkSetupCommand) {
combinedInstructions += sdkSetupCommand;
}

// Step 2: Add browserstack.yml setup
if (ymlInstructions) {
combinedInstructions += "\n\n---STEP---\n" + ymlInstructions;
}

// Step 3: Add language/framework-specific setup
if (instructionsForProjectConfiguration) {
combinedInstructions += "\n\n" + instructionsForProjectConfiguration;
}

// Step 4: Add Percy setup if applicable
if (enablePercy) {
const percyInstructions = getPercyInstructions(
detectedLanguage,
detectedBrowserAutomationFramework,
detectedTestingFramework,
);

if (percyInstructions) {
fullInstructions += formatPercyInstructions(percyInstructions);
combinedInstructions +=
"\n\n" + formatPercyInstructions(percyInstructions);
} else {
throw new Error(
`Percy is currently not supported through MCP for ${detectedLanguage} with ${detectedTestingFramework}. If you want to run the test cases without Percy, disable Percy and run it again.`,
);
}
}

fullInstructions += `\n\nAfter setting up the files above, follow these final steps:\n${instructionsForProjectConfiguration}`;
// Apply consistent formatting for all configurations
return formatFinalInstructions(combinedInstructions);
}

// Helper function to apply consistent formatting to all instruction types
function formatFinalInstructions(combinedInstructions: string): CallToolResult {
const fullInstructions = `⚠️ IMPORTANT: DO NOT SKIP ANY STEP
All the setup steps described in this file MUST be executed regardless of any existing configuration or setup.
This ensures proper BrowserStack SDK setup.

${formatInstructionsWithNumbers(combinedInstructions)}`;

return {
content: [
Expand All @@ -124,21 +160,25 @@ export default function addSDKTools(server: McpServer) {
.describe(
"The automation framework configured in the project. Example: 'playwright', 'selenium'",
),

detectedTestingFramework: z
.nativeEnum(SDKSupportedTestingFrameworkEnum)
.describe(
"The testing framework used in the project. Be precise with framework selection Example: 'webdriverio', 'jest', 'pytest', 'junit4', 'junit5', 'mocha'",
),

detectedLanguage: z
.nativeEnum(SDKSupportedLanguageEnum)
.describe(
"The programming language used in the project. Example: 'nodejs', 'python', 'java', 'csharp'",
),

desiredPlatforms: z
.array(z.enum(["windows", "macos", "android", "ios"]))
.describe(
"The platforms the user wants to test on. Always ask this to the user, do not try to infer this.",
),

enablePercy: z
.boolean()
.optional()
Expand All @@ -147,16 +187,20 @@ export default function addSDKTools(server: McpServer) {
"Set to true if the user wants to enable Percy for visual testing. Defaults to false.",
),
},

async (args) => {
try {
trackMCP("runTestsOnBrowserStack", server.server.getClientVersion()!);

return await bootstrapProjectWithSDK({
detectedBrowserAutomationFramework:
args.detectedBrowserAutomationFramework as SDKSupportedBrowserAutomationFramework,

detectedTestingFramework:
args.detectedTestingFramework as SDKSupportedTestingFramework,

detectedLanguage: args.detectedLanguage as SDKSupportedLanguage,

desiredPlatforms: args.desiredPlatforms,
enablePercy: args.enablePercy,
});
Expand All @@ -166,6 +210,7 @@ export default function addSDKTools(server: McpServer) {
server.server.getClientVersion()!,
error,
);

return {
content: [
{
Expand Down
18 changes: 16 additions & 2 deletions src/tools/sdk-utils/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,18 @@ export function getSDKPrefixCommand(
): string {
switch (language) {
case "nodejs":
return `Install BrowserStack Node SDK\nusing command | npm i -D browserstack-node-sdk@latest\n| and then run following command to setup browserstack sdk:\n npx setup --username ${process.env.BROWSERSTACK_USERNAME} --key ${process.env.BROWSERSTACK_ACCESS_KEY}\n\n. This will create browserstack.yml file in the project root. Edit the file to add your desired platforms and browsers. If the file is not created :\n`;
return `---STEP---
Install BrowserStack Node SDK using command:
\`\`\`bash
npm i -D browserstack-node-sdk@latest
\`\`\`
---STEP---
Run the following command to setup browserstack sdk:
\`\`\`bash
npx setup --username ${process.env.BROWSERSTACK_USERNAME} --key ${process.env.BROWSERSTACK_ACCESS_KEY}
\`\`\`
---STEP---
Edit the browserstack.yml file that was created in the project root to add your desired platforms and browsers.`;

case "java": {
const mavenFramework = getJavaFrameworkForMaven(framework);
Expand All @@ -45,11 +56,14 @@ export function getSDKPrefixCommand(

const platformLabel = isWindows ? "Windows" : "macOS/Linux";

return `Install BrowserStack Java SDK
return `---STEP---
Install BrowserStack Java SDK

**Maven command for ${framework} (${platformLabel}):**
Run the command, it is required to generate the browserstack-sdk-archetype-integrate project:
${mavenCommand}

Alternative setup for Gradle users:
${GRADLE_SETUP_INSTRUCTIONS}`;
}

Expand Down
Loading