xcode_open_project
Open Xcode projects or workspaces from the command line to access development environments and initiate build processes.
Instructions
Open an Xcode project or workspace
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| xcodeproj | Yes | Absolute path to the .xcodeproj file (or .xcworkspace if available) - e.g., /path/to/project.xcodeproj |
Implementation Reference
- src/tools/ProjectTools.ts:129-167 (handler)Core handler implementation: validates project path, prefers .xcworkspace over .xcodeproj, ensures Xcode is running, executes JXA script to open project in Xcode.public static async openProject(projectPath: string): Promise<McpResult> { const validationError = PathValidator.validateProjectPath(projectPath); if (validationError) return validationError; // Check for workspace preference: if we're opening a .xcodeproj file, // check if there's a corresponding .xcworkspace file in the same directory let actualPath = projectPath; if (projectPath.endsWith('.xcodeproj')) { const { existsSync } = await import('fs'); const workspacePath = projectPath.replace(/\.xcodeproj$/, '.xcworkspace'); if (existsSync(workspacePath)) { actualPath = workspacePath; } } // Ensure Xcode is running before trying to open project const xcodeError = await this.ensureXcodeIsRunning(); if (xcodeError) return xcodeError; const script = ` const app = Application('Xcode'); app.open(${JSON.stringify(actualPath)}); 'Project opened successfully'; `; try { const result = await JXAExecutor.execute(script); // If we automatically chose a workspace over a project, indicate this in the response if (actualPath !== projectPath && actualPath.endsWith('.xcworkspace')) { return { content: [{ type: 'text', text: `Opened workspace instead of project: ${result}` }] }; } return { content: [{ type: 'text', text: result }] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: 'text', text: `Failed to open project: ${errorMessage}` }] }; } }
- src/XcodeServer.ts:362-380 (handler)MCP CallToolRequestSchema dispatch handler: validates parameters, calls ProjectTools.openProject, updates currentProjectPath on success.case 'xcode_open_project': if (!args.xcodeproj) { throw new McpError( ErrorCode.InvalidParams, this.preferredXcodeproj ? `Missing required parameter: xcodeproj (no preferred value was applied)\n\n💡 Expected: absolute path to .xcodeproj or .xcworkspace file` : `Missing required parameter: xcodeproj\n\n💡 Expected: absolute path to .xcodeproj or .xcworkspace file` ); } const result = await ProjectTools.openProject(args.xcodeproj as string); if (result && 'content' in result && result.content?.[0] && 'text' in result.content[0]) { const textContent = result.content[0]; if (textContent.type === 'text' && typeof textContent.text === 'string') { if (!textContent.text.includes('Error') && !textContent.text.includes('does not exist')) { this.currentProjectPath = args.xcodeproj as string; } } } return result;
- src/shared/toolDefinitions.ts:17-32 (schema)Tool schema definition: inputSchema for xcodeproj parameter, description, and conditional required fields based on preferences.{ name: 'xcode_open_project', description: 'Open an Xcode project or workspace', inputSchema: { type: 'object', properties: { xcodeproj: { type: 'string', description: preferredXcodeproj ? `Absolute path to the .xcodeproj file (or .xcworkspace if available) - defaults to ${preferredXcodeproj}` : 'Absolute path to the .xcodeproj file (or .xcworkspace if available) - e.g., /path/to/project.xcodeproj', }, }, required: preferredXcodeproj ? [] : ['xcodeproj'], }, },
- src/XcodeServer.ts:301-319 (registration)Registers tool list handler using getToolDefinitions which includes xcode_open_project schema.this.server.setRequestHandler(ListToolsRequestSchema, async () => { const toolOptions: { includeClean: boolean; preferredScheme?: string; preferredXcodeproj?: string; } = { includeClean: this.includeClean }; if (this.preferredScheme) toolOptions.preferredScheme = this.preferredScheme; if (this.preferredXcodeproj) toolOptions.preferredXcodeproj = this.preferredXcodeproj; const toolDefinitions = getToolDefinitions(toolOptions); return { tools: toolDefinitions.map(tool => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema })), }; });
- src/tools/ProjectTools.ts:9-127 (helper)Helper method called by openProject to ensure Xcode is running before opening the project, launches if necessary.public static async ensureXcodeIsRunning(): Promise<McpResult | null> { // First check if Xcode is already running const checkScript = ` (function() { try { const app = Application('Xcode'); if (app.running()) { return 'Xcode is already running'; } else { return 'Xcode is not running'; } } catch (error) { return 'Xcode is not running: ' + error.message; } })() `; try { const checkResult = await JXAExecutor.execute(checkScript); if (checkResult.includes('already running')) { return null; // All good, Xcode is running } } catch (error) { // Continue to launch Xcode } // Get the Xcode path from xcode-select let xcodePath: string; try { const { spawn } = await import('child_process'); const xcodeSelectResult = await new Promise<string>((resolve, reject) => { const process = spawn('xcode-select', ['-p']); let stdout = ''; let stderr = ''; process.stdout.on('data', (data) => { stdout += data.toString(); }); process.stderr.on('data', (data) => { stderr += data.toString(); }); process.on('close', (code) => { if (code === 0) { resolve(stdout.trim()); } else { reject(new Error(`xcode-select failed with code ${code}: ${stderr}`)); } }); }); if (!xcodeSelectResult || xcodeSelectResult.trim() === '') { return { content: [{ type: 'text', text: '❌ No Xcode installation found\n\n💡 To fix this:\n• Install Xcode from the Mac App Store\n• Run: sudo xcode-select -s /Applications/Xcode.app/Contents/Developer' }] }; } // Convert from Developer path to app path xcodePath = xcodeSelectResult.replace('/Contents/Developer', ''); } catch (error) { return { content: [{ type: 'text', text: `❌ Failed to determine Xcode path: ${error instanceof Error ? error.message : String(error)}\n\n💡 Ensure Xcode is properly installed and xcode-select is configured` }] }; } // Launch Xcode const launchScript = ` (function() { try { const app = Application(${JSON.stringify(xcodePath)}); app.launch(); // Wait for Xcode to start let attempts = 0; while (!app.running() && attempts < 30) { delay(1); attempts++; } if (app.running()) { return 'Xcode launched successfully from ' + ${JSON.stringify(xcodePath)}; } else { return 'Failed to launch Xcode - timed out after 30 seconds'; } } catch (error) { return 'Failed to launch Xcode: ' + error.message; } })() `; try { const launchResult = await JXAExecutor.execute(launchScript); if (launchResult.includes('launched successfully')) { return null; // Success } else { return { content: [{ type: 'text', text: `❌ ${launchResult}\n\n💡 Try:\n• Manually launching Xcode once\n• Checking Xcode installation\n• Ensuring sufficient system resources` }] }; } } catch (error) { return { content: [{ type: 'text', text: `❌ Failed to launch Xcode: ${error instanceof Error ? error.message : String(error)}` }] }; } }