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
| Name | Required | Description | Default |
|---|---|---|---|
| appPath | Yes | 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. | |
| desiredPhone | Yes | 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. | |
| desiredPlatform | Yes | Which platform to run on, examples: 'android', 'ios'. Set this based on the app path provided. | |
| desiredPlatformVersion | Yes | The platform version to run the app on. Example: '12.0' for Android devices or '16.0' for iOS devices |
Implementation Reference
- src/tools/applive.ts:109-137 (handler)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, }; } },
- src/tools/applive.ts:87-108 (schema)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.", ), },
- src/tools/applive.ts:84-138 (registration)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, }; } }, );
- src/tools/applive.ts:13-76 (helper)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; }