Skip to main content
Glama

install_app

Install an application on a simulator using the specified .app bundle path. Optionally target a specific device by UDID or name, defaulting to the booted device. Part of the MCP Xcode server for managing Apple platform development workflows.

Instructions

Install an app on the simulator

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
appPathYesPath to the .app bundle
deviceIdNoDevice UDID or name of the simulator (optional, uses booted device if not specified)

Implementation Reference

  • The MCPController implementation for the 'install_app' tool. Defines the tool name, description, input schema, getToolDefinition, and execute method that orchestrates installation.
    export class InstallAppController implements MCPController { // MCP Tool metadata readonly name = 'install_app'; readonly description = 'Install an app on the simulator'; constructor( private useCase: InstallAppUseCase ) {} get inputSchema() { return { type: 'object' as const, properties: { appPath: { type: 'string', description: 'Path to the .app bundle' }, simulatorId: { type: 'string', description: 'Device UDID or name of the simulator (optional, uses booted device if not specified)' } }, required: ['appPath'] }; } getToolDefinition() { return { name: this.name, description: this.description, inputSchema: this.inputSchema }; } async execute(args: unknown): Promise<{ content: Array<{ type: string; text: string }> }> { try { // Type guard for input if (!args || typeof args !== 'object') { throw new Error('Invalid input: expected an object'); } const input = args as InstallAppArgs; // Create domain request (validation happens here) const request = InstallRequest.create(input.appPath, input.simulatorId); // Execute use case const result = await this.useCase.execute(request); // Format response return { content: [{ type: 'text', text: this.formatResult(result) }] }; } catch (error: any) { // Handle validation and use case errors consistently const message = ErrorFormatter.format(error); return { content: [{ type: 'text', text: `❌ ${message}` }] }; } } private formatResult(result: InstallResult): string { const { outcome, diagnostics } = result; switch (outcome) { case InstallOutcome.Succeeded: return `✅ Successfully installed ${diagnostics.bundleId} on ${diagnostics.simulatorName} (${diagnostics.simulatorId?.toString()})`; case InstallOutcome.Failed: const { error } = diagnostics; if (error instanceof NoBootedSimulatorError) { return `❌ No booted simulator found. Please boot a simulator first or specify a simulator ID.`; } if (error instanceof SimulatorNotFoundError) { return `❌ Simulator not found: ${error.simulatorId}`; } if (error instanceof InstallCommandFailedError) { const message = ErrorFormatter.format(error); // Include simulator context if available if (diagnostics.simulatorName && diagnostics.simulatorId) { return `❌ ${diagnostics.simulatorName} (${diagnostics.simulatorId.toString()}) - ${message}`; } return `❌ ${message}`; } // Shouldn't happen but handle gracefully const fallbackMessage = error ? ErrorFormatter.format(error) : 'Install operation failed'; return `❌ ${fallbackMessage}`; } } }
  • src/index.ts:77-118 (registration)
    Registers the InstallAppController (via factory) into the MCP server's tools map, enabling the 'install_app' tool.
    private registerTools() { // Create instances of all tools const toolInstances = [ // Simulator management ListSimulatorsControllerFactory.create(), BootSimulatorControllerFactory.create(), ShutdownSimulatorControllerFactory.create(), // new ViewSimulatorScreenTool(), // Build and test // new BuildSwiftPackageTool(), // new RunSwiftPackageTool(), BuildXcodeControllerFactory.create(), InstallAppControllerFactory.create(), // new RunXcodeTool(), // new TestXcodeTool(), // new TestSwiftPackageTool(), // new CleanBuildTool(), // Archive and export // new ArchiveProjectTool(), // new ExportIPATool(), // Project info and schemes // new ListSchemesTool(), // new GetBuildSettingsTool(), // new GetProjectInfoTool(), // new ListTargetsTool(), // App management // new InstallAppTool(), // new UninstallAppTool(), // Device logs // new GetDeviceLogsTool(), // Advanced project management // new ManageDependenciesTool() ]; // Register each tool by its name for (const tool of toolInstances) { const definition = tool.getToolDefinition(); this.tools.set(definition.name, tool); } logger.info({ toolCount: this.tools.size }, 'Tools registered'); }
  • JSON Schema for input validation of the 'install_app' tool parameters.
    get inputSchema() { return { type: 'object' as const, properties: { appPath: { type: 'string', description: 'Path to the .app bundle' }, simulatorId: { type: 'string', description: 'Device UDID or name of the simulator (optional, uses booted device if not specified)' } }, required: ['appPath'] }; }
  • Use case containing the core business logic for the install_app tool, handling simulator selection, booting, and app installation via ports.
    export class InstallAppUseCase { constructor( private simulatorLocator: ISimulatorLocator, private simulatorControl: ISimulatorControl, private appInstaller: IAppInstaller, private logManager: ILogManager ) {} async execute(request: InstallRequest): Promise<InstallResult> { // Get app name from the AppPath value object const appName = request.appPath.name; // Find target simulator const simulator = request.simulatorId ? await this.simulatorLocator.findSimulator(request.simulatorId.toString()) : await this.simulatorLocator.findBootedSimulator(); if (!simulator) { this.logManager.saveDebugData('install-app-failed', { reason: 'simulator_not_found', requestedId: request.simulatorId?.toString() }, appName); const error = request.simulatorId ? new SimulatorNotFoundError(request.simulatorId) : new NoBootedSimulatorError(); return InstallResult.failed(error, request.appPath, request.simulatorId); } // Boot simulator if needed (only when specific ID provided) if (request.simulatorId) { if (simulator.state === SimulatorState.Shutdown) { try { await this.simulatorControl.boot(simulator.id); this.logManager.saveDebugData('simulator-auto-booted', { simulatorId: simulator.id, simulatorName: simulator.name }, appName); } catch (error: any) { this.logManager.saveDebugData('simulator-boot-failed', { simulatorId: simulator.id, error: error.message }, appName); const installError = new InstallCommandFailedError(error.message || error.toString()); return InstallResult.failed( installError, request.appPath, DeviceId.create(simulator.id), simulator.name ); } } } // Install the app try { await this.appInstaller.installApp( request.appPath.toString(), simulator.id ); this.logManager.saveDebugData('install-app-success', { simulator: simulator.name, simulatorId: simulator.id, app: appName }, appName); // Try to get bundle ID from app (could be enhanced later) const bundleId = appName; // For now, use app name as bundle ID return InstallResult.succeeded( bundleId, DeviceId.create(simulator.id), simulator.name, request.appPath ); } catch (error: any) { this.logManager.saveDebugData('install-app-error', { simulator: simulator.name, simulatorId: simulator.id, app: appName, error: error.message }, appName); const installError = new InstallCommandFailedError(error.message || error.toString()); return InstallResult.failed( installError, request.appPath, DeviceId.create(simulator.id), simulator.name ); } }

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/Stefan-Nitu/mcp-xcode'

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