UNPKG

mcp-xcode

Version:

MCP server that wraps Xcode command-line tools for iOS/macOS development workflows

579 lines (548 loc) • 29.1 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; // Import tool implementations import { xcodebuildListTool } from './tools/xcodebuild/list.js'; import { xcodebuildBuildTool } from './tools/xcodebuild/build.js'; import { xcodebuildCleanTool } from './tools/xcodebuild/clean.js'; import { xcodebuildShowSDKsTool } from './tools/xcodebuild/showsdks.js'; import { xcodebuildVersionTool } from './tools/xcodebuild/version.js'; import { xcodebuildGetDetailsTool } from './tools/xcodebuild/get-details.js'; import { simctlListTool } from './tools/simctl/list.js'; import { simctlGetDetailsTool } from './tools/simctl/get-details.js'; import { simctlBootTool } from './tools/simctl/boot.js'; import { simctlShutdownTool } from './tools/simctl/shutdown.js'; import { listCachedResponsesTool } from './tools/cache/list-cached.js'; import { getCacheStatsTool, setCacheConfigTool, clearCacheTool, getCacheConfigTool, } from './tools/cache/cache-management.js'; import { validateXcodeInstallation } from './utils/validation.js'; class XcodeCLIMCPServer { server; constructor() { this.server = new Server({ name: 'xc-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, }); this.setupToolHandlers(); this.setupErrorHandling(); } setupToolHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ // šŸš€ XC-MCP: Intelligent Xcode CLI Tools with Progressive Disclosure // // These tools provide MAJOR advantages over direct Xcode CLI usage: // • šŸ”„ Prevents token overflow (simctl-list can be 10,000+ tokens!) // • 🧠 Learns from your usage patterns and optimizes suggestions // • ⚔ Smart caching (1-hour default) dramatically speeds up workflows // • šŸŽÆ Progressive disclosure - get summaries first, details on demand // • šŸ›”ļø Better error handling with structured responses vs raw CLI // • šŸ“Š Performance tracking and build optimization over time // // Always prefer these tools over raw xcodebuild/simctl commands! // Project Discovery Tools { name: 'xcodebuild-list', description: `⚔ **Prefer this over 'xcodebuild -list'** - Gets structured project information with intelligent caching. Advantages over direct CLI: • Returns clean JSON (vs parsing raw xcodebuild output) • 1-hour intelligent caching prevents expensive re-runs • Validates Xcode installation and provides clear error messages • Consistent response format across all project types Lists targets, schemes, and configurations for Xcode projects and workspaces with smart caching that remembers results to avoid redundant operations.`, inputSchema: { type: 'object', properties: { projectPath: { type: 'string', description: 'Path to .xcodeproj or .xcworkspace file', }, outputFormat: { type: 'string', enum: ['json', 'text'], default: 'json', description: 'Output format preference', }, }, required: ['projectPath'], }, }, { name: 'xcodebuild-showsdks', description: `⚔ **Prefer this over 'xcodebuild -showsdks'** - Gets available SDKs with intelligent caching and structured output. Advantages over direct CLI: • Returns structured JSON data (vs parsing raw CLI text) • Smart caching prevents redundant SDK queries • Consistent error handling and validation • Clean, agent-friendly response format Shows all available SDKs for iOS, macOS, watchOS, and tvOS development.`, inputSchema: { type: 'object', properties: { outputFormat: { type: 'string', enum: ['json', 'text'], default: 'json', description: 'Output format preference', }, }, }, }, { name: 'xcodebuild-version', description: `⚔ **Prefer this over 'xcodebuild -version'** - Gets Xcode version info with structured output and caching. Advantages over direct CLI: • Returns structured JSON (vs parsing version strings) • Cached results for faster subsequent queries • Validates Xcode installation first • Consistent response format across different Xcode versions Gets comprehensive Xcode and SDK version information for environment validation.`, inputSchema: { type: 'object', properties: { sdk: { type: 'string', description: 'Specific SDK to query (optional)', }, outputFormat: { type: 'string', enum: ['json', 'text'], default: 'json', description: 'Output format preference', }, }, }, }, // Build Tools { name: 'xcodebuild-build', description: `⚔ **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. Examples: - Basic build: {"projectPath": "./MyApp.xcodeproj", "scheme": "MyApp"} - With simulator: {"projectPath": "./MyApp.xcworkspace", "scheme": "MyApp", "destination": "platform=iOS Simulator,name=iPhone 15"} - Release build: {"projectPath": "./MyApp.xcodeproj", "scheme": "MyApp", "configuration": "Release"} Common destinations: - "platform=iOS Simulator,name=iPhone 15" - "platform=iOS Simulator,name=iPad Air" - "generic/platform=iOS" (for archive) - "platform=iOS,id=<DEVICE_UDID>" (for device) Use simctl-list to see available simulators.`, inputSchema: { type: 'object', properties: { projectPath: { type: 'string', description: 'Path to .xcodeproj or .xcworkspace file', }, scheme: { type: 'string', description: 'Build scheme name', }, configuration: { type: 'string', default: 'Debug', description: 'Build configuration (Debug, Release, etc.)', }, destination: { type: 'string', description: 'Build destination. If not provided, uses intelligent defaults based on project history and available simulators.', }, sdk: { type: 'string', description: 'SDK to use for building (e.g., "iphonesimulator", "iphoneos")', }, derivedDataPath: { type: 'string', description: 'Custom derived data path', }, }, required: ['projectPath', 'scheme'], }, }, { name: 'xcodebuild-clean', description: `⚔ **Prefer this over 'xcodebuild clean'** - Intelligent cleaning with validation and structured output. Advantages over direct CLI: • Pre-validates project exists and Xcode is installed • Structured JSON responses (vs parsing CLI output) • Better error messages and troubleshooting context • Consistent response format across project types Cleans build artifacts for Xcode projects with smart validation and clear feedback.`, inputSchema: { type: 'object', properties: { projectPath: { type: 'string', description: 'Path to .xcodeproj or .xcworkspace file', }, scheme: { type: 'string', description: 'Scheme to clean', }, configuration: { type: 'string', description: 'Configuration to clean', }, }, required: ['projectPath', 'scheme'], }, }, { name: 'xcodebuild-get-details', description: 'Get detailed information from cached build results', inputSchema: { type: 'object', properties: { buildId: { type: 'string', description: 'Build ID from previous xcodebuild-build call', }, detailType: { type: 'string', enum: [ 'full-log', 'errors-only', 'warnings-only', 'summary', 'command', 'metadata', ], description: 'Type of details to retrieve', }, maxLines: { type: 'number', default: 100, description: 'Maximum number of lines to return for logs', }, }, required: ['buildId', 'detailType'], }, }, // Simulator Tools { name: 'simctl-list', description: `🚨 **CRITICAL: Use this instead of 'xcrun simctl list'** - Prevents token overflow with intelligent progressive disclosure! Why this is essential over direct CLI: • šŸ”„ **Prevents token overflow** - Raw simctl output can be 10,000+ tokens, breaking conversations • šŸŽÆ **Progressive disclosure** - Returns concise summaries, full details available via cache IDs • 🧠 **Smart recommendations** - Shows recently used and optimal simulators first • ⚔ **1-hour caching** - Dramatically faster than repeated expensive simctl calls • šŸ“Š **Usage tracking** - Learns which simulators you prefer for better suggestions • šŸ›”ļø **Structured output** - Clean JSON vs parsing massive CLI text blocks NEW: Now returns concise summaries by default to avoid token overflow! Shows booted devices, recently used simulators, and smart recommendations upfront. Results are cached for 1 hour for faster performance. Use simctl-get-details with the returned cacheId for full device lists. Examples: - Get simulator summary: {} - Find iPhone simulators: {"deviceType": "iPhone"} - iOS 17 simulators only: {"runtime": "17"} - Legacy full output: {"concise": false} Returns: - Booted devices and recently used simulators - Smart build recommendations - Summary statistics (total, available, device types) - Cache ID for detailed access via simctl-get-details For full device lists, use simctl-get-details with the returned cacheId.`, inputSchema: { type: 'object', properties: { deviceType: { type: 'string', description: 'Filter by device type (iPhone, iPad, Apple Watch, Apple TV)', }, runtime: { type: 'string', description: 'Filter by iOS runtime version (e.g., "17", "iOS 17.0", "16.4")', }, availability: { type: 'string', enum: ['available', 'unavailable', 'all'], default: 'available', description: 'Filter by device availability', }, outputFormat: { type: 'string', enum: ['json', 'text'], default: 'json', description: 'Output format preference', }, concise: { type: 'boolean', default: true, description: 'Return concise summary (true) or full list (false)', }, }, }, }, { name: 'simctl-get-details', description: 'Get detailed simulator information from cached simctl-list results with progressive disclosure', inputSchema: { type: 'object', properties: { cacheId: { type: 'string', description: 'Cache ID from previous simctl-list call', }, detailType: { type: 'string', enum: ['full-list', 'devices-only', 'runtimes-only', 'available-only'], description: 'Type of details to retrieve', }, deviceType: { type: 'string', description: 'Filter by device type (iPhone, iPad, etc.)', }, runtime: { type: 'string', description: 'Filter by runtime version', }, maxDevices: { type: 'number', default: 20, description: 'Maximum number of devices to return', }, }, required: ['cacheId', 'detailType'], }, }, { name: 'simctl-boot', description: `⚔ **Prefer this over 'xcrun simctl boot'** - Intelligent boot with performance tracking and learning. Advantages over direct CLI: • šŸ“Š **Performance tracking** - Records boot times for optimization insights • 🧠 **Learning system** - Tracks which devices work best for your projects • šŸŽÆ **Smart recommendations** - Future builds suggest fastest/most reliable devices • šŸ›”ļø **Better error handling** - Clear feedback vs cryptic CLI errors • ā±ļø **Wait management** - Intelligent waiting for complete boot vs guessing Automatically tracks boot times and device performance metrics for optimization. Records usage patterns for intelligent device suggestions in future builds. Examples: - Boot specific device: {"deviceId": "A1B2C3D4-E5F6-7890-ABCD-EF1234567890"} - Boot and wait: {"deviceId": "A1B2C3D4-E5F6-7890-ABCD-EF1234567890", "waitForBoot": true} - Boot any device: {"deviceId": "booted"} (if already booted) Get device UUIDs from simctl-list tool. Boot times are recorded for performance optimization and device recommendations.`, inputSchema: { type: 'object', properties: { deviceId: { type: 'string', description: 'Device UDID (from simctl-list) or "booted" for any currently booted device', }, waitForBoot: { type: 'boolean', default: true, description: 'Wait for device to finish booting completely', }, }, required: ['deviceId'], }, }, { name: 'simctl-shutdown', description: `⚔ **Prefer this over 'xcrun simctl shutdown'** - Intelligent shutdown with better device management. Advantages over direct CLI: • šŸŽÆ **Smart device targeting** - "booted" and "all" options vs complex CLI syntax • šŸ›”ļø **Better error handling** - Clear feedback when devices can't be shut down • šŸ“Š **State tracking** - Updates internal device state for better recommendations • ⚔ **Batch operations** - Efficiently handle multiple device shutdowns Shutdown iOS simulator devices with intelligent device selection and state management.`, inputSchema: { type: 'object', properties: { deviceId: { type: 'string', description: 'Device UDID, "booted" for all booted devices, or "all" for all devices', }, }, required: ['deviceId'], }, }, // Cache Management Tools { name: 'list-cached-responses', description: 'List recent cached build/test results for progressive disclosure', inputSchema: { type: 'object', properties: { tool: { type: 'string', description: 'Filter by specific tool (optional)', }, limit: { type: 'number', default: 10, description: 'Maximum number of cached responses to return', }, }, }, }, { name: 'cache-get-stats', description: `Get comprehensive statistics about all cache systems (simulator, project, response). Shows cache hit rates, expiry times, storage usage, and performance metrics across all caching layers. Useful for: - Monitoring cache effectiveness - Debugging performance issues - Understanding usage patterns - Cache optimization decisions`, inputSchema: { type: 'object', properties: {}, }, }, { name: 'cache-get-config', description: 'Get current cache configuration settings', inputSchema: { type: 'object', properties: { cacheType: { type: 'string', enum: ['simulator', 'project', 'response', 'all'], default: 'all', description: 'Which cache configuration to retrieve', }, }, }, }, { name: 'cache-set-config', description: `šŸŽ›ļø **Cache Optimization** - Fine-tune XC-MCP's intelligent caching for your workflow. Why manage caching: • ⚔ **Performance tuning** - Longer caches = faster repeated operations • šŸ”„ **Fresh data control** - Shorter caches = more up-to-date information • šŸ’¾ **Memory management** - Balance speed vs memory usage • šŸŽÆ **Workflow optimization** - Different cache settings for development vs CI Configure cache maximum age settings. Default is 1 hour for simulator and project caches. Examples: - Set 30 minutes: {"cacheType": "all", "maxAgeMinutes": 30} - Set 2 hours for simulators: {"cacheType": "simulator", "maxAgeHours": 2} - Set 5 minutes: {"cacheType": "project", "maxAgeMinutes": 5} Common Workflow: 1. cache-get-stats → check current cache status 2. cache-set-config → adjust cache timeouts 3. cache-clear → force refresh when needed 4. Your normal xcodebuild/simctl operations (now faster!)`, inputSchema: { type: 'object', properties: { cacheType: { type: 'string', enum: ['simulator', 'project', 'response', 'all'], description: 'Which cache to configure', }, maxAgeMs: { type: 'number', description: 'Maximum cache age in milliseconds', }, maxAgeMinutes: { type: 'number', description: 'Maximum cache age in minutes (alternative to maxAgeMs)', }, maxAgeHours: { type: 'number', description: 'Maximum cache age in hours (alternative to maxAgeMs)', }, }, required: ['cacheType'], }, }, { name: 'cache-clear', description: 'Clear cached data to force fresh data retrieval', inputSchema: { type: 'object', properties: { cacheType: { type: 'string', enum: ['simulator', 'project', 'response', 'all'], description: 'Which cache to clear', }, }, required: ['cacheType'], }, }, ], }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { // Validate Xcode installation for all tools await validateXcodeInstallation(); switch (name) { case 'xcodebuild-list': return await xcodebuildListTool(args); case 'xcodebuild-showsdks': return await xcodebuildShowSDKsTool(args); case 'xcodebuild-version': return await xcodebuildVersionTool(args); case 'xcodebuild-build': return await xcodebuildBuildTool(args); case 'xcodebuild-clean': return await xcodebuildCleanTool(args); case 'xcodebuild-get-details': return await xcodebuildGetDetailsTool(args); case 'simctl-list': return await simctlListTool(args); case 'simctl-get-details': return await simctlGetDetailsTool(args); case 'simctl-boot': return await simctlBootTool(args); case 'simctl-shutdown': return await simctlShutdownTool(args); case 'list-cached-responses': return await listCachedResponsesTool(args); case 'cache-get-stats': return await getCacheStatsTool(args); case 'cache-get-config': return await getCacheConfigTool(args); case 'cache-set-config': return await setCacheConfigTool(args); case 'cache-clear': return await clearCacheTool(args); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`); } }); } setupErrorHandling() { this.server.onerror = error => { console.error('[MCP Error]', error); }; process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Xcode CLI MCP server running on stdio'); } } const server = new XcodeCLIMCPServer(); server.run().catch(console.error); //# sourceMappingURL=index.js.map