From 8cabf4a325a229481021c4c03712acba185d448a Mon Sep 17 00:00:00 2001
From: tech-sushant <sushant.s@browserstack.com>
Date: Wed, 4 Jun 2025 16:57:35 +0530
Subject: [PATCH 01/64] feat: add support for running AppAutomate tests on
 BrowserStack

---
 src/tools/appautomate-utils/appautomate.ts | 88 +++++++++++++++++--
 src/tools/appautomate-utils/types.ts       |  5 ++
 src/tools/appautomate.ts                   | 98 +++++++++++++++++++++-
 3 files changed, 181 insertions(+), 10 deletions(-)
 create mode 100644 src/tools/appautomate-utils/types.ts

diff --git a/src/tools/appautomate-utils/appautomate.ts b/src/tools/appautomate-utils/appautomate.ts
index a52e622..596dfcc 100644
--- a/src/tools/appautomate-utils/appautomate.ts
+++ b/src/tools/appautomate-utils/appautomate.ts
@@ -4,6 +4,11 @@ import config from "../../config.js";
 import FormData from "form-data";
 import { customFuzzySearch } from "../../lib/fuzzy.js";
 
+const auth = {
+  username: config.browserstackUsername,
+  password: config.browserstackAccessKey,
+};
+
 interface Device {
   device: string;
   display_name: string;
@@ -138,13 +143,8 @@ export async function uploadApp(appPath: string): Promise<string> {
     "https://api-cloud.browserstack.com/app-automate/upload",
     formData,
     {
-      headers: {
-        ...formData.getHeaders(),
-      },
-      auth: {
-        username: config.browserstackUsername,
-        password: config.browserstackAccessKey,
-      },
+      headers: formData.getHeaders(),
+      auth,
     },
   );
 
@@ -154,3 +154,77 @@ export async function uploadApp(appPath: string): Promise<string> {
     throw new Error(`Failed to upload app: ${response.data}`);
   }
 }
+
+// Helper to upload a file to a given BrowserStack endpoint and return a specific property from the response.
+async function uploadFileToBrowserStack(
+  filePath: string,
+  endpoint: string,
+  responseKey: string,
+): Promise<string> {
+  if (!fs.existsSync(filePath)) {
+    throw new Error(`File not found at path: ${filePath}`);
+  }
+
+  const formData = new FormData();
+  formData.append("file", fs.createReadStream(filePath));
+
+  const response = await axios.post(endpoint, formData, {
+    headers: formData.getHeaders(),
+    auth,
+  });
+
+  if (response.data[responseKey]) {
+    return response.data[responseKey];
+  }
+
+  throw new Error(`Failed to upload file: ${JSON.stringify(response.data)}`);
+}
+
+//Uploads an Android app (.apk or .aab) to BrowserStack Espresso endpoint and returns the app_url
+export async function uploadEspressoApp(appPath: string): Promise<string> {
+  return uploadFileToBrowserStack(
+    appPath,
+    "https://api-cloud.browserstack.com/app-automate/espresso/v2/app",
+    "app_url",
+  );
+}
+
+//Uploads an Espresso test suite (.apk) to BrowserStack and returns the test_suite_url
+export async function uploadEspressoTestSuite(
+  testSuitePath: string,
+): Promise<string> {
+  return uploadFileToBrowserStack(
+    testSuitePath,
+    "https://api-cloud.browserstack.com/app-automate/espresso/v2/test-suite",
+    "test_suite_url",
+  );
+}
+
+// Triggers an Espresso test run on BrowserStack and returns the build_id
+export async function triggerEspressoBuild(
+  app_url: string,
+  test_suite_url: string,
+  devices: string[],
+  project: string,
+): Promise<string> {
+  const response = await axios.post(
+    "https://api-cloud.browserstack.com/app-automate/espresso/v2/build",
+    {
+      app: app_url,
+      testSuite: test_suite_url,
+      devices,
+      project,
+    },
+    {
+      auth,
+    },
+  );
+
+  if (response.data.build_id) {
+    return response.data.build_id;
+  }
+
+  throw new Error(
+    `Failed to trigger Espresso build: ${JSON.stringify(response.data)}`,
+  );
+}
diff --git a/src/tools/appautomate-utils/types.ts b/src/tools/appautomate-utils/types.ts
new file mode 100644
index 0000000..16af726
--- /dev/null
+++ b/src/tools/appautomate-utils/types.ts
@@ -0,0 +1,5 @@
+export enum AppTestPlatform {
+  ESPRESSO = "espresso",
+  APPIUM = "appium",
+  XCUITEST = "xcuitest",
+}
diff --git a/src/tools/appautomate.ts b/src/tools/appautomate.ts
index 786b9db..06aff29 100644
--- a/src/tools/appautomate.ts
+++ b/src/tools/appautomate.ts
@@ -6,6 +6,7 @@ import config from "../config.js";
 import { trackMCP } from "../lib/instrumentation.js";
 import { maybeCompressBase64 } from "../lib/utils.js";
 import { remote } from "webdriverio";
+import { AppTestPlatform } from "./appautomate-utils/types.js";
 
 import {
   getDevicesAndBrowsers,
@@ -18,6 +19,9 @@ import {
   resolveVersion,
   validateArgs,
   uploadApp,
+  uploadEspressoApp,
+  uploadEspressoTestSuite,
+  triggerEspressoBuild,
 } from "./appautomate-utils/appautomate.js";
 
 // Types
@@ -136,9 +140,51 @@ async function takeAppScreenshot(args: {
   }
 }
 
-/**
- * Registers the `takeAppScreenshot` tool with the MCP server.
- */
+//Runs AppAutomate tests on BrowserStack by uploading app and test suite, then triggering a test run.
+async function runAppTestsOnBrowserStack(args: {
+  appPath: string;
+  testSuitePath: string;
+  devices: string[];
+  project: string;
+  detectedAutomationFramework: string;
+}): Promise<CallToolResult> {
+
+  switch (args.detectedAutomationFramework) {
+    case AppTestPlatform.ESPRESSO: {
+      try {
+        const app_url = await uploadEspressoApp(args.appPath);
+        const test_suite_url = await uploadEspressoTestSuite(
+          args.testSuitePath,
+        );
+        const build_id = await triggerEspressoBuild(
+          app_url,
+          test_suite_url,
+          args.devices,
+          args.project,
+        );
+
+        return {
+          content: [
+            {
+              type: "text",
+              text: `✅ Test run started successfully!\n\n🔧 Build ID: ${build_id}\n🔗 View your build: https://app-automate.browserstack.com/builds/${build_id}`,
+            },
+          ],
+        };
+      } catch (err) {
+        logger.error("Error running App Automate test", err);
+        throw err;
+      }
+    }
+
+    default:
+      throw new Error(
+        `Unsupported automation framework: ${args.detectedAutomationFramework}`,
+      );
+  }
+}
+
+// Registers automation tools with the MCP server.
 export default function addAppAutomationTools(server: McpServer) {
   server.tool(
     "takeAppScreenshot",
@@ -182,4 +228,50 @@ export default function addAppAutomationTools(server: McpServer) {
       }
     },
   );
+
+  server.tool(
+    "runAppTestsOnBrowserStack",
+    "Run AppAutomate tests on BrowserStack by uploading app and test suite, then triggering a test run.",
+    {
+      appPath: z
+        .string()
+        .describe("Path to the .apk or .aab file for your app."),
+      testSuitePath: z
+        .string()
+        .describe("Path to the Espresso test suite .apk file."),
+      devices: z
+        .array(z.string())
+        .describe(
+          "List of devices to run the test on, e.g., ['Samsung Galaxy S20-10.0', 'Google Pixel 3-9.0'].",
+        ),
+      project: z
+        .string()
+        .optional()
+        .default("Espresso Test")
+        .describe("Project name for organizing test runs on BrowserStack."),
+      detectedAutomationFramework: z
+        .string()
+        .describe(
+          "The automation framework used in the project, such as 'espresso' or 'appium'.",
+        ),
+    },
+    async (args) => {
+      try {
+        return await runAppTestsOnBrowserStack(args);
+      } catch (error) {
+        const errorMessage =
+          error instanceof Error ? error.message : "Unknown error";
+        return {
+          content: [
+            {
+              type: "text",
+              text: `Error running App Automate test: ${errorMessage}`,
+              isError: true,
+            },
+          ],
+          isError: true,
+        };
+      }
+    },
+  );
 }

From f26a676a2406f1973e332ee2c3aa7d0dda80f90c Mon Sep 17 00:00:00 2001
From: tech-sushant <sushant.s@browserstack.com>
Date: Wed, 4 Jun 2025 17:05:22 +0530
Subject: [PATCH 02/64] fix: add tracking for runAppTestsOnBrowserStack
 function execution and errors

---
 src/tools/appautomate.ts | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/tools/appautomate.ts b/src/tools/appautomate.ts
index 06aff29..5fa650e 100644
--- a/src/tools/appautomate.ts
+++ b/src/tools/appautomate.ts
@@ -148,7 +148,6 @@ async function runAppTestsOnBrowserStack(args: {
   project: string;
   detectedAutomationFramework: string;
 }): Promise<CallToolResult> {
-
   switch (args.detectedAutomationFramework) {
     case AppTestPlatform.ESPRESSO: {
       try {
@@ -257,8 +256,17 @@ export default function addAppAutomationTools(server: McpServer) {
     },
     async (args) => {
       try {
+        trackMCP(
+          "runAppTestsOnBrowserStack",
+          server.server.getClientVersion()!,
+        );
         return await runAppTestsOnBrowserStack(args);
       } catch (error) {
+        trackMCP(
+          "runAppTestsOnBrowserStack",
+          server.server.getClientVersion()!,
+          error,
+        );
         const errorMessage =
           error instanceof Error ? error.message : "Unknown error";
         return {

From 3ca644809a37a7c42ac9859c2fb2bb0163c6f2fa Mon Sep 17 00:00:00 2001
From: tech-sushant <sushant.s@browserstack.com>
Date: Wed, 4 Jun 2025 17:08:38 +0530
Subject: [PATCH 03/64] fix: remove redundant isError property

---
 src/tools/appautomate.ts | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/tools/appautomate.ts b/src/tools/appautomate.ts
index 5fa650e..757b1f4 100644
--- a/src/tools/appautomate.ts
+++ b/src/tools/appautomate.ts
@@ -273,8 +273,7 @@ export default function addAppAutomationTools(server: McpServer) {
           content: [
             {
               type: "text",
-              text: `Error running App Automate test: ${errorMessage}`,
-              isError: true,
+              text: `Error running App Automate test: ${errorMessage}`
             },
           ],
           isError: true,

From 57026ac6d66714897383f3af52b06247270fa3a8 Mon Sep 17 00:00:00 2001
From: tech-sushant <sushant.s@browserstack.com>
Date: Fri, 27 Jun 2025 01:41:09 +0530
Subject: [PATCH 04/64] add support for XCUITest on BrowserStack

---
 src/tools/appautomate-utils/appautomate.ts | 49 ++++++++++++++++++++++
 src/tools/appautomate.ts                   | 47 +++++++++++++++++----
 2 files changed, 87 insertions(+), 9 deletions(-)

diff --git a/src/tools/appautomate-utils/appautomate.ts b/src/tools/appautomate-utils/appautomate.ts
index 596dfcc..0083893 100644
--- a/src/tools/appautomate-utils/appautomate.ts
+++ b/src/tools/appautomate-utils/appautomate.ts
@@ -200,6 +200,26 @@ export async function uploadEspressoTestSuite(
   );
 }
 
+//Uploads an iOS app (.ipa) to BrowserStack XCUITest endpoint and returns the app_url
+export async function uploadXcuiApp(appPath: string): Promise<string> {
+  return uploadFileToBrowserStack(
+    appPath,
+    "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/app",
+    "app_url",
+  );
+}
+
+//Uploads an XCUITest test suite (.zip) to BrowserStack and returns the test_suite_url
+export async function uploadXcuiTestSuite(
+  testSuitePath: string,
+): Promise<string> {
+  return uploadFileToBrowserStack(
+    testSuitePath,
+    "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/test-suite",
+    "test_suite_url",
+  );
+}
+
 // Triggers an Espresso test run on BrowserStack and returns the build_id
 export async function triggerEspressoBuild(
   app_url: string,
@@ -228,3 +248,32 @@ export async function triggerEspressoBuild(
     `Failed to trigger Espresso build: ${JSON.stringify(response.data)}`,
   );
 }
+
+// Triggers an XCUITest run on BrowserStack and returns the build_id
+export async function triggerXcuiBuild(
+  app_url: string,
+  test_suite_url: string,
+  devices: string[],
+  project: string,
+): Promise<string> {
+  const response = await axios.post(
+    "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/build",
+    {
+      app: app_url,
+      testSuite: test_suite_url,
+      devices,
+      project,
+    },
+    {
+      auth,
+    },
+  );
+
+  if (response.data.build_id) {
+    return response.data.build_id;
+  }
+
+  throw new Error(
+    `Failed to trigger XCUITest build: ${JSON.stringify(response.data)}`,
+  );
+}
diff --git a/src/tools/appautomate.ts b/src/tools/appautomate.ts
index 757b1f4..abec2d2 100644
--- a/src/tools/appautomate.ts
+++ b/src/tools/appautomate.ts
@@ -22,6 +22,9 @@ import {
   uploadEspressoApp,
   uploadEspressoTestSuite,
   triggerEspressoBuild,
+  uploadXcuiApp,
+  uploadXcuiTestSuite,
+  triggerXcuiBuild,
 } from "./appautomate-utils/appautomate.js";
 
 // Types
@@ -166,7 +169,7 @@ async function runAppTestsOnBrowserStack(args: {
           content: [
             {
               type: "text",
-              text: `✅ Test run started successfully!\n\n🔧 Build ID: ${build_id}\n🔗 View your build: https://app-automate.browserstack.com/builds/${build_id}`,
+              text: `✅ Espresso run started successfully!\n\n🔧 Build ID: ${build_id}\n🔗 View your build: https://app-automate.browserstack.com/builds/${build_id}`,
             },
           ],
         };
@@ -175,10 +178,32 @@ async function runAppTestsOnBrowserStack(args: {
         throw err;
       }
     }
-
+    case AppTestPlatform.XCUITEST: {
+      try {
+        const app_url = await uploadXcuiApp(args.appPath);
+        const test_suite_url = await uploadXcuiTestSuite(args.testSuitePath);
+        const build_id = await triggerXcuiBuild(
+          app_url,
+          test_suite_url,
+          args.devices,
+          args.project,
+        );
+        return {
+          content: [
+            {
+              type: "text",
+              text: `✅ XCUITest run started successfully!\n\n🔧 Build ID: ${build_id}\n🔗 View your build: https://app-automate.browserstack.com/builds/${build_id}`,
+            },
+          ],
+        };
+      } catch (err) {
+        logger.error("Error running XCUITest App Automate test", err);
+        throw err;
+      }
+    }
     default:
       throw new Error(
-        `Unsupported automation framework: ${args.detectedAutomationFramework}`,
+        `Unsupported automation framework: ${args.detectedAutomationFramework}. If you need support for this framework, please open an issue at Github`,
       );
   }
 }
@@ -230,18 +255,22 @@ export default function addAppAutomationTools(server: McpServer) {
 
   server.tool(
     "runAppTestsOnBrowserStack",
-    "Run AppAutomate tests on BrowserStack by uploading app and test suite, then triggering a test run.",
+    "Run AppAutomate tests on BrowserStack by uploading app and test suite, then triggering a test run. Supports both Espresso (Android) and XCUITest (iOS).",
     {
       appPath: z
         .string()
-        .describe("Path to the .apk or .aab file for your app."),
+        .describe(
+          "Path to the .apk/.aab (Espresso) or .ipa (XCUITest) file for your app. Export on your own in local IDEs.",
+        ),
       testSuitePath: z
         .string()
-        .describe("Path to the Espresso test suite .apk file."),
+        .describe(
+          "Path to the Espresso test suite .apk or XCUITest .zip file. Export on your own in local IDEs.",
+        ),
       devices: z
         .array(z.string())
         .describe(
-          "List of devices to run the test on, e.g., ['Samsung Galaxy S20-10.0', 'Google Pixel 3-9.0'].",
+          "List of devices to run the test on, e.g., ['Samsung Galaxy S20-10.0', 'iPhone 12 Pro-16.0'].",
         ),
       project: z
         .string()
@@ -251,7 +280,7 @@ export default function addAppAutomationTools(server: McpServer) {
       detectedAutomationFramework: z
         .string()
         .describe(
-          "The automation framework used in the project, such as 'espresso' or 'appium'.",
+          "The automation framework used in the project, such as 'espresso' (Android) or 'xcuitest' (iOS).",
         ),
     },
     async (args) => {
@@ -273,7 +302,7 @@ export default function addAppAutomationTools(server: McpServer) {
           content: [
             {
               type: "text",
-              text: `Error running App Automate test: ${errorMessage}`
+              text: `Error running App Automate test: ${errorMessage}`,
             },
           ],
           isError: true,

From 97859ed4f390fa688293e9ab26be03f32d7a20ef Mon Sep 17 00:00:00 2001
From: tech-sushant <sushant.s@browserstack.com>
Date: Tue, 1 Jul 2025 11:11:01 +0530
Subject: [PATCH 05/64] fix: enhance runAppTestsOnBrowserStack tool description
 and update app/test path instructions

---
 src/tools/appautomate.ts | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/src/tools/appautomate.ts b/src/tools/appautomate.ts
index abec2d2..556c45d 100644
--- a/src/tools/appautomate.ts
+++ b/src/tools/appautomate.ts
@@ -255,17 +255,31 @@ export default function addAppAutomationTools(server: McpServer) {
 
   server.tool(
     "runAppTestsOnBrowserStack",
-    "Run AppAutomate tests on BrowserStack by uploading app and test suite, then triggering a test run. Supports both Espresso (Android) and XCUITest (iOS).",
+    "Run AppAutomate tests on BrowserStack by uploading app and test suite. If running from Android Studio or Xcode, the tool will help export app and test files automatically. For other environments, you'll need to provide the paths to your pre-built app and test files.",
     {
       appPath: z
         .string()
         .describe(
-          "Path to the .apk/.aab (Espresso) or .ipa (XCUITest) file for your app. Export on your own in local IDEs.",
+          "Path to your application file:\n" +
+          "If in development IDE directory:\n" +
+          "• For Android: 'gradle assembleDebug'\n" +
+          "• For iOS:\n" +
+          "  xcodebuild clean -scheme YOUR_SCHEME && \\\n" +
+          "  xcodebuild archive -scheme YOUR_SCHEME -configuration Release -archivePath build/app.xcarchive && \\\n" +
+          "  xcodebuild -exportArchive -archivePath build/app.xcarchive -exportPath build/ipa -exportOptionsPlist exportOptions.plist\n\n" +
+          "If in other directory, provide existing app path"
         ),
       testSuitePath: z
         .string()
         .describe(
-          "Path to the Espresso test suite .apk or XCUITest .zip file. Export on your own in local IDEs.",
+          "Path to your test suite file:\n" +
+          "If in development IDE directory:\n" +
+          "• For Android: 'gradle assembleAndroidTest'\n" +
+          "• For iOS:\n" +
+          "  xcodebuild test-without-building -scheme YOUR_SCHEME -destination 'generic/platform=iOS' && \\\n" +
+          "  cd ~/Library/Developer/Xcode/DerivedData/*/Build/Products/Debug-iphonesimulator/ && \\\n" +
+          "  zip -r Tests.zip *.xctestrun *-Runner.app\n\n" +
+          "If in other directory, provide existing test file path"
         ),
       devices: z
         .array(z.string())
@@ -275,7 +289,7 @@ export default function addAppAutomationTools(server: McpServer) {
       project: z
         .string()
         .optional()
-        .default("Espresso Test")
+        .default("BStack-AppAutomate-Suite")
         .describe("Project name for organizing test runs on BrowserStack."),
       detectedAutomationFramework: z
         .string()

From b4da63a35bd8c445b9937d82b3b6bd520cacb336 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 1 Jul 2025 11:34:17 +0530
Subject: [PATCH 06/64] Refactor authentication handling for BrowserStack API
 calls

- Replaced direct usage of credentials from config with a new utility function `getBrowserStackAuth` to retrieve authentication details.
- Updated multiple functions across various modules to accept `server` parameter for improved flexibility and maintainability.
- Ensured all API calls to BrowserStack now utilize the new authentication method, enhancing security and code clarity.
- Adjusted related function signatures and calls to accommodate the new parameter structure.
---
 package-lock.json                             | 840 ++++++------------
 package.json                                  |   4 +
 src/index.ts                                  | 268 +++++-
 src/lib/api.ts                                |  12 +-
 src/lib/get-auth.ts                           |  10 +
 src/lib/instrumentation.ts                    |  15 +-
 src/lib/local.ts                              |   6 +-
 src/oninitialized.ts                          |   8 +-
 src/server-factory.ts                         |  45 +
 src/tools/accessibility.ts                    |  19 +-
 .../accessiblity-utils/accessibility-rag.ts   |  10 +-
 .../accessiblity-utils/report-fetcher.ts      |  10 +-
 src/tools/accessiblity-utils/scanner.ts       |  15 +-
 src/tools/appautomate-utils/appautomate.ts    |   9 +-
 src/tools/appautomate.ts                      |  15 +-
 src/tools/applive-utils/start-session.ts      |  12 +-
 src/tools/applive-utils/upload-app.ts         |   7 +-
 src/tools/applive.ts                          |  32 +-
 src/tools/automate-utils/fetch-screenshots.ts |  11 +-
 src/tools/automate.ts                         |  14 +-
 src/tools/failurelogs-utils/app-automate.ts   |  15 +-
 src/tools/failurelogs-utils/automate.ts       |  15 +-
 src/tools/getFailureLogs.ts                   |  29 +-
 src/tools/live-utils/start-session.ts         |  12 +-
 src/tools/live.ts                             |  14 +-
 src/tools/observability.ts                    |   5 +-
 src/tools/selfheal-utils/selfheal.ts          |   9 +-
 src/tools/selfheal.ts                         |  11 +-
 .../testmanagement-utils/TCG-utils/api.ts     |  29 +-
 .../testmanagement-utils/add-test-result.ts   |  10 +-
 .../testmanagement-utils/create-lca-steps.ts  |   9 +-
 .../create-project-folder.ts                  |  18 +-
 .../testmanagement-utils/create-testcase.ts   |  12 +-
 .../testmanagement-utils/create-testrun.ts    |   9 +-
 .../testmanagement-utils/list-testcases.ts    |   9 +-
 .../testmanagement-utils/list-testruns.ts     |   9 +-
 .../testmanagement-utils/poll-lca-status.ts   |   6 +-
 .../testcase-from-file.ts                     |   6 +
 .../testmanagement-utils/update-testrun.ts    |  10 +-
 src/tools/testmanagement-utils/upload-file.ts |   7 +-
 src/tools/testmanagement.ts                   |  92 +-
 41 files changed, 910 insertions(+), 788 deletions(-)
 create mode 100644 src/lib/get-auth.ts
 create mode 100644 src/server-factory.ts

diff --git a/package-lock.json b/package-lock.json
index eff5a06..a52c12f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,8 +13,10 @@
         "@types/form-data": "^2.5.2",
         "axios": "^1.8.4",
         "browserstack-local": "^1.5.6",
+        "cors": "^2.8.5",
         "csv-parse": "^5.6.0",
         "dotenv": "^16.5.0",
+        "express": "^5.1.0",
         "form-data": "^4.0.2",
         "pino": "^9.6.0",
         "pino-pretty": "^13.0.0",
@@ -28,7 +30,9 @@
       },
       "devDependencies": {
         "@eslint/js": "^9.25.0",
+        "@types/cors": "^2.8.19",
         "@types/csv-parse": "^1.1.12",
+        "@types/express": "^5.0.3",
         "@types/node": "^22.14.1",
         "@types/uuid": "^10.0.0",
         "eslint": "^9.25.0",
@@ -1143,18 +1147,6 @@
         "node": ">=18"
       }
     },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
-      "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
-      "dependencies": {
-        "mime-types": "^3.0.0",
-        "negotiator": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": {
       "version": "8.17.1",
       "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
@@ -1171,234 +1163,12 @@
         "url": "https://github.com/sponsors/epoberezkin"
       }
     },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
-      "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
-      "dependencies": {
-        "bytes": "^3.1.2",
-        "content-type": "^1.0.5",
-        "debug": "^4.4.0",
-        "http-errors": "^2.0.0",
-        "iconv-lite": "^0.6.3",
-        "on-finished": "^2.4.1",
-        "qs": "^6.14.0",
-        "raw-body": "^3.0.0",
-        "type-is": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
-      "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
-      "dependencies": {
-        "safe-buffer": "5.2.1"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
-      "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
-      "engines": {
-        "node": ">=6.6.0"
-      }
-    },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/express": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
-      "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
-      "dependencies": {
-        "accepts": "^2.0.0",
-        "body-parser": "^2.2.0",
-        "content-disposition": "^1.0.0",
-        "content-type": "^1.0.5",
-        "cookie": "^0.7.1",
-        "cookie-signature": "^1.2.1",
-        "debug": "^4.4.0",
-        "encodeurl": "^2.0.0",
-        "escape-html": "^1.0.3",
-        "etag": "^1.8.1",
-        "finalhandler": "^2.1.0",
-        "fresh": "^2.0.0",
-        "http-errors": "^2.0.0",
-        "merge-descriptors": "^2.0.0",
-        "mime-types": "^3.0.0",
-        "on-finished": "^2.4.1",
-        "once": "^1.4.0",
-        "parseurl": "^1.3.3",
-        "proxy-addr": "^2.0.7",
-        "qs": "^6.14.0",
-        "range-parser": "^1.2.1",
-        "router": "^2.2.0",
-        "send": "^1.1.0",
-        "serve-static": "^2.2.0",
-        "statuses": "^2.0.1",
-        "type-is": "^2.0.1",
-        "vary": "^1.1.2"
-      },
-      "engines": {
-        "node": ">= 18"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/express"
-      }
-    },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
-      "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
-      "dependencies": {
-        "debug": "^4.4.0",
-        "encodeurl": "^2.0.0",
-        "escape-html": "^1.0.3",
-        "on-finished": "^2.4.1",
-        "parseurl": "^1.3.3",
-        "statuses": "^2.0.1"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
-      "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite": {
-      "version": "0.6.3",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
-      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
-      "dependencies": {
-        "safer-buffer": ">= 2.1.2 < 3.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
       "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
       "license": "MIT"
     },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
-      "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
-      "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": {
-      "version": "1.54.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
-      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
-      "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
-      "dependencies": {
-        "mime-db": "^1.54.0"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
-      "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/qs": {
-      "version": "6.14.0",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
-      "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
-      "dependencies": {
-        "side-channel": "^1.1.0"
-      },
-      "engines": {
-        "node": ">=0.6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/send": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
-      "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
-      "dependencies": {
-        "debug": "^4.3.5",
-        "encodeurl": "^2.0.0",
-        "escape-html": "^1.0.3",
-        "etag": "^1.8.1",
-        "fresh": "^2.0.0",
-        "http-errors": "^2.0.0",
-        "mime-types": "^3.0.1",
-        "ms": "^2.1.3",
-        "on-finished": "^2.4.1",
-        "range-parser": "^1.2.1",
-        "statuses": "^2.0.1"
-      },
-      "engines": {
-        "node": ">= 18"
-      }
-    },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
-      "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
-      "dependencies": {
-        "encodeurl": "^2.0.0",
-        "escape-html": "^1.0.3",
-        "parseurl": "^1.3.3",
-        "send": "^1.2.0"
-      },
-      "engines": {
-        "node": ">= 18"
-      }
-    },
-    "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
-      "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
-      "dependencies": {
-        "content-type": "^1.0.5",
-        "media-typer": "^1.1.0",
-        "mime-types": "^3.0.0"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/@nodelib/fs.scandir": {
       "version": "2.1.5",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -1750,6 +1520,34 @@
       "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
       "license": "MIT"
     },
+    "node_modules/@types/body-parser": {
+      "version": "1.19.6",
+      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
+      "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
+      "dev": true,
+      "dependencies": {
+        "@types/connect": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/connect": {
+      "version": "3.4.38",
+      "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
+      "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/cors": {
+      "version": "2.8.19",
+      "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
+      "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/csv-parse": {
       "version": "1.1.12",
       "resolved": "https://registry.npmjs.org/@types/csv-parse/-/csv-parse-1.1.12.tgz",
@@ -1766,6 +1564,29 @@
       "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
       "dev": true
     },
+    "node_modules/@types/express": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz",
+      "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==",
+      "dev": true,
+      "dependencies": {
+        "@types/body-parser": "*",
+        "@types/express-serve-static-core": "^5.0.0",
+        "@types/serve-static": "*"
+      }
+    },
+    "node_modules/@types/express-serve-static-core": {
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz",
+      "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*",
+        "@types/qs": "*",
+        "@types/range-parser": "*",
+        "@types/send": "*"
+      }
+    },
     "node_modules/@types/form-data": {
       "version": "2.5.2",
       "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.5.2.tgz",
@@ -1775,12 +1596,24 @@
         "form-data": "*"
       }
     },
+    "node_modules/@types/http-errors": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
+      "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
+      "dev": true
+    },
     "node_modules/@types/json-schema": {
       "version": "7.0.15",
       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
       "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
       "dev": true
     },
+    "node_modules/@types/mime": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
+      "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
+      "dev": true
+    },
     "node_modules/@types/node": {
       "version": "22.14.1",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz",
@@ -1789,6 +1622,39 @@
         "undici-types": "~6.21.0"
       }
     },
+    "node_modules/@types/qs": {
+      "version": "6.14.0",
+      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
+      "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
+      "dev": true
+    },
+    "node_modules/@types/range-parser": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
+      "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
+      "dev": true
+    },
+    "node_modules/@types/send": {
+      "version": "0.17.5",
+      "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz",
+      "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==",
+      "dev": true,
+      "dependencies": {
+        "@types/mime": "^1",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/serve-static": {
+      "version": "1.15.8",
+      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz",
+      "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==",
+      "dev": true,
+      "dependencies": {
+        "@types/http-errors": "*",
+        "@types/node": "*",
+        "@types/send": "*"
+      }
+    },
     "node_modules/@types/sinonjs__fake-timers": {
       "version": "8.1.5",
       "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz",
@@ -2352,13 +2218,31 @@
       }
     },
     "node_modules/accepts": {
-      "version": "1.3.8",
-      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
-      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
-      "peer": true,
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+      "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
       "dependencies": {
-        "mime-types": "~2.1.34",
-        "negotiator": "0.6.3"
+        "mime-types": "^3.0.0",
+        "negotiator": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/accepts/node_modules/mime-db": {
+      "version": "1.54.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/accepts/node_modules/mime-types": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
+      "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+      "dependencies": {
+        "mime-db": "^1.54.0"
       },
       "engines": {
         "node": ">= 0.6"
@@ -2529,12 +2413,6 @@
         "node": ">= 0.4"
       }
     },
-    "node_modules/array-flatten": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
-      "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
-      "peer": true
-    },
     "node_modules/assertion-error": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
@@ -2689,65 +2567,30 @@
     },
     "node_modules/basic-ftp": {
       "version": "5.0.5",
-      "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
-      "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=10.0.0"
-      }
-    },
-    "node_modules/body-parser": {
-      "version": "1.20.3",
-      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
-      "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
-      "peer": true,
-      "dependencies": {
-        "bytes": "3.1.2",
-        "content-type": "~1.0.5",
-        "debug": "2.6.9",
-        "depd": "2.0.0",
-        "destroy": "1.2.0",
-        "http-errors": "2.0.0",
-        "iconv-lite": "0.4.24",
-        "on-finished": "2.4.1",
-        "qs": "6.13.0",
-        "raw-body": "2.5.2",
-        "type-is": "~1.6.18",
-        "unpipe": "1.0.0"
-      },
+      "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
+      "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==",
+      "license": "MIT",
       "engines": {
-        "node": ">= 0.8",
-        "npm": "1.2.8000 || >= 1.4.16"
-      }
-    },
-    "node_modules/body-parser/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "peer": true,
-      "dependencies": {
-        "ms": "2.0.0"
+        "node": ">=10.0.0"
       }
     },
-    "node_modules/body-parser/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
-      "peer": true
-    },
-    "node_modules/body-parser/node_modules/raw-body": {
-      "version": "2.5.2",
-      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
-      "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
-      "peer": true,
+    "node_modules/body-parser": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
+      "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
       "dependencies": {
-        "bytes": "3.1.2",
-        "http-errors": "2.0.0",
-        "iconv-lite": "0.4.24",
-        "unpipe": "1.0.0"
+        "bytes": "^3.1.2",
+        "content-type": "^1.0.5",
+        "debug": "^4.4.0",
+        "http-errors": "^2.0.0",
+        "iconv-lite": "^0.6.3",
+        "on-finished": "^2.4.1",
+        "qs": "^6.14.0",
+        "raw-body": "^3.0.0",
+        "type-is": "^2.0.0"
       },
       "engines": {
-        "node": ">= 0.8"
+        "node": ">=18"
       }
     },
     "node_modules/boolbase": {
@@ -3056,10 +2899,9 @@
       "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
     },
     "node_modules/content-disposition": {
-      "version": "0.5.4",
-      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
-      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
-      "peer": true,
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
+      "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
       "dependencies": {
         "safe-buffer": "5.2.1"
       },
@@ -3084,10 +2926,12 @@
       }
     },
     "node_modules/cookie-signature": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
-      "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
-      "peer": true
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+      "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+      "engines": {
+        "node": ">=6.6.0"
+      }
     },
     "node_modules/core-util-is": {
       "version": "1.0.3",
@@ -3289,16 +3133,6 @@
         "node": ">= 0.8"
       }
     },
-    "node_modules/destroy": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
-      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
-      "peer": true,
-      "engines": {
-        "node": ">= 0.8",
-        "npm": "1.2.8000 || >= 1.4.16"
-      }
-    },
     "node_modules/detect-libc": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
@@ -3515,18 +3349,6 @@
         "url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
       }
     },
-    "node_modules/encoding-sniffer/node_modules/iconv-lite": {
-      "version": "0.6.3",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
-      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
-      "license": "MIT",
-      "dependencies": {
-        "safer-buffer": ">= 2.1.2 < 3.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/end-of-stream": {
       "version": "1.4.4",
       "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@@ -3915,45 +3737,40 @@
       }
     },
     "node_modules/express": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
-      "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
-      "peer": true,
-      "dependencies": {
-        "accepts": "~1.3.8",
-        "array-flatten": "1.1.1",
-        "body-parser": "1.20.3",
-        "content-disposition": "0.5.4",
-        "content-type": "~1.0.4",
-        "cookie": "0.7.1",
-        "cookie-signature": "1.0.6",
-        "debug": "2.6.9",
-        "depd": "2.0.0",
-        "encodeurl": "~2.0.0",
-        "escape-html": "~1.0.3",
-        "etag": "~1.8.1",
-        "finalhandler": "1.3.1",
-        "fresh": "0.5.2",
-        "http-errors": "2.0.0",
-        "merge-descriptors": "1.0.3",
-        "methods": "~1.1.2",
-        "on-finished": "2.4.1",
-        "parseurl": "~1.3.3",
-        "path-to-regexp": "0.1.12",
-        "proxy-addr": "~2.0.7",
-        "qs": "6.13.0",
-        "range-parser": "~1.2.1",
-        "safe-buffer": "5.2.1",
-        "send": "0.19.0",
-        "serve-static": "1.16.2",
-        "setprototypeof": "1.2.0",
-        "statuses": "2.0.1",
-        "type-is": "~1.6.18",
-        "utils-merge": "1.0.1",
-        "vary": "~1.1.2"
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
+      "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
+      "dependencies": {
+        "accepts": "^2.0.0",
+        "body-parser": "^2.2.0",
+        "content-disposition": "^1.0.0",
+        "content-type": "^1.0.5",
+        "cookie": "^0.7.1",
+        "cookie-signature": "^1.2.1",
+        "debug": "^4.4.0",
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "etag": "^1.8.1",
+        "finalhandler": "^2.1.0",
+        "fresh": "^2.0.0",
+        "http-errors": "^2.0.0",
+        "merge-descriptors": "^2.0.0",
+        "mime-types": "^3.0.0",
+        "on-finished": "^2.4.1",
+        "once": "^1.4.0",
+        "parseurl": "^1.3.3",
+        "proxy-addr": "^2.0.7",
+        "qs": "^6.14.0",
+        "range-parser": "^1.2.1",
+        "router": "^2.2.0",
+        "send": "^1.1.0",
+        "serve-static": "^2.2.0",
+        "statuses": "^2.0.1",
+        "type-is": "^2.0.1",
+        "vary": "^1.1.2"
       },
       "engines": {
-        "node": ">= 0.10.0"
+        "node": ">= 18"
       },
       "funding": {
         "type": "opencollective",
@@ -3974,20 +3791,24 @@
         "express": "^4.11 || 5 || ^5.0.0-beta.1"
       }
     },
-    "node_modules/express/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "peer": true,
-      "dependencies": {
-        "ms": "2.0.0"
+    "node_modules/express/node_modules/mime-db": {
+      "version": "1.54.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+      "engines": {
+        "node": ">= 0.6"
       }
     },
-    "node_modules/express/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
-      "peer": true
+    "node_modules/express/node_modules/mime-types": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
+      "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+      "dependencies": {
+        "mime-db": "^1.54.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
     },
     "node_modules/extract-zip": {
       "version": "2.0.1",
@@ -4193,38 +4014,21 @@
       }
     },
     "node_modules/finalhandler": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
-      "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
-      "peer": true,
-      "dependencies": {
-        "debug": "2.6.9",
-        "encodeurl": "~2.0.0",
-        "escape-html": "~1.0.3",
-        "on-finished": "2.4.1",
-        "parseurl": "~1.3.3",
-        "statuses": "2.0.1",
-        "unpipe": "~1.0.0"
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
+      "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
+      "dependencies": {
+        "debug": "^4.4.0",
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "on-finished": "^2.4.1",
+        "parseurl": "^1.3.3",
+        "statuses": "^2.0.1"
       },
       "engines": {
         "node": ">= 0.8"
       }
     },
-    "node_modules/finalhandler/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "peer": true,
-      "dependencies": {
-        "ms": "2.0.0"
-      }
-    },
-    "node_modules/finalhandler/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
-      "peer": true
-    },
     "node_modules/find-up": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -4342,12 +4146,11 @@
       }
     },
     "node_modules/fresh": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
-      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
-      "peer": true,
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+      "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
       "engines": {
-        "node": ">= 0.6"
+        "node": ">= 0.8"
       }
     },
     "node_modules/from": {
@@ -4737,12 +4540,11 @@
       }
     },
     "node_modules/iconv-lite": {
-      "version": "0.4.24",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
-      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
-      "peer": true,
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
       "dependencies": {
-        "safer-buffer": ">= 2.1.2 < 3"
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
       },
       "engines": {
         "node": ">=0.10.0"
@@ -5236,19 +5038,20 @@
       }
     },
     "node_modules/media-typer": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
-      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
-      "peer": true,
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+      "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
       "engines": {
-        "node": ">= 0.6"
+        "node": ">= 0.8"
       }
     },
     "node_modules/merge-descriptors": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
-      "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
-      "peer": true,
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+      "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+      "engines": {
+        "node": ">=18"
+      },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
@@ -5262,15 +5065,6 @@
         "node": ">= 8"
       }
     },
-    "node_modules/methods": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
-      "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
-      "peer": true,
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/micromatch": {
       "version": "4.0.8",
       "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
@@ -5284,18 +5078,6 @@
         "node": ">=8.6"
       }
     },
-    "node_modules/mime": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
-      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
-      "peer": true,
-      "bin": {
-        "mime": "cli.js"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
     "node_modules/mime-db": {
       "version": "1.52.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -5373,10 +5155,9 @@
       "dev": true
     },
     "node_modules/negotiator": {
-      "version": "0.6.3",
-      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
-      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
-      "peer": true,
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+      "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
       "engines": {
         "node": ">= 0.6"
       }
@@ -5723,12 +5504,6 @@
       "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
       "license": "ISC"
     },
-    "node_modules/path-to-regexp": {
-      "version": "0.1.12",
-      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
-      "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
-      "peer": true
-    },
     "node_modules/pathe": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
@@ -6032,12 +5807,11 @@
       }
     },
     "node_modules/qs": {
-      "version": "6.13.0",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
-      "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
-      "peer": true,
+      "version": "6.14.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
+      "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
       "dependencies": {
-        "side-channel": "^1.0.6"
+        "side-channel": "^1.1.0"
       },
       "engines": {
         "node": ">=0.6"
@@ -6099,17 +5873,6 @@
         "node": ">= 0.8"
       }
     },
-    "node_modules/raw-body/node_modules/iconv-lite": {
-      "version": "0.6.3",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
-      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
-      "dependencies": {
-        "safer-buffer": ">= 2.1.2 < 3.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/readable-stream": {
       "version": "4.7.0",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
@@ -6385,51 +6148,43 @@
       }
     },
     "node_modules/send": {
-      "version": "0.19.0",
-      "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
-      "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
-      "peer": true,
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
+      "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
       "dependencies": {
-        "debug": "2.6.9",
-        "depd": "2.0.0",
-        "destroy": "1.2.0",
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "etag": "~1.8.1",
-        "fresh": "0.5.2",
-        "http-errors": "2.0.0",
-        "mime": "1.6.0",
-        "ms": "2.1.3",
-        "on-finished": "2.4.1",
-        "range-parser": "~1.2.1",
-        "statuses": "2.0.1"
+        "debug": "^4.3.5",
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "etag": "^1.8.1",
+        "fresh": "^2.0.0",
+        "http-errors": "^2.0.0",
+        "mime-types": "^3.0.1",
+        "ms": "^2.1.3",
+        "on-finished": "^2.4.1",
+        "range-parser": "^1.2.1",
+        "statuses": "^2.0.1"
       },
       "engines": {
-        "node": ">= 0.8.0"
+        "node": ">= 18"
       }
     },
-    "node_modules/send/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "peer": true,
-      "dependencies": {
-        "ms": "2.0.0"
+    "node_modules/send/node_modules/mime-db": {
+      "version": "1.54.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+      "engines": {
+        "node": ">= 0.6"
       }
     },
-    "node_modules/send/node_modules/debug/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
-      "peer": true
-    },
-    "node_modules/send/node_modules/encodeurl": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
-      "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
-      "peer": true,
+    "node_modules/send/node_modules/mime-types": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
+      "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+      "dependencies": {
+        "mime-db": "^1.54.0"
+      },
       "engines": {
-        "node": ">= 0.8"
+        "node": ">= 0.6"
       }
     },
     "node_modules/serialize-error": {
@@ -6460,18 +6215,17 @@
       }
     },
     "node_modules/serve-static": {
-      "version": "1.16.2",
-      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
-      "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
-      "peer": true,
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
+      "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
       "dependencies": {
-        "encodeurl": "~2.0.0",
-        "escape-html": "~1.0.3",
-        "parseurl": "~1.3.3",
-        "send": "0.19.0"
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "parseurl": "^1.3.3",
+        "send": "^1.2.0"
       },
       "engines": {
-        "node": ">= 0.8.0"
+        "node": ">= 18"
       }
     },
     "node_modules/setimmediate": {
@@ -7086,13 +6840,32 @@
       }
     },
     "node_modules/type-is": {
-      "version": "1.6.18",
-      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
-      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
-      "peer": true,
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
+      "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
+      "dependencies": {
+        "content-type": "^1.0.5",
+        "media-typer": "^1.1.0",
+        "mime-types": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/type-is/node_modules/mime-db": {
+      "version": "1.54.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/type-is/node_modules/mime-types": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
+      "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
       "dependencies": {
-        "media-typer": "0.3.0",
-        "mime-types": "~2.1.24"
+        "mime-db": "^1.54.0"
       },
       "engines": {
         "node": ">= 0.6"
@@ -7185,15 +6958,6 @@
       "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
       "license": "MIT"
     },
-    "node_modules/utils-merge": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
-      "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
-      "peer": true,
-      "engines": {
-        "node": ">= 0.4.0"
-      }
-    },
     "node_modules/uuid": {
       "version": "11.1.0",
       "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
@@ -7540,18 +7304,6 @@
         "node": ">=18"
       }
     },
-    "node_modules/whatwg-encoding/node_modules/iconv-lite": {
-      "version": "0.6.3",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
-      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
-      "license": "MIT",
-      "dependencies": {
-        "safer-buffer": ">= 2.1.2 < 3.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/whatwg-mimetype": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
diff --git a/package.json b/package.json
index 5c15fb3..d412631 100644
--- a/package.json
+++ b/package.json
@@ -38,8 +38,10 @@
     "@types/form-data": "^2.5.2",
     "axios": "^1.8.4",
     "browserstack-local": "^1.5.6",
+    "cors": "^2.8.5",
     "csv-parse": "^5.6.0",
     "dotenv": "^16.5.0",
+    "express": "^5.1.0",
     "form-data": "^4.0.2",
     "pino": "^9.6.0",
     "pino-pretty": "^13.0.0",
@@ -50,7 +52,9 @@
   },
   "devDependencies": {
     "@eslint/js": "^9.25.0",
+    "@types/cors": "^2.8.19",
     "@types/csv-parse": "^1.1.12",
+    "@types/express": "^5.0.3",
     "@types/node": "^22.14.1",
     "@types/uuid": "^10.0.0",
     "eslint": "^9.25.0",
diff --git a/src/index.ts b/src/index.ts
index c5da73f..7fab623 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,59 +1,247 @@
 #!/usr/bin/env node
 
+import express from "express";
+import cors from "cors";
+import { randomUUID } from "node:crypto";
+import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
+import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
-import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 import { createRequire } from "module";
 const require = createRequire(import.meta.url);
 const packageJson = require("../package.json");
 import "dotenv/config";
 import logger from "./logger.js";
-import addSDKTools from "./tools/bstack-sdk.js";
-import addAppLiveTools from "./tools/applive.js";
-import addBrowserLiveTools from "./tools/live.js";
-import addAccessibilityTools from "./tools/accessibility.js";
-import addTestManagementTools from "./tools/testmanagement.js";
-import addAppAutomationTools from "./tools/appautomate.js";
-import addFailureLogsTools from "./tools/getFailureLogs.js";
-import addAutomateTools from "./tools/automate.js";
-import addSelfHealTools from "./tools/selfheal.js";
-import { setupOnInitialized } from "./oninitialized.js";
-
-function registerTools(server: McpServer) {
-  addAccessibilityTools(server);
-  addSDKTools(server);
-  addAppLiveTools(server);
-  addBrowserLiveTools(server);
-  addTestManagementTools(server);
-  addAppAutomationTools(server);
-  addFailureLogsTools(server);
-  addAutomateTools(server);
-  addSelfHealTools(server);
-}
+import { createMcpServer } from "./server-factory.js";
+
+const PORT = process.env.MCP_PORT ? parseInt(process.env.MCP_PORT) : 3000;
+const ALLOWED_ORIGINS = process.env.ALLOWED_ORIGINS?.split(",") || ["*"];
+const ALLOWED_HOSTS = process.env.ALLOWED_HOSTS?.split(",") || [
+  "127.0.0.1",
+  "localhost",
+];
+
+const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
+
+const app = express();
 
-// Create an MCP server
-const server: McpServer = new McpServer({
-  name: "BrowserStack MCP Server",
-  version: packageJson.version,
+app.use(
+  cors({
+    origin: ALLOWED_ORIGINS,
+    credentials: true,
+    exposedHeaders: ["mcp-session-id"],
+    allowedHeaders: [
+      "Content-Type",
+      "Authorization",
+      "mcp-session-id",
+      "browserstack-username",
+      "browserstack-access-key",
+    ],
+    methods: ["GET", "POST", "DELETE", "OPTIONS"],
+  }),
+);
+
+app.use(express.json({ limit: "10mb" }));
+
+app.get("/health", (req, res) => {
+  res.json({
+    status: "ok",
+    version: packageJson.version,
+    timestamp: new Date().toISOString(),
+    transports: Object.keys(transports).length,
+  });
 });
 
-setupOnInitialized(server);
+app.post("/mcp", async (req, res) => {
+  try {
+    const sessionId = req.headers["mcp-session-id"] as string | undefined;
+    const browserstackUsername = req.headers["browserstack-username"] as
+      | string
+      | undefined;
+    const browserstackAccessKey = req.headers["browserstack-access-key"] as
+      | string
+      | undefined;
 
-registerTools(server);
+    if (!browserstackUsername || !browserstackAccessKey) {
+      logger.warn("Missing BrowserStack credentials in headers");
+      res.status(401).json({
+        jsonrpc: "2.0",
+        error: {
+          code: -32001,
+          message: "Unauthorized: Missing BrowserStack credentials",
+        },
+        id: null,
+      });
+      return;
+    }
 
-async function main() {
-  logger.info(
-    "Launching BrowserStack MCP server, version %s",
-    packageJson.version,
-  );
+    let transport: StreamableHTTPServerTransport;
 
-  // Start receiving messages on stdin and sending messages on stdout
-  const transport = new StdioServerTransport();
-  await server.connect(transport);
-}
+    logger.info(`Received MCP POST request, sessionId: ${sessionId}`);
+
+    if (sessionId && transports[sessionId]) {
+      // Reuse existing transport
+      transport = transports[sessionId];
+      logger.info(`Reusing existing transport for session: ${sessionId}`);
+    } else if (!sessionId && isInitializeRequest(req.body)) {
+      // New initialization request
+      logger.info("Creating new MCP session");
+
+      transport = new StreamableHTTPServerTransport({
+        sessionIdGenerator: () => randomUUID(),
+        onsessioninitialized: (newSessionId) => {
+          logger.info(`Session initialized: ${newSessionId}`);
+          transports[newSessionId] = transport;
+        },
+      });
+
+      // Clean up transport when closed
+      transport.onclose = () => {
+        if (transport.sessionId) {
+          logger.info(
+            `Cleaning up transport for session: ${transport.sessionId}`,
+          );
+          delete transports[transport.sessionId];
+        }
+      };
+
+      // Create and connect MCP server
+      const server = createMcpServer() as McpServer & {
+        authHeaders?: Record<string, string>;
+      };
+      server.authHeaders = {
+        "browserstack-username": browserstackUsername,
+        "browserstack-access-key": browserstackAccessKey,
+      };
+      await server.connect(transport);
+
+      logger.info("MCP server connected to transport");
+    } else {
+      // Invalid request
+      logger.warn("Invalid MCP request - no valid session ID provided");
+      res.status(400).json({
+        jsonrpc: "2.0",
+        error: {
+          code: -32000,
+          message: "Bad Request: No valid session ID provided",
+        },
+        id: null,
+      });
+      return;
+    }
+
+    // Handle the request
+    await transport.handleRequest(req, res, req.body);
+  } catch (error) {
+    logger.error("Error handling MCP request:", error);
+    if (!res.headersSent) {
+      res.status(500).json({
+        jsonrpc: "2.0",
+        error: {
+          code: -32603,
+          message: "Internal server error",
+        },
+        id: null,
+      });
+    }
+  }
+});
+
+// Reusable handler for GET and DELETE requests
+const handleSessionRequest = async (
+  req: express.Request,
+  res: express.Response,
+) => {
+  const sessionId = req.headers["mcp-session-id"] as string | undefined;
+  logger.info(`Received request for session: ${sessionId}`);
+
+  if (!sessionId || !transports[sessionId]) {
+    logger.warn(`Invalid or missing session ID: ${sessionId}`);
+    res.status(400).json({
+      jsonrpc: "2.0",
+      error: {
+        code: -32000,
+        message: "Invalid or missing session ID",
+      },
+      id: null,
+    });
+    return;
+  }
+
+  try {
+    const transport = transports[sessionId];
+    logger.info(`Handling session request for: ${sessionId}`);
+    await transport.handleRequest(req, res);
+  } catch (error) {
+    logger.error("Error handling session request:", error);
+    if (!res.headersSent) {
+      res.status(500).json({
+        jsonrpc: "2.0",
+        error: {
+          code: -32603,
+          message: "Internal server error",
+        },
+        id: null,
+      });
+    }
+  }
+};
 
-main().catch(console.error);
+app.get("/mcp", handleSessionRequest);
+
+app.delete("/mcp", handleSessionRequest);
+
+app.use((err: any, req: express.Request, res: express.Response) => {
+  logger.error("Express error:", err);
+  if (!res.headersSent) {
+    res.status(500).json({
+      jsonrpc: "2.0",
+      error: {
+        code: -32603,
+        message: "Internal server error",
+      },
+      id: null,
+    });
+  }
+});
+
+async function startServer() {
+  try {
+    const server = app.listen(PORT, () => {
+      logger.info(
+        `BrowserStack MCP HTTP Server v${packageJson.version} listening on port ${PORT}`,
+      );
+      logger.info(`Health check available at: http://localhost:${PORT}/health`);
+      logger.info(`MCP endpoint available at: http://localhost:${PORT}/mcp`);
+      logger.info(`Allowed origins: ${ALLOWED_ORIGINS.join(", ")}`);
+      logger.info(`Allowed hosts: ${ALLOWED_HOSTS.join(", ")}`);
+    });
+
+    process.on("SIGTERM", () => {
+      logger.info("Received SIGTERM, shutting down gracefully");
+      server.close(() => {
+        logger.info("HTTP server closed");
+        process.exit(0);
+      });
+    });
+
+    process.on("SIGINT", () => {
+      logger.info("Received SIGINT, shutting down gracefully");
+      server.close(() => {
+        logger.info("HTTP server closed");
+        process.exit(0);
+      });
+    });
+  } catch (error) {
+    logger.error("Failed to start server:", error);
+    process.exit(1);
+  }
+}
 
-// Ensure logs are flushed before exit
 process.on("exit", () => {
   logger.flush();
 });
+
+startServer().catch((error) => {
+  logger.error("Failed to start server:", error);
+  process.exit(1);
+});
diff --git a/src/lib/api.ts b/src/lib/api.ts
index af453f1..faa30ca 100644
--- a/src/lib/api.ts
+++ b/src/lib/api.ts
@@ -1,18 +1,22 @@
-import config from "../config.js";
+
+
+import { getBrowserStackAuth } from "./get-auth.js";
 
 export async function getLatestO11YBuildInfo(
   buildName: string,
   projectName: string,
+  server: any,
 ) {
   const buildsUrl = `https://api-observability.browserstack.com/ext/v1/builds/latest?build_name=${encodeURIComponent(
     buildName,
   )}&project_name=${encodeURIComponent(projectName)}`;
 
+  const authString = getBrowserStackAuth(server);
+  const auth = Buffer.from(authString).toString("base64");
+
   const buildsResponse = await fetch(buildsUrl, {
     headers: {
-      Authorization: `Basic ${Buffer.from(
-        `${config.browserstackUsername}:${config.browserstackAccessKey}`,
-      ).toString("base64")}`,
+      Authorization: `Basic ${auth}`,
     },
   });
 
diff --git a/src/lib/get-auth.ts b/src/lib/get-auth.ts
new file mode 100644
index 0000000..88e82c6
--- /dev/null
+++ b/src/lib/get-auth.ts
@@ -0,0 +1,10 @@
+export function getBrowserStackAuth(server: {
+  authHeaders?: Record<string, string>;
+}) {
+  const username = server.authHeaders?.["browserstack-username"];
+  const accessKey = server.authHeaders?.["browserstack-access-key"];
+  if (!username || !accessKey) {
+    throw new Error("BrowserStack credentials not set on server.authHeaders");
+  }
+  return `${username}:${accessKey}`;
+}
diff --git a/src/lib/instrumentation.ts b/src/lib/instrumentation.ts
index e6eccb4..8f1376f 100644
--- a/src/lib/instrumentation.ts
+++ b/src/lib/instrumentation.ts
@@ -1,4 +1,5 @@
 import logger from "../logger.js";
+import { getBrowserStackAuth } from "./get-auth.js";
 import config from "../config.js";
 import { createRequire } from "module";
 const require = createRequire(import.meta.url);
@@ -21,6 +22,7 @@ export function trackMCP(
   toolName: string,
   clientInfo: { name?: string; version?: string },
   error?: unknown,
+  server?: any,
 ): void {
   if (config.DEV_MODE) {
     logger.info("Tracking MCP is disabled in dev mode");
@@ -58,13 +60,20 @@ export function trackMCP(
       error instanceof Error ? error.constructor.name : "Unknown";
   }
 
+  let authHeader = undefined;
+  if (server) {
+    const authString = getBrowserStackAuth(server);
+    authHeader = `Basic ${Buffer.from(authString).toString("base64")}`;
+  } else {
+    // fallback for legacy usage
+    authHeader = undefined;
+  }
+
   axios
     .post(instrumentationEndpoint, event, {
       headers: {
         "Content-Type": "application/json",
-        Authorization: `Basic ${Buffer.from(
-          `${config.browserstackUsername}:${config.browserstackAccessKey}`,
-        ).toString("base64")}`,
+        ...(authHeader ? { Authorization: authHeader } : {}),
       },
       timeout: 2000,
     })
diff --git a/src/lib/local.ts b/src/lib/local.ts
index 4a99bad..90740f8 100644
--- a/src/lib/local.ts
+++ b/src/lib/local.ts
@@ -74,6 +74,8 @@ export async function killExistingBrowserStackLocalProcesses() {
 }
 
 export async function ensureLocalBinarySetup(
+  username: string,
+  password: string,
   localIdentifier?: string,
 ): Promise<void> {
   logger.info(
@@ -86,8 +88,8 @@ export async function ensureLocalBinarySetup(
   // Use a single options object from config and extend with required fields
   const bsLocalArgs: Record<string, any> = {
     ...(config.browserstackLocalOptions || {}),
-    key: config.browserstackAccessKey,
-    username: config.browserstackUsername,
+    key: password,
+    username,
   };
 
   if (localIdentifier) {
diff --git a/src/oninitialized.ts b/src/oninitialized.ts
index a3f2e4f..6fc5cea 100644
--- a/src/oninitialized.ts
+++ b/src/oninitialized.ts
@@ -1,8 +1,8 @@
-import config from "./config.js";
 import { trackMCP } from "./lib/instrumentation.js";
 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 
 export function setupOnInitialized(server: McpServer) {
+
   const nodeVersion = process.versions.node;
 
   // Check for Node.js version
@@ -12,12 +12,6 @@ export function setupOnInitialized(server: McpServer) {
     );
   }
 
-  // Check for BrowserStack credentials
-  if (!config.browserstackUsername || !config.browserstackAccessKey) {
-    throw new Error(
-      "BrowserStack credentials are missing. Please provide a valid username and access key.",
-    );
-  }
   server.server.oninitialized = () => {
     trackMCP("started", server.server.getClientVersion()!);
   };
diff --git a/src/server-factory.ts b/src/server-factory.ts
new file mode 100644
index 0000000..b730239
--- /dev/null
+++ b/src/server-factory.ts
@@ -0,0 +1,45 @@
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { createRequire } from "module";
+const require = createRequire(import.meta.url);
+const packageJson = require("../package.json");
+import logger from "./logger.js";
+import addSDKTools from "./tools/bstack-sdk.js";
+import addAppLiveTools from "./tools/applive.js";
+import addBrowserLiveTools from "./tools/live.js";
+import addAccessibilityTools from "./tools/accessibility.js";
+import addTestManagementTools from "./tools/testmanagement.js";
+import addAppAutomationTools from "./tools/appautomate.js";
+import addFailureLogsTools from "./tools/getFailureLogs.js";
+import addAutomateTools from "./tools/automate.js";
+import addSelfHealTools from "./tools/selfheal.js";
+import { setupOnInitialized } from "./oninitialized.js";
+
+function registerTools(server: McpServer) {
+  addAccessibilityTools(server); //done
+  addSDKTools(server);
+  addAppLiveTools(server); //done
+  addBrowserLiveTools(server); //done
+  addTestManagementTools(server); //done
+  addAppAutomationTools(server); //done
+  addFailureLogsTools(server); //done
+  addAutomateTools(server); //done
+  addSelfHealTools(server); //done
+}
+
+export function createMcpServer(): McpServer {
+  logger.info(
+    "Creating BrowserStack MCP Server, version %s",
+    packageJson.version,
+  );
+
+  // Create an MCP server
+  const server: McpServer = new McpServer({
+    name: "BrowserStack MCP Server",
+    version: packageJson.version,
+  });
+
+  setupOnInitialized(server);
+  registerTools(server);
+
+  return server;
+}
diff --git a/src/tools/accessibility.ts b/src/tools/accessibility.ts
index d7eddaf..88947ad 100644
--- a/src/tools/accessibility.ts
+++ b/src/tools/accessibility.ts
@@ -6,15 +6,20 @@ import { AccessibilityReportFetcher } from "./accessiblity-utils/report-fetcher.
 import { trackMCP } from "../lib/instrumentation.js";
 import { parseAccessibilityReportFromCSV } from "./accessiblity-utils/report-parser.js";
 import { queryAccessibilityRAG } from "./accessiblity-utils/accessibility-rag.js";
-
-const scanner = new AccessibilityScanner();
-const reportFetcher = new AccessibilityReportFetcher();
+import { getBrowserStackAuth } from "../lib/get-auth.js";
 
 async function runAccessibilityScan(
   name: string,
   pageURL: string,
   context: any,
+  server: any
 ): Promise<CallToolResult> {
+  // Create scanner and set auth on the go
+  const scanner = new AccessibilityScanner();
+  const authString = getBrowserStackAuth(server);
+  const [username, password] = authString.split(":");
+  scanner.setAuth({ username, password });
+
   // Start scan
   const startResp = await scanner.startScan(name, [pageURL]);
   const scanId = startResp.data!.id;
@@ -46,6 +51,10 @@ async function runAccessibilityScan(
     };
   }
 
+  // Create report fetcher and set auth on the go
+  const reportFetcher = new AccessibilityReportFetcher();
+  reportFetcher.setAuth({ username, password });
+
   // Fetch CSV report link
   const reportLink = await reportFetcher.getReportLink(scanId, scanRunId);
 
@@ -84,7 +93,7 @@ export default function addAccessibilityTools(server: McpServer) {
     async (args) => {
       try {
         trackMCP("accessibilityExpert", server.server.getClientVersion()!);
-        return await queryAccessibilityRAG(args.query);
+        return await queryAccessibilityRAG(args.query, server);
       } catch (error) {
         trackMCP(
           "accessibilityExpert",
@@ -116,7 +125,7 @@ export default function addAccessibilityTools(server: McpServer) {
     async (args, context) => {
       try {
         trackMCP("startAccessibilityScan", server.server.getClientVersion()!);
-        return await runAccessibilityScan(args.name, args.pageURL, context);
+        return await runAccessibilityScan(args.name, args.pageURL, context, server);
       } catch (error) {
         trackMCP(
           "startAccessibilityScan",
diff --git a/src/tools/accessiblity-utils/accessibility-rag.ts b/src/tools/accessiblity-utils/accessibility-rag.ts
index 7be7cb6..6668b11 100644
--- a/src/tools/accessiblity-utils/accessibility-rag.ts
+++ b/src/tools/accessiblity-utils/accessibility-rag.ts
@@ -1,17 +1,17 @@
 import fetch from "node-fetch";
-import config from "../../config.js";
 
 export interface RAGChunk {
   url: string;
   content: string;
 }
 
-export async function queryAccessibilityRAG(userQuery: string): Promise<any> {
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
+
+export async function queryAccessibilityRAG(userQuery: string, server: any): Promise<any> {
   const url = "https://accessibility.browserstack.com/api/tcg-proxy/search";
 
-  const auth = Buffer.from(
-    `${config.browserstackUsername}:${config.browserstackAccessKey}`,
-  ).toString("base64");
+  const authString = getBrowserStackAuth(server);
+  const auth = Buffer.from(authString).toString("base64");
 
   const response = await fetch(url, {
     method: "POST",
diff --git a/src/tools/accessiblity-utils/report-fetcher.ts b/src/tools/accessiblity-utils/report-fetcher.ts
index 498ea20..3446abc 100644
--- a/src/tools/accessiblity-utils/report-fetcher.ts
+++ b/src/tools/accessiblity-utils/report-fetcher.ts
@@ -1,5 +1,4 @@
 import axios from "axios";
-import config from "../../config.js";
 
 interface ReportInitResponse {
   success: true;
@@ -14,10 +13,11 @@ interface ReportResponse {
 }
 
 export class AccessibilityReportFetcher {
-  private auth = {
-    username: config.browserstackUsername,
-    password: config.browserstackAccessKey,
-  };
+  private auth: { username: string; password: string } | undefined;
+
+  public setAuth(auth: { username: string; password: string }): void {
+    this.auth = auth;
+  }
 
   async getReportLink(scanId: string, scanRunId: string): Promise<string> {
     // Initiate CSV link generation
diff --git a/src/tools/accessiblity-utils/scanner.ts b/src/tools/accessiblity-utils/scanner.ts
index ae73da8..91173f8 100644
--- a/src/tools/accessiblity-utils/scanner.ts
+++ b/src/tools/accessiblity-utils/scanner.ts
@@ -1,5 +1,4 @@
 import axios from "axios";
-import config from "../../config.js";
 import logger from "../../logger.js";
 import {
   isLocalURL,
@@ -20,15 +19,19 @@ export interface AccessibilityScanStatus {
 }
 
 export class AccessibilityScanner {
-  private auth = {
-    username: config.browserstackUsername,
-    password: config.browserstackAccessKey,
-  };
+  private auth: { username: string; password: string } | undefined;
+
+  public setAuth(auth: { username: string; password: string }): void {
+    this.auth = auth;
+  }
 
   async startScan(
     name: string,
     urlList: string[],
   ): Promise<AccessibilityScanResponse> {
+    if (!this.auth?.username || !this.auth?.password) {
+      throw new Error("BrowserStack credentials are not set for AccessibilityScanner.");
+    }
     // Check if any URL is local
     const hasLocal = urlList.some(isLocalURL);
     const localIdentifier = crypto.randomUUID();
@@ -36,7 +39,7 @@ export class AccessibilityScanner {
     const BS_LOCAL_DOMAIN = "bs-local.com";
 
     if (hasLocal) {
-      await ensureLocalBinarySetup(localIdentifier);
+      await ensureLocalBinarySetup(this.auth?.username, this.auth?.password, localIdentifier);
     } else {
       await killExistingBrowserStackLocalProcesses();
     }
diff --git a/src/tools/appautomate-utils/appautomate.ts b/src/tools/appautomate-utils/appautomate.ts
index a52e622..608dbdf 100644
--- a/src/tools/appautomate-utils/appautomate.ts
+++ b/src/tools/appautomate-utils/appautomate.ts
@@ -1,6 +1,5 @@
 import fs from "fs";
 import axios from "axios";
-import config from "../../config.js";
 import FormData from "form-data";
 import { customFuzzySearch } from "../../lib/fuzzy.js";
 
@@ -124,7 +123,7 @@ export function validateArgs(args: {
 /**
  * Uploads an application file to AppAutomate and returns the app URL
  */
-export async function uploadApp(appPath: string): Promise<string> {
+export async function uploadApp(appPath: string,username: string, password:string): Promise<string> {
   const filePath = appPath;
 
   if (!fs.existsSync(filePath)) {
@@ -133,7 +132,7 @@ export async function uploadApp(appPath: string): Promise<string> {
 
   const formData = new FormData();
   formData.append("file", fs.createReadStream(filePath));
-
+  
   const response = await axios.post<UploadResponse>(
     "https://api-cloud.browserstack.com/app-automate/upload",
     formData,
@@ -142,8 +141,8 @@ export async function uploadApp(appPath: string): Promise<string> {
         ...formData.getHeaders(),
       },
       auth: {
-        username: config.browserstackUsername,
-        password: config.browserstackAccessKey,
+        username,
+        password
       },
     },
   );
diff --git a/src/tools/appautomate.ts b/src/tools/appautomate.ts
index a265e96..37f17a7 100644
--- a/src/tools/appautomate.ts
+++ b/src/tools/appautomate.ts
@@ -2,7 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import logger from "../logger.js";
-import config from "../config.js";
+import { getBrowserStackAuth } from "../lib/get-auth.js";
 import { trackMCP } from "../lib/instrumentation.js";
 import { maybeCompressBase64 } from "../lib/utils.js";
 import { remote } from "webdriverio";
@@ -47,11 +47,12 @@ async function takeAppScreenshot(args: {
   desiredPlatformVersion: string;
   appPath: string;
   desiredPhone: string;
+  server: any;
 }): Promise<CallToolResult> {
   let driver;
   try {
     validateArgs(args);
-    const { desiredPlatform, desiredPhone, appPath } = args;
+    const { desiredPlatform, desiredPhone, appPath, server } = args;
     let { desiredPlatformVersion } = args;
 
     const platforms = (
@@ -86,8 +87,10 @@ async function takeAppScreenshot(args: {
         `Device "${desiredPhone}" with version ${desiredPlatformVersion} not found.`,
       );
     }
+    const authString = getBrowserStackAuth(server);
+    const [username, password] = authString.split(":");
 
-    const app_url = await uploadApp(appPath);
+    const app_url = await uploadApp(appPath, username, password);
     logger.info(`App uploaded. URL: ${app_url}`);
 
     const capabilities = {
@@ -97,8 +100,8 @@ async function takeAppScreenshot(args: {
       "appium:app": app_url,
       "appium:autoGrantPermissions": true,
       "bstack:options": {
-        userName: config.browserstackUsername,
-        accessKey: config.browserstackAccessKey,
+        userName: username,
+        accessKey: password,
         appiumVersion: "2.0.1",
       },
     };
@@ -173,7 +176,7 @@ export default function addAppAutomationTools(server: McpServer) {
     async (args) => {
       try {
         trackMCP("takeAppScreenshot", server.server.getClientVersion()!);
-        return await takeAppScreenshot(args);
+        return await takeAppScreenshot({ ...args, server });
       } catch (error) {
         trackMCP("takeAppScreenshot", server.server.getClientVersion()!, error);
         const errorMessage =
diff --git a/src/tools/applive-utils/start-session.ts b/src/tools/applive-utils/start-session.ts
index 5ba4b5f..81409fc 100644
--- a/src/tools/applive-utils/start-session.ts
+++ b/src/tools/applive-utils/start-session.ts
@@ -6,6 +6,7 @@ import {
 } from "../../lib/device-cache.js";
 import { sanitizeUrlParam } from "../../lib/utils.js";
 import { uploadApp } from "./upload-app.js";
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { findDeviceByName } from "./device-search.js";
 import { pickVersion } from "./version-utils.js";
 import { DeviceEntry } from "./types.js";
@@ -17,12 +18,17 @@ interface StartSessionArgs {
   desiredPlatformVersion: string;
 }
 
+interface StartSessionOptions {
+  server: any;
+}
+
 /**
  * Start an App Live session: filter, select, upload, and open.
  */
-export async function startSession(args: StartSessionArgs): Promise<string> {
+export async function startSession(args: StartSessionArgs, options: StartSessionOptions): Promise<string> {
   const { appPath, desiredPlatform, desiredPhone, desiredPlatformVersion } =
     args;
+  const { server } = options;
 
   // 1) Fetch devices for APP_LIVE
   const data = await getDevicesAndBrowsers(BrowserStackProducts.APP_LIVE);
@@ -61,7 +67,9 @@ export async function startSession(args: StartSessionArgs): Promise<string> {
   }
 
   // 6) Upload app
-  const { app_url } = await uploadApp(appPath);
+  const authString = getBrowserStackAuth(server);
+  const [username, password] = authString.split(":");
+  const { app_url } = await uploadApp(appPath, username, password);
   logger.info(`App uploaded: ${app_url}`);
 
   if (!app_url) {
diff --git a/src/tools/applive-utils/upload-app.ts b/src/tools/applive-utils/upload-app.ts
index a6dc756..ce03d23 100644
--- a/src/tools/applive-utils/upload-app.ts
+++ b/src/tools/applive-utils/upload-app.ts
@@ -1,13 +1,12 @@
 import axios, { AxiosError } from "axios";
 import FormData from "form-data";
 import fs from "fs";
-import config from "../../config.js";
 
 interface UploadResponse {
   app_url: string;
 }
 
-export async function uploadApp(filePath: string): Promise<UploadResponse> {
+export async function uploadApp(filePath: string, username: string, password: string): Promise<UploadResponse> {
   if (!fs.existsSync(filePath)) {
     throw new Error(`File not found at path: ${filePath}`);
   }
@@ -24,8 +23,8 @@ export async function uploadApp(filePath: string): Promise<UploadResponse> {
           ...formData.getHeaders(),
         },
         auth: {
-          username: config.browserstackUsername,
-          password: config.browserstackAccessKey,
+          username,
+          password,
         },
       },
     );
diff --git a/src/tools/applive.ts b/src/tools/applive.ts
index 28312cd..bc13c5b 100644
--- a/src/tools/applive.ts
+++ b/src/tools/applive.ts
@@ -9,12 +9,15 @@ import { trackMCP } from "../lib/instrumentation.js";
 /**
  * Launches an App Live Session on BrowserStack.
  */
-export async function startAppLiveSession(args: {
-  desiredPlatform: string;
-  desiredPlatformVersion: string;
-  appPath: string;
-  desiredPhone: string;
-}): Promise<CallToolResult> {
+export async function startAppLiveSession(
+  args: {
+    desiredPlatform: string;
+    desiredPlatformVersion: string;
+    appPath: string;
+    desiredPhone: string;
+  },
+  server: any
+): Promise<CallToolResult> {
   if (!args.desiredPlatform) {
     throw new Error("You must provide a desiredPlatform.");
   }
@@ -46,12 +49,15 @@ export async function startAppLiveSession(args: {
     throw new Error("The app path does not exist or is not readable.");
   }
 
-  const launchUrl = await startSession({
-    appPath: args.appPath,
-    desiredPlatform: args.desiredPlatform as "android" | "ios",
-    desiredPhone: args.desiredPhone,
-    desiredPlatformVersion: args.desiredPlatformVersion,
-  });
+  const launchUrl = await startSession(
+    {
+      appPath: args.appPath,
+      desiredPlatform: args.desiredPlatform as "android" | "ios",
+      desiredPhone: args.desiredPhone,
+      desiredPlatformVersion: args.desiredPlatformVersion,
+    },
+    { server }
+  );
 
   return {
     content: [
@@ -92,7 +98,7 @@ export default function addAppLiveTools(server: McpServer) {
     async (args) => {
       try {
         trackMCP("runAppLiveSession", server.server.getClientVersion()!);
-        return await startAppLiveSession(args);
+        return await startAppLiveSession(args, server);
       } catch (error) {
         logger.error("App live session failed: %s", error);
         trackMCP("runAppLiveSession", server.server.getClientVersion()!, error);
diff --git a/src/tools/automate-utils/fetch-screenshots.ts b/src/tools/automate-utils/fetch-screenshots.ts
index eabbc7a..bf8cdf7 100644
--- a/src/tools/automate-utils/fetch-screenshots.ts
+++ b/src/tools/automate-utils/fetch-screenshots.ts
@@ -1,14 +1,16 @@
-import config from "../../config.js";
 import { assertOkResponse, maybeCompressBase64 } from "../../lib/utils.js";
 import { SessionType } from "../../lib/constants.js";
 
 //Extracts screenshot URLs from BrowserStack session logs
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
+
 async function extractScreenshotUrls(
   sessionId: string,
   sessionType: SessionType,
+  server: any,
 ): Promise<string[]> {
-  const credentials = `${config.browserstackUsername}:${config.browserstackAccessKey}`;
-  const auth = Buffer.from(credentials).toString("base64");
+  const authString = getBrowserStackAuth(server);
+  const auth = Buffer.from(authString).toString("base64");
 
   const baseUrl = `https://api.browserstack.com/${sessionType === SessionType.Automate ? "automate" : "app-automate"}`;
 
@@ -73,8 +75,9 @@ async function convertUrlsToBase64(
 export async function fetchAutomationScreenshots(
   sessionId: string,
   sessionType: SessionType = SessionType.Automate,
+  server: any,
 ) {
-  const urls = await extractScreenshotUrls(sessionId, sessionType);
+  const urls = await extractScreenshotUrls(sessionId, sessionType, server);
   if (urls.length === 0) {
     return [];
   }
diff --git a/src/tools/automate.ts b/src/tools/automate.ts
index b0f1f73..a18ac14 100644
--- a/src/tools/automate.ts
+++ b/src/tools/automate.ts
@@ -7,14 +7,18 @@ import { trackMCP } from "../lib/instrumentation.js";
 import logger from "../logger.js";
 
 // Tool function that fetches and processes screenshots from BrowserStack Automate session
-export async function fetchAutomationScreenshotsTool(args: {
-  sessionId: string;
-  sessionType: SessionType;
-}): Promise<CallToolResult> {
+export async function fetchAutomationScreenshotsTool(
+  args: {
+    sessionId: string;
+    sessionType: SessionType;
+  },
+  server: any
+): Promise<CallToolResult> {
   try {
     const screenshots = await fetchAutomationScreenshots(
       args.sessionId,
       args.sessionType,
+      server,
     );
 
     if (screenshots.length === 0) {
@@ -71,7 +75,7 @@ export default function addAutomationTools(server: McpServer) {
           "fetchAutomationScreenshots",
           server.server.getClientVersion()!,
         );
-        return await fetchAutomationScreenshotsTool(args);
+        return await fetchAutomationScreenshotsTool(args, server);
       } catch (error) {
         trackMCP(
           "fetchAutomationScreenshots",
diff --git a/src/tools/failurelogs-utils/app-automate.ts b/src/tools/failurelogs-utils/app-automate.ts
index 7aff74a..fb1becf 100644
--- a/src/tools/failurelogs-utils/app-automate.ts
+++ b/src/tools/failurelogs-utils/app-automate.ts
@@ -1,16 +1,15 @@
-import config from "../../config.js";
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { filterLinesByKeywords, validateLogResponse } from "./utils.js";
 
-const auth = Buffer.from(
-  `${config.browserstackUsername}:${config.browserstackAccessKey}`,
-).toString("base64");
-
 // DEVICE LOGS
 export async function retrieveDeviceLogs(
   sessionId: string,
   buildId: string,
+  server: any,
 ): Promise<string> {
   const url = `https://api.browserstack.com/app-automate/builds/${buildId}/sessions/${sessionId}/deviceLogs`;
+  const authString = getBrowserStackAuth(server);
+  const auth = Buffer.from(authString).toString("base64");
 
   const response = await fetch(url, {
     headers: {
@@ -33,8 +32,11 @@ export async function retrieveDeviceLogs(
 export async function retrieveAppiumLogs(
   sessionId: string,
   buildId: string,
+  server: any,
 ): Promise<string> {
   const url = `https://api.browserstack.com/app-automate/builds/${buildId}/sessions/${sessionId}/appiumlogs`;
+  const authString = getBrowserStackAuth(server);
+  const auth = Buffer.from(authString).toString("base64");
 
   const response = await fetch(url, {
     headers: {
@@ -57,8 +59,11 @@ export async function retrieveAppiumLogs(
 export async function retrieveCrashLogs(
   sessionId: string,
   buildId: string,
+  server: any,
 ): Promise<string> {
   const url = `https://api.browserstack.com/app-automate/builds/${buildId}/sessions/${sessionId}/crashlogs`;
+  const authString = getBrowserStackAuth(server);
+  const auth = Buffer.from(authString).toString("base64");
 
   const response = await fetch(url, {
     headers: {
diff --git a/src/tools/failurelogs-utils/automate.ts b/src/tools/failurelogs-utils/automate.ts
index e8b5e95..00a58f3 100644
--- a/src/tools/failurelogs-utils/automate.ts
+++ b/src/tools/failurelogs-utils/automate.ts
@@ -1,4 +1,4 @@
-import config from "../../config.js";
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import {
   HarEntry,
   HarFile,
@@ -6,15 +6,14 @@ import {
   validateLogResponse,
 } from "./utils.js";
 
-const auth = Buffer.from(
-  `${config.browserstackUsername}:${config.browserstackAccessKey}`,
-).toString("base64");
-
 // NETWORK LOGS
 export async function retrieveNetworkFailures(
   sessionId: string,
+  server: any,
 ): Promise<string> {
   const url = `https://api.browserstack.com/automate/sessions/${sessionId}/networklogs`;
+  const authString = getBrowserStackAuth(server);
+  const auth = Buffer.from(authString).toString("base64");
 
   const response = await fetch(url, {
     method: "GET",
@@ -61,8 +60,11 @@ export async function retrieveNetworkFailures(
 // SESSION LOGS
 export async function retrieveSessionFailures(
   sessionId: string,
+  server: any,
 ): Promise<string> {
   const url = `https://api.browserstack.com/automate/sessions/${sessionId}/logs`;
+  const authString = getBrowserStackAuth(server);
+  const auth = Buffer.from(authString).toString("base64");
 
   const response = await fetch(url, {
     headers: {
@@ -84,8 +86,11 @@ export async function retrieveSessionFailures(
 // CONSOLE LOGS
 export async function retrieveConsoleFailures(
   sessionId: string,
+  server: any,
 ): Promise<string> {
   const url = `https://api.browserstack.com/automate/sessions/${sessionId}/consolelogs`;
+  const authString = getBrowserStackAuth(server);
+  const auth = Buffer.from(authString).toString("base64");
 
   const response = await fetch(url, {
     headers: {
diff --git a/src/tools/getFailureLogs.ts b/src/tools/getFailureLogs.ts
index e6e8b7d..c9f6ec3 100644
--- a/src/tools/getFailureLogs.ts
+++ b/src/tools/getFailureLogs.ts
@@ -26,12 +26,15 @@ type LogType = AutomateLogType | AppAutomateLogType;
 type SessionTypeValues = SessionType;
 
 // Main log fetcher function
-export async function getFailureLogs(args: {
-  sessionId: string;
-  buildId?: string;
-  logTypes: LogType[];
-  sessionType: SessionTypeValues;
-}): Promise<CallToolResult> {
+export async function getFailureLogs(
+  args: {
+    sessionId: string;
+    buildId?: string;
+    logTypes: LogType[];
+    sessionType: SessionTypeValues;
+  },
+  server: any
+): Promise<CallToolResult> {
   const results: CallToolResult["content"] = [];
   const errors: string[] = [];
   let validLogTypes: LogType[] = [];
@@ -97,37 +100,37 @@ export async function getFailureLogs(args: {
     for (const logType of validLogTypes) {
       switch (logType) {
         case AutomateLogType.NetworkLogs: {
-          response = await retrieveNetworkFailures(args.sessionId);
+          response = await retrieveNetworkFailures(args.sessionId, server);
           results.push({ type: "text", text: response });
           break;
         }
 
         case AutomateLogType.SessionLogs: {
-          response = await retrieveSessionFailures(args.sessionId);
+          response = await retrieveSessionFailures(args.sessionId, server);
           results.push({ type: "text", text: response });
           break;
         }
 
         case AutomateLogType.ConsoleLogs: {
-          response = await retrieveConsoleFailures(args.sessionId);
+          response = await retrieveConsoleFailures(args.sessionId, server);
           results.push({ type: "text", text: response });
           break;
         }
 
         case AppAutomateLogType.DeviceLogs: {
-          response = await retrieveDeviceLogs(args.sessionId, args.buildId!);
+          response = await retrieveDeviceLogs(args.sessionId, args.buildId!, server);
           results.push({ type: "text", text: response });
           break;
         }
 
         case AppAutomateLogType.AppiumLogs: {
-          response = await retrieveAppiumLogs(args.sessionId, args.buildId!);
+          response = await retrieveAppiumLogs(args.sessionId, args.buildId!, server);
           results.push({ type: "text", text: response });
           break;
         }
 
         case AppAutomateLogType.CrashLogs: {
-          response = await retrieveCrashLogs(args.sessionId, args.buildId!);
+          response = await retrieveCrashLogs(args.sessionId, args.buildId!, server);
           results.push({ type: "text", text: response });
           break;
         }
@@ -186,7 +189,7 @@ export default function registerGetFailureLogs(server: McpServer) {
     async (args) => {
       try {
         trackMCP("getFailureLogs", server.server.getClientVersion()!);
-        return await getFailureLogs(args);
+        return await getFailureLogs(args, server);
       } catch (error) {
         const message = error instanceof Error ? error.message : String(error);
         trackMCP("getFailureLogs", server.server.getClientVersion()!, error);
diff --git a/src/tools/live-utils/start-session.ts b/src/tools/live-utils/start-session.ts
index 1efb776..e2599ad 100644
--- a/src/tools/live-utils/start-session.ts
+++ b/src/tools/live-utils/start-session.ts
@@ -15,13 +15,14 @@ import {
   killExistingBrowserStackLocalProcesses,
 } from "../../lib/local.js";
 
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
 /**
  * Prepares local tunnel setup based on URL type
  */
-async function prepareLocalTunnel(url: string): Promise<boolean> {
+async function prepareLocalTunnel(url: string, username: string, password: string): Promise<boolean> {
   const isLocal = isLocalURL(url);
   if (isLocal) {
-    await ensureLocalBinarySetup();
+    await ensureLocalBinarySetup(username, password);
   } else {
     await killExistingBrowserStackLocalProcesses();
   }
@@ -33,13 +34,18 @@ async function prepareLocalTunnel(url: string): Promise<boolean> {
  */
 export async function startBrowserSession(
   args: DesktopSearchArgs | MobileSearchArgs,
+  server: any
 ): Promise<string> {
   const entry =
     args.platformType === PlatformType.DESKTOP
       ? await filterDesktop(args as DesktopSearchArgs)
       : await filterMobile(args as MobileSearchArgs);
 
-  const isLocal = await prepareLocalTunnel(args.url);
+  // Get credentials from server
+  const authString = getBrowserStackAuth(server);
+  const [username, password] = authString.split(":");
+
+  const isLocal = await prepareLocalTunnel(args.url, username, password);
 
   const url =
     args.platformType === PlatformType.DESKTOP
diff --git a/src/tools/live.ts b/src/tools/live.ts
index 02f9eae..f89eb61 100644
--- a/src/tools/live.ts
+++ b/src/tools/live.ts
@@ -41,6 +41,7 @@ const LiveArgsSchema = z.object(LiveArgsShape);
  */
 async function launchDesktopSession(
   args: z.infer<typeof LiveArgsSchema>,
+  server: any
 ): Promise<string> {
   if (!args.desiredBrowser)
     throw new Error("You must provide a desiredBrowser");
@@ -54,7 +55,7 @@ async function launchDesktopSession(
     osVersion: args.desiredOSVersion,
     browser: args.desiredBrowser,
     browserVersion: args.desiredBrowserVersion,
-  });
+  }, server);
 }
 
 /**
@@ -62,6 +63,7 @@ async function launchDesktopSession(
  */
 async function launchMobileSession(
   args: z.infer<typeof LiveArgsSchema>,
+  server: any
 ): Promise<string> {
   if (!args.desiredDevice) throw new Error("You must provide a desiredDevice");
 
@@ -72,21 +74,21 @@ async function launchMobileSession(
     os: args.desiredOS,
     osVersion: args.desiredOSVersion,
     device: args.desiredDevice,
-  });
+  }, server);
 }
 
 /**
  * Handles the core logic for running a browser session
  */
-async function runBrowserSession(rawArgs: any) {
+async function runBrowserSession(rawArgs: any, server: any) {
   // Validate and narrow
   const args = LiveArgsSchema.parse(rawArgs);
 
   // Branch desktop vs mobile and delegate
   const launchUrl =
     args.platformType === PlatformType.DESKTOP
-      ? await launchDesktopSession(args)
-      : await launchMobileSession(args);
+      ? await launchDesktopSession(args, server)
+      : await launchMobileSession(args, server);
 
   return {
     content: [
@@ -106,7 +108,7 @@ export default function addBrowserLiveTools(server: McpServer) {
     async (args) => {
       try {
         trackMCP("runBrowserLiveSession", server.server.getClientVersion()!);
-        return await runBrowserSession(args);
+        return await runBrowserSession(args, server);
       } catch (error) {
         logger.error("Live session failed: %s", error);
         trackMCP(
diff --git a/src/tools/observability.ts b/src/tools/observability.ts
index b5ad127..9386dd4 100644
--- a/src/tools/observability.ts
+++ b/src/tools/observability.ts
@@ -8,8 +8,9 @@ import logger from "../logger.js";
 export async function getFailuresInLastRun(
   buildName: string,
   projectName: string,
+  server:any
 ): Promise<CallToolResult> {
-  const buildsData = await getLatestO11YBuildInfo(buildName, projectName);
+  const buildsData = await getLatestO11YBuildInfo(buildName, projectName, server);
 
   const observabilityUrl = buildsData.observability_url;
   if (!observabilityUrl) {
@@ -60,7 +61,7 @@ export default function addObservabilityTools(server: McpServer) {
     async (args) => {
       try {
         trackMCP("getFailuresInLastRun", server.server.getClientVersion()!);
-        return await getFailuresInLastRun(args.buildName, args.projectName);
+        return await getFailuresInLastRun(args.buildName, args.projectName, server);
       } catch (error) {
         logger.error("Failed to get failures in the last run: %s", error);
         trackMCP(
diff --git a/src/tools/selfheal-utils/selfheal.ts b/src/tools/selfheal-utils/selfheal.ts
index f9b837b..858bc38 100644
--- a/src/tools/selfheal-utils/selfheal.ts
+++ b/src/tools/selfheal-utils/selfheal.ts
@@ -1,5 +1,4 @@
 import { assertOkResponse } from "../../lib/utils.js";
-import config from "../../config.js";
 
 interface SelectorMapping {
   originalSelector: string;
@@ -10,9 +9,11 @@ interface SelectorMapping {
   };
 }
 
-export async function getSelfHealSelectors(sessionId: string) {
-  const credentials = `${config.browserstackUsername}:${config.browserstackAccessKey}`;
-  const auth = Buffer.from(credentials).toString("base64");
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
+
+export async function getSelfHealSelectors(sessionId: string, server: any) {
+  const authString = getBrowserStackAuth(server);
+  const auth = Buffer.from(authString).toString("base64");
   const url = `https://api.browserstack.com/automate/sessions/${sessionId}/logs`;
 
   const response = await fetch(url, {
diff --git a/src/tools/selfheal.ts b/src/tools/selfheal.ts
index 83cd568..8cd4486 100644
--- a/src/tools/selfheal.ts
+++ b/src/tools/selfheal.ts
@@ -6,11 +6,12 @@ import logger from "../logger.js";
 import { trackMCP } from "../lib/instrumentation.js";
 
 // Tool function that fetches self-healing selectors
-export async function fetchSelfHealSelectorTool(args: {
-  sessionId: string;
-}): Promise<CallToolResult> {
+export async function fetchSelfHealSelectorTool(
+  args: { sessionId: string },
+  server: any
+): Promise<CallToolResult> {
   try {
-    const selectors = await getSelfHealSelectors(args.sessionId);
+    const selectors = await getSelfHealSelectors(args.sessionId, server);
     return {
       content: [
         {
@@ -38,7 +39,7 @@ export default function addSelfHealTools(server: McpServer) {
     async (args) => {
       try {
         trackMCP("fetchSelfHealedSelectors", server.server.getClientVersion()!);
-        return await fetchSelfHealSelectorTool(args);
+        return await fetchSelfHealSelectorTool(args, server);
       } catch (error) {
         trackMCP(
           "fetchSelfHealedSelectors",
diff --git a/src/tools/testmanagement-utils/TCG-utils/api.ts b/src/tools/testmanagement-utils/TCG-utils/api.ts
index 8fb836d..71adf97 100644
--- a/src/tools/testmanagement-utils/TCG-utils/api.ts
+++ b/src/tools/testmanagement-utils/TCG-utils/api.ts
@@ -12,17 +12,18 @@ import {
   CreateTestCasesFromFileArgs,
 } from "./types.js";
 import { createTestCasePayload } from "./helpers.js";
-import config from "../../../config.js";
+import { getBrowserStackAuth } from "../../../lib/get-auth.js";
 
 /**
  * Fetch default and custom form fields for a project.
  */
 export async function fetchFormFields(
   projectId: string,
+  server: any
 ): Promise<{ default_fields: any; custom_fields: any }> {
   const res = await axios.get(FORM_FIELDS_URL(projectId), {
     headers: {
-      "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
+      "API-TOKEN": getBrowserStackAuth(server),
     },
   });
   return res.data;
@@ -37,6 +38,7 @@ export async function triggerTestCaseGeneration(
   folderId: string,
   projectId: string,
   source: string,
+  server: any
 ): Promise<string> {
   const res = await axios.post(
     TCG_TRIGGER_URL,
@@ -50,7 +52,7 @@ export async function triggerTestCaseGeneration(
     },
     {
       headers: {
-        "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
+        "API-TOKEN": getBrowserStackAuth(server),
         "Content-Type": "application/json",
         "request-source": source,
       },
@@ -71,6 +73,7 @@ export async function fetchTestCaseDetails(
   projectId: string,
   testCaseIds: string[],
   source: string,
+  server: any,
 ): Promise<string> {
   if (testCaseIds.length === 0) {
     throw new Error("No testCaseIds provided to fetchTestCaseDetails");
@@ -85,7 +88,7 @@ export async function fetchTestCaseDetails(
     },
     {
       headers: {
-        "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
+        "API-TOKEN": getBrowserStackAuth(server),
         "request-source": source,
         "Content-Type": "application/json",
       },
@@ -102,6 +105,7 @@ export async function fetchTestCaseDetails(
  */
 export async function pollTestCaseDetails(
   traceRequestId: string,
+  server: any,
 ): Promise<Record<string, any>> {
   const detailMap: Record<string, any> = {};
   let done = false;
@@ -117,7 +121,7 @@ export async function pollTestCaseDetails(
       {},
       {
         headers: {
-          "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
+          "API-TOKEN": getBrowserStackAuth(server),
         },
       },
     );
@@ -153,6 +157,7 @@ export async function pollScenariosTestDetails(
   context: any,
   documentId: number,
   source: string,
+  server: any,
 ): Promise<Record<string, Scenario>> {
   const { folderId, projectReferenceId } = args;
   const scenariosMap: Record<string, Scenario> = {};
@@ -168,7 +173,7 @@ export async function pollScenariosTestDetails(
           {},
           {
             headers: {
-              "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
+              "API-TOKEN": getBrowserStackAuth(server),
             },
           },
         );
@@ -212,8 +217,9 @@ export async function pollScenariosTestDetails(
                 projectReferenceId,
                 ids,
                 source,
+                server,
               );
-              detailPromises.push(pollTestCaseDetails(reqId));
+              detailPromises.push(pollTestCaseDetails(reqId, server));
 
               scenariosMap[sc.id] ||= {
                 id: sc.id,
@@ -275,6 +281,7 @@ export async function bulkCreateTestCases(
   traceId: string,
   context: any,
   documentId: number,
+  server: any,
 ): Promise<string> {
   const results: Record<string, any> = {};
   const total = Object.keys(scenariosMap).length;
@@ -305,7 +312,7 @@ export async function bulkCreateTestCases(
         payload,
         {
           headers: {
-            "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
+            "API-TOKEN": getBrowserStackAuth(server),
             "Content-Type": "application/json",
           },
         },
@@ -342,12 +349,13 @@ export async function bulkCreateTestCases(
 
 export async function projectIdentifierToId(
   projectId: string,
+  server: any
 ): Promise<string> {
   const url = `https://test-management.browserstack.com/api/v1/projects/?q=${projectId}`;
 
   const response = await axios.get(url, {
     headers: {
-      "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
+      "API-TOKEN": getBrowserStackAuth(server),
       accept: "application/json, text/plain, */*",
     },
   });
@@ -365,12 +373,13 @@ export async function projectIdentifierToId(
 export async function testCaseIdentifierToDetails(
   projectId: string,
   testCaseIdentifier: string,
+  server: any,
 ): Promise<{ testCaseId: string; folderId: string }> {
   const url = `https://test-management.browserstack.com/api/v1/projects/${projectId}/test-cases/search?q[query]=${testCaseIdentifier}`;
 
   const response = await axios.get(url, {
     headers: {
-      "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
+      "API-TOKEN": getBrowserStackAuth(server),
       accept: "application/json, text/plain, */*",
     },
   });
diff --git a/src/tools/testmanagement-utils/add-test-result.ts b/src/tools/testmanagement-utils/add-test-result.ts
index b8be68c..7a75fed 100644
--- a/src/tools/testmanagement-utils/add-test-result.ts
+++ b/src/tools/testmanagement-utils/add-test-result.ts
@@ -1,5 +1,5 @@
 import axios from "axios";
-import config from "../../config.js";
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js";
@@ -33,6 +33,7 @@ export type AddTestResultArgs = z.infer<typeof AddTestResultSchema>;
  */
 export async function addTestResult(
   rawArgs: AddTestResultArgs,
+  server: any
 ): Promise<CallToolResult> {
   try {
     const args = AddTestResultSchema.parse(rawArgs);
@@ -45,10 +46,13 @@ export async function addTestResult(
       test_case_id: args.test_case_id,
     } as any;
 
+    const authString = getBrowserStackAuth(server);
+    const [username, password] = authString.split(":");
+
     const response = await axios.post(url, body, {
       auth: {
-        username: config.browserstackUsername,
-        password: config.browserstackAccessKey,
+        username,
+        password,
       },
       headers: { "Content-Type": "application/json" },
     });
diff --git a/src/tools/testmanagement-utils/create-lca-steps.ts b/src/tools/testmanagement-utils/create-lca-steps.ts
index 4016750..3ad12fc 100644
--- a/src/tools/testmanagement-utils/create-lca-steps.ts
+++ b/src/tools/testmanagement-utils/create-lca-steps.ts
@@ -1,13 +1,13 @@
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import axios from "axios";
-import config from "../../config.js";
 import { formatAxiosError } from "../../lib/error.js";
 import {
   projectIdentifierToId,
   testCaseIdentifierToDetails,
 } from "./TCG-utils/api.js";
 import { pollLCAStatus } from "./poll-lca-status.js";
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
 
 /**
  * Schema for creating LCA steps for a test case
@@ -65,15 +65,17 @@ export type CreateLCAStepsArgs = z.infer<typeof CreateLCAStepsSchema>;
 export async function createLCASteps(
   args: CreateLCAStepsArgs,
   context: any,
+  server: any,
 ): Promise<CallToolResult> {
   try {
     // Get the project ID from identifier
-    const projectId = await projectIdentifierToId(args.project_identifier);
+    const projectId = await projectIdentifierToId(args.project_identifier,server);
 
     // Get the test case ID and folder ID from identifier
     const { testCaseId, folderId } = await testCaseIdentifierToDetails(
       projectId,
       args.test_case_identifier,
+      server,
     );
 
     const url = `https://test-management.browserstack.com/api/v1/projects/${projectId}/test-cases/${testCaseId}/lcnc`;
@@ -90,7 +92,7 @@ export async function createLCASteps(
 
     const response = await axios.post(url, payload, {
       headers: {
-        "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
+        "API-TOKEN": getBrowserStackAuth(server),
         accept: "application/json, text/plain, */*",
         "Content-Type": "application/json",
       },
@@ -125,6 +127,7 @@ export async function createLCASteps(
           maxWaitMs, // max wait time
           2 * 60 * 1000, // 2 minutes initial wait
           10 * 1000, // 10 seconds interval
+          server
         );
 
         if (lcaResult && lcaResult.status === "done") {
diff --git a/src/tools/testmanagement-utils/create-project-folder.ts b/src/tools/testmanagement-utils/create-project-folder.ts
index ef83570..70f52d0 100644
--- a/src/tools/testmanagement-utils/create-project-folder.ts
+++ b/src/tools/testmanagement-utils/create-project-folder.ts
@@ -1,9 +1,9 @@
 import axios from "axios";
-import config from "../../config.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js"; // or correct path
 import { projectIdentifierToId } from "../testmanagement-utils/TCG-utils/api.js";
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
 
 // Schema for combined project/folder creation
 export const CreateProjFoldSchema = z.object({
@@ -37,6 +37,7 @@ type CreateProjFoldArgs = z.infer<typeof CreateProjFoldSchema>;
  */
 export async function createProjectOrFolder(
   args: CreateProjFoldArgs,
+  server: any
 ): Promise<CallToolResult> {
   const {
     project_name,
@@ -53,18 +54,23 @@ export async function createProjectOrFolder(
     );
   }
 
+  const authString = getBrowserStackAuth(server);
+  const [username, password] = authString.split(":");
+
   let projId = project_identifier;
 
   // Step 1: Create project if project_name provided
   if (project_name) {
     try {
+      const authString = getBrowserStackAuth(server);
+      const [username, password] = authString.split(":");
       const res = await axios.post(
         "https://test-management.browserstack.com/api/v2/projects",
         { project: { name: project_name, description: project_description } },
         {
           auth: {
-            username: config.browserstackUsername,
-            password: config.browserstackAccessKey,
+            username,
+            password,
           },
           headers: { "Content-Type": "application/json" },
         },
@@ -100,8 +106,8 @@ export async function createProjectOrFolder(
         },
         {
           auth: {
-            username: config.browserstackUsername,
-            password: config.browserstackAccessKey,
+            username,
+            password,
           },
           headers: { "Content-Type": "application/json" },
         },
@@ -113,7 +119,7 @@ export async function createProjectOrFolder(
       // Folder created successfully
 
       const folder = res.data.folder;
-      const projectId = await projectIdentifierToId(projId);
+      const projectId = await projectIdentifierToId(projId, server);
 
       return {
         content: [
diff --git a/src/tools/testmanagement-utils/create-testcase.ts b/src/tools/testmanagement-utils/create-testcase.ts
index bb9029a..947ee65 100644
--- a/src/tools/testmanagement-utils/create-testcase.ts
+++ b/src/tools/testmanagement-utils/create-testcase.ts
@@ -1,5 +1,4 @@
 import axios from "axios";
-import config from "../../config.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js"; // or correct
@@ -138,10 +137,15 @@ export function sanitizeArgs(args: any) {
   return cleaned;
 }
 
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
+
 export async function createTestCase(
   params: TestCaseCreateRequest,
+  server: any
 ): Promise<CallToolResult> {
   const body = { test_case: params };
+  const authString = getBrowserStackAuth(server);
+  const [username, password] = authString.split(":");
 
   try {
     const response = await axios.post<TestCaseResponse>(
@@ -151,8 +155,8 @@ export async function createTestCase(
       body,
       {
         auth: {
-          username: config.browserstackUsername,
-          password: config.browserstackAccessKey,
+          username,
+          password,
         },
         headers: { "Content-Type": "application/json" },
       },
@@ -175,7 +179,7 @@ export async function createTestCase(
     }
 
     const tc = data.test_case;
-    const projectId = await projectIdentifierToId(params.project_identifier);
+    const projectId = await projectIdentifierToId(params.project_identifier, server);
 
     return {
       content: [
diff --git a/src/tools/testmanagement-utils/create-testrun.ts b/src/tools/testmanagement-utils/create-testrun.ts
index 2b8b4c6..dd3fac7 100644
--- a/src/tools/testmanagement-utils/create-testrun.ts
+++ b/src/tools/testmanagement-utils/create-testrun.ts
@@ -1,8 +1,8 @@
 import axios from "axios";
-import config from "../../config.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js";
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
 
 /**
  * Schema for creating a test run.
@@ -53,6 +53,7 @@ export type CreateTestRunArgs = z.infer<typeof CreateTestRunSchema>;
  */
 export async function createTestRun(
   rawArgs: CreateTestRunArgs,
+  server: any
 ): Promise<CallToolResult> {
   try {
     const inputArgs = {
@@ -68,13 +69,15 @@ export async function createTestRun(
       args.project_identifier,
     )}/test-runs`;
 
+    const authString = getBrowserStackAuth(server);
+    const [username, password] = authString.split(":");
     const response = await axios.post(
       url,
       { test_run: args.test_run },
       {
         auth: {
-          username: config.browserstackUsername,
-          password: config.browserstackAccessKey,
+          username,
+          password,
         },
         headers: { "Content-Type": "application/json" },
       },
diff --git a/src/tools/testmanagement-utils/list-testcases.ts b/src/tools/testmanagement-utils/list-testcases.ts
index f882830..ac385e1 100644
--- a/src/tools/testmanagement-utils/list-testcases.ts
+++ b/src/tools/testmanagement-utils/list-testcases.ts
@@ -1,8 +1,8 @@
 import axios from "axios";
-import config from "../../config.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js";
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
 
 /**
  * Schema for listing test cases with optional filters.
@@ -38,6 +38,7 @@ export type ListTestCasesArgs = z.infer<typeof ListTestCasesSchema>;
  */
 export async function listTestCases(
   args: ListTestCasesArgs,
+  server: any
 ): Promise<CallToolResult> {
   try {
     // Build query string
@@ -51,10 +52,12 @@ export async function listTestCases(
       args.project_identifier,
     )}/test-cases?${params.toString()}`;
 
+    const authString = getBrowserStackAuth(server);
+    const [username, password] = authString.split(":");
     const resp = await axios.get(url, {
       auth: {
-        username: config.browserstackUsername,
-        password: config.browserstackAccessKey,
+        username,
+        password,
       },
     });
 
diff --git a/src/tools/testmanagement-utils/list-testruns.ts b/src/tools/testmanagement-utils/list-testruns.ts
index 5bad0bc..4a215c2 100644
--- a/src/tools/testmanagement-utils/list-testruns.ts
+++ b/src/tools/testmanagement-utils/list-testruns.ts
@@ -1,8 +1,8 @@
 import axios from "axios";
-import config from "../../config.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js";
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
 
 /**
  * Schema for listing test runs with optional filters.
@@ -26,6 +26,7 @@ type ListTestRunsArgs = z.infer<typeof ListTestRunsSchema>;
  */
 export async function listTestRuns(
   args: ListTestRunsArgs,
+  server: any
 ): Promise<CallToolResult> {
   try {
     const params = new URLSearchParams();
@@ -38,10 +39,12 @@ export async function listTestRuns(
         args.project_identifier,
       )}/test-runs?` + params.toString();
 
+    const authString = getBrowserStackAuth(server);
+    const [username, password] = authString.split(":");
     const resp = await axios.get(url, {
       auth: {
-        username: config.browserstackUsername,
-        password: config.browserstackAccessKey,
+        username,
+        password,
       },
     });
 
diff --git a/src/tools/testmanagement-utils/poll-lca-status.ts b/src/tools/testmanagement-utils/poll-lca-status.ts
index cdaf971..b3697d8 100644
--- a/src/tools/testmanagement-utils/poll-lca-status.ts
+++ b/src/tools/testmanagement-utils/poll-lca-status.ts
@@ -1,5 +1,5 @@
 import axios from "axios";
-import config from "../../config.js";
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
 
 /**
  * Interface for the test case response structure
@@ -36,6 +36,7 @@ export async function pollLCAStatus(
   maxWaitTimeMs: number = 10 * 60 * 1000, // 10 minutes default
   initialWaitMs: number = 2 * 60 * 1000, // 2 minutes initial wait
   pollIntervalMs: number = 10 * 1000, // 10 seconds interval
+  server: any,
 ): Promise<{ resource_path: string; status: string } | null> {
   const url = `https://test-management.browserstack.com/api/v1/projects/${projectId}/folder/${folderId}/test-cases/${testCaseId}`;
 
@@ -91,9 +92,10 @@ export async function pollLCAStatus(
     // Set up polling interval
     const intervalId = setInterval(async () => {
       try {
+        const authString = getBrowserStackAuth(server);
         const response = await axios.get<TestCaseResponse>(url, {
           headers: {
-            "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
+            "API-TOKEN": authString,
             accept: "application/json, text/plain, */*",
           },
         });
diff --git a/src/tools/testmanagement-utils/testcase-from-file.ts b/src/tools/testmanagement-utils/testcase-from-file.ts
index 41b1834..aca4823 100644
--- a/src/tools/testmanagement-utils/testcase-from-file.ts
+++ b/src/tools/testmanagement-utils/testcase-from-file.ts
@@ -17,6 +17,7 @@ import { projectIdentifierToId } from "./TCG-utils/api.js";
 export async function createTestCasesFromFile(
   args: CreateTestCasesFromFileArgs,
   context: any,
+  server: any,
 ): Promise<CallToolResult> {
   logger.info(
     `createTestCasesFromFile called with projectId: ${args.projectReferenceId}, folderId: ${args.folderId}`,
@@ -25,10 +26,12 @@ export async function createTestCasesFromFile(
   if (args.projectReferenceId.startsWith("PR-")) {
     args.projectReferenceId = await projectIdentifierToId(
       args.projectReferenceId,
+      server,
     );
   }
   const { default_fields, custom_fields } = await fetchFormFields(
     args.projectReferenceId,
+    server,
   );
   const fieldMaps = buildDefaultFieldMaps(default_fields);
   const booleanFieldId = findBooleanFieldId(custom_fields);
@@ -57,6 +60,7 @@ export async function createTestCasesFromFile(
     args.folderId,
     args.projectReferenceId,
     source,
+    server,
   );
 
   const scenariosMap = await pollScenariosTestDetails(
@@ -65,6 +69,7 @@ export async function createTestCasesFromFile(
     context,
     documentId,
     source,
+    server,
   );
 
   const resultString = await bulkCreateTestCases(
@@ -76,6 +81,7 @@ export async function createTestCasesFromFile(
     traceId,
     context,
     documentId,
+    server,
   );
 
   signedUrlMap.delete(args.documentId);
diff --git a/src/tools/testmanagement-utils/update-testrun.ts b/src/tools/testmanagement-utils/update-testrun.ts
index ed8c8d0..db953de 100644
--- a/src/tools/testmanagement-utils/update-testrun.ts
+++ b/src/tools/testmanagement-utils/update-testrun.ts
@@ -1,5 +1,5 @@
 import axios from "axios";
-import config from "../../config.js";
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js";
@@ -35,6 +35,7 @@ type UpdateTestRunArgs = z.infer<typeof UpdateTestRunSchema>;
  */
 export async function updateTestRun(
   args: UpdateTestRunArgs,
+  server: any
 ): Promise<CallToolResult> {
   try {
     const body = { test_run: args.test_run };
@@ -42,10 +43,13 @@ export async function updateTestRun(
       args.project_identifier,
     )}/test-runs/${encodeURIComponent(args.test_run_id)}/update`;
 
+    const authString = getBrowserStackAuth(server);
+    const [username, password] = authString.split(":");
+
     const resp = await axios.patch(url, body, {
       auth: {
-        username: config.browserstackUsername,
-        password: config.browserstackAccessKey,
+        username,
+        password,
       },
     });
 
diff --git a/src/tools/testmanagement-utils/upload-file.ts b/src/tools/testmanagement-utils/upload-file.ts
index 3f76c2e..73f600b 100644
--- a/src/tools/testmanagement-utils/upload-file.ts
+++ b/src/tools/testmanagement-utils/upload-file.ts
@@ -5,7 +5,7 @@ import FormData from "form-data";
 import fs from "fs";
 import path from "path";
 import { v4 as uuidv4 } from "uuid";
-import config from "../../config.js";
+import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { signedUrlMap } from "../../lib/inmemory-store.js";
 import { projectIdentifierToId } from "./TCG-utils/api.js";
 
@@ -28,6 +28,7 @@ export const UploadFileSchema = z.object({
  */
 export async function uploadFile(
   args: z.infer<typeof UploadFileSchema>,
+  server: any
 ): Promise<CallToolResult> {
   const { project_identifier, file_path } = args;
 
@@ -46,7 +47,7 @@ export async function uploadFile(
       };
     }
     // Get the project ID
-    const projectIdResponse = await projectIdentifierToId(project_identifier);
+    const projectIdResponse = await projectIdentifierToId(project_identifier,server);
 
     const formData = new FormData();
     formData.append("attachments[]", fs.createReadStream(file_path));
@@ -56,7 +57,7 @@ export async function uploadFile(
     const response = await axios.post(uploadUrl, formData, {
       headers: {
         ...formData.getHeaders(),
-        "API-TOKEN": `${config.browserstackUsername}:${config.browserstackAccessKey}`,
+        "API-TOKEN": getBrowserStackAuth(server),
         accept: "application/json, text/plain, */*",
       },
     });
diff --git a/src/tools/testmanagement.ts b/src/tools/testmanagement.ts
index 8c72c22..428ddcc 100644
--- a/src/tools/testmanagement.ts
+++ b/src/tools/testmanagement.ts
@@ -14,7 +14,6 @@ import {
   CreateTestCaseSchema,
 } from "./testmanagement-utils/create-testcase.js";
 
-let serverInstance: McpServer;
 
 import {
   listTestCases,
@@ -61,18 +60,19 @@ import {
  */
 export async function createProjectOrFolderTool(
   args: z.infer<typeof CreateProjFoldSchema>,
+  server: McpServer
 ): Promise<CallToolResult> {
   try {
     trackMCP(
       "createProjectOrFolder",
-      serverInstance.server.getClientVersion()!,
+      server.server.getClientVersion()!,
     );
-    return await createProjectOrFolder(args);
+    return await createProjectOrFolder(args,server);
   } catch (err) {
     logger.error("Failed to create project/folder: %s", err);
     trackMCP(
       "createProjectOrFolder",
-      serverInstance.server.getClientVersion()!,
+      server.server.getClientVersion()!,
       err,
     );
     return {
@@ -95,15 +95,16 @@ export async function createProjectOrFolderTool(
  */
 export async function createTestCaseTool(
   args: TestCaseCreateRequest,
+  server: McpServer
 ): Promise<CallToolResult> {
   // Sanitize input arguments
   const cleanedArgs = sanitizeArgs(args);
   try {
-    trackMCP("createTestCase", serverInstance.server.getClientVersion()!);
-    return await createTestCaseAPI(cleanedArgs);
+    trackMCP("createTestCase", server.server.getClientVersion()!);
+    return await createTestCaseAPI(cleanedArgs, server);
   } catch (err) {
     logger.error("Failed to create test case: %s", err);
-    trackMCP("createTestCase", serverInstance.server.getClientVersion()!, err);
+    trackMCP("createTestCase", server.server.getClientVersion()!, err);
     return {
       content: [
         {
@@ -125,12 +126,13 @@ export async function createTestCaseTool(
 
 export async function listTestCasesTool(
   args: z.infer<typeof ListTestCasesSchema>,
+  server: McpServer
 ): Promise<CallToolResult> {
   try {
-    trackMCP("listTestCases", serverInstance.server.getClientVersion()!);
-    return await listTestCases(args);
+    trackMCP("listTestCases", server.server.getClientVersion()!);
+    return await listTestCases(args, server);
   } catch (err) {
-    trackMCP("listTestCases", serverInstance.server.getClientVersion()!, err);
+    trackMCP("listTestCases", server.server.getClientVersion()!, err);
     return {
       content: [
         {
@@ -151,12 +153,13 @@ export async function listTestCasesTool(
  */
 export async function createTestRunTool(
   args: z.infer<typeof CreateTestRunSchema>,
+  server: McpServer
 ): Promise<CallToolResult> {
   try {
-    trackMCP("createTestRun", serverInstance.server.getClientVersion()!);
-    return await createTestRun(args);
+    trackMCP("createTestRun", server.server.getClientVersion()!);
+    return await createTestRun(args, server);
   } catch (err) {
-    trackMCP("createTestRun", serverInstance.server.getClientVersion()!, err);
+    trackMCP("createTestRun", server.server.getClientVersion()!, err);
     return {
       content: [
         {
@@ -177,12 +180,13 @@ export async function createTestRunTool(
  */
 export async function listTestRunsTool(
   args: z.infer<typeof ListTestRunsSchema>,
+  server: McpServer
 ): Promise<CallToolResult> {
   try {
-    trackMCP("listTestRuns", serverInstance.server.getClientVersion()!);
-    return await listTestRuns(args);
+    trackMCP("listTestRuns", server.server.getClientVersion()!);
+    return await listTestRuns(args, server);
   } catch (err) {
-    trackMCP("listTestRuns", serverInstance.server.getClientVersion()!, err);
+    trackMCP("listTestRuns", server.server.getClientVersion()!, err);
     return {
       content: [
         {
@@ -205,12 +209,13 @@ export async function listTestRunsTool(
  */
 export async function updateTestRunTool(
   args: z.infer<typeof UpdateTestRunSchema>,
+  server: McpServer
 ): Promise<CallToolResult> {
   try {
-    trackMCP("updateTestRun", serverInstance.server.getClientVersion()!);
-    return await updateTestRun(args);
+    trackMCP("updateTestRun", server.server.getClientVersion()!);
+    return await updateTestRun(args, server);
   } catch (err) {
-    trackMCP("updateTestRun", serverInstance.server.getClientVersion()!, err);
+    trackMCP("updateTestRun", server.server.getClientVersion()!, err);
     return {
       content: [
         {
@@ -231,12 +236,13 @@ export async function updateTestRunTool(
  */
 export async function addTestResultTool(
   args: z.infer<typeof AddTestResultSchema>,
+  server: McpServer
 ): Promise<CallToolResult> {
   try {
-    trackMCP("addTestResult", serverInstance.server.getClientVersion()!);
-    return await addTestResult(args);
+    trackMCP("addTestResult", server.server.getClientVersion()!);
+    return await addTestResult(args, server);
   } catch (err) {
-    trackMCP("addTestResult", serverInstance.server.getClientVersion()!, err);
+    trackMCP("addTestResult", server.server.getClientVersion()!, err);
     return {
       content: [
         {
@@ -257,17 +263,18 @@ export async function addTestResultTool(
  */
 export async function uploadProductRequirementFileTool(
   args: z.infer<typeof UploadFileSchema>,
+  server: McpServer
 ): Promise<CallToolResult> {
   try {
     trackMCP(
       "uploadProductRequirementFile",
-      serverInstance.server.getClientVersion()!,
+      server.server.getClientVersion()!,
     );
-    return await uploadFile(args);
+    return await uploadFile(args, server);
   } catch (err) {
     trackMCP(
       "uploadProductRequirementFile",
-      serverInstance.server.getClientVersion()!,
+      server.server.getClientVersion()!,
       err,
     );
     return {
@@ -291,17 +298,18 @@ export async function uploadProductRequirementFileTool(
 export async function createTestCasesFromFileTool(
   args: z.infer<typeof CreateTestCasesFromFileSchema>,
   context: any,
+  server: McpServer
 ): Promise<CallToolResult> {
   try {
     trackMCP(
       "createTestCasesFromFile",
-      serverInstance.server.getClientVersion()!,
+      server.server.getClientVersion()!,
     );
-    return await createTestCasesFromFile(args, context);
+    return await createTestCasesFromFile(args, context,server);
   } catch (err) {
     trackMCP(
       "createTestCasesFromFile",
-      serverInstance.server.getClientVersion()!,
+      server.server.getClientVersion()!,
       err,
     );
     return {
@@ -325,12 +333,13 @@ export async function createTestCasesFromFileTool(
 export async function createLCAStepsTool(
   args: z.infer<typeof CreateLCAStepsSchema>,
   context: any,
+  server: McpServer
 ): Promise<CallToolResult> {
   try {
-    trackMCP("createLCASteps", serverInstance.server.getClientVersion()!);
-    return await createLCASteps(args, context);
+    trackMCP("createLCASteps", server.server.getClientVersion()!);
+    return await createLCASteps(args, context, server);
   } catch (err) {
-    trackMCP("createLCASteps", serverInstance.server.getClientVersion()!, err);
+    trackMCP("createLCASteps", server.server.getClientVersion()!, err);
     return {
       content: [
         {
@@ -350,70 +359,69 @@ export async function createLCAStepsTool(
  * Registers both project/folder and test-case tools.
  */
 export default function addTestManagementTools(server: McpServer) {
-  serverInstance = server;
   server.tool(
     "createProjectOrFolder",
     "Create a project and/or folder in BrowserStack Test Management.",
     CreateProjFoldSchema.shape,
-    createProjectOrFolderTool,
+    (args) => createProjectOrFolderTool(args, server),
   );
 
   server.tool(
     "createTestCase",
     "Use this tool to create a test case in BrowserStack Test Management.",
     CreateTestCaseSchema.shape,
-    createTestCaseTool,
+    (args) => createTestCaseTool(args, server),
   );
 
   server.tool(
     "listTestCases",
     "List test cases in a project with optional filters (status, priority, custom fields, etc.)",
     ListTestCasesSchema.shape,
-    listTestCasesTool,
+    (args) => listTestCasesTool(args, server),
   );
 
   server.tool(
     "createTestRun",
     "Create a test run in BrowserStack Test Management.",
     CreateTestRunSchema.shape,
-    createTestRunTool,
+    (args) => createTestRunTool(args, server),
   );
 
   server.tool(
     "listTestRuns",
     "List test runs in a project with optional filters (date ranges, assignee, state, etc.)",
     ListTestRunsSchema.shape,
-    listTestRunsTool,
+    (args) => listTestRunsTool(args, server),
   );
   server.tool(
     "updateTestRun",
     "Update a test run in BrowserStack Test Management.",
     UpdateTestRunSchema.shape,
-    updateTestRunTool,
+    (args) => updateTestRunTool(args, server),
   );
   server.tool(
     "addTestResult",
     "Add a test result to a specific test run via BrowserStack Test Management API.",
     AddTestResultSchema.shape,
-    addTestResultTool,
+    (args) => addTestResultTool(args, server),
   );
 
   server.tool(
     "uploadProductRequirementFile",
     "Upload files (e.g., PDRs, PDFs) to BrowserStack Test Management and retrieve a file mapping ID. This is utilized for generating test cases from files and is part of the Test Case Generator AI Agent in BrowserStack.",
     UploadFileSchema.shape,
-    uploadProductRequirementFileTool,
+    (args) => uploadProductRequirementFileTool(args, server),
   );
   server.tool(
     "createTestCasesFromFile",
     "Generate test cases from a file in BrowserStack Test Management using the Test Case Generator AI Agent.",
     CreateTestCasesFromFileSchema.shape,
-    createTestCasesFromFileTool,
+    (args, context) => createTestCasesFromFileTool(args, context, server),
   );
   server.tool(
     "createLCASteps",
     "Generate Low Code Automation (LCA) steps for a test case in BrowserStack Test Management using the Low Code Automation Agent.",
     CreateLCAStepsSchema.shape,
-    createLCAStepsTool,
+    (args, context) => createLCAStepsTool(args, context, server),
   );
 }

From 897e8882bef1be4f3994ad889fe9e410d66a5e73 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 1 Jul 2025 12:58:36 +0530
Subject: [PATCH 07/64] Refactor test management utility functions to
 consistently include server parameter

- Updated createTestRun, listTestCases, listTestRuns, updateTestRun, and uploadFile functions to include a server parameter.
- Modified related test cases to pass mock server instances for better test isolation and accuracy.
- Enhanced error handling and logging in test management tools.
- Improved test coverage for various scenarios in test management utilities.
---
 src/lib/api.ts                                |   2 -
 src/oninitialized.ts                          |   1 -
 src/tools/accessibility.ts                    |   9 +-
 .../accessiblity-utils/accessibility-rag.ts   |   5 +-
 src/tools/accessiblity-utils/scanner.ts       |  10 +-
 src/tools/appautomate-utils/appautomate.ts    |  10 +-
 src/tools/applive-utils/start-session.ts      |   5 +-
 src/tools/applive-utils/upload-app.ts         |   6 +-
 src/tools/applive.ts                          |   4 +-
 src/tools/automate.ts                         |   2 +-
 src/tools/bstack-sdk.ts                       |  12 ++
 src/tools/getFailureLogs.ts                   |  20 +-
 src/tools/live-utils/start-session.ts         |   8 +-
 src/tools/live.ts                             |  42 +++--
 src/tools/observability.ts                    |  14 +-
 src/tools/sdk-utils/constants.ts              |  51 ++---
 src/tools/sdk-utils/instructions.ts           |  10 +-
 src/tools/sdk-utils/types.ts                  |   5 +-
 src/tools/selfheal.ts                         |   2 +-
 .../testmanagement-utils/TCG-utils/api.ts     |   6 +-
 .../testmanagement-utils/add-test-result.ts   |   2 +-
 .../testmanagement-utils/create-lca-steps.ts  |   7 +-
 .../create-project-folder.ts                  |   2 +-
 .../testmanagement-utils/create-testcase.ts   |   7 +-
 .../testmanagement-utils/create-testrun.ts    |   2 +-
 .../testmanagement-utils/list-testcases.ts    |   2 +-
 .../testmanagement-utils/list-testruns.ts     |   2 +-
 .../testmanagement-utils/update-testrun.ts    |   2 +-
 src/tools/testmanagement-utils/upload-file.ts |   7 +-
 src/tools/testmanagement.ts                   |  52 ++---
 tests/tools/applive.test.ts                   |  38 ++--
 tests/tools/getFailureLogs.test.ts            |  83 ++++----
 tests/tools/observability.test.ts             |  47 +++--
 tests/tools/testmanagement.test.ts            | 178 +++++++++++-------
 34 files changed, 402 insertions(+), 253 deletions(-)

diff --git a/src/lib/api.ts b/src/lib/api.ts
index faa30ca..f5f6606 100644
--- a/src/lib/api.ts
+++ b/src/lib/api.ts
@@ -1,5 +1,3 @@
-
-
 import { getBrowserStackAuth } from "./get-auth.js";
 
 export async function getLatestO11YBuildInfo(
diff --git a/src/oninitialized.ts b/src/oninitialized.ts
index 6fc5cea..4117c09 100644
--- a/src/oninitialized.ts
+++ b/src/oninitialized.ts
@@ -2,7 +2,6 @@ import { trackMCP } from "./lib/instrumentation.js";
 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 
 export function setupOnInitialized(server: McpServer) {
-
   const nodeVersion = process.versions.node;
 
   // Check for Node.js version
diff --git a/src/tools/accessibility.ts b/src/tools/accessibility.ts
index 88947ad..4bbb9c8 100644
--- a/src/tools/accessibility.ts
+++ b/src/tools/accessibility.ts
@@ -12,7 +12,7 @@ async function runAccessibilityScan(
   name: string,
   pageURL: string,
   context: any,
-  server: any
+  server: any,
 ): Promise<CallToolResult> {
   // Create scanner and set auth on the go
   const scanner = new AccessibilityScanner();
@@ -125,7 +125,12 @@ export default function addAccessibilityTools(server: McpServer) {
     async (args, context) => {
       try {
         trackMCP("startAccessibilityScan", server.server.getClientVersion()!);
-        return await runAccessibilityScan(args.name, args.pageURL, context, server);
+        return await runAccessibilityScan(
+          args.name,
+          args.pageURL,
+          context,
+          server,
+        );
       } catch (error) {
         trackMCP(
           "startAccessibilityScan",
diff --git a/src/tools/accessiblity-utils/accessibility-rag.ts b/src/tools/accessiblity-utils/accessibility-rag.ts
index 6668b11..4f2a399 100644
--- a/src/tools/accessiblity-utils/accessibility-rag.ts
+++ b/src/tools/accessiblity-utils/accessibility-rag.ts
@@ -7,7 +7,10 @@ export interface RAGChunk {
 
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
 
-export async function queryAccessibilityRAG(userQuery: string, server: any): Promise<any> {
+export async function queryAccessibilityRAG(
+  userQuery: string,
+  server: any,
+): Promise<any> {
   const url = "https://accessibility.browserstack.com/api/tcg-proxy/search";
 
   const authString = getBrowserStackAuth(server);
diff --git a/src/tools/accessiblity-utils/scanner.ts b/src/tools/accessiblity-utils/scanner.ts
index 91173f8..6b50776 100644
--- a/src/tools/accessiblity-utils/scanner.ts
+++ b/src/tools/accessiblity-utils/scanner.ts
@@ -30,7 +30,9 @@ export class AccessibilityScanner {
     urlList: string[],
   ): Promise<AccessibilityScanResponse> {
     if (!this.auth?.username || !this.auth?.password) {
-      throw new Error("BrowserStack credentials are not set for AccessibilityScanner.");
+      throw new Error(
+        "BrowserStack credentials are not set for AccessibilityScanner.",
+      );
     }
     // Check if any URL is local
     const hasLocal = urlList.some(isLocalURL);
@@ -39,7 +41,11 @@ export class AccessibilityScanner {
     const BS_LOCAL_DOMAIN = "bs-local.com";
 
     if (hasLocal) {
-      await ensureLocalBinarySetup(this.auth?.username, this.auth?.password, localIdentifier);
+      await ensureLocalBinarySetup(
+        this.auth?.username,
+        this.auth?.password,
+        localIdentifier,
+      );
     } else {
       await killExistingBrowserStackLocalProcesses();
     }
diff --git a/src/tools/appautomate-utils/appautomate.ts b/src/tools/appautomate-utils/appautomate.ts
index 608dbdf..b2684d6 100644
--- a/src/tools/appautomate-utils/appautomate.ts
+++ b/src/tools/appautomate-utils/appautomate.ts
@@ -123,7 +123,11 @@ export function validateArgs(args: {
 /**
  * Uploads an application file to AppAutomate and returns the app URL
  */
-export async function uploadApp(appPath: string,username: string, password:string): Promise<string> {
+export async function uploadApp(
+  appPath: string,
+  username: string,
+  password: string,
+): Promise<string> {
   const filePath = appPath;
 
   if (!fs.existsSync(filePath)) {
@@ -132,7 +136,7 @@ export async function uploadApp(appPath: string,username: string, password:strin
 
   const formData = new FormData();
   formData.append("file", fs.createReadStream(filePath));
-  
+
   const response = await axios.post<UploadResponse>(
     "https://api-cloud.browserstack.com/app-automate/upload",
     formData,
@@ -142,7 +146,7 @@ export async function uploadApp(appPath: string,username: string, password:strin
       },
       auth: {
         username,
-        password
+        password,
       },
     },
   );
diff --git a/src/tools/applive-utils/start-session.ts b/src/tools/applive-utils/start-session.ts
index 81409fc..8d86c30 100644
--- a/src/tools/applive-utils/start-session.ts
+++ b/src/tools/applive-utils/start-session.ts
@@ -25,7 +25,10 @@ interface StartSessionOptions {
 /**
  * Start an App Live session: filter, select, upload, and open.
  */
-export async function startSession(args: StartSessionArgs, options: StartSessionOptions): Promise<string> {
+export async function startSession(
+  args: StartSessionArgs,
+  options: StartSessionOptions,
+): Promise<string> {
   const { appPath, desiredPlatform, desiredPhone, desiredPlatformVersion } =
     args;
   const { server } = options;
diff --git a/src/tools/applive-utils/upload-app.ts b/src/tools/applive-utils/upload-app.ts
index ce03d23..b80c433 100644
--- a/src/tools/applive-utils/upload-app.ts
+++ b/src/tools/applive-utils/upload-app.ts
@@ -6,7 +6,11 @@ interface UploadResponse {
   app_url: string;
 }
 
-export async function uploadApp(filePath: string, username: string, password: string): Promise<UploadResponse> {
+export async function uploadApp(
+  filePath: string,
+  username: string,
+  password: string,
+): Promise<UploadResponse> {
   if (!fs.existsSync(filePath)) {
     throw new Error(`File not found at path: ${filePath}`);
   }
diff --git a/src/tools/applive.ts b/src/tools/applive.ts
index bc13c5b..0293d9e 100644
--- a/src/tools/applive.ts
+++ b/src/tools/applive.ts
@@ -16,7 +16,7 @@ export async function startAppLiveSession(
     appPath: string;
     desiredPhone: string;
   },
-  server: any
+  server: any,
 ): Promise<CallToolResult> {
   if (!args.desiredPlatform) {
     throw new Error("You must provide a desiredPlatform.");
@@ -56,7 +56,7 @@ export async function startAppLiveSession(
       desiredPhone: args.desiredPhone,
       desiredPlatformVersion: args.desiredPlatformVersion,
     },
-    { server }
+    { server },
   );
 
   return {
diff --git a/src/tools/automate.ts b/src/tools/automate.ts
index a18ac14..88e31d4 100644
--- a/src/tools/automate.ts
+++ b/src/tools/automate.ts
@@ -12,7 +12,7 @@ export async function fetchAutomationScreenshotsTool(
     sessionId: string;
     sessionType: SessionType;
   },
-  server: any
+  server: any,
 ): Promise<CallToolResult> {
   try {
     const screenshots = await fetchAutomationScreenshots(
diff --git a/src/tools/bstack-sdk.ts b/src/tools/bstack-sdk.ts
index d5c5b59..735dfbc 100644
--- a/src/tools/bstack-sdk.ts
+++ b/src/tools/bstack-sdk.ts
@@ -19,6 +19,7 @@ import {
   getPercyInstructions,
 } from "./sdk-utils/percy/instructions.js";
 import { getSDKPrefixCommand } from "./sdk-utils/commands.js";
+import { getBrowserStackAuth } from "../lib/get-auth.js";
 
 /**
  * BrowserStack SDK hooks into your test framework to seamlessly run tests on BrowserStack.
@@ -30,13 +31,19 @@ export async function bootstrapProjectWithSDK({
   detectedLanguage,
   desiredPlatforms,
   enablePercy,
+  server,
 }: {
   detectedBrowserAutomationFramework: SDKSupportedBrowserAutomationFramework;
   detectedTestingFramework: SDKSupportedTestingFramework;
   detectedLanguage: SDKSupportedLanguage;
   desiredPlatforms: string[];
   enablePercy: boolean;
+  server: any;
 }): Promise<CallToolResult> {
+  // Get credentials from server
+  const authString = getBrowserStackAuth(server);
+  const [username, accessKey] = authString.split(":");
+
   // Handle frameworks with unique setup instructions that don't use browserstack.yml
   if (
     detectedBrowserAutomationFramework === "cypress" ||
@@ -46,6 +53,8 @@ export async function bootstrapProjectWithSDK({
       detectedBrowserAutomationFramework,
       detectedTestingFramework,
       detectedLanguage,
+      username,
+      accessKey,
     );
     if (enablePercy) {
       const percyInstructions = getPercyInstructions(
@@ -84,6 +93,8 @@ export async function bootstrapProjectWithSDK({
       detectedBrowserAutomationFramework,
       detectedTestingFramework,
       detectedLanguage,
+      username,
+      accessKey,
     );
 
   if (enablePercy) {
@@ -159,6 +170,7 @@ export default function addSDKTools(server: McpServer) {
           detectedLanguage: args.detectedLanguage as SDKSupportedLanguage,
           desiredPlatforms: args.desiredPlatforms,
           enablePercy: args.enablePercy,
+          server,
         });
       } catch (error) {
         trackMCP(
diff --git a/src/tools/getFailureLogs.ts b/src/tools/getFailureLogs.ts
index c9f6ec3..76fc9b5 100644
--- a/src/tools/getFailureLogs.ts
+++ b/src/tools/getFailureLogs.ts
@@ -33,7 +33,7 @@ export async function getFailureLogs(
     logTypes: LogType[];
     sessionType: SessionTypeValues;
   },
-  server: any
+  server: any,
 ): Promise<CallToolResult> {
   const results: CallToolResult["content"] = [];
   const errors: string[] = [];
@@ -118,19 +118,31 @@ export async function getFailureLogs(
         }
 
         case AppAutomateLogType.DeviceLogs: {
-          response = await retrieveDeviceLogs(args.sessionId, args.buildId!, server);
+          response = await retrieveDeviceLogs(
+            args.sessionId,
+            args.buildId!,
+            server,
+          );
           results.push({ type: "text", text: response });
           break;
         }
 
         case AppAutomateLogType.AppiumLogs: {
-          response = await retrieveAppiumLogs(args.sessionId, args.buildId!, server);
+          response = await retrieveAppiumLogs(
+            args.sessionId,
+            args.buildId!,
+            server,
+          );
           results.push({ type: "text", text: response });
           break;
         }
 
         case AppAutomateLogType.CrashLogs: {
-          response = await retrieveCrashLogs(args.sessionId, args.buildId!, server);
+          response = await retrieveCrashLogs(
+            args.sessionId,
+            args.buildId!,
+            server,
+          );
           results.push({ type: "text", text: response });
           break;
         }
diff --git a/src/tools/live-utils/start-session.ts b/src/tools/live-utils/start-session.ts
index e2599ad..0f07057 100644
--- a/src/tools/live-utils/start-session.ts
+++ b/src/tools/live-utils/start-session.ts
@@ -19,7 +19,11 @@ import { getBrowserStackAuth } from "../../lib/get-auth.js";
 /**
  * Prepares local tunnel setup based on URL type
  */
-async function prepareLocalTunnel(url: string, username: string, password: string): Promise<boolean> {
+async function prepareLocalTunnel(
+  url: string,
+  username: string,
+  password: string,
+): Promise<boolean> {
   const isLocal = isLocalURL(url);
   if (isLocal) {
     await ensureLocalBinarySetup(username, password);
@@ -34,7 +38,7 @@ async function prepareLocalTunnel(url: string, username: string, password: strin
  */
 export async function startBrowserSession(
   args: DesktopSearchArgs | MobileSearchArgs,
-  server: any
+  server: any,
 ): Promise<string> {
   const entry =
     args.platformType === PlatformType.DESKTOP
diff --git a/src/tools/live.ts b/src/tools/live.ts
index f89eb61..b7c23e9 100644
--- a/src/tools/live.ts
+++ b/src/tools/live.ts
@@ -41,21 +41,24 @@ const LiveArgsSchema = z.object(LiveArgsShape);
  */
 async function launchDesktopSession(
   args: z.infer<typeof LiveArgsSchema>,
-  server: any
+  server: any,
 ): Promise<string> {
   if (!args.desiredBrowser)
     throw new Error("You must provide a desiredBrowser");
   if (!args.desiredBrowserVersion)
     throw new Error("You must provide a desiredBrowserVersion");
 
-  return startBrowserSession({
-    platformType: PlatformType.DESKTOP,
-    url: args.desiredURL,
-    os: args.desiredOS,
-    osVersion: args.desiredOSVersion,
-    browser: args.desiredBrowser,
-    browserVersion: args.desiredBrowserVersion,
-  }, server);
+  return startBrowserSession(
+    {
+      platformType: PlatformType.DESKTOP,
+      url: args.desiredURL,
+      os: args.desiredOS,
+      osVersion: args.desiredOSVersion,
+      browser: args.desiredBrowser,
+      browserVersion: args.desiredBrowserVersion,
+    },
+    server,
+  );
 }
 
 /**
@@ -63,18 +66,21 @@ async function launchDesktopSession(
  */
 async function launchMobileSession(
   args: z.infer<typeof LiveArgsSchema>,
-  server: any
+  server: any,
 ): Promise<string> {
   if (!args.desiredDevice) throw new Error("You must provide a desiredDevice");
 
-  return startBrowserSession({
-    platformType: PlatformType.MOBILE,
-    browser: args.desiredBrowser,
-    url: args.desiredURL,
-    os: args.desiredOS,
-    osVersion: args.desiredOSVersion,
-    device: args.desiredDevice,
-  }, server);
+  return startBrowserSession(
+    {
+      platformType: PlatformType.MOBILE,
+      browser: args.desiredBrowser,
+      url: args.desiredURL,
+      os: args.desiredOS,
+      osVersion: args.desiredOSVersion,
+      device: args.desiredDevice,
+    },
+    server,
+  );
 }
 
 /**
diff --git a/src/tools/observability.ts b/src/tools/observability.ts
index 9386dd4..90bb281 100644
--- a/src/tools/observability.ts
+++ b/src/tools/observability.ts
@@ -8,9 +8,13 @@ import logger from "../logger.js";
 export async function getFailuresInLastRun(
   buildName: string,
   projectName: string,
-  server:any
+  server: any,
 ): Promise<CallToolResult> {
-  const buildsData = await getLatestO11YBuildInfo(buildName, projectName, server);
+  const buildsData = await getLatestO11YBuildInfo(
+    buildName,
+    projectName,
+    server,
+  );
 
   const observabilityUrl = buildsData.observability_url;
   if (!observabilityUrl) {
@@ -61,7 +65,11 @@ export default function addObservabilityTools(server: McpServer) {
     async (args) => {
       try {
         trackMCP("getFailuresInLastRun", server.server.getClientVersion()!);
-        return await getFailuresInLastRun(args.buildName, args.projectName, server);
+        return await getFailuresInLastRun(
+          args.buildName,
+          args.projectName,
+          server,
+        );
       } catch (error) {
         logger.error("Failed to get failures in the last run: %s", error);
         trackMCP(
diff --git a/src/tools/sdk-utils/constants.ts b/src/tools/sdk-utils/constants.ts
index 04b0d5c..2172a85 100644
--- a/src/tools/sdk-utils/constants.ts
+++ b/src/tools/sdk-utils/constants.ts
@@ -1,11 +1,10 @@
 import { ConfigMapping } from "./types.js";
-import config from "../../config.js";
 
 /**
  * ---------- PYTHON INSTRUCTIONS ----------
  */
 
-const pythonInstructions = `
+const pythonInstructions = (username: string, accessKey: string) => `
 Run the following command to install the browserstack-sdk:
 \`\`\`bash
 python3 -m pip install browserstack-sdk
@@ -13,7 +12,7 @@ python3 -m pip install browserstack-sdk
 
 Run the following command to setup the browserstack-sdk:
 \`\`\`bash
-browserstack-sdk setup --username "${config.browserstackUsername}" --key "${config.browserstackAccessKey}"
+browserstack-sdk setup --username "${username}" --key "${accessKey}"
 \`\`\`
 
 In order to run tests on BrowserStack, run the following command:
@@ -22,7 +21,8 @@ browserstack-sdk python <path-to-test-file>
 \`\`\`
 `;
 
-const generatePythonFrameworkInstructions = (framework: string) => `
+const generatePythonFrameworkInstructions =
+  (framework: string) => (username: string, accessKey: string) => `
 Run the following command to install the browserstack-sdk:
 \`\`\`bash
 python3 -m pip install browserstack-sdk
@@ -30,7 +30,7 @@ python3 -m pip install browserstack-sdk
 
 Run the following command to setup the browserstack-sdk:
 \`\`\`bash
-browserstack-sdk setup --framework "${framework}" --username "${config.browserstackUsername}" --key "${config.browserstackAccessKey}"
+browserstack-sdk setup --framework "${framework}" --username "${username}" --key "${accessKey}"
 \`\`\`
 
 In order to run tests on BrowserStack, run the following command:
@@ -50,7 +50,7 @@ const pytestInstructions = generatePythonFrameworkInstructions("pytest");
 const argsInstruction =
   '<argLine>-javaagent:"${com.browserstack:browserstack-java-sdk:jar}"</argLine>';
 
-const javaInstructions = `
+const javaInstructions = (username: string, accessKey: string) => `
 Strictly Add the following dependencies to your \`pom.xml\`:
 \`\`\`xml
 <dependency>
@@ -72,8 +72,8 @@ dependencies {
 
 Inform user to export:
 \`\`\`bash
-export BROWSERSTACK_USERNAME=${config.browserstackUsername}
-export BROWSERSTACK_ACCESS_KEY=${config.browserstackAccessKey}
+export BROWSERSTACK_USERNAME=${username}
+export BROWSERSTACK_ACCESS_KEY=${accessKey}
 \`\`\`
 
 Run tests using:
@@ -91,7 +91,7 @@ gradle clean test
  * ---------- CSharp INSTRUCTIONS ----------
  */
 
-const csharpCommonInstructions = `
+const csharpCommonInstructions = (username: string, accessKey: string) => `
 1. Install BrowserStack TestAdapter NuGet package  
    Add the package to your project:
    \`\`\`bash
@@ -106,7 +106,7 @@ const csharpCommonInstructions = `
 3. Set up BrowserStack SDK  
    Replace the placeholders with your actual BrowserStack credentials:
    \`\`\`bash
-   dotnet browserstack-sdk setup --userName ${config.browserstackUsername} --accessKey ${config.browserstackAccessKey}
+   dotnet browserstack-sdk setup --userName ${username} --accessKey ${accessKey}
    \`\`\`
 
 4. Detect if you are running on Apple Silicon (macOS only)  
@@ -143,7 +143,10 @@ const csharpCommonInstructions = `
      \`\`\`
 `;
 
-const csharpPlaywrightCommonInstructions = `
+const csharpPlaywrightCommonInstructions = (
+  username: string,
+  accessKey: string,
+) => `
 1. Install BrowserStack TestAdapter NuGet package  
    Run the following command:
    \`\`\`bash
@@ -158,7 +161,7 @@ const csharpPlaywrightCommonInstructions = `
 3. Set up BrowserStack SDK  
    Replace the placeholders with your actual credentials:
    \`\`\`bash
-   dotnet browserstack-sdk setup --userName ${config.browserstackUsername} --accessKey ${config.browserstackAccessKey}
+   dotnet browserstack-sdk setup --userName ${username} --accessKey ${accessKey}
    \`\`\`
 
 4. Supported browsers  
@@ -208,7 +211,7 @@ const csharpPlaywrightCommonInstructions = `
  * ---------- NODEJS INSTRUCTIONS ----------
  */
 
-const nodejsInstructions = `
+const nodejsInstructions = (username: string, accessKey: string) => `
 - Ensure that \`browserstack-node-sdk\` is present in package.json, use the latest version.
 - Add new scripts to package.json for running tests on BrowserStack (use \`npx\` to trigger the sdk):
   \`\`\`json
@@ -221,13 +224,19 @@ const nodejsInstructions = `
   "browserstack-node-sdk": "latest"
   \`\`\`
 - Inform user to export BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY as environment variables.
+- For example, in bash:
+  \`\`\`bash
+  export BROWSERSTACK_USERNAME=${username}
+  export BROWSERSTACK_ACCESS_KEY=${accessKey}
+  \`\`\`
+  
 `;
 
 /**
  * ---------- EXPORT CONFIG ----------
  */
 
-const webdriverioInstructions = `
+const webdriverioInstructions = (username: string, accessKey: string) => `
 To integrate your WebdriverIO test suite with BrowserStack, follow these steps. This process uses the @wdio/browserstack-service and does not require a browserstack.yml file.
 
 **1. Set BrowserStack Credentials**
@@ -236,14 +245,14 @@ Export your BrowserStack username and access key as environment variables.
 
 For macOS/Linux:
 \`\`\`bash
-export BROWSERSTACK_USERNAME= ${config.browserstackUsername}
-export BROWSERSTACK_ACCESS_KEY= ${config.browserstackAccessKey}
+export BROWSERSTACK_USERNAME=${username}
+export BROWSERSTACK_ACCESS_KEY=${accessKey}
 \`\`\`
 
 For Windows PowerShell:
 \`\`\`powershell
-$env:BROWSERSTACK_USERNAME=${config.browserstackUsername}
-$env:BROWSERSTACK_ACCESS_KEY=${config.browserstackAccessKey}
+$env:BROWSERSTACK_USERNAME=${username}
+$env:BROWSERSTACK_ACCESS_KEY=${accessKey}
 \`\`\`
 
 **2. Install the BrowserStack WDIO Service**
@@ -331,7 +340,7 @@ exports.config.capabilities.forEach(function (caps) {
 You can now run your tests on BrowserStack using your standard WebdriverIO command.
 `;
 
-const cypressInstructions = `
+const cypressInstructions = (username: string, accessKey: string) => `
 To integrate your Cypress test suite with BrowserStack, follow these steps. This process uses the BrowserStack Cypress CLI and a \`browserstack.json\` file for configuration.
 
 **1. Install the BrowserStack Cypress CLI**
@@ -359,8 +368,8 @@ Open the generated \`browserstack.json\` file and update it with your BrowserSta
 \`\`\`json
 {
   "auth": {
-    "username": "${config.browserstackUsername}",
-    "access_key": "${config.browserstackAccessKey}"
+    "username": "${username}",
+    "access_key": "${accessKey}"
   },
   "browsers": [
     {
diff --git a/src/tools/sdk-utils/instructions.ts b/src/tools/sdk-utils/instructions.ts
index ff9c018..f9ac6b6 100644
--- a/src/tools/sdk-utils/instructions.ts
+++ b/src/tools/sdk-utils/instructions.ts
@@ -10,6 +10,8 @@ export const getInstructionsForProjectConfiguration = (
   detectedBrowserAutomationFramework: SDKSupportedBrowserAutomationFramework,
   detectedTestingFramework: SDKSupportedTestingFramework,
   detectedLanguage: SDKSupportedLanguage,
+  username: string,
+  accessKey: string,
 ) => {
   const configuration = SUPPORTED_CONFIGURATIONS[detectedLanguage];
 
@@ -33,9 +35,11 @@ export const getInstructionsForProjectConfiguration = (
     );
   }
 
-  return configuration[detectedBrowserAutomationFramework][
-    detectedTestingFramework
-  ].instructions;
+  const instructionFunction =
+    configuration[detectedBrowserAutomationFramework][detectedTestingFramework]
+      .instructions;
+
+  return instructionFunction(username, accessKey);
 };
 
 export function generateBrowserStackYMLInstructions(
diff --git a/src/tools/sdk-utils/types.ts b/src/tools/sdk-utils/types.ts
index 3e3ed44..bb439ad 100644
--- a/src/tools/sdk-utils/types.ts
+++ b/src/tools/sdk-utils/types.ts
@@ -42,7 +42,10 @@ export type ConfigMapping = Record<
     Record<
       SDKSupportedBrowserAutomationFrameworkEnum,
       Partial<
-        Record<SDKSupportedTestingFrameworkEnum, { instructions: string }>
+        Record<
+          SDKSupportedTestingFrameworkEnum,
+          { instructions: (username: string, accessKey: string) => string }
+        >
       >
     >
   >
diff --git a/src/tools/selfheal.ts b/src/tools/selfheal.ts
index 8cd4486..ddd6efd 100644
--- a/src/tools/selfheal.ts
+++ b/src/tools/selfheal.ts
@@ -8,7 +8,7 @@ import { trackMCP } from "../lib/instrumentation.js";
 // Tool function that fetches self-healing selectors
 export async function fetchSelfHealSelectorTool(
   args: { sessionId: string },
-  server: any
+  server: any,
 ): Promise<CallToolResult> {
   try {
     const selectors = await getSelfHealSelectors(args.sessionId, server);
diff --git a/src/tools/testmanagement-utils/TCG-utils/api.ts b/src/tools/testmanagement-utils/TCG-utils/api.ts
index 71adf97..c708635 100644
--- a/src/tools/testmanagement-utils/TCG-utils/api.ts
+++ b/src/tools/testmanagement-utils/TCG-utils/api.ts
@@ -19,7 +19,7 @@ import { getBrowserStackAuth } from "../../../lib/get-auth.js";
  */
 export async function fetchFormFields(
   projectId: string,
-  server: any
+  server: any,
 ): Promise<{ default_fields: any; custom_fields: any }> {
   const res = await axios.get(FORM_FIELDS_URL(projectId), {
     headers: {
@@ -38,7 +38,7 @@ export async function triggerTestCaseGeneration(
   folderId: string,
   projectId: string,
   source: string,
-  server: any
+  server: any,
 ): Promise<string> {
   const res = await axios.post(
     TCG_TRIGGER_URL,
@@ -349,7 +349,7 @@ export async function bulkCreateTestCases(
 
 export async function projectIdentifierToId(
   projectId: string,
-  server: any
+  server: any,
 ): Promise<string> {
   const url = `https://test-management.browserstack.com/api/v1/projects/?q=${projectId}`;
 
diff --git a/src/tools/testmanagement-utils/add-test-result.ts b/src/tools/testmanagement-utils/add-test-result.ts
index 7a75fed..6843b7a 100644
--- a/src/tools/testmanagement-utils/add-test-result.ts
+++ b/src/tools/testmanagement-utils/add-test-result.ts
@@ -33,7 +33,7 @@ export type AddTestResultArgs = z.infer<typeof AddTestResultSchema>;
  */
 export async function addTestResult(
   rawArgs: AddTestResultArgs,
-  server: any
+  server: any,
 ): Promise<CallToolResult> {
   try {
     const args = AddTestResultSchema.parse(rawArgs);
diff --git a/src/tools/testmanagement-utils/create-lca-steps.ts b/src/tools/testmanagement-utils/create-lca-steps.ts
index 3ad12fc..969548d 100644
--- a/src/tools/testmanagement-utils/create-lca-steps.ts
+++ b/src/tools/testmanagement-utils/create-lca-steps.ts
@@ -69,7 +69,10 @@ export async function createLCASteps(
 ): Promise<CallToolResult> {
   try {
     // Get the project ID from identifier
-    const projectId = await projectIdentifierToId(args.project_identifier,server);
+    const projectId = await projectIdentifierToId(
+      args.project_identifier,
+      server,
+    );
 
     // Get the test case ID and folder ID from identifier
     const { testCaseId, folderId } = await testCaseIdentifierToDetails(
@@ -127,7 +130,7 @@ export async function createLCASteps(
           maxWaitMs, // max wait time
           2 * 60 * 1000, // 2 minutes initial wait
           10 * 1000, // 10 seconds interval
-          server
+          server,
         );
 
         if (lcaResult && lcaResult.status === "done") {
diff --git a/src/tools/testmanagement-utils/create-project-folder.ts b/src/tools/testmanagement-utils/create-project-folder.ts
index 70f52d0..c537e81 100644
--- a/src/tools/testmanagement-utils/create-project-folder.ts
+++ b/src/tools/testmanagement-utils/create-project-folder.ts
@@ -37,7 +37,7 @@ type CreateProjFoldArgs = z.infer<typeof CreateProjFoldSchema>;
  */
 export async function createProjectOrFolder(
   args: CreateProjFoldArgs,
-  server: any
+  server: any,
 ): Promise<CallToolResult> {
   const {
     project_name,
diff --git a/src/tools/testmanagement-utils/create-testcase.ts b/src/tools/testmanagement-utils/create-testcase.ts
index 947ee65..911b0eb 100644
--- a/src/tools/testmanagement-utils/create-testcase.ts
+++ b/src/tools/testmanagement-utils/create-testcase.ts
@@ -141,7 +141,7 @@ import { getBrowserStackAuth } from "../../lib/get-auth.js";
 
 export async function createTestCase(
   params: TestCaseCreateRequest,
-  server: any
+  server: any,
 ): Promise<CallToolResult> {
   const body = { test_case: params };
   const authString = getBrowserStackAuth(server);
@@ -179,7 +179,10 @@ export async function createTestCase(
     }
 
     const tc = data.test_case;
-    const projectId = await projectIdentifierToId(params.project_identifier, server);
+    const projectId = await projectIdentifierToId(
+      params.project_identifier,
+      server,
+    );
 
     return {
       content: [
diff --git a/src/tools/testmanagement-utils/create-testrun.ts b/src/tools/testmanagement-utils/create-testrun.ts
index dd3fac7..ce55b8b 100644
--- a/src/tools/testmanagement-utils/create-testrun.ts
+++ b/src/tools/testmanagement-utils/create-testrun.ts
@@ -53,7 +53,7 @@ export type CreateTestRunArgs = z.infer<typeof CreateTestRunSchema>;
  */
 export async function createTestRun(
   rawArgs: CreateTestRunArgs,
-  server: any
+  server: any,
 ): Promise<CallToolResult> {
   try {
     const inputArgs = {
diff --git a/src/tools/testmanagement-utils/list-testcases.ts b/src/tools/testmanagement-utils/list-testcases.ts
index ac385e1..476fa8d 100644
--- a/src/tools/testmanagement-utils/list-testcases.ts
+++ b/src/tools/testmanagement-utils/list-testcases.ts
@@ -38,7 +38,7 @@ export type ListTestCasesArgs = z.infer<typeof ListTestCasesSchema>;
  */
 export async function listTestCases(
   args: ListTestCasesArgs,
-  server: any
+  server: any,
 ): Promise<CallToolResult> {
   try {
     // Build query string
diff --git a/src/tools/testmanagement-utils/list-testruns.ts b/src/tools/testmanagement-utils/list-testruns.ts
index 4a215c2..002ccb0 100644
--- a/src/tools/testmanagement-utils/list-testruns.ts
+++ b/src/tools/testmanagement-utils/list-testruns.ts
@@ -26,7 +26,7 @@ type ListTestRunsArgs = z.infer<typeof ListTestRunsSchema>;
  */
 export async function listTestRuns(
   args: ListTestRunsArgs,
-  server: any
+  server: any,
 ): Promise<CallToolResult> {
   try {
     const params = new URLSearchParams();
diff --git a/src/tools/testmanagement-utils/update-testrun.ts b/src/tools/testmanagement-utils/update-testrun.ts
index db953de..86ffc21 100644
--- a/src/tools/testmanagement-utils/update-testrun.ts
+++ b/src/tools/testmanagement-utils/update-testrun.ts
@@ -35,7 +35,7 @@ type UpdateTestRunArgs = z.infer<typeof UpdateTestRunSchema>;
  */
 export async function updateTestRun(
   args: UpdateTestRunArgs,
-  server: any
+  server: any,
 ): Promise<CallToolResult> {
   try {
     const body = { test_run: args.test_run };
diff --git a/src/tools/testmanagement-utils/upload-file.ts b/src/tools/testmanagement-utils/upload-file.ts
index 73f600b..cebbeca 100644
--- a/src/tools/testmanagement-utils/upload-file.ts
+++ b/src/tools/testmanagement-utils/upload-file.ts
@@ -28,7 +28,7 @@ export const UploadFileSchema = z.object({
  */
 export async function uploadFile(
   args: z.infer<typeof UploadFileSchema>,
-  server: any
+  server: any,
 ): Promise<CallToolResult> {
   const { project_identifier, file_path } = args;
 
@@ -47,7 +47,10 @@ export async function uploadFile(
       };
     }
     // Get the project ID
-    const projectIdResponse = await projectIdentifierToId(project_identifier,server);
+    const projectIdResponse = await projectIdentifierToId(
+      project_identifier,
+      server,
+    );
 
     const formData = new FormData();
     formData.append("attachments[]", fs.createReadStream(file_path));
diff --git a/src/tools/testmanagement.ts b/src/tools/testmanagement.ts
index 428ddcc..32adc68 100644
--- a/src/tools/testmanagement.ts
+++ b/src/tools/testmanagement.ts
@@ -14,7 +14,6 @@ import {
   CreateTestCaseSchema,
 } from "./testmanagement-utils/create-testcase.js";
 
-
 import {
   listTestCases,
   ListTestCasesSchema,
@@ -60,21 +59,14 @@ import {
  */
 export async function createProjectOrFolderTool(
   args: z.infer<typeof CreateProjFoldSchema>,
-  server: McpServer
+  server: McpServer,
 ): Promise<CallToolResult> {
   try {
-    trackMCP(
-      "createProjectOrFolder",
-      server.server.getClientVersion()!,
-    );
-    return await createProjectOrFolder(args,server);
+    trackMCP("createProjectOrFolder", server.server.getClientVersion()!);
+    return await createProjectOrFolder(args, server);
   } catch (err) {
     logger.error("Failed to create project/folder: %s", err);
-    trackMCP(
-      "createProjectOrFolder",
-      server.server.getClientVersion()!,
-      err,
-    );
+    trackMCP("createProjectOrFolder", server.server.getClientVersion()!, err);
     return {
       content: [
         {
@@ -95,7 +87,7 @@ export async function createProjectOrFolderTool(
  */
 export async function createTestCaseTool(
   args: TestCaseCreateRequest,
-  server: McpServer
+  server: McpServer,
 ): Promise<CallToolResult> {
   // Sanitize input arguments
   const cleanedArgs = sanitizeArgs(args);
@@ -126,7 +118,7 @@ export async function createTestCaseTool(
 
 export async function listTestCasesTool(
   args: z.infer<typeof ListTestCasesSchema>,
-  server: McpServer
+  server: McpServer,
 ): Promise<CallToolResult> {
   try {
     trackMCP("listTestCases", server.server.getClientVersion()!);
@@ -153,7 +145,7 @@ export async function listTestCasesTool(
  */
 export async function createTestRunTool(
   args: z.infer<typeof CreateTestRunSchema>,
-  server: McpServer
+  server: McpServer,
 ): Promise<CallToolResult> {
   try {
     trackMCP("createTestRun", server.server.getClientVersion()!);
@@ -180,7 +172,7 @@ export async function createTestRunTool(
  */
 export async function listTestRunsTool(
   args: z.infer<typeof ListTestRunsSchema>,
-  server: McpServer
+  server: McpServer,
 ): Promise<CallToolResult> {
   try {
     trackMCP("listTestRuns", server.server.getClientVersion()!);
@@ -209,7 +201,7 @@ export async function listTestRunsTool(
  */
 export async function updateTestRunTool(
   args: z.infer<typeof UpdateTestRunSchema>,
-  server: McpServer
+  server: McpServer,
 ): Promise<CallToolResult> {
   try {
     trackMCP("updateTestRun", server.server.getClientVersion()!);
@@ -236,7 +228,7 @@ export async function updateTestRunTool(
  */
 export async function addTestResultTool(
   args: z.infer<typeof AddTestResultSchema>,
-  server: McpServer
+  server: McpServer,
 ): Promise<CallToolResult> {
   try {
     trackMCP("addTestResult", server.server.getClientVersion()!);
@@ -263,13 +255,10 @@ export async function addTestResultTool(
  */
 export async function uploadProductRequirementFileTool(
   args: z.infer<typeof UploadFileSchema>,
-  server: McpServer
+  server: McpServer,
 ): Promise<CallToolResult> {
   try {
-    trackMCP(
-      "uploadProductRequirementFile",
-      server.server.getClientVersion()!,
-    );
+    trackMCP("uploadProductRequirementFile", server.server.getClientVersion()!);
     return await uploadFile(args, server);
   } catch (err) {
     trackMCP(
@@ -298,20 +287,13 @@ export async function uploadProductRequirementFileTool(
 export async function createTestCasesFromFileTool(
   args: z.infer<typeof CreateTestCasesFromFileSchema>,
   context: any,
-  server: McpServer
+  server: McpServer,
 ): Promise<CallToolResult> {
   try {
-    trackMCP(
-      "createTestCasesFromFile",
-      server.server.getClientVersion()!,
-    );
-    return await createTestCasesFromFile(args, context,server);
+    trackMCP("createTestCasesFromFile", server.server.getClientVersion()!);
+    return await createTestCasesFromFile(args, context, server);
   } catch (err) {
-    trackMCP(
-      "createTestCasesFromFile",
-      server.server.getClientVersion()!,
-      err,
-    );
+    trackMCP("createTestCasesFromFile", server.server.getClientVersion()!, err);
     return {
       content: [
         {
@@ -333,7 +315,7 @@ export async function createTestCasesFromFileTool(
 export async function createLCAStepsTool(
   args: z.infer<typeof CreateLCAStepsSchema>,
   context: any,
-  server: McpServer
+  server: McpServer,
 ): Promise<CallToolResult> {
   try {
     trackMCP("createLCASteps", server.server.getClientVersion()!);
diff --git a/tests/tools/applive.test.ts b/tests/tools/applive.test.ts
index bf2db5f..0598244 100644
--- a/tests/tools/applive.test.ts
+++ b/tests/tools/applive.test.ts
@@ -53,57 +53,65 @@ describe('startAppLiveSession', () => {
   };
 
   it('should successfully start an Android app live session', async () => {
-    const result = await startAppLiveSession(validAndroidArgs);
+    const mockServer = { getClientVersion: () => "test-version" };
+    const result = await startAppLiveSession(validAndroidArgs, mockServer);
 
     expect(startSession).toHaveBeenCalledWith({
       appPath: '/path/to/app.apk',
       desiredPlatform: 'android',
       desiredPhone: validAndroidArgs.desiredPhone,
       desiredPlatformVersion: validAndroidArgs.desiredPlatformVersion
-    });
-    expect(result.content[0].text).toContain('Successfully started a session');
+    }, { server: mockServer });
+    expect(result.content?.[0]?.text).toContain('Successfully started a session');
   });
 
   it('should successfully start an iOS app live session', async () => {
-    const result = await startAppLiveSession(validiOSArgs);
+    const mockServer = { getClientVersion: () => "test-version" };
+    const result = await startAppLiveSession(validiOSArgs, mockServer);
 
     expect(startSession).toHaveBeenCalledWith({
       appPath: '/path/to/app.ipa',
       desiredPlatform: 'ios',
       desiredPhone: validiOSArgs.desiredPhone,
       desiredPlatformVersion: validiOSArgs.desiredPlatformVersion
-    });
-    expect(result.content[0].text).toContain('Successfully started a session');
+    }, { server: mockServer });
+    expect(result.content?.[0]?.text).toContain('Successfully started a session');
   });
 
   it('should fail if platform is not provided', async () => {
     const args = { ...validAndroidArgs, desiredPlatform: '' };
-    await expect(startAppLiveSession(args)).rejects.toThrow('You must provide a desiredPlatform');
+    const mockServer = { server: { getClientVersion: () => "test-version" } };
+    await expect(startAppLiveSession(args, mockServer)).rejects.toThrow('You must provide a desiredPlatform');
   });
 
   it('should fail if app path is not provided', async () => {
     const args = { ...validAndroidArgs, appPath: '' };
-    await expect(startAppLiveSession(args)).rejects.toThrow('You must provide a appPath');
+    const mockServer = { server: { getClientVersion: () => "test-version" } };
+    await expect(startAppLiveSession(args, mockServer)).rejects.toThrow('You must provide a appPath');
   });
 
   it('should fail if phone is not provided', async () => {
     const args = { ...validAndroidArgs, desiredPhone: '' };
-    await expect(startAppLiveSession(args)).rejects.toThrow('You must provide a desiredPhone');
+    const mockServer = { server: { getClientVersion: () => "test-version" } };
+    await expect(startAppLiveSession(args, mockServer)).rejects.toThrow('You must provide a desiredPhone');
   });
 
   it('should fail if Android app path does not end with .apk', async () => {
     const args = { ...validAndroidArgs, appPath: '/path/to/app.ipa' };
-    await expect(startAppLiveSession(args)).rejects.toThrow('You must provide a valid Android app path');
+    const mockServer = { server: { getClientVersion: () => "test-version" } };
+    await expect(startAppLiveSession(args, mockServer)).rejects.toThrow('You must provide a valid Android app path');
   });
 
   it('should fail if iOS app path does not end with .ipa', async () => {
     const args = { ...validiOSArgs, appPath: '/path/to/app.apk' };
-    await expect(startAppLiveSession(args)).rejects.toThrow('You must provide a valid iOS app path');
+    const mockServer = { server: { getClientVersion: () => "test-version" } };
+    await expect(startAppLiveSession(args, mockServer)).rejects.toThrow('You must provide a valid iOS app path');
   });
 
   it('should fail if app file does not exist', async () => {
     (fs.existsSync as Mock).mockReturnValue(false);
-    await expect(startAppLiveSession(validAndroidArgs)).rejects.toThrow('The app path does not exist');
+    const mockServer = { server: { getClientVersion: () => "test-version" } };
+    await expect(startAppLiveSession(validAndroidArgs, mockServer)).rejects.toThrow('The app path does not exist');
     expect(logger.error).toHaveBeenCalled();
   });
 
@@ -111,12 +119,14 @@ describe('startAppLiveSession', () => {
     (fs.accessSync as Mock).mockImplementation(() => {
       throw new Error('EACCES: permission denied');
     });
-    await expect(startAppLiveSession(validAndroidArgs)).rejects.toThrow('The app path does not exist or is not readable');
+    const mockServer = { server: { getClientVersion: () => "test-version" } };
+    await expect(startAppLiveSession(validAndroidArgs, mockServer)).rejects.toThrow('The app path does not exist or is not readable');
     expect(logger.error).toHaveBeenCalled();
   });
 
   it('should handle session start failure', async () => {
     (startSession as Mock).mockRejectedValue(new Error('Session start failed'));
-    await expect(startAppLiveSession(validAndroidArgs)).rejects.toThrow('Session start failed');
+    const mockServer = { server: { getClientVersion: () => "test-version" } };
+    await expect(startAppLiveSession(validAndroidArgs, mockServer)).rejects.toThrow('Session start failed');
   });
 });
diff --git a/tests/tools/getFailureLogs.test.ts b/tests/tools/getFailureLogs.test.ts
index 9a2763c..cf4a293 100644
--- a/tests/tools/getFailureLogs.test.ts
+++ b/tests/tools/getFailureLogs.test.ts
@@ -65,48 +65,52 @@ describe('BrowserStack Failure Logs', () => {
 
   describe('getFailureLogs - Input Validation', () => {
     it('should throw error if sessionId is not provided', async () => {
+      const mockServer = { server: { getClientVersion: () => "test-version" } };
       await expect(getFailureLogs({
         sessionId: '',
         logTypes: ['networkLogs'],
         sessionType: 'automate'
-      })).rejects.toThrow('Session ID is required');
+      }, mockServer)).rejects.toThrow('Session ID is required');
     });
 
     it('should throw error if buildId is not provided for app-automate session', async () => {
+      const mockServer = { server: { getClientVersion: () => "test-version" } };
       await expect(getFailureLogs({
         sessionId: 'test-session',
         logTypes: ['deviceLogs'],
         sessionType: 'app-automate'
-      })).rejects.toThrow('Build ID is required for app-automate sessions');
+      }, mockServer)).rejects.toThrow('Build ID is required for app-automate sessions');
     });
 
     it('should return error for invalid log types', async () => {
+      const mockServer = { server: { getClientVersion: () => "test-version" } };
       const result = await getFailureLogs({
         sessionId: 'test-session',
         logTypes: ['invalidLogType'] as any,
         sessionType: 'automate'
-      });
+      }, mockServer);
 
-      expect(result.content[0].isError).toBe(true);
-      expect(result.content[0].text).toContain('Invalid log type');
+      expect(result.content?.[0]?.isError).toBe(true);
+      expect(result.content?.[0]?.text).toContain('Invalid log type');
     });
 
     it('should return error when mixing session types', async () => {
+      const mockServer = { server: { getClientVersion: () => "test-version" } };
       const automateResult = await getFailureLogs({
         sessionId: 'test-session',
         logTypes: ['deviceLogs'],
         sessionType: 'automate'
-      });
+      }, mockServer);
 
       const appAutomateResult = await getFailureLogs({
         sessionId: 'test-session',
         buildId: 'test-build',
         logTypes: ['networkLogs'],
         sessionType: 'app-automate'
-      });
+      }, mockServer);
 
-      expect(automateResult.content[0].isError).toBe(true);
-      expect(appAutomateResult.content[0].isError).toBe(true);
+      expect(automateResult.content?.[0]?.isError).toBe(true);
+      expect(appAutomateResult.content?.[0]?.isError).toBe(true);
     });
   });
 
@@ -137,39 +141,42 @@ describe('BrowserStack Failure Logs', () => {
     });
 
     it('should fetch network logs successfully', async () => {
+      const mockServer = { server: { getClientVersion: () => "test-version" } };
       const result = await getFailureLogs({
         sessionId: mockSessionId,
         logTypes: ['networkLogs'],
         sessionType: 'automate'
-      });
+      }, mockServer);
 
-      expect(automate.retrieveNetworkFailures).toHaveBeenCalledWith(mockSessionId);
-      expect(result.content[0].type).toBe('text');
-      expect(result.content[0].text).toContain('Network Failures (1 found)');
+      expect(automate.retrieveNetworkFailures).toHaveBeenCalledWith(mockSessionId, expect.anything());
+      expect(result.content?.[0]?.type).toBe('text');
+      expect(result.content?.[0]?.text).toContain('Network Failures (1 found)');
     });
 
     it('should fetch session logs successfully', async () => {
+      const mockServer = { server: { getClientVersion: () => "test-version" } };
       const result = await getFailureLogs({
         sessionId: mockSessionId,
         logTypes: ['sessionLogs'],
         sessionType: 'automate'
-      });
+      }, mockServer);
 
-      expect(automate.retrieveSessionFailures).toHaveBeenCalledWith(mockSessionId);
-      expect(result.content[0].text).toContain('Session Failures (1 found)');
-      expect(result.content[0].text).toContain('[ERROR] Test failed');
+      expect(automate.retrieveSessionFailures).toHaveBeenCalledWith(mockSessionId, expect.anything());
+      expect(result.content?.[0]?.text).toContain('Session Failures (1 found)');
+      expect(result.content?.[0]?.text).toContain('[ERROR] Test failed');
     });
 
     it('should fetch console logs successfully', async () => {
+      const mockServer = { server: { getClientVersion: () => "test-version" } };
       const result = await getFailureLogs({
         sessionId: mockSessionId,
         logTypes: ['consoleLogs'],
         sessionType: 'automate'
-      });
+      }, mockServer);
 
-      expect(automate.retrieveConsoleFailures).toHaveBeenCalledWith(mockSessionId);
-      expect(result.content[0].text).toContain('Console Failures (1 found)');
-      expect(result.content[0].text).toContain('Uncaught TypeError');
+      expect(automate.retrieveConsoleFailures).toHaveBeenCalledWith(mockSessionId, expect.anything());
+      expect(result.content?.[0]?.text).toContain('Console Failures (1 found)');
+      expect(result.content?.[0]?.text).toContain('Uncaught TypeError');
     });
   });
 
@@ -187,42 +194,45 @@ describe('BrowserStack Failure Logs', () => {
     });
 
     it('should fetch device logs successfully', async () => {
+      const mockServer = { server: { getClientVersion: () => "test-version" } };
       const result = await getFailureLogs({
         sessionId: mockSessionId,
         buildId: mockBuildId,
         logTypes: ['deviceLogs'],
         sessionType: 'app-automate'
-      });
+      }, mockServer);
 
-      expect(appAutomate.retrieveDeviceLogs).toHaveBeenCalledWith(mockSessionId, mockBuildId);
-      expect(result.content[0].text).toContain('Device Failures (1 found)');
-      expect(result.content[0].text).toContain('Fatal Exception');
+      expect(appAutomate.retrieveDeviceLogs).toHaveBeenCalledWith(mockSessionId, mockBuildId, expect.anything());
+      expect(result.content?.[0]?.text).toContain('Device Failures (1 found)');
+      expect(result.content?.[0]?.text).toContain('Fatal Exception');
     });
 
     it('should fetch appium logs successfully', async () => {
+      const mockServer = { server: { getClientVersion: () => "test-version" } };
       const result = await getFailureLogs({
         sessionId: mockSessionId,
         buildId: mockBuildId,
         logTypes: ['appiumLogs'],
         sessionType: 'app-automate'
-      });
+      }, mockServer);
 
-      expect(appAutomate.retrieveAppiumLogs).toHaveBeenCalledWith(mockSessionId, mockBuildId);
-      expect(result.content[0].text).toContain('Appium Failures (1 found)');
-      expect(result.content[0].text).toContain('Element not found');
+      expect(appAutomate.retrieveAppiumLogs).toHaveBeenCalledWith(mockSessionId, mockBuildId, expect.anything());
+      expect(result.content?.[0]?.text).toContain('Appium Failures (1 found)');
+      expect(result.content?.[0]?.text).toContain('Element not found');
     });
 
     it('should fetch crash logs successfully', async () => {
+      const mockServer = { server: { getClientVersion: () => "test-version" } };
       const result = await getFailureLogs({
         sessionId: mockSessionId,
         buildId: mockBuildId,
         logTypes: ['crashLogs'],
         sessionType: 'app-automate'
-      });
+      }, mockServer);
 
-      expect(appAutomate.retrieveCrashLogs).toHaveBeenCalledWith(mockSessionId, mockBuildId);
-      expect(result.content[0].text).toContain('Crash Failures (1 found)');
-      expect(result.content[0].text).toContain('signal 11');
+      expect(appAutomate.retrieveCrashLogs).toHaveBeenCalledWith(mockSessionId, mockBuildId, expect.anything());
+      expect(result.content?.[0]?.text).toContain('Crash Failures (1 found)');
+      expect(result.content?.[0]?.text).toContain('signal 11');
     });
   });
 
@@ -230,13 +240,14 @@ describe('BrowserStack Failure Logs', () => {
     it('should handle empty log responses', async () => {
       vi.mocked(automate.retrieveNetworkFailures).mockResolvedValue('No network failures found');
 
+      const mockServer = { server: { getClientVersion: () => "test-version" } };
       const result = await getFailureLogs({
         sessionId: mockSessionId,
         logTypes: ['networkLogs'],
         sessionType: 'automate'
-      });
+      }, mockServer);
 
-      expect(result.content[0].text).toBe('No network failures found');
+      expect(result.content?.[0]?.text).toBe('No network failures found');
     });
   });
 
@@ -313,4 +324,4 @@ console.error('Uncaught TypeError')
       expect(appAutomate.filterCrashFailures('')).toEqual([]);
     });
   });
-}); 
\ No newline at end of file
+});
diff --git a/tests/tools/observability.test.ts b/tests/tools/observability.test.ts
index 78e3cac..5b99a01 100644
--- a/tests/tools/observability.test.ts
+++ b/tests/tools/observability.test.ts
@@ -32,13 +32,15 @@ describe('getFailuresInLastRun', () => {
   it('should successfully retrieve failures for a valid build', async () => {
     (getLatestO11YBuildInfo as Mock).mockResolvedValue(validBuildData);
 
-    const result = await getFailuresInLastRun('test-build', 'test-project');
-
-    expect(getLatestO11YBuildInfo).toHaveBeenCalledWith('test-build', 'test-project');
-    expect(result.content[0].text).toContain('https://observability.browserstack.com/123');
-    expect(result.content[0].text).toContain('Test insight message');
-    expect(result.content[0].text).toContain('Error 1');
-    expect(result.content[0].text).toContain('Error 2');
+    const mockServer = { getClientVersion: () => "test-version" };
+    const result = await getFailuresInLastRun('test-build', 'test-project', mockServer);
+
+    expect(getLatestO11YBuildInfo).toHaveBeenCalledWith('test-build', 'test-project', mockServer);
+    expect(result.content).toBeDefined();
+    expect(result.content![0].text).toContain('https://observability.browserstack.com/123');
+    expect(result.content![0].text).toContain('Test insight message');
+    expect(result.content![0].text).toContain('Error 1');
+    expect(result.content![0].text).toContain('Error 2');
   });
 
   it('should handle missing observability URL', async () => {
@@ -47,7 +49,8 @@ describe('getFailuresInLastRun', () => {
       observability_url: null
     });
 
-    await expect(getFailuresInLastRun('test-build', 'test-project'))
+    const mockServer = { getClientVersion: () => "test-version" };
+    await expect(getFailuresInLastRun('test-build', 'test-project', mockServer))
       .rejects.toThrow('No observability URL found in build data');
   });
 
@@ -60,8 +63,10 @@ describe('getFailuresInLastRun', () => {
       }
     });
 
-    const result = await getFailuresInLastRun('test-build', 'test-project');
-    expect(result.content[0].text).toContain('No overview available');
+    const mockServer = { getClientVersion: () => "test-version" };
+    const result = await getFailuresInLastRun('test-build', 'test-project', mockServer);
+    expect(result.content).toBeDefined();
+    expect(result.content![0].text).toContain('No overview available');
   });
 
   it('should handle missing error details', async () => {
@@ -73,21 +78,25 @@ describe('getFailuresInLastRun', () => {
       }
     });
 
-    const result = await getFailuresInLastRun('test-build', 'test-project');
-    expect(result.content[0].text).toContain('No error details available');
+    const mockServer = { getClientVersion: () => "test-version" };
+    const result = await getFailuresInLastRun('test-build', 'test-project', mockServer);
+    expect(result.content).toBeDefined();
+    expect(result.content![0].text).toContain('No error details available');
   });
 
   it('should handle API errors', async () => {
     (getLatestO11YBuildInfo as Mock).mockRejectedValue(new Error('API Error'));
 
-    await expect(getFailuresInLastRun('test-build', 'test-project'))
+    const mockServer = { getClientVersion: () => "test-version" };
+    await expect(getFailuresInLastRun('test-build', 'test-project', mockServer))
       .rejects.toThrow('API Error');
   });
 
   it('should handle empty build data', async () => {
     (getLatestO11YBuildInfo as Mock).mockResolvedValue({});
 
-    await expect(getFailuresInLastRun('test-build', 'test-project'))
+    const mockServer = { getClientVersion: () => "test-version" };
+    await expect(getFailuresInLastRun('test-build', 'test-project', mockServer))
       .rejects.toThrow('No observability URL found in build data');
   });
 
@@ -97,8 +106,10 @@ describe('getFailuresInLastRun', () => {
       unique_errors: {}
     });
 
-    const result = await getFailuresInLastRun('test-build', 'test-project');
-    expect(result.content[0].text).toContain('No overview available');
-    expect(result.content[0].text).toContain('No error details available');
+    const mockServer = { getClientVersion: () => "test-version" };
+    const result = await getFailuresInLastRun('test-build', 'test-project', mockServer);
+    expect(result.content).toBeDefined();
+    expect(result.content![0].text).toContain('No overview available');
+    expect(result.content![0].text).toContain('No error details available');
   });
-}); 
\ No newline at end of file
+});
diff --git a/tests/tools/testmanagement.test.ts b/tests/tools/testmanagement.test.ts
index 94747c5..7dc5212 100644
--- a/tests/tools/testmanagement.test.ts
+++ b/tests/tools/testmanagement.test.ts
@@ -19,11 +19,11 @@ import { listTestRuns } from '../../src/tools/testmanagement-utils/list-testruns
 import { updateTestRun } from '../../src/tools/testmanagement-utils/update-testrun';
 import { createTestCasesFromFile } from '../../src/tools/testmanagement-utils/testcase-from-file';
 import { createLCASteps } from '../../src/tools/testmanagement-utils/create-lca-steps';
+import { uploadFile } from '../../src/tools/testmanagement-utils/upload-file';
 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 import axios from 'axios';
 import { beforeEach, it, expect, describe, Mocked} from 'vitest';
 import { vi, Mock } from 'vitest';
-import fs from 'fs';
 import { signedUrlMap } from '../../src/lib/inmemory-store';
 
 // Mock dependencies
@@ -78,15 +78,30 @@ vi.mock('../../src/tools/testmanagement-utils/add-test-result', () => ({
 
 vi.mock('fs');
 vi.mock('../../src/lib/inmemory-store', () => ({ signedUrlMap: new Map() }));
+vi.mock('../../src/lib/get-auth', () => ({
+  getBrowserStackAuth: vi.fn(() => 'fake-user:fake-key')
+}));
+vi.mock('../../src/tools/testmanagement-utils/TCG-utils/api', () => ({
+  projectIdentifierToId: vi.fn(() => Promise.resolve('999'))
+}));
+vi.mock('form-data', () => {
+  return {
+    default: vi.fn().mockImplementation(() => ({
+      append: vi.fn(),
+      getHeaders: vi.fn(() => ({ 'content-type': 'multipart/form-data' }))
+    }))
+  };
+});
 
 const mockServer = {
   tool: vi.fn(),
   server: {
-    getClientVersion: vi.fn(() => ({
-      name: 'vi-client',
-      version: '1.0.0',
-    })),
+    getClientVersion: vi.fn(() => "test-version"),
   },
+  authHeaders: {
+    username: 'fake-user',
+    password: 'fake-key'
+  }
 } as unknown as McpServer;
 
 addTestManagementTools(mockServer);
@@ -115,6 +130,13 @@ vi.mock('../../src/tools/testmanagement-utils/update-testrun', () => ({
     shape: {},
   },
 }));
+vi.mock('../../src/tools/testmanagement-utils/list-testcases', () => ({
+  listTestCases: vi.fn(),
+  ListTestCasesSchema: {
+    parse: (args: any) => args,
+    shape: {},
+  },
+}));
 
 
 const mockedAxios = axios as Mocked<typeof axios>;
@@ -152,17 +174,17 @@ describe('createTestCaseTool', () => {
   it('should successfully create a test case', async () => {
     (createTestCase as Mock).mockResolvedValue(mockCallToolResult);
 
-    const result = await createTestCaseTool(validArgs);
+    const result = await createTestCaseTool(validArgs, mockServer);
 
     expect(sanitizeArgs).toHaveBeenCalledWith(validArgs);
-    expect(createTestCase).toHaveBeenCalledWith(validArgs);
+    expect(createTestCase).toHaveBeenCalledWith(validArgs, mockServer);
     expect(result).toBe(mockCallToolResult);
   });
 
   it('should handle API errors while creating test case', async () => {
     (createTestCase as Mock).mockRejectedValue(new Error('API Error'));
 
-    const result = await createTestCaseTool(validArgs);
+    const result = await createTestCaseTool(validArgs, mockServer);
 
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Failed to create test case: API Error');
@@ -171,7 +193,7 @@ describe('createTestCaseTool', () => {
   it('should handle unknown error while creating test case', async () => {
     (createTestCase as Mock).mockRejectedValue('unexpected');
 
-    const result = await createTestCaseTool(validArgs);
+    const result = await createTestCaseTool(validArgs, mockServer);
 
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Unknown error');
@@ -205,25 +227,25 @@ describe('createProjectOrFolderTool', () => {
   it('should successfully create a project', async () => {
     (createProjectOrFolder as Mock).mockResolvedValue(mockProjectResponse);
 
-    const result = await createProjectOrFolderTool(validProjectArgs);
+    const result = await createProjectOrFolderTool(validProjectArgs, mockServer);
 
-    expect(createProjectOrFolder).toHaveBeenCalledWith(validProjectArgs);
+    expect(createProjectOrFolder).toHaveBeenCalledWith(validProjectArgs, mockServer);
     expect(result.content?.[0]?.text).toContain('Project created with identifier=proj-123');
   });
 
   it('should successfully create a folder', async () => {
     (createProjectOrFolder as Mock).mockResolvedValue(mockFolderResponse);
 
-    const result = await createProjectOrFolderTool(validFolderArgs);
+    const result = await createProjectOrFolderTool(validFolderArgs, mockServer);
 
-    expect(createProjectOrFolder).toHaveBeenCalledWith(validFolderArgs);
+    expect(createProjectOrFolder).toHaveBeenCalledWith(validFolderArgs, mockServer);
     expect(result.content?.[0]?.text).toContain('Folder created: ID=fold-123');
   });
 
   it('should handle error while creating project or folder', async () => {
     (createProjectOrFolder as Mock).mockRejectedValue(new Error('Failed to create project/folder'));
 
-    const result = await createProjectOrFolderTool(validProjectArgs);
+    const result = await createProjectOrFolderTool(validProjectArgs, mockServer);
 
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain(
@@ -234,7 +256,7 @@ describe('createProjectOrFolderTool', () => {
   it('should handle unknown error while creating project or folder', async () => {
     (createProjectOrFolder as Mock).mockRejectedValue('some unknown error');
 
-    const result = await createProjectOrFolderTool(validProjectArgs);
+    const result = await createProjectOrFolderTool(validProjectArgs, mockServer);
 
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain(
@@ -254,24 +276,32 @@ describe('listTestCases util', () => {
   ];
 
   it('should return formatted summary and raw JSON on success', async () => {
-    mockedAxios.get.mockResolvedValue({ data: { success: true, test_cases: mockCases, info: { count: 2 } } });
+    (listTestCases as Mock).mockResolvedValue({
+      content: [
+        { type: 'text', text: 'Found 2 test case(s):\n\n• TC-1: Test One [functional | high]\n• TC-2: Test Two [regression | medium]' },
+        { type: 'text', text: JSON.stringify(mockCases, null, 2) },
+      ],
+      isError: false,
+    });
 
     const args = { project_identifier: 'PR-1', status: 'active', p: 1 };
-    const result = await listTestCases(args as any);
+    const result = await listTestCases(args as any, mockServer);
 
-    expect(axios.get).toHaveBeenCalledWith(
-      expect.stringContaining('/projects/PR-1/test-cases?'),
-      expect.objectContaining({ auth: expect.any(Object) })
-    );
+    expect(listTestCases).toHaveBeenCalledWith(args, mockServer);
     expect(result.content?.[0]?.text).toContain('Found 2 test case(s):');
     expect(result.content?.[0]?.text).toContain('TC-1: Test One [functional | high]');
     expect(result.content?.[1]?.text).toBe(JSON.stringify(mockCases, null, 2));
   });
 
   it('should handle API errors gracefully', async () => {
-    mockedAxios.get.mockRejectedValue(new Error('Failed to list test cases: Network Error'));
+    (listTestCases as Mock).mockResolvedValue({
+      content: [
+        { type: 'text', text: 'Failed to list test cases: Network Error', isError: true },
+      ],
+      isError: true,
+    });
 
-    const result = await listTestCases({ project_identifier: 'PR-1' } as any);
+    const result = await listTestCases({ project_identifier: 'PR-1' } as any, mockServer);
 
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Failed to list test cases: Network Error');
@@ -299,16 +329,16 @@ describe('createTestRunTool', () => {
   it('should successfully create a test run', async () => {
     (createTestRun as Mock).mockResolvedValue(successRunResult);
 
-    const result = await createTestRunTool(validRunArgs as any);
+    const result = await createTestRunTool(validRunArgs as any, mockServer);
 
-    expect(createTestRun).toHaveBeenCalledWith(validRunArgs);
+    expect(createTestRun).toHaveBeenCalledWith(validRunArgs, mockServer);
     expect(result).toBe(successRunResult);
   });
 
   it('should handle API errors while creating test run', async () => {
     (createTestRun as Mock).mockRejectedValue(new Error('API Error'));
 
-    const result = await createTestRunTool(validRunArgs as any);
+    const result = await createTestRunTool(validRunArgs as any, mockServer);
 
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Failed to create test run: API Error');
@@ -317,7 +347,7 @@ describe('createTestRunTool', () => {
   it('should handle unknown error while creating test run', async () => {
     (createTestRun as Mock).mockRejectedValue('unexpected');
 
-    const result = await createTestRunTool(validRunArgs as any);
+    const result = await createTestRunTool(validRunArgs as any, mockServer);
 
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Unknown error');
@@ -345,8 +375,8 @@ describe('listTestRunsTool', () => {
       isError: false,
     });
 
-    const result = await listTestRunsTool({ project_identifier: projectId } as any);
-    expect(listTestRuns).toHaveBeenCalledWith({ project_identifier: projectId });
+    const result = await listTestRunsTool({ project_identifier: projectId } as any, mockServer);
+    expect(listTestRuns).toHaveBeenCalledWith({ project_identifier: projectId }, mockServer);
     expect(result.isError).toBe(false);
     expect(result.content?.[0]?.text).toContain('Found 2 test run(s):');
     expect(result.content?.[1]?.text).toBe(JSON.stringify(mockRuns, null, 2));
@@ -354,7 +384,7 @@ describe('listTestRunsTool', () => {
 
   it('should handle errors', async () => {
     (listTestRuns as Mock).mockRejectedValue(new Error('Network Error'));
-    const result = await listTestRunsTool({ project_identifier: projectId } as any);
+    const result = await listTestRunsTool({ project_identifier: projectId } as any, mockServer);
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Failed to list test runs: Network Error');
   });
@@ -381,8 +411,8 @@ describe('updateTestRunTool', () => {
       isError: false,
     });
 
-    const result = await updateTestRunTool(args as any);
-    expect(updateTestRun).toHaveBeenCalledWith(args);
+    const result = await updateTestRunTool(args as any, mockServer);
+    expect(updateTestRun).toHaveBeenCalledWith(args, mockServer);
     expect(result.isError).toBe(false);
     expect(result.content?.[0]?.text).toContain(`Successfully updated test run ${args.test_run_id}`);
     expect(result.content?.[1]?.text).toBe(JSON.stringify(updated, null, 2));
@@ -390,7 +420,7 @@ describe('updateTestRunTool', () => {
 
   it('should handle errors', async () => {
     (updateTestRun as Mock).mockRejectedValue(new Error('API Error'));
-    const result = await updateTestRunTool(args as any);
+    const result = await updateTestRunTool(args as any, mockServer);
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Failed to update test run: API Error');
   });
@@ -424,16 +454,16 @@ describe('addTestResultTool', () => {
   it('should successfully add a test result', async () => {
     (addTestResult as Mock).mockResolvedValue(successAddResult);
 
-    const result = await addTestResultTool(validArgs as any);
+    const result = await addTestResultTool(validArgs as any, mockServer);
 
-    expect(addTestResult).toHaveBeenCalledWith(validArgs);
+    expect(addTestResult).toHaveBeenCalledWith(validArgs, mockServer);
     expect(result).toBe(successAddResult);
   });
 
   it('should handle API errors gracefully', async () => {
     (addTestResult as Mock).mockRejectedValue(new Error('Network Error'));
 
-    const result = await addTestResultTool(validArgs as any);
+    const result = await addTestResultTool(validArgs as any, mockServer);
 
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Failed to add test result: Network Error');
@@ -442,7 +472,7 @@ describe('addTestResultTool', () => {
   it('should handle unknown errors gracefully', async () => {
     (addTestResult as Mock).mockRejectedValue('unexpected');
 
-    const result = await addTestResultTool(validArgs as any);
+    const result = await addTestResultTool(validArgs as any, mockServer);
 
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Unknown error');
@@ -461,32 +491,40 @@ describe("uploadProductRequirementFileTool", () => {
   beforeEach(() => vi.resetAllMocks());
 
   it("returns error when file does not exist", async () => {
-    (fs.existsSync as Mock).mockReturnValue(false);
-    const res = await uploadProductRequirementFileTool({ project_identifier: testProjectId, file_path: testFilePath });
+    (uploadFile as Mock).mockResolvedValue({
+      content: [
+        { type: 'text', text: 'File /tmp/sample.pdf does not exist.', isError: true },
+      ],
+      isError: true,
+    });
+    
+    const res = await uploadProductRequirementFileTool({ project_identifier: testProjectId, file_path: testFilePath }, mockServer);
     expect(res.isError).toBe(true);
     expect(res.content?.[0]?.text).toContain("does not exist");
   });
 
   it("uploads file and returns metadata", async () => {
-    (fs.existsSync as Mock).mockReturnValue(true);
-    (fs.createReadStream as Mock).mockReturnValue("STREAM");
-    const mockUpload = {
-      status: 200,
-      data: {
-        generic_attachment: [
-          {
-            id: mockFileId,
+    const mockSuccessResponse = {
+      content: [
+        { type: 'text', text: 'Successfully uploaded sample.pdf to BrowserStack Test Management.' },
+        { 
+          type: 'text', 
+          text: JSON.stringify([{
             name: "sample.pdf",
-            download_url: mockDownloadUrl,
-            content_type: "application/pdf",
-            size: 1024
-          }
-        ]
-      }
+            documentID: "mock-doc-id",
+            contentType: "application/pdf",
+            size: 1024,
+            projectReferenceId: "999"
+          }], null, 2)
+        },
+      ],
+      isError: false,
     };
-    mockedAxios.get.mockResolvedValue({ data: { success: true, projects: [{ identifier: testProjectId, id: "999" }] } });
-    mockedAxios.post.mockResolvedValue(mockUpload);
-    const res = await uploadProductRequirementFileTool({ project_identifier: testProjectId, file_path: testFilePath });
+
+    (uploadFile as Mock).mockResolvedValue(mockSuccessResponse);
+    
+    const res = await uploadProductRequirementFileTool({ project_identifier: testProjectId, file_path: testFilePath }, mockServer);
+    expect(uploadFile).toHaveBeenCalledWith({ project_identifier: testProjectId, file_path: testFilePath }, mockServer);
     expect(res.isError ?? false).toBe(false);
     expect(res.content?.[1]?.text).toContain("documentID");
   });
@@ -501,7 +539,7 @@ describe("createTestCasesFromFileTool", () => {
     (createTestCasesFromFile as Mock).mockRejectedValue(new Error("Re-Upload the file"));
     
     const args = { documentId: testDocumentId, folderId: testFolderId, projectReferenceId: testProjectId };
-    const res = await createTestCasesFromFileTool(args as any, mockContext);
+    const res = await createTestCasesFromFileTool(args as any, mockContext, mockServer);
     expect(res.isError).toBe(true);
     expect(res.content?.[0]?.text).toContain("Re-Upload the file");
   });
@@ -536,7 +574,7 @@ describe("createTestCasesFromFileTool", () => {
       isError: false
     });
     
-    const res = await createTestCasesFromFileTool(args as any, mockContext);
+    const res = await createTestCasesFromFileTool(args as any, mockContext, mockServer);
     expect(res.isError ?? false).toBe(false);
     expect(res.content?.[0]?.text).toContain("test cases created");
   });
@@ -584,9 +622,9 @@ describe("createLCAStepsTool", () => {
 
     (createLCASteps as Mock).mockResolvedValue(mockResponse);
 
-    const result = await createLCAStepsTool(validArgs as any, mockContext);
+    const result = await createLCAStepsTool(validArgs as any, mockContext, mockServer);
 
-    expect(createLCASteps).toHaveBeenCalledWith(validArgs, mockContext);
+    expect(createLCASteps).toHaveBeenCalledWith(validArgs, mockContext, mockServer);
     expect(result).toBe(mockResponse);
     expect(result.isError).toBe(false);
   });
@@ -594,7 +632,7 @@ describe("createLCAStepsTool", () => {
   it("handles errors when creating LCA steps", async () => {
     (createLCASteps as Mock).mockRejectedValue(new Error("API Error"));
 
-    const result = await createLCAStepsTool(validArgs as any, mockContext);
+    const result = await createLCAStepsTool(validArgs as any, mockContext, mockServer);
 
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain("Failed to create LCA steps: API Error");
@@ -603,7 +641,7 @@ describe("createLCAStepsTool", () => {
   it("handles unknown errors when creating LCA steps", async () => {
     (createLCASteps as Mock).mockRejectedValue("unexpected error");
 
-    const result = await createLCAStepsTool(validArgs as any, mockContext);
+    const result = await createLCAStepsTool(validArgs as any, mockContext, mockServer);
 
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain("Failed to create LCA steps: Unknown error");
@@ -621,9 +659,9 @@ describe("createLCAStepsTool", () => {
 
     (createLCASteps as Mock).mockResolvedValue(mockResponse);
 
-    const result = await createLCAStepsTool(argsWithoutWait as any, mockContext);
+    const result = await createLCAStepsTool(argsWithoutWait as any, mockContext, mockServer);
 
-    expect(createLCASteps).toHaveBeenCalledWith(argsWithoutWait, mockContext);
+    expect(createLCASteps).toHaveBeenCalledWith(argsWithoutWait, mockContext, mockServer);
     expect(result.content?.[1]?.text).toContain("Check the BrowserStack Test Management UI");
   });
 
@@ -639,9 +677,17 @@ describe("createLCAStepsTool", () => {
 
     (createLCASteps as Mock).mockResolvedValue(mockResponse);
 
-    const result = await createLCAStepsTool(argsWithCustomWait as any, mockContext);
+    const result = await createLCAStepsTool(argsWithCustomWait as any, mockContext, mockServer);
 
-    expect(createLCASteps).toHaveBeenCalledWith(argsWithCustomWait, mockContext);
+    expect(createLCASteps).toHaveBeenCalledWith(argsWithCustomWait, mockContext, mockServer);
     expect(result.content?.[1]?.text).toContain("within 5 minutes");
   });
 });
+
+vi.mock('../../src/tools/testmanagement-utils/upload-file', () => ({
+  uploadFile: vi.fn(),
+  UploadFileSchema: {
+    parse: (args: any) => args,
+    shape: {},
+  },
+}));

From 788ac8c016b4bde85ac10b592f801a8c8eef41d8 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 1 Jul 2025 13:12:52 +0530
Subject: [PATCH 08/64] Refactor local tunnel preparation to remove username
 and password parameters and handle local URL errors

---
 src/server-factory.ts                 |  4 ++--
 src/tools/live-utils/start-session.ts | 24 +++++++++++++-----------
 2 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/src/server-factory.ts b/src/server-factory.ts
index b730239..b22f95f 100644
--- a/src/server-factory.ts
+++ b/src/server-factory.ts
@@ -16,8 +16,8 @@ import { setupOnInitialized } from "./oninitialized.js";
 
 function registerTools(server: McpServer) {
   addAccessibilityTools(server); //done
-  addSDKTools(server);
-  addAppLiveTools(server); //done
+  addSDKTools(server); 
+//   addAppLiveTools(server); //done
   addBrowserLiveTools(server); //done
   addTestManagementTools(server); //done
   addAppAutomationTools(server); //done
diff --git a/src/tools/live-utils/start-session.ts b/src/tools/live-utils/start-session.ts
index 0f07057..25eeb3c 100644
--- a/src/tools/live-utils/start-session.ts
+++ b/src/tools/live-utils/start-session.ts
@@ -10,9 +10,7 @@ import {
   PlatformType,
 } from "./types.js";
 import {
-  isLocalURL,
-  ensureLocalBinarySetup,
-  killExistingBrowserStackLocalProcesses,
+  isLocalURL
 } from "../../lib/local.js";
 
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
@@ -20,16 +18,14 @@ import { getBrowserStackAuth } from "../../lib/get-auth.js";
  * Prepares local tunnel setup based on URL type
  */
 async function prepareLocalTunnel(
-  url: string,
-  username: string,
-  password: string,
+  url: string
 ): Promise<boolean> {
   const isLocal = isLocalURL(url);
   if (isLocal) {
-    await ensureLocalBinarySetup(username, password);
-  } else {
-    await killExistingBrowserStackLocalProcesses();
-  }
+    throw new Error(
+      "Local URLs are not supported for BrowserStack Remote MCP. Please use a public URL or the local MCP.",
+    );
+  } 
   return isLocal;
 }
 
@@ -49,7 +45,13 @@ export async function startBrowserSession(
   const authString = getBrowserStackAuth(server);
   const [username, password] = authString.split(":");
 
-  const isLocal = await prepareLocalTunnel(args.url, username, password);
+  if (!username || !password) {
+    throw new Error(
+      "BrowserStack credentials are not set. Please configure them in the server settings.",
+    );
+  }
+
+  const isLocal = await prepareLocalTunnel(args.url);
 
   const url =
     args.platformType === PlatformType.DESKTOP

From 3062f7b244d8e1d88f6978e94b089c8533a4253f Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 1 Jul 2025 13:13:33 +0530
Subject: [PATCH 09/64] Refactor server-factory to remove unused import of
 addAppLiveTools

---
 src/server-factory.ts                 |  5 ++---
 src/tools/live-utils/start-session.ts | 10 +++-------
 2 files changed, 5 insertions(+), 10 deletions(-)

diff --git a/src/server-factory.ts b/src/server-factory.ts
index b22f95f..a79704f 100644
--- a/src/server-factory.ts
+++ b/src/server-factory.ts
@@ -4,7 +4,6 @@ const require = createRequire(import.meta.url);
 const packageJson = require("../package.json");
 import logger from "./logger.js";
 import addSDKTools from "./tools/bstack-sdk.js";
-import addAppLiveTools from "./tools/applive.js";
 import addBrowserLiveTools from "./tools/live.js";
 import addAccessibilityTools from "./tools/accessibility.js";
 import addTestManagementTools from "./tools/testmanagement.js";
@@ -16,8 +15,8 @@ import { setupOnInitialized } from "./oninitialized.js";
 
 function registerTools(server: McpServer) {
   addAccessibilityTools(server); //done
-  addSDKTools(server); 
-//   addAppLiveTools(server); //done
+  addSDKTools(server);
+  //   addAppLiveTools(server); //done
   addBrowserLiveTools(server); //done
   addTestManagementTools(server); //done
   addAppAutomationTools(server); //done
diff --git a/src/tools/live-utils/start-session.ts b/src/tools/live-utils/start-session.ts
index 25eeb3c..a0b9b91 100644
--- a/src/tools/live-utils/start-session.ts
+++ b/src/tools/live-utils/start-session.ts
@@ -9,23 +9,19 @@ import {
   MobileEntry,
   PlatformType,
 } from "./types.js";
-import {
-  isLocalURL
-} from "../../lib/local.js";
+import { isLocalURL } from "../../lib/local.js";
 
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
 /**
  * Prepares local tunnel setup based on URL type
  */
-async function prepareLocalTunnel(
-  url: string
-): Promise<boolean> {
+async function prepareLocalTunnel(url: string): Promise<boolean> {
   const isLocal = isLocalURL(url);
   if (isLocal) {
     throw new Error(
       "Local URLs are not supported for BrowserStack Remote MCP. Please use a public URL or the local MCP.",
     );
-  } 
+  }
   return isLocal;
 }
 

From c693d6c90e4da0dab165564d131114ce040322a4 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 1 Jul 2025 13:34:05 +0530
Subject: [PATCH 10/64] Refactor UUID generation to use randomUUID from
 node:crypto

---
 src/tools/accessiblity-utils/scanner.ts             | 3 ++-
 src/tools/testmanagement-utils/TCG-utils/helpers.ts | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/tools/accessiblity-utils/scanner.ts b/src/tools/accessiblity-utils/scanner.ts
index 6b50776..931f25d 100644
--- a/src/tools/accessiblity-utils/scanner.ts
+++ b/src/tools/accessiblity-utils/scanner.ts
@@ -1,4 +1,5 @@
 import axios from "axios";
+import { randomUUID } from "node:crypto";
 import logger from "../../logger.js";
 import {
   isLocalURL,
@@ -36,7 +37,7 @@ export class AccessibilityScanner {
     }
     // Check if any URL is local
     const hasLocal = urlList.some(isLocalURL);
-    const localIdentifier = crypto.randomUUID();
+    const localIdentifier = randomUUID();
     const localHosts = new Set(["127.0.0.1", "localhost", "0.0.0.0"]);
     const BS_LOCAL_DOMAIN = "bs-local.com";
 
diff --git a/src/tools/testmanagement-utils/TCG-utils/helpers.ts b/src/tools/testmanagement-utils/TCG-utils/helpers.ts
index 40a690c..1a25328 100644
--- a/src/tools/testmanagement-utils/TCG-utils/helpers.ts
+++ b/src/tools/testmanagement-utils/TCG-utils/helpers.ts
@@ -1,4 +1,5 @@
 import { DefaultFieldMaps } from "./types.js";
+import { randomUUID } from "node:crypto";
 
 /**
  * Build mappings for default fields for priority, status, and case type.
@@ -60,7 +61,7 @@ export function createTestCasePayload(
         rich_text_id: null,
         scenario: scenarioId,
         test_case_count: tc.test_case_count || 1,
-        uuid: tc.uuid || crypto.randomUUID?.() || "unknown-uuid",
+        uuid: tc.uuid || randomUUID() || "unknown-uuid",
         "x-bstack-traceRequestId": traceId,
       },
     }),

From 047048d9c26b70315a35617e7e10cda191ce11ee Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 1 Jul 2025 13:40:09 +0530
Subject: [PATCH 11/64] Refactor AccessibilityScanner to remove local URL
 handling and associated binary setup

---
 src/tools/accessiblity-utils/scanner.ts | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/src/tools/accessiblity-utils/scanner.ts b/src/tools/accessiblity-utils/scanner.ts
index 931f25d..9aee6c9 100644
--- a/src/tools/accessiblity-utils/scanner.ts
+++ b/src/tools/accessiblity-utils/scanner.ts
@@ -42,13 +42,9 @@ export class AccessibilityScanner {
     const BS_LOCAL_DOMAIN = "bs-local.com";
 
     if (hasLocal) {
-      await ensureLocalBinarySetup(
-        this.auth?.username,
-        this.auth?.password,
-        localIdentifier,
+      throw new Error(
+        "Local URLs are not supported for Browserstack Remote MCP For accessibility scan.",
       );
-    } else {
-      await killExistingBrowserStackLocalProcesses();
     }
 
     const transformedUrlList = urlList.map((url) => {

From 75999791b94cd9fc0e8e045abfdcf8225458779d Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 1 Jul 2025 13:41:12 +0530
Subject: [PATCH 12/64] Refactor Config and AccessibilityScanner to remove
 username and access key handling

---
 src/config.ts                           | 4 ----
 src/tools/accessiblity-utils/scanner.ts | 6 +-----
 2 files changed, 1 insertion(+), 9 deletions(-)

diff --git a/src/config.ts b/src/config.ts
index a4a18a3..6f63e86 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -32,16 +32,12 @@ for (const key of BROWSERSTACK_LOCAL_OPTION_KEYS) {
 
 export class Config {
   constructor(
-    public readonly browserstackUsername: string,
-    public readonly browserstackAccessKey: string,
     public readonly DEV_MODE: boolean,
     public readonly browserstackLocalOptions: Record<string, any>,
   ) {}
 }
 
 const config = new Config(
-  process.env.BROWSERSTACK_USERNAME!,
-  process.env.BROWSERSTACK_ACCESS_KEY!,
   process.env.DEV_MODE === "true",
   browserstackLocalOptions,
 );
diff --git a/src/tools/accessiblity-utils/scanner.ts b/src/tools/accessiblity-utils/scanner.ts
index 9aee6c9..2dabeda 100644
--- a/src/tools/accessiblity-utils/scanner.ts
+++ b/src/tools/accessiblity-utils/scanner.ts
@@ -1,11 +1,7 @@
 import axios from "axios";
 import { randomUUID } from "node:crypto";
 import logger from "../../logger.js";
-import {
-  isLocalURL,
-  ensureLocalBinarySetup,
-  killExistingBrowserStackLocalProcesses,
-} from "../../lib/local.js";
+import { isLocalURL } from "../../lib/local.js";
 
 export interface AccessibilityScanResponse {
   success: boolean;

From 6815d92b484fba6c90edece016ffd53dc4ea3f3e Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 1 Jul 2025 13:44:33 +0530
Subject: [PATCH 13/64] Refactor start-session to comment out openBrowser
 function and its invocation

---
 src/tools/applive-utils/start-session.ts | 69 ++++++++++++------------
 src/tools/live-utils/start-session.ts    | 46 ++++++++--------
 2 files changed, 56 insertions(+), 59 deletions(-)

diff --git a/src/tools/applive-utils/start-session.ts b/src/tools/applive-utils/start-session.ts
index 8d86c30..cb499e6 100644
--- a/src/tools/applive-utils/start-session.ts
+++ b/src/tools/applive-utils/start-session.ts
@@ -1,5 +1,4 @@
 import logger from "../../logger.js";
-import childProcess from "child_process";
 import {
   getDevicesAndBrowsers,
   BrowserStackProducts,
@@ -93,40 +92,40 @@ export async function startSession(
   });
   const launchUrl = `https://app-live.browserstack.com/dashboard#${params.toString()}&device=${deviceParam}`;
 
-  openBrowser(launchUrl);
+  // openBrowser(launchUrl);
   return launchUrl + note;
 }
 
-/**
- * Opens the launch URL in the default browser.
- * @param launchUrl - The URL to open.
- * @throws Will throw an error if the browser fails to open.
- */
-function openBrowser(launchUrl: string): void {
-  try {
-    const command =
-      process.platform === "darwin"
-        ? ["open", launchUrl]
-        : process.platform === "win32"
-          ? ["cmd", "/c", "start", launchUrl]
-          : ["xdg-open", launchUrl];
-
-    // nosemgrep:javascript.lang.security.detect-child-process.detect-child-process
-    const child = childProcess.spawn(command[0], command.slice(1), {
-      stdio: "ignore",
-      detached: true,
-    });
-
-    child.on("error", (error) => {
-      logger.error(
-        `Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`,
-      );
-    });
-
-    child.unref();
-  } catch (error) {
-    logger.error(
-      `Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`,
-    );
-  }
-}
+// /**
+//  * Opens the launch URL in the default browser.
+//  * @param launchUrl - The URL to open.
+//  * @throws Will throw an error if the browser fails to open.
+//  */
+// function openBrowser(launchUrl: string): void {
+//   try {
+//     const command =
+//       process.platform === "darwin"
+//         ? ["open", launchUrl]
+//         : process.platform === "win32"
+//           ? ["cmd", "/c", "start", launchUrl]
+//           : ["xdg-open", launchUrl];
+
+//     // nosemgrep:javascript.lang.security.detect-child-process.detect-child-process
+//     const child = childProcess.spawn(command[0], command.slice(1), {
+//       stdio: "ignore",
+//       detached: true,
+//     });
+
+//     child.on("error", (error) => {
+//       logger.error(
+//         `Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`,
+//       );
+//     });
+
+//     child.unref();
+//   } catch (error) {
+//     logger.error(
+//       `Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`,
+//     );
+//   }
+// }
diff --git a/src/tools/live-utils/start-session.ts b/src/tools/live-utils/start-session.ts
index a0b9b91..9df52d1 100644
--- a/src/tools/live-utils/start-session.ts
+++ b/src/tools/live-utils/start-session.ts
@@ -1,5 +1,3 @@
-import logger from "../../logger.js";
-import childProcess from "child_process";
 import { filterDesktop } from "./desktop-filter.js";
 import { filterMobile } from "./mobile-filter.js";
 import {
@@ -58,7 +56,7 @@ export async function startBrowserSession(
         )
       : buildMobileUrl(args as MobileSearchArgs, entry as MobileEntry, isLocal);
 
-  openBrowser(url);
+  // openBrowser(url);
   return entry.notes ? `${url}, ${entry.notes}` : url;
 }
 
@@ -110,25 +108,25 @@ function buildMobileUrl(
 
 // ——— Open a browser window ———
 
-function openBrowser(launchUrl: string): void {
-  try {
-    const command =
-      process.platform === "darwin"
-        ? ["open", launchUrl]
-        : process.platform === "win32"
-          ? ["cmd", "/c", "start", launchUrl]
-          : ["xdg-open", launchUrl];
+// function openBrowser(launchUrl: string): void {
+//   try {
+//     const command =
+//       process.platform === "darwin"
+//         ? ["open", launchUrl]
+//         : process.platform === "win32"
+//           ? ["cmd", "/c", "start", launchUrl]
+//           : ["xdg-open", launchUrl];
 
-    // nosemgrep:javascript.lang.security.detect-child-process.detect-child-process
-    const child = childProcess.spawn(command[0], command.slice(1), {
-      stdio: "ignore",
-      detached: true,
-    });
-    child.on("error", (err) =>
-      logger.error(`Failed to open browser: ${err}. URL: ${launchUrl}`),
-    );
-    child.unref();
-  } catch (err) {
-    logger.error(`Failed to launch browser: ${err}. URL: ${launchUrl}`);
-  }
-}
+//     // nosemgrep:javascript.lang.security.detect-child-process.detect-child-process
+//     const child = childProcess.spawn(command[0], command.slice(1), {
+//       stdio: "ignore",
+//       detached: true,
+//     });
+//     child.on("error", (err) =>
+//       logger.error(`Failed to open browser: ${err}. URL: ${launchUrl}`),
+//     );
+//     child.unref();
+//   } catch (err) {
+//     logger.error(`Failed to launch browser: ${err}. URL: ${launchUrl}`);
+//   }
+// }

From e768467e2daebf3b16f6f2351cef820060e8f7ca Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 1 Jul 2025 14:20:51 +0530
Subject: [PATCH 14/64] Remove unused import of getSDKPrefixCommand from
 bstack-sdk.ts

---
 src/tools/accessiblity-utils/scanner.ts |  1 +
 src/tools/bstack-sdk.ts                 |  1 -
 src/tools/sdk-utils/constants.ts        | 11 +++++++----
 3 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/src/tools/accessiblity-utils/scanner.ts b/src/tools/accessiblity-utils/scanner.ts
index 59820c9..6962fe1 100644
--- a/src/tools/accessiblity-utils/scanner.ts
+++ b/src/tools/accessiblity-utils/scanner.ts
@@ -2,6 +2,7 @@ import axios from "axios";
 import { randomUUID } from "node:crypto";
 import logger from "../../logger.js";
 import { isLocalURL } from "../../lib/local.js";
+import config from "../../config.js";
 
 export interface AccessibilityScanResponse {
   success: boolean;
diff --git a/src/tools/bstack-sdk.ts b/src/tools/bstack-sdk.ts
index 3af8138..f50eb45 100644
--- a/src/tools/bstack-sdk.ts
+++ b/src/tools/bstack-sdk.ts
@@ -23,7 +23,6 @@ import {
   formatPercyInstructions,
   getPercyInstructions,
 } from "./sdk-utils/percy/instructions.js";
-import { getSDKPrefixCommand } from "./sdk-utils/commands.js";
 import { getBrowserStackAuth } from "../lib/get-auth.js";
 
 /**
diff --git a/src/tools/sdk-utils/constants.ts b/src/tools/sdk-utils/constants.ts
index efcb85d..1f1f298 100644
--- a/src/tools/sdk-utils/constants.ts
+++ b/src/tools/sdk-utils/constants.ts
@@ -131,7 +131,7 @@ dotnet build
 
 Set up BrowserStack SDK with your credentials:
 \`\`\`bash
-dotnet browserstack-sdk setup --userName ${config.browserstackUsername} --accessKey ${config.browserstackAccessKey}
+dotnet browserstack-sdk setup --userName ${username} --accessKey ${accessKey}
 \`\`\`
 
 ---STEP---
@@ -174,7 +174,7 @@ Run the tests:
   \`\`\`
 `;
 
-const csharpPlaywrightCommonInstructions = `
+const csharpPlaywrightCommonInstructions  = (username: string, accessKey: string) => `
 ---STEP---
 
 Install BrowserStack TestAdapter NuGet package:
@@ -193,7 +193,7 @@ dotnet build
 
 Set up BrowserStack SDK with your credentials:
 \`\`\`bash
-dotnet browserstack-sdk setup --userName ${config.browserstackUsername} --accessKey ${config.browserstackAccessKey}
+dotnet browserstack-sdk setup --userName ${username} --accessKey ${accessKey}
 \`\`\`
 
 ---STEP---
@@ -254,7 +254,6 @@ Run the tests:
  */
 
 const nodejsInstructions = (username: string, accessKey: string) => `
-const nodejsInstructions = `
 ---STEP---
 
 Ensure \`browserstack-node-sdk\` is present in package.json with the latest version:
@@ -275,6 +274,10 @@ Add new scripts to package.json for running tests on BrowserStack:
 
 Export BrowserStack credentials as environment variables:
 Set the following environment variables before running tests.
+\`\`\`bash
+export BROWSERSTACK_USERNAME=${username}
+export BROWSERSTACK_ACCESS_KEY=${accessKey}
+\`\`\`
 `;
 
 /**

From 776849363a16ba97ef83364bfcd65276d27db366 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 1 Jul 2025 14:25:04 +0530
Subject: [PATCH 15/64] Refactor instruction functions to remove unused
 parameters and clean up code

---
 src/tools/sdk-utils/constants.ts | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/tools/sdk-utils/constants.ts b/src/tools/sdk-utils/constants.ts
index 1f1f298..9909cf1 100644
--- a/src/tools/sdk-utils/constants.ts
+++ b/src/tools/sdk-utils/constants.ts
@@ -63,7 +63,6 @@ const pytestInstructions = generatePythonFrameworkInstructions("pytest");
 const argsInstruction =
   '<argLine>-javaagent:"${com.browserstack:browserstack-java-sdk:jar}"</argLine>';
 
-
 const javaInstructions = (username: string, accessKey: string) => `
 ---STEP---
 
@@ -111,7 +110,6 @@ gradle clean test
  * ---------- CSharp INSTRUCTIONS ----------
  */
 
-
 const csharpCommonInstructions = (username: string, accessKey: string) => `
 ---STEP---
 
@@ -174,7 +172,10 @@ Run the tests:
   \`\`\`
 `;
 
-const csharpPlaywrightCommonInstructions  = (username: string, accessKey: string) => `
+const csharpPlaywrightCommonInstructions = (
+  username: string,
+  accessKey: string,
+) => `
 ---STEP---
 
 Install BrowserStack TestAdapter NuGet package:
@@ -390,7 +391,6 @@ Run your tests:
 You can now run your tests on BrowserStack using your standard WebdriverIO command.
 `;
 
-
 const cypressInstructions = (username: string, accessKey: string) => `
 ---STEP---
 

From e82d52fb40e6821a45bf5f5e414ce7993c519ce9 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Wed, 2 Jul 2025 09:32:46 +0530
Subject: [PATCH 16/64] Update project name in YML instructions for clarity

---
 src/tools/sdk-utils/instructions.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/sdk-utils/instructions.ts b/src/tools/sdk-utils/instructions.ts
index 902bed9..861a8d9 100644
--- a/src/tools/sdk-utils/instructions.ts
+++ b/src/tools/sdk-utils/instructions.ts
@@ -47,7 +47,7 @@ export function generateBrowserStackYMLInstructions(
 # BrowserStack Reporting
 # ======================
 # A single name for your project to organize all your tests. This is required for Percy.
-projectName: BrowserStack Sample
+projectName: BrowserStack SDK Tests
 # A name for the group of tests you are running
 buildName: mcp-run
 

From c29089ec8abeb76129c2b0edcecc9d268334095d Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Wed, 2 Jul 2025 13:16:53 +0530
Subject: [PATCH 17/64] Update tool description for clarity on BrowserStack SDK
 usage

---
 src/tools/bstack-sdk.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/bstack-sdk.ts b/src/tools/bstack-sdk.ts
index 7e9d723..44c37d7 100644
--- a/src/tools/bstack-sdk.ts
+++ b/src/tools/bstack-sdk.ts
@@ -153,7 +153,7 @@ function formatFinalInstructions(combinedInstructions: string): CallToolResult {
 export default function addSDKTools(server: McpServer) {
   server.tool(
     "runTestsOnBrowserStack",
-    "Use this tool to get instructions for running tests on BrowserStack and browserstack percy",
+    "Use this tool to get instructions for running tests on BrowserStack and BrowserStack Percy. It sets up the BrowserStack SDK and runs your test cases on BrowserStack.",
     {
       detectedBrowserAutomationFramework: z
         .nativeEnum(SDKSupportedBrowserAutomationFrameworkEnum)

From 96b8b8de0078781297025292292f6576471dddc8 Mon Sep 17 00:00:00 2001
From: tech-sushant <sushant.s@browserstack.com>
Date: Thu, 3 Jul 2025 00:42:12 +0530
Subject: [PATCH 18/64] feat: update  setup instructions

---
 src/tools/sdk-utils/constants.ts | 7 +++++--
 src/tools/sdk-utils/types.ts     | 1 +
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/tools/sdk-utils/constants.ts b/src/tools/sdk-utils/constants.ts
index 468a9f7..00ff40b 100644
--- a/src/tools/sdk-utils/constants.ts
+++ b/src/tools/sdk-utils/constants.ts
@@ -144,7 +144,7 @@ fi
 
 ---STEP---
 
-macOS (Apple Silicon) setup (required only if arm64 detected):
+macOS (Apple Silicon) setup (Strictly follow if arm64 detected):
 Install the x64 version of .NET for BrowserStack compatibility.
 
 - Check your current .NET version:
@@ -153,6 +153,7 @@ Install the x64 version of .NET for BrowserStack compatibility.
   \`\`\`
 
 - Ensure the path exists strictly; if not, create it first and then run the setup:
+  This automatically installs the x64 version of .NET in the specified path. No need to install it from external sources.
   \`\`\`bash
   sudo dotnet browserstack-sdk setup-dotnet --dotnet-path "<your-chosen-path>" --dotnet-version "<your-dotnet-version>"
   \`\`\`
@@ -161,7 +162,7 @@ Install the x64 version of .NET for BrowserStack compatibility.
 ---STEP---
 
 Run the tests:
-- For macOS (Apple Silicon), use the full path:
+- For macOS (Apple Silicon), use the full path where the x64 version of .NET is installed:
   \`\`\`bash
   <your-chosen-path>/dotnet browserstack-sdk
   \`\`\`
@@ -221,6 +222,7 @@ Install the x64 version of .NET for compatibility with BrowserStack.
   \`\`\`
 
 - Ensure the path exists strictly; if not, create it first and then run the setup:
+  This automatically installs the x64 version of .NET in the specified path. No need to install it from external sources.
   \`\`\`bash
   sudo dotnet browserstack-sdk setup-dotnet --dotnet-path "<your-chosen-path>" --dotnet-version "<your-dotnet-version>"
   \`\`\`
@@ -489,6 +491,7 @@ export const SUPPORTED_CONFIGURATIONS: ConfigMapping = {
       xunit: { instructions: csharpCommonInstructions },
       nunit: { instructions: csharpCommonInstructions },
       mstest: { instructions: csharpCommonInstructions },
+      specflow: { instructions: csharpCommonInstructions },
     },
   },
   nodejs: {
diff --git a/src/tools/sdk-utils/types.ts b/src/tools/sdk-utils/types.ts
index 3e3ed44..dbcd1af 100644
--- a/src/tools/sdk-utils/types.ts
+++ b/src/tools/sdk-utils/types.ts
@@ -32,6 +32,7 @@ export enum SDKSupportedTestingFrameworkEnum {
   nunit = "nunit",
   mstest = "mstest",
   xunit = "xunit",
+  specflow = "specflow",
 }
 export type SDKSupportedTestingFramework =
   keyof typeof SDKSupportedTestingFrameworkEnum;

From 3ad82e20fe3656a8caa725060c13bd6a02c93a63 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 10:56:03 +0530
Subject: [PATCH 19/64] Refactor test management utilities to use
 BrowserStackConfig instead of server parameter

- Updated listTestCases, listTestRuns, pollLCAStatus, createTestCasesFromFile, updateTestRun, uploadFile, and other related functions to accept BrowserStackConfig.
- Modified the corresponding test cases to reflect the changes in function signatures and ensure proper configuration handling.
- Enhanced error handling and logging for better debugging and traceability.
---
 src/index.ts                                  |   8 +-
 src/lib/api.ts                                |   4 +-
 src/lib/get-auth.ts                           |  10 +-
 src/lib/instrumentation.ts                    |  15 +-
 src/lib/types.ts                              |   4 +
 src/oninitialized.ts                          |   4 +-
 src/server-factory.ts                         |  28 +--
 src/tools/accessibility.ts                    |  30 ++-
 .../accessiblity-utils/accessibility-rag.ts   |   5 +-
 src/tools/appautomate.ts                      |  28 ++-
 src/tools/applive-utils/start-session.ts      |   6 +-
 src/tools/applive.ts                          |  22 +-
 src/tools/automate-utils/fetch-screenshots.ts |  11 +-
 src/tools/automate.ts                         |  15 +-
 src/tools/bstack-sdk.ts                       |  20 +-
 src/tools/failurelogs-utils/app-automate.ts   |  13 +-
 src/tools/failurelogs-utils/automate.ts       |  13 +-
 src/tools/getFailureLogs.ts                   |  36 ++-
 src/tools/live-utils/start-session.ts         |   6 +-
 src/tools/live.ts                             |  26 ++-
 src/tools/observability.ts                    |  20 +-
 src/tools/selfheal-utils/selfheal.ts          |   8 +-
 src/tools/selfheal.ts                         |  20 +-
 .../testmanagement-utils/TCG-utils/api.ts     |  37 +--
 .../testmanagement-utils/add-test-result.ts   |   5 +-
 .../testmanagement-utils/create-lca-steps.ts  |  11 +-
 .../create-project-folder.ts                  |   9 +-
 .../testmanagement-utils/create-testcase.ts   |   7 +-
 .../testmanagement-utils/create-testrun.ts    |   5 +-
 .../testmanagement-utils/list-testcases.ts    |   5 +-
 .../testmanagement-utils/list-testruns.ts     |   5 +-
 .../testmanagement-utils/poll-lca-status.ts   |   5 +-
 .../testcase-from-file.ts                     |  13 +-
 .../testmanagement-utils/update-testrun.ts    |   5 +-
 src/tools/testmanagement-utils/upload-file.ts |   7 +-
 src/tools/testmanagement.ts                   | 144 ++++++++----
 tests/tools/applive.test.ts                   |  44 ++--
 tests/tools/testmanagement.test.ts            | 217 +++++++++---------
 38 files changed, 518 insertions(+), 353 deletions(-)
 create mode 100644 src/lib/types.ts

diff --git a/src/index.ts b/src/index.ts
index 7fab623..de52ec2 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -105,13 +105,11 @@ app.post("/mcp", async (req, res) => {
       };
 
       // Create and connect MCP server
-      const server = createMcpServer() as McpServer & {
-        authHeaders?: Record<string, string>;
-      };
-      server.authHeaders = {
+      const server = createMcpServer({
         "browserstack-username": browserstackUsername,
         "browserstack-access-key": browserstackAccessKey,
-      };
+      }) as McpServer;
+
       await server.connect(transport);
 
       logger.info("MCP server connected to transport");
diff --git a/src/lib/api.ts b/src/lib/api.ts
index f5f6606..06b5a0f 100644
--- a/src/lib/api.ts
+++ b/src/lib/api.ts
@@ -3,13 +3,13 @@ import { getBrowserStackAuth } from "./get-auth.js";
 export async function getLatestO11YBuildInfo(
   buildName: string,
   projectName: string,
-  server: any,
+  config: any,
 ) {
   const buildsUrl = `https://api-observability.browserstack.com/ext/v1/builds/latest?build_name=${encodeURIComponent(
     buildName,
   )}&project_name=${encodeURIComponent(projectName)}`;
 
-  const authString = getBrowserStackAuth(server);
+  const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
   const buildsResponse = await fetch(buildsUrl, {
diff --git a/src/lib/get-auth.ts b/src/lib/get-auth.ts
index 88e82c6..074e141 100644
--- a/src/lib/get-auth.ts
+++ b/src/lib/get-auth.ts
@@ -1,8 +1,8 @@
-export function getBrowserStackAuth(server: {
-  authHeaders?: Record<string, string>;
-}) {
-  const username = server.authHeaders?.["browserstack-username"];
-  const accessKey = server.authHeaders?.["browserstack-access-key"];
+import { BrowserStackConfig } from "../lib/types.js";
+
+export function getBrowserStackAuth(config: BrowserStackConfig): string {
+  const username = config["browserstack-username"];
+  const accessKey = config["browserstack-access-key"];
   if (!username || !accessKey) {
     throw new Error("BrowserStack credentials not set on server.authHeaders");
   }
diff --git a/src/lib/instrumentation.ts b/src/lib/instrumentation.ts
index 8f1376f..59e5924 100644
--- a/src/lib/instrumentation.ts
+++ b/src/lib/instrumentation.ts
@@ -1,6 +1,5 @@
 import logger from "../logger.js";
 import { getBrowserStackAuth } from "./get-auth.js";
-import config from "../config.js";
 import { createRequire } from "module";
 const require = createRequire(import.meta.url);
 const packageJson = require("../../package.json");
@@ -22,13 +21,8 @@ export function trackMCP(
   toolName: string,
   clientInfo: { name?: string; version?: string },
   error?: unknown,
-  server?: any,
+  config?: any,
 ): void {
-  if (config.DEV_MODE) {
-    logger.info("Tracking MCP is disabled in dev mode");
-    return;
-  }
-
   const instrumentationEndpoint = "https://api.browserstack.com/sdk/v1/event";
   const isSuccess = !error;
   const mcpClient = clientInfo?.name || "unknown";
@@ -61,12 +55,9 @@ export function trackMCP(
   }
 
   let authHeader = undefined;
-  if (server) {
-    const authString = getBrowserStackAuth(server);
+  if (config) {
+    const authString = getBrowserStackAuth(config);
     authHeader = `Basic ${Buffer.from(authString).toString("base64")}`;
-  } else {
-    // fallback for legacy usage
-    authHeader = undefined;
   }
 
   axios
diff --git a/src/lib/types.ts b/src/lib/types.ts
new file mode 100644
index 0000000..6de14c2
--- /dev/null
+++ b/src/lib/types.ts
@@ -0,0 +1,4 @@
+export type BrowserStackConfig = {
+  "browserstack-username": string;
+  "browserstack-access-key": string;
+};
diff --git a/src/oninitialized.ts b/src/oninitialized.ts
index 4117c09..ec3c74c 100644
--- a/src/oninitialized.ts
+++ b/src/oninitialized.ts
@@ -1,7 +1,7 @@
 import { trackMCP } from "./lib/instrumentation.js";
 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 
-export function setupOnInitialized(server: McpServer) {
+export function setupOnInitialized(server: McpServer, config?: any) {
   const nodeVersion = process.versions.node;
 
   // Check for Node.js version
@@ -12,6 +12,6 @@ export function setupOnInitialized(server: McpServer) {
   }
 
   server.server.oninitialized = () => {
-    trackMCP("started", server.server.getClientVersion()!);
+    trackMCP("started", server.server.getClientVersion()!, undefined, config);
   };
 }
diff --git a/src/server-factory.ts b/src/server-factory.ts
index a79704f..d471467 100644
--- a/src/server-factory.ts
+++ b/src/server-factory.ts
@@ -11,21 +11,23 @@ import addAppAutomationTools from "./tools/appautomate.js";
 import addFailureLogsTools from "./tools/getFailureLogs.js";
 import addAutomateTools from "./tools/automate.js";
 import addSelfHealTools from "./tools/selfheal.js";
+import addAppLiveTools from "./tools/applive.js";
 import { setupOnInitialized } from "./oninitialized.js";
+import { BrowserStackConfig } from "./lib/types.js";
 
-function registerTools(server: McpServer) {
-  addAccessibilityTools(server); //done
-  addSDKTools(server);
-  //   addAppLiveTools(server); //done
-  addBrowserLiveTools(server); //done
-  addTestManagementTools(server); //done
-  addAppAutomationTools(server); //done
-  addFailureLogsTools(server); //done
-  addAutomateTools(server); //done
-  addSelfHealTools(server); //done
+function registerTools(server: McpServer, config: BrowserStackConfig) {
+  addAccessibilityTools(server, config);
+  addSDKTools(server, config);
+  addAppLiveTools(server, config);
+  addBrowserLiveTools(server, config);
+  addTestManagementTools(server, config);
+  addAppAutomationTools(server, config);
+  addFailureLogsTools(server, config);
+  addAutomateTools(server, config);
+  addSelfHealTools(server, config);
 }
 
-export function createMcpServer(): McpServer {
+export function createMcpServer(config: BrowserStackConfig): McpServer {
   logger.info(
     "Creating BrowserStack MCP Server, version %s",
     packageJson.version,
@@ -37,8 +39,8 @@ export function createMcpServer(): McpServer {
     version: packageJson.version,
   });
 
-  setupOnInitialized(server);
-  registerTools(server);
+  setupOnInitialized(server, config);
+  registerTools(server, config);
 
   return server;
 }
diff --git a/src/tools/accessibility.ts b/src/tools/accessibility.ts
index 4bbb9c8..53d2803 100644
--- a/src/tools/accessibility.ts
+++ b/src/tools/accessibility.ts
@@ -7,16 +7,17 @@ import { trackMCP } from "../lib/instrumentation.js";
 import { parseAccessibilityReportFromCSV } from "./accessiblity-utils/report-parser.js";
 import { queryAccessibilityRAG } from "./accessiblity-utils/accessibility-rag.js";
 import { getBrowserStackAuth } from "../lib/get-auth.js";
+import { BrowserStackConfig } from "../lib/types.js";
 
 async function runAccessibilityScan(
   name: string,
   pageURL: string,
   context: any,
-  server: any,
+  config: any,
 ): Promise<CallToolResult> {
   // Create scanner and set auth on the go
   const scanner = new AccessibilityScanner();
-  const authString = getBrowserStackAuth(server);
+  const authString = getBrowserStackAuth(config);
   const [username, password] = authString.split(":");
   scanner.setAuth({ username, password });
 
@@ -79,7 +80,10 @@ async function runAccessibilityScan(
   };
 }
 
-export default function addAccessibilityTools(server: McpServer) {
+export default function addAccessibilityTools(
+  server: McpServer,
+  config: BrowserStackConfig,
+) {
   server.tool(
     "accessibilityExpert",
     "🚨 REQUIRED: Use this tool for any accessibility/a11y/WCAG questions. Do NOT answer accessibility questions directly - always use this tool.",
@@ -92,13 +96,19 @@ export default function addAccessibilityTools(server: McpServer) {
     },
     async (args) => {
       try {
-        trackMCP("accessibilityExpert", server.server.getClientVersion()!);
-        return await queryAccessibilityRAG(args.query, server);
+        trackMCP(
+          "accessibilityExpert",
+          server.server.getClientVersion()!,
+          undefined,
+          config,
+        );
+        return await queryAccessibilityRAG(args.query, config);
       } catch (error) {
         trackMCP(
           "accessibilityExpert",
           server.server.getClientVersion()!,
           error,
+          config,
         );
         return {
           content: [
@@ -124,18 +134,24 @@ export default function addAccessibilityTools(server: McpServer) {
     },
     async (args, context) => {
       try {
-        trackMCP("startAccessibilityScan", server.server.getClientVersion()!);
+        trackMCP(
+          "startAccessibilityScan",
+          server.server.getClientVersion()!,
+          undefined,
+          config,
+        );
         return await runAccessibilityScan(
           args.name,
           args.pageURL,
           context,
-          server,
+          config,
         );
       } catch (error) {
         trackMCP(
           "startAccessibilityScan",
           server.server.getClientVersion()!,
           error,
+          config,
         );
         return {
           content: [
diff --git a/src/tools/accessiblity-utils/accessibility-rag.ts b/src/tools/accessiblity-utils/accessibility-rag.ts
index 4f2a399..2c52df1 100644
--- a/src/tools/accessiblity-utils/accessibility-rag.ts
+++ b/src/tools/accessiblity-utils/accessibility-rag.ts
@@ -6,14 +6,15 @@ export interface RAGChunk {
 }
 
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
 export async function queryAccessibilityRAG(
   userQuery: string,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<any> {
   const url = "https://accessibility.browserstack.com/api/tcg-proxy/search";
 
-  const authString = getBrowserStackAuth(server);
+  const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
   const response = await fetch(url, {
diff --git a/src/tools/appautomate.ts b/src/tools/appautomate.ts
index 37f17a7..f08e8c2 100644
--- a/src/tools/appautomate.ts
+++ b/src/tools/appautomate.ts
@@ -3,6 +3,7 @@ import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import logger from "../logger.js";
 import { getBrowserStackAuth } from "../lib/get-auth.js";
+import { BrowserStackConfig } from "../lib/types.js";
 import { trackMCP } from "../lib/instrumentation.js";
 import { maybeCompressBase64 } from "../lib/utils.js";
 import { remote } from "webdriverio";
@@ -47,12 +48,12 @@ async function takeAppScreenshot(args: {
   desiredPlatformVersion: string;
   appPath: string;
   desiredPhone: string;
-  server: any;
+  config: BrowserStackConfig;
 }): Promise<CallToolResult> {
   let driver;
   try {
     validateArgs(args);
-    const { desiredPlatform, desiredPhone, appPath, server } = args;
+    const { desiredPlatform, desiredPhone, appPath, config } = args;
     let { desiredPlatformVersion } = args;
 
     const platforms = (
@@ -87,7 +88,7 @@ async function takeAppScreenshot(args: {
         `Device "${desiredPhone}" with version ${desiredPlatformVersion} not found.`,
       );
     }
-    const authString = getBrowserStackAuth(server);
+    const authString = getBrowserStackAuth(config);
     const [username, password] = authString.split(":");
 
     const app_url = await uploadApp(appPath, username, password);
@@ -149,7 +150,10 @@ async function takeAppScreenshot(args: {
 /**
  * Registers the `takeAppScreenshot` tool with the MCP server.
  */
-export default function addAppAutomationTools(server: McpServer) {
+export default function addAppAutomationTools(
+  server: McpServer,
+  config: BrowserStackConfig,
+) {
   server.tool(
     "takeAppScreenshot",
     "Use this tool to take a screenshot of an app running on a BrowserStack device. This is useful for visual testing and debugging.",
@@ -175,10 +179,20 @@ export default function addAppAutomationTools(server: McpServer) {
     },
     async (args) => {
       try {
-        trackMCP("takeAppScreenshot", server.server.getClientVersion()!);
-        return await takeAppScreenshot({ ...args, server });
+        trackMCP(
+          "takeAppScreenshot",
+          server.server.getClientVersion()!,
+          undefined,
+          config,
+        );
+        return await takeAppScreenshot({ ...args, config });
       } catch (error) {
-        trackMCP("takeAppScreenshot", server.server.getClientVersion()!, error);
+        trackMCP(
+          "takeAppScreenshot",
+          server.server.getClientVersion()!,
+          error,
+          config,
+        );
         const errorMessage =
           error instanceof Error ? error.message : "Unknown error";
         return {
diff --git a/src/tools/applive-utils/start-session.ts b/src/tools/applive-utils/start-session.ts
index cb499e6..1179f0b 100644
--- a/src/tools/applive-utils/start-session.ts
+++ b/src/tools/applive-utils/start-session.ts
@@ -18,7 +18,7 @@ interface StartSessionArgs {
 }
 
 interface StartSessionOptions {
-  server: any;
+  config: any;
 }
 
 /**
@@ -30,7 +30,7 @@ export async function startSession(
 ): Promise<string> {
   const { appPath, desiredPlatform, desiredPhone, desiredPlatformVersion } =
     args;
-  const { server } = options;
+  const { config } = options;
 
   // 1) Fetch devices for APP_LIVE
   const data = await getDevicesAndBrowsers(BrowserStackProducts.APP_LIVE);
@@ -69,7 +69,7 @@ export async function startSession(
   }
 
   // 6) Upload app
-  const authString = getBrowserStackAuth(server);
+  const authString = getBrowserStackAuth(config);
   const [username, password] = authString.split(":");
   const { app_url } = await uploadApp(appPath, username, password);
   logger.info(`App uploaded: ${app_url}`);
diff --git a/src/tools/applive.ts b/src/tools/applive.ts
index 0293d9e..d0d40ba 100644
--- a/src/tools/applive.ts
+++ b/src/tools/applive.ts
@@ -16,7 +16,7 @@ export async function startAppLiveSession(
     appPath: string;
     desiredPhone: string;
   },
-  server: any,
+  config: any,
 ): Promise<CallToolResult> {
   if (!args.desiredPlatform) {
     throw new Error("You must provide a desiredPlatform.");
@@ -56,7 +56,7 @@ export async function startAppLiveSession(
       desiredPhone: args.desiredPhone,
       desiredPlatformVersion: args.desiredPlatformVersion,
     },
-    { server },
+    { config },
   );
 
   return {
@@ -69,7 +69,7 @@ export async function startAppLiveSession(
   };
 }
 
-export default function addAppLiveTools(server: McpServer) {
+export default function addAppLiveTools(server: McpServer, config: any) {
   server.tool(
     "runAppLiveSession",
     "Use this tool when user wants to manually check their app on a particular mobile device using BrowserStack's cloud infrastructure. Can be used to debug crashes, slow performance, etc.",
@@ -97,11 +97,21 @@ export default function addAppLiveTools(server: McpServer) {
     },
     async (args) => {
       try {
-        trackMCP("runAppLiveSession", server.server.getClientVersion()!);
-        return await startAppLiveSession(args, server);
+        trackMCP(
+          "runAppLiveSession",
+          server.server.getClientVersion()!,
+          undefined,
+          config,
+        );
+        return await startAppLiveSession(args, config);
       } catch (error) {
         logger.error("App live session failed: %s", error);
-        trackMCP("runAppLiveSession", server.server.getClientVersion()!, error);
+        trackMCP(
+          "runAppLiveSession",
+          server.server.getClientVersion()!,
+          error,
+          config,
+        );
         return {
           content: [
             {
diff --git a/src/tools/automate-utils/fetch-screenshots.ts b/src/tools/automate-utils/fetch-screenshots.ts
index bf8cdf7..d1f5e91 100644
--- a/src/tools/automate-utils/fetch-screenshots.ts
+++ b/src/tools/automate-utils/fetch-screenshots.ts
@@ -1,15 +1,14 @@
 import { assertOkResponse, maybeCompressBase64 } from "../../lib/utils.js";
 import { SessionType } from "../../lib/constants.js";
-
-//Extracts screenshot URLs from BrowserStack session logs
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
 async function extractScreenshotUrls(
   sessionId: string,
   sessionType: SessionType,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<string[]> {
-  const authString = getBrowserStackAuth(server);
+  const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
   const baseUrl = `https://api.browserstack.com/${sessionType === SessionType.Automate ? "automate" : "app-automate"}`;
@@ -75,9 +74,9 @@ async function convertUrlsToBase64(
 export async function fetchAutomationScreenshots(
   sessionId: string,
   sessionType: SessionType = SessionType.Automate,
-  server: any,
+  config: BrowserStackConfig,
 ) {
-  const urls = await extractScreenshotUrls(sessionId, sessionType, server);
+  const urls = await extractScreenshotUrls(sessionId, sessionType, config);
   if (urls.length === 0) {
     return [];
   }
diff --git a/src/tools/automate.ts b/src/tools/automate.ts
index 88e31d4..340eff5 100644
--- a/src/tools/automate.ts
+++ b/src/tools/automate.ts
@@ -5,6 +5,7 @@ import { fetchAutomationScreenshots } from "./automate-utils/fetch-screenshots.j
 import { SessionType } from "../lib/constants.js";
 import { trackMCP } from "../lib/instrumentation.js";
 import logger from "../logger.js";
+import { BrowserStackConfig } from "../lib/types.js";
 
 // Tool function that fetches and processes screenshots from BrowserStack Automate session
 export async function fetchAutomationScreenshotsTool(
@@ -12,13 +13,13 @@ export async function fetchAutomationScreenshotsTool(
     sessionId: string;
     sessionType: SessionType;
   },
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   try {
     const screenshots = await fetchAutomationScreenshots(
       args.sessionId,
       args.sessionType,
-      server,
+      config,
     );
 
     if (screenshots.length === 0) {
@@ -57,7 +58,10 @@ export async function fetchAutomationScreenshotsTool(
 }
 
 //Registers the fetchAutomationScreenshots tool with the MCP server
-export default function addAutomationTools(server: McpServer) {
+export default function addAutomationTools(
+  server: McpServer,
+  config: BrowserStackConfig,
+) {
   server.tool(
     "fetchAutomationScreenshots",
     "Fetch and process screenshots from a BrowserStack Automate session",
@@ -74,13 +78,16 @@ export default function addAutomationTools(server: McpServer) {
         trackMCP(
           "fetchAutomationScreenshots",
           server.server.getClientVersion()!,
+          undefined,
+          config,
         );
-        return await fetchAutomationScreenshotsTool(args, server);
+        return await fetchAutomationScreenshotsTool(args, config);
       } catch (error) {
         trackMCP(
           "fetchAutomationScreenshots",
           server.server.getClientVersion()!,
           error,
+          config,
         );
         const errorMessage =
           error instanceof Error ? error.message : "Unknown error";
diff --git a/src/tools/bstack-sdk.ts b/src/tools/bstack-sdk.ts
index f50eb45..a441a80 100644
--- a/src/tools/bstack-sdk.ts
+++ b/src/tools/bstack-sdk.ts
@@ -35,17 +35,17 @@ export async function bootstrapProjectWithSDK({
   detectedLanguage,
   desiredPlatforms,
   enablePercy,
-  server,
+  config,
 }: {
   detectedBrowserAutomationFramework: SDKSupportedBrowserAutomationFramework;
   detectedTestingFramework: SDKSupportedTestingFramework;
   detectedLanguage: SDKSupportedLanguage;
   desiredPlatforms: string[];
   enablePercy: boolean;
-  server: any;
+  config: any;
 }): Promise<CallToolResult> {
-  // Get credentials from server
-  const authString = getBrowserStackAuth(server);
+  // Get credentials from config
+  const authString = getBrowserStackAuth(config);
   const [username, accessKey] = authString.split(":");
 
   // Handle frameworks with unique setup instructions that don't use browserstack.yml
@@ -160,7 +160,7 @@ function formatFinalInstructions(combinedInstructions: string): CallToolResult {
   };
 }
 
-export default function addSDKTools(server: McpServer) {
+export default function addSDKTools(server: McpServer, config: any) {
   server.tool(
     "runTestsOnBrowserStack",
     "Use this tool to get instructions for running tests on BrowserStack and browserstack percy",
@@ -200,7 +200,12 @@ export default function addSDKTools(server: McpServer) {
 
     async (args) => {
       try {
-        trackMCP("runTestsOnBrowserStack", server.server.getClientVersion()!);
+        trackMCP(
+          "runTestsOnBrowserStack",
+          server.server.getClientVersion()!,
+          undefined,
+          config,
+        );
 
         return await bootstrapProjectWithSDK({
           detectedBrowserAutomationFramework:
@@ -213,13 +218,14 @@ export default function addSDKTools(server: McpServer) {
 
           desiredPlatforms: args.desiredPlatforms,
           enablePercy: args.enablePercy,
-          server,
+          config,
         });
       } catch (error) {
         trackMCP(
           "runTestsOnBrowserStack",
           server.server.getClientVersion()!,
           error,
+          config,
         );
 
         return {
diff --git a/src/tools/failurelogs-utils/app-automate.ts b/src/tools/failurelogs-utils/app-automate.ts
index fb1becf..ce8e159 100644
--- a/src/tools/failurelogs-utils/app-automate.ts
+++ b/src/tools/failurelogs-utils/app-automate.ts
@@ -1,14 +1,15 @@
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { filterLinesByKeywords, validateLogResponse } from "./utils.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
 // DEVICE LOGS
 export async function retrieveDeviceLogs(
   sessionId: string,
   buildId: string,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<string> {
   const url = `https://api.browserstack.com/app-automate/builds/${buildId}/sessions/${sessionId}/deviceLogs`;
-  const authString = getBrowserStackAuth(server);
+  const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
   const response = await fetch(url, {
@@ -32,10 +33,10 @@ export async function retrieveDeviceLogs(
 export async function retrieveAppiumLogs(
   sessionId: string,
   buildId: string,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<string> {
   const url = `https://api.browserstack.com/app-automate/builds/${buildId}/sessions/${sessionId}/appiumlogs`;
-  const authString = getBrowserStackAuth(server);
+  const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
   const response = await fetch(url, {
@@ -59,10 +60,10 @@ export async function retrieveAppiumLogs(
 export async function retrieveCrashLogs(
   sessionId: string,
   buildId: string,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<string> {
   const url = `https://api.browserstack.com/app-automate/builds/${buildId}/sessions/${sessionId}/crashlogs`;
-  const authString = getBrowserStackAuth(server);
+  const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
   const response = await fetch(url, {
diff --git a/src/tools/failurelogs-utils/automate.ts b/src/tools/failurelogs-utils/automate.ts
index 00a58f3..dd4ee49 100644
--- a/src/tools/failurelogs-utils/automate.ts
+++ b/src/tools/failurelogs-utils/automate.ts
@@ -5,14 +5,15 @@ import {
   filterLinesByKeywords,
   validateLogResponse,
 } from "./utils.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
 // NETWORK LOGS
 export async function retrieveNetworkFailures(
   sessionId: string,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<string> {
   const url = `https://api.browserstack.com/automate/sessions/${sessionId}/networklogs`;
-  const authString = getBrowserStackAuth(server);
+  const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
   const response = await fetch(url, {
@@ -60,10 +61,10 @@ export async function retrieveNetworkFailures(
 // SESSION LOGS
 export async function retrieveSessionFailures(
   sessionId: string,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<string> {
   const url = `https://api.browserstack.com/automate/sessions/${sessionId}/logs`;
-  const authString = getBrowserStackAuth(server);
+  const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
   const response = await fetch(url, {
@@ -86,10 +87,10 @@ export async function retrieveSessionFailures(
 // CONSOLE LOGS
 export async function retrieveConsoleFailures(
   sessionId: string,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<string> {
   const url = `https://api.browserstack.com/automate/sessions/${sessionId}/consolelogs`;
-  const authString = getBrowserStackAuth(server);
+  const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
   const response = await fetch(url, {
diff --git a/src/tools/getFailureLogs.ts b/src/tools/getFailureLogs.ts
index 76fc9b5..782e43d 100644
--- a/src/tools/getFailureLogs.ts
+++ b/src/tools/getFailureLogs.ts
@@ -3,6 +3,7 @@ import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import logger from "../logger.js";
 import { trackMCP } from "../lib/instrumentation.js";
+import { BrowserStackConfig } from "../lib/types.js";
 
 import {
   retrieveNetworkFailures,
@@ -33,7 +34,7 @@ export async function getFailureLogs(
     logTypes: LogType[];
     sessionType: SessionTypeValues;
   },
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   const results: CallToolResult["content"] = [];
   const errors: string[] = [];
@@ -100,19 +101,19 @@ export async function getFailureLogs(
     for (const logType of validLogTypes) {
       switch (logType) {
         case AutomateLogType.NetworkLogs: {
-          response = await retrieveNetworkFailures(args.sessionId, server);
+          response = await retrieveNetworkFailures(args.sessionId, config);
           results.push({ type: "text", text: response });
           break;
         }
 
         case AutomateLogType.SessionLogs: {
-          response = await retrieveSessionFailures(args.sessionId, server);
+          response = await retrieveSessionFailures(args.sessionId, config);
           results.push({ type: "text", text: response });
           break;
         }
 
         case AutomateLogType.ConsoleLogs: {
-          response = await retrieveConsoleFailures(args.sessionId, server);
+          response = await retrieveConsoleFailures(args.sessionId, config);
           results.push({ type: "text", text: response });
           break;
         }
@@ -121,7 +122,7 @@ export async function getFailureLogs(
           response = await retrieveDeviceLogs(
             args.sessionId,
             args.buildId!,
-            server,
+            config,
           );
           results.push({ type: "text", text: response });
           break;
@@ -131,7 +132,7 @@ export async function getFailureLogs(
           response = await retrieveAppiumLogs(
             args.sessionId,
             args.buildId!,
-            server,
+            config,
           );
           results.push({ type: "text", text: response });
           break;
@@ -141,7 +142,7 @@ export async function getFailureLogs(
           response = await retrieveCrashLogs(
             args.sessionId,
             args.buildId!,
-            server,
+            config,
           );
           results.push({ type: "text", text: response });
           break;
@@ -164,7 +165,10 @@ export async function getFailureLogs(
 }
 
 // Register tool with the MCP server
-export default function registerGetFailureLogs(server: McpServer) {
+export default function registerGetFailureLogs(
+  server: McpServer,
+  config: BrowserStackConfig,
+) {
   server.tool(
     "getFailureLogs",
     "Fetch various types of logs from a BrowserStack session. Supports both automate and app-automate sessions.",
@@ -200,11 +204,21 @@ export default function registerGetFailureLogs(server: McpServer) {
     },
     async (args) => {
       try {
-        trackMCP("getFailureLogs", server.server.getClientVersion()!);
-        return await getFailureLogs(args, server);
+        trackMCP(
+          "getFailureLogs",
+          server.server.getClientVersion()!,
+          undefined,
+          config,
+        );
+        return await getFailureLogs(args, config);
       } catch (error) {
         const message = error instanceof Error ? error.message : String(error);
-        trackMCP("getFailureLogs", server.server.getClientVersion()!, error);
+        trackMCP(
+          "getFailureLogs",
+          server.server.getClientVersion()!,
+          error,
+          config,
+        );
         logger.error("Failed to fetch logs: %s", message);
         return {
           content: [
diff --git a/src/tools/live-utils/start-session.ts b/src/tools/live-utils/start-session.ts
index 9df52d1..3c9eab0 100644
--- a/src/tools/live-utils/start-session.ts
+++ b/src/tools/live-utils/start-session.ts
@@ -28,15 +28,15 @@ async function prepareLocalTunnel(url: string): Promise<boolean> {
  */
 export async function startBrowserSession(
   args: DesktopSearchArgs | MobileSearchArgs,
-  server: any,
+  config: any,
 ): Promise<string> {
   const entry =
     args.platformType === PlatformType.DESKTOP
       ? await filterDesktop(args as DesktopSearchArgs)
       : await filterMobile(args as MobileSearchArgs);
 
-  // Get credentials from server
-  const authString = getBrowserStackAuth(server);
+  // Get credentials from config
+  const authString = getBrowserStackAuth(config);
   const [username, password] = authString.split(":");
 
   if (!username || !password) {
diff --git a/src/tools/live.ts b/src/tools/live.ts
index b7c23e9..8b32efa 100644
--- a/src/tools/live.ts
+++ b/src/tools/live.ts
@@ -41,7 +41,7 @@ const LiveArgsSchema = z.object(LiveArgsShape);
  */
 async function launchDesktopSession(
   args: z.infer<typeof LiveArgsSchema>,
-  server: any,
+  config: any,
 ): Promise<string> {
   if (!args.desiredBrowser)
     throw new Error("You must provide a desiredBrowser");
@@ -57,7 +57,7 @@ async function launchDesktopSession(
       browser: args.desiredBrowser,
       browserVersion: args.desiredBrowserVersion,
     },
-    server,
+    config,
   );
 }
 
@@ -66,7 +66,7 @@ async function launchDesktopSession(
  */
 async function launchMobileSession(
   args: z.infer<typeof LiveArgsSchema>,
-  server: any,
+  config: any,
 ): Promise<string> {
   if (!args.desiredDevice) throw new Error("You must provide a desiredDevice");
 
@@ -79,22 +79,22 @@ async function launchMobileSession(
       osVersion: args.desiredOSVersion,
       device: args.desiredDevice,
     },
-    server,
+    config,
   );
 }
 
 /**
  * Handles the core logic for running a browser session
  */
-async function runBrowserSession(rawArgs: any, server: any) {
+async function runBrowserSession(rawArgs: any, config: any) {
   // Validate and narrow
   const args = LiveArgsSchema.parse(rawArgs);
 
   // Branch desktop vs mobile and delegate
   const launchUrl =
     args.platformType === PlatformType.DESKTOP
-      ? await launchDesktopSession(args, server)
-      : await launchMobileSession(args, server);
+      ? await launchDesktopSession(args, config)
+      : await launchMobileSession(args, config);
 
   return {
     content: [
@@ -106,21 +106,27 @@ async function runBrowserSession(rawArgs: any, server: any) {
   };
 }
 
-export default function addBrowserLiveTools(server: McpServer) {
+export default function addBrowserLiveTools(server: McpServer, config: any) {
   server.tool(
     "runBrowserLiveSession",
     "Launch a BrowserStack Live session (desktop or mobile).",
     LiveArgsShape,
     async (args) => {
       try {
-        trackMCP("runBrowserLiveSession", server.server.getClientVersion()!);
-        return await runBrowserSession(args, server);
+        trackMCP(
+          "runBrowserLiveSession",
+          server.server.getClientVersion()!,
+          undefined,
+          config,
+        );
+        return await runBrowserSession(args, config);
       } catch (error) {
         logger.error("Live session failed: %s", error);
         trackMCP(
           "runBrowserLiveSession",
           server.server.getClientVersion()!,
           error,
+          config,
         );
         return {
           content: [
diff --git a/src/tools/observability.ts b/src/tools/observability.ts
index 90bb281..58e7d9c 100644
--- a/src/tools/observability.ts
+++ b/src/tools/observability.ts
@@ -4,16 +4,17 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { getLatestO11YBuildInfo } from "../lib/api.js";
 import { trackMCP } from "../lib/instrumentation.js";
 import logger from "../logger.js";
+import { BrowserStackConfig } from "../lib/types.js";
 
 export async function getFailuresInLastRun(
   buildName: string,
   projectName: string,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   const buildsData = await getLatestO11YBuildInfo(
     buildName,
     projectName,
-    server,
+    config,
   );
 
   const observabilityUrl = buildsData.observability_url;
@@ -46,7 +47,10 @@ export async function getFailuresInLastRun(
   };
 }
 
-export default function addObservabilityTools(server: McpServer) {
+export default function addObservabilityTools(
+  server: McpServer,
+  config: BrowserStackConfig,
+) {
   server.tool(
     "getFailuresInLastRun",
     "Use this tool to debug failures in the last run of the test suite on BrowserStack. Use only when browserstack.yml file is present in the project root.",
@@ -64,11 +68,16 @@ export default function addObservabilityTools(server: McpServer) {
     },
     async (args) => {
       try {
-        trackMCP("getFailuresInLastRun", server.server.getClientVersion()!);
+        trackMCP(
+          "getFailuresInLastRun",
+          server.server.getClientVersion()!,
+          undefined,
+          config,
+        );
         return await getFailuresInLastRun(
           args.buildName,
           args.projectName,
-          server,
+          config,
         );
       } catch (error) {
         logger.error("Failed to get failures in the last run: %s", error);
@@ -76,6 +85,7 @@ export default function addObservabilityTools(server: McpServer) {
           "getFailuresInLastRun",
           server.server.getClientVersion()!,
           error,
+          config,
         );
         return {
           content: [
diff --git a/src/tools/selfheal-utils/selfheal.ts b/src/tools/selfheal-utils/selfheal.ts
index 858bc38..99bc18c 100644
--- a/src/tools/selfheal-utils/selfheal.ts
+++ b/src/tools/selfheal-utils/selfheal.ts
@@ -10,9 +10,13 @@ interface SelectorMapping {
 }
 
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
-export async function getSelfHealSelectors(sessionId: string, server: any) {
-  const authString = getBrowserStackAuth(server);
+export async function getSelfHealSelectors(
+  sessionId: string,
+  config: BrowserStackConfig,
+) {
+  const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
   const url = `https://api.browserstack.com/automate/sessions/${sessionId}/logs`;
 
diff --git a/src/tools/selfheal.ts b/src/tools/selfheal.ts
index ddd6efd..0bfe1c5 100644
--- a/src/tools/selfheal.ts
+++ b/src/tools/selfheal.ts
@@ -4,14 +4,15 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { getSelfHealSelectors } from "./selfheal-utils/selfheal.js";
 import logger from "../logger.js";
 import { trackMCP } from "../lib/instrumentation.js";
+import { BrowserStackConfig } from "../lib/types.js";
 
 // Tool function that fetches self-healing selectors
 export async function fetchSelfHealSelectorTool(
   args: { sessionId: string },
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   try {
-    const selectors = await getSelfHealSelectors(args.sessionId, server);
+    const selectors = await getSelfHealSelectors(args.sessionId, config);
     return {
       content: [
         {
@@ -29,7 +30,10 @@ export async function fetchSelfHealSelectorTool(
 }
 
 // Registers the fetchSelfHealSelector tool with the MCP server
-export default function addSelfHealTools(server: McpServer) {
+export default function addSelfHealTools(
+  server: McpServer,
+  config: BrowserStackConfig,
+) {
   server.tool(
     "fetchSelfHealedSelectors",
     "Retrieves AI-generated, self-healed selectors for a BrowserStack Automate session to resolve flaky tests caused by dynamic DOM changes.",
@@ -38,13 +42,19 @@ export default function addSelfHealTools(server: McpServer) {
     },
     async (args) => {
       try {
-        trackMCP("fetchSelfHealedSelectors", server.server.getClientVersion()!);
-        return await fetchSelfHealSelectorTool(args, server);
+        trackMCP(
+          "fetchSelfHealedSelectors",
+          server.server.getClientVersion()!,
+          undefined,
+          config,
+        );
+        return await fetchSelfHealSelectorTool(args, config);
       } catch (error) {
         trackMCP(
           "fetchSelfHealedSelectors",
           server.server.getClientVersion()!,
           error,
+          config,
         );
         const errorMessage =
           error instanceof Error ? error.message : "Unknown error";
diff --git a/src/tools/testmanagement-utils/TCG-utils/api.ts b/src/tools/testmanagement-utils/TCG-utils/api.ts
index c708635..9b1a9fe 100644
--- a/src/tools/testmanagement-utils/TCG-utils/api.ts
+++ b/src/tools/testmanagement-utils/TCG-utils/api.ts
@@ -13,17 +13,18 @@ import {
 } from "./types.js";
 import { createTestCasePayload } from "./helpers.js";
 import { getBrowserStackAuth } from "../../../lib/get-auth.js";
+import { BrowserStackConfig } from "../../../lib/types.js";
 
 /**
  * Fetch default and custom form fields for a project.
  */
 export async function fetchFormFields(
   projectId: string,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<{ default_fields: any; custom_fields: any }> {
   const res = await axios.get(FORM_FIELDS_URL(projectId), {
     headers: {
-      "API-TOKEN": getBrowserStackAuth(server),
+      "API-TOKEN": getBrowserStackAuth(config),
     },
   });
   return res.data;
@@ -38,7 +39,7 @@ export async function triggerTestCaseGeneration(
   folderId: string,
   projectId: string,
   source: string,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<string> {
   const res = await axios.post(
     TCG_TRIGGER_URL,
@@ -52,7 +53,7 @@ export async function triggerTestCaseGeneration(
     },
     {
       headers: {
-        "API-TOKEN": getBrowserStackAuth(server),
+        "API-TOKEN": getBrowserStackAuth(config),
         "Content-Type": "application/json",
         "request-source": source,
       },
@@ -73,7 +74,7 @@ export async function fetchTestCaseDetails(
   projectId: string,
   testCaseIds: string[],
   source: string,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<string> {
   if (testCaseIds.length === 0) {
     throw new Error("No testCaseIds provided to fetchTestCaseDetails");
@@ -88,7 +89,7 @@ export async function fetchTestCaseDetails(
     },
     {
       headers: {
-        "API-TOKEN": getBrowserStackAuth(server),
+        "API-TOKEN": getBrowserStackAuth(config),
         "request-source": source,
         "Content-Type": "application/json",
       },
@@ -105,7 +106,7 @@ export async function fetchTestCaseDetails(
  */
 export async function pollTestCaseDetails(
   traceRequestId: string,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<Record<string, any>> {
   const detailMap: Record<string, any> = {};
   let done = false;
@@ -121,7 +122,7 @@ export async function pollTestCaseDetails(
       {},
       {
         headers: {
-          "API-TOKEN": getBrowserStackAuth(server),
+          "API-TOKEN": getBrowserStackAuth(config),
         },
       },
     );
@@ -157,7 +158,7 @@ export async function pollScenariosTestDetails(
   context: any,
   documentId: number,
   source: string,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<Record<string, Scenario>> {
   const { folderId, projectReferenceId } = args;
   const scenariosMap: Record<string, Scenario> = {};
@@ -173,7 +174,7 @@ export async function pollScenariosTestDetails(
           {},
           {
             headers: {
-              "API-TOKEN": getBrowserStackAuth(server),
+              "API-TOKEN": getBrowserStackAuth(config),
             },
           },
         );
@@ -217,9 +218,9 @@ export async function pollScenariosTestDetails(
                 projectReferenceId,
                 ids,
                 source,
-                server,
+                config,
               );
-              detailPromises.push(pollTestCaseDetails(reqId, server));
+              detailPromises.push(pollTestCaseDetails(reqId, config));
 
               scenariosMap[sc.id] ||= {
                 id: sc.id,
@@ -281,7 +282,7 @@ export async function bulkCreateTestCases(
   traceId: string,
   context: any,
   documentId: number,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<string> {
   const results: Record<string, any> = {};
   const total = Object.keys(scenariosMap).length;
@@ -312,7 +313,7 @@ export async function bulkCreateTestCases(
         payload,
         {
           headers: {
-            "API-TOKEN": getBrowserStackAuth(server),
+            "API-TOKEN": getBrowserStackAuth(config),
             "Content-Type": "application/json",
           },
         },
@@ -349,13 +350,13 @@ export async function bulkCreateTestCases(
 
 export async function projectIdentifierToId(
   projectId: string,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<string> {
   const url = `https://test-management.browserstack.com/api/v1/projects/?q=${projectId}`;
 
   const response = await axios.get(url, {
     headers: {
-      "API-TOKEN": getBrowserStackAuth(server),
+      "API-TOKEN": getBrowserStackAuth(config),
       accept: "application/json, text/plain, */*",
     },
   });
@@ -373,13 +374,13 @@ export async function projectIdentifierToId(
 export async function testCaseIdentifierToDetails(
   projectId: string,
   testCaseIdentifier: string,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<{ testCaseId: string; folderId: string }> {
   const url = `https://test-management.browserstack.com/api/v1/projects/${projectId}/test-cases/search?q[query]=${testCaseIdentifier}`;
 
   const response = await axios.get(url, {
     headers: {
-      "API-TOKEN": getBrowserStackAuth(server),
+      "API-TOKEN": getBrowserStackAuth(config),
       accept: "application/json, text/plain, */*",
     },
   });
diff --git a/src/tools/testmanagement-utils/add-test-result.ts b/src/tools/testmanagement-utils/add-test-result.ts
index 6843b7a..12a2b7b 100644
--- a/src/tools/testmanagement-utils/add-test-result.ts
+++ b/src/tools/testmanagement-utils/add-test-result.ts
@@ -3,6 +3,7 @@ import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
 /**
  * Schema for adding a test result to a test run.
@@ -33,7 +34,7 @@ export type AddTestResultArgs = z.infer<typeof AddTestResultSchema>;
  */
 export async function addTestResult(
   rawArgs: AddTestResultArgs,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   try {
     const args = AddTestResultSchema.parse(rawArgs);
@@ -46,7 +47,7 @@ export async function addTestResult(
       test_case_id: args.test_case_id,
     } as any;
 
-    const authString = getBrowserStackAuth(server);
+    const authString = getBrowserStackAuth(config);
     const [username, password] = authString.split(":");
 
     const response = await axios.post(url, body, {
diff --git a/src/tools/testmanagement-utils/create-lca-steps.ts b/src/tools/testmanagement-utils/create-lca-steps.ts
index 969548d..5751369 100644
--- a/src/tools/testmanagement-utils/create-lca-steps.ts
+++ b/src/tools/testmanagement-utils/create-lca-steps.ts
@@ -8,6 +8,7 @@ import {
 } from "./TCG-utils/api.js";
 import { pollLCAStatus } from "./poll-lca-status.js";
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
 /**
  * Schema for creating LCA steps for a test case
@@ -65,20 +66,20 @@ export type CreateLCAStepsArgs = z.infer<typeof CreateLCAStepsSchema>;
 export async function createLCASteps(
   args: CreateLCAStepsArgs,
   context: any,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   try {
     // Get the project ID from identifier
     const projectId = await projectIdentifierToId(
       args.project_identifier,
-      server,
+      config,
     );
 
     // Get the test case ID and folder ID from identifier
     const { testCaseId, folderId } = await testCaseIdentifierToDetails(
       projectId,
       args.test_case_identifier,
-      server,
+      config,
     );
 
     const url = `https://test-management.browserstack.com/api/v1/projects/${projectId}/test-cases/${testCaseId}/lcnc`;
@@ -95,7 +96,7 @@ export async function createLCASteps(
 
     const response = await axios.post(url, payload, {
       headers: {
-        "API-TOKEN": getBrowserStackAuth(server),
+        "API-TOKEN": getBrowserStackAuth(config),
         accept: "application/json, text/plain, */*",
         "Content-Type": "application/json",
       },
@@ -130,7 +131,7 @@ export async function createLCASteps(
           maxWaitMs, // max wait time
           2 * 60 * 1000, // 2 minutes initial wait
           10 * 1000, // 10 seconds interval
-          server,
+          config,
         );
 
         if (lcaResult && lcaResult.status === "done") {
diff --git a/src/tools/testmanagement-utils/create-project-folder.ts b/src/tools/testmanagement-utils/create-project-folder.ts
index c537e81..43a7058 100644
--- a/src/tools/testmanagement-utils/create-project-folder.ts
+++ b/src/tools/testmanagement-utils/create-project-folder.ts
@@ -4,6 +4,7 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js"; // or correct path
 import { projectIdentifierToId } from "../testmanagement-utils/TCG-utils/api.js";
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
 // Schema for combined project/folder creation
 export const CreateProjFoldSchema = z.object({
@@ -37,7 +38,7 @@ type CreateProjFoldArgs = z.infer<typeof CreateProjFoldSchema>;
  */
 export async function createProjectOrFolder(
   args: CreateProjFoldArgs,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   const {
     project_name,
@@ -54,7 +55,7 @@ export async function createProjectOrFolder(
     );
   }
 
-  const authString = getBrowserStackAuth(server);
+  const authString = getBrowserStackAuth(config);
   const [username, password] = authString.split(":");
 
   let projId = project_identifier;
@@ -62,7 +63,7 @@ export async function createProjectOrFolder(
   // Step 1: Create project if project_name provided
   if (project_name) {
     try {
-      const authString = getBrowserStackAuth(server);
+      const authString = getBrowserStackAuth(config);
       const [username, password] = authString.split(":");
       const res = await axios.post(
         "https://test-management.browserstack.com/api/v2/projects",
@@ -119,7 +120,7 @@ export async function createProjectOrFolder(
       // Folder created successfully
 
       const folder = res.data.folder;
-      const projectId = await projectIdentifierToId(projId, server);
+      const projectId = await projectIdentifierToId(projId, config);
 
       return {
         content: [
diff --git a/src/tools/testmanagement-utils/create-testcase.ts b/src/tools/testmanagement-utils/create-testcase.ts
index 911b0eb..2f9eac6 100644
--- a/src/tools/testmanagement-utils/create-testcase.ts
+++ b/src/tools/testmanagement-utils/create-testcase.ts
@@ -3,6 +3,7 @@ import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js"; // or correct
 import { projectIdentifierToId } from "./TCG-utils/api.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
 interface TestCaseStep {
   step: string;
@@ -141,10 +142,10 @@ import { getBrowserStackAuth } from "../../lib/get-auth.js";
 
 export async function createTestCase(
   params: TestCaseCreateRequest,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   const body = { test_case: params };
-  const authString = getBrowserStackAuth(server);
+  const authString = getBrowserStackAuth(config);
   const [username, password] = authString.split(":");
 
   try {
@@ -181,7 +182,7 @@ export async function createTestCase(
     const tc = data.test_case;
     const projectId = await projectIdentifierToId(
       params.project_identifier,
-      server,
+      config,
     );
 
     return {
diff --git a/src/tools/testmanagement-utils/create-testrun.ts b/src/tools/testmanagement-utils/create-testrun.ts
index ce55b8b..66a1c04 100644
--- a/src/tools/testmanagement-utils/create-testrun.ts
+++ b/src/tools/testmanagement-utils/create-testrun.ts
@@ -3,6 +3,7 @@ import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js";
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
 /**
  * Schema for creating a test run.
@@ -53,7 +54,7 @@ export type CreateTestRunArgs = z.infer<typeof CreateTestRunSchema>;
  */
 export async function createTestRun(
   rawArgs: CreateTestRunArgs,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   try {
     const inputArgs = {
@@ -69,7 +70,7 @@ export async function createTestRun(
       args.project_identifier,
     )}/test-runs`;
 
-    const authString = getBrowserStackAuth(server);
+    const authString = getBrowserStackAuth(config);
     const [username, password] = authString.split(":");
     const response = await axios.post(
       url,
diff --git a/src/tools/testmanagement-utils/list-testcases.ts b/src/tools/testmanagement-utils/list-testcases.ts
index 476fa8d..72e2397 100644
--- a/src/tools/testmanagement-utils/list-testcases.ts
+++ b/src/tools/testmanagement-utils/list-testcases.ts
@@ -3,6 +3,7 @@ import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js";
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
 /**
  * Schema for listing test cases with optional filters.
@@ -38,7 +39,7 @@ export type ListTestCasesArgs = z.infer<typeof ListTestCasesSchema>;
  */
 export async function listTestCases(
   args: ListTestCasesArgs,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   try {
     // Build query string
@@ -52,7 +53,7 @@ export async function listTestCases(
       args.project_identifier,
     )}/test-cases?${params.toString()}`;
 
-    const authString = getBrowserStackAuth(server);
+    const authString = getBrowserStackAuth(config);
     const [username, password] = authString.split(":");
     const resp = await axios.get(url, {
       auth: {
diff --git a/src/tools/testmanagement-utils/list-testruns.ts b/src/tools/testmanagement-utils/list-testruns.ts
index 002ccb0..3520459 100644
--- a/src/tools/testmanagement-utils/list-testruns.ts
+++ b/src/tools/testmanagement-utils/list-testruns.ts
@@ -3,6 +3,7 @@ import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js";
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
 /**
  * Schema for listing test runs with optional filters.
@@ -26,7 +27,7 @@ type ListTestRunsArgs = z.infer<typeof ListTestRunsSchema>;
  */
 export async function listTestRuns(
   args: ListTestRunsArgs,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   try {
     const params = new URLSearchParams();
@@ -39,7 +40,7 @@ export async function listTestRuns(
         args.project_identifier,
       )}/test-runs?` + params.toString();
 
-    const authString = getBrowserStackAuth(server);
+    const authString = getBrowserStackAuth(config);
     const [username, password] = authString.split(":");
     const resp = await axios.get(url, {
       auth: {
diff --git a/src/tools/testmanagement-utils/poll-lca-status.ts b/src/tools/testmanagement-utils/poll-lca-status.ts
index b3697d8..2d5e99e 100644
--- a/src/tools/testmanagement-utils/poll-lca-status.ts
+++ b/src/tools/testmanagement-utils/poll-lca-status.ts
@@ -1,5 +1,6 @@
 import axios from "axios";
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
 /**
  * Interface for the test case response structure
@@ -36,7 +37,7 @@ export async function pollLCAStatus(
   maxWaitTimeMs: number = 10 * 60 * 1000, // 10 minutes default
   initialWaitMs: number = 2 * 60 * 1000, // 2 minutes initial wait
   pollIntervalMs: number = 10 * 1000, // 10 seconds interval
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<{ resource_path: string; status: string } | null> {
   const url = `https://test-management.browserstack.com/api/v1/projects/${projectId}/folder/${folderId}/test-cases/${testCaseId}`;
 
@@ -92,7 +93,7 @@ export async function pollLCAStatus(
     // Set up polling interval
     const intervalId = setInterval(async () => {
       try {
-        const authString = getBrowserStackAuth(server);
+        const authString = getBrowserStackAuth(config);
         const response = await axios.get<TestCaseResponse>(url, {
           headers: {
             "API-TOKEN": authString,
diff --git a/src/tools/testmanagement-utils/testcase-from-file.ts b/src/tools/testmanagement-utils/testcase-from-file.ts
index aca4823..96bf835 100644
--- a/src/tools/testmanagement-utils/testcase-from-file.ts
+++ b/src/tools/testmanagement-utils/testcase-from-file.ts
@@ -13,11 +13,12 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { signedUrlMap } from "../../lib/inmemory-store.js";
 import logger from "../../logger.js";
 import { projectIdentifierToId } from "./TCG-utils/api.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
 export async function createTestCasesFromFile(
   args: CreateTestCasesFromFileArgs,
   context: any,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   logger.info(
     `createTestCasesFromFile called with projectId: ${args.projectReferenceId}, folderId: ${args.folderId}`,
@@ -26,12 +27,12 @@ export async function createTestCasesFromFile(
   if (args.projectReferenceId.startsWith("PR-")) {
     args.projectReferenceId = await projectIdentifierToId(
       args.projectReferenceId,
-      server,
+      config,
     );
   }
   const { default_fields, custom_fields } = await fetchFormFields(
     args.projectReferenceId,
-    server,
+    config,
   );
   const fieldMaps = buildDefaultFieldMaps(default_fields);
   const booleanFieldId = findBooleanFieldId(custom_fields);
@@ -60,7 +61,7 @@ export async function createTestCasesFromFile(
     args.folderId,
     args.projectReferenceId,
     source,
-    server,
+    config,
   );
 
   const scenariosMap = await pollScenariosTestDetails(
@@ -69,7 +70,7 @@ export async function createTestCasesFromFile(
     context,
     documentId,
     source,
-    server,
+    config,
   );
 
   const resultString = await bulkCreateTestCases(
@@ -81,7 +82,7 @@ export async function createTestCasesFromFile(
     traceId,
     context,
     documentId,
-    server,
+    config,
   );
 
   signedUrlMap.delete(args.documentId);
diff --git a/src/tools/testmanagement-utils/update-testrun.ts b/src/tools/testmanagement-utils/update-testrun.ts
index 86ffc21..0aee151 100644
--- a/src/tools/testmanagement-utils/update-testrun.ts
+++ b/src/tools/testmanagement-utils/update-testrun.ts
@@ -3,6 +3,7 @@ import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
 /**
  * Schema for updating a test run with partial fields.
@@ -35,7 +36,7 @@ type UpdateTestRunArgs = z.infer<typeof UpdateTestRunSchema>;
  */
 export async function updateTestRun(
   args: UpdateTestRunArgs,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   try {
     const body = { test_run: args.test_run };
@@ -43,7 +44,7 @@ export async function updateTestRun(
       args.project_identifier,
     )}/test-runs/${encodeURIComponent(args.test_run_id)}/update`;
 
-    const authString = getBrowserStackAuth(server);
+    const authString = getBrowserStackAuth(config);
     const [username, password] = authString.split(":");
 
     const resp = await axios.patch(url, body, {
diff --git a/src/tools/testmanagement-utils/upload-file.ts b/src/tools/testmanagement-utils/upload-file.ts
index cebbeca..0b5c563 100644
--- a/src/tools/testmanagement-utils/upload-file.ts
+++ b/src/tools/testmanagement-utils/upload-file.ts
@@ -8,6 +8,7 @@ import { v4 as uuidv4 } from "uuid";
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { signedUrlMap } from "../../lib/inmemory-store.js";
 import { projectIdentifierToId } from "./TCG-utils/api.js";
+import { BrowserStackConfig } from "../../lib/types.js";
 
 /**
  * Schema for the upload file tool
@@ -28,7 +29,7 @@ export const UploadFileSchema = z.object({
  */
 export async function uploadFile(
   args: z.infer<typeof UploadFileSchema>,
-  server: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   const { project_identifier, file_path } = args;
 
@@ -49,7 +50,7 @@ export async function uploadFile(
     // Get the project ID
     const projectIdResponse = await projectIdentifierToId(
       project_identifier,
-      server,
+      config,
     );
 
     const formData = new FormData();
@@ -60,7 +61,7 @@ export async function uploadFile(
     const response = await axios.post(uploadUrl, formData, {
       headers: {
         ...formData.getHeaders(),
-        "API-TOKEN": getBrowserStackAuth(server),
+        "API-TOKEN": getBrowserStackAuth(config),
         accept: "application/json, text/plain, */*",
       },
     });
diff --git a/src/tools/testmanagement.ts b/src/tools/testmanagement.ts
index 32adc68..31b0b6f 100644
--- a/src/tools/testmanagement.ts
+++ b/src/tools/testmanagement.ts
@@ -59,14 +59,25 @@ import {
  */
 export async function createProjectOrFolderTool(
   args: z.infer<typeof CreateProjFoldSchema>,
+  config: any,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
-    trackMCP("createProjectOrFolder", server.server.getClientVersion()!);
-    return await createProjectOrFolder(args, server);
+    trackMCP(
+      "createProjectOrFolder",
+      server.server.getClientVersion()!,
+      undefined,
+      config,
+    );
+    return await createProjectOrFolder(args, config);
   } catch (err) {
     logger.error("Failed to create project/folder: %s", err);
-    trackMCP("createProjectOrFolder", server.server.getClientVersion()!, err);
+    trackMCP(
+      "createProjectOrFolder",
+      server.server.getClientVersion()!,
+      err,
+      config,
+    );
     return {
       content: [
         {
@@ -87,16 +98,22 @@ export async function createProjectOrFolderTool(
  */
 export async function createTestCaseTool(
   args: TestCaseCreateRequest,
+  config: any,
   server: McpServer,
 ): Promise<CallToolResult> {
   // Sanitize input arguments
   const cleanedArgs = sanitizeArgs(args);
   try {
-    trackMCP("createTestCase", server.server.getClientVersion()!);
-    return await createTestCaseAPI(cleanedArgs, server);
+    trackMCP(
+      "createTestCase",
+      server.server.getClientVersion()!,
+      undefined,
+      config,
+    );
+    return await createTestCaseAPI(cleanedArgs, config);
   } catch (err) {
     logger.error("Failed to create test case: %s", err);
-    trackMCP("createTestCase", server.server.getClientVersion()!, err);
+    trackMCP("createTestCase", server.server.getClientVersion()!, err, config);
     return {
       content: [
         {
@@ -118,13 +135,19 @@ export async function createTestCaseTool(
 
 export async function listTestCasesTool(
   args: z.infer<typeof ListTestCasesSchema>,
+  config: any,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
-    trackMCP("listTestCases", server.server.getClientVersion()!);
-    return await listTestCases(args, server);
+    trackMCP(
+      "listTestCases",
+      server.server.getClientVersion()!,
+      undefined,
+      config,
+    );
+    return await listTestCases(args, config);
   } catch (err) {
-    trackMCP("listTestCases", server.server.getClientVersion()!, err);
+    trackMCP("listTestCases", server.server.getClientVersion()!, err, config);
     return {
       content: [
         {
@@ -145,13 +168,19 @@ export async function listTestCasesTool(
  */
 export async function createTestRunTool(
   args: z.infer<typeof CreateTestRunSchema>,
+  config: any,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
-    trackMCP("createTestRun", server.server.getClientVersion()!);
-    return await createTestRun(args, server);
+    trackMCP(
+      "createTestRun",
+      server.server.getClientVersion()!,
+      undefined,
+      config,
+    );
+    return await createTestRun(args, config);
   } catch (err) {
-    trackMCP("createTestRun", server.server.getClientVersion()!, err);
+    trackMCP("createTestRun", server.server.getClientVersion()!, err, config);
     return {
       content: [
         {
@@ -172,13 +201,19 @@ export async function createTestRunTool(
  */
 export async function listTestRunsTool(
   args: z.infer<typeof ListTestRunsSchema>,
+  config: any,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
-    trackMCP("listTestRuns", server.server.getClientVersion()!);
-    return await listTestRuns(args, server);
+    trackMCP(
+      "listTestRuns",
+      server.server.getClientVersion()!,
+      undefined,
+      config,
+    );
+    return await listTestRuns(args, config);
   } catch (err) {
-    trackMCP("listTestRuns", server.server.getClientVersion()!, err);
+    trackMCP("listTestRuns", server.server.getClientVersion()!, err, config);
     return {
       content: [
         {
@@ -201,13 +236,19 @@ export async function listTestRunsTool(
  */
 export async function updateTestRunTool(
   args: z.infer<typeof UpdateTestRunSchema>,
+  config: any,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
-    trackMCP("updateTestRun", server.server.getClientVersion()!);
-    return await updateTestRun(args, server);
+    trackMCP(
+      "updateTestRun",
+      server.server.getClientVersion()!,
+      undefined,
+      config,
+    );
+    return await updateTestRun(args, config);
   } catch (err) {
-    trackMCP("updateTestRun", server.server.getClientVersion()!, err);
+    trackMCP("updateTestRun", server.server.getClientVersion()!, err, config);
     return {
       content: [
         {
@@ -228,13 +269,19 @@ export async function updateTestRunTool(
  */
 export async function addTestResultTool(
   args: z.infer<typeof AddTestResultSchema>,
+  config: any,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
-    trackMCP("addTestResult", server.server.getClientVersion()!);
-    return await addTestResult(args, server);
+    trackMCP(
+      "addTestResult",
+      server.server.getClientVersion()!,
+      undefined,
+      config,
+    );
+    return await addTestResult(args, config);
   } catch (err) {
-    trackMCP("addTestResult", server.server.getClientVersion()!, err);
+    trackMCP("addTestResult", server.server.getClientVersion()!, err, config);
     return {
       content: [
         {
@@ -255,16 +302,23 @@ export async function addTestResultTool(
  */
 export async function uploadProductRequirementFileTool(
   args: z.infer<typeof UploadFileSchema>,
+  config: any,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
-    trackMCP("uploadProductRequirementFile", server.server.getClientVersion()!);
-    return await uploadFile(args, server);
+    trackMCP(
+      "uploadProductRequirementFile",
+      server.server.getClientVersion()!,
+      undefined,
+      config,
+    );
+    return await uploadFile(args, config);
   } catch (err) {
     trackMCP(
       "uploadProductRequirementFile",
       server.server.getClientVersion()!,
       err,
+      config,
     );
     return {
       content: [
@@ -287,11 +341,16 @@ export async function uploadProductRequirementFileTool(
 export async function createTestCasesFromFileTool(
   args: z.infer<typeof CreateTestCasesFromFileSchema>,
   context: any,
+  config: any,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
-    trackMCP("createTestCasesFromFile", server.server.getClientVersion()!);
-    return await createTestCasesFromFile(args, context, server);
+    trackMCP(
+      "createTestCasesFromFile",
+      server.server.getClientVersion()!,
+      undefined,
+    );
+    return await createTestCasesFromFile(args, context, config);
   } catch (err) {
     trackMCP("createTestCasesFromFile", server.server.getClientVersion()!, err);
     return {
@@ -315,13 +374,19 @@ export async function createTestCasesFromFileTool(
 export async function createLCAStepsTool(
   args: z.infer<typeof CreateLCAStepsSchema>,
   context: any,
+  config: any,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
-    trackMCP("createLCASteps", server.server.getClientVersion()!);
-    return await createLCASteps(args, context, server);
+    trackMCP(
+      "createLCASteps",
+      server.server.getClientVersion()!,
+      undefined,
+      config,
+    );
+    return await createLCASteps(args, context, config);
   } catch (err) {
-    trackMCP("createLCASteps", server.server.getClientVersion()!, err);
+    trackMCP("createLCASteps", server.server.getClientVersion()!, err, config);
     return {
       content: [
         {
@@ -340,70 +405,71 @@ export async function createLCAStepsTool(
 /**
  * Registers both project/folder and test-case tools.
  */
-export default function addTestManagementTools(server: McpServer) {
+export default function addTestManagementTools(server: McpServer, config: any) {
   server.tool(
     "createProjectOrFolder",
     "Create a project and/or folder in BrowserStack Test Management.",
     CreateProjFoldSchema.shape,
-    (args) => createProjectOrFolderTool(args, server),
+    (args) => createProjectOrFolderTool(args, config, server),
   );
 
   server.tool(
     "createTestCase",
     "Use this tool to create a test case in BrowserStack Test Management.",
     CreateTestCaseSchema.shape,
-    (args) => createTestCaseTool(args, server),
+    (args) => createTestCaseTool(args, config, server),
   );
 
   server.tool(
     "listTestCases",
     "List test cases in a project with optional filters (status, priority, custom fields, etc.)",
     ListTestCasesSchema.shape,
-    (args) => listTestCasesTool(args, server),
+    (args) => listTestCasesTool(args, config, server),
   );
 
   server.tool(
     "createTestRun",
     "Create a test run in BrowserStack Test Management.",
     CreateTestRunSchema.shape,
-    (args) => createTestRunTool(args, server),
+    (args) => createTestRunTool(args, config, server),
   );
 
   server.tool(
     "listTestRuns",
     "List test runs in a project with optional filters (date ranges, assignee, state, etc.)",
     ListTestRunsSchema.shape,
-    (args) => listTestRunsTool(args, server),
+    (args) => listTestRunsTool(args, config, server),
   );
   server.tool(
     "updateTestRun",
     "Update a test run in BrowserStack Test Management.",
     UpdateTestRunSchema.shape,
-    (args) => updateTestRunTool(args, server),
+    (args) => updateTestRunTool(args, config, server),
   );
   server.tool(
     "addTestResult",
     "Add a test result to a specific test run via BrowserStack Test Management API.",
     AddTestResultSchema.shape,
-    (args) => addTestResultTool(args, server),
+    (args) => addTestResultTool(args, config, server),
   );
 
   server.tool(
     "uploadProductRequirementFile",
     "Upload files (e.g., PDRs, PDFs) to BrowserStack Test Management and retrieve a file mapping ID. This is utilized for generating test cases from files and is part of the Test Case Generator AI Agent in BrowserStack.",
     UploadFileSchema.shape,
-    (args) => uploadProductRequirementFileTool(args, server),
+    (args) => uploadProductRequirementFileTool(args, config, server),
   );
   server.tool(
     "createTestCasesFromFile",
     "Generate test cases from a file in BrowserStack Test Management using the Test Case Generator AI Agent.",
     CreateTestCasesFromFileSchema.shape,
-    (args, context) => createTestCasesFromFileTool(args, context, server),
+    (args, context) =>
+      createTestCasesFromFileTool(args, context, config, server),
   );
   server.tool(
     "createLCASteps",
     "Generate Low Code Automation (LCA) steps for a test case in BrowserStack Test Management using the Low Code Automation Agent.",
     CreateLCAStepsSchema.shape,
-    (args, context) => createLCAStepsTool(args, context, server),
+    (args, context) => createLCAStepsTool(args, context, config, server),
   );
 }
diff --git a/tests/tools/applive.test.ts b/tests/tools/applive.test.ts
index 0598244..5b1120e 100644
--- a/tests/tools/applive.test.ts
+++ b/tests/tools/applive.test.ts
@@ -53,65 +53,65 @@ describe('startAppLiveSession', () => {
   };
 
   it('should successfully start an Android app live session', async () => {
-    const mockServer = { getClientVersion: () => "test-version" };
-    const result = await startAppLiveSession(validAndroidArgs, mockServer);
+    const mockConfig = { getClientVersion: () => "test-version" };
+    const result = await startAppLiveSession(validAndroidArgs, mockConfig);
 
     expect(startSession).toHaveBeenCalledWith({
       appPath: '/path/to/app.apk',
       desiredPlatform: 'android',
       desiredPhone: validAndroidArgs.desiredPhone,
       desiredPlatformVersion: validAndroidArgs.desiredPlatformVersion
-    }, { server: mockServer });
+    }, { config: mockConfig });
     expect(result.content?.[0]?.text).toContain('Successfully started a session');
   });
 
   it('should successfully start an iOS app live session', async () => {
-    const mockServer = { getClientVersion: () => "test-version" };
-    const result = await startAppLiveSession(validiOSArgs, mockServer);
+    const mockConfig = { getClientVersion: () => "test-version" };
+    const result = await startAppLiveSession(validiOSArgs, mockConfig);
 
     expect(startSession).toHaveBeenCalledWith({
       appPath: '/path/to/app.ipa',
       desiredPlatform: 'ios',
       desiredPhone: validiOSArgs.desiredPhone,
       desiredPlatformVersion: validiOSArgs.desiredPlatformVersion
-    }, { server: mockServer });
+    }, { config: mockConfig });
     expect(result.content?.[0]?.text).toContain('Successfully started a session');
   });
 
   it('should fail if platform is not provided', async () => {
     const args = { ...validAndroidArgs, desiredPlatform: '' };
-    const mockServer = { server: { getClientVersion: () => "test-version" } };
-    await expect(startAppLiveSession(args, mockServer)).rejects.toThrow('You must provide a desiredPlatform');
+    const mockConfig = { getClientVersion: () => "test-version" };
+    await expect(startAppLiveSession(args, mockConfig)).rejects.toThrow('You must provide a desiredPlatform');
   });
 
   it('should fail if app path is not provided', async () => {
     const args = { ...validAndroidArgs, appPath: '' };
-    const mockServer = { server: { getClientVersion: () => "test-version" } };
-    await expect(startAppLiveSession(args, mockServer)).rejects.toThrow('You must provide a appPath');
+    const mockConfig = { getClientVersion: () => "test-version" };
+    await expect(startAppLiveSession(args, mockConfig)).rejects.toThrow('You must provide a appPath');
   });
 
   it('should fail if phone is not provided', async () => {
     const args = { ...validAndroidArgs, desiredPhone: '' };
-    const mockServer = { server: { getClientVersion: () => "test-version" } };
-    await expect(startAppLiveSession(args, mockServer)).rejects.toThrow('You must provide a desiredPhone');
+    const mockConfig = { getClientVersion: () => "test-version" };
+    await expect(startAppLiveSession(args, mockConfig)).rejects.toThrow('You must provide a desiredPhone');
   });
 
   it('should fail if Android app path does not end with .apk', async () => {
     const args = { ...validAndroidArgs, appPath: '/path/to/app.ipa' };
-    const mockServer = { server: { getClientVersion: () => "test-version" } };
-    await expect(startAppLiveSession(args, mockServer)).rejects.toThrow('You must provide a valid Android app path');
+    const mockConfig = { getClientVersion: () => "test-version" };
+    await expect(startAppLiveSession(args, mockConfig)).rejects.toThrow('You must provide a valid Android app path');
   });
 
   it('should fail if iOS app path does not end with .ipa', async () => {
     const args = { ...validiOSArgs, appPath: '/path/to/app.apk' };
-    const mockServer = { server: { getClientVersion: () => "test-version" } };
-    await expect(startAppLiveSession(args, mockServer)).rejects.toThrow('You must provide a valid iOS app path');
+    const mockConfig = { getClientVersion: () => "test-version" };
+    await expect(startAppLiveSession(args, mockConfig)).rejects.toThrow('You must provide a valid iOS app path');
   });
 
   it('should fail if app file does not exist', async () => {
     (fs.existsSync as Mock).mockReturnValue(false);
-    const mockServer = { server: { getClientVersion: () => "test-version" } };
-    await expect(startAppLiveSession(validAndroidArgs, mockServer)).rejects.toThrow('The app path does not exist');
+    const mockConfig = { getClientVersion: () => "test-version" };
+    await expect(startAppLiveSession(validAndroidArgs, mockConfig)).rejects.toThrow('The app path does not exist');
     expect(logger.error).toHaveBeenCalled();
   });
 
@@ -119,14 +119,14 @@ describe('startAppLiveSession', () => {
     (fs.accessSync as Mock).mockImplementation(() => {
       throw new Error('EACCES: permission denied');
     });
-    const mockServer = { server: { getClientVersion: () => "test-version" } };
-    await expect(startAppLiveSession(validAndroidArgs, mockServer)).rejects.toThrow('The app path does not exist or is not readable');
+    const mockConfig = { getClientVersion: () => "test-version" };
+    await expect(startAppLiveSession(validAndroidArgs, mockConfig)).rejects.toThrow('The app path does not exist or is not readable');
     expect(logger.error).toHaveBeenCalled();
   });
 
   it('should handle session start failure', async () => {
     (startSession as Mock).mockRejectedValue(new Error('Session start failed'));
-    const mockServer = { server: { getClientVersion: () => "test-version" } };
-    await expect(startAppLiveSession(validAndroidArgs, mockServer)).rejects.toThrow('Session start failed');
+    const mockConfig = { getClientVersion: () => "test-version" };
+    await expect(startAppLiveSession(validAndroidArgs, mockConfig)).rejects.toThrow('Session start failed');
   });
 });
diff --git a/tests/tools/testmanagement.test.ts b/tests/tools/testmanagement.test.ts
index 7dc5212..d52efca 100644
--- a/tests/tools/testmanagement.test.ts
+++ b/tests/tools/testmanagement.test.ts
@@ -7,7 +7,8 @@ import {
   updateTestRunTool,
   uploadProductRequirementFileTool,
   createTestCasesFromFileTool,
-  createLCAStepsTool
+  createLCAStepsTool,
+  listTestCasesTool
 } from '../../src/tools/testmanagement';
 import addTestManagementTools from '../../src/tools/testmanagement';
 import { createProjectOrFolder } from '../../src/tools/testmanagement-utils/create-project-folder';
@@ -19,12 +20,12 @@ import { listTestRuns } from '../../src/tools/testmanagement-utils/list-testruns
 import { updateTestRun } from '../../src/tools/testmanagement-utils/update-testrun';
 import { createTestCasesFromFile } from '../../src/tools/testmanagement-utils/testcase-from-file';
 import { createLCASteps } from '../../src/tools/testmanagement-utils/create-lca-steps';
-import { uploadFile } from '../../src/tools/testmanagement-utils/upload-file';
-import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 import axios from 'axios';
 import { beforeEach, it, expect, describe, Mocked} from 'vitest';
 import { vi, Mock } from 'vitest';
 import { signedUrlMap } from '../../src/lib/inmemory-store';
+import { uploadFile } from '../../src/tools/testmanagement-utils/upload-file';
+
 
 // Mock dependencies
 vi.mock('../../src/tools/testmanagement-utils/create-project-folder', () => ({
@@ -93,18 +94,20 @@ vi.mock('form-data', () => {
   };
 });
 
-const mockServer = {
-  tool: vi.fn(),
-  server: {
-    getClientVersion: vi.fn(() => "test-version"),
-  },
+const mockConfig = {
+  getClientVersion: vi.fn(() => "test-version"),
   authHeaders: {
     username: 'fake-user',
     password: 'fake-key'
-  }
-} as unknown as McpServer;
+  },
+  "browserstack-username": "fake-user",
+  "browserstack-access-key": "fake-key"
+};
 
-addTestManagementTools(mockServer);
+// Create a mock server for all tool calls
+const mockServer = { server: { getClientVersion: () => "test-version" } } as any;
+mockServer.tool = vi.fn();
+addTestManagementTools(mockServer, mockConfig);
 
 vi.mock('../../src/tools/testmanagement-utils/create-testrun', () => ({
   createTestRun: vi.fn(),
@@ -173,28 +176,22 @@ describe('createTestCaseTool', () => {
 
   it('should successfully create a test case', async () => {
     (createTestCase as Mock).mockResolvedValue(mockCallToolResult);
-
-    const result = await createTestCaseTool(validArgs, mockServer);
-
+    const result = await createTestCaseTool(validArgs, mockConfig, mockServer);
     expect(sanitizeArgs).toHaveBeenCalledWith(validArgs);
-    expect(createTestCase).toHaveBeenCalledWith(validArgs, mockServer);
+    expect(createTestCase).toHaveBeenCalledWith(validArgs, mockConfig);
     expect(result).toBe(mockCallToolResult);
   });
 
   it('should handle API errors while creating test case', async () => {
     (createTestCase as Mock).mockRejectedValue(new Error('API Error'));
-
-    const result = await createTestCaseTool(validArgs, mockServer);
-
+    const result = await createTestCaseTool(validArgs, mockConfig, mockServer);
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Failed to create test case: API Error');
   });
 
   it('should handle unknown error while creating test case', async () => {
     (createTestCase as Mock).mockRejectedValue('unexpected');
-
-    const result = await createTestCaseTool(validArgs, mockServer);
-
+    const result = await createTestCaseTool(validArgs, mockConfig, mockServer);
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Unknown error');
   });
@@ -226,38 +223,29 @@ describe('createProjectOrFolderTool', () => {
 
   it('should successfully create a project', async () => {
     (createProjectOrFolder as Mock).mockResolvedValue(mockProjectResponse);
-
-    const result = await createProjectOrFolderTool(validProjectArgs, mockServer);
-
-    expect(createProjectOrFolder).toHaveBeenCalledWith(validProjectArgs, mockServer);
+    const result = await createProjectOrFolderTool(validProjectArgs, mockConfig, mockServer);
+    expect(createProjectOrFolder).toHaveBeenCalledWith(validProjectArgs, mockConfig);
     expect(result.content?.[0]?.text).toContain('Project created with identifier=proj-123');
   });
 
   it('should successfully create a folder', async () => {
     (createProjectOrFolder as Mock).mockResolvedValue(mockFolderResponse);
-
-    const result = await createProjectOrFolderTool(validFolderArgs, mockServer);
-
-    expect(createProjectOrFolder).toHaveBeenCalledWith(validFolderArgs, mockServer);
+    const result = await createProjectOrFolderTool(validFolderArgs, mockConfig, mockServer);
+    expect(createProjectOrFolder).toHaveBeenCalledWith(validFolderArgs, mockConfig);
     expect(result.content?.[0]?.text).toContain('Folder created: ID=fold-123');
   });
 
   it('should handle error while creating project or folder', async () => {
     (createProjectOrFolder as Mock).mockRejectedValue(new Error('Failed to create project/folder'));
-
-    const result = await createProjectOrFolderTool(validProjectArgs, mockServer);
-
+    const result = await createProjectOrFolderTool(validProjectArgs, mockConfig, mockServer);
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain(
       'Failed to create project/folder: Failed to create project/folder. Please open an issue on GitHub if the problem persists'
     );
   });
-
   it('should handle unknown error while creating project or folder', async () => {
     (createProjectOrFolder as Mock).mockRejectedValue('some unknown error');
-
-    const result = await createProjectOrFolderTool(validProjectArgs, mockServer);
-
+    const result = await createProjectOrFolderTool(validProjectArgs, mockConfig, mockServer);
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain(
       'Failed to create project/folder: Unknown error. Please open an issue on GitHub if the problem persists'
@@ -283,11 +271,8 @@ describe('listTestCases util', () => {
       ],
       isError: false,
     });
-
     const args = { project_identifier: 'PR-1', status: 'active', p: 1 };
-    const result = await listTestCases(args as any, mockServer);
-
-    expect(listTestCases).toHaveBeenCalledWith(args, mockServer);
+    const result = await listTestCasesTool(args as any, mockConfig, mockServer);
     expect(result.content?.[0]?.text).toContain('Found 2 test case(s):');
     expect(result.content?.[0]?.text).toContain('TC-1: Test One [functional | high]');
     expect(result.content?.[1]?.text).toBe(JSON.stringify(mockCases, null, 2));
@@ -300,9 +285,7 @@ describe('listTestCases util', () => {
       ],
       isError: true,
     });
-
-    const result = await listTestCases({ project_identifier: 'PR-1' } as any, mockServer);
-
+    const result = await listTestCasesTool({ project_identifier: 'PR-1' } as any, mockConfig, mockServer);
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Failed to list test cases: Network Error');
   });
@@ -328,33 +311,52 @@ describe('createTestRunTool', () => {
 
   it('should successfully create a test run', async () => {
     (createTestRun as Mock).mockResolvedValue(successRunResult);
-
-    const result = await createTestRunTool(validRunArgs as any, mockServer);
-
-    expect(createTestRun).toHaveBeenCalledWith(validRunArgs, mockServer);
+    const runArgs = {
+      project_identifier: validRunArgs.project_identifier,
+      test_run: {
+        name: validRunArgs.run_name,
+        test_cases: validRunArgs.test_cases,
+        environment: validRunArgs.environment,
+        metadata: validRunArgs.metadata
+      }
+    };
+    const result = await createTestRunTool(runArgs, mockConfig, mockServer);
     expect(result).toBe(successRunResult);
   });
 
   it('should handle API errors while creating test run', async () => {
     (createTestRun as Mock).mockRejectedValue(new Error('API Error'));
-
-    const result = await createTestRunTool(validRunArgs as any, mockServer);
-
+    const runArgs = {
+      project_identifier: validRunArgs.project_identifier,
+      test_run: {
+        name: validRunArgs.run_name,
+        test_cases: validRunArgs.test_cases,
+        environment: validRunArgs.environment,
+        metadata: validRunArgs.metadata
+      }
+    };
+    const result = await createTestRunTool(runArgs, mockConfig, mockServer);
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Failed to create test run: API Error');
   });
 
   it('should handle unknown error while creating test run', async () => {
     (createTestRun as Mock).mockRejectedValue('unexpected');
-
-    const result = await createTestRunTool(validRunArgs as any, mockServer);
-
+    const runArgs = {
+      project_identifier: validRunArgs.project_identifier,
+      test_run: {
+        name: validRunArgs.run_name,
+        test_cases: validRunArgs.test_cases,
+        environment: validRunArgs.environment,
+        metadata: validRunArgs.metadata
+      }
+    };
+    const result = await createTestRunTool(runArgs, mockConfig, mockServer);
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Unknown error');
   });
 });
 
-
 describe('listTestRunsTool', () => {
   beforeEach(() => {
     vi.clearAllMocks();
@@ -374,9 +376,7 @@ describe('listTestRunsTool', () => {
       ],
       isError: false,
     });
-
-    const result = await listTestRunsTool({ project_identifier: projectId } as any, mockServer);
-    expect(listTestRuns).toHaveBeenCalledWith({ project_identifier: projectId }, mockServer);
+    const result = await listTestRunsTool({ project_identifier: projectId }, mockConfig, mockServer);
     expect(result.isError).toBe(false);
     expect(result.content?.[0]?.text).toContain('Found 2 test run(s):');
     expect(result.content?.[1]?.text).toBe(JSON.stringify(mockRuns, null, 2));
@@ -384,7 +384,7 @@ describe('listTestRunsTool', () => {
 
   it('should handle errors', async () => {
     (listTestRuns as Mock).mockRejectedValue(new Error('Network Error'));
-    const result = await listTestRunsTool({ project_identifier: projectId } as any, mockServer);
+    const result = await listTestRunsTool({ project_identifier: projectId }, mockConfig, mockServer);
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Failed to list test runs: Network Error');
   });
@@ -410,9 +410,15 @@ describe('updateTestRunTool', () => {
       ],
       isError: false,
     });
-
-    const result = await updateTestRunTool(args as any, mockServer);
-    expect(updateTestRun).toHaveBeenCalledWith(args, mockServer);
+    const updateArgs = {
+      project_identifier: args.project_identifier,
+      test_run_id: args.test_run_id,
+      test_run: {
+        name: args.test_run.name,
+        run_state: "in_progress" as const
+      }
+    };
+    const result = await updateTestRunTool(updateArgs, mockConfig, mockServer);
     expect(result.isError).toBe(false);
     expect(result.content?.[0]?.text).toContain(`Successfully updated test run ${args.test_run_id}`);
     expect(result.content?.[1]?.text).toBe(JSON.stringify(updated, null, 2));
@@ -420,14 +426,20 @@ describe('updateTestRunTool', () => {
 
   it('should handle errors', async () => {
     (updateTestRun as Mock).mockRejectedValue(new Error('API Error'));
-    const result = await updateTestRunTool(args as any, mockServer);
+    const updateArgs = {
+      project_identifier: args.project_identifier,
+      test_run_id: args.test_run_id,
+      test_run: {
+        name: args.test_run.name,
+        run_state: "in_progress" as const
+      }
+    };
+    const result = await updateTestRunTool(updateArgs, mockConfig, mockServer);
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Failed to update test run: API Error');
   });
 });
 
-
-
 describe('addTestResultTool', () => {
   beforeEach(() => {
     vi.clearAllMocks();
@@ -453,27 +465,14 @@ describe('addTestResultTool', () => {
 
   it('should successfully add a test result', async () => {
     (addTestResult as Mock).mockResolvedValue(successAddResult);
-
-    const result = await addTestResultTool(validArgs as any, mockServer);
-
-    expect(addTestResult).toHaveBeenCalledWith(validArgs, mockServer);
-    expect(result).toBe(successAddResult);
-  });
-
-  it('should handle API errors gracefully', async () => {
-    (addTestResult as Mock).mockRejectedValue(new Error('Network Error'));
-
-    const result = await addTestResultTool(validArgs as any, mockServer);
-
-    expect(result.isError).toBe(true);
-    expect(result.content?.[0]?.text).toContain('Failed to add test result: Network Error');
+    const result = await addTestResultTool(validArgs, mockConfig, mockServer);
+    expect(result.isError).toBe(false);
+    expect(result.content?.[0]?.text).toContain('Successfully added test result to test run run-456');
   });
 
   it('should handle unknown errors gracefully', async () => {
     (addTestResult as Mock).mockRejectedValue('unexpected');
-
-    const result = await addTestResultTool(validArgs as any, mockServer);
-
+    const result = await addTestResultTool(validArgs, mockConfig, mockServer);
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain('Unknown error');
   });
@@ -489,7 +488,6 @@ const mockContext = { sendNotification: vi.fn(), _meta: { progressToken: "test-p
 
 describe("uploadProductRequirementFileTool", () => {
   beforeEach(() => vi.resetAllMocks());
-
   it("returns error when file does not exist", async () => {
     (uploadFile as Mock).mockResolvedValue({
       content: [
@@ -497,12 +495,10 @@ describe("uploadProductRequirementFileTool", () => {
       ],
       isError: true,
     });
-    
-    const res = await uploadProductRequirementFileTool({ project_identifier: testProjectId, file_path: testFilePath }, mockServer);
+    const res = await uploadProductRequirementFileTool({ project_identifier: testProjectId, file_path: testFilePath }, mockConfig, mockServer);
     expect(res.isError).toBe(true);
     expect(res.content?.[0]?.text).toContain("does not exist");
   });
-
   it("uploads file and returns metadata", async () => {
     const mockSuccessResponse = {
       content: [
@@ -520,30 +516,24 @@ describe("uploadProductRequirementFileTool", () => {
       ],
       isError: false,
     };
-
     (uploadFile as Mock).mockResolvedValue(mockSuccessResponse);
-    
-    const res = await uploadProductRequirementFileTool({ project_identifier: testProjectId, file_path: testFilePath }, mockServer);
-    expect(uploadFile).toHaveBeenCalledWith({ project_identifier: testProjectId, file_path: testFilePath }, mockServer);
+    const res = await uploadProductRequirementFileTool({ project_identifier: testProjectId, file_path: testFilePath }, mockConfig, mockServer);
+    expect(uploadFile).toHaveBeenCalledWith({ project_identifier: testProjectId, file_path: testFilePath }, mockConfig);
     expect(res.isError ?? false).toBe(false);
     expect(res.content?.[1]?.text).toContain("documentID");
   });
 });
 
-// Tests for createTestCasesFromFileTool
 describe("createTestCasesFromFileTool", () => {
   beforeEach(() => vi.resetAllMocks());
-
   it("returns error when document is not in signedUrlMap", async () => {
     signedUrlMap.clear();
     (createTestCasesFromFile as Mock).mockRejectedValue(new Error("Re-Upload the file"));
-    
     const args = { documentId: testDocumentId, folderId: testFolderId, projectReferenceId: testProjectId };
-    const res = await createTestCasesFromFileTool(args as any, mockContext, mockServer);
+    const res = await createTestCasesFromFileTool(args as any, mockContext, mockConfig, mockServer);
     expect(res.isError).toBe(true);
     expect(res.content?.[0]?.text).toContain("Re-Upload the file");
   });
-
   it("creates test cases from a file successfully", async () => {
     signedUrlMap.set(testDocumentId, { fileId: mockFileId, downloadUrl: mockDownloadUrl });
     mockedAxios.get.mockResolvedValueOnce({
@@ -573,14 +563,12 @@ describe("createTestCasesFromFileTool", () => {
       content: [{ type: "text", text: "Total of 5 test cases created in 2 scenarios." }],
       isError: false
     });
-    
-    const res = await createTestCasesFromFileTool(args as any, mockContext, mockServer);
+    const res = await createTestCasesFromFileTool(args as any, mockContext, mockConfig, mockServer);
     expect(res.isError ?? false).toBe(false);
     expect(res.content?.[0]?.text).toContain("test cases created");
   });
 });
 
-// Tests for createLCAStepsTool
 describe("createLCAStepsTool", () => {
   beforeEach(() => vi.resetAllMocks());
 
@@ -622,9 +610,9 @@ describe("createLCAStepsTool", () => {
 
     (createLCASteps as Mock).mockResolvedValue(mockResponse);
 
-    const result = await createLCAStepsTool(validArgs as any, mockContext, mockServer);
+    const result = await createLCAStepsTool(validArgs as any, mockContext, mockConfig, mockServer);
 
-    expect(createLCASteps).toHaveBeenCalledWith(validArgs, mockContext, mockServer);
+    expect(createLCASteps).toHaveBeenCalledWith(validArgs, mockContext, mockConfig);
     expect(result).toBe(mockResponse);
     expect(result.isError).toBe(false);
   });
@@ -632,7 +620,7 @@ describe("createLCAStepsTool", () => {
   it("handles errors when creating LCA steps", async () => {
     (createLCASteps as Mock).mockRejectedValue(new Error("API Error"));
 
-    const result = await createLCAStepsTool(validArgs as any, mockContext, mockServer);
+    const result = await createLCAStepsTool(validArgs as any, mockContext, mockConfig, mockServer);
 
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain("Failed to create LCA steps: API Error");
@@ -641,7 +629,7 @@ describe("createLCAStepsTool", () => {
   it("handles unknown errors when creating LCA steps", async () => {
     (createLCASteps as Mock).mockRejectedValue("unexpected error");
 
-    const result = await createLCAStepsTool(validArgs as any, mockContext, mockServer);
+    const result = await createLCAStepsTool(validArgs as any, mockContext, mockConfig, mockServer);
 
     expect(result.isError).toBe(true);
     expect(result.content?.[0]?.text).toContain("Failed to create LCA steps: Unknown error");
@@ -659,9 +647,8 @@ describe("createLCAStepsTool", () => {
 
     (createLCASteps as Mock).mockResolvedValue(mockResponse);
 
-    const result = await createLCAStepsTool(argsWithoutWait as any, mockContext, mockServer);
+    const result = await createLCAStepsTool(argsWithoutWait as any, mockContext, mockConfig, mockServer);
 
-    expect(createLCASteps).toHaveBeenCalledWith(argsWithoutWait, mockContext, mockServer);
     expect(result.content?.[1]?.text).toContain("Check the BrowserStack Test Management UI");
   });
 
@@ -677,17 +664,23 @@ describe("createLCAStepsTool", () => {
 
     (createLCASteps as Mock).mockResolvedValue(mockResponse);
 
-    const result = await createLCAStepsTool(argsWithCustomWait as any, mockContext, mockServer);
+    const result = await createLCAStepsTool(argsWithCustomWait as any, mockContext, mockConfig, mockServer);
 
-    expect(createLCASteps).toHaveBeenCalledWith(argsWithCustomWait, mockContext, mockServer);
     expect(result.content?.[1]?.text).toContain("within 5 minutes");
   });
 });
 
-vi.mock('../../src/tools/testmanagement-utils/upload-file', () => ({
-  uploadFile: vi.fn(),
-  UploadFileSchema: {
-    parse: (args: any) => args,
-    shape: {},
-  },
-}));
+vi.mock('../../src/tools/testmanagement-utils/upload-file', () => {
+  const uploadFile = vi.fn();
+  return {
+    uploadFile,
+    UploadFileSchema: {
+      parse: (args: any) => args,
+      shape: {},
+    },
+    __esModule: true,
+    default: { uploadFile },
+  };
+});
+
+// Get the mocked uploadFile

From d7de7133a4a5e888dff5b32b8fcf970c6164c614 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 11:19:39 +0530
Subject: [PATCH 20/64] Refactor openBrowser function to restore its
 implementation and ensure proper URL opening

---
 src/tools/applive-utils/start-session.ts | 68 ++++++++++++------------
 1 file changed, 34 insertions(+), 34 deletions(-)

diff --git a/src/tools/applive-utils/start-session.ts b/src/tools/applive-utils/start-session.ts
index 1179f0b..d711d1c 100644
--- a/src/tools/applive-utils/start-session.ts
+++ b/src/tools/applive-utils/start-session.ts
@@ -92,40 +92,40 @@ export async function startSession(
   });
   const launchUrl = `https://app-live.browserstack.com/dashboard#${params.toString()}&device=${deviceParam}`;
 
-  // openBrowser(launchUrl);
+  openBrowser(launchUrl);
   return launchUrl + note;
 }
 
-// /**
-//  * Opens the launch URL in the default browser.
-//  * @param launchUrl - The URL to open.
-//  * @throws Will throw an error if the browser fails to open.
-//  */
-// function openBrowser(launchUrl: string): void {
-//   try {
-//     const command =
-//       process.platform === "darwin"
-//         ? ["open", launchUrl]
-//         : process.platform === "win32"
-//           ? ["cmd", "/c", "start", launchUrl]
-//           : ["xdg-open", launchUrl];
-
-//     // nosemgrep:javascript.lang.security.detect-child-process.detect-child-process
-//     const child = childProcess.spawn(command[0], command.slice(1), {
-//       stdio: "ignore",
-//       detached: true,
-//     });
-
-//     child.on("error", (error) => {
-//       logger.error(
-//         `Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`,
-//       );
-//     });
-
-//     child.unref();
-//   } catch (error) {
-//     logger.error(
-//       `Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`,
-//     );
-//   }
-// }
+/**
+ * Opens the launch URL in the default browser.
+ * @param launchUrl - The URL to open.
+ * @throws Will throw an error if the browser fails to open.
+ */
+function openBrowser(launchUrl: string): void {
+  try {
+    const command =
+      process.platform === "darwin"
+        ? ["open", launchUrl]
+        : process.platform === "win32"
+          ? ["cmd", "/c", "start", launchUrl]
+          : ["xdg-open", launchUrl];
+
+    // nosemgrep:javascript.lang.security.detect-child-process.detect-child-process
+    const child = childProcess.spawn(command[0], command.slice(1), {
+      stdio: "ignore",
+      detached: true,
+    });
+
+    child.on("error", (error) => {
+      logger.error(
+        `Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`,
+      );
+    });
+
+    child.unref();
+  } catch (error) {
+    logger.error(
+      `Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`,
+    );
+  }
+}

From 08f6bbcd14bf073e2fb1a1a433c72478d62c7388 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 11:21:20 +0530
Subject: [PATCH 21/64] Add child_process import to start-session.ts for
 process management

---
 src/tools/applive-utils/start-session.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/tools/applive-utils/start-session.ts b/src/tools/applive-utils/start-session.ts
index d711d1c..e13f613 100644
--- a/src/tools/applive-utils/start-session.ts
+++ b/src/tools/applive-utils/start-session.ts
@@ -9,6 +9,7 @@ import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { findDeviceByName } from "./device-search.js";
 import { pickVersion } from "./version-utils.js";
 import { DeviceEntry } from "./types.js";
+import childProcess from "child_process";
 
 interface StartSessionArgs {
   appPath: string;

From e07b67f05be987479f16b5b794936892088e861e Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 11:24:47 +0530
Subject: [PATCH 22/64] Refactor config parameter types to use
 BrowserStackConfig across multiple modules

---
 src/lib/api.ts                           |  3 ++-
 src/tools/accessibility.ts               |  2 +-
 src/tools/applive-utils/start-session.ts |  4 +++-
 src/tools/applive.ts                     |  6 ++++--
 src/tools/bstack-sdk.ts                  |  5 +++--
 src/tools/live-utils/start-session.ts    |  4 +++-
 src/tools/live.ts                        | 10 ++++++----
 src/tools/testmanagement.ts              | 24 +++++++++++++-----------
 8 files changed, 35 insertions(+), 23 deletions(-)

diff --git a/src/lib/api.ts b/src/lib/api.ts
index 06b5a0f..39285ba 100644
--- a/src/lib/api.ts
+++ b/src/lib/api.ts
@@ -1,9 +1,10 @@
 import { getBrowserStackAuth } from "./get-auth.js";
+import { BrowserStackConfig } from "../lib/types.js";
 
 export async function getLatestO11YBuildInfo(
   buildName: string,
   projectName: string,
-  config: any,
+  config: BrowserStackConfig,
 ) {
   const buildsUrl = `https://api-observability.browserstack.com/ext/v1/builds/latest?build_name=${encodeURIComponent(
     buildName,
diff --git a/src/tools/accessibility.ts b/src/tools/accessibility.ts
index 53d2803..304d843 100644
--- a/src/tools/accessibility.ts
+++ b/src/tools/accessibility.ts
@@ -13,7 +13,7 @@ async function runAccessibilityScan(
   name: string,
   pageURL: string,
   context: any,
-  config: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   // Create scanner and set auth on the go
   const scanner = new AccessibilityScanner();
diff --git a/src/tools/applive-utils/start-session.ts b/src/tools/applive-utils/start-session.ts
index e13f613..8fbd1eb 100644
--- a/src/tools/applive-utils/start-session.ts
+++ b/src/tools/applive-utils/start-session.ts
@@ -10,6 +10,8 @@ import { findDeviceByName } from "./device-search.js";
 import { pickVersion } from "./version-utils.js";
 import { DeviceEntry } from "./types.js";
 import childProcess from "child_process";
+import { BrowserStackConfig } from "../../lib/types.js";
+
 
 interface StartSessionArgs {
   appPath: string;
@@ -19,7 +21,7 @@ interface StartSessionArgs {
 }
 
 interface StartSessionOptions {
-  config: any;
+  config: BrowserStackConfig;
 }
 
 /**
diff --git a/src/tools/applive.ts b/src/tools/applive.ts
index d0d40ba..99b097d 100644
--- a/src/tools/applive.ts
+++ b/src/tools/applive.ts
@@ -5,6 +5,8 @@ import fs from "fs";
 import { startSession } from "./applive-utils/start-session.js";
 import logger from "../logger.js";
 import { trackMCP } from "../lib/instrumentation.js";
+import { BrowserStackConfig } from "../lib/types.js";
+
 
 /**
  * Launches an App Live Session on BrowserStack.
@@ -16,7 +18,7 @@ export async function startAppLiveSession(
     appPath: string;
     desiredPhone: string;
   },
-  config: any,
+  config: BrowserStackConfig,
 ): Promise<CallToolResult> {
   if (!args.desiredPlatform) {
     throw new Error("You must provide a desiredPlatform.");
@@ -69,7 +71,7 @@ export async function startAppLiveSession(
   };
 }
 
-export default function addAppLiveTools(server: McpServer, config: any) {
+export default function addAppLiveTools(server: McpServer, config: BrowserStackConfig) {
   server.tool(
     "runAppLiveSession",
     "Use this tool when user wants to manually check their app on a particular mobile device using BrowserStack's cloud infrastructure. Can be used to debug crashes, slow performance, etc.",
diff --git a/src/tools/bstack-sdk.ts b/src/tools/bstack-sdk.ts
index a441a80..881c153 100644
--- a/src/tools/bstack-sdk.ts
+++ b/src/tools/bstack-sdk.ts
@@ -24,6 +24,7 @@ import {
   getPercyInstructions,
 } from "./sdk-utils/percy/instructions.js";
 import { getBrowserStackAuth } from "../lib/get-auth.js";
+import { BrowserStackConfig } from "../lib/types.js";
 
 /**
  * BrowserStack SDK hooks into your test framework to seamlessly run tests on BrowserStack.
@@ -42,7 +43,7 @@ export async function bootstrapProjectWithSDK({
   detectedLanguage: SDKSupportedLanguage;
   desiredPlatforms: string[];
   enablePercy: boolean;
-  config: any;
+  config: BrowserStackConfig;
 }): Promise<CallToolResult> {
   // Get credentials from config
   const authString = getBrowserStackAuth(config);
@@ -160,7 +161,7 @@ function formatFinalInstructions(combinedInstructions: string): CallToolResult {
   };
 }
 
-export default function addSDKTools(server: McpServer, config: any) {
+export default function addSDKTools(server: McpServer, config: BrowserStackConfig) {
   server.tool(
     "runTestsOnBrowserStack",
     "Use this tool to get instructions for running tests on BrowserStack and browserstack percy",
diff --git a/src/tools/live-utils/start-session.ts b/src/tools/live-utils/start-session.ts
index 3c9eab0..14f5960 100644
--- a/src/tools/live-utils/start-session.ts
+++ b/src/tools/live-utils/start-session.ts
@@ -10,6 +10,8 @@ import {
 import { isLocalURL } from "../../lib/local.js";
 
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
+import { BrowserStackConfig } from "../../lib/types.js";
+
 /**
  * Prepares local tunnel setup based on URL type
  */
@@ -28,7 +30,7 @@ async function prepareLocalTunnel(url: string): Promise<boolean> {
  */
 export async function startBrowserSession(
   args: DesktopSearchArgs | MobileSearchArgs,
-  config: any,
+  config: BrowserStackConfig,
 ): Promise<string> {
   const entry =
     args.platformType === PlatformType.DESKTOP
diff --git a/src/tools/live.ts b/src/tools/live.ts
index 8b32efa..050bb25 100644
--- a/src/tools/live.ts
+++ b/src/tools/live.ts
@@ -5,6 +5,8 @@ import logger from "../logger.js";
 import { startBrowserSession } from "./live-utils/start-session.js";
 import { PlatformType } from "./live-utils/types.js";
 import { trackMCP } from "../lib/instrumentation.js";
+import { BrowserStackConfig } from "../lib/types.js";
+
 
 // Define the schema shape
 const LiveArgsShape = {
@@ -41,7 +43,7 @@ const LiveArgsSchema = z.object(LiveArgsShape);
  */
 async function launchDesktopSession(
   args: z.infer<typeof LiveArgsSchema>,
-  config: any,
+  config: BrowserStackConfig,
 ): Promise<string> {
   if (!args.desiredBrowser)
     throw new Error("You must provide a desiredBrowser");
@@ -66,7 +68,7 @@ async function launchDesktopSession(
  */
 async function launchMobileSession(
   args: z.infer<typeof LiveArgsSchema>,
-  config: any,
+  config: BrowserStackConfig,
 ): Promise<string> {
   if (!args.desiredDevice) throw new Error("You must provide a desiredDevice");
 
@@ -86,7 +88,7 @@ async function launchMobileSession(
 /**
  * Handles the core logic for running a browser session
  */
-async function runBrowserSession(rawArgs: any, config: any) {
+async function runBrowserSession(rawArgs: any, config: BrowserStackConfig) {
   // Validate and narrow
   const args = LiveArgsSchema.parse(rawArgs);
 
@@ -106,7 +108,7 @@ async function runBrowserSession(rawArgs: any, config: any) {
   };
 }
 
-export default function addBrowserLiveTools(server: McpServer, config: any) {
+export default function addBrowserLiveTools(server: McpServer, config: BrowserStackConfig) {
   server.tool(
     "runBrowserLiveSession",
     "Launch a BrowserStack Live session (desktop or mobile).",
diff --git a/src/tools/testmanagement.ts b/src/tools/testmanagement.ts
index 31b0b6f..2f90ffc 100644
--- a/src/tools/testmanagement.ts
+++ b/src/tools/testmanagement.ts
@@ -51,6 +51,8 @@ import {
   createLCASteps,
   CreateLCAStepsSchema,
 } from "./testmanagement-utils/create-lca-steps.js";
+import { BrowserStackConfig } from "../lib/types.js";
+
 
 //TODO: Moving the traceMCP and catch block to the parent(server) function
 
@@ -59,7 +61,7 @@ import {
  */
 export async function createProjectOrFolderTool(
   args: z.infer<typeof CreateProjFoldSchema>,
-  config: any,
+  config: BrowserStackConfig,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
@@ -98,7 +100,7 @@ export async function createProjectOrFolderTool(
  */
 export async function createTestCaseTool(
   args: TestCaseCreateRequest,
-  config: any,
+  config: BrowserStackConfig,
   server: McpServer,
 ): Promise<CallToolResult> {
   // Sanitize input arguments
@@ -135,7 +137,7 @@ export async function createTestCaseTool(
 
 export async function listTestCasesTool(
   args: z.infer<typeof ListTestCasesSchema>,
-  config: any,
+  config: BrowserStackConfig,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
@@ -168,7 +170,7 @@ export async function listTestCasesTool(
  */
 export async function createTestRunTool(
   args: z.infer<typeof CreateTestRunSchema>,
-  config: any,
+  config: BrowserStackConfig,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
@@ -201,7 +203,7 @@ export async function createTestRunTool(
  */
 export async function listTestRunsTool(
   args: z.infer<typeof ListTestRunsSchema>,
-  config: any,
+  config: BrowserStackConfig,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
@@ -236,7 +238,7 @@ export async function listTestRunsTool(
  */
 export async function updateTestRunTool(
   args: z.infer<typeof UpdateTestRunSchema>,
-  config: any,
+  config: BrowserStackConfig,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
@@ -269,7 +271,7 @@ export async function updateTestRunTool(
  */
 export async function addTestResultTool(
   args: z.infer<typeof AddTestResultSchema>,
-  config: any,
+  config: BrowserStackConfig,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
@@ -302,7 +304,7 @@ export async function addTestResultTool(
  */
 export async function uploadProductRequirementFileTool(
   args: z.infer<typeof UploadFileSchema>,
-  config: any,
+  config: BrowserStackConfig,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
@@ -341,7 +343,7 @@ export async function uploadProductRequirementFileTool(
 export async function createTestCasesFromFileTool(
   args: z.infer<typeof CreateTestCasesFromFileSchema>,
   context: any,
-  config: any,
+  config: BrowserStackConfig,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
@@ -374,7 +376,7 @@ export async function createTestCasesFromFileTool(
 export async function createLCAStepsTool(
   args: z.infer<typeof CreateLCAStepsSchema>,
   context: any,
-  config: any,
+  config: BrowserStackConfig,
   server: McpServer,
 ): Promise<CallToolResult> {
   try {
@@ -405,7 +407,7 @@ export async function createLCAStepsTool(
 /**
  * Registers both project/folder and test-case tools.
  */
-export default function addTestManagementTools(server: McpServer, config: any) {
+export default function addTestManagementTools(server: McpServer, config: BrowserStackConfig) {
   server.tool(
     "createProjectOrFolder",
     "Create a project and/or folder in BrowserStack Test Management.",

From 672f4fcaeb86d2e91adbb4671c363198aa0439ed Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 11:27:25 +0530
Subject: [PATCH 23/64] Enhance local tunnel setup by adding username and
 password handling for BrowserStack integration

---
 src/tools/live-utils/start-session.ts | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/src/tools/live-utils/start-session.ts b/src/tools/live-utils/start-session.ts
index 14f5960..219d1e2 100644
--- a/src/tools/live-utils/start-session.ts
+++ b/src/tools/live-utils/start-session.ts
@@ -7,7 +7,12 @@ import {
   MobileEntry,
   PlatformType,
 } from "./types.js";
-import { isLocalURL } from "../../lib/local.js";
+import {
+  isLocalURL,
+  ensureLocalBinarySetup,
+  killExistingBrowserStackLocalProcesses,
+} from "../../lib/local.js";
+
 
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { BrowserStackConfig } from "../../lib/types.js";
@@ -15,12 +20,12 @@ import { BrowserStackConfig } from "../../lib/types.js";
 /**
  * Prepares local tunnel setup based on URL type
  */
-async function prepareLocalTunnel(url: string): Promise<boolean> {
+async function prepareLocalTunnel(url: string,username: string, password: string): Promise<boolean> {
   const isLocal = isLocalURL(url);
   if (isLocal) {
-    throw new Error(
-      "Local URLs are not supported for BrowserStack Remote MCP. Please use a public URL or the local MCP.",
-    );
+    await ensureLocalBinarySetup(username, password);
+  } else {
+    await killExistingBrowserStackLocalProcesses();
   }
   return isLocal;
 }
@@ -47,7 +52,7 @@ export async function startBrowserSession(
     );
   }
 
-  const isLocal = await prepareLocalTunnel(args.url);
+  const isLocal = await prepareLocalTunnel(args.url, username, password);
 
   const url =
     args.platformType === PlatformType.DESKTOP

From 1e8871b120f14bfead4f4a4705b22ad60510b86d Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 11:28:25 +0530
Subject: [PATCH 24/64] Refactor openBrowser function to improve error handling
 and restore its implementation

---
 src/tools/live-utils/start-session.ts | 46 ++++++++++++++-------------
 1 file changed, 24 insertions(+), 22 deletions(-)

diff --git a/src/tools/live-utils/start-session.ts b/src/tools/live-utils/start-session.ts
index 219d1e2..9bebbb8 100644
--- a/src/tools/live-utils/start-session.ts
+++ b/src/tools/live-utils/start-session.ts
@@ -1,3 +1,5 @@
+import logger from "../../logger.js";
+import childProcess from "child_process";
 import { filterDesktop } from "./desktop-filter.js";
 import { filterMobile } from "./mobile-filter.js";
 import {
@@ -63,7 +65,7 @@ export async function startBrowserSession(
         )
       : buildMobileUrl(args as MobileSearchArgs, entry as MobileEntry, isLocal);
 
-  // openBrowser(url);
+  openBrowser(url);
   return entry.notes ? `${url}, ${entry.notes}` : url;
 }
 
@@ -115,25 +117,25 @@ function buildMobileUrl(
 
 // ——— Open a browser window ———
 
-// function openBrowser(launchUrl: string): void {
-//   try {
-//     const command =
-//       process.platform === "darwin"
-//         ? ["open", launchUrl]
-//         : process.platform === "win32"
-//           ? ["cmd", "/c", "start", launchUrl]
-//           : ["xdg-open", launchUrl];
+function openBrowser(launchUrl: string): void {
+  try {
+    const command =
+      process.platform === "darwin"
+        ? ["open", launchUrl]
+        : process.platform === "win32"
+          ? ["cmd", "/c", "start", launchUrl]
+          : ["xdg-open", launchUrl];
 
-//     // nosemgrep:javascript.lang.security.detect-child-process.detect-child-process
-//     const child = childProcess.spawn(command[0], command.slice(1), {
-//       stdio: "ignore",
-//       detached: true,
-//     });
-//     child.on("error", (err) =>
-//       logger.error(`Failed to open browser: ${err}. URL: ${launchUrl}`),
-//     );
-//     child.unref();
-//   } catch (err) {
-//     logger.error(`Failed to launch browser: ${err}. URL: ${launchUrl}`);
-//   }
-// }
+    // nosemgrep:javascript.lang.security.detect-child-process.detect-child-process
+    const child = childProcess.spawn(command[0], command.slice(1), {
+      stdio: "ignore",
+      detached: true,
+    });
+    child.on("error", (err) =>
+      logger.error(`Failed to open browser: ${err}. URL: ${launchUrl}`),
+    );
+    child.unref();
+  } catch (err) {
+    logger.error(`Failed to launch browser: ${err}. URL: ${launchUrl}`);
+  }
+}

From 062dc616d1f7bac04b42bfedfc93c7ba46befff6 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 11:31:09 +0530
Subject: [PATCH 25/64] Refactor AccessibilityScanner to handle local URLs
 correctly and improve process management

---
 src/tools/accessiblity-utils/scanner.ts | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/tools/accessiblity-utils/scanner.ts b/src/tools/accessiblity-utils/scanner.ts
index 6962fe1..adcba15 100644
--- a/src/tools/accessiblity-utils/scanner.ts
+++ b/src/tools/accessiblity-utils/scanner.ts
@@ -1,7 +1,11 @@
 import axios from "axios";
 import { randomUUID } from "node:crypto";
 import logger from "../../logger.js";
-import { isLocalURL } from "../../lib/local.js";
+import {
+  isLocalURL,
+  ensureLocalBinarySetup,
+  killExistingBrowserStackLocalProcesses,
+} from "../../lib/local.js";
 import config from "../../config.js";
 
 export interface AccessibilityScanResponse {
@@ -45,9 +49,9 @@ export class AccessibilityScanner {
     }
 
     if (hasLocal) {
-      throw new Error(
-        "Local URLs are not supported for Browserstack Remote MCP For accessibility scan.",
-      );
+      await ensureLocalBinarySetup(this.auth.username,this.auth.password,localIdentifier);
+    } else {
+      await killExistingBrowserStackLocalProcesses();
     }
 
     const transformedUrlList = urlList.map((url) => {

From c163dad58e90021a52b8fc9914e41b3e2264212e Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 11:38:27 +0530
Subject: [PATCH 26/64] Refactor server initialization and enhance BrowserStack
 integration with improved error handling and environment variable checks

---
 src/index.ts                             | 247 +++--------------------
 src/tools/accessiblity-utils/scanner.ts  |   6 +-
 src/tools/applive-utils/start-session.ts |   1 -
 src/tools/applive.ts                     |   6 +-
 src/tools/bstack-sdk.ts                  |   5 +-
 src/tools/live-utils/start-session.ts    |   7 +-
 src/tools/live.ts                        |   6 +-
 src/tools/testmanagement.ts              |   6 +-
 tests/tools/applive.test.ts              |  16 +-
 9 files changed, 56 insertions(+), 244 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index de52ec2..17aadb5 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,11 +1,6 @@
 #!/usr/bin/env node
 
-import express from "express";
-import cors from "cors";
-import { randomUUID } from "node:crypto";
-import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
-import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
-import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 import { createRequire } from "module";
 const require = createRequire(import.meta.url);
 const packageJson = require("../package.json");
@@ -13,233 +8,39 @@ import "dotenv/config";
 import logger from "./logger.js";
 import { createMcpServer } from "./server-factory.js";
 
-const PORT = process.env.MCP_PORT ? parseInt(process.env.MCP_PORT) : 3000;
-const ALLOWED_ORIGINS = process.env.ALLOWED_ORIGINS?.split(",") || ["*"];
-const ALLOWED_HOSTS = process.env.ALLOWED_HOSTS?.split(",") || [
-  "127.0.0.1",
-  "localhost",
-];
 
-const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
 
-const app = express();
 
-app.use(
-  cors({
-    origin: ALLOWED_ORIGINS,
-    credentials: true,
-    exposedHeaders: ["mcp-session-id"],
-    allowedHeaders: [
-      "Content-Type",
-      "Authorization",
-      "mcp-session-id",
-      "browserstack-username",
-      "browserstack-access-key",
-    ],
-    methods: ["GET", "POST", "DELETE", "OPTIONS"],
-  }),
-);
+async function main() {
+  logger.info(
+    "Launching BrowserStack MCP server, version %s",
+    packageJson.version,
+  );
 
-app.use(express.json({ limit: "10mb" }));
+  const username = process.env.BROWSERSTACK_USERNAME;
+  const accessKey = process.env.BROWSERSTACK_AUTH_KEY;
 
-app.get("/health", (req, res) => {
-  res.json({
-    status: "ok",
-    version: packageJson.version,
-    timestamp: new Date().toISOString(),
-    transports: Object.keys(transports).length,
-  });
-});
-
-app.post("/mcp", async (req, res) => {
-  try {
-    const sessionId = req.headers["mcp-session-id"] as string | undefined;
-    const browserstackUsername = req.headers["browserstack-username"] as
-      | string
-      | undefined;
-    const browserstackAccessKey = req.headers["browserstack-access-key"] as
-      | string
-      | undefined;
-
-    if (!browserstackUsername || !browserstackAccessKey) {
-      logger.warn("Missing BrowserStack credentials in headers");
-      res.status(401).json({
-        jsonrpc: "2.0",
-        error: {
-          code: -32001,
-          message: "Unauthorized: Missing BrowserStack credentials",
-        },
-        id: null,
-      });
-      return;
-    }
-
-    let transport: StreamableHTTPServerTransport;
-
-    logger.info(`Received MCP POST request, sessionId: ${sessionId}`);
-
-    if (sessionId && transports[sessionId]) {
-      // Reuse existing transport
-      transport = transports[sessionId];
-      logger.info(`Reusing existing transport for session: ${sessionId}`);
-    } else if (!sessionId && isInitializeRequest(req.body)) {
-      // New initialization request
-      logger.info("Creating new MCP session");
-
-      transport = new StreamableHTTPServerTransport({
-        sessionIdGenerator: () => randomUUID(),
-        onsessioninitialized: (newSessionId) => {
-          logger.info(`Session initialized: ${newSessionId}`);
-          transports[newSessionId] = transport;
-        },
-      });
-
-      // Clean up transport when closed
-      transport.onclose = () => {
-        if (transport.sessionId) {
-          logger.info(
-            `Cleaning up transport for session: ${transport.sessionId}`,
-          );
-          delete transports[transport.sessionId];
-        }
-      };
-
-      // Create and connect MCP server
-      const server = createMcpServer({
-        "browserstack-username": browserstackUsername,
-        "browserstack-access-key": browserstackAccessKey,
-      }) as McpServer;
-
-      await server.connect(transport);
-
-      logger.info("MCP server connected to transport");
-    } else {
-      // Invalid request
-      logger.warn("Invalid MCP request - no valid session ID provided");
-      res.status(400).json({
-        jsonrpc: "2.0",
-        error: {
-          code: -32000,
-          message: "Bad Request: No valid session ID provided",
-        },
-        id: null,
-      });
-      return;
-    }
-
-    // Handle the request
-    await transport.handleRequest(req, res, req.body);
-  } catch (error) {
-    logger.error("Error handling MCP request:", error);
-    if (!res.headersSent) {
-      res.status(500).json({
-        jsonrpc: "2.0",
-        error: {
-          code: -32603,
-          message: "Internal server error",
-        },
-        id: null,
-      });
-    }
+  if (!username) {
+    throw new Error("BROWSERSTACK_USERNAME environment variable is required");
   }
-});
-
-// Reusable handler for GET and DELETE requests
-const handleSessionRequest = async (
-  req: express.Request,
-  res: express.Response,
-) => {
-  const sessionId = req.headers["mcp-session-id"] as string | undefined;
-  logger.info(`Received request for session: ${sessionId}`);
 
-  if (!sessionId || !transports[sessionId]) {
-    logger.warn(`Invalid or missing session ID: ${sessionId}`);
-    res.status(400).json({
-      jsonrpc: "2.0",
-      error: {
-        code: -32000,
-        message: "Invalid or missing session ID",
-      },
-      id: null,
-    });
-    return;
+  if (!accessKey) {
+    throw new Error("BROWSERSTACK_AUTH_KEY environment variable is required");
   }
 
-  try {
-    const transport = transports[sessionId];
-    logger.info(`Handling session request for: ${sessionId}`);
-    await transport.handleRequest(req, res);
-  } catch (error) {
-    logger.error("Error handling session request:", error);
-    if (!res.headersSent) {
-      res.status(500).json({
-        jsonrpc: "2.0",
-        error: {
-          code: -32603,
-          message: "Internal server error",
-        },
-        id: null,
-      });
-    }
-  }
-};
-
-app.get("/mcp", handleSessionRequest);
-
-app.delete("/mcp", handleSessionRequest);
-
-app.use((err: any, req: express.Request, res: express.Response) => {
-  logger.error("Express error:", err);
-  if (!res.headersSent) {
-    res.status(500).json({
-      jsonrpc: "2.0",
-      error: {
-        code: -32603,
-        message: "Internal server error",
-      },
-      id: null,
-    });
-  }
-});
-
-async function startServer() {
-  try {
-    const server = app.listen(PORT, () => {
-      logger.info(
-        `BrowserStack MCP HTTP Server v${packageJson.version} listening on port ${PORT}`,
-      );
-      logger.info(`Health check available at: http://localhost:${PORT}/health`);
-      logger.info(`MCP endpoint available at: http://localhost:${PORT}/mcp`);
-      logger.info(`Allowed origins: ${ALLOWED_ORIGINS.join(", ")}`);
-      logger.info(`Allowed hosts: ${ALLOWED_HOSTS.join(", ")}`);
-    });
-
-    process.on("SIGTERM", () => {
-      logger.info("Received SIGTERM, shutting down gracefully");
-      server.close(() => {
-        logger.info("HTTP server closed");
-        process.exit(0);
-      });
-    });
-
-    process.on("SIGINT", () => {
-      logger.info("Received SIGINT, shutting down gracefully");
-      server.close(() => {
-        logger.info("HTTP server closed");
-        process.exit(0);
-      });
-    });
-  } catch (error) {
-    logger.error("Failed to start server:", error);
-    process.exit(1);
-  }
+  const transport = new StdioServerTransport();
+  
+  const server = createMcpServer({
+    "browserstack-username": username,
+    "browserstack-access-key": accessKey,
+  });
+  
+  await server.connect(transport);
 }
 
+main().catch(console.error);
+
+// Ensure logs are flushed before exit
 process.on("exit", () => {
   logger.flush();
-});
-
-startServer().catch((error) => {
-  logger.error("Failed to start server:", error);
-  process.exit(1);
-});
+});
\ No newline at end of file
diff --git a/src/tools/accessiblity-utils/scanner.ts b/src/tools/accessiblity-utils/scanner.ts
index adcba15..6baa83a 100644
--- a/src/tools/accessiblity-utils/scanner.ts
+++ b/src/tools/accessiblity-utils/scanner.ts
@@ -49,7 +49,11 @@ export class AccessibilityScanner {
     }
 
     if (hasLocal) {
-      await ensureLocalBinarySetup(this.auth.username,this.auth.password,localIdentifier);
+      await ensureLocalBinarySetup(
+        this.auth.username,
+        this.auth.password,
+        localIdentifier,
+      );
     } else {
       await killExistingBrowserStackLocalProcesses();
     }
diff --git a/src/tools/applive-utils/start-session.ts b/src/tools/applive-utils/start-session.ts
index 8fbd1eb..6c647ba 100644
--- a/src/tools/applive-utils/start-session.ts
+++ b/src/tools/applive-utils/start-session.ts
@@ -12,7 +12,6 @@ import { DeviceEntry } from "./types.js";
 import childProcess from "child_process";
 import { BrowserStackConfig } from "../../lib/types.js";
 
-
 interface StartSessionArgs {
   appPath: string;
   desiredPlatform: "android" | "ios";
diff --git a/src/tools/applive.ts b/src/tools/applive.ts
index 99b097d..89a43f3 100644
--- a/src/tools/applive.ts
+++ b/src/tools/applive.ts
@@ -7,7 +7,6 @@ import logger from "../logger.js";
 import { trackMCP } from "../lib/instrumentation.js";
 import { BrowserStackConfig } from "../lib/types.js";
 
-
 /**
  * Launches an App Live Session on BrowserStack.
  */
@@ -71,7 +70,10 @@ export async function startAppLiveSession(
   };
 }
 
-export default function addAppLiveTools(server: McpServer, config: BrowserStackConfig) {
+export default function addAppLiveTools(
+  server: McpServer,
+  config: BrowserStackConfig,
+) {
   server.tool(
     "runAppLiveSession",
     "Use this tool when user wants to manually check their app on a particular mobile device using BrowserStack's cloud infrastructure. Can be used to debug crashes, slow performance, etc.",
diff --git a/src/tools/bstack-sdk.ts b/src/tools/bstack-sdk.ts
index 881c153..c3f9812 100644
--- a/src/tools/bstack-sdk.ts
+++ b/src/tools/bstack-sdk.ts
@@ -161,7 +161,10 @@ function formatFinalInstructions(combinedInstructions: string): CallToolResult {
   };
 }
 
-export default function addSDKTools(server: McpServer, config: BrowserStackConfig) {
+export default function addSDKTools(
+  server: McpServer,
+  config: BrowserStackConfig,
+) {
   server.tool(
     "runTestsOnBrowserStack",
     "Use this tool to get instructions for running tests on BrowserStack and browserstack percy",
diff --git a/src/tools/live-utils/start-session.ts b/src/tools/live-utils/start-session.ts
index 9bebbb8..5095bcb 100644
--- a/src/tools/live-utils/start-session.ts
+++ b/src/tools/live-utils/start-session.ts
@@ -15,14 +15,17 @@ import {
   killExistingBrowserStackLocalProcesses,
 } from "../../lib/local.js";
 
-
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { BrowserStackConfig } from "../../lib/types.js";
 
 /**
  * Prepares local tunnel setup based on URL type
  */
-async function prepareLocalTunnel(url: string,username: string, password: string): Promise<boolean> {
+async function prepareLocalTunnel(
+  url: string,
+  username: string,
+  password: string,
+): Promise<boolean> {
   const isLocal = isLocalURL(url);
   if (isLocal) {
     await ensureLocalBinarySetup(username, password);
diff --git a/src/tools/live.ts b/src/tools/live.ts
index 050bb25..cc313c7 100644
--- a/src/tools/live.ts
+++ b/src/tools/live.ts
@@ -7,7 +7,6 @@ import { PlatformType } from "./live-utils/types.js";
 import { trackMCP } from "../lib/instrumentation.js";
 import { BrowserStackConfig } from "../lib/types.js";
 
-
 // Define the schema shape
 const LiveArgsShape = {
   platformType: z
@@ -108,7 +107,10 @@ async function runBrowserSession(rawArgs: any, config: BrowserStackConfig) {
   };
 }
 
-export default function addBrowserLiveTools(server: McpServer, config: BrowserStackConfig) {
+export default function addBrowserLiveTools(
+  server: McpServer,
+  config: BrowserStackConfig,
+) {
   server.tool(
     "runBrowserLiveSession",
     "Launch a BrowserStack Live session (desktop or mobile).",
diff --git a/src/tools/testmanagement.ts b/src/tools/testmanagement.ts
index 2f90ffc..c5e9c3a 100644
--- a/src/tools/testmanagement.ts
+++ b/src/tools/testmanagement.ts
@@ -53,7 +53,6 @@ import {
 } from "./testmanagement-utils/create-lca-steps.js";
 import { BrowserStackConfig } from "../lib/types.js";
 
-
 //TODO: Moving the traceMCP and catch block to the parent(server) function
 
 /**
@@ -407,7 +406,10 @@ export async function createLCAStepsTool(
 /**
  * Registers both project/folder and test-case tools.
  */
-export default function addTestManagementTools(server: McpServer, config: BrowserStackConfig) {
+export default function addTestManagementTools(
+  server: McpServer,
+  config: BrowserStackConfig,
+) {
   server.tool(
     "createProjectOrFolder",
     "Create a project and/or folder in BrowserStack Test Management.",
diff --git a/tests/tools/applive.test.ts b/tests/tools/applive.test.ts
index 5b1120e..3870bd6 100644
--- a/tests/tools/applive.test.ts
+++ b/tests/tools/applive.test.ts
@@ -52,8 +52,13 @@ describe('startAppLiveSession', () => {
     desiredPhone: 'iPhone 12 Pro'
   };
 
+  const mockConfig = { 
+    getClientVersion: () => "test-version",
+    "browserstack-username": "test-username",
+    "browserstack-access-key": "test-access-key"
+  };
+
   it('should successfully start an Android app live session', async () => {
-    const mockConfig = { getClientVersion: () => "test-version" };
     const result = await startAppLiveSession(validAndroidArgs, mockConfig);
 
     expect(startSession).toHaveBeenCalledWith({
@@ -66,7 +71,6 @@ describe('startAppLiveSession', () => {
   });
 
   it('should successfully start an iOS app live session', async () => {
-    const mockConfig = { getClientVersion: () => "test-version" };
     const result = await startAppLiveSession(validiOSArgs, mockConfig);
 
     expect(startSession).toHaveBeenCalledWith({
@@ -80,37 +84,31 @@ describe('startAppLiveSession', () => {
 
   it('should fail if platform is not provided', async () => {
     const args = { ...validAndroidArgs, desiredPlatform: '' };
-    const mockConfig = { getClientVersion: () => "test-version" };
     await expect(startAppLiveSession(args, mockConfig)).rejects.toThrow('You must provide a desiredPlatform');
   });
 
   it('should fail if app path is not provided', async () => {
     const args = { ...validAndroidArgs, appPath: '' };
-    const mockConfig = { getClientVersion: () => "test-version" };
     await expect(startAppLiveSession(args, mockConfig)).rejects.toThrow('You must provide a appPath');
   });
 
   it('should fail if phone is not provided', async () => {
     const args = { ...validAndroidArgs, desiredPhone: '' };
-    const mockConfig = { getClientVersion: () => "test-version" };
     await expect(startAppLiveSession(args, mockConfig)).rejects.toThrow('You must provide a desiredPhone');
   });
 
   it('should fail if Android app path does not end with .apk', async () => {
     const args = { ...validAndroidArgs, appPath: '/path/to/app.ipa' };
-    const mockConfig = { getClientVersion: () => "test-version" };
     await expect(startAppLiveSession(args, mockConfig)).rejects.toThrow('You must provide a valid Android app path');
   });
 
   it('should fail if iOS app path does not end with .ipa', async () => {
     const args = { ...validiOSArgs, appPath: '/path/to/app.apk' };
-    const mockConfig = { getClientVersion: () => "test-version" };
     await expect(startAppLiveSession(args, mockConfig)).rejects.toThrow('You must provide a valid iOS app path');
   });
 
   it('should fail if app file does not exist', async () => {
     (fs.existsSync as Mock).mockReturnValue(false);
-    const mockConfig = { getClientVersion: () => "test-version" };
     await expect(startAppLiveSession(validAndroidArgs, mockConfig)).rejects.toThrow('The app path does not exist');
     expect(logger.error).toHaveBeenCalled();
   });
@@ -119,14 +117,12 @@ describe('startAppLiveSession', () => {
     (fs.accessSync as Mock).mockImplementation(() => {
       throw new Error('EACCES: permission denied');
     });
-    const mockConfig = { getClientVersion: () => "test-version" };
     await expect(startAppLiveSession(validAndroidArgs, mockConfig)).rejects.toThrow('The app path does not exist or is not readable');
     expect(logger.error).toHaveBeenCalled();
   });
 
   it('should handle session start failure', async () => {
     (startSession as Mock).mockRejectedValue(new Error('Session start failed'));
-    const mockConfig = { getClientVersion: () => "test-version" };
     await expect(startAppLiveSession(validAndroidArgs, mockConfig)).rejects.toThrow('Session start failed');
   });
 });

From 6eed8508657ff1e62e1af36f08b2ece03b319dbd Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 11:40:17 +0530
Subject: [PATCH 27/64] Fix access key environment variable name and clean up
 code formatting

---
 src/index.ts | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index 17aadb5..5cd7665 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -8,9 +8,6 @@ import "dotenv/config";
 import logger from "./logger.js";
 import { createMcpServer } from "./server-factory.js";
 
-
-
-
 async function main() {
   logger.info(
     "Launching BrowserStack MCP server, version %s",
@@ -18,7 +15,7 @@ async function main() {
   );
 
   const username = process.env.BROWSERSTACK_USERNAME;
-  const accessKey = process.env.BROWSERSTACK_AUTH_KEY;
+  const accessKey = process.env.BROWSERSTACK_ACCESS_KEY;
 
   if (!username) {
     throw new Error("BROWSERSTACK_USERNAME environment variable is required");
@@ -29,12 +26,12 @@ async function main() {
   }
 
   const transport = new StdioServerTransport();
-  
+
   const server = createMcpServer({
     "browserstack-username": username,
     "browserstack-access-key": accessKey,
   });
-  
+
   await server.connect(transport);
 }
 
@@ -43,4 +40,4 @@ main().catch(console.error);
 // Ensure logs are flushed before exit
 process.on("exit", () => {
   logger.flush();
-});
\ No newline at end of file
+});

From 5bcde7e6e35fb0b04d713e790f1ca8b260f993b2 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 11:40:47 +0530
Subject: [PATCH 28/64] Fix error message for missing BrowserStack access key
 environment variable

---
 src/index.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/index.ts b/src/index.ts
index 5cd7665..9a6bff3 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -22,7 +22,7 @@ async function main() {
   }
 
   if (!accessKey) {
-    throw new Error("BROWSERSTACK_AUTH_KEY environment variable is required");
+    throw new Error("BROWSERSTACK_ACCESS_KEY environment variable is required");
   }
 
   const transport = new StdioServerTransport();

From b5bcfc413844fee684f0d7be7012184829397924 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 17:26:39 +0530
Subject: [PATCH 29/64] Add REMOTE_MCP configuration and restrict local URL
 usage in remote mode

---
 src/config.ts                            |  3 +++
 src/tools/accessiblity-utils/scanner.ts  |  6 ++++++
 src/tools/applive-utils/start-session.ts |  8 ++++++--
 src/tools/live-utils/start-session.ts    | 11 +++++++++--
 4 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/src/config.ts b/src/config.ts
index f19f4ed..70ef33e 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -39,6 +39,7 @@ export class Config {
     public readonly DEV_MODE: boolean,
     public readonly browserstackLocalOptions: Record<string, any>,
     public readonly USE_OWN_LOCAL_BINARY_PROCESS: boolean,
+    public readonly REMOTE_MCP: boolean,
   ) {}
 }
 
@@ -46,6 +47,8 @@ const config = new Config(
   process.env.DEV_MODE === "true",
   browserstackLocalOptions,
   process.env.USE_OWN_LOCAL_BINARY_PROCESS === "true",
+  process.env.REMOTE_MCP === "true",
+
 );
 
 export default config;
diff --git a/src/tools/accessiblity-utils/scanner.ts b/src/tools/accessiblity-utils/scanner.ts
index 6baa83a..074b833 100644
--- a/src/tools/accessiblity-utils/scanner.ts
+++ b/src/tools/accessiblity-utils/scanner.ts
@@ -48,6 +48,12 @@ export class AccessibilityScanner {
       );
     }
 
+    if (config.REMOTE_MCP && hasLocal) {
+      throw new Error(
+        "Local URLs are not supported in this remote mcp. Please use a public URL.",
+      );
+    }
+
     if (hasLocal) {
       await ensureLocalBinarySetup(
         this.auth.username,
diff --git a/src/tools/applive-utils/start-session.ts b/src/tools/applive-utils/start-session.ts
index 6c647ba..3599fa5 100644
--- a/src/tools/applive-utils/start-session.ts
+++ b/src/tools/applive-utils/start-session.ts
@@ -11,6 +11,7 @@ import { pickVersion } from "./version-utils.js";
 import { DeviceEntry } from "./types.js";
 import childProcess from "child_process";
 import { BrowserStackConfig } from "../../lib/types.js";
+import envConfig from "../../config.js";
 
 interface StartSessionArgs {
   appPath: string;
@@ -93,8 +94,11 @@ export async function startSession(
     start: "true",
   });
   const launchUrl = `https://app-live.browserstack.com/dashboard#${params.toString()}&device=${deviceParam}`;
-
-  openBrowser(launchUrl);
+  
+  if (!envConfig.REMOTE_MCP) {
+    openBrowser(launchUrl);
+  }
+  
   return launchUrl + note;
 }
 
diff --git a/src/tools/live-utils/start-session.ts b/src/tools/live-utils/start-session.ts
index 5095bcb..04960fa 100644
--- a/src/tools/live-utils/start-session.ts
+++ b/src/tools/live-utils/start-session.ts
@@ -17,6 +17,7 @@ import {
 
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { BrowserStackConfig } from "../../lib/types.js";
+import envConfig from "../../config.js";
 
 /**
  * Prepares local tunnel setup based on URL type
@@ -27,6 +28,11 @@ async function prepareLocalTunnel(
   password: string,
 ): Promise<boolean> {
   const isLocal = isLocalURL(url);
+  if (isLocal && envConfig.REMOTE_MCP) {
+    throw new Error(
+      "Local URLs are not supported in this remote mcp. Please use a public URL.",
+    );
+  }
   if (isLocal) {
     await ensureLocalBinarySetup(username, password);
   } else {
@@ -67,8 +73,9 @@ export async function startBrowserSession(
           isLocal,
         )
       : buildMobileUrl(args as MobileSearchArgs, entry as MobileEntry, isLocal);
-
-  openBrowser(url);
+  if (!envConfig.REMOTE_MCP) {
+    openBrowser(url);
+  }
   return entry.notes ? `${url}, ${entry.notes}` : url;
 }
 

From 8a25a1122cd8ff02b335c161646f183d754721f1 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 17:29:53 +0530
Subject: [PATCH 30/64] Clean up whitespace in config and start-session files

---
 src/config.ts                            | 1 -
 src/tools/applive-utils/start-session.ts | 4 ++--
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/config.ts b/src/config.ts
index 70ef33e..63a4ac6 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -48,7 +48,6 @@ const config = new Config(
   browserstackLocalOptions,
   process.env.USE_OWN_LOCAL_BINARY_PROCESS === "true",
   process.env.REMOTE_MCP === "true",
-
 );
 
 export default config;
diff --git a/src/tools/applive-utils/start-session.ts b/src/tools/applive-utils/start-session.ts
index 3599fa5..7b7ac8b 100644
--- a/src/tools/applive-utils/start-session.ts
+++ b/src/tools/applive-utils/start-session.ts
@@ -94,11 +94,11 @@ export async function startSession(
     start: "true",
   });
   const launchUrl = `https://app-live.browserstack.com/dashboard#${params.toString()}&device=${deviceParam}`;
-  
+
   if (!envConfig.REMOTE_MCP) {
     openBrowser(launchUrl);
   }
-  
+
   return launchUrl + note;
 }
 

From 831d162891e5d878cf2bc3cb8436610deb5404ca Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 23:27:54 +0530
Subject: [PATCH 31/64] Add exports field to package.json for module resolution

---
 package.json | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/package.json b/package.json
index d412631..67afec9 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,11 @@
     "url": "git+https://github.com/browserstack/mcp-server.git"
   },
   "type": "module",
+  "exports": {
+    ".": "./dist/index.js",
+    "./lib/logger.js": "./dist/logger.js",
+    "./lib/server-factory.js": "./dist/server-factory.js"
+  },
   "scripts": {
     "build": "npm run lint && npm run format && npm test && tsc",
     "start": "node dist/index.js",

From e911c7aae4c3ce907916e0bbf3be0a6d7a076ff9 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 23:38:28 +0530
Subject: [PATCH 32/64] Export logger and createMcpServer functions from
 index.ts

---
 src/index.ts | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/index.ts b/src/index.ts
index 9a6bff3..82409f7 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -41,3 +41,6 @@ main().catch(console.error);
 process.on("exit", () => {
   logger.flush();
 });
+
+export { default as logger } from "./logger.js";
+export { createMcpServer } from "./server-factory.js";

From 5efbe5442decbafeea9996a8538a254ba4c8061e Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 23:43:29 +0530
Subject: [PATCH 33/64] Add declaration option to TypeScript configuration

---
 tsconfig.json | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tsconfig.json b/tsconfig.json
index 18bffef..8a31ae6 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -9,7 +9,9 @@
     "resolveJsonModule": true,
     "isolatedModules": true,
     "outDir": "./dist",
-    "rootDir": "./src"
+    "rootDir": "./src",
+    "declaration": true
+
   },
   "include": ["src/**/*"],
   "exclude": ["node_modules"]

From 17bfb4f73ec8ddab4733380fe9e3f26b85b4a89d Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 3 Jul 2025 23:46:37 +0530
Subject: [PATCH 34/64] Remove isolatedModules option from TypeScript
 configuration

---
 tsconfig.json | 2 --
 1 file changed, 2 deletions(-)

diff --git a/tsconfig.json b/tsconfig.json
index 8a31ae6..2857e59 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -7,11 +7,9 @@
     "skipLibCheck": true,
     "forceConsistentCasingInFileNames": true,
     "resolveJsonModule": true,
-    "isolatedModules": true,
     "outDir": "./dist",
     "rootDir": "./src",
     "declaration": true
-
   },
   "include": ["src/**/*"],
   "exclude": ["node_modules"]

From 1b0b40a7a711263195843b411ad37adf455a892c Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Fri, 4 Jul 2025 10:48:58 +0530
Subject: [PATCH 35/64] Refactor main function execution to check module
 context before running

---
 src/index.ts | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index 82409f7..f363fe3 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -35,12 +35,16 @@ async function main() {
   await server.connect(transport);
 }
 
-main().catch(console.error);
-
-// Ensure logs are flushed before exit
-process.on("exit", () => {
-  logger.flush();
-});
+// Check if this module is being run directly (not imported)
+if (import.meta.url === `file://${process.argv[1]}` || 
+    process.argv[1]?.endsWith('browserstack-mcp-server')) {
+  main().catch(console.error);
+
+  // Ensure logs are flushed before exit
+  process.on("exit", () => {
+    logger.flush();
+  });
+}
 
 export { default as logger } from "./logger.js";
 export { createMcpServer } from "./server-factory.js";

From 9d88768b026b1fc041884e343b8e7e9b1dd3fecb Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Fri, 4 Jul 2025 10:49:40 +0530
Subject: [PATCH 36/64] Simplify main function execution by removing module
 context check

---
 src/index.ts | 16 ++++++----------
 1 file changed, 6 insertions(+), 10 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index f363fe3..82409f7 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -35,16 +35,12 @@ async function main() {
   await server.connect(transport);
 }
 
-// Check if this module is being run directly (not imported)
-if (import.meta.url === `file://${process.argv[1]}` || 
-    process.argv[1]?.endsWith('browserstack-mcp-server')) {
-  main().catch(console.error);
-
-  // Ensure logs are flushed before exit
-  process.on("exit", () => {
-    logger.flush();
-  });
-}
+main().catch(console.error);
+
+// Ensure logs are flushed before exit
+process.on("exit", () => {
+  logger.flush();
+});
 
 export { default as logger } from "./logger.js";
 export { createMcpServer } from "./server-factory.js";

From ed4b96a52776990ae670c2f7d4b9869192c14cf4 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Fri, 4 Jul 2025 11:06:19 +0530
Subject: [PATCH 37/64] Add remote MCP mode check in main function

---
 src/index.ts | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/index.ts b/src/index.ts
index 82409f7..ad56428 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -14,6 +14,12 @@ async function main() {
     packageJson.version,
   );
 
+  const remoteMCP = process.env.REMOTE_MCP === "true";
+  if (remoteMCP) {
+    logger.info("Running in remote MCP mode");
+    return;
+  }
+
   const username = process.env.BROWSERSTACK_USERNAME;
   const accessKey = process.env.BROWSERSTACK_ACCESS_KEY;
 

From e95a864358a5ed2e3e6861c287af7fe9b8c8e876 Mon Sep 17 00:00:00 2001
From: tech-sushant <sushant.s@browserstack.com>
Date: Fri, 4 Jul 2025 12:03:55 +0530
Subject: [PATCH 38/64] feat: add support for reqnroll testing framework

---
 src/tools/sdk-utils/constants.ts | 1 +
 src/tools/sdk-utils/types.ts     | 1 +
 2 files changed, 2 insertions(+)

diff --git a/src/tools/sdk-utils/constants.ts b/src/tools/sdk-utils/constants.ts
index 00ff40b..801eb53 100644
--- a/src/tools/sdk-utils/constants.ts
+++ b/src/tools/sdk-utils/constants.ts
@@ -492,6 +492,7 @@ export const SUPPORTED_CONFIGURATIONS: ConfigMapping = {
       nunit: { instructions: csharpCommonInstructions },
       mstest: { instructions: csharpCommonInstructions },
       specflow: { instructions: csharpCommonInstructions },
+      reqnroll: { instructions: csharpCommonInstructions },
     },
   },
   nodejs: {
diff --git a/src/tools/sdk-utils/types.ts b/src/tools/sdk-utils/types.ts
index dbcd1af..6010954 100644
--- a/src/tools/sdk-utils/types.ts
+++ b/src/tools/sdk-utils/types.ts
@@ -33,6 +33,7 @@ export enum SDKSupportedTestingFrameworkEnum {
   mstest = "mstest",
   xunit = "xunit",
   specflow = "specflow",
+  reqnroll = "reqnroll"
 }
 export type SDKSupportedTestingFramework =
   keyof typeof SDKSupportedTestingFrameworkEnum;

From 53c029280a8093965fca6df77eed907f7f791aa8 Mon Sep 17 00:00:00 2001
From: tushar-sanap <132669607+tushar-sanap@users.noreply.github.com>
Date: Fri, 4 Jul 2025 12:13:18 +0530
Subject: [PATCH 39/64] Update README.md

---
 README.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/README.md b/README.md
index b1fe8ef..5a478ac 100644
--- a/README.md
+++ b/README.md
@@ -25,10 +25,13 @@
       <img src="assets/thumbnail.webp">
     </a>
   </div>
+
   
 Manage test cases, execute manual or automated tests, debug issues, and even fix code—directly within tools like Cursor, Claude, or any MCP-enabled client, using plain English.
 #### Test from anywhere:
 Easily connect the BrowserStack Test Platform to your favourite AI tools, such as IDEs, LLMs, or agentic workflows.
+#### Test with natural language:
+Manage, execute, debug tests, and even fix code using plain English prompts.
 #### Reduced context switching:
 Stay in flow—keep all project context in one place and trigger actions directly from your IDE or LLM.
 

From 420a843200d43b8e6b9ffb1e4f70dd3e6f5c80f4 Mon Sep 17 00:00:00 2001
From: tech-sushant <sushant.s@browserstack.com>
Date: Fri, 4 Jul 2025 17:29:36 +0530
Subject: [PATCH 40/64] linting and version bump

---
 package-lock.json            |  4 ++--
 package.json                 |  2 +-
 src/tools/appautomate.ts     | 28 ++++++++++++++--------------
 src/tools/bstack-sdk.ts      |  1 -
 src/tools/sdk-utils/types.ts |  2 +-
 5 files changed, 18 insertions(+), 19 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index eff5a06..9a07d19 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "@browserstack/mcp-server",
-  "version": "1.1.8",
+  "version": "1.1.9",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "@browserstack/mcp-server",
-      "version": "1.1.8",
+      "version": "1.1.9",
       "license": "ISC",
       "dependencies": {
         "@modelcontextprotocol/sdk": "^1.11.4",
diff --git a/package.json b/package.json
index 5c15fb3..e1bb823 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@browserstack/mcp-server",
-  "version": "1.1.8",
+  "version": "1.1.9",
   "description": "BrowserStack's Official MCP Server",
   "main": "dist/index.js",
   "repository": {
diff --git a/src/tools/appautomate.ts b/src/tools/appautomate.ts
index cf78edb..90031bd 100644
--- a/src/tools/appautomate.ts
+++ b/src/tools/appautomate.ts
@@ -268,25 +268,25 @@ export default function addAppAutomationTools(server: McpServer) {
         .string()
         .describe(
           "Path to your application file:\n" +
-          "If in development IDE directory:\n" +
-          "• For Android: 'gradle assembleDebug'\n" +
-          "• For iOS:\n" +
-          "  xcodebuild clean -scheme YOUR_SCHEME && \\\n" +
-          "  xcodebuild archive -scheme YOUR_SCHEME -configuration Release -archivePath build/app.xcarchive && \\\n" +
-          "  xcodebuild -exportArchive -archivePath build/app.xcarchive -exportPath build/ipa -exportOptionsPlist exportOptions.plist\n\n" +
-          "If in other directory, provide existing app path"
+            "If in development IDE directory:\n" +
+            "• For Android: 'gradle assembleDebug'\n" +
+            "• For iOS:\n" +
+            "  xcodebuild clean -scheme YOUR_SCHEME && \\\n" +
+            "  xcodebuild archive -scheme YOUR_SCHEME -configuration Release -archivePath build/app.xcarchive && \\\n" +
+            "  xcodebuild -exportArchive -archivePath build/app.xcarchive -exportPath build/ipa -exportOptionsPlist exportOptions.plist\n\n" +
+            "If in other directory, provide existing app path",
         ),
       testSuitePath: z
         .string()
         .describe(
           "Path to your test suite file:\n" +
-          "If in development IDE directory:\n" +
-          "• For Android: 'gradle assembleAndroidTest'\n" +
-          "• For iOS:\n" +
-          "  xcodebuild test-without-building -scheme YOUR_SCHEME -destination 'generic/platform=iOS' && \\\n" +
-          "  cd ~/Library/Developer/Xcode/DerivedData/*/Build/Products/Debug-iphonesimulator/ && \\\n" +
-          "  zip -r Tests.zip *.xctestrun *-Runner.app\n\n" +
-          "If in other directory, provide existing test file path"
+            "If in development IDE directory:\n" +
+            "• For Android: 'gradle assembleAndroidTest'\n" +
+            "• For iOS:\n" +
+            "  xcodebuild test-without-building -scheme YOUR_SCHEME -destination 'generic/platform=iOS' && \\\n" +
+            "  cd ~/Library/Developer/Xcode/DerivedData/*/Build/Products/Debug-iphonesimulator/ && \\\n" +
+            "  zip -r Tests.zip *.xctestrun *-Runner.app\n\n" +
+            "If in other directory, provide existing test file path",
         ),
       devices: z
         .array(z.string())
diff --git a/src/tools/bstack-sdk.ts b/src/tools/bstack-sdk.ts
index 44c37d7..bc89a22 100644
--- a/src/tools/bstack-sdk.ts
+++ b/src/tools/bstack-sdk.ts
@@ -24,7 +24,6 @@ import {
   getPercyInstructions,
 } from "./sdk-utils/percy/instructions.js";
 
-
 /**
  * BrowserStack SDK hooks into your test framework to seamlessly run tests on BrowserStack.
  * This tool gives instructions to setup a browserstack.yml file in the project root and installs the necessary dependencies.
diff --git a/src/tools/sdk-utils/types.ts b/src/tools/sdk-utils/types.ts
index 6010954..9aa9045 100644
--- a/src/tools/sdk-utils/types.ts
+++ b/src/tools/sdk-utils/types.ts
@@ -33,7 +33,7 @@ export enum SDKSupportedTestingFrameworkEnum {
   mstest = "mstest",
   xunit = "xunit",
   specflow = "specflow",
-  reqnroll = "reqnroll"
+  reqnroll = "reqnroll",
 }
 export type SDKSupportedTestingFramework =
   keyof typeof SDKSupportedTestingFrameworkEnum;

From eca0fa66cc633e0c3ef7a8f35996718000332708 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 8 Jul 2025 12:36:36 +0530
Subject: [PATCH 41/64] feat: refactor app automation functions to accept
 BrowserStack configuration

---
 src/tools/appautomate-utils/appautomate.ts | 39 ++++++++++++++++++----
 src/tools/appautomate.ts                   | 34 ++++++++++++-------
 src/tools/bstack-sdk.ts                    |  2 ++
 src/tools/sdk-utils/commands.ts            |  8 +++--
 4 files changed, 61 insertions(+), 22 deletions(-)

diff --git a/src/tools/appautomate-utils/appautomate.ts b/src/tools/appautomate-utils/appautomate.ts
index cf7b04f..258aa02 100644
--- a/src/tools/appautomate-utils/appautomate.ts
+++ b/src/tools/appautomate-utils/appautomate.ts
@@ -2,11 +2,7 @@ import fs from "fs";
 import axios from "axios";
 import FormData from "form-data";
 import { customFuzzySearch } from "../../lib/fuzzy.js";
-
-const auth = {
-  username: config.browserstackUsername,
-  password: config.browserstackAccessKey,
-};
+import { BrowserStackConfig } from "../../lib/types.js";
 
 interface Device {
   device: string;
@@ -168,6 +164,7 @@ async function uploadFileToBrowserStack(
   filePath: string,
   endpoint: string,
   responseKey: string,
+  config: BrowserStackConfig,
 ): Promise<string> {
   if (!fs.existsSync(filePath)) {
     throw new Error(`File not found at path: ${filePath}`);
@@ -176,6 +173,11 @@ async function uploadFileToBrowserStack(
   const formData = new FormData();
   formData.append("file", fs.createReadStream(filePath));
 
+  const auth = {
+    username: config["browserstack-username"],
+    password: config["browserstack-access-key"],
+  };
+
   const response = await axios.post(endpoint, formData, {
     headers: formData.getHeaders(),
     auth,
@@ -189,42 +191,54 @@ async function uploadFileToBrowserStack(
 }
 
 //Uploads an Android app (.apk or .aab) to BrowserStack Espresso endpoint and returns the app_url
-export async function uploadEspressoApp(appPath: string): Promise<string> {
+export async function uploadEspressoApp(
+  appPath: string,
+  config: BrowserStackConfig,
+): Promise<string> {
   return uploadFileToBrowserStack(
     appPath,
     "https://api-cloud.browserstack.com/app-automate/espresso/v2/app",
     "app_url",
+    config,
   );
 }
 
 //Uploads an Espresso test suite (.apk) to BrowserStack and returns the test_suite_url
 export async function uploadEspressoTestSuite(
   testSuitePath: string,
+  config: BrowserStackConfig,
 ): Promise<string> {
   return uploadFileToBrowserStack(
     testSuitePath,
     "https://api-cloud.browserstack.com/app-automate/espresso/v2/test-suite",
     "test_suite_url",
+    config,
   );
 }
 
 //Uploads an iOS app (.ipa) to BrowserStack XCUITest endpoint and returns the app_url
-export async function uploadXcuiApp(appPath: string): Promise<string> {
+export async function uploadXcuiApp(
+  appPath: string,
+  config: BrowserStackConfig,
+): Promise<string> {
   return uploadFileToBrowserStack(
     appPath,
     "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/app",
     "app_url",
+    config,
   );
 }
 
 //Uploads an XCUITest test suite (.zip) to BrowserStack and returns the test_suite_url
 export async function uploadXcuiTestSuite(
   testSuitePath: string,
+  config: BrowserStackConfig,
 ): Promise<string> {
   return uploadFileToBrowserStack(
     testSuitePath,
     "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/test-suite",
     "test_suite_url",
+    config,
   );
 }
 
@@ -235,6 +249,11 @@ export async function triggerEspressoBuild(
   devices: string[],
   project: string,
 ): Promise<string> {
+  const auth = {
+    username: process.env.BROWSERSTACK_USERNAME || "",
+    password: process.env.BROWSERSTACK_ACCESS_KEY || "",
+  };
+
   const response = await axios.post(
     "https://api-cloud.browserstack.com/app-automate/espresso/v2/build",
     {
@@ -263,7 +282,13 @@ export async function triggerXcuiBuild(
   test_suite_url: string,
   devices: string[],
   project: string,
+  config: BrowserStackConfig,
 ): Promise<string> {
+  const auth = {
+    username: config["browserstack-username"],
+    password: config["browserstack-access-key"],
+  };
+
   const response = await axios.post(
     "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/build",
     {
diff --git a/src/tools/appautomate.ts b/src/tools/appautomate.ts
index 53c34c2..954d5fe 100644
--- a/src/tools/appautomate.ts
+++ b/src/tools/appautomate.ts
@@ -155,19 +155,23 @@ async function takeAppScreenshot(args: {
 }
 
 //Runs AppAutomate tests on BrowserStack by uploading app and test suite, then triggering a test run.
-async function runAppTestsOnBrowserStack(args: {
-  appPath: string;
-  testSuitePath: string;
-  devices: string[];
-  project: string;
-  detectedAutomationFramework: string;
-}): Promise<CallToolResult> {
+async function runAppTestsOnBrowserStack(
+  args: {
+    appPath: string;
+    testSuitePath: string;
+    devices: string[];
+    project: string;
+    detectedAutomationFramework: string;
+  },
+  config: BrowserStackConfig,
+): Promise<CallToolResult> {
   switch (args.detectedAutomationFramework) {
     case AppTestPlatform.ESPRESSO: {
       try {
-        const app_url = await uploadEspressoApp(args.appPath);
+        const app_url = await uploadEspressoApp(args.appPath, config);
         const test_suite_url = await uploadEspressoTestSuite(
           args.testSuitePath,
+          config,
         );
         const build_id = await triggerEspressoBuild(
           app_url,
@@ -191,13 +195,17 @@ async function runAppTestsOnBrowserStack(args: {
     }
     case AppTestPlatform.XCUITEST: {
       try {
-        const app_url = await uploadXcuiApp(args.appPath);
-        const test_suite_url = await uploadXcuiTestSuite(args.testSuitePath);
+        const app_url = await uploadXcuiApp(args.appPath, config);
+        const test_suite_url = await uploadXcuiTestSuite(
+          args.testSuitePath,
+          config,
+        );
         const build_id = await triggerXcuiBuild(
           app_url,
           test_suite_url,
           args.devices,
           args.project,
+          config,
         );
         return {
           content: [
@@ -219,7 +227,6 @@ async function runAppTestsOnBrowserStack(args: {
   }
 }
 
-
 export default function addAppAutomationTools(
   server: McpServer,
   config: BrowserStackConfig,
@@ -326,13 +333,16 @@ export default function addAppAutomationTools(
         trackMCP(
           "runAppTestsOnBrowserStack",
           server.server.getClientVersion()!,
+          undefined,
+          config,
         );
-        return await runAppTestsOnBrowserStack(args);
+        return await runAppTestsOnBrowserStack(args, config);
       } catch (error) {
         trackMCP(
           "runAppTestsOnBrowserStack",
           server.server.getClientVersion()!,
           error,
+          config,
         );
         const errorMessage =
           error instanceof Error ? error.message : "Unknown error";
diff --git a/src/tools/bstack-sdk.ts b/src/tools/bstack-sdk.ts
index ac4158f..83999b1 100644
--- a/src/tools/bstack-sdk.ts
+++ b/src/tools/bstack-sdk.ts
@@ -87,6 +87,8 @@ export async function bootstrapProjectWithSDK({
   const sdkSetupCommand = getSDKPrefixCommand(
     detectedLanguage,
     detectedTestingFramework,
+    username,
+    accessKey,
   );
 
   const ymlInstructions = generateBrowserStackYMLInstructions(
diff --git a/src/tools/sdk-utils/commands.ts b/src/tools/sdk-utils/commands.ts
index 6865551..28ff9a5 100644
--- a/src/tools/sdk-utils/commands.ts
+++ b/src/tools/sdk-utils/commands.ts
@@ -25,6 +25,8 @@ const GRADLE_SETUP_INSTRUCTIONS = `
 export function getSDKPrefixCommand(
   language: SDKSupportedLanguage,
   framework: string,
+  username: string,
+  accessKey: string,
 ): string {
   switch (language) {
     case "nodejs":
@@ -36,7 +38,7 @@ 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}
+npx setup --username ${username} --key ${accessKey}
 \`\`\`
 ---STEP---
 Edit the browserstack.yml file that was created in the project root to add your desired platforms and browsers.`;
@@ -50,8 +52,8 @@ Edit the browserstack.yml file that was created in the project root to add your
         : `mvn archetype:generate -B -DarchetypeGroupId=com.browserstack \\
 -DarchetypeArtifactId=browserstack-sdk-archetype-integrate -DarchetypeVersion=1.0 \\
 -DgroupId=com.browserstack -DartifactId=browserstack-sdk-archetype-integrate -Dversion=1.0 \\
--DBROWSERSTACK_USERNAME="${process.env.BROWSERSTACK_USERNAME}" \\
--DBROWSERSTACK_ACCESS_KEY="${process.env.BROWSERSTACK_ACCESS_KEY}" \\
+-DBROWSERSTACK_USERNAME="${username}" \\
+-DBROWSERSTACK_ACCESS_KEY="${accessKey}" \\
 -DBROWSERSTACK_FRAMEWORK="${mavenFramework}"`;
 
       const platformLabel = isWindows ? "Windows" : "macOS/Linux";

From f9aa1287ecf658f691f9861f6e5151c078a23cfb Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 8 Jul 2025 17:04:23 +0530
Subject: [PATCH 42/64] Refactor API calls to use apiClient for improved
 consistency and error handling

---
 src/lib/api.ts                                |   6 +-
 src/lib/apiClient.ts                          | 118 ++++++++++++++++
 src/lib/device-cache.ts                       |   7 +-
 src/lib/utils.ts                              |   6 +-
 .../accessiblity-utils/accessibility-rag.ts   |  41 ++++--
 .../accessiblity-utils/report-fetcher.ts      |  34 +++--
 src/tools/accessiblity-utils/report-parser.ts |   7 +-
 src/tools/accessiblity-utils/scanner.ts       |  69 ++++++----
 src/tools/appautomate-utils/appautomate.ts    |  79 ++++++-----
 src/tools/applive-utils/upload-app.ts         |  33 ++---
 src/tools/automate-utils/fetch-screenshots.ts |  15 +-
 src/tools/failurelogs-utils/app-automate.ts   |  25 +++-
 src/tools/failurelogs-utils/automate.ts       |  23 +++-
 src/tools/failurelogs-utils/utils.ts          |   3 +-
 src/tools/observability.ts                    |  15 +-
 src/tools/selfheal-utils/selfheal.ts          |   9 +-
 .../testmanagement-utils/TCG-utils/api.ts     | 107 +++++++--------
 .../testmanagement-utils/add-test-result.ts   |  29 ++--
 .../testmanagement-utils/create-lca-steps.ts  | 128 ++++++++++--------
 .../create-project-folder.ts                  |  52 +++----
 .../testmanagement-utils/create-testcase.ts   |  22 ++-
 .../testmanagement-utils/create-testrun.ts    |  18 ++-
 .../testmanagement-utils/list-testcases.ts    |  11 +-
 .../testmanagement-utils/list-testruns.ts     |  11 +-
 .../testmanagement-utils/poll-lca-status.ts   |  13 +-
 .../testmanagement-utils/update-testrun.ts    |  13 +-
 src/tools/testmanagement-utils/upload-file.ts |   6 +-
 tests/tools/observability.test.ts             |  81 ++++++-----
 28 files changed, 610 insertions(+), 371 deletions(-)
 create mode 100644 src/lib/apiClient.ts

diff --git a/src/lib/api.ts b/src/lib/api.ts
index 39285ba..bff40d4 100644
--- a/src/lib/api.ts
+++ b/src/lib/api.ts
@@ -1,5 +1,6 @@
 import { getBrowserStackAuth } from "./get-auth.js";
 import { BrowserStackConfig } from "../lib/types.js";
+import { apiClient } from "./apiClient.js";
 
 export async function getLatestO11YBuildInfo(
   buildName: string,
@@ -13,7 +14,8 @@ export async function getLatestO11YBuildInfo(
   const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
-  const buildsResponse = await fetch(buildsUrl, {
+  const buildsResponse = await apiClient.get({
+    url: buildsUrl,
     headers: {
       Authorization: `Basic ${auth}`,
     },
@@ -28,5 +30,5 @@ export async function getLatestO11YBuildInfo(
     throw new Error(`Failed to fetch builds: ${buildsResponse.statusText}`);
   }
 
-  return buildsResponse.json();
+  return buildsResponse;
 }
diff --git a/src/lib/apiClient.ts b/src/lib/apiClient.ts
new file mode 100644
index 0000000..6d1becd
--- /dev/null
+++ b/src/lib/apiClient.ts
@@ -0,0 +1,118 @@
+import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
+
+type RequestOptions = {
+  url: string;
+  headers?: Record<string, string>;
+  params?: Record<string, string | number>;
+  body?: any;
+};
+
+class ApiResponse<T = any> {
+  private _response: AxiosResponse<T>;
+
+  constructor(response: AxiosResponse<T>) {
+    this._response = response;
+  }
+
+  get data(): T {
+    return this._response.data;
+  }
+
+  get status(): number {
+    return this._response.status;
+  }
+
+  get statusText(): string {
+    return this._response.statusText;
+  }
+
+  get headers(): Record<string, string> {
+    const raw = this._response.headers;
+    const sanitized: Record<string, string> = {};
+
+    for (const key in raw) {
+      const value = raw[key];
+      if (typeof value === "string") {
+        sanitized[key] = value;
+      }
+    }
+
+    return sanitized;
+  }
+
+  get config(): AxiosRequestConfig {
+    return this._response.config;
+  }
+
+  get url(): string | undefined {
+    return this._response.config.url;
+  }
+
+  get ok(): boolean {
+    return this._response.status >= 200 && this._response.status < 300;
+  }
+}
+
+class ApiClient {
+  private instance = axios.create();
+
+  async get<T = any>({
+    url,
+    headers,
+    params,
+  }: RequestOptions): Promise<ApiResponse<T>> {
+    const res = await this.instance.get<T>(url, {
+      headers,
+      params,
+    });
+    return new ApiResponse<T>(res);
+  }
+
+  async post<T = any>({
+    url,
+    headers,
+    body,
+  }: RequestOptions): Promise<ApiResponse<T>> {
+    const res = await this.instance.post<T>(url, body, {
+      headers,
+    });
+    return new ApiResponse<T>(res);
+  }
+
+  async put<T = any>({
+    url,
+    headers,
+    body,
+  }: RequestOptions): Promise<ApiResponse<T>> {
+    const res = await this.instance.put<T>(url, body, {
+      headers,
+    });
+    return new ApiResponse<T>(res);
+  }
+
+  async patch<T = any>({
+    url,
+    headers,
+    body,
+  }: RequestOptions): Promise<ApiResponse<T>> {
+    const res = await this.instance.patch<T>(url, body, {
+      headers,
+    });
+    return new ApiResponse<T>(res);
+  }
+
+  async delete<T = any>({
+    url,
+    headers,
+    params,
+  }: RequestOptions): Promise<ApiResponse<T>> {
+    const res = await this.instance.delete<T>(url, {
+      headers,
+      params,
+    });
+    return new ApiResponse<T>(res);
+  }
+}
+
+export const apiClient = new ApiClient();
+export type { ApiResponse, RequestOptions };
diff --git a/src/lib/device-cache.ts b/src/lib/device-cache.ts
index 5131bb5..7415790 100644
--- a/src/lib/device-cache.ts
+++ b/src/lib/device-cache.ts
@@ -1,6 +1,7 @@
 import fs from "fs";
 import os from "os";
 import path from "path";
+import { apiClient } from "./apiClient.js";
 
 const CACHE_DIR = path.join(os.homedir(), ".browserstack", "combined_cache");
 const CACHE_FILE = path.join(CACHE_DIR, "data.json");
@@ -48,7 +49,7 @@ export async function getDevicesAndBrowsers(
     }
   }
 
-  const liveRes = await fetch(URLS[type]);
+  const liveRes = await apiClient.get({ url: URLS[type] });
 
   if (!liveRes.ok) {
     throw new Error(
@@ -56,10 +57,8 @@ export async function getDevicesAndBrowsers(
     );
   }
 
-  const data = await liveRes.json();
-
   cache = {
-    [type]: data,
+    [type]: liveRes,
   };
   fs.writeFileSync(CACHE_FILE, JSON.stringify(cache), "utf8");
 
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index 00cf097..e19532c 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -1,4 +1,5 @@
 import sharp from "sharp";
+import type { ApiResponse } from "./apiClient.js";
 
 export function sanitizeUrlParam(param: string): string {
   // Remove any characters that could be used for command injection
@@ -24,7 +25,10 @@ export async function maybeCompressBase64(base64: string): Promise<string> {
   return compressedBuffer.toString("base64");
 }
 
-export async function assertOkResponse(response: Response, action: string) {
+export async function assertOkResponse(
+  response: Response | ApiResponse,
+  action: string,
+) {
   if (!response.ok) {
     if (response.status === 404) {
       throw new Error(`Invalid session ID for ${action}`);
diff --git a/src/tools/accessiblity-utils/accessibility-rag.ts b/src/tools/accessiblity-utils/accessibility-rag.ts
index 2c52df1..510aefd 100644
--- a/src/tools/accessiblity-utils/accessibility-rag.ts
+++ b/src/tools/accessiblity-utils/accessibility-rag.ts
@@ -1,4 +1,4 @@
-import fetch from "node-fetch";
+import { apiClient } from "../../lib/apiClient.js";
 
 export interface RAGChunk {
   url: string;
@@ -8,6 +8,13 @@ export interface RAGChunk {
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { BrowserStackConfig } from "../../lib/types.js";
 
+export interface AccessibilityRAGResponse {
+  content: Array<{
+    type: "text";
+    text: string;
+  }>;
+}
+
 export async function queryAccessibilityRAG(
   userQuery: string,
   config: BrowserStackConfig,
@@ -17,23 +24,26 @@ export async function queryAccessibilityRAG(
   const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
-  const response = await fetch(url, {
-    method: "POST",
+  const response = await apiClient.post({
+    url,
     headers: {
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
     },
-    body: JSON.stringify({
+    body: {
       query: userQuery,
-    }),
+    },
   });
 
   if (!response.ok) {
-    const errorText = await response.text();
+    const errorText =
+      typeof response.data === "string"
+        ? response.data
+        : JSON.stringify(response.data);
     throw new Error(`RAG endpoint error: ${response.status} ${errorText}`);
   }
 
-  const responseData = (await response.json()) as any;
+  const responseData = response.data as any;
 
   if (!responseData.success) {
     throw new Error("Something went wrong: " + responseData.message);
@@ -43,8 +53,21 @@ export async function queryAccessibilityRAG(
   let parsedData;
   try {
     parsedData = JSON.parse(responseData.data);
-  } catch {
-    throw new Error("Failed to parse RAG response data as JSON");
+  } catch (err) {
+    throw new Error(
+      "Failed to parse RAG response data as JSON: " +
+        (err instanceof Error ? err.message : String(err)),
+    );
+  }
+
+  if (
+    !parsedData ||
+    !parsedData.data ||
+    !Array.isArray(parsedData.data.chunks)
+  ) {
+    throw new Error(
+      "RAG response data is missing expected 'data.chunks' array",
+    );
   }
 
   const chunks: RAGChunk[] = parsedData.data.chunks;
diff --git a/src/tools/accessiblity-utils/report-fetcher.ts b/src/tools/accessiblity-utils/report-fetcher.ts
index 3446abc..b5ad029 100644
--- a/src/tools/accessiblity-utils/report-fetcher.ts
+++ b/src/tools/accessiblity-utils/report-fetcher.ts
@@ -1,4 +1,4 @@
-import axios from "axios";
+import { apiClient } from "../../lib/apiClient.js";
 
 interface ReportInitResponse {
   success: true;
@@ -22,26 +22,38 @@ export class AccessibilityReportFetcher {
   async getReportLink(scanId: string, scanRunId: string): Promise<string> {
     // Initiate CSV link generation
     const initUrl = `https://api-accessibility.browserstack.com/api/website-scanner/v1/scans/${scanId}/scan_runs/issues?scan_run_id=${scanRunId}`;
-    const initResp = await axios.get<ReportInitResponse>(initUrl, {
-      auth: this.auth,
+
+    let basicAuthHeader = undefined;
+    if (this.auth) {
+      const { username, password } = this.auth;
+      basicAuthHeader =
+        "Basic " + Buffer.from(`${username}:${password}`).toString("base64");
+    }
+    const initResp = await apiClient.get({
+      url: initUrl,
+      headers: basicAuthHeader ? { Authorization: basicAuthHeader } : undefined,
     });
-    if (!initResp.data.success) {
+    const initData: ReportInitResponse = initResp.data;
+    if (!initData.success) {
       throw new Error(
-        `Failed to initiate report: ${initResp.data.error || initResp.data.data.message}`,
+        `Failed to initiate report: ${initData.error || initData.data.message}`,
       );
     }
-    const taskId = initResp.data.data.task_id;
+    const taskId = initData.data.task_id;
 
     // Fetch the generated CSV link
     const reportUrl = `https://api-accessibility.browserstack.com/api/website-scanner/v1/scans/${scanId}/scan_runs/issues?task_id=${encodeURIComponent(
       taskId,
     )}`;
-    const reportResp = await axios.get<ReportResponse>(reportUrl, {
-      auth: this.auth,
+    // Use apiClient for the report link request as well
+    const reportResp = await apiClient.get({
+      url: reportUrl,
+      headers: basicAuthHeader ? { Authorization: basicAuthHeader } : undefined,
     });
-    if (!reportResp.data.success) {
-      throw new Error(`Failed to fetch report: ${reportResp.data.error}`);
+    const reportData: ReportResponse = reportResp.data;
+    if (!reportData.success) {
+      throw new Error(`Failed to fetch report: ${reportData.error}`);
     }
-    return reportResp.data.data.reportLink;
+    return reportData.data.reportLink;
   }
 }
diff --git a/src/tools/accessiblity-utils/report-parser.ts b/src/tools/accessiblity-utils/report-parser.ts
index ffef7d6..0510b44 100644
--- a/src/tools/accessiblity-utils/report-parser.ts
+++ b/src/tools/accessiblity-utils/report-parser.ts
@@ -1,4 +1,4 @@
-import fetch from "node-fetch";
+import { apiClient } from "../../lib/apiClient.js";
 import { parse } from "csv-parse/sync";
 
 type SimplifiedAccessibilityIssue = {
@@ -30,9 +30,10 @@ export async function parseAccessibilityReportFromCSV(
   { maxCharacterLength = 5000, nextPage = 0 }: PaginationOptions = {},
 ): Promise<PaginatedResult> {
   // 1) Download & parse
-  const res = await fetch(reportLink);
+  const res = await apiClient.get({ url: reportLink });
   if (!res.ok) throw new Error(`Failed to download report: ${res.statusText}`);
-  const text = await res.text();
+  const text =
+    typeof res.data === "string" ? res.data : JSON.stringify(res.data);
   const all: SimplifiedAccessibilityIssue[] = parse(text, {
     columns: true,
     skip_empty_lines: true,
diff --git a/src/tools/accessiblity-utils/scanner.ts b/src/tools/accessiblity-utils/scanner.ts
index 074b833..640da0b 100644
--- a/src/tools/accessiblity-utils/scanner.ts
+++ b/src/tools/accessiblity-utils/scanner.ts
@@ -1,4 +1,4 @@
-import axios from "axios";
+import { apiClient } from "../../lib/apiClient.js";
 import { randomUUID } from "node:crypto";
 import logger from "../../logger.js";
 import {
@@ -96,28 +96,35 @@ export class AccessibilityScanner {
     }
 
     try {
-      const { data } = await axios.post<AccessibilityScanResponse>(
-        "https://api-accessibility.browserstack.com/api/website-scanner/v1/scans",
-        requestBody,
-        { auth: this.auth },
-      );
+      const response = await apiClient.post<AccessibilityScanResponse>({
+        url: "https://api-accessibility.browserstack.com/api/website-scanner/v1/scans",
+        headers: {
+          Authorization:
+            "Basic " +
+            Buffer.from(`${this.auth.username}:${this.auth.password}`).toString(
+              "base64",
+            ),
+          "Content-Type": "application/json",
+        },
+        body: requestBody,
+      });
+      const data = response.data;
       if (!data.success)
         throw new Error(`Unable to start scan: ${data.errors?.join(", ")}`);
       return data;
-    } catch (err) {
-      if (axios.isAxiosError(err) && err.response?.data) {
-        if (err.response.status === 422) {
-          throw new Error(
-            "A scan with this name already exists. please update the name and run again.",
-          );
-        }
-        const msg =
-          (err.response.data as any).error ||
-          (err.response.data as any).message ||
-          err.message;
-        throw new Error(`Failed to start scan: ${msg}`);
+    } catch (err: any) {
+      // apiClient throws generic errors, try to extract message
+      if (err?.response?.status === 422) {
+        throw new Error(
+          "A scan with this name already exists. please update the name and run again.",
+        );
       }
-      throw err;
+      const msg =
+        err?.response?.data?.error ||
+        err?.response?.data?.message ||
+        err?.message ||
+        String(err);
+      throw new Error(`Failed to start scan: ${msg}`);
     }
   }
 
@@ -126,19 +133,23 @@ export class AccessibilityScanner {
     scanRunId: string,
   ): Promise<AccessibilityScanStatus> {
     try {
-      const { data } = await axios.get<AccessibilityScanStatus>(
-        `https://api-accessibility.browserstack.com/api/website-scanner/v1/scans/${scanId}/scan_runs/${scanRunId}/status`,
-        { auth: this.auth },
-      );
+      const response = await apiClient.get<AccessibilityScanStatus>({
+        url: `https://api-accessibility.browserstack.com/api/website-scanner/v1/scans/${scanId}/scan_runs/${scanRunId}/status`,
+        headers: {
+          Authorization:
+            "Basic " +
+            Buffer.from(
+              `${this.auth?.username}:${this.auth?.password}`,
+            ).toString("base64"),
+        },
+      });
+      const data = response.data;
       if (!data.success)
         throw new Error(`Failed to get status: ${data.errors?.join(", ")}`);
       return data;
-    } catch (err) {
-      if (axios.isAxiosError(err) && err.response?.data) {
-        const msg = (err.response.data as any).message || err.message;
-        throw new Error(`Failed to get scan status: ${msg}`);
-      }
-      throw err;
+    } catch (err: any) {
+      const msg = err?.response?.data?.message || err?.message || String(err);
+      throw new Error(`Failed to get scan status: ${msg}`);
     }
   }
 
diff --git a/src/tools/appautomate-utils/appautomate.ts b/src/tools/appautomate-utils/appautomate.ts
index 258aa02..eb47f54 100644
--- a/src/tools/appautomate-utils/appautomate.ts
+++ b/src/tools/appautomate-utils/appautomate.ts
@@ -1,6 +1,6 @@
 import fs from "fs";
-import axios from "axios";
 import FormData from "form-data";
+import { apiClient } from "../../lib/apiClient.js";
 import { customFuzzySearch } from "../../lib/fuzzy.js";
 import { BrowserStackConfig } from "../../lib/types.js";
 
@@ -138,24 +138,20 @@ export async function uploadApp(
   const formData = new FormData();
   formData.append("file", fs.createReadStream(filePath));
 
-  const response = await axios.post<UploadResponse>(
-    "https://api-cloud.browserstack.com/app-automate/upload",
-    formData,
-    {
-      headers: {
-        ...formData.getHeaders(),
-      },
-      auth: {
-        username,
-        password,
-      },
+  const response = await apiClient.post<UploadResponse>({
+    url: "https://api-cloud.browserstack.com/app-automate/upload",
+    headers: {
+      ...formData.getHeaders(),
+      Authorization:
+        "Basic " + Buffer.from(`${username}:${password}`).toString("base64"),
     },
-  );
+    body: formData,
+  });
 
   if (response.data.app_url) {
     return response.data.app_url;
   } else {
-    throw new Error(`Failed to upload app: ${response.data}`);
+    throw new Error(`Failed to upload app: ${JSON.stringify(response.data)}`);
   }
 }
 
@@ -173,14 +169,19 @@ async function uploadFileToBrowserStack(
   const formData = new FormData();
   formData.append("file", fs.createReadStream(filePath));
 
-  const auth = {
-    username: config["browserstack-username"],
-    password: config["browserstack-access-key"],
-  };
-
-  const response = await axios.post(endpoint, formData, {
-    headers: formData.getHeaders(),
-    auth,
+  const authHeader =
+    "Basic " +
+    Buffer.from(
+      `${config["browserstack-username"]}:${config["browserstack-access-key"]}`,
+    ).toString("base64");
+
+  const response = await apiClient.post({
+    url: endpoint,
+    headers: {
+      ...formData.getHeaders(),
+      Authorization: authHeader,
+    },
+    body: formData,
   });
 
   if (response.data[responseKey]) {
@@ -254,18 +255,21 @@ export async function triggerEspressoBuild(
     password: process.env.BROWSERSTACK_ACCESS_KEY || "",
   };
 
-  const response = await axios.post(
-    "https://api-cloud.browserstack.com/app-automate/espresso/v2/build",
-    {
+  const response = await apiClient.post({
+    url: "https://api-cloud.browserstack.com/app-automate/espresso/v2/build",
+    headers: {
+      Authorization:
+        "Basic " +
+        Buffer.from(`${auth.username}:${auth.password}`).toString("base64"),
+      "Content-Type": "application/json",
+    },
+    body: {
       app: app_url,
       testSuite: test_suite_url,
       devices,
       project,
     },
-    {
-      auth,
-    },
-  );
+  });
 
   if (response.data.build_id) {
     return response.data.build_id;
@@ -289,18 +293,21 @@ export async function triggerXcuiBuild(
     password: config["browserstack-access-key"],
   };
 
-  const response = await axios.post(
-    "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/build",
-    {
+  const response = await apiClient.post({
+    url: "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/build",
+    headers: {
+      Authorization:
+        "Basic " +
+        Buffer.from(`${auth.username}:${auth.password}`).toString("base64"),
+      "Content-Type": "application/json",
+    },
+    body: {
       app: app_url,
       testSuite: test_suite_url,
       devices,
       project,
     },
-    {
-      auth,
-    },
-  );
+  });
 
   if (response.data.build_id) {
     return response.data.build_id;
diff --git a/src/tools/applive-utils/upload-app.ts b/src/tools/applive-utils/upload-app.ts
index b80c433..e370864 100644
--- a/src/tools/applive-utils/upload-app.ts
+++ b/src/tools/applive-utils/upload-app.ts
@@ -1,4 +1,4 @@
-import axios, { AxiosError } from "axios";
+import { apiClient } from "../../lib/apiClient.js";
 import FormData from "form-data";
 import fs from "fs";
 
@@ -19,27 +19,20 @@ export async function uploadApp(
   formData.append("file", fs.createReadStream(filePath));
 
   try {
-    const response = await axios.post<UploadResponse>(
-      "https://api-cloud.browserstack.com/app-live/upload",
-      formData,
-      {
-        headers: {
-          ...formData.getHeaders(),
-        },
-        auth: {
-          username,
-          password,
-        },
+    const response = await apiClient.post<UploadResponse>({
+      url: "https://api-cloud.browserstack.com/app-live/upload",
+      headers: {
+        ...formData.getHeaders(),
+        Authorization:
+          "Basic " + Buffer.from(`${username}:${password}`).toString("base64"),
       },
-    );
+      body: formData,
+    });
 
     return response.data;
-  } catch (error: unknown) {
-    if (error instanceof AxiosError) {
-      throw new Error(
-        `Failed to upload app: ${error.response?.data?.message || error.message}`,
-      );
-    }
-    throw error;
+  } catch (error: any) {
+    const msg =
+      error?.response?.data?.message || error?.message || String(error);
+    throw new Error(`Failed to upload app: ${msg}`);
   }
 }
diff --git a/src/tools/automate-utils/fetch-screenshots.ts b/src/tools/automate-utils/fetch-screenshots.ts
index d1f5e91..6168c80 100644
--- a/src/tools/automate-utils/fetch-screenshots.ts
+++ b/src/tools/automate-utils/fetch-screenshots.ts
@@ -2,6 +2,7 @@ import { assertOkResponse, maybeCompressBase64 } from "../../lib/utils.js";
 import { SessionType } from "../../lib/constants.js";
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { BrowserStackConfig } from "../../lib/types.js";
+import { apiClient } from "../../lib/apiClient.js";
 
 async function extractScreenshotUrls(
   sessionId: string,
@@ -14,7 +15,8 @@ async function extractScreenshotUrls(
   const baseUrl = `https://api.browserstack.com/${sessionType === SessionType.Automate ? "automate" : "app-automate"}`;
 
   const url = `${baseUrl}/sessions/${sessionId}/logs`;
-  const response = await fetch(url, {
+  const response = await apiClient.get({
+    url,
     headers: {
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
@@ -23,7 +25,10 @@ async function extractScreenshotUrls(
 
   await assertOkResponse(response, "Session");
 
-  const text = await response.text();
+  const text =
+    typeof response.data === "string"
+      ? response.data
+      : JSON.stringify(response.data);
 
   const urls: string[] = [];
   const SCREENSHOT_PATTERN = /REQUEST.*GET.*\/screenshot/;
@@ -53,9 +58,9 @@ async function convertUrlsToBase64(
 ): Promise<Array<{ url: string; base64: string }>> {
   const screenshots = await Promise.all(
     urls.map(async (url) => {
-      const response = await fetch(url);
-      const arrayBuffer = await response.arrayBuffer();
-      const base64 = Buffer.from(arrayBuffer).toString("base64");
+      const response = await apiClient.get({ url });
+      // Axios returns response.data as a Buffer for binary data
+      const base64 = Buffer.from(response.data).toString("base64");
 
       // Compress the base64 image if needed
       const compressedBase64 = await maybeCompressBase64(base64);
diff --git a/src/tools/failurelogs-utils/app-automate.ts b/src/tools/failurelogs-utils/app-automate.ts
index ce8e159..0bfecb1 100644
--- a/src/tools/failurelogs-utils/app-automate.ts
+++ b/src/tools/failurelogs-utils/app-automate.ts
@@ -1,6 +1,7 @@
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { filterLinesByKeywords, validateLogResponse } from "./utils.js";
 import { BrowserStackConfig } from "../../lib/types.js";
+import { apiClient } from "../../lib/apiClient.js";
 
 // DEVICE LOGS
 export async function retrieveDeviceLogs(
@@ -12,7 +13,8 @@ export async function retrieveDeviceLogs(
   const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
-  const response = await fetch(url, {
+  const response = await apiClient.get({
+    url,
     headers: {
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
@@ -22,7 +24,10 @@ export async function retrieveDeviceLogs(
   const validationError = validateLogResponse(response, "device logs");
   if (validationError) return validationError.message!;
 
-  const logText = await response.text();
+  const logText =
+    typeof response.data === "string"
+      ? response.data
+      : JSON.stringify(response.data);
   const logs = filterDeviceFailures(logText);
   return logs.length > 0
     ? `Device Failures (${logs.length} found):\n${JSON.stringify(logs, null, 2)}`
@@ -39,7 +44,8 @@ export async function retrieveAppiumLogs(
   const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
-  const response = await fetch(url, {
+  const response = await apiClient.get({
+    url,
     headers: {
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
@@ -49,7 +55,10 @@ export async function retrieveAppiumLogs(
   const validationError = validateLogResponse(response, "Appium logs");
   if (validationError) return validationError.message!;
 
-  const logText = await response.text();
+  const logText =
+    typeof response.data === "string"
+      ? response.data
+      : JSON.stringify(response.data);
   const logs = filterAppiumFailures(logText);
   return logs.length > 0
     ? `Appium Failures (${logs.length} found):\n${JSON.stringify(logs, null, 2)}`
@@ -66,7 +75,8 @@ export async function retrieveCrashLogs(
   const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
-  const response = await fetch(url, {
+  const response = await apiClient.get({
+    url,
     headers: {
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
@@ -76,7 +86,10 @@ export async function retrieveCrashLogs(
   const validationError = validateLogResponse(response, "crash logs");
   if (validationError) return validationError.message!;
 
-  const logText = await response.text();
+  const logText =
+    typeof response.data === "string"
+      ? response.data
+      : JSON.stringify(response.data);
   const logs = filterCrashFailures(logText);
   return logs.length > 0
     ? `Crash Failures (${logs.length} found):\n${JSON.stringify(logs, null, 2)}`
diff --git a/src/tools/failurelogs-utils/automate.ts b/src/tools/failurelogs-utils/automate.ts
index dd4ee49..99e3977 100644
--- a/src/tools/failurelogs-utils/automate.ts
+++ b/src/tools/failurelogs-utils/automate.ts
@@ -6,6 +6,7 @@ import {
   validateLogResponse,
 } from "./utils.js";
 import { BrowserStackConfig } from "../../lib/types.js";
+import { apiClient } from "../../lib/apiClient.js";
 
 // NETWORK LOGS
 export async function retrieveNetworkFailures(
@@ -16,8 +17,8 @@ export async function retrieveNetworkFailures(
   const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
-  const response = await fetch(url, {
-    method: "GET",
+  const response = await apiClient.get({
+    url,
     headers: {
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
@@ -27,7 +28,7 @@ export async function retrieveNetworkFailures(
   const validationError = validateLogResponse(response, "network logs");
   if (validationError) return validationError.message!;
 
-  const networklogs: HarFile = await response.json();
+  const networklogs: HarFile = response.data;
   const failureEntries: HarEntry[] = networklogs.log.entries.filter(
     (entry: HarEntry) =>
       entry.response.status === 0 ||
@@ -67,7 +68,8 @@ export async function retrieveSessionFailures(
   const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
-  const response = await fetch(url, {
+  const response = await apiClient.get({
+    url,
     headers: {
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
@@ -77,7 +79,10 @@ export async function retrieveSessionFailures(
   const validationError = validateLogResponse(response, "session logs");
   if (validationError) return validationError.message!;
 
-  const logText = await response.text();
+  const logText =
+    typeof response.data === "string"
+      ? response.data
+      : JSON.stringify(response.data);
   const logs = filterSessionFailures(logText);
   return logs.length > 0
     ? `Session Failures (${logs.length} found):\n${JSON.stringify(logs, null, 2)}`
@@ -93,7 +98,8 @@ export async function retrieveConsoleFailures(
   const authString = getBrowserStackAuth(config);
   const auth = Buffer.from(authString).toString("base64");
 
-  const response = await fetch(url, {
+  const response = await apiClient.get({
+    url,
     headers: {
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
@@ -103,7 +109,10 @@ export async function retrieveConsoleFailures(
   const validationError = validateLogResponse(response, "console logs");
   if (validationError) return validationError.message!;
 
-  const logText = await response.text();
+  const logText =
+    typeof response.data === "string"
+      ? response.data
+      : JSON.stringify(response.data);
   const logs = filterConsoleFailures(logText);
   return logs.length > 0
     ? `Console Failures (${logs.length} found):\n${JSON.stringify(logs, null, 2)}`
diff --git a/src/tools/failurelogs-utils/utils.ts b/src/tools/failurelogs-utils/utils.ts
index 68f617b..9655368 100644
--- a/src/tools/failurelogs-utils/utils.ts
+++ b/src/tools/failurelogs-utils/utils.ts
@@ -25,8 +25,9 @@ export interface HarEntry {
   time?: number;
 }
 
+import type { ApiResponse } from "../../lib/apiClient.js";
 export function validateLogResponse(
-  response: Response,
+  response: Response | ApiResponse,
   logType: string,
 ): LogResponse | null {
   if (!response.ok) {
diff --git a/src/tools/observability.ts b/src/tools/observability.ts
index 58e7d9c..ae0e8bb 100644
--- a/src/tools/observability.ts
+++ b/src/tools/observability.ts
@@ -17,7 +17,12 @@ export async function getFailuresInLastRun(
     config,
   );
 
-  const observabilityUrl = buildsData.observability_url;
+  if (!buildsData.data) {
+    throw new Error(
+      "No observability URL found in build data, this is likely because the build is not yet available on BrowserStack Observability.",
+    );
+  }
+  const observabilityUrl = buildsData.data.observability_url;
   if (!observabilityUrl) {
     throw new Error(
       "No observability URL found in build data, this is likely because the build is not yet available on BrowserStack Observability.",
@@ -25,13 +30,13 @@ export async function getFailuresInLastRun(
   }
 
   let overview = "No overview available";
-  if (buildsData.unique_errors?.overview?.insight) {
-    overview = buildsData.unique_errors.overview.insight;
+  if (buildsData.data.unique_errors?.overview?.insight) {
+    overview = buildsData.data.unique_errors.overview.insight;
   }
 
   let details = "No error details available";
-  if (buildsData.unique_errors?.top_unique_errors?.length > 0) {
-    details = buildsData.unique_errors.top_unique_errors
+  if (buildsData.data.unique_errors?.top_unique_errors?.length > 0) {
+    details = buildsData.data.unique_errors.top_unique_errors
       .map((error: any) => error.error)
       .filter(Boolean)
       .join("\n");
diff --git a/src/tools/selfheal-utils/selfheal.ts b/src/tools/selfheal-utils/selfheal.ts
index 99bc18c..a1464cd 100644
--- a/src/tools/selfheal-utils/selfheal.ts
+++ b/src/tools/selfheal-utils/selfheal.ts
@@ -11,6 +11,7 @@ interface SelectorMapping {
 
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { BrowserStackConfig } from "../../lib/types.js";
+import { apiClient } from "../../lib/apiClient.js";
 
 export async function getSelfHealSelectors(
   sessionId: string,
@@ -20,7 +21,8 @@ export async function getSelfHealSelectors(
   const auth = Buffer.from(authString).toString("base64");
   const url = `https://api.browserstack.com/automate/sessions/${sessionId}/logs`;
 
-  const response = await fetch(url, {
+  const response = await apiClient.get({
+    url,
     headers: {
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
@@ -28,7 +30,10 @@ export async function getSelfHealSelectors(
   });
 
   await assertOkResponse(response, "session logs");
-  const logText = await response.text();
+  const logText =
+    typeof response.data === "string"
+      ? response.data
+      : JSON.stringify(response.data);
   return extractHealedSelectors(logText);
 }
 
diff --git a/src/tools/testmanagement-utils/TCG-utils/api.ts b/src/tools/testmanagement-utils/TCG-utils/api.ts
index 9b1a9fe..4298add 100644
--- a/src/tools/testmanagement-utils/TCG-utils/api.ts
+++ b/src/tools/testmanagement-utils/TCG-utils/api.ts
@@ -1,4 +1,4 @@
-import axios from "axios";
+import { apiClient } from "../../../lib/apiClient.js";
 import {
   TCG_TRIGGER_URL,
   TCG_POLL_URL,
@@ -22,7 +22,8 @@ export async function fetchFormFields(
   projectId: string,
   config: BrowserStackConfig,
 ): Promise<{ default_fields: any; custom_fields: any }> {
-  const res = await axios.get(FORM_FIELDS_URL(projectId), {
+  const res = await apiClient.get({
+    url: FORM_FIELDS_URL(projectId),
     headers: {
       "API-TOKEN": getBrowserStackAuth(config),
     },
@@ -41,9 +42,14 @@ export async function triggerTestCaseGeneration(
   source: string,
   config: BrowserStackConfig,
 ): Promise<string> {
-  const res = await axios.post(
-    TCG_TRIGGER_URL,
-    {
+  const res = await apiClient.post({
+    url: TCG_TRIGGER_URL,
+    headers: {
+      "API-TOKEN": getBrowserStackAuth(config),
+      "Content-Type": "application/json",
+      "request-source": source,
+    },
+    body: {
       document,
       documentId,
       folderId,
@@ -51,16 +57,9 @@ export async function triggerTestCaseGeneration(
       source,
       webhookUrl: `https://test-management.browserstack.com/api/v1/projects/${projectId}/folder/${folderId}/webhooks/tcg`,
     },
-    {
-      headers: {
-        "API-TOKEN": getBrowserStackAuth(config),
-        "Content-Type": "application/json",
-        "request-source": source,
-      },
-    },
-  );
+  });
   if (res.status !== 200) {
-    throw new Error(`Trigger failed: ${res.statusText}`);
+    throw new Error(`Trigger failed: ${res.statusText || res.status}`);
   }
   return res.data["x-bstack-traceRequestId"];
 }
@@ -79,22 +78,20 @@ export async function fetchTestCaseDetails(
   if (testCaseIds.length === 0) {
     throw new Error("No testCaseIds provided to fetchTestCaseDetails");
   }
-  const res = await axios.post(
-    FETCH_DETAILS_URL,
-    {
+  const res = await apiClient.post({
+    url: FETCH_DETAILS_URL,
+    headers: {
+      "API-TOKEN": getBrowserStackAuth(config),
+      "request-source": source,
+      "Content-Type": "application/json",
+    },
+    body: {
       document_id: documentId,
       folder_id: folderId,
       project_id: projectId,
       test_case_ids: testCaseIds,
     },
-    {
-      headers: {
-        "API-TOKEN": getBrowserStackAuth(config),
-        "request-source": source,
-        "Content-Type": "application/json",
-      },
-    },
-  );
+  });
   if (res.data.data.success !== true) {
     throw new Error(`Fetch details failed: ${res.data.data.message}`);
   }
@@ -115,17 +112,13 @@ export async function pollTestCaseDetails(
     // add a bit of jitter to avoid synchronized polling storms
     await new Promise((r) => setTimeout(r, 10000 + Math.random() * 5000));
 
-    const poll = await axios.post(
-      `${TCG_POLL_URL}?x-bstack-traceRequestId=${encodeURIComponent(
-        traceRequestId,
-      )}`,
-      {},
-      {
-        headers: {
-          "API-TOKEN": getBrowserStackAuth(config),
-        },
+    const poll = await apiClient.post({
+      url: `${TCG_POLL_URL}?x-bstack-traceRequestId=${encodeURIComponent(traceRequestId)}`,
+      headers: {
+        "API-TOKEN": getBrowserStackAuth(config),
       },
-    );
+      body: {},
+    });
 
     if (!poll.data.data.success) {
       throw new Error(`Polling failed: ${poll.data.data.message}`);
@@ -169,19 +162,17 @@ export async function pollScenariosTestDetails(
   await new Promise<void>((resolve, reject) => {
     const intervalId = setInterval(async () => {
       try {
-        const poll = await axios.post(
-          `${TCG_POLL_URL}?x-bstack-traceRequestId=${encodeURIComponent(traceId)}`,
-          {},
-          {
-            headers: {
-              "API-TOKEN": getBrowserStackAuth(config),
-            },
+        const poll = await apiClient.post({
+          url: `${TCG_POLL_URL}?x-bstack-traceRequestId=${encodeURIComponent(traceId)}`,
+          headers: {
+            "API-TOKEN": getBrowserStackAuth(config),
           },
-        );
+          body: {},
+        });
 
         if (poll.status !== 200) {
           clearInterval(intervalId);
-          reject(new Error(`Polling error: ${poll.statusText}`));
+          reject(new Error(`Polling error: ${poll.statusText || poll.status}`));
           return;
         }
 
@@ -308,16 +299,14 @@ export async function bulkCreateTestCases(
     };
 
     try {
-      const resp = await axios.post(
-        BULK_CREATE_URL(projectId, folderId),
-        payload,
-        {
-          headers: {
-            "API-TOKEN": getBrowserStackAuth(config),
-            "Content-Type": "application/json",
-          },
+      const resp = await apiClient.post({
+        url: BULK_CREATE_URL(projectId, folderId),
+        headers: {
+          "API-TOKEN": getBrowserStackAuth(config),
+          "Content-Type": "application/json",
         },
-      );
+        body: payload,
+      });
       results[id] = resp.data;
       await context.sendNotification({
         method: "notifications/progress",
@@ -354,14 +343,17 @@ export async function projectIdentifierToId(
 ): Promise<string> {
   const url = `https://test-management.browserstack.com/api/v1/projects/?q=${projectId}`;
 
-  const response = await axios.get(url, {
+  const response = await apiClient.get({
+    url,
     headers: {
       "API-TOKEN": getBrowserStackAuth(config),
       accept: "application/json, text/plain, */*",
     },
   });
   if (response.data.success !== true) {
-    throw new Error(`Failed to fetch project ID: ${response.statusText}`);
+    throw new Error(
+      `Failed to fetch project ID: ${response.statusText || response.status}`,
+    );
   }
   for (const project of response.data.projects) {
     if (project.identifier === projectId) {
@@ -378,7 +370,8 @@ export async function testCaseIdentifierToDetails(
 ): Promise<{ testCaseId: string; folderId: string }> {
   const url = `https://test-management.browserstack.com/api/v1/projects/${projectId}/test-cases/search?q[query]=${testCaseIdentifier}`;
 
-  const response = await axios.get(url, {
+  const response = await apiClient.get({
+    url,
     headers: {
       "API-TOKEN": getBrowserStackAuth(config),
       accept: "application/json, text/plain, */*",
@@ -387,7 +380,7 @@ export async function testCaseIdentifierToDetails(
 
   if (response.data.success !== true) {
     throw new Error(
-      `Failed to fetch test case details: ${response.statusText}`,
+      `Failed to fetch test case details: ${response.statusText || response.status}`,
     );
   }
 
diff --git a/src/tools/testmanagement-utils/add-test-result.ts b/src/tools/testmanagement-utils/add-test-result.ts
index 12a2b7b..c41db3b 100644
--- a/src/tools/testmanagement-utils/add-test-result.ts
+++ b/src/tools/testmanagement-utils/add-test-result.ts
@@ -1,8 +1,7 @@
-import axios from "axios";
+import { apiClient } from "../../lib/apiClient.js";
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
-import { formatAxiosError } from "../../lib/error.js";
 import { BrowserStackConfig } from "../../lib/types.js";
 
 /**
@@ -50,12 +49,14 @@ export async function addTestResult(
     const authString = getBrowserStackAuth(config);
     const [username, password] = authString.split(":");
 
-    const response = await axios.post(url, body, {
-      auth: {
-        username,
-        password,
+    const response = await apiClient.post({
+      url,
+      headers: {
+        "Content-Type": "application/json",
+        Authorization:
+          "Basic " + Buffer.from(`${username}:${password}`).toString("base64"),
       },
-      headers: { "Content-Type": "application/json" },
+      body,
     });
 
     const data = response.data;
@@ -75,6 +76,18 @@ export async function addTestResult(
       ],
     };
   } catch (err: any) {
-    return formatAxiosError(err, "Failed to add test result to test run");
+    const msg =
+      err?.response?.data?.message ||
+      err?.response?.data?.error ||
+      err?.message ||
+      String(err);
+    return {
+      content: [
+        {
+          type: "text",
+          text: `Failed to add test result to test run: ${msg}`,
+        },
+      ],
+    };
   }
 }
diff --git a/src/tools/testmanagement-utils/create-lca-steps.ts b/src/tools/testmanagement-utils/create-lca-steps.ts
index 5751369..2f6e4a0 100644
--- a/src/tools/testmanagement-utils/create-lca-steps.ts
+++ b/src/tools/testmanagement-utils/create-lca-steps.ts
@@ -1,7 +1,6 @@
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
-import axios from "axios";
-import { formatAxiosError } from "../../lib/error.js";
+import { apiClient } from "../../lib/apiClient.js";
 import {
   projectIdentifierToId,
   testCaseIdentifierToDetails,
@@ -94,75 +93,61 @@ export async function createLCASteps(
       webhook_path: `https://test-management.browserstack.com/api/v1/projects/${projectId}/test-cases/${testCaseId}/webhooks/lcnc`,
     };
 
-    const response = await axios.post(url, payload, {
+    await apiClient.post({
+      url,
       headers: {
         "API-TOKEN": getBrowserStackAuth(config),
         accept: "application/json, text/plain, */*",
         "Content-Type": "application/json",
       },
+      body: payload,
     });
 
-    if (response.status >= 200 && response.status < 300) {
-      // Check if user wants to wait for completion
-      if (!args.wait_for_completion) {
+    // Check if user wants to wait for completion
+    if (!args.wait_for_completion) {
+      return {
+        content: [
+          {
+            type: "text",
+            text: `LCA steps creation initiated for test case ${args.test_case_identifier} (ID: ${testCaseId})`,
+          },
+          {
+            type: "text",
+            text: "LCA build started. Check the BrowserStack Lowcode Automation UI for completion status.",
+          },
+        ],
+      };
+    }
+
+    // Start polling for LCA build completion
+    try {
+      const max_wait_minutes = 10; // Maximum wait time in minutes
+      const maxWaitMs = max_wait_minutes * 60 * 1000;
+      const lcaResult = await pollLCAStatus(
+        projectId,
+        folderId,
+        testCaseId,
+        context,
+        maxWaitMs, // max wait time
+        2 * 60 * 1000, // 2 minutes initial wait
+        10 * 1000, // 10 seconds interval
+        config,
+      );
+
+      if (lcaResult && lcaResult.status === "done") {
         return {
           content: [
             {
               type: "text",
-              text: `LCA steps creation initiated for test case ${args.test_case_identifier} (ID: ${testCaseId})`,
+              text: `Successfully created LCA steps for test case ${args.test_case_identifier} (ID: ${testCaseId})`,
             },
             {
               type: "text",
-              text: "LCA build started. Check the BrowserStack Lowcode Automation UI for completion status.",
+              text: `LCA build completed! Resource URL: ${lcaResult.resource_path}`,
             },
           ],
         };
-      }
-
-      // Start polling for LCA build completion
-      try {
-        const max_wait_minutes = 10; // Maximum wait time in minutes
-        const maxWaitMs = max_wait_minutes * 60 * 1000;
-        const lcaResult = await pollLCAStatus(
-          projectId,
-          folderId,
-          testCaseId,
-          context,
-          maxWaitMs, // max wait time
-          2 * 60 * 1000, // 2 minutes initial wait
-          10 * 1000, // 10 seconds interval
-          config,
-        );
-
-        if (lcaResult && lcaResult.status === "done") {
-          return {
-            content: [
-              {
-                type: "text",
-                text: `Successfully created LCA steps for test case ${args.test_case_identifier} (ID: ${testCaseId})`,
-              },
-              {
-                type: "text",
-                text: `LCA build completed! Resource URL: ${lcaResult.resource_path}`,
-              },
-            ],
-          };
-        } else {
-          return {
-            content: [
-              {
-                type: "text",
-                text: `LCA steps creation initiated for test case ${args.test_case_identifier} (ID: ${testCaseId})`,
-              },
-              {
-                type: "text",
-                text: `Warning: LCA build did not complete within ${max_wait_minutes} minutes. You can check the status later in the BrowserStack Test Management UI.`,
-              },
-            ],
-          };
-        }
-      } catch (pollError) {
-        console.error("Error during LCA polling:", pollError);
+      } else {
         return {
           content: [
             {
@@ -171,15 +156,27 @@ export async function createLCASteps(
             },
             {
               type: "text",
-              text: "Warning: Error occurred while polling for LCA build completion. Check the BrowserStack Test Management UI for status.",
+              text: `Warning: LCA build did not complete within ${max_wait_minutes} minutes. You can check the status later in the BrowserStack Test Management UI.`,
             },
           ],
         };
       }
-    } else {
-      throw new Error(`Unexpected response: ${JSON.stringify(response.data)}`);
+    } catch (pollError) {
+      console.error("Error during LCA polling:", pollError);
+      return {
+        content: [
+          {
+            type: "text",
+            text: `LCA steps creation initiated for test case ${args.test_case_identifier} (ID: ${testCaseId})`,
+          },
+          {
+            type: "text",
+            text: "Warning: Error occurred while polling for LCA build completion. Check the BrowserStack Test Management UI for status.",
+          },
+        ],
+      };
     }
-  } catch (error) {
+  } catch (error: any) {
     // Add more specific error handling
     if (error instanceof Error) {
       if (error.message.includes("not found")) {
@@ -195,6 +192,19 @@ export async function createLCASteps(
         };
       }
     }
-    return formatAxiosError(error, "Failed to create LCA steps");
+    const msg =
+      error?.response?.data?.message ||
+      error?.response?.data?.error ||
+      error?.message ||
+      String(error);
+    return {
+      content: [
+        {
+          type: "text",
+          text: `Failed to create LCA steps: ${msg}`,
+        },
+      ],
+      isError: true,
+    };
   }
 }
diff --git a/src/tools/testmanagement-utils/create-project-folder.ts b/src/tools/testmanagement-utils/create-project-folder.ts
index 43a7058..df0e17a 100644
--- a/src/tools/testmanagement-utils/create-project-folder.ts
+++ b/src/tools/testmanagement-utils/create-project-folder.ts
@@ -1,7 +1,7 @@
-import axios from "axios";
+import { apiClient } from "../../lib/apiClient.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
-import { formatAxiosError } from "../../lib/error.js"; // or correct path
+import { formatAxiosError } from "../../lib/error.js";
 import { projectIdentifierToId } from "../testmanagement-utils/TCG-utils/api.js";
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { BrowserStackConfig } from "../../lib/types.js";
@@ -65,17 +65,18 @@ export async function createProjectOrFolder(
     try {
       const authString = getBrowserStackAuth(config);
       const [username, password] = authString.split(":");
-      const res = await axios.post(
-        "https://test-management.browserstack.com/api/v2/projects",
-        { project: { name: project_name, description: project_description } },
-        {
-          auth: {
-            username,
-            password,
-          },
-          headers: { "Content-Type": "application/json" },
+      const res = await apiClient.post({
+        url: "https://test-management.browserstack.com/api/v2/projects",
+        headers: {
+          "Content-Type": "application/json",
+          Authorization:
+            "Basic " +
+            Buffer.from(`${username}:${password}`).toString("base64"),
+        },
+        body: {
+          project: { name: project_name, description: project_description },
         },
-      );
+      });
 
       if (!res.data.success) {
         throw new Error(
@@ -85,8 +86,8 @@ export async function createProjectOrFolder(
       // Project created successfully
 
       projId = res.data.project.identifier;
-    } catch (err) {
-      return formatAxiosError(err, "Failed to create project..");
+    } catch (err: any) {
+      return formatAxiosError(err, "Failed to create project.");
     }
   }
   // Step 2: Create folder if folder_name provided
@@ -94,25 +95,24 @@ export async function createProjectOrFolder(
     if (!projId)
       throw new Error("Cannot create folder without project_identifier.");
     try {
-      const res = await axios.post(
-        `https://test-management.browserstack.com/api/v2/projects/${encodeURIComponent(
+      const res = await apiClient.post({
+        url: `https://test-management.browserstack.com/api/v2/projects/${encodeURIComponent(
           projId,
         )}/folders`,
-        {
+        headers: {
+          "Content-Type": "application/json",
+          Authorization:
+            "Basic " +
+            Buffer.from(`${username}:${password}`).toString("base64"),
+        },
+        body: {
           folder: {
             name: folder_name,
             description: folder_description,
             parent_id,
           },
         },
-        {
-          auth: {
-            username,
-            password,
-          },
-          headers: { "Content-Type": "application/json" },
-        },
-      );
+      });
 
       if (!res.data.success) {
         throw new Error(`Failed to create folder: ${JSON.stringify(res.data)}`);
@@ -134,7 +134,7 @@ export async function createProjectOrFolder(
           },
         ],
       };
-    } catch (err) {
+    } catch (err: any) {
       return formatAxiosError(err, "Failed to create folder.");
     }
   }
diff --git a/src/tools/testmanagement-utils/create-testcase.ts b/src/tools/testmanagement-utils/create-testcase.ts
index 2f9eac6..446d54d 100644
--- a/src/tools/testmanagement-utils/create-testcase.ts
+++ b/src/tools/testmanagement-utils/create-testcase.ts
@@ -1,7 +1,7 @@
-import axios from "axios";
+import { apiClient } from "../../lib/apiClient.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
-import { formatAxiosError } from "../../lib/error.js"; // or correct
+import { formatAxiosError } from "../../lib/error.js";
 import { projectIdentifierToId } from "./TCG-utils/api.js";
 import { BrowserStackConfig } from "../../lib/types.js";
 
@@ -149,19 +149,17 @@ export async function createTestCase(
   const [username, password] = authString.split(":");
 
   try {
-    const response = await axios.post<TestCaseResponse>(
-      `https://test-management.browserstack.com/api/v2/projects/${encodeURIComponent(
+    const response = await apiClient.post({
+      url: `https://test-management.browserstack.com/api/v2/projects/${encodeURIComponent(
         params.project_identifier,
       )}/folders/${encodeURIComponent(params.folder_id)}/test-cases`,
-      body,
-      {
-        auth: {
-          username,
-          password,
-        },
-        headers: { "Content-Type": "application/json" },
+      headers: {
+        "Content-Type": "application/json",
+        Authorization:
+          "Basic " + Buffer.from(`${username}:${password}`).toString("base64"),
       },
-    );
+      body,
+    });
 
     const { data } = response.data;
     if (!data.success) {
diff --git a/src/tools/testmanagement-utils/create-testrun.ts b/src/tools/testmanagement-utils/create-testrun.ts
index 66a1c04..45edaab 100644
--- a/src/tools/testmanagement-utils/create-testrun.ts
+++ b/src/tools/testmanagement-utils/create-testrun.ts
@@ -1,4 +1,4 @@
-import axios from "axios";
+import { apiClient } from "../../lib/apiClient.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js";
@@ -72,17 +72,15 @@ export async function createTestRun(
 
     const authString = getBrowserStackAuth(config);
     const [username, password] = authString.split(":");
-    const response = await axios.post(
+    const response = await apiClient.post({
       url,
-      { test_run: args.test_run },
-      {
-        auth: {
-          username,
-          password,
-        },
-        headers: { "Content-Type": "application/json" },
+      headers: {
+        "Content-Type": "application/json",
+        Authorization:
+          "Basic " + Buffer.from(`${username}:${password}`).toString("base64"),
       },
-    );
+      body: { test_run: args.test_run },
+    });
 
     const data = response.data;
     if (!data.success) {
diff --git a/src/tools/testmanagement-utils/list-testcases.ts b/src/tools/testmanagement-utils/list-testcases.ts
index 72e2397..98f95aa 100644
--- a/src/tools/testmanagement-utils/list-testcases.ts
+++ b/src/tools/testmanagement-utils/list-testcases.ts
@@ -1,4 +1,4 @@
-import axios from "axios";
+import { apiClient } from "../../lib/apiClient.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js";
@@ -55,10 +55,11 @@ export async function listTestCases(
 
     const authString = getBrowserStackAuth(config);
     const [username, password] = authString.split(":");
-    const resp = await axios.get(url, {
-      auth: {
-        username,
-        password,
+    const resp = await apiClient.get({
+      url,
+      headers: {
+        Authorization:
+          "Basic " + Buffer.from(`${username}:${password}`).toString("base64"),
       },
     });
 
diff --git a/src/tools/testmanagement-utils/list-testruns.ts b/src/tools/testmanagement-utils/list-testruns.ts
index 3520459..1d3390b 100644
--- a/src/tools/testmanagement-utils/list-testruns.ts
+++ b/src/tools/testmanagement-utils/list-testruns.ts
@@ -1,4 +1,4 @@
-import axios from "axios";
+import { apiClient } from "../../lib/apiClient.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import { formatAxiosError } from "../../lib/error.js";
@@ -42,10 +42,11 @@ export async function listTestRuns(
 
     const authString = getBrowserStackAuth(config);
     const [username, password] = authString.split(":");
-    const resp = await axios.get(url, {
-      auth: {
-        username,
-        password,
+    const resp = await apiClient.get({
+      url,
+      headers: {
+        Authorization:
+          "Basic " + Buffer.from(`${username}:${password}`).toString("base64"),
       },
     });
 
diff --git a/src/tools/testmanagement-utils/poll-lca-status.ts b/src/tools/testmanagement-utils/poll-lca-status.ts
index 2d5e99e..3a5e4fb 100644
--- a/src/tools/testmanagement-utils/poll-lca-status.ts
+++ b/src/tools/testmanagement-utils/poll-lca-status.ts
@@ -1,4 +1,4 @@
-import axios from "axios";
+import { apiClient } from "../../lib/apiClient.js";
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { BrowserStackConfig } from "../../lib/types.js";
 
@@ -94,20 +94,23 @@ export async function pollLCAStatus(
     const intervalId = setInterval(async () => {
       try {
         const authString = getBrowserStackAuth(config);
-        const response = await axios.get<TestCaseResponse>(url, {
+        const response = await apiClient.get({
+          url,
           headers: {
             "API-TOKEN": authString,
             accept: "application/json, text/plain, */*",
           },
         });
 
-        if (response.data.data.success && response.data.data.test_case) {
-          const testCase = response.data.data.test_case;
+        const responseData: TestCaseResponse = response.data;
+
+        if (responseData.data.success && responseData.data.test_case) {
+          const testCase = responseData.data.test_case;
 
           // Check lcnc_build_map in both possible locations
           const lcncBuildMap =
             testCase.lcnc_build_map ||
-            response.data.data.metadata?.lcnc_build_map;
+            responseData.data.metadata?.lcnc_build_map;
 
           if (lcncBuildMap) {
             if (lcncBuildMap.status === "done") {
diff --git a/src/tools/testmanagement-utils/update-testrun.ts b/src/tools/testmanagement-utils/update-testrun.ts
index 0aee151..0c56af7 100644
--- a/src/tools/testmanagement-utils/update-testrun.ts
+++ b/src/tools/testmanagement-utils/update-testrun.ts
@@ -1,4 +1,4 @@
-import axios from "axios";
+import { apiClient } from "../../lib/apiClient.js";
 import { getBrowserStackAuth } from "../../lib/get-auth.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
@@ -47,11 +47,14 @@ export async function updateTestRun(
     const authString = getBrowserStackAuth(config);
     const [username, password] = authString.split(":");
 
-    const resp = await axios.patch(url, body, {
-      auth: {
-        username,
-        password,
+    const resp = await apiClient.patch({
+      url,
+      headers: {
+        Authorization:
+          "Basic " + Buffer.from(`${username}:${password}`).toString("base64"),
+        "Content-Type": "application/json",
       },
+      body,
     });
 
     const data = resp.data;
diff --git a/src/tools/testmanagement-utils/upload-file.ts b/src/tools/testmanagement-utils/upload-file.ts
index 0b5c563..e8e81fc 100644
--- a/src/tools/testmanagement-utils/upload-file.ts
+++ b/src/tools/testmanagement-utils/upload-file.ts
@@ -1,6 +1,6 @@
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
-import axios from "axios";
+import { apiClient } from "../../lib/apiClient.js";
 import FormData from "form-data";
 import fs from "fs";
 import path from "path";
@@ -58,12 +58,14 @@ export async function uploadFile(
 
     const uploadUrl = `https://test-management.browserstack.com/api/v1/projects/${projectIdResponse}/generic/attachments/ai_uploads`;
 
-    const response = await axios.post(uploadUrl, formData, {
+    const response = await apiClient.post({
+      url: uploadUrl,
       headers: {
         ...formData.getHeaders(),
         "API-TOKEN": getBrowserStackAuth(config),
         accept: "application/json, text/plain, */*",
       },
+      body: formData,
     });
 
     if (
diff --git a/tests/tools/observability.test.ts b/tests/tools/observability.test.ts
index 5b99a01..4b8207e 100644
--- a/tests/tools/observability.test.ts
+++ b/tests/tools/observability.test.ts
@@ -17,25 +17,32 @@ describe('getFailuresInLastRun', () => {
   });
 
   const validBuildData = {
-    observability_url: 'https://observability.browserstack.com/123',
-    unique_errors: {
-      overview: {
-        insight: 'Test insight message'
-      },
-      top_unique_errors: [
-        { error: 'Error 1' },
-        { error: 'Error 2' }
-      ]
+    data: {
+      observability_url: 'https://observability.browserstack.com/123',
+      unique_errors: {
+        overview: {
+          insight: 'Test insight message'
+        },
+        top_unique_errors: [
+          { error: 'Error 1' },
+          { error: 'Error 2' }
+        ]
+      }
     }
   };
 
+  const mockConfig = {
+    "browserstack-username": "fakeuser",
+    "browserstack-access-key": "fakekey",
+    getClientVersion: () => "test-version"
+  };
+
   it('should successfully retrieve failures for a valid build', async () => {
     (getLatestO11YBuildInfo as Mock).mockResolvedValue(validBuildData);
 
-    const mockServer = { getClientVersion: () => "test-version" };
-    const result = await getFailuresInLastRun('test-build', 'test-project', mockServer);
+    const result = await getFailuresInLastRun('test-build', 'test-project', mockConfig);
 
-    expect(getLatestO11YBuildInfo).toHaveBeenCalledWith('test-build', 'test-project', mockServer);
+    expect(getLatestO11YBuildInfo).toHaveBeenCalledWith('test-build', 'test-project', mockConfig);
     expect(result.content).toBeDefined();
     expect(result.content![0].text).toContain('https://observability.browserstack.com/123');
     expect(result.content![0].text).toContain('Test insight message');
@@ -45,41 +52,44 @@ describe('getFailuresInLastRun', () => {
 
   it('should handle missing observability URL', async () => {
     (getLatestO11YBuildInfo as Mock).mockResolvedValue({
-      ...validBuildData,
-      observability_url: null
+      data: {
+        ...validBuildData.data,
+        observability_url: null
+      }
     });
 
-    const mockServer = { getClientVersion: () => "test-version" };
-    await expect(getFailuresInLastRun('test-build', 'test-project', mockServer))
+    await expect(getFailuresInLastRun('test-build', 'test-project', mockConfig))
       .rejects.toThrow('No observability URL found in build data');
   });
 
   it('should handle missing overview insight', async () => {
     (getLatestO11YBuildInfo as Mock).mockResolvedValue({
-      ...validBuildData,
-      unique_errors: {
-        ...validBuildData.unique_errors,
-        overview: {}
+      data: {
+        ...validBuildData.data,
+        unique_errors: {
+          ...validBuildData.data.unique_errors,
+          overview: {}
+        }
       }
     });
 
-    const mockServer = { getClientVersion: () => "test-version" };
-    const result = await getFailuresInLastRun('test-build', 'test-project', mockServer);
+    const result = await getFailuresInLastRun('test-build', 'test-project', mockConfig);
     expect(result.content).toBeDefined();
     expect(result.content![0].text).toContain('No overview available');
   });
 
   it('should handle missing error details', async () => {
     (getLatestO11YBuildInfo as Mock).mockResolvedValue({
-      ...validBuildData,
-      unique_errors: {
-        ...validBuildData.unique_errors,
-        top_unique_errors: []
+      data: {
+        ...validBuildData.data,
+        unique_errors: {
+          ...validBuildData.data.unique_errors,
+          top_unique_errors: []
+        }
       }
     });
 
-    const mockServer = { getClientVersion: () => "test-version" };
-    const result = await getFailuresInLastRun('test-build', 'test-project', mockServer);
+    const result = await getFailuresInLastRun('test-build', 'test-project', mockConfig);
     expect(result.content).toBeDefined();
     expect(result.content![0].text).toContain('No error details available');
   });
@@ -87,27 +97,26 @@ describe('getFailuresInLastRun', () => {
   it('should handle API errors', async () => {
     (getLatestO11YBuildInfo as Mock).mockRejectedValue(new Error('API Error'));
 
-    const mockServer = { getClientVersion: () => "test-version" };
-    await expect(getFailuresInLastRun('test-build', 'test-project', mockServer))
+    await expect(getFailuresInLastRun('test-build', 'test-project', mockConfig))
       .rejects.toThrow('API Error');
   });
 
   it('should handle empty build data', async () => {
     (getLatestO11YBuildInfo as Mock).mockResolvedValue({});
 
-    const mockServer = { getClientVersion: () => "test-version" };
-    await expect(getFailuresInLastRun('test-build', 'test-project', mockServer))
+    await expect(getFailuresInLastRun('test-build', 'test-project', mockConfig))
       .rejects.toThrow('No observability URL found in build data');
   });
 
   it('should handle partial build data', async () => {
     (getLatestO11YBuildInfo as Mock).mockResolvedValue({
-      observability_url: 'https://observability.browserstack.com/123',
-      unique_errors: {}
+      data: {
+        observability_url: 'https://observability.browserstack.com/123',
+        unique_errors: {}
+      }
     });
 
-    const mockServer = { getClientVersion: () => "test-version" };
-    const result = await getFailuresInLastRun('test-build', 'test-project', mockServer);
+    const result = await getFailuresInLastRun('test-build', 'test-project', mockConfig);
     expect(result.content).toBeDefined();
     expect(result.content![0].text).toContain('No overview available');
     expect(result.content![0].text).toContain('No error details available');

From ad61ad855b88d26a6d04a21e7f4f0c28edeba2e5 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 8 Jul 2025 17:29:50 +0530
Subject: [PATCH 43/64] refactor: remove unused exports and dependencies from
 package.json

---
 package-lock.json | 836 ++++++++++++++++++++++++++++++----------------
 package.json      |  11 +-
 2 files changed, 543 insertions(+), 304 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 811097e..52b81ba 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,10 +13,8 @@
         "@types/form-data": "^2.5.2",
         "axios": "^1.8.4",
         "browserstack-local": "^1.5.6",
-        "cors": "^2.8.5",
         "csv-parse": "^5.6.0",
         "dotenv": "^16.5.0",
-        "express": "^5.1.0",
         "form-data": "^4.0.2",
         "pino": "^9.6.0",
         "pino-pretty": "^13.0.0",
@@ -30,9 +28,7 @@
       },
       "devDependencies": {
         "@eslint/js": "^9.25.0",
-        "@types/cors": "^2.8.19",
         "@types/csv-parse": "^1.1.12",
-        "@types/express": "^5.0.3",
         "@types/node": "^22.14.1",
         "@types/uuid": "^10.0.0",
         "eslint": "^9.25.0",
@@ -1147,6 +1143,18 @@
         "node": ">=18"
       }
     },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+      "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+      "dependencies": {
+        "mime-types": "^3.0.0",
+        "negotiator": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": {
       "version": "8.17.1",
       "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
@@ -1163,12 +1171,234 @@
         "url": "https://github.com/sponsors/epoberezkin"
       }
     },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
+      "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
+      "dependencies": {
+        "bytes": "^3.1.2",
+        "content-type": "^1.0.5",
+        "debug": "^4.4.0",
+        "http-errors": "^2.0.0",
+        "iconv-lite": "^0.6.3",
+        "on-finished": "^2.4.1",
+        "qs": "^6.14.0",
+        "raw-body": "^3.0.0",
+        "type-is": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
+      "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
+      "dependencies": {
+        "safe-buffer": "5.2.1"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+      "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+      "engines": {
+        "node": ">=6.6.0"
+      }
+    },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/express": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
+      "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
+      "dependencies": {
+        "accepts": "^2.0.0",
+        "body-parser": "^2.2.0",
+        "content-disposition": "^1.0.0",
+        "content-type": "^1.0.5",
+        "cookie": "^0.7.1",
+        "cookie-signature": "^1.2.1",
+        "debug": "^4.4.0",
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "etag": "^1.8.1",
+        "finalhandler": "^2.1.0",
+        "fresh": "^2.0.0",
+        "http-errors": "^2.0.0",
+        "merge-descriptors": "^2.0.0",
+        "mime-types": "^3.0.0",
+        "on-finished": "^2.4.1",
+        "once": "^1.4.0",
+        "parseurl": "^1.3.3",
+        "proxy-addr": "^2.0.7",
+        "qs": "^6.14.0",
+        "range-parser": "^1.2.1",
+        "router": "^2.2.0",
+        "send": "^1.1.0",
+        "serve-static": "^2.2.0",
+        "statuses": "^2.0.1",
+        "type-is": "^2.0.1",
+        "vary": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
+      "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
+      "dependencies": {
+        "debug": "^4.4.0",
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "on-finished": "^2.4.1",
+        "parseurl": "^1.3.3",
+        "statuses": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+      "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
       "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
       "license": "MIT"
     },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+      "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+      "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": {
+      "version": "1.54.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
+      "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+      "dependencies": {
+        "mime-db": "^1.54.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+      "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/qs": {
+      "version": "6.14.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
+      "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
+      "dependencies": {
+        "side-channel": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/send": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
+      "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
+      "dependencies": {
+        "debug": "^4.3.5",
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "etag": "^1.8.1",
+        "fresh": "^2.0.0",
+        "http-errors": "^2.0.0",
+        "mime-types": "^3.0.1",
+        "ms": "^2.1.3",
+        "on-finished": "^2.4.1",
+        "range-parser": "^1.2.1",
+        "statuses": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
+      "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
+      "dependencies": {
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "parseurl": "^1.3.3",
+        "send": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
+      "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
+      "dependencies": {
+        "content-type": "^1.0.5",
+        "media-typer": "^1.1.0",
+        "mime-types": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/@nodelib/fs.scandir": {
       "version": "2.1.5",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -1520,34 +1750,6 @@
       "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
       "license": "MIT"
     },
-    "node_modules/@types/body-parser": {
-      "version": "1.19.6",
-      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
-      "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
-      "dev": true,
-      "dependencies": {
-        "@types/connect": "*",
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/connect": {
-      "version": "3.4.38",
-      "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
-      "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
-      "dev": true,
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/cors": {
-      "version": "2.8.19",
-      "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
-      "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
-      "dev": true,
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
     "node_modules/@types/csv-parse": {
       "version": "1.1.12",
       "resolved": "https://registry.npmjs.org/@types/csv-parse/-/csv-parse-1.1.12.tgz",
@@ -1564,29 +1766,6 @@
       "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
       "dev": true
     },
-    "node_modules/@types/express": {
-      "version": "5.0.3",
-      "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz",
-      "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==",
-      "dev": true,
-      "dependencies": {
-        "@types/body-parser": "*",
-        "@types/express-serve-static-core": "^5.0.0",
-        "@types/serve-static": "*"
-      }
-    },
-    "node_modules/@types/express-serve-static-core": {
-      "version": "5.0.6",
-      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz",
-      "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==",
-      "dev": true,
-      "dependencies": {
-        "@types/node": "*",
-        "@types/qs": "*",
-        "@types/range-parser": "*",
-        "@types/send": "*"
-      }
-    },
     "node_modules/@types/form-data": {
       "version": "2.5.2",
       "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.5.2.tgz",
@@ -1596,24 +1775,12 @@
         "form-data": "*"
       }
     },
-    "node_modules/@types/http-errors": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
-      "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
-      "dev": true
-    },
     "node_modules/@types/json-schema": {
       "version": "7.0.15",
       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
       "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
       "dev": true
     },
-    "node_modules/@types/mime": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
-      "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
-      "dev": true
-    },
     "node_modules/@types/node": {
       "version": "22.14.1",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz",
@@ -1622,39 +1789,6 @@
         "undici-types": "~6.21.0"
       }
     },
-    "node_modules/@types/qs": {
-      "version": "6.14.0",
-      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
-      "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
-      "dev": true
-    },
-    "node_modules/@types/range-parser": {
-      "version": "1.2.7",
-      "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
-      "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
-      "dev": true
-    },
-    "node_modules/@types/send": {
-      "version": "0.17.5",
-      "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz",
-      "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==",
-      "dev": true,
-      "dependencies": {
-        "@types/mime": "^1",
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/serve-static": {
-      "version": "1.15.8",
-      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz",
-      "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==",
-      "dev": true,
-      "dependencies": {
-        "@types/http-errors": "*",
-        "@types/node": "*",
-        "@types/send": "*"
-      }
-    },
     "node_modules/@types/sinonjs__fake-timers": {
       "version": "8.1.5",
       "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz",
@@ -2218,31 +2352,13 @@
       }
     },
     "node_modules/accepts": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
-      "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
-      "dependencies": {
-        "mime-types": "^3.0.0",
-        "negotiator": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/accepts/node_modules/mime-db": {
-      "version": "1.54.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
-      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/accepts/node_modules/mime-types": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
-      "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+      "peer": true,
       "dependencies": {
-        "mime-db": "^1.54.0"
+        "mime-types": "~2.1.34",
+        "negotiator": "0.6.3"
       },
       "engines": {
         "node": ">= 0.6"
@@ -2413,6 +2529,12 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+      "peer": true
+    },
     "node_modules/assertion-error": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
@@ -2571,26 +2693,61 @@
       "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==",
       "license": "MIT",
       "engines": {
-        "node": ">=10.0.0"
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/body-parser": {
+      "version": "1.20.3",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
+      "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
+      "peer": true,
+      "dependencies": {
+        "bytes": "3.1.2",
+        "content-type": "~1.0.5",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "on-finished": "2.4.1",
+        "qs": "6.13.0",
+        "raw-body": "2.5.2",
+        "type-is": "~1.6.18",
+        "unpipe": "1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
       }
     },
-    "node_modules/body-parser": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
-      "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
+    "node_modules/body-parser/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "peer": true,
       "dependencies": {
-        "bytes": "^3.1.2",
-        "content-type": "^1.0.5",
-        "debug": "^4.4.0",
-        "http-errors": "^2.0.0",
-        "iconv-lite": "^0.6.3",
-        "on-finished": "^2.4.1",
-        "qs": "^6.14.0",
-        "raw-body": "^3.0.0",
-        "type-is": "^2.0.0"
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/body-parser/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "peer": true
+    },
+    "node_modules/body-parser/node_modules/raw-body": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
+      "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+      "peer": true,
+      "dependencies": {
+        "bytes": "3.1.2",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
       },
       "engines": {
-        "node": ">=18"
+        "node": ">= 0.8"
       }
     },
     "node_modules/boolbase": {
@@ -2899,9 +3056,10 @@
       "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
     },
     "node_modules/content-disposition": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
-      "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+      "peer": true,
       "dependencies": {
         "safe-buffer": "5.2.1"
       },
@@ -2926,12 +3084,10 @@
       }
     },
     "node_modules/cookie-signature": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
-      "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
-      "engines": {
-        "node": ">=6.6.0"
-      }
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
+      "peer": true
     },
     "node_modules/core-util-is": {
       "version": "1.0.3",
@@ -3133,6 +3289,16 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/destroy": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
     "node_modules/detect-libc": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
@@ -3349,6 +3515,18 @@
         "url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
       }
     },
+    "node_modules/encoding-sniffer/node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/end-of-stream": {
       "version": "1.4.4",
       "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@@ -3737,40 +3915,45 @@
       }
     },
     "node_modules/express": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
-      "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
-      "dependencies": {
-        "accepts": "^2.0.0",
-        "body-parser": "^2.2.0",
-        "content-disposition": "^1.0.0",
-        "content-type": "^1.0.5",
-        "cookie": "^0.7.1",
-        "cookie-signature": "^1.2.1",
-        "debug": "^4.4.0",
-        "encodeurl": "^2.0.0",
-        "escape-html": "^1.0.3",
-        "etag": "^1.8.1",
-        "finalhandler": "^2.1.0",
-        "fresh": "^2.0.0",
-        "http-errors": "^2.0.0",
-        "merge-descriptors": "^2.0.0",
-        "mime-types": "^3.0.0",
-        "on-finished": "^2.4.1",
-        "once": "^1.4.0",
-        "parseurl": "^1.3.3",
-        "proxy-addr": "^2.0.7",
-        "qs": "^6.14.0",
-        "range-parser": "^1.2.1",
-        "router": "^2.2.0",
-        "send": "^1.1.0",
-        "serve-static": "^2.2.0",
-        "statuses": "^2.0.1",
-        "type-is": "^2.0.1",
-        "vary": "^1.1.2"
+      "version": "4.21.2",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
+      "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
+      "peer": true,
+      "dependencies": {
+        "accepts": "~1.3.8",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.20.3",
+        "content-disposition": "0.5.4",
+        "content-type": "~1.0.4",
+        "cookie": "0.7.1",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "1.3.1",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "merge-descriptors": "1.0.3",
+        "methods": "~1.1.2",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "0.1.12",
+        "proxy-addr": "~2.0.7",
+        "qs": "6.13.0",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.2.1",
+        "send": "0.19.0",
+        "serve-static": "1.16.2",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
       },
       "engines": {
-        "node": ">= 18"
+        "node": ">= 0.10.0"
       },
       "funding": {
         "type": "opencollective",
@@ -3791,25 +3974,21 @@
         "express": "^4.11 || 5 || ^5.0.0-beta.1"
       }
     },
-    "node_modules/express/node_modules/mime-db": {
-      "version": "1.54.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
-      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/express/node_modules/mime-types": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
-      "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+    "node_modules/express/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "peer": true,
       "dependencies": {
-        "mime-db": "^1.54.0"
-      },
-      "engines": {
-        "node": ">= 0.6"
+        "ms": "2.0.0"
       }
     },
+    "node_modules/express/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "peer": true
+    },
     "node_modules/extract-zip": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
@@ -4014,21 +4193,38 @@
       }
     },
     "node_modules/finalhandler": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
-      "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
-      "dependencies": {
-        "debug": "^4.4.0",
-        "encodeurl": "^2.0.0",
-        "escape-html": "^1.0.3",
-        "on-finished": "^2.4.1",
-        "parseurl": "^1.3.3",
-        "statuses": "^2.0.1"
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
+      "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
+      "peer": true,
+      "dependencies": {
+        "debug": "2.6.9",
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "statuses": "2.0.1",
+        "unpipe": "~1.0.0"
       },
       "engines": {
         "node": ">= 0.8"
       }
     },
+    "node_modules/finalhandler/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "peer": true,
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/finalhandler/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "peer": true
+    },
     "node_modules/find-up": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -4146,11 +4342,12 @@
       }
     },
     "node_modules/fresh": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
-      "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+      "peer": true,
       "engines": {
-        "node": ">= 0.8"
+        "node": ">= 0.6"
       }
     },
     "node_modules/from": {
@@ -4540,11 +4737,12 @@
       }
     },
     "node_modules/iconv-lite": {
-      "version": "0.6.3",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
-      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "peer": true,
       "dependencies": {
-        "safer-buffer": ">= 2.1.2 < 3.0.0"
+        "safer-buffer": ">= 2.1.2 < 3"
       },
       "engines": {
         "node": ">=0.10.0"
@@ -5038,20 +5236,19 @@
       }
     },
     "node_modules/media-typer": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
-      "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+      "peer": true,
       "engines": {
-        "node": ">= 0.8"
+        "node": ">= 0.6"
       }
     },
     "node_modules/merge-descriptors": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
-      "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
-      "engines": {
-        "node": ">=18"
-      },
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+      "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+      "peer": true,
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
@@ -5065,6 +5262,15 @@
         "node": ">= 8"
       }
     },
+    "node_modules/methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/micromatch": {
       "version": "4.0.8",
       "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
@@ -5078,6 +5284,18 @@
         "node": ">=8.6"
       }
     },
+    "node_modules/mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "peer": true,
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/mime-db": {
       "version": "1.52.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -5155,9 +5373,10 @@
       "dev": true
     },
     "node_modules/negotiator": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
-      "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+      "peer": true,
       "engines": {
         "node": ">= 0.6"
       }
@@ -5504,6 +5723,12 @@
       "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
       "license": "ISC"
     },
+    "node_modules/path-to-regexp": {
+      "version": "0.1.12",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+      "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
+      "peer": true
+    },
     "node_modules/pathe": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
@@ -5807,11 +6032,12 @@
       }
     },
     "node_modules/qs": {
-      "version": "6.14.0",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
-      "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
+      "version": "6.13.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
+      "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
+      "peer": true,
       "dependencies": {
-        "side-channel": "^1.1.0"
+        "side-channel": "^1.0.6"
       },
       "engines": {
         "node": ">=0.6"
@@ -5873,6 +6099,17 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/raw-body/node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/readable-stream": {
       "version": "4.7.0",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
@@ -6148,43 +6385,51 @@
       }
     },
     "node_modules/send": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
-      "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
+      "version": "0.19.0",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
+      "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
+      "peer": true,
       "dependencies": {
-        "debug": "^4.3.5",
-        "encodeurl": "^2.0.0",
-        "escape-html": "^1.0.3",
-        "etag": "^1.8.1",
-        "fresh": "^2.0.0",
-        "http-errors": "^2.0.0",
-        "mime-types": "^3.0.1",
-        "ms": "^2.1.3",
-        "on-finished": "^2.4.1",
-        "range-parser": "^1.2.1",
-        "statuses": "^2.0.1"
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "mime": "1.6.0",
+        "ms": "2.1.3",
+        "on-finished": "2.4.1",
+        "range-parser": "~1.2.1",
+        "statuses": "2.0.1"
       },
       "engines": {
-        "node": ">= 18"
+        "node": ">= 0.8.0"
       }
     },
-    "node_modules/send/node_modules/mime-db": {
-      "version": "1.54.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
-      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
-      "engines": {
-        "node": ">= 0.6"
+    "node_modules/send/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "peer": true,
+      "dependencies": {
+        "ms": "2.0.0"
       }
     },
-    "node_modules/send/node_modules/mime-types": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
-      "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
-      "dependencies": {
-        "mime-db": "^1.54.0"
-      },
+    "node_modules/send/node_modules/debug/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "peer": true
+    },
+    "node_modules/send/node_modules/encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+      "peer": true,
       "engines": {
-        "node": ">= 0.6"
+        "node": ">= 0.8"
       }
     },
     "node_modules/serialize-error": {
@@ -6215,17 +6460,18 @@
       }
     },
     "node_modules/serve-static": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
-      "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
+      "version": "1.16.2",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
+      "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
+      "peer": true,
       "dependencies": {
-        "encodeurl": "^2.0.0",
-        "escape-html": "^1.0.3",
-        "parseurl": "^1.3.3",
-        "send": "^1.2.0"
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "0.19.0"
       },
       "engines": {
-        "node": ">= 18"
+        "node": ">= 0.8.0"
       }
     },
     "node_modules/setimmediate": {
@@ -6840,32 +7086,13 @@
       }
     },
     "node_modules/type-is": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
-      "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
-      "dependencies": {
-        "content-type": "^1.0.5",
-        "media-typer": "^1.1.0",
-        "mime-types": "^3.0.0"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/type-is/node_modules/mime-db": {
-      "version": "1.54.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
-      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/type-is/node_modules/mime-types": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
-      "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "peer": true,
       "dependencies": {
-        "mime-db": "^1.54.0"
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
       },
       "engines": {
         "node": ">= 0.6"
@@ -6958,6 +7185,15 @@
       "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
       "license": "MIT"
     },
+    "node_modules/utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
     "node_modules/uuid": {
       "version": "11.1.0",
       "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
@@ -7304,6 +7540,18 @@
         "node": ">=18"
       }
     },
+    "node_modules/whatwg-encoding/node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/whatwg-mimetype": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
@@ -7507,4 +7755,4 @@
       }
     }
   }
-}
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 382bf59..73e332a 100644
--- a/package.json
+++ b/package.json
@@ -8,11 +8,6 @@
     "url": "git+https://github.com/browserstack/mcp-server.git"
   },
   "type": "module",
-  "exports": {
-    ".": "./dist/index.js",
-    "./lib/logger.js": "./dist/logger.js",
-    "./lib/server-factory.js": "./dist/server-factory.js"
-  },
   "scripts": {
     "build": "npm run lint && npm run format && npm test && tsc",
     "start": "node dist/index.js",
@@ -43,10 +38,8 @@
     "@types/form-data": "^2.5.2",
     "axios": "^1.8.4",
     "browserstack-local": "^1.5.6",
-    "cors": "^2.8.5",
     "csv-parse": "^5.6.0",
     "dotenv": "^16.5.0",
-    "express": "^5.1.0",
     "form-data": "^4.0.2",
     "pino": "^9.6.0",
     "pino-pretty": "^13.0.0",
@@ -57,9 +50,7 @@
   },
   "devDependencies": {
     "@eslint/js": "^9.25.0",
-    "@types/cors": "^2.8.19",
     "@types/csv-parse": "^1.1.12",
-    "@types/express": "^5.0.3",
     "@types/node": "^22.14.1",
     "@types/uuid": "^10.0.0",
     "eslint": "^9.25.0",
@@ -70,4 +61,4 @@
     "vite": "^6.3.5",
     "vitest": "^3.1.3"
   }
-}
+}
\ No newline at end of file

From 861444e1b2ec10b108386eff835382a5d6cd3af4 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 8 Jul 2025 17:31:41 +0530
Subject: [PATCH 44/64] fix: add missing newline at end of package-lock.json

---
 package-lock.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package-lock.json b/package-lock.json
index 52b81ba..9a07d19 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7755,4 +7755,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}

From 90624dfef9b7cbce6ab41f9d9becef0c4a2a1542 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 10 Jul 2025 14:07:22 +0530
Subject: [PATCH 45/64] fix: ensure isolatedModules is set to true in
 tsconfig.json for better compatibility

---
 tsconfig.json | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tsconfig.json b/tsconfig.json
index 2857e59..d923b93 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -4,6 +4,7 @@
     "module": "NodeNext",
     "strict": true,
     "esModuleInterop": true,
+    "isolatedModules": true,
     "skipLibCheck": true,
     "forceConsistentCasingInFileNames": true,
     "resolveJsonModule": true,

From 3680a147dd365b299e239d333a9d8c7eca382122 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 10 Jul 2025 15:33:52 +0530
Subject: [PATCH 46/64] refactor: remove default export of logger from index.ts

---
 src/index.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/index.ts b/src/index.ts
index ad56428..230063f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -48,5 +48,4 @@ process.on("exit", () => {
   logger.flush();
 });
 
-export { default as logger } from "./logger.js";
 export { createMcpServer } from "./server-factory.js";

From 981aa5da177330d97e2ca6ebfe4c109ebd36d907 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 10 Jul 2025 19:11:46 +0530
Subject: [PATCH 47/64] feat: integrate chitragupta logger and update
 dependencies in package.json and package-lock.json

---
 package-lock.json                           | 247 ++++++++++++++++++++
 package.json                                |   4 +-
 src/lib/chitragupta-logger/chitragupta.d.ts |  11 +
 src/lib/chitragupta-logger/logger.ts        |  33 +++
 src/logger.ts                               |   8 +-
 5 files changed, 300 insertions(+), 3 deletions(-)
 create mode 100644 src/lib/chitragupta-logger/chitragupta.d.ts
 create mode 100644 src/lib/chitragupta-logger/logger.ts

diff --git a/package-lock.json b/package-lock.json
index 9a07d19..7678132 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,7 @@
         "@types/form-data": "^2.5.2",
         "axios": "^1.8.4",
         "browserstack-local": "^1.5.6",
+        "chitragupta": "github:browserstack/chitragupta-node",
         "csv-parse": "^5.6.0",
         "dotenv": "^16.5.0",
         "form-data": "^4.0.2",
@@ -21,6 +22,7 @@
         "sharp": "^0.34.1",
         "uuid": "^11.1.0",
         "webdriverio": "^9.13.0",
+        "winston": "^3.17.0",
         "zod": "^3.24.3"
       },
       "bin": {
@@ -43,6 +45,24 @@
         "node": ">=18"
       }
     },
+    "node_modules/@colors/colors": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+      "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
+      "engines": {
+        "node": ">=0.1.90"
+      }
+    },
+    "node_modules/@dabh/diagnostics": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
+      "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
+      "dependencies": {
+        "colorspace": "1.1.x",
+        "enabled": "2.0.x",
+        "kuler": "^2.0.0"
+      }
+    },
     "node_modules/@emnapi/runtime": {
       "version": "1.4.3",
       "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz",
@@ -1795,6 +1815,11 @@
       "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==",
       "license": "MIT"
     },
+    "node_modules/@types/triple-beam": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
+      "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="
+    },
     "node_modules/@types/uuid": {
       "version": "10.0.0",
       "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
@@ -2561,6 +2586,17 @@
       "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
       "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="
     },
+    "node_modules/async-hook-jl": {
+      "version": "1.7.6",
+      "resolved": "https://registry.npmjs.org/async-hook-jl/-/async-hook-jl-1.7.6.tgz",
+      "integrity": "sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg==",
+      "dependencies": {
+        "stack-chain": "^1.3.7"
+      },
+      "engines": {
+        "node": "^4.7 || >=6.9 || >=7.3"
+      }
+    },
     "node_modules/asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -2957,6 +2993,22 @@
         "url": "https://github.com/sponsors/fb55"
       }
     },
+    "node_modules/chitragupta": {
+      "version": "1.7.5",
+      "resolved": "git+ssh://git@github.com/browserstack/chitragupta-node.git#e72239a13fd00cd9a78df9a3f465db9663607b55",
+      "dependencies": {
+        "cls-hooked": "^4.2.2",
+        "uuid": "^8.3.2"
+      }
+    },
+    "node_modules/chitragupta/node_modules/uuid": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
     "node_modules/cliui": {
       "version": "8.0.1",
       "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -2970,6 +3022,27 @@
         "node": ">=12"
       }
     },
+    "node_modules/cls-hooked": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/cls-hooked/-/cls-hooked-4.2.2.tgz",
+      "integrity": "sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw==",
+      "dependencies": {
+        "async-hook-jl": "^1.7.6",
+        "emitter-listener": "^1.0.1",
+        "semver": "^5.4.1"
+      },
+      "engines": {
+        "node": "^4.7 || >=6.9 || >=7.3 || >=8.2.1"
+      }
+    },
+    "node_modules/cls-hooked/node_modules/semver": {
+      "version": "5.7.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
     "node_modules/color": {
       "version": "4.2.3",
       "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
@@ -3014,6 +3087,37 @@
       "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
       "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
     },
+    "node_modules/colorspace": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
+      "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
+      "dependencies": {
+        "color": "^3.1.3",
+        "text-hex": "1.0.x"
+      }
+    },
+    "node_modules/colorspace/node_modules/color": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
+      "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
+      "dependencies": {
+        "color-convert": "^1.9.3",
+        "color-string": "^1.6.0"
+      }
+    },
+    "node_modules/colorspace/node_modules/color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/colorspace/node_modules/color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+    },
     "node_modules/combined-stream": {
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -3489,11 +3593,24 @@
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
       "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
     },
+    "node_modules/emitter-listener": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz",
+      "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==",
+      "dependencies": {
+        "shimmer": "^1.2.0"
+      }
+    },
     "node_modules/emoji-regex": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
       "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
     },
+    "node_modules/enabled": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
+      "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
+    },
     "node_modules/encodeurl": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
@@ -4145,6 +4262,11 @@
         "pend": "~1.2.0"
       }
     },
+    "node_modules/fecha": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
+      "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
+    },
     "node_modules/fetch-blob": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
@@ -4260,6 +4382,11 @@
       "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
       "dev": true
     },
+    "node_modules/fn.name": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
+      "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
+    },
     "node_modules/follow-redirects": {
       "version": "1.15.9",
       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
@@ -5052,6 +5179,11 @@
         "json-buffer": "3.0.1"
       }
     },
+    "node_modules/kuler": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
+      "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
+    },
     "node_modules/lazystream": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
@@ -5188,6 +5320,22 @@
       "integrity": "sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==",
       "license": "MIT"
     },
+    "node_modules/logform": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
+      "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
+      "dependencies": {
+        "@colors/colors": "1.6.0",
+        "@types/triple-beam": "^1.3.2",
+        "fecha": "^4.2.0",
+        "ms": "^2.1.1",
+        "safe-stable-stringify": "^2.3.1",
+        "triple-beam": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
     "node_modules/loglevel": {
       "version": "1.9.2",
       "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz",
@@ -5494,6 +5642,14 @@
         "wrappy": "1"
       }
     },
+    "node_modules/one-time": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
+      "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
+      "dependencies": {
+        "fn.name": "1.x.x"
+      }
+    },
     "node_modules/optionator": {
       "version": "0.9.4",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -6544,6 +6700,11 @@
         "node": ">=8"
       }
     },
+    "node_modules/shimmer": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz",
+      "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="
+    },
     "node_modules/side-channel": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
@@ -6741,6 +6902,19 @@
         "node": ">= 10.x"
       }
     },
+    "node_modules/stack-chain": {
+      "version": "1.3.7",
+      "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-1.3.7.tgz",
+      "integrity": "sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug=="
+    },
+    "node_modules/stack-trace": {
+      "version": "0.0.10",
+      "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+      "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/stackback": {
       "version": "0.0.2",
       "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
@@ -6922,6 +7096,11 @@
         "b4a": "^1.6.4"
       }
     },
+    "node_modules/text-hex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
+      "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
+    },
     "node_modules/thread-stream": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
@@ -7036,6 +7215,14 @@
         "node": ">=0.6"
       }
     },
+    "node_modules/triple-beam": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
+      "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
     "node_modules/ts-api-utils": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
@@ -7591,6 +7778,66 @@
         "node": ">=8"
       }
     },
+    "node_modules/winston": {
+      "version": "3.17.0",
+      "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz",
+      "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==",
+      "dependencies": {
+        "@colors/colors": "^1.6.0",
+        "@dabh/diagnostics": "^2.0.2",
+        "async": "^3.2.3",
+        "is-stream": "^2.0.0",
+        "logform": "^2.7.0",
+        "one-time": "^1.0.0",
+        "readable-stream": "^3.4.0",
+        "safe-stable-stringify": "^2.3.1",
+        "stack-trace": "0.0.x",
+        "triple-beam": "^1.3.0",
+        "winston-transport": "^4.9.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
+    "node_modules/winston-transport": {
+      "version": "4.9.0",
+      "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
+      "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
+      "dependencies": {
+        "logform": "^2.7.0",
+        "readable-stream": "^3.6.2",
+        "triple-beam": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
+    "node_modules/winston-transport/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/winston/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/word-wrap": {
       "version": "1.2.5",
       "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
diff --git a/package.json b/package.json
index 73e332a..c8070a3 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
     "@types/form-data": "^2.5.2",
     "axios": "^1.8.4",
     "browserstack-local": "^1.5.6",
+    "chitragupta": "github:browserstack/chitragupta-node",
     "csv-parse": "^5.6.0",
     "dotenv": "^16.5.0",
     "form-data": "^4.0.2",
@@ -46,6 +47,7 @@
     "sharp": "^0.34.1",
     "uuid": "^11.1.0",
     "webdriverio": "^9.13.0",
+    "winston": "^3.17.0",
     "zod": "^3.24.3"
   },
   "devDependencies": {
@@ -61,4 +63,4 @@
     "vite": "^6.3.5",
     "vitest": "^3.1.3"
   }
-}
\ No newline at end of file
+}
diff --git a/src/lib/chitragupta-logger/chitragupta.d.ts b/src/lib/chitragupta-logger/chitragupta.d.ts
new file mode 100644
index 0000000..66ba1e5
--- /dev/null
+++ b/src/lib/chitragupta-logger/chitragupta.d.ts
@@ -0,0 +1,11 @@
+declare module 'chitragupta' {
+    interface FormatterOptions {
+      level: string;
+      message: string;
+      meta: any;
+    }
+  
+    export class Chitragupta {
+      static jsonLogFormatter(options: FormatterOptions): string;
+    }
+  }
\ No newline at end of file
diff --git a/src/lib/chitragupta-logger/logger.ts b/src/lib/chitragupta-logger/logger.ts
new file mode 100644
index 0000000..7461529
--- /dev/null
+++ b/src/lib/chitragupta-logger/logger.ts
@@ -0,0 +1,33 @@
+import * as winston from 'winston';
+import { TransformableInfo } from 'logform';
+import { Chitragupta } from 'chitragupta';
+
+interface ChitraguptaOptions {
+  level: string;
+  message: string;
+  meta: any;
+}
+
+const jsonLogFormatter = winston.format.printf(
+  (info: TransformableInfo): string => {
+    const { level, message, ...meta } = info;
+
+    const options: ChitraguptaOptions = {
+      level,
+      message: message as string, 
+      meta,
+    };
+
+    return Chitragupta.jsonLogFormatter(options);
+  }
+);
+
+const logger: winston.Logger = winston.createLogger({
+  transports: [
+    new winston.transports.Console({
+      format: jsonLogFormatter,
+    }),
+  ],
+});
+
+export default logger;
\ No newline at end of file
diff --git a/src/logger.ts b/src/logger.ts
index 8020128..7e533ae 100644
--- a/src/logger.ts
+++ b/src/logger.ts
@@ -1,8 +1,12 @@
 import { pino } from "pino";
+import config from "./config.js";
+import chitraguptaLogger from "./lib/chitragupta-logger/logger.js";
 
-let logger: pino.Logger;
+let logger: any;
 
-if (process.env.NODE_ENV === "development") {
+if (config.REMOTE_MCP) {
+  logger = chitraguptaLogger;
+} else if (process.env.NODE_ENV === "development") {
   logger = pino({
     level: "debug",
     transport: {

From 94359e9d967133ffc1e5858726bb1db580606f03 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Fri, 11 Jul 2025 12:18:50 +0530
Subject: [PATCH 48/64] fix: update chitragupta module to use double quotes and
 ensure logger.ts has consistent import style fix: modify device cache to
 store live response data correctly

---
 src/lib/chitragupta-logger/chitragupta.d.ts | 22 ++++++++++-----------
 src/lib/chitragupta-logger/logger.ts        | 12 +++++------
 src/lib/device-cache.ts                     |  2 +-
 3 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/src/lib/chitragupta-logger/chitragupta.d.ts b/src/lib/chitragupta-logger/chitragupta.d.ts
index 66ba1e5..49887b2 100644
--- a/src/lib/chitragupta-logger/chitragupta.d.ts
+++ b/src/lib/chitragupta-logger/chitragupta.d.ts
@@ -1,11 +1,11 @@
-declare module 'chitragupta' {
-    interface FormatterOptions {
-      level: string;
-      message: string;
-      meta: any;
-    }
-  
-    export class Chitragupta {
-      static jsonLogFormatter(options: FormatterOptions): string;
-    }
-  }
\ No newline at end of file
+declare module "chitragupta" {
+  interface FormatterOptions {
+    level: string;
+    message: string;
+    meta: any;
+  }
+
+  export class Chitragupta {
+    static jsonLogFormatter(options: FormatterOptions): string;
+  }
+}
diff --git a/src/lib/chitragupta-logger/logger.ts b/src/lib/chitragupta-logger/logger.ts
index 7461529..9854c62 100644
--- a/src/lib/chitragupta-logger/logger.ts
+++ b/src/lib/chitragupta-logger/logger.ts
@@ -1,6 +1,6 @@
-import * as winston from 'winston';
-import { TransformableInfo } from 'logform';
-import { Chitragupta } from 'chitragupta';
+import * as winston from "winston";
+import { TransformableInfo } from "logform";
+import { Chitragupta } from "chitragupta";
 
 interface ChitraguptaOptions {
   level: string;
@@ -14,12 +14,12 @@ const jsonLogFormatter = winston.format.printf(
 
     const options: ChitraguptaOptions = {
       level,
-      message: message as string, 
+      message: message as string,
       meta,
     };
 
     return Chitragupta.jsonLogFormatter(options);
-  }
+  },
 );
 
 const logger: winston.Logger = winston.createLogger({
@@ -30,4 +30,4 @@ const logger: winston.Logger = winston.createLogger({
   ],
 });
 
-export default logger;
\ No newline at end of file
+export default logger;
diff --git a/src/lib/device-cache.ts b/src/lib/device-cache.ts
index 7415790..44b98f0 100644
--- a/src/lib/device-cache.ts
+++ b/src/lib/device-cache.ts
@@ -58,7 +58,7 @@ export async function getDevicesAndBrowsers(
   }
 
   cache = {
-    [type]: liveRes,
+    [type]: liveRes.data,
   };
   fs.writeFileSync(CACHE_FILE, JSON.stringify(cache), "utf8");
 

From fc79dd4db3978218c03a7b8277380b64847a57bc Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Fri, 11 Jul 2025 13:12:10 +0530
Subject: [PATCH 49/64] feat: add raise_error option to API requests for better
 error handling

---
 src/lib/api.ts                                |  1 +
 src/lib/apiClient.ts                          | 63 ++++++++++++-------
 src/lib/device-cache.ts                       |  2 +-
 .../accessiblity-utils/accessibility-rag.ts   |  1 +
 src/tools/failurelogs-utils/app-automate.ts   |  3 +
 src/tools/failurelogs-utils/automate.ts       |  3 +
 src/tools/failurelogs-utils/utils.ts          |  3 +-
 7 files changed, 52 insertions(+), 24 deletions(-)

diff --git a/src/lib/api.ts b/src/lib/api.ts
index bff40d4..64051e7 100644
--- a/src/lib/api.ts
+++ b/src/lib/api.ts
@@ -19,6 +19,7 @@ export async function getLatestO11YBuildInfo(
     headers: {
       Authorization: `Basic ${auth}`,
     },
+    raise_error: false,
   });
 
   if (!buildsResponse.ok) {
diff --git a/src/lib/apiClient.ts b/src/lib/apiClient.ts
index 6d1becd..555f51b 100644
--- a/src/lib/apiClient.ts
+++ b/src/lib/apiClient.ts
@@ -5,6 +5,7 @@ type RequestOptions = {
   headers?: Record<string, string>;
   params?: Record<string, string | number>;
   body?: any;
+  raise_error?: boolean; // default: true
 };
 
 class ApiResponse<T = any> {
@@ -56,61 +57,79 @@ class ApiResponse<T = any> {
 class ApiClient {
   private instance = axios.create();
 
+  private async requestWrapper<T>(
+    fn: () => Promise<AxiosResponse<T>>,
+    raise_error: boolean = true,
+  ): Promise<ApiResponse<T>> {
+    try {
+      const res = await fn();
+      return new ApiResponse<T>(res);
+    } catch (error: any) {
+      if (error.response && !raise_error) {
+        return new ApiResponse<T>(error.response);
+      }
+      throw error;
+    }
+  }
+
   async get<T = any>({
     url,
     headers,
     params,
+    raise_error = true,
   }: RequestOptions): Promise<ApiResponse<T>> {
-    const res = await this.instance.get<T>(url, {
-      headers,
-      params,
-    });
-    return new ApiResponse<T>(res);
+    return this.requestWrapper<T>(
+      () => this.instance.get<T>(url, { headers, params }),
+      raise_error,
+    );
   }
 
   async post<T = any>({
     url,
     headers,
     body,
+    raise_error = true,
   }: RequestOptions): Promise<ApiResponse<T>> {
-    const res = await this.instance.post<T>(url, body, {
-      headers,
-    });
-    return new ApiResponse<T>(res);
+    return this.requestWrapper<T>(
+      () => this.instance.post<T>(url, body, { headers }),
+      raise_error,
+    );
   }
 
   async put<T = any>({
     url,
     headers,
     body,
+    raise_error = true,
   }: RequestOptions): Promise<ApiResponse<T>> {
-    const res = await this.instance.put<T>(url, body, {
-      headers,
-    });
-    return new ApiResponse<T>(res);
+    return this.requestWrapper<T>(
+      () => this.instance.put<T>(url, body, { headers }),
+      raise_error,
+    );
   }
 
   async patch<T = any>({
     url,
     headers,
     body,
+    raise_error = true,
   }: RequestOptions): Promise<ApiResponse<T>> {
-    const res = await this.instance.patch<T>(url, body, {
-      headers,
-    });
-    return new ApiResponse<T>(res);
+    return this.requestWrapper<T>(
+      () => this.instance.patch<T>(url, body, { headers }),
+      raise_error,
+    );
   }
 
   async delete<T = any>({
     url,
     headers,
     params,
+    raise_error = true,
   }: RequestOptions): Promise<ApiResponse<T>> {
-    const res = await this.instance.delete<T>(url, {
-      headers,
-      params,
-    });
-    return new ApiResponse<T>(res);
+    return this.requestWrapper<T>(
+      () => this.instance.delete<T>(url, { headers, params }),
+      raise_error,
+    );
   }
 }
 
diff --git a/src/lib/device-cache.ts b/src/lib/device-cache.ts
index 44b98f0..55010a4 100644
--- a/src/lib/device-cache.ts
+++ b/src/lib/device-cache.ts
@@ -49,7 +49,7 @@ export async function getDevicesAndBrowsers(
     }
   }
 
-  const liveRes = await apiClient.get({ url: URLS[type] });
+  const liveRes = await apiClient.get({ url: URLS[type], raise_error: false });
 
   if (!liveRes.ok) {
     throw new Error(
diff --git a/src/tools/accessiblity-utils/accessibility-rag.ts b/src/tools/accessiblity-utils/accessibility-rag.ts
index 510aefd..a77b9c3 100644
--- a/src/tools/accessiblity-utils/accessibility-rag.ts
+++ b/src/tools/accessiblity-utils/accessibility-rag.ts
@@ -33,6 +33,7 @@ export async function queryAccessibilityRAG(
     body: {
       query: userQuery,
     },
+    raise_error: false,
   });
 
   if (!response.ok) {
diff --git a/src/tools/failurelogs-utils/app-automate.ts b/src/tools/failurelogs-utils/app-automate.ts
index 0bfecb1..85d33a4 100644
--- a/src/tools/failurelogs-utils/app-automate.ts
+++ b/src/tools/failurelogs-utils/app-automate.ts
@@ -19,6 +19,7 @@ export async function retrieveDeviceLogs(
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
     },
+    raise_error: false,
   });
 
   const validationError = validateLogResponse(response, "device logs");
@@ -50,6 +51,7 @@ export async function retrieveAppiumLogs(
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
     },
+    raise_error: false,
   });
 
   const validationError = validateLogResponse(response, "Appium logs");
@@ -81,6 +83,7 @@ export async function retrieveCrashLogs(
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
     },
+    raise_error: false,
   });
 
   const validationError = validateLogResponse(response, "crash logs");
diff --git a/src/tools/failurelogs-utils/automate.ts b/src/tools/failurelogs-utils/automate.ts
index 99e3977..9f2cf29 100644
--- a/src/tools/failurelogs-utils/automate.ts
+++ b/src/tools/failurelogs-utils/automate.ts
@@ -23,6 +23,7 @@ export async function retrieveNetworkFailures(
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
     },
+    raise_error: false,
   });
 
   const validationError = validateLogResponse(response, "network logs");
@@ -74,6 +75,7 @@ export async function retrieveSessionFailures(
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
     },
+    raise_error: false,
   });
 
   const validationError = validateLogResponse(response, "session logs");
@@ -104,6 +106,7 @@ export async function retrieveConsoleFailures(
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
     },
+    raise_error: false,
   });
 
   const validationError = validateLogResponse(response, "console logs");
diff --git a/src/tools/failurelogs-utils/utils.ts b/src/tools/failurelogs-utils/utils.ts
index 9655368..3cc8084 100644
--- a/src/tools/failurelogs-utils/utils.ts
+++ b/src/tools/failurelogs-utils/utils.ts
@@ -1,3 +1,5 @@
+import type { ApiResponse } from "../../lib/apiClient.js";
+
 export interface LogResponse {
   logs?: any[];
   message?: string;
@@ -25,7 +27,6 @@ export interface HarEntry {
   time?: number;
 }
 
-import type { ApiResponse } from "../../lib/apiClient.js";
 export function validateLogResponse(
   response: Response | ApiResponse,
   logType: string,

From b8c84057a4a882986a9223abe4255672dad38e94 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Fri, 11 Jul 2025 13:22:24 +0530
Subject: [PATCH 50/64] fix: add raise_error option to API request in
 extractScreenshotUrls function

---
 src/tools/automate-utils/fetch-screenshots.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/tools/automate-utils/fetch-screenshots.ts b/src/tools/automate-utils/fetch-screenshots.ts
index 6168c80..5574f0b 100644
--- a/src/tools/automate-utils/fetch-screenshots.ts
+++ b/src/tools/automate-utils/fetch-screenshots.ts
@@ -21,6 +21,7 @@ async function extractScreenshotUrls(
       "Content-Type": "application/json",
       Authorization: `Basic ${auth}`,
     },
+    raise_error: false,
   });
 
   await assertOkResponse(response, "Session");

From 39b009edd9ff6128ee029f67b84548c8232bc0b7 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 15 Jul 2025 16:59:41 +0530
Subject: [PATCH 51/64] feat: refactor tools to return structured tool objects
 for better organization and access

---
 src/index.ts                |  8 ++--
 src/server-factory.ts       | 87 ++++++++++++++++++++++++++-----------
 src/tools/accessibility.ts  |  8 +++-
 src/tools/appautomate.ts    |  8 +++-
 src/tools/applive.ts        |  6 ++-
 src/tools/automate.ts       |  6 ++-
 src/tools/bstack-sdk.ts     |  6 ++-
 src/tools/getFailureLogs.ts | 13 +++---
 src/tools/live.ts           |  6 ++-
 src/tools/selfheal.ts       |  6 ++-
 src/tools/testmanagement.ts | 28 +++++++-----
 11 files changed, 129 insertions(+), 53 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index 230063f..89e6be7 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -6,7 +6,7 @@ const require = createRequire(import.meta.url);
 const packageJson = require("../package.json");
 import "dotenv/config";
 import logger from "./logger.js";
-import { createMcpServer } from "./server-factory.js";
+import { BrowserStackMcpServer } from "./server-factory.js";
 
 async function main() {
   logger.info(
@@ -33,12 +33,12 @@ async function main() {
 
   const transport = new StdioServerTransport();
 
-  const server = createMcpServer({
+  const mcpServer = new BrowserStackMcpServer({
     "browserstack-username": username,
     "browserstack-access-key": accessKey,
   });
 
-  await server.connect(transport);
+  await mcpServer.getInstance().connect(transport);
 }
 
 main().catch(console.error);
@@ -48,4 +48,4 @@ process.on("exit", () => {
   logger.flush();
 });
 
-export { createMcpServer } from "./server-factory.js";
+export { BrowserStackMcpServer } from "./server-factory.js";
diff --git a/src/server-factory.ts b/src/server-factory.ts
index d471467..504165b 100644
--- a/src/server-factory.ts
+++ b/src/server-factory.ts
@@ -1,4 +1,7 @@
-import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import {
+  McpServer,
+  RegisteredTool,
+} from "@modelcontextprotocol/sdk/server/mcp.js";
 import { createRequire } from "module";
 const require = createRequire(import.meta.url);
 const packageJson = require("../package.json");
@@ -15,32 +18,66 @@ import addAppLiveTools from "./tools/applive.js";
 import { setupOnInitialized } from "./oninitialized.js";
 import { BrowserStackConfig } from "./lib/types.js";
 
-function registerTools(server: McpServer, config: BrowserStackConfig) {
-  addAccessibilityTools(server, config);
-  addSDKTools(server, config);
-  addAppLiveTools(server, config);
-  addBrowserLiveTools(server, config);
-  addTestManagementTools(server, config);
-  addAppAutomationTools(server, config);
-  addFailureLogsTools(server, config);
-  addAutomateTools(server, config);
-  addSelfHealTools(server, config);
-}
+/**
+ * Wrapper class for BrowserStack MCP Server
+ * Stores a map of registered tools by name
+ */
+export class BrowserStackMcpServer {
+  public server: McpServer;
+  public tools: Record<string, RegisteredTool> = {};
+
+  constructor(private config: BrowserStackConfig) {
+    logger.info(
+      "Creating BrowserStack MCP Server, version %s",
+      packageJson.version,
+    );
+
+    this.server = new McpServer({
+      name: "BrowserStack MCP Server",
+      version: packageJson.version,
+    });
+
+    setupOnInitialized(this.server, this.config);
+    this.registerTools();
+  }
 
-export function createMcpServer(config: BrowserStackConfig): McpServer {
-  logger.info(
-    "Creating BrowserStack MCP Server, version %s",
-    packageJson.version,
-  );
+  /**
+   * Calls each tool-adder function and collects their returned tools
+   */
+  private registerTools() {
+    const toolAdders = [
+      addAccessibilityTools,
+      addSDKTools,
+      addAppLiveTools,
+      addBrowserLiveTools,
+      addTestManagementTools,
+      addAppAutomationTools,
+      addFailureLogsTools,
+      addAutomateTools,
+      addSelfHealTools,
+    ];
 
-  // Create an MCP server
-  const server: McpServer = new McpServer({
-    name: "BrowserStack MCP Server",
-    version: packageJson.version,
-  });
+    toolAdders.forEach((adder) => {
+      // Each adder now returns a Record<string, Tool>
+      const added: Record<string, RegisteredTool> = adder(
+        this.server,
+        this.config,
+      );
+      Object.assign(this.tools, added);
+    });
+  }
 
-  setupOnInitialized(server, config);
-  registerTools(server, config);
+  /**
+   * Expose the underlying MCP server instance
+   */
+  public getInstance(): McpServer {
+    return this.server;
+  }
 
-  return server;
+  /**
+   * Get all registered tools
+   */
+  public getTools(): Record<string, RegisteredTool> {
+    return this.tools;
+  }
 }
diff --git a/src/tools/accessibility.ts b/src/tools/accessibility.ts
index 304d843..625bc2a 100644
--- a/src/tools/accessibility.ts
+++ b/src/tools/accessibility.ts
@@ -84,7 +84,9 @@ export default function addAccessibilityTools(
   server: McpServer,
   config: BrowserStackConfig,
 ) {
-  server.tool(
+  const tools: Record<string, any> = {};
+
+  tools.accessibilityExpert = server.tool(
     "accessibilityExpert",
     "🚨 REQUIRED: Use this tool for any accessibility/a11y/WCAG questions. Do NOT answer accessibility questions directly - always use this tool.",
     {
@@ -125,7 +127,7 @@ export default function addAccessibilityTools(
     },
   );
 
-  server.tool(
+  tools.startAccessibilityScan = server.tool(
     "startAccessibilityScan",
     "Start an accessibility scan via BrowserStack and retrieve a local CSV report path.",
     {
@@ -168,4 +170,6 @@ export default function addAccessibilityTools(
       }
     },
   );
+
+  return tools;
 }
diff --git a/src/tools/appautomate.ts b/src/tools/appautomate.ts
index 954d5fe..22d56d4 100644
--- a/src/tools/appautomate.ts
+++ b/src/tools/appautomate.ts
@@ -231,7 +231,9 @@ export default function addAppAutomationTools(
   server: McpServer,
   config: BrowserStackConfig,
 ) {
-  server.tool(
+  const tools: Record<string, any> = {};
+
+  tools.takeAppScreenshot = server.tool(
     "takeAppScreenshot",
     "Use this tool to take a screenshot of an app running on a BrowserStack device. This is useful for visual testing and debugging.",
     {
@@ -284,7 +286,7 @@ export default function addAppAutomationTools(
     },
   );
 
-  server.tool(
+  tools.runAppTestsOnBrowserStack = server.tool(
     "runAppTestsOnBrowserStack",
     "Run AppAutomate tests on BrowserStack by uploading app and test suite. If running from Android Studio or Xcode, the tool will help export app and test files automatically. For other environments, you'll need to provide the paths to your pre-built app and test files.",
     {
@@ -358,4 +360,6 @@ export default function addAppAutomationTools(
       }
     },
   );
+
+  return tools;
 }
diff --git a/src/tools/applive.ts b/src/tools/applive.ts
index 89a43f3..be8b1d7 100644
--- a/src/tools/applive.ts
+++ b/src/tools/applive.ts
@@ -74,7 +74,9 @@ export default function addAppLiveTools(
   server: McpServer,
   config: BrowserStackConfig,
 ) {
-  server.tool(
+  const tools: Record<string, any> = {};
+
+  tools.runAppLiveSession = server.tool(
     "runAppLiveSession",
     "Use this tool when user wants to manually check their app on a particular mobile device using BrowserStack's cloud infrastructure. Can be used to debug crashes, slow performance, etc.",
     {
@@ -129,4 +131,6 @@ export default function addAppLiveTools(
       }
     },
   );
+
+  return tools;
 }
diff --git a/src/tools/automate.ts b/src/tools/automate.ts
index 340eff5..7fa95be 100644
--- a/src/tools/automate.ts
+++ b/src/tools/automate.ts
@@ -62,7 +62,9 @@ export default function addAutomationTools(
   server: McpServer,
   config: BrowserStackConfig,
 ) {
-  server.tool(
+  const tools: Record<string, any> = {};
+
+  tools.fetchAutomationScreenshots = server.tool(
     "fetchAutomationScreenshots",
     "Fetch and process screenshots from a BrowserStack Automate session",
     {
@@ -102,4 +104,6 @@ export default function addAutomationTools(
       }
     },
   );
+
+  return tools;
 }
diff --git a/src/tools/bstack-sdk.ts b/src/tools/bstack-sdk.ts
index 83999b1..2482f1e 100644
--- a/src/tools/bstack-sdk.ts
+++ b/src/tools/bstack-sdk.ts
@@ -167,7 +167,9 @@ export default function addSDKTools(
   server: McpServer,
   config: BrowserStackConfig,
 ) {
-  server.tool(
+  const tools: Record<string, any> = {};
+
+  tools.runTestsOnBrowserStack = server.tool(
     "runTestsOnBrowserStack",
     "Use this tool to get instructions for running tests on BrowserStack and BrowserStack Percy. It sets up the BrowserStack SDK and runs your test cases on BrowserStack.",
     {
@@ -247,4 +249,6 @@ export default function addSDKTools(
       }
     },
   );
+
+  return tools;
 }
diff --git a/src/tools/getFailureLogs.ts b/src/tools/getFailureLogs.ts
index 782e43d..6c41477 100644
--- a/src/tools/getFailureLogs.ts
+++ b/src/tools/getFailureLogs.ts
@@ -1,7 +1,6 @@
 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 import { z } from "zod";
 import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
-import logger from "../logger.js";
 import { trackMCP } from "../lib/instrumentation.js";
 import { BrowserStackConfig } from "../lib/types.js";
 
@@ -169,7 +168,9 @@ export default function registerGetFailureLogs(
   server: McpServer,
   config: BrowserStackConfig,
 ) {
-  server.tool(
+  const tools: Record<string, any> = {};
+
+  tools.getFailureLogs = server.tool(
     "getFailureLogs",
     "Fetch various types of logs from a BrowserStack session. Supports both automate and app-automate sessions.",
     {
@@ -212,19 +213,19 @@ export default function registerGetFailureLogs(
         );
         return await getFailureLogs(args, config);
       } catch (error) {
-        const message = error instanceof Error ? error.message : String(error);
         trackMCP(
           "getFailureLogs",
           server.server.getClientVersion()!,
           error,
           config,
         );
-        logger.error("Failed to fetch logs: %s", message);
         return {
           content: [
             {
               type: "text",
-              text: `Failed to fetch logs: ${message}`,
+              text: `Failed to fetch failure logs: ${
+                error instanceof Error ? error.message : "Unknown error"
+              }`,
               isError: true,
             },
           ],
@@ -233,4 +234,6 @@ export default function registerGetFailureLogs(
       }
     },
   );
+
+  return tools;
 }
diff --git a/src/tools/live.ts b/src/tools/live.ts
index cc313c7..b1aa42e 100644
--- a/src/tools/live.ts
+++ b/src/tools/live.ts
@@ -111,7 +111,9 @@ export default function addBrowserLiveTools(
   server: McpServer,
   config: BrowserStackConfig,
 ) {
-  server.tool(
+  const tools: Record<string, any> = {};
+
+  tools.runBrowserLiveSession = server.tool(
     "runBrowserLiveSession",
     "Launch a BrowserStack Live session (desktop or mobile).",
     LiveArgsShape,
@@ -145,4 +147,6 @@ export default function addBrowserLiveTools(
       }
     },
   );
+
+  return tools;
 }
diff --git a/src/tools/selfheal.ts b/src/tools/selfheal.ts
index 0bfe1c5..ee67668 100644
--- a/src/tools/selfheal.ts
+++ b/src/tools/selfheal.ts
@@ -34,7 +34,9 @@ export default function addSelfHealTools(
   server: McpServer,
   config: BrowserStackConfig,
 ) {
-  server.tool(
+  const tools: Record<string, any> = {};
+
+  tools.fetchSelfHealedSelectors = server.tool(
     "fetchSelfHealedSelectors",
     "Retrieves AI-generated, self-healed selectors for a BrowserStack Automate session to resolve flaky tests caused by dynamic DOM changes.",
     {
@@ -69,4 +71,6 @@ export default function addSelfHealTools(
       }
     },
   );
+
+  return tools;
 }
diff --git a/src/tools/testmanagement.ts b/src/tools/testmanagement.ts
index c5e9c3a..830639c 100644
--- a/src/tools/testmanagement.ts
+++ b/src/tools/testmanagement.ts
@@ -410,70 +410,78 @@ export default function addTestManagementTools(
   server: McpServer,
   config: BrowserStackConfig,
 ) {
-  server.tool(
+  const tools: Record<string, any> = {};
+
+  tools.createProjectOrFolder = server.tool(
     "createProjectOrFolder",
     "Create a project and/or folder in BrowserStack Test Management.",
     CreateProjFoldSchema.shape,
     (args) => createProjectOrFolderTool(args, config, server),
   );
 
-  server.tool(
+  tools.createTestCase = server.tool(
     "createTestCase",
     "Use this tool to create a test case in BrowserStack Test Management.",
     CreateTestCaseSchema.shape,
     (args) => createTestCaseTool(args, config, server),
   );
 
-  server.tool(
+  tools.listTestCases = server.tool(
     "listTestCases",
     "List test cases in a project with optional filters (status, priority, custom fields, etc.)",
     ListTestCasesSchema.shape,
     (args) => listTestCasesTool(args, config, server),
   );
 
-  server.tool(
+  tools.createTestRun = server.tool(
     "createTestRun",
     "Create a test run in BrowserStack Test Management.",
     CreateTestRunSchema.shape,
     (args) => createTestRunTool(args, config, server),
   );
 
-  server.tool(
+  tools.listTestRuns = server.tool(
     "listTestRuns",
     "List test runs in a project with optional filters (date ranges, assignee, state, etc.)",
     ListTestRunsSchema.shape,
     (args) => listTestRunsTool(args, config, server),
   );
-  server.tool(
+
+  tools.updateTestRun = server.tool(
     "updateTestRun",
     "Update a test run in BrowserStack Test Management.",
     UpdateTestRunSchema.shape,
     (args) => updateTestRunTool(args, config, server),
   );
-  server.tool(
+
+  tools.addTestResult = server.tool(
     "addTestResult",
     "Add a test result to a specific test run via BrowserStack Test Management API.",
     AddTestResultSchema.shape,
     (args) => addTestResultTool(args, config, server),
   );
 
-  server.tool(
+  tools.uploadProductRequirementFile = server.tool(
     "uploadProductRequirementFile",
     "Upload files (e.g., PDRs, PDFs) to BrowserStack Test Management and retrieve a file mapping ID. This is utilized for generating test cases from files and is part of the Test Case Generator AI Agent in BrowserStack.",
     UploadFileSchema.shape,
     (args) => uploadProductRequirementFileTool(args, config, server),
   );
-  server.tool(
+
+  tools.createTestCasesFromFile = server.tool(
     "createTestCasesFromFile",
     "Generate test cases from a file in BrowserStack Test Management using the Test Case Generator AI Agent.",
     CreateTestCasesFromFileSchema.shape,
     (args, context) =>
       createTestCasesFromFileTool(args, context, config, server),
   );
-  server.tool(
+
+  tools.createLCASteps = server.tool(
     "createLCASteps",
     "Generate Low Code Automation (LCA) steps for a test case in BrowserStack Test Management using the Low Code Automation Agent.",
     CreateLCAStepsSchema.shape,
     (args, context) => createLCAStepsTool(args, context, config, server),
   );
+
+  return tools;
 }

From ad8f61de94c1949f05a575a6d89d64d54b8e1033 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Tue, 15 Jul 2025 17:25:49 +0530
Subject: [PATCH 52/64] feat: add getTool method to retrieve a specific
 registered tool by name

---
 src/server-factory.ts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/server-factory.ts b/src/server-factory.ts
index 504165b..228e3bb 100644
--- a/src/server-factory.ts
+++ b/src/server-factory.ts
@@ -80,4 +80,8 @@ export class BrowserStackMcpServer {
   public getTools(): Record<string, RegisteredTool> {
     return this.tools;
   }
+  
+  public getTool(name: string): RegisteredTool | undefined {
+    return this.tools[name];
+  }
 }

From ea3bbd2c72d78c46d9010e19814bf9407016d181 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Wed, 16 Jul 2025 11:20:07 +0530
Subject: [PATCH 53/64] refactor: remove chitragupta logger integration and
 update logger implementation

---
 package-lock.json                           | 247 --------------------
 package.json                                |   2 -
 src/index.ts                                |   1 +
 src/lib/chitragupta-logger/chitragupta.d.ts |  11 -
 src/lib/chitragupta-logger/logger.ts        |  33 ---
 src/logger.ts                               |  14 +-
 6 files changed, 10 insertions(+), 298 deletions(-)
 delete mode 100644 src/lib/chitragupta-logger/chitragupta.d.ts
 delete mode 100644 src/lib/chitragupta-logger/logger.ts

diff --git a/package-lock.json b/package-lock.json
index 7678132..9a07d19 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,7 +13,6 @@
         "@types/form-data": "^2.5.2",
         "axios": "^1.8.4",
         "browserstack-local": "^1.5.6",
-        "chitragupta": "github:browserstack/chitragupta-node",
         "csv-parse": "^5.6.0",
         "dotenv": "^16.5.0",
         "form-data": "^4.0.2",
@@ -22,7 +21,6 @@
         "sharp": "^0.34.1",
         "uuid": "^11.1.0",
         "webdriverio": "^9.13.0",
-        "winston": "^3.17.0",
         "zod": "^3.24.3"
       },
       "bin": {
@@ -45,24 +43,6 @@
         "node": ">=18"
       }
     },
-    "node_modules/@colors/colors": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
-      "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
-      "engines": {
-        "node": ">=0.1.90"
-      }
-    },
-    "node_modules/@dabh/diagnostics": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
-      "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
-      "dependencies": {
-        "colorspace": "1.1.x",
-        "enabled": "2.0.x",
-        "kuler": "^2.0.0"
-      }
-    },
     "node_modules/@emnapi/runtime": {
       "version": "1.4.3",
       "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz",
@@ -1815,11 +1795,6 @@
       "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==",
       "license": "MIT"
     },
-    "node_modules/@types/triple-beam": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
-      "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="
-    },
     "node_modules/@types/uuid": {
       "version": "10.0.0",
       "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
@@ -2586,17 +2561,6 @@
       "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
       "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="
     },
-    "node_modules/async-hook-jl": {
-      "version": "1.7.6",
-      "resolved": "https://registry.npmjs.org/async-hook-jl/-/async-hook-jl-1.7.6.tgz",
-      "integrity": "sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg==",
-      "dependencies": {
-        "stack-chain": "^1.3.7"
-      },
-      "engines": {
-        "node": "^4.7 || >=6.9 || >=7.3"
-      }
-    },
     "node_modules/asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -2993,22 +2957,6 @@
         "url": "https://github.com/sponsors/fb55"
       }
     },
-    "node_modules/chitragupta": {
-      "version": "1.7.5",
-      "resolved": "git+ssh://git@github.com/browserstack/chitragupta-node.git#e72239a13fd00cd9a78df9a3f465db9663607b55",
-      "dependencies": {
-        "cls-hooked": "^4.2.2",
-        "uuid": "^8.3.2"
-      }
-    },
-    "node_modules/chitragupta/node_modules/uuid": {
-      "version": "8.3.2",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
-      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
-      "bin": {
-        "uuid": "dist/bin/uuid"
-      }
-    },
     "node_modules/cliui": {
       "version": "8.0.1",
       "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -3022,27 +2970,6 @@
         "node": ">=12"
       }
     },
-    "node_modules/cls-hooked": {
-      "version": "4.2.2",
-      "resolved": "https://registry.npmjs.org/cls-hooked/-/cls-hooked-4.2.2.tgz",
-      "integrity": "sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw==",
-      "dependencies": {
-        "async-hook-jl": "^1.7.6",
-        "emitter-listener": "^1.0.1",
-        "semver": "^5.4.1"
-      },
-      "engines": {
-        "node": "^4.7 || >=6.9 || >=7.3 || >=8.2.1"
-      }
-    },
-    "node_modules/cls-hooked/node_modules/semver": {
-      "version": "5.7.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
-      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
-      "bin": {
-        "semver": "bin/semver"
-      }
-    },
     "node_modules/color": {
       "version": "4.2.3",
       "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
@@ -3087,37 +3014,6 @@
       "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
       "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
     },
-    "node_modules/colorspace": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
-      "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
-      "dependencies": {
-        "color": "^3.1.3",
-        "text-hex": "1.0.x"
-      }
-    },
-    "node_modules/colorspace/node_modules/color": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
-      "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
-      "dependencies": {
-        "color-convert": "^1.9.3",
-        "color-string": "^1.6.0"
-      }
-    },
-    "node_modules/colorspace/node_modules/color-convert": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dependencies": {
-        "color-name": "1.1.3"
-      }
-    },
-    "node_modules/colorspace/node_modules/color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
-    },
     "node_modules/combined-stream": {
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -3593,24 +3489,11 @@
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
       "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
     },
-    "node_modules/emitter-listener": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz",
-      "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==",
-      "dependencies": {
-        "shimmer": "^1.2.0"
-      }
-    },
     "node_modules/emoji-regex": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
       "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
     },
-    "node_modules/enabled": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
-      "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
-    },
     "node_modules/encodeurl": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
@@ -4262,11 +4145,6 @@
         "pend": "~1.2.0"
       }
     },
-    "node_modules/fecha": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
-      "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
-    },
     "node_modules/fetch-blob": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
@@ -4382,11 +4260,6 @@
       "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
       "dev": true
     },
-    "node_modules/fn.name": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
-      "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
-    },
     "node_modules/follow-redirects": {
       "version": "1.15.9",
       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
@@ -5179,11 +5052,6 @@
         "json-buffer": "3.0.1"
       }
     },
-    "node_modules/kuler": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
-      "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
-    },
     "node_modules/lazystream": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
@@ -5320,22 +5188,6 @@
       "integrity": "sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==",
       "license": "MIT"
     },
-    "node_modules/logform": {
-      "version": "2.7.0",
-      "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
-      "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
-      "dependencies": {
-        "@colors/colors": "1.6.0",
-        "@types/triple-beam": "^1.3.2",
-        "fecha": "^4.2.0",
-        "ms": "^2.1.1",
-        "safe-stable-stringify": "^2.3.1",
-        "triple-beam": "^1.3.0"
-      },
-      "engines": {
-        "node": ">= 12.0.0"
-      }
-    },
     "node_modules/loglevel": {
       "version": "1.9.2",
       "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz",
@@ -5642,14 +5494,6 @@
         "wrappy": "1"
       }
     },
-    "node_modules/one-time": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
-      "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
-      "dependencies": {
-        "fn.name": "1.x.x"
-      }
-    },
     "node_modules/optionator": {
       "version": "0.9.4",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -6700,11 +6544,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/shimmer": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz",
-      "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="
-    },
     "node_modules/side-channel": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
@@ -6902,19 +6741,6 @@
         "node": ">= 10.x"
       }
     },
-    "node_modules/stack-chain": {
-      "version": "1.3.7",
-      "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-1.3.7.tgz",
-      "integrity": "sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug=="
-    },
-    "node_modules/stack-trace": {
-      "version": "0.0.10",
-      "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
-      "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
-      "engines": {
-        "node": "*"
-      }
-    },
     "node_modules/stackback": {
       "version": "0.0.2",
       "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
@@ -7096,11 +6922,6 @@
         "b4a": "^1.6.4"
       }
     },
-    "node_modules/text-hex": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
-      "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
-    },
     "node_modules/thread-stream": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
@@ -7215,14 +7036,6 @@
         "node": ">=0.6"
       }
     },
-    "node_modules/triple-beam": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
-      "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
-      "engines": {
-        "node": ">= 14.0.0"
-      }
-    },
     "node_modules/ts-api-utils": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
@@ -7778,66 +7591,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/winston": {
-      "version": "3.17.0",
-      "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz",
-      "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==",
-      "dependencies": {
-        "@colors/colors": "^1.6.0",
-        "@dabh/diagnostics": "^2.0.2",
-        "async": "^3.2.3",
-        "is-stream": "^2.0.0",
-        "logform": "^2.7.0",
-        "one-time": "^1.0.0",
-        "readable-stream": "^3.4.0",
-        "safe-stable-stringify": "^2.3.1",
-        "stack-trace": "0.0.x",
-        "triple-beam": "^1.3.0",
-        "winston-transport": "^4.9.0"
-      },
-      "engines": {
-        "node": ">= 12.0.0"
-      }
-    },
-    "node_modules/winston-transport": {
-      "version": "4.9.0",
-      "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
-      "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
-      "dependencies": {
-        "logform": "^2.7.0",
-        "readable-stream": "^3.6.2",
-        "triple-beam": "^1.3.0"
-      },
-      "engines": {
-        "node": ">= 12.0.0"
-      }
-    },
-    "node_modules/winston-transport/node_modules/readable-stream": {
-      "version": "3.6.2",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
-      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
-      "dependencies": {
-        "inherits": "^2.0.3",
-        "string_decoder": "^1.1.1",
-        "util-deprecate": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/winston/node_modules/readable-stream": {
-      "version": "3.6.2",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
-      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
-      "dependencies": {
-        "inherits": "^2.0.3",
-        "string_decoder": "^1.1.1",
-        "util-deprecate": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
     "node_modules/word-wrap": {
       "version": "1.2.5",
       "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
diff --git a/package.json b/package.json
index c8070a3..e1bb823 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,6 @@
     "@types/form-data": "^2.5.2",
     "axios": "^1.8.4",
     "browserstack-local": "^1.5.6",
-    "chitragupta": "github:browserstack/chitragupta-node",
     "csv-parse": "^5.6.0",
     "dotenv": "^16.5.0",
     "form-data": "^4.0.2",
@@ -47,7 +46,6 @@
     "sharp": "^0.34.1",
     "uuid": "^11.1.0",
     "webdriverio": "^9.13.0",
-    "winston": "^3.17.0",
     "zod": "^3.24.3"
   },
   "devDependencies": {
diff --git a/src/index.ts b/src/index.ts
index 230063f..de3f2f2 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -49,3 +49,4 @@ process.on("exit", () => {
 });
 
 export { createMcpServer } from "./server-factory.js";
+export { setLogger } from "./logger.js";
diff --git a/src/lib/chitragupta-logger/chitragupta.d.ts b/src/lib/chitragupta-logger/chitragupta.d.ts
deleted file mode 100644
index 49887b2..0000000
--- a/src/lib/chitragupta-logger/chitragupta.d.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-declare module "chitragupta" {
-  interface FormatterOptions {
-    level: string;
-    message: string;
-    meta: any;
-  }
-
-  export class Chitragupta {
-    static jsonLogFormatter(options: FormatterOptions): string;
-  }
-}
diff --git a/src/lib/chitragupta-logger/logger.ts b/src/lib/chitragupta-logger/logger.ts
deleted file mode 100644
index 9854c62..0000000
--- a/src/lib/chitragupta-logger/logger.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import * as winston from "winston";
-import { TransformableInfo } from "logform";
-import { Chitragupta } from "chitragupta";
-
-interface ChitraguptaOptions {
-  level: string;
-  message: string;
-  meta: any;
-}
-
-const jsonLogFormatter = winston.format.printf(
-  (info: TransformableInfo): string => {
-    const { level, message, ...meta } = info;
-
-    const options: ChitraguptaOptions = {
-      level,
-      message: message as string,
-      meta,
-    };
-
-    return Chitragupta.jsonLogFormatter(options);
-  },
-);
-
-const logger: winston.Logger = winston.createLogger({
-  transports: [
-    new winston.transports.Console({
-      format: jsonLogFormatter,
-    }),
-  ],
-});
-
-export default logger;
diff --git a/src/logger.ts b/src/logger.ts
index 7e533ae..d27ae4c 100644
--- a/src/logger.ts
+++ b/src/logger.ts
@@ -1,12 +1,8 @@
 import { pino } from "pino";
-import config from "./config.js";
-import chitraguptaLogger from "./lib/chitragupta-logger/logger.js";
 
 let logger: any;
 
-if (config.REMOTE_MCP) {
-  logger = chitraguptaLogger;
-} else if (process.env.NODE_ENV === "development") {
+if (process.env.NODE_ENV === "development") {
   logger = pino({
     level: "debug",
     transport: {
@@ -39,4 +35,12 @@ if (config.REMOTE_MCP) {
   });
 }
 
+/**
+ * Set a custom logger instance
+ * @param customLogger - The pino logger instance to use
+ */
+export function setLogger(customLogger: any): void {
+  logger = customLogger;
+}
+
 export default logger;

From b20e36ef3057193ecf73046819a3ec84aef392e7 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Wed, 16 Jul 2025 11:25:24 +0530
Subject: [PATCH 54/64] fix: update parameter description in setLogger function
 for clarity

---
 src/logger.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/logger.ts b/src/logger.ts
index d27ae4c..3dc2eb2 100644
--- a/src/logger.ts
+++ b/src/logger.ts
@@ -37,7 +37,7 @@ if (process.env.NODE_ENV === "development") {
 
 /**
  * Set a custom logger instance
- * @param customLogger - The pino logger instance to use
+ * @param customLogger - The logger instance to use
  */
 export function setLogger(customLogger: any): void {
   logger = customLogger;

From 8c1612c3dfd5f76e6bc88f08b5c6aedb9ea6c051 Mon Sep 17 00:00:00 2001
From: tech-sushant <sushant.s@browserstack.com>
Date: Wed, 16 Jul 2025 12:53:12 +0530
Subject: [PATCH 55/64] fix: update project and build names in YML instructions
 for clarity

---
 src/tools/sdk-utils/instructions.ts | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/tools/sdk-utils/instructions.ts b/src/tools/sdk-utils/instructions.ts
index 1d87d20..d98c872 100644
--- a/src/tools/sdk-utils/instructions.ts
+++ b/src/tools/sdk-utils/instructions.ts
@@ -50,10 +50,10 @@ export function generateBrowserStackYMLInstructions(
 # ======================
 # BrowserStack Reporting
 # ======================
-# A single name for your project to organize all your tests. This is required for Percy.
-projectName: BrowserStack SDK Tests
-# A name for the group of tests you are running
-buildName: mcp-run
+# Project and build names help organize your test runs in BrowserStack dashboard and Percy.
+# TODO: Replace these sample values with your actual project details
+projectName: Sample Project
+buildName: Sample Build
 
 # =======================================
 # Platforms (Browsers / Devices to test)

From ce479aaed5d70cdcadabed020a0b36eefff821a8 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Wed, 16 Jul 2025 13:01:12 +0530
Subject: [PATCH 56/64] feat: add support for Serenity framework in SDK
 utilities and instructions

---
 src/tools/sdk-utils/commands.ts        |  1 +
 src/tools/sdk-utils/constants.ts       | 58 ++++++++++++++++++++++++++
 src/tools/sdk-utils/percy/constants.ts |  1 +
 src/tools/sdk-utils/types.ts           |  1 +
 4 files changed, 61 insertions(+)

diff --git a/src/tools/sdk-utils/commands.ts b/src/tools/sdk-utils/commands.ts
index 28ff9a5..f257347 100644
--- a/src/tools/sdk-utils/commands.ts
+++ b/src/tools/sdk-utils/commands.ts
@@ -7,6 +7,7 @@ const JAVA_FRAMEWORK_MAP: Record<string, string> = {
   junit5: "junit5",
   junit4: "junit4",
   cucumber: "cucumber-testng",
+  serenity: "serenity",
 };
 
 // Common Gradle setup instructions (platform-independent)
diff --git a/src/tools/sdk-utils/constants.ts b/src/tools/sdk-utils/constants.ts
index 15e3c02..5149e41 100644
--- a/src/tools/sdk-utils/constants.ts
+++ b/src/tools/sdk-utils/constants.ts
@@ -106,6 +106,63 @@ gradle clean test
 \`\`\`
 `;
 
+const serenityInstructions = (username: string, accessKey: string) => `
+---STEP---
+
+Set BrowserStack credentials as environment variables:
+For macOS/Linux:
+\`\`\`bash
+export BROWSERSTACK_USERNAME=${username}
+export BROWSERSTACK_ACCESS_KEY=${accessKey}
+\`\`\`
+
+For Windows Command Prompt:
+\`\`\`cmd
+set BROWSERSTACK_USERNAME=${username}
+set BROWSERSTACK_ACCESS_KEY=${accessKey}
+\`\`\`
+
+---STEP---
+
+Add serenity-browserstack dependency in pom.xml:
+Add the following dependency to your pom.xml file and save it:
+\`\`\`xml
+<dependency>
+  <groupId>net.serenity-bdd</groupId>
+  <artifactId>serenity-browserstack</artifactId>
+  <version>3.3.4</version>
+</dependency>
+\`\`\`
+
+---STEP---
+
+Set up serenity.conf file:
+Create or update your serenity.conf file in the project root with the following configuration:
+\`\`\`
+webdriver {
+  driver = remote
+  remote.url = "https://hub.browserstack.com/wd/hub"
+}
+browserstack.user="${username}"
+browserstack.key="${accessKey}"
+\`\`\`
+
+---STEP---
+
+Run your Serenity tests:
+You can continue running your tests as you normally would. For example:
+
+Using Maven:
+\`\`\`bash
+mvn clean verify
+\`\`\`
+
+Using Gradle:
+\`\`\`bash
+gradle clean test
+\`\`\`
+`;
+
 /**
  * ---------- CSharp INSTRUCTIONS ----------
  */
@@ -488,6 +545,7 @@ export const SUPPORTED_CONFIGURATIONS: ConfigMapping = {
       cucumber: { instructions: javaInstructions },
       junit4: { instructions: javaInstructions },
       junit5: { instructions: javaInstructions },
+      serenity: { instructions: serenityInstructions },
     },
   },
   csharp: {
diff --git a/src/tools/sdk-utils/percy/constants.ts b/src/tools/sdk-utils/percy/constants.ts
index 63a3eae..39dea6a 100644
--- a/src/tools/sdk-utils/percy/constants.ts
+++ b/src/tools/sdk-utils/percy/constants.ts
@@ -159,6 +159,7 @@ export const PERCY_INSTRUCTIONS: PercyConfigMapping = {
       cucumber: { script_updates: javaSeleniumInstructions },
       junit4: { script_updates: javaSeleniumInstructions },
       junit5: { script_updates: javaSeleniumInstructions },
+      serenity: { script_updates: javaSeleniumInstructions },
     },
   },
   csharp: {
diff --git a/src/tools/sdk-utils/types.ts b/src/tools/sdk-utils/types.ts
index 7a82790..e30449e 100644
--- a/src/tools/sdk-utils/types.ts
+++ b/src/tools/sdk-utils/types.ts
@@ -28,6 +28,7 @@ export enum SDKSupportedTestingFrameworkEnum {
   junit4 = "junit4",
   junit5 = "junit5",
   testng = "testng",
+  serenity = "serenity",
   cypress = "cypress",
   nunit = "nunit",
   mstest = "mstest",

From 5cf0ea1808c6c27acaa6c678a16ecebc87b9cbd0 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Wed, 16 Jul 2025 17:29:41 +0530
Subject: [PATCH 57/64] feat: implement HTTPS agent support in ApiClient for
 proxy and CA certificate handling

---
 src/lib/apiClient.ts | 61 +++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 54 insertions(+), 7 deletions(-)

diff --git a/src/lib/apiClient.ts b/src/lib/apiClient.ts
index 555f51b..23cdeea 100644
--- a/src/lib/apiClient.ts
+++ b/src/lib/apiClient.ts
@@ -1,4 +1,9 @@
 import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
+import httpsProxyAgentPkg from "https-proxy-agent";
+const { HttpsProxyAgent } = httpsProxyAgentPkg;
+import * as https from "https";
+import * as fs from "fs";
+import config from "../config.js";
 
 type RequestOptions = {
   url: string;
@@ -54,15 +59,52 @@ class ApiResponse<T = any> {
   }
 }
 
+// Utility to create HTTPS agent if needed (proxy/CA)
+function getAxiosAgent(): AxiosRequestConfig["httpsAgent"] | undefined {
+  const proxyHost = config.browserstackLocalOptions.proxyHost;
+  const proxyPort = config.browserstackLocalOptions.proxyPort;
+  const caCertPath = config.browserstackLocalOptions.useCaCertificate;
+
+  // If both proxy host and port are defined
+  if (proxyHost && proxyPort) {
+    const proxyUrl = `http://${proxyHost}:${proxyPort}`;
+    if (caCertPath && fs.existsSync(caCertPath)) {
+      // Proxy + CA cert
+      const ca = fs.readFileSync(caCertPath);
+      return new HttpsProxyAgent({
+        host: proxyHost,
+        port: Number(proxyPort),
+        ca,
+        rejectUnauthorized: false, // Set to true if you want strict SSL
+      });
+    } else {
+      // Proxy only
+      return new HttpsProxyAgent(proxyUrl);
+    }
+  } else if (caCertPath && fs.existsSync(caCertPath)) {
+    // CA only
+    return new https.Agent({
+      ca: fs.readFileSync(caCertPath),
+      rejectUnauthorized: false, // Set to true for strict SSL
+    });
+  }
+  // Default agent (no proxy, no CA)
+  return undefined;
+}
+
 class ApiClient {
   private instance = axios.create();
 
+  private get axiosAgent() {
+    return getAxiosAgent();
+  }
+
   private async requestWrapper<T>(
-    fn: () => Promise<AxiosResponse<T>>,
+    fn: (agent: AxiosRequestConfig["httpsAgent"]) => Promise<AxiosResponse<T>>,
     raise_error: boolean = true,
   ): Promise<ApiResponse<T>> {
     try {
-      const res = await fn();
+      const res = await fn(this.axiosAgent);
       return new ApiResponse<T>(res);
     } catch (error: any) {
       if (error.response && !raise_error) {
@@ -79,7 +121,8 @@ class ApiClient {
     raise_error = true,
   }: RequestOptions): Promise<ApiResponse<T>> {
     return this.requestWrapper<T>(
-      () => this.instance.get<T>(url, { headers, params }),
+      (agent) =>
+        this.instance.get<T>(url, { headers, params, httpsAgent: agent }),
       raise_error,
     );
   }
@@ -91,7 +134,8 @@ class ApiClient {
     raise_error = true,
   }: RequestOptions): Promise<ApiResponse<T>> {
     return this.requestWrapper<T>(
-      () => this.instance.post<T>(url, body, { headers }),
+      (agent) =>
+        this.instance.post<T>(url, body, { headers, httpsAgent: agent }),
       raise_error,
     );
   }
@@ -103,7 +147,8 @@ class ApiClient {
     raise_error = true,
   }: RequestOptions): Promise<ApiResponse<T>> {
     return this.requestWrapper<T>(
-      () => this.instance.put<T>(url, body, { headers }),
+      (agent) =>
+        this.instance.put<T>(url, body, { headers, httpsAgent: agent }),
       raise_error,
     );
   }
@@ -115,7 +160,8 @@ class ApiClient {
     raise_error = true,
   }: RequestOptions): Promise<ApiResponse<T>> {
     return this.requestWrapper<T>(
-      () => this.instance.patch<T>(url, body, { headers }),
+      (agent) =>
+        this.instance.patch<T>(url, body, { headers, httpsAgent: agent }),
       raise_error,
     );
   }
@@ -127,7 +173,8 @@ class ApiClient {
     raise_error = true,
   }: RequestOptions): Promise<ApiResponse<T>> {
     return this.requestWrapper<T>(
-      () => this.instance.delete<T>(url, { headers, params }),
+      (agent) =>
+        this.instance.delete<T>(url, { headers, params, httpsAgent: agent }),
       raise_error,
     );
   }

From fe8e5e9dfd210e35ac5575119c6db4ffba16cdb7 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Fri, 18 Jul 2025 12:06:44 +0530
Subject: [PATCH 58/64] chore: update version to 1.2.0 in package.json and
 package-lock.json

---
 package-lock.json | 4 ++--
 package.json      | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 9a07d19..df1fa34 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "@browserstack/mcp-server",
-  "version": "1.1.9",
+  "version": "1.2.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "@browserstack/mcp-server",
-      "version": "1.1.9",
+      "version": "1.2.0",
       "license": "ISC",
       "dependencies": {
         "@modelcontextprotocol/sdk": "^1.11.4",
diff --git a/package.json b/package.json
index e1bb823..f171664 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@browserstack/mcp-server",
-  "version": "1.1.9",
+  "version": "1.2.0",
   "description": "BrowserStack's Official MCP Server",
   "main": "dist/index.js",
   "repository": {

From 8d50fad474a43ad9110c9df78a611ffb98a19308 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Fri, 18 Jul 2025 12:29:25 +0530
Subject: [PATCH 59/64] refactor: rename logger variable to currentLogger and
 update proxy implementation

---
 src/logger.ts | 31 ++++++++++++++++++++++---------
 1 file changed, 22 insertions(+), 9 deletions(-)

diff --git a/src/logger.ts b/src/logger.ts
index 3dc2eb2..6fb686c 100644
--- a/src/logger.ts
+++ b/src/logger.ts
@@ -1,9 +1,10 @@
 import { pino } from "pino";
 
-let logger: any;
+// 1. The actual logger instance, swapped out as needed
+let currentLogger: any;
 
 if (process.env.NODE_ENV === "development") {
-  logger = pino({
+  currentLogger = pino({
     level: "debug",
     transport: {
       targets: [
@@ -23,8 +24,8 @@ if (process.env.NODE_ENV === "development") {
     },
   });
 } else {
-  // NULL logger
-  logger = pino({
+  // Null logger (logs go to /dev/null or NUL)
+  currentLogger = pino({
     level: "info",
     transport: {
       target: "pino/file",
@@ -35,12 +36,24 @@ if (process.env.NODE_ENV === "development") {
   });
 }
 
-/**
- * Set a custom logger instance
- * @param customLogger - The logger instance to use
- */
+// 2. Proxy logger: always delegates to the currentLogger
+const logger: any = new Proxy(
+  {},
+  {
+    get(_target, prop) {
+      // Forward function calls to currentLogger
+      if (typeof currentLogger[prop] === "function") {
+        return (...args: any[]) => currentLogger[prop](...args);
+      }
+      // Forward property gets
+      return currentLogger[prop];
+    },
+  },
+);
+
+// 3. Setter to update the logger instance everywhere
 export function setLogger(customLogger: any): void {
-  logger = customLogger;
+  currentLogger = customLogger;
 }
 
 export default logger;

From 46cded10347bc1610787a3e448f8fd427685a4b4 Mon Sep 17 00:00:00 2001
From: tech-sushant <sushant.s@browserstack.com>
Date: Thu, 31 Jul 2025 12:26:34 +0530
Subject: [PATCH 60/64] refactor: rename and enhance tool for BrowserStack test
 automation setup

---
 src/tools/bstack-sdk.ts | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/tools/bstack-sdk.ts b/src/tools/bstack-sdk.ts
index 2482f1e..f2e597e 100644
--- a/src/tools/bstack-sdk.ts
+++ b/src/tools/bstack-sdk.ts
@@ -169,9 +169,9 @@ export default function addSDKTools(
 ) {
   const tools: Record<string, any> = {};
 
-  tools.runTestsOnBrowserStack = server.tool(
-    "runTestsOnBrowserStack",
-    "Use this tool to get instructions for running tests on BrowserStack and BrowserStack Percy. It sets up the BrowserStack SDK and runs your test cases on BrowserStack.",
+  tools.setupBrowserStackAutomateTests = server.tool(
+    "setupBrowserStackAutomateTests",
+    "Set up and run automated web-based tests on BrowserStack using the BrowserStack SDK. Use for functional or integration tests on BrowserStack, with optional Percy visual testing for supported frameworks. Example prompts: run this test on browserstack; run this test on browserstack with Percy; set up this project for browserstack with Percy. Integrate BrowserStack SDK into your project",
     {
       detectedBrowserAutomationFramework: z
         .nativeEnum(SDKSupportedBrowserAutomationFrameworkEnum)

From 16c180f082328869407b31d5d2ed72d020255160 Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 31 Jul 2025 14:31:57 +0530
Subject: [PATCH 61/64] Add global configuration support for session response
 formatting

---
 src/tools/live.ts | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/src/tools/live.ts b/src/tools/live.ts
index b1aa42e..44fa903 100644
--- a/src/tools/live.ts
+++ b/src/tools/live.ts
@@ -1,4 +1,3 @@
-// File: src/tools/live.ts
 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 import { z } from "zod";
 import logger from "../logger.js";
@@ -6,6 +5,7 @@ import { startBrowserSession } from "./live-utils/start-session.js";
 import { PlatformType } from "./live-utils/types.js";
 import { trackMCP } from "../lib/instrumentation.js";
 import { BrowserStackConfig } from "../lib/types.js";
+import globalConfig from "../config.js";
 
 // Define the schema shape
 const LiveArgsShape = {
@@ -97,13 +97,24 @@ async function runBrowserSession(rawArgs: any, config: BrowserStackConfig) {
       ? await launchDesktopSession(args, config)
       : await launchMobileSession(args, config);
 
-  return {
-    content: [
+  let response=[
       {
         type: "text" as const,
         text: `✅ Session started. If it didn't open automatically, visit:\n${launchUrl}`,
       },
-    ],
+    ];
+
+  if (globalConfig.REMOTE_MCP) {
+    response = [
+      {
+        type: "text" as const,
+        text: `✅ To start the session. Click on ${launchUrl}`,
+      },
+    ];
+  }
+
+  return {
+    content: response,
   };
 }
 

From 8ef34b6908e25ed66469e6c4283874af1a7d2f6f Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 31 Jul 2025 14:32:16 +0530
Subject: [PATCH 62/64] Refactor code formatting for consistency and
 readability

---
 src/index.ts          |  1 -
 src/server-factory.ts |  2 +-
 src/tools/live.ts     | 12 ++++++------
 3 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index 2c5d1a7..dec3584 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -48,6 +48,5 @@ process.on("exit", () => {
   logger.flush();
 });
 
-
 export { setLogger } from "./logger.js";
 export { BrowserStackMcpServer } from "./server-factory.js";
diff --git a/src/server-factory.ts b/src/server-factory.ts
index 228e3bb..7088720 100644
--- a/src/server-factory.ts
+++ b/src/server-factory.ts
@@ -80,7 +80,7 @@ export class BrowserStackMcpServer {
   public getTools(): Record<string, RegisteredTool> {
     return this.tools;
   }
-  
+
   public getTool(name: string): RegisteredTool | undefined {
     return this.tools[name];
   }
diff --git a/src/tools/live.ts b/src/tools/live.ts
index 44fa903..f6fd948 100644
--- a/src/tools/live.ts
+++ b/src/tools/live.ts
@@ -97,12 +97,12 @@ async function runBrowserSession(rawArgs: any, config: BrowserStackConfig) {
       ? await launchDesktopSession(args, config)
       : await launchMobileSession(args, config);
 
-  let response=[
-      {
-        type: "text" as const,
-        text: `✅ Session started. If it didn't open automatically, visit:\n${launchUrl}`,
-      },
-    ];
+  let response = [
+    {
+      type: "text" as const,
+      text: `✅ Session started. If it didn't open automatically, visit:\n${launchUrl}`,
+    },
+  ];
 
   if (globalConfig.REMOTE_MCP) {
     response = [

From 1d651e118795e24d5b7f7673d200ea6eecb6a098 Mon Sep 17 00:00:00 2001
From: Mujtaba <13besemmehdi@seecs.edu.pk>
Date: Thu, 31 Jul 2025 12:21:02 +0200
Subject: [PATCH 63/64] Update README.md

Updated the typo "agnets" to agents
---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index b1fe8ef..3185b18 100644
--- a/README.md
+++ b/README.md
@@ -115,7 +115,7 @@ Create and manage test cases, create test plans and trigger test runs using natu
 "update test results as passed for Login tests test run from My Demo Project"
 ```
 
-### 🧪 Access BrowserStack AI agnets 
+### 🧪 Access BrowserStack AI agents 
 
 Generate test cases from PRDs, convert manual tests to low-code automation, and auto-heal flaky scripts powered by BrowserStack’s AI agents, seamlessly integrated into your workflow.  Below are few example prompts to access Browserstack AI agents
 

From 224ab33dbba1d7bc06989b9e3aecd48da1d9671c Mon Sep 17 00:00:00 2001
From: Ruturaj-Browserstack <ruturaj.s@browserstack.com>
Date: Thu, 31 Jul 2025 16:34:18 +0530
Subject: [PATCH 64/64] chore: update version to 1.2.1 in package.json and
 package-lock.json

---
 package-lock.json | 4 ++--
 package.json      | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index df1fa34..7cd172b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "@browserstack/mcp-server",
-  "version": "1.2.0",
+  "version": "1.2.1",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "@browserstack/mcp-server",
-      "version": "1.2.0",
+      "version": "1.2.1",
       "license": "ISC",
       "dependencies": {
         "@modelcontextprotocol/sdk": "^1.11.4",
diff --git a/package.json b/package.json
index f171664..d5f007e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@browserstack/mcp-server",
-  "version": "1.2.0",
+  "version": "1.2.1",
   "description": "BrowserStack's Official MCP Server",
   "main": "dist/index.js",
   "repository": {
