install_app
Install an app bundle on an iOS simulator to test and run applications during development. Specify the app path and optionally target a specific simulator device.
Instructions
Install an app on the simulator
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| appPath | Yes | Path to the .app bundle | |
| simulatorId | No | Device UDID or name of the simulator (optional, uses booted device if not specified) |
Implementation Reference
- The execute method implements the core logic of the 'install_app' MCP tool: validates input, creates domain request, executes the use case, and returns formatted success/error response.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}` }] }; } }
- Defines the input JSON schema for the 'install_app' tool: requires appPath string, optional simulatorId.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'] }; }
- src/index.ts:77-117 (registration)Registers the InstallAppController (created via factory) to the MCP server's tools Map by name for handling tool calls.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');
- Core business logic for app installation: locates/boots simulator if needed, installs app via ports, handles all errors and logging.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 ); } }
- Dependency injection factory that wires all adapters, use case, controller, and decorators for the install_app tool.static create(): MCPController { // Create the shell executor that all adapters will use const execAsync = promisify(exec); const executor = new ShellCommandExecutorAdapter(execAsync); // Create infrastructure adapters const simulatorLocator = new SimulatorLocatorAdapter(executor); const simulatorControl = new SimulatorControlAdapter(executor); const appInstaller = new AppInstallerAdapter(executor); const logManager = new LogManagerInstance(); // Create the use case with all dependencies const useCase = new InstallAppUseCase( simulatorLocator, simulatorControl, appInstaller, logManager ); // Create the controller const controller = new InstallAppController(useCase); // Create dependency checker const dependencyChecker = new DependencyChecker(executor); // Wrap with dependency checking decorator const decoratedController = new DependencyCheckingDecorator( controller, ['xcrun'], // simctl is part of xcrun dependencyChecker ); return decoratedController; }