Skip to main content
Glama

launch_app_device

Launch an app on a physical Apple device by specifying the device UDID and app bundle ID using XcodeBuildMCP. Simplifies app deployment and testing workflows on iOS, iPadOS, watchOS, tvOS, and visionOS devices.

Instructions

Launches an app on a physical Apple device (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro). Requires deviceId and bundleId.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
bundleIdYesBundle identifier of the app to launch (e.g., "com.example.MyApp")
deviceIdYesUDID of the device (obtained from list_devices)

Implementation Reference

  • The main handler function launch_app_deviceLogic that executes the tool logic: launches the app using xcrun devicectl, parses JSON output for process ID, and returns success/error response.
    export async function launch_app_deviceLogic(
      params: LaunchAppDeviceParams,
      executor: CommandExecutor,
    ): Promise<ToolResponse> {
      const { deviceId, bundleId } = params;
    
      log('info', `Launching app ${bundleId} on device ${deviceId}`);
    
      try {
        // Use JSON output to capture process ID
        const tempJsonPath = join(tmpdir(), `launch-${Date.now()}.json`);
    
        const result = await executor(
          [
            'xcrun',
            'devicectl',
            'device',
            'process',
            'launch',
            '--device',
            deviceId,
            '--json-output',
            tempJsonPath,
            '--terminate-existing',
            bundleId,
          ],
          'Launch app on device',
          true, // useShell
          undefined, // env
        );
    
        if (!result.success) {
          return {
            content: [
              {
                type: 'text',
                text: `Failed to launch app: ${result.error}`,
              },
            ],
            isError: true,
          };
        }
    
        // Parse JSON to extract process ID
        let processId: number | undefined;
        try {
          const jsonContent = await fs.readFile(tempJsonPath, 'utf8');
          const parsedData: unknown = JSON.parse(jsonContent);
    
          // Type guard to validate the parsed data structure
          if (
            parsedData &&
            typeof parsedData === 'object' &&
            'result' in parsedData &&
            parsedData.result &&
            typeof parsedData.result === 'object' &&
            'process' in parsedData.result &&
            parsedData.result.process &&
            typeof parsedData.result.process === 'object' &&
            'processIdentifier' in parsedData.result.process &&
            typeof parsedData.result.process.processIdentifier === 'number'
          ) {
            const launchData = parsedData as LaunchDataResponse;
            processId = launchData.result?.process?.processIdentifier;
          }
    
          // Clean up temp file
          await fs.unlink(tempJsonPath).catch(() => {});
        } catch (error) {
          log('warn', `Failed to parse launch JSON output: ${error}`);
        }
    
        let responseText = `✅ App launched successfully\n\n${result.output}`;
    
        if (processId) {
          responseText += `\n\nProcess ID: ${processId}`;
          responseText += `\n\nNext Steps:`;
          responseText += `\n1. Interact with your app on the device`;
          responseText += `\n2. Stop the app: stop_app_device({ deviceId: "${deviceId}", processId: ${processId} })`;
        }
    
        return {
          content: [
            {
              type: 'text',
              text: responseText,
            },
          ],
        };
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        log('error', `Error launching app on device: ${errorMessage}`);
        return {
          content: [
            {
              type: 'text',
              text: `Failed to launch app on device: ${errorMessage}`,
            },
          ],
          isError: true,
        };
      }
    }
  • Zod schema defining input parameters: deviceId (UDID) and bundleId.
    const launchAppDeviceSchema = z.object({
      deviceId: z.string().describe('UDID of the device (obtained from list_devices)'),
      bundleId: z
        .string()
        .describe('Bundle identifier of the app to launch (e.g., "com.example.MyApp")'),
    });
  • Tool registration: exports default object with name, description, schema (without deviceId for session), and handler created via createSessionAwareTool.
    export default {
      name: 'launch_app_device',
      description: 'Launches an app on a connected device.',
      schema: launchAppDeviceSchema.omit({ deviceId: true } as const).shape,
      handler: createSessionAwareTool<LaunchAppDeviceParams>({
        internalSchema: launchAppDeviceSchema as unknown as z.ZodType<LaunchAppDeviceParams>,
        logicFunction: launch_app_deviceLogic,
        getExecutor: getDefaultCommandExecutor,
        requirements: [{ allOf: ['deviceId'], message: 'deviceId is required' }],
      }),
    };
Behavior2/5

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

No annotations are provided, so the description carries the full burden. It states the action ('Launches') but doesn't disclose behavioral traits such as whether this requires specific permissions, if the app must be installed first, what happens if the app is already running, error conditions, or the expected response format. For a mutation tool with zero annotation coverage, this is a significant gap in transparency.

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

Conciseness5/5

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

The description is two sentences with zero waste: the first sentence states the purpose and scope, and the second specifies required parameters. It is appropriately sized and front-loaded, with every sentence earning its place by adding essential information.

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

Completeness3/5

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

Given the tool's moderate complexity (a mutation tool with 2 parameters), no annotations, and no output schema, the description is adequate but has clear gaps. It covers the purpose and parameters but lacks behavioral details (e.g., permissions, error handling) and output information. It meets minimum viability but doesn't fully compensate for the missing structured data.

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%, with both parameters ('deviceId' and 'bundleId') well-documented in the schema (e.g., 'deviceId' is described as 'UDID of the device (obtained from list_devices)'). The description mentions these parameters but adds minimal semantic value beyond what the schema provides, such as clarifying their necessity. Baseline 3 is appropriate when the schema does the heavy lifting.

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

Purpose5/5

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

The description clearly states the specific action ('Launches an app'), the target resource ('on a physical Apple device'), and enumerates the supported device types (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro). It distinguishes from siblings like 'launch_app_sim' (for simulators) and 'launch_mac_app' (for Mac apps) by specifying physical devices.

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

Usage Guidelines4/5

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

The description provides clear context for when to use this tool (launching apps on physical Apple devices) and mentions prerequisites ('Requires deviceId and bundleId'), with 'deviceId' explicitly linked to 'list_devices'. However, it doesn't explicitly state when NOT to use it (e.g., for simulators or Mac apps) or name alternative tools like 'launch_app_sim', though the context is implied by specifying physical devices.

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/getsentry/XcodeBuildMCP'

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