Skip to main content
Glama

Xcode MCP Server

by r-huijts
index.tsโ€ข33.5 kB
import { z } from "zod"; import { promisify } from "util"; import { exec } from "child_process"; import * as fs from "fs/promises"; import * as path from "path"; import { XcodeServer } from "../../server.js"; import { ProjectNotFoundError, XcodeServerError, CommandExecutionError, PathAccessError } from "../../utils/errors.js"; const execAsync = promisify(exec); interface GemIssue { type: string; gem: string; fix: string; } interface CocoaPodsCheck { installed: boolean; version?: string; error?: unknown; } interface RubyCheck { rubyVersion?: string; issues?: GemIssue[]; error?: unknown; } interface PodInfo { name: string; version: string; isOutdated?: boolean; latestVersion?: string; } /** * Parse CocoaPods version from Podfile.lock */ async function getPodfileLockVersion(podfileLockPath: string): Promise<string | null> { try { const content = await fs.readFile(podfileLockPath, 'utf-8'); const match = content.match(/COCOAPODS: ([\d.]+)/); return match ? match[1] : null; } catch { return null; } } /** * Check if CocoaPods is installed and working properly */ async function checkCocoaPodsInstallation(): Promise<CocoaPodsCheck> { try { // Check if pod command exists await execAsync('which pod'); // Check CocoaPods version const { stdout } = await execAsync('pod --version'); return { installed: true, version: stdout.trim() }; } catch (error) { return { installed: false, error }; } } /** * Check for version compatibility and provide update instructions if needed */ async function checkVersionCompatibility(projectRoot: string): Promise<void> { const podfileLockPath = path.join(projectRoot, 'Podfile.lock'); const [installedVersion, lockVersion] = await Promise.all([ checkCocoaPodsInstallation().then(check => check.version), getPodfileLockVersion(podfileLockPath) ]); if (lockVersion && installedVersion) { // Split version strings into components and convert to numbers const installedParts = installedVersion.split('.').map(Number); const lockParts = lockVersion.split('.').map(Number); // Ensure we have at least 3 parts (major.minor.patch) const installedMajor = installedParts[0] || 0; const installedMinor = installedParts[1] || 0; const installedPatch = installedParts[2] || 0; const lockMajor = lockParts[0] || 0; const lockMinor = lockParts[1] || 0; const lockPatch = lockParts[2] || 0; // Compare versions: first by major, then minor, then patch if (installedMajor < lockMajor || (installedMajor === lockMajor && installedMinor < lockMinor) || (installedMajor === lockMajor && installedMinor === lockMinor && installedPatch < lockPatch)) { throw new XcodeServerError( `CocoaPods version mismatch detected:\n` + `- Installed version: ${installedVersion}\n` + `- Version used in Podfile.lock: ${lockVersion}\n\n` + `To resolve this, you can either:\n` + `1. Update CocoaPods to match or exceed version ${lockVersion}:\n` + ` sudo gem install cocoapods\n\n` + `2. Or regenerate Podfile.lock with your current version:\n` + ` rm Podfile.lock\n` + ` pod install\n\n` + `Option 1 (updating CocoaPods) is recommended.` ); } } } /** * Check Ruby environment */ async function checkRubyEnvironment(): Promise<RubyCheck> { try { const { stdout: rubyVersion } = await execAsync('ruby --version'); const { stdout: gemList } = await execAsync('gem list'); const issues: GemIssue[] = []; // Check for common gem issues if (gemList.includes('ffi') && gemList.includes('extensions are not built')) { issues.push({ type: 'gem_issue', gem: 'ffi', fix: 'Run: sudo gem pristine ffi --version 1.15.5' }); } // Check for activesupport issues which are common with CocoaPods if (gemList.includes('activesupport') && gemList.includes('incompatible')) { issues.push({ type: 'gem_issue', gem: 'activesupport', fix: 'Run: sudo gem uninstall activesupport && sudo gem install activesupport -v 6.1.7.6' }); } // Check for common image_optim issues if (gemList.includes('image_optim') && gemList.includes('cannot load')) { issues.push({ type: 'gem_issue', gem: 'image_optim', fix: 'Run: brew install optipng jpegoptim' }); } // Check for common xcodeproj issues if (gemList.includes('xcodeproj') && gemList.includes('incompatible')) { issues.push({ type: 'gem_issue', gem: 'xcodeproj', fix: 'Run: sudo gem uninstall xcodeproj && sudo gem install xcodeproj' }); } // Check for common json issues if (gemList.includes('json') && gemList.includes('incompatible')) { issues.push({ type: 'gem_issue', gem: 'json', fix: 'Run: sudo gem uninstall json && sudo gem install json' }); } // Check for architecture issues on Apple Silicon if (rubyVersion.includes('arm64') && gemList.includes('incompatible architecture')) { issues.push({ type: 'architecture_issue', gem: 'multiple', fix: 'You may need to use Rosetta for Ruby: arch -x86_64 pod install' }); } return { rubyVersion: rubyVersion.trim(), issues }; } catch (error) { return { error }; } } /** * Extract installed pods from Podfile.lock content */ async function extractInstalledPods(podfileLockPath: string): Promise<PodInfo[]> { try { const lockContent = await fs.readFile(podfileLockPath, 'utf-8'); const podsSection = lockContent.match(/PODS:\s*\n([\s\S]+?)(?:\n\n|$)/); if (!podsSection || !podsSection[1]) { return []; } const podLines = podsSection[1].split('\n'); const installedPods: PodInfo[] = []; for (const line of podLines) { // Match both standard pod entries and pods with dependencies // Format: " - PodName (1.2.3)" or " - PodName/SubSpec (1.2.3)" const match = line.match(/\s*-\s+([^(/]+(?:\/[^(]*)?)\s+\(([^)]+)\)/); if (match) { installedPods.push({ name: match[1].trim(), version: match[2].trim() }); } } return installedPods; } catch (error) { console.error("Error extracting pods from Podfile.lock:", error); return []; } } /** * Check for outdated pods in the project */ async function checkOutdatedPods(projectDir: string): Promise<PodInfo[]> { try { // Get the list of installed pods first const podfileLockPath = path.join(projectDir, 'Podfile.lock'); const installedPods = await extractInstalledPods(podfileLockPath); // Run pod outdated to get the list of outdated pods const { stdout } = await execAsync('pod outdated --no-repo-update', { cwd: projectDir }); // Parse the output to identify outdated pods and their latest versions const outdatedPods = new Map<string, string>(); const outdatedPattern = /- (.*?) \((.*?) -> (.*?)\)/g; let match; while ((match = outdatedPattern.exec(stdout)) !== null) { const [, podName, , latestVersion] = match; // Skip currentVersion as it's not used outdatedPods.set(podName.trim(), latestVersion.trim()); } // Update the installed pods list with outdated information return installedPods.map(pod => { const latestVersion = outdatedPods.get(pod.name); if (latestVersion) { return { ...pod, isOutdated: true, latestVersion }; } return pod; }); } catch (error) { console.error("Error checking for outdated pods:", error); return []; } } /** * Get installation instructions based on the current system state */ function getInstallationInstructions(cocoaPodsCheck: CocoaPodsCheck, rubyCheck: RubyCheck): string { const instructions: string[] = []; if (!cocoaPodsCheck.installed) { instructions.push( "CocoaPods is not installed. To install it:", "1. First, ensure you have Ruby installed (macOS comes with it pre-installed)", "2. Run: sudo gem install cocoapods", "3. After installation, run: pod setup" ); } if (rubyCheck.issues?.length) { instructions.push( "\nRuby gem issues detected:", ...rubyCheck.issues.map(issue => `- ${issue.fix}`) ); } if (!instructions.length) { return "CocoaPods appears to be properly installed."; } return instructions.join('\n'); } /** * Check if a specific pod exists in the Podfile */ async function checkPodExists(podfilePath: string, podName: string): Promise<boolean> { try { const content = await fs.readFile(podfilePath, 'utf-8'); const podRegex = new RegExp(`pod\\s+['"](${podName})['"]`, 'i'); return podRegex.test(content); } catch { return false; } } /** * Register CocoaPods related tools */ export function registerCocoaPodsTools(server: XcodeServer) { // Register "pod_install" server.server.tool( "pod_install", "Runs 'pod install' in the active project directory to install CocoaPods dependencies.", { repoUpdate: z.boolean().optional().describe("Whether to update the spec repositories before installation. Defaults to false."), cleanInstall: z.boolean().optional().describe("Ignore the contents of the project cache and force a full pod installation."), verbose: z.boolean().optional().describe("Show more debugging information during installation.") }, async ({ repoUpdate = false, cleanInstall = false, verbose = false }) => { try { if (!server.activeProject) throw new ProjectNotFoundError(); // Check CocoaPods installation first const cocoaPodsCheck = await checkCocoaPodsInstallation(); const rubyCheck = await checkRubyEnvironment(); if (!cocoaPodsCheck.installed || (rubyCheck.issues && rubyCheck.issues.length > 0)) { const instructions = getInstallationInstructions(cocoaPodsCheck, rubyCheck); throw new XcodeServerError( "CocoaPods installation issues detected. Please fix the following issues before proceeding:\n\n" + instructions ); } // Use the active directory rather than deriving from project path const activeDirectory = server.directoryState.getActiveDirectory(); // Validate this is properly within project boundaries server.pathManager.validatePathForReading(activeDirectory); // Check version compatibility before proceeding await checkVersionCompatibility(activeDirectory); const podfilePath = path.join(activeDirectory, 'Podfile'); try { // Check if Podfile exists await fs.access(podfilePath); } catch { throw new XcodeServerError("No Podfile found in the project directory. This project doesn't use CocoaPods."); } try { // Set UTF-8 encoding and run pod install const env = { ...process.env, LANG: 'en_US.UTF-8', LC_ALL: 'en_US.UTF-8' }; // Build the command with appropriate flags let cmd = 'pod install'; if (repoUpdate) { cmd += ' --repo-update'; } else { cmd += ' --no-repo-update'; } if (cleanInstall) { cmd += ' --clean-install'; } if (verbose) { cmd += ' --verbose'; } const { stdout, stderr } = await execAsync(cmd, { cwd: activeDirectory, env }); return { content: [{ type: "text", text: `CocoaPods installation completed successfully:\n\n${stdout}\n${stderr ? 'Console output:\n' + stderr : ''}` }] }; } catch (error) { let stderr = ''; if (error instanceof Error && 'stderr' in error) { stderr = (error as any).stderr; } // Check for specific error patterns and provide helpful messages if (stderr.includes('Podfile syntax error')) { throw new XcodeServerError( "Podfile syntax error detected. Please check your Podfile format.\n" + "Common issues:\n" + "1. Missing 'end' keyword\n" + "2. Incorrect target name\n" + "3. Invalid pod specifications\n\n" + stderr ); } if (stderr.includes('unicode_normalize')) { throw new XcodeServerError( "CocoaPods UTF-8 encoding issue detected. Please ensure your terminal is configured for UTF-8:\n" + "Add to your ~/.zshrc or ~/.bash_profile:\n" + "export LANG=en_US.UTF-8\n" + "export LC_ALL=en_US.UTF-8" ); } if (stderr.includes('CDN: trunk URL couldn\'t be downloaded')) { throw new XcodeServerError( "CocoaPods CDN connection issue. Try one of the following:\n" + "1. Check your internet connection\n" + "2. Run 'pod repo update'\n" + "3. Try again with '--repo-update' flag\n" + "4. If behind a proxy, configure CocoaPods to use your proxy settings" ); } if (stderr.includes('pod: command not found')) { throw new XcodeServerError( "CocoaPods is not installed or not in your PATH. To install CocoaPods:\n" + "1. Run: sudo gem install cocoapods\n" + "2. After installation, run: pod setup" ); } if (stderr.includes('incompatible architecture')) { throw new XcodeServerError( "Architecture compatibility issue detected. This often happens with M1/M2 Macs.\n" + "Try one of the following:\n" + "1. Install ffi gem: sudo gem install ffi\n" + "2. Install cocoapods with arch flag: sudo arch -x86_64 gem install cocoapods\n" + "3. Run pod install with arch flag: arch -x86_64 pod install" ); } if (stderr.includes('Could not find compatible versions for pod')) { throw new XcodeServerError( "Version compatibility issue detected.\n" + "Common solutions:\n" + "1. Update your CocoaPods spec repos: pod repo update\n" + "2. Check the version constraints in your Podfile\n" + "3. Try using a more specific version constraint\n" + "4. Check if the pod has been updated recently\n\n" + stderr ); } throw new CommandExecutionError( 'pod install', stderr || (error instanceof Error ? error.message : String(error)) ); } } catch (error) { if (error instanceof PathAccessError) { throw new Error(`Access denied: ${error.message}`); } else { throw error; } } } ); // Register "pod_update" server.server.tool( "pod_update", "Runs 'pod update' in the active project directory to update CocoaPods dependencies.", { pods: z.array(z.string()).optional().describe("Optional list of specific pods to update. If not provided, updates all pods."), repoUpdate: z.boolean().optional().describe("Whether to update the spec repositories before updating pods. Defaults to true."), excludePods: z.array(z.string()).optional().describe("Optional list of pods to exclude during update."), cleanInstall: z.boolean().optional().describe("Ignore the contents of the project cache and force a full pod installation."), sources: z.array(z.string()).optional().describe("Optional list of sources from which to update dependent pods.") }, async ({ pods, repoUpdate = true, excludePods, cleanInstall, sources }) => { try { if (!server.activeProject) throw new ProjectNotFoundError(); // Use the active directory rather than deriving from project path const activeDirectory = server.directoryState.getActiveDirectory(); // Validate this is properly within project boundaries server.pathManager.validatePathForReading(activeDirectory); // Check version compatibility before proceeding await checkVersionCompatibility(activeDirectory); const podfilePath = path.join(activeDirectory, 'Podfile'); try { // Check if Podfile exists await fs.access(podfilePath); } catch { throw new XcodeServerError("No Podfile found in the project directory. This project doesn't use CocoaPods."); } // If specific pods are provided, verify they exist in the Podfile if (pods && pods.length > 0) { const nonExistentPods: string[] = []; for (const podName of pods) { const exists = await checkPodExists(podfilePath, podName); if (!exists) { nonExistentPods.push(podName); } } if (nonExistentPods.length > 0) { throw new XcodeServerError( `The following pods were not found in your Podfile: ${nonExistentPods.join(', ')}\n` + `Please check the pod names and try again.` ); } } try { // Set UTF-8 encoding const env = { ...process.env, LANG: 'en_US.UTF-8', LC_ALL: 'en_US.UTF-8' }; // Build the command let cmd = 'pod update'; // Add specific pods to update if (pods && pods.length > 0) { cmd += ' ' + pods.map(pod => pod.trim()).join(' '); } // Add repo update flag if (!repoUpdate) { cmd += ' --no-repo-update'; } // Add excluded pods if (excludePods && excludePods.length > 0) { cmd += ` --exclude-pods=${excludePods.join(',')}`; } // Add clean install flag if (cleanInstall) { cmd += ' --clean-install'; } // Add sources if (sources && sources.length > 0) { cmd += ` --sources=${sources.join(',')}`; } const { stdout, stderr } = await execAsync(cmd, { cwd: activeDirectory, env }); return { content: [{ type: "text", text: `CocoaPods update completed successfully:\n\n${stdout}\n${stderr ? 'Console output:\n' + stderr : ''}` }] }; } catch (error) { let stderr = ''; if (error instanceof Error && 'stderr' in error) { stderr = (error as any).stderr; } // Handle specific error cases if (stderr.includes('The dependency') && stderr.includes('is not used in any concrete target')) { throw new XcodeServerError( "Error: Some dependencies you're trying to update aren't used in any target.\n" + "Try updating specific pods that are actually used in your project." ); } if (stderr.includes('pod: command not found')) { throw new XcodeServerError( "CocoaPods is not installed or not in your PATH. To install CocoaPods:\n" + "1. Run: sudo gem install cocoapods\n" + "2. After installation, run: pod setup" ); } if (stderr.includes('Could not find compatible versions for pod')) { throw new XcodeServerError( "Version compatibility issue detected.\n" + "Common solutions:\n" + "1. Update your CocoaPods spec repos: pod repo update\n" + "2. Check the version constraints in your Podfile\n" + "3. Try using a more specific version constraint\n" + "4. Check if the pod has been updated recently\n\n" + stderr ); } if (stderr.includes('CDN: trunk URL couldn\'t be downloaded')) { throw new XcodeServerError( "CocoaPods CDN connection issue. Try one of the following:\n" + "1. Check your internet connection\n" + "2. Run 'pod repo update'\n" + "3. Try again with '--repo-update' flag\n" + "4. If behind a proxy, configure CocoaPods to use your proxy settings" ); } throw new CommandExecutionError( 'pod update', stderr || (error instanceof Error ? error.message : String(error)) ); } } catch (error) { if (error instanceof PathAccessError) { throw new Error(`Access denied: ${error.message}`); } else { throw error; } } } ); // Register "pod_outdated" server.server.tool( "pod_outdated", "Shows outdated pods in the current project and their available updates.", { ignorePrerelease: z.boolean().optional().describe("Don't consider prerelease versions to be updates. Defaults to true."), repoUpdate: z.boolean().optional().describe("Whether to update the spec repositories before checking for outdated pods. Defaults to false.") }, async ({ ignorePrerelease = true, repoUpdate = false }) => { try { if (!server.activeProject) throw new ProjectNotFoundError(); // Use the active directory const activeDirectory = server.directoryState.getActiveDirectory(); // Validate the path server.pathManager.validatePathForReading(activeDirectory); const podfilePath = path.join(activeDirectory, 'Podfile'); const podfileLockPath = path.join(activeDirectory, 'Podfile.lock'); try { // Check if Podfile and Podfile.lock exist await fs.access(podfilePath); await fs.access(podfileLockPath); } catch { throw new XcodeServerError("Podfile or Podfile.lock not found. Make sure you have a CocoaPods project and have run 'pod install' first."); } try { // Build command with options let cmd = 'pod outdated'; if (ignorePrerelease) { cmd += ' --ignore-prerelease'; } if (!repoUpdate) { cmd += ' --no-repo-update'; } const { stdout, stderr } = await execAsync(cmd, { cwd: activeDirectory }); // Parse the outdated pods information const outdatedPods: PodInfo[] = []; const outdatedPattern = /- (.*?) \((.*?) -> (.*?)\)/g; let match; while ((match = outdatedPattern.exec(stdout)) !== null) { const [, podName, currentVersion, latestVersion] = match; outdatedPods.push({ name: podName.trim(), version: currentVersion.trim(), isOutdated: true, latestVersion: latestVersion.trim() }); } // Get the list of installed pods for comparison const installedPods = await extractInstalledPods(podfileLockPath); // Generate the response with a structured format const summary = outdatedPods.length > 0 ? `Found ${outdatedPods.length} outdated pods.` : 'All pods are up to date!'; return { content: [{ type: "text", text: `${summary}\n\n` + `${stdout}\n\n` + `Installed Pods: ${installedPods.length}\n` + `Outdated Pods: ${outdatedPods.length}\n\n` + `${stderr ? 'Console output:\n' + stderr : ''}` }] }; } catch (error) { let stderr = ''; if (error instanceof Error && 'stderr' in error) { stderr = (error as any).stderr; } throw new CommandExecutionError( 'pod outdated', stderr || (error instanceof Error ? error.message : String(error)) ); } } catch (error) { if (error instanceof PathAccessError) { throw new Error(`Access denied: ${error.message}`); } else { throw error; } } } ); // Register "pod_repo_update" server.server.tool( "pod_repo_update", "Updates the local clone of the CocoaPods spec repositories.", { verbose: z.boolean().optional().describe("Show more detailed output."), silent: z.boolean().optional().describe("Show nothing during update.") }, async ({ verbose = false, silent = false }) => { try { // Build command with options let cmd = 'pod repo update'; if (verbose) { cmd += ' --verbose'; } if (silent) { cmd += ' --silent'; } const { stdout, stderr } = await execAsync(cmd); return { content: [{ type: "text", text: `CocoaPods repository update completed successfully:\n\n${stdout}\n${stderr ? 'Console output:\n' + stderr : ''}` }] }; } catch (error) { let stderr = ''; if (error instanceof Error && 'stderr' in error) { stderr = (error as any).stderr; } throw new CommandExecutionError( 'pod repo update', stderr || (error instanceof Error ? error.message : String(error)) ); } } ); // Register "pod_deintegrate" server.server.tool( "pod_deintegrate", "Deintegrate CocoaPods from the active project, removing all traces of CocoaPods.", {}, async () => { try { if (!server.activeProject) throw new ProjectNotFoundError(); // Use the active directory const activeDirectory = server.directoryState.getActiveDirectory(); // Validate path server.pathManager.validatePathForWriting(activeDirectory); const podfilePath = path.join(activeDirectory, 'Podfile'); try { // Check if Podfile exists await fs.access(podfilePath); } catch { throw new XcodeServerError("No Podfile found in the project directory. This project doesn't use CocoaPods."); } try { const { stdout, stderr } = await execAsync('pod deintegrate', { cwd: activeDirectory }); return { content: [{ type: "text", text: `CocoaPods has been deintegrated from the project:\n\n${stdout}\n${stderr ? 'Console output:\n' + stderr : ''}` }] }; } catch (error) { let stderr = ''; if (error instanceof Error && 'stderr' in error) { stderr = (error as any).stderr; } throw new CommandExecutionError( 'pod deintegrate', stderr || (error instanceof Error ? error.message : String(error)) ); } } catch (error) { if (error instanceof PathAccessError) { throw new Error(`Access denied: ${error.message}`); } else { throw error; } } } ); // Register "check_cocoapods" server.server.tool( "check_cocoapods", "Checks if the active project uses CocoaPods and returns setup information.", { includeOutdated: z.boolean().optional().describe("Check for outdated pods and include update information. Default: false") }, async ({ includeOutdated = false }) => { try { if (!server.activeProject) throw new ProjectNotFoundError(); // Use the active directory for checking const activeDirectory = server.directoryState.getActiveDirectory(); // Validate this path is allowed server.pathManager.validatePathForReading(activeDirectory); const podfilePath = path.join(activeDirectory, 'Podfile'); const podfileLockPath = path.join(activeDirectory, 'Podfile.lock'); // Check if Podfile and Podfile.lock exist const [podfileExists, podfileLockExists] = await Promise.all([ fs.access(podfilePath).then(() => true).catch(() => false), fs.access(podfileLockPath).then(() => true).catch(() => false) ]); const cocoaPodsCheck = await checkCocoaPodsInstallation(); const rubyCheck = await checkRubyEnvironment(); // If it has a Podfile, it's using CocoaPods const usesCocoaPods = podfileExists; // Get Podfile content if it exists let podfileContent = null; if (podfileExists) { try { podfileContent = await fs.readFile(podfilePath, 'utf-8'); } catch { // Ignore errors reading the file } } // Get installed pods and check for outdated ones if requested let installedPods: PodInfo[] = []; if (podfileLockExists) { installedPods = await extractInstalledPods(podfileLockPath); if (includeOutdated && usesCocoaPods) { try { const outdatedInfo = await checkOutdatedPods(activeDirectory); // Merge the outdated info into the installed pods installedPods = installedPods.map(pod => { const outdatedPod = outdatedInfo.find(p => p.name === pod.name && p.isOutdated); if (outdatedPod) { return { ...pod, isOutdated: true, latestVersion: outdatedPod.latestVersion }; } return pod; }); } catch (error) { console.error("Error checking for outdated pods:", error); } } } // Get info about CocoaPods repositories let repoInfo = null; if (cocoaPodsCheck.installed) { try { const { stdout } = await execAsync('pod repo list'); repoInfo = stdout.trim(); } catch { // Ignore repo listing errors } } return { content: [{ type: "text", text: JSON.stringify({ usesCocoaPods, podfileExists, podfileLockExists, installedPods, podCount: installedPods.length, outdatedCount: installedPods.filter(pod => pod.isOutdated).length, cocoaPodsInstalled: cocoaPodsCheck.installed, cocoaPodsVersion: cocoaPodsCheck.version, podfileSummary: podfileContent ? `${podfileContent.split('\n').slice(0, 10).join('\n')}${podfileContent.split('\n').length > 10 ? '\n...(truncated)' : ''}` : null, installationInstructions: getInstallationInstructions(cocoaPodsCheck, rubyCheck), repoInfo }, null, 2) }] }; } catch (error) { if (error instanceof PathAccessError) { throw new Error(`Access denied: ${error.message}`); } else { throw error; } } } ); // Register "pod_init" server.server.tool( "pod_init", "Generate a Podfile for the current project directory.", {}, async () => { try { if (!server.activeProject) throw new ProjectNotFoundError(); // Use the active directory const activeDirectory = server.directoryState.getActiveDirectory(); // Validate path server.pathManager.validatePathForWriting(activeDirectory); const podfilePath = path.join(activeDirectory, 'Podfile'); // Check if Podfile already exists try { await fs.access(podfilePath); throw new XcodeServerError("A Podfile already exists in this directory. If you want to recreate it, delete the existing one first."); } catch (error) { // Error means file doesn't exist, which is what we want if (error instanceof XcodeServerError) { throw error; } } try { const { stdout, stderr } = await execAsync('pod init', { cwd: activeDirectory }); // Read the generated Podfile let podfileContent = ""; try { podfileContent = await fs.readFile(podfilePath, 'utf-8'); } catch { // Ignore errors reading the file } return { content: [{ type: "text", text: `Podfile has been created successfully:\n\n${stdout}\n${stderr ? 'Console output:\n' + stderr : ''}\n\nGenerated Podfile content:\n\n${podfileContent}` }] }; } catch (error) { let stderr = ''; if (error instanceof Error && 'stderr' in error) { stderr = (error as any).stderr; } throw new CommandExecutionError( 'pod init', stderr || (error instanceof Error ? error.message : String(error)) ); } } catch (error) { if (error instanceof PathAccessError) { throw new Error(`Access denied: ${error.message}`); } else { throw error; } } } ); }

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/r-huijts/xcode-mcp-server'

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