Skip to main content
Glama
browserstack

BrowserStack MCP server

Official

runAppLiveSession

Manually test and debug mobile apps on specific devices using BrowserStack's cloud infrastructure. Install .ipa or .apk files, check for crashes, and analyze performance issues.

Instructions

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.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
appPathYesThe path to the .ipa or .apk file to install on the device. Always ask the user for the app path, do not assume it.
desiredPhoneYesThe full name of the device to run the app on. Example: 'iPhone 12 Pro' or 'Samsung Galaxy S20' or 'Google Pixel 6'. Always ask the user for the device they want to use, do not assume it.
desiredPlatformYesWhich platform to run on, examples: 'android', 'ios'. Set this based on the app path provided.
desiredPlatformVersionYesThe platform version to run the app on. Example: '12.0' for Android devices or '16.0' for iOS devices

Implementation Reference

  • Inline async handler function that executes the tool: tracks the call, invokes startAppLiveSession, and returns success/error response.
    async (args) => {
      try {
        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,
          config,
        );
        return {
          content: [
            {
              type: "text",
              text: `Failed to start app live session: ${error instanceof Error ? error.message : String(error)}`,
              isError: true,
            },
          ],
          isError: true,
        };
      }
    },
  • Zod schema for tool inputs: desiredPhone, desiredPlatformVersion, desiredPlatform, appPath.
    {
      desiredPhone: z
        .string()
        .describe(
          "The full name of the device to run the app on. Example: 'iPhone 12 Pro' or 'Samsung Galaxy S20' or 'Google Pixel 6'. Always ask the user for the device they want to use, do not assume it. ",
        ),
      desiredPlatformVersion: z
        .string()
        .describe(
          "Specifies the platform version to run the app on. For example, use '12.0' for Android or '16.0' for iOS. If the user says 'latest', 'newest', or similar, normalize it to 'latest'. Likewise, convert terms like 'earliest' or 'oldest' to 'oldest'.",
        ),
      desiredPlatform: z
        .enum(["android", "ios"])
        .describe(
          "Which platform to run on, examples: 'android', 'ios'. Set this based on the app path provided.",
        ),
      appPath: z
        .string()
        .describe(
          "The path to the .ipa or .apk file to install on the device. Always ask the user for the app path, do not assume it.",
        ),
    },
  • Tool registration via server.tool call within addAppLiveTools, which is later invoked from server-factory.ts.
    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.",
      {
        desiredPhone: z
          .string()
          .describe(
            "The full name of the device to run the app on. Example: 'iPhone 12 Pro' or 'Samsung Galaxy S20' or 'Google Pixel 6'. Always ask the user for the device they want to use, do not assume it. ",
          ),
        desiredPlatformVersion: z
          .string()
          .describe(
            "Specifies the platform version to run the app on. For example, use '12.0' for Android or '16.0' for iOS. If the user says 'latest', 'newest', or similar, normalize it to 'latest'. Likewise, convert terms like 'earliest' or 'oldest' to 'oldest'.",
          ),
        desiredPlatform: z
          .enum(["android", "ios"])
          .describe(
            "Which platform to run on, examples: 'android', 'ios'. Set this based on the app path provided.",
          ),
        appPath: z
          .string()
          .describe(
            "The path to the .ipa or .apk file to install on the device. Always ask the user for the app path, do not assume it.",
          ),
      },
      async (args) => {
        try {
          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,
            config,
          );
          return {
            content: [
              {
                type: "text",
                text: `Failed to start app live session: ${error instanceof Error ? error.message : String(error)}`,
                isError: true,
              },
            ],
            isError: true,
          };
        }
      },
    );
  • Helper function performing input validation and delegating to startSession for launching the session.
    export async function startAppLiveSession(
      args: {
        desiredPlatform: string;
        desiredPlatformVersion: string;
        appPath?: string;
        desiredPhone: string;
        browserstackAppUrl?: string;
      },
      config: BrowserStackConfig,
    ): Promise<CallToolResult> {
      if (!args.desiredPlatform) {
        throw new Error("You must provide a desiredPlatform.");
      }
    
      if (!args.appPath && !args.browserstackAppUrl) {
        throw new Error("You must provide either appPath or browserstackAppUrl.");
      }
    
      if (!args.desiredPhone) {
        throw new Error("You must provide a desiredPhone.");
      }
    
      // Only validate app path if it's provided (not using browserstackAppUrl)
      if (args.appPath) {
        if (args.desiredPlatform === "android" && !args.appPath.endsWith(".apk")) {
          throw new Error("You must provide a valid Android app path.");
        }
    
        if (args.desiredPlatform === "ios" && !args.appPath.endsWith(".ipa")) {
          throw new Error("You must provide a valid iOS app path.");
        }
    
        // check if the app path exists && is readable
        try {
          if (!fs.existsSync(args.appPath)) {
            throw new Error("The app path does not exist.");
          }
          fs.accessSync(args.appPath, fs.constants.R_OK);
        } catch (error) {
          logger.error("The app path does not exist or is not readable: %s", error);
          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,
          browserstackAppUrl: args.browserstackAppUrl,
        },
        { config },
      );
    
      return {
        content: [
          {
            type: "text",
            text: `Successfully started a session. If a browser window did not open automatically, use the following URL to start the session: ${launchUrl}`,
          },
        ],
      };
    }
  • Core helper implementing session start: device selection/filtering, app upload, URL generation, and browser launch.
    export async function startSession(
      args: StartSessionArgs,
      options: StartSessionOptions,
    ): Promise<string> {
      const {
        appPath,
        desiredPlatform,
        desiredPhone,
        desiredPlatformVersion,
        browserstackAppUrl,
      } = args;
      const { config } = options;
    
      // 1) Fetch devices for APP_LIVE
      const data = await getDevicesAndBrowsers(BrowserStackProducts.APP_LIVE);
      const all: DeviceEntry[] = data.mobile.flatMap((grp: any) =>
        grp.devices.map((dev: any) => ({ ...dev, os: grp.os })),
      );
    
      // 2) Filter by OS
      const osMatches = all.filter((d) => d.os === desiredPlatform);
      if (!osMatches.length) {
        throw new Error(`No devices for OS "${desiredPlatform}"`);
      }
    
      // 3) Select by name
      const nameMatches = findDeviceByName(osMatches, desiredPhone);
    
      // 4) Resolve version
      const versions = [...new Set(nameMatches.map((d) => d.os_version))];
      const version = pickVersion(versions, desiredPlatformVersion);
    
      // 5) Final candidates for version
      const final = nameMatches.filter((d) => d.os_version === version);
      if (!final.length) {
        throw new Error(
          `No devices for version "${version}" on ${desiredPlatform}`,
        );
      }
      const selected = final[0];
      let note = "";
      if (
        version != desiredPlatformVersion &&
        desiredPlatformVersion !== "latest" &&
        desiredPlatformVersion !== "oldest"
      ) {
        note = `\n Note: The requested version "${desiredPlatformVersion}" is not available. Using "${version}" instead.`;
      }
    
      // 6) Upload app or use provided URL
      let app_url: string;
      if (browserstackAppUrl) {
        app_url = browserstackAppUrl;
        logger.info(`Using provided BrowserStack app URL: ${app_url}`);
      } else {
        if (!appPath) {
          throw new Error(
            "appPath is required when browserstackAppUrl is not provided",
          );
        }
        const authString = getBrowserStackAuth(config);
        const [username, password] = authString.split(":");
        const result = await uploadApp(appPath, username, password);
        app_url = result.app_url;
        logger.info(`App uploaded: ${app_url}`);
      }
    
      if (!app_url) {
        throw new Error("Failed to upload app. Please try again.");
      }
    
      // 7) Build URL & open
      const deviceParam = sanitizeUrlParam(
        selected.display_name.replace(/\s+/g, "+"),
      );
      const params = new URLSearchParams({
        os: desiredPlatform,
        os_version: version,
        app_hashed_id: app_url.split("bs://").pop() || "",
        scale_to_fit: "true",
        speed: "1",
        start: "true",
      });
      const launchUrl = `https://app-live.browserstack.com/dashboard#${params.toString()}&device=${deviceParam}`;
    
      if (!envConfig.REMOTE_MCP) {
        openBrowser(launchUrl);
      }
    
      return launchUrl + note;
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It mentions the tool uses BrowserStack's cloud infrastructure and can be used for debugging, but doesn't describe what happens during the session, whether it's interactive or automated, session duration limits, authentication requirements, or what output to expect. Significant behavioral details are missing for a cloud-based testing tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately concise with two sentences that efficiently convey the tool's purpose and primary use cases. It's front-loaded with the main purpose and follows with specific examples. No wasted words, though it could be slightly more structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 4 required parameters, no annotations, and no output schema, the description is insufficient. It doesn't explain what happens after the session starts, what kind of output or interface the user gets, session management, or error handling. The description leaves too many operational questions unanswered for a cloud-based testing tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already fully documents all 4 parameters. The description doesn't add any parameter-specific information beyond what's in the schema descriptions. The baseline score of 3 is appropriate when the schema does all the parameter documentation work.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'manually check their app on a particular mobile device using BrowserStack's cloud infrastructure' with specific use cases like debugging crashes and slow performance. It distinguishes from sibling tools by focusing on mobile app testing rather than browser sessions or test automation, though it doesn't explicitly name alternatives.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides some usage context ('when user wants to manually check their app') and implies this is for interactive debugging rather than automated testing. However, it doesn't explicitly state when to use this vs. sibling tools like 'runTestsOnBrowserStack' or 'runBrowserLiveSession', nor does it mention any prerequisites or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/browserstack/mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server