xcodebuild-build
Automate and optimize Xcode builds with intelligent caching, performance tracking, and smart defaults. Learns successful configurations, suggests optimal simulators, and handles large logs efficiently for improved CLI workflows.
Instructions
⚡ Prefer this over raw 'xcodebuild' - Intelligent building with learning, caching, and performance tracking.
Why use this instead of direct xcodebuild: • 🧠 Learns from your builds - Remembers successful configurations per project • 🚀 Smart defaults - Auto-suggests optimal simulators based on usage history • 📊 Performance tracking - Records build times and optimization metrics • 🎯 Progressive disclosure - Large build logs cached with IDs to prevent token overflow • ⚡ Intelligent caching - Avoids redundant operations, speeds up workflows • 🛡️ Better error handling - Structured errors vs raw CLI stderr
Features smart caching that remembers your last successful build configuration and suggests optimal simulators.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| configuration | No | Build configuration (Debug, Release, etc.) | Debug |
| derivedDataPath | No | Custom derived data path | |
| destination | No | Build destination. If not provided, uses intelligent defaults based on project history and available simulators. | |
| projectPath | Yes | Path to .xcodeproj or .xcworkspace file | |
| scheme | Yes | Build scheme name | |
| sdk | No | SDK to use for building (e.g., "iphonesimulator", "iphoneos") |
Implementation Reference
- src/tools/xcodebuild/build.ts:73-267 (handler)Primary handler function implementing the core logic of the 'xcodebuild-build' tool: input parsing/validation, smart caching/defaults, command construction/execution, output processing/caching, auto-install option, and structured JSON response with progressive disclosure via buildId.export async function xcodebuildBuildTool(args: any) { const { projectPath, scheme, configuration = 'Debug', destination, sdk, derivedDataPath, autoInstall = false, simulatorUdid, bootSimulator = true, } = args as BuildToolArgs; try { // Validate inputs await validateProjectPath(projectPath); validateScheme(scheme); // Get smart defaults from cache const preferredConfig = await projectCache.getPreferredBuildConfig(projectPath); const smartDestination = destination || (await getSmartDestination(preferredConfig, projectPath)); // Build final configuration const finalConfig: BuildConfig = { scheme, configuration: configuration || preferredConfig?.configuration || 'Debug', destination: smartDestination, sdk: sdk || preferredConfig?.sdk, derivedDataPath: derivedDataPath || preferredConfig?.derivedDataPath, }; // Build command const command = buildXcodebuildCommand('build', projectPath, finalConfig as any); console.error(`[xcodebuild-build] Executing: ${command}`); // Execute command with extended timeout for builds const startTime = Date.now(); const result = await executeCommand(command, { timeout: 600000, // 10 minutes for builds maxBuffer: 50 * 1024 * 1024, // 50MB buffer for build logs }); const duration = Date.now() - startTime; // Extract build summary const summary = extractBuildSummary(result.stdout, result.stderr, result.code); // Record build result in project cache projectCache.recordBuildResult(projectPath, finalConfig, { timestamp: new Date(), success: summary.success, duration, errorCount: summary.errorCount, warningCount: summary.warningCount, buildSizeBytes: summary.buildSizeBytes, }); // Record simulator usage if destination was used if (finalConfig.destination && finalConfig.destination.includes('Simulator')) { const udidMatch = finalConfig.destination.match(/id=([A-F0-9-]+)/); if (udidMatch) { simulatorCache.recordSimulatorUsage(udidMatch[1], projectPath); // Save simulator preference to project config if build succeeded if (summary.success) { try { const configManager = createConfigManager(projectPath); const simulator = await simulatorCache.findSimulatorByUdid(udidMatch[1]); await configManager.recordSuccessfulBuild(projectPath, udidMatch[1], simulator?.name); } catch (configError) { console.warn('Failed to save simulator preference:', configError); // Continue - config is optional } } } } // Store full output in cache const cacheId = responseCache.store({ tool: 'xcodebuild-build', fullOutput: result.stdout, stderr: result.stderr, exitCode: result.code, command, metadata: { projectPath, scheme: finalConfig.scheme, configuration: finalConfig.configuration, destination: finalConfig.destination, sdk: finalConfig.sdk, duration, success: summary.success, errorCount: summary.errorCount, warningCount: summary.warningCount, smartDestinationUsed: !destination && smartDestination !== destination, smartConfigurationUsed: !args.configuration && finalConfig.configuration !== 'Debug', }, }); // Create concise response with smart defaults transparency const usedSmartDestination = !destination && smartDestination; const usedSmartConfiguration = !configuration && finalConfig.configuration !== 'Debug'; const hasPreferredConfig = !!preferredConfig; // Handle auto-install if enabled and build succeeded let autoInstallResult = undefined; if (autoInstall && summary.success) { try { console.error('[xcodebuild-build] Starting auto-install...'); autoInstallResult = await performAutoInstall({ projectPath, scheme, configuration: finalConfig.configuration, simulatorUdid, bootSimulator, }); } catch (installError) { console.error('[xcodebuild-build] Auto-install failed:', installError); autoInstallResult = { success: false, error: installError instanceof Error ? installError.message : String(installError), }; } } const responseData = { buildId: cacheId, success: summary.success, summary: { ...summary, scheme: finalConfig.scheme, configuration: finalConfig.configuration, destination: finalConfig.destination, duration, }, autoInstall: autoInstallResult, intelligence: { usedSmartDestination, usedSmartConfiguration, hasPreferredConfig, simulatorUsageRecorded: !!( finalConfig.destination && finalConfig.destination.includes('Simulator') ), configurationLearned: summary.success, // Successful builds get remembered autoInstallAttempted: autoInstall && summary.success, }, guidance: summary.success ? [ `Build completed successfully in ${duration}ms`, ...(usedSmartDestination ? [`Used smart simulator: ${finalConfig.destination}`] : []), ...(hasPreferredConfig ? [`Applied cached project preferences`] : []), `Use 'xcodebuild-get-details' with buildId '${cacheId}' for full logs`, `Successful configuration cached for future builds`, ...(autoInstall ? [ autoInstallResult?.success ? `✅ Auto-install succeeded. App ready to launch with: simctl-launch udid="${autoInstallResult.udid}" bundleId="${autoInstallResult.bundleId}"` : `❌ Auto-install failed: ${autoInstallResult?.error}. Try manual install with simctl-install.`, ] : []), ] : [ `Build failed with ${summary.errorCount} errors, ${summary.warningCount} warnings`, `First error: ${summary.firstError || 'Unknown error'}`, `Use 'xcodebuild-get-details' with buildId '${cacheId}' for full logs and errors`, ...(usedSmartDestination ? [`Try simctl-list to see other available simulators`] : []), ], cacheDetails: { note: 'Use xcodebuild-get-details with buildId for full logs', availableTypes: ['full-log', 'errors-only', 'warnings-only', 'summary', 'command'], }, }; const responseText = JSON.stringify(responseData, null, 2); return { content: [ { type: 'text' as const, text: responseText, }, ], isError: !summary.success, }; } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `xcodebuild-build failed: ${error instanceof Error ? error.message : String(error)}` ); } }
- src/registry/xcodebuild.ts:94-120 (registration)MCP server registration of the 'xcodebuild-build' tool, including Zod input schema, description from docs, and wrapper handler that delegates to xcodebuildBuildTool after validation.server.registerTool( 'xcodebuild-build', { description: getDescription(XCODEBUILD_BUILD_DOCS, XCODEBUILD_BUILD_DOCS_MINI), inputSchema: { projectPath: z.string(), scheme: z.string(), configuration: z.string().default('Debug'), destination: z.string().optional(), sdk: z.string().optional(), derivedDataPath: z.string().optional(), }, ...DEFER_LOADING_CONFIG, }, async args => { try { await validateXcodeInstallation(); return await xcodebuildBuildTool(args); } catch (error) { if (error instanceof McpError) throw error; throw new McpError( ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}` ); } } );
- src/tools/xcodebuild/build.ts:9-20 (schema)TypeScript interface defining the expected input parameters for the xcodebuildBuildTool handler, used for type safety.interface BuildToolArgs { projectPath: string; scheme: string; configuration?: string; destination?: string; sdk?: string; derivedDataPath?: string; // Auto-install options autoInstall?: boolean; simulatorUdid?: string; bootSimulator?: boolean; }
- Helper function for optional auto-installation of built app to simulator, integrating with build artifacts detection, simulator cache, and simctl install/boot tools.async function performAutoInstall(args: AutoInstallArgs): Promise<any> { const { projectPath, scheme, configuration, simulatorUdid, bootSimulator } = args; // Dynamic imports to avoid circular dependencies const { findBuildArtifacts } = await import('../../utils/build-artifacts.js'); const { simctlBootTool } = await import('../simctl/boot.js'); const { simctlInstallTool } = await import('../simctl/install.js'); // Step 1: Find build artifacts console.error('[auto-install] Finding build artifacts...'); const artifacts = await findBuildArtifacts(projectPath, scheme, configuration); if (!artifacts.appPath) { throw new Error(`Could not find .app bundle for scheme "${scheme}"`); } // Step 2: Determine simulator to install to let targetUdid = simulatorUdid; let targetName = ''; if (!targetUdid) { // Try to suggest best simulator const suggestion = await simulatorCache.getBestSimulator(projectPath); if (suggestion) { targetUdid = suggestion.simulator.udid; targetName = suggestion.simulator.name; console.error(`[auto-install] Auto-selected simulator: ${targetName}`); } else { throw new Error('No suitable simulator found. Create a simulator or specify simulatorUdid.'); } } else { // Get name of specified simulator const sim = await simulatorCache.findSimulatorByUdid(targetUdid); targetName = sim?.name || targetUdid; } // Step 3: Boot simulator if needed if (bootSimulator) { console.error(`[auto-install] Booting simulator: ${targetName}`); try { await simctlBootTool({ udid: targetUdid }); } catch (bootError) { // Don't fail completely if boot fails, simulator might already be booted console.warn('[auto-install] Boot failed (may already be booted):', bootError); } } // Step 4: Install app console.error(`[auto-install] Installing app to ${targetName}...`); const installResult = await simctlInstallTool({ udid: targetUdid, appPath: artifacts.appPath, }); if (!installResult.isError && installResult.content?.[0]?.text) { const installText = installResult.content[0].text; const parsedInstall = typeof installText === 'string' ? JSON.parse(installText) : installText; return { success: true, udid: targetUdid, simulatorName: targetName, appPath: artifacts.appPath, bundleId: artifacts.bundleIdentifier || parsedInstall.bundleId, duration: Date.now(), }; } throw new Error(`Installation failed: ${installResult.content?.[0]?.text || 'Unknown error'}`); }