mcp-xcode
Version:
MCP server that wraps Xcode command-line tools for iOS/macOS development workflows
97 lines • 3.91 kB
JavaScript
import { validateDeviceId } from '../../utils/validation.js';
import { executeCommand, buildSimctlCommand } from '../../utils/command.js';
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
import { simulatorCache } from '../../state/simulator-cache.js';
export async function simctlBootTool(args) {
const { deviceId, waitForBoot = true } = args;
try {
// Validate inputs
validateDeviceId(deviceId);
// Build boot command
const bootCommand = buildSimctlCommand('boot', { deviceId });
console.error(`[simctl-boot] Executing: ${bootCommand}`);
// Execute boot command
const startTime = Date.now();
const bootResult = await executeCommand(bootCommand, {
timeout: 120000, // 2 minutes for boot
});
let bootStatus = {
success: bootResult.code === 0,
command: bootCommand,
output: bootResult.stdout,
error: bootResult.stderr,
exitCode: bootResult.code,
bootTime: Date.now() - startTime,
};
// If boot failed due to device already being booted, that's actually OK
if (!bootStatus.success &&
bootResult.stderr.includes('Unable to boot device in current state: Booted')) {
bootStatus = {
...bootStatus,
success: true,
error: 'Device was already booted',
};
}
// Wait for boot to complete if requested
if (waitForBoot && bootStatus.success) {
try {
await waitForDeviceBoot(deviceId);
bootStatus.bootTime = Date.now() - startTime;
}
catch (waitError) {
throw new McpError(ErrorCode.InternalError, `Device booted but failed to wait for completion: ${waitError}`);
}
}
// Record boot event in cache
if (bootStatus.success) {
simulatorCache.recordBootEvent(deviceId, true, bootStatus.bootTime);
}
// Format response
const responseText = JSON.stringify(bootStatus, null, 2);
return {
content: [
{
type: 'text',
text: responseText,
},
],
isError: !bootStatus.success,
};
}
catch (error) {
if (error instanceof McpError) {
throw error;
}
throw new McpError(ErrorCode.InternalError, `simctl-boot failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
async function waitForDeviceBoot(deviceId, timeoutMs = 120000) {
const startTime = Date.now();
const pollInterval = 2000; // Poll every 2 seconds
while (Date.now() - startTime < timeoutMs) {
try {
// Check device status
const statusCommand = `xcrun simctl list devices -j`;
const result = await executeCommand(statusCommand);
if (result.code === 0) {
const deviceList = JSON.parse(result.stdout);
// Find the device in the list
for (const devices of Object.values(deviceList.devices)) {
const deviceArray = devices;
const device = deviceArray.find(d => d.udid === deviceId);
if (device && device.state === 'Booted') {
return; // Device is fully booted
}
}
}
// Wait before next poll
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
catch (error) {
// Continue polling on errors
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
}
throw new Error(`Device ${deviceId} did not boot within ${timeoutMs}ms`);
}
//# sourceMappingURL=boot.js.map