UNPKG

apple-dev-mcp

Version:

Complete Apple development guidance: Human Interface Guidelines (design) + Technical Documentation for iOS, macOS, watchOS, tvOS, and visionOS

291 lines 13.2 kB
#!/usr/bin/env node /** * Apple Dev MCP Server * * A Model Context Protocol server that provides complete Apple development guidance, * combining Human Interface Guidelines (design) with Technical Documentation (API). * * @author Tanner Maasen * @license MIT */ // Very first debug message to test if stderr works at all console.error('🎬 Apple Dev MCP Server script starting - VERY FIRST MESSAGE'); // Aggressively silence all logging to prevent MCP protocol interference process.env.DEBUG = ''; // NEVER silence console.error for debugging // Temporarily disable ALL console silencing to debug // if (process.env.NODE_ENV !== 'development') { // console.log = () => {}; // console.warn = () => {}; // console.info = () => {}; // console.debug = () => {}; // // Keep console.error for MCP debugging as per documentation // } console.error('🔍 NODE_ENV:', process.env.NODE_ENV); console.error('🔍 DEBUG MODE: console.error should work now'); // TEMPORARILY DISABLE ALL STDERR FILTERING FOR DEBUGGING // const originalStderrWrite = process.stderr.write; // process.stderr.write = function(chunk: any, encoding?: any, callback?: any) { // // Let ALL messages through for debugging // return originalStderrWrite.call(this, chunk, encoding, callback); // }; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { ListToolsRequestSchema, CallToolRequestSchema, ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { HIGCache } from './cache.js'; import { HIGToolProvider } from './tools.js'; import { AppleContentAPIClient } from './services/apple-content-api-client.service.js'; class AppleHIGMCPServer { server; cache; toolProvider; appleContentAPIClient; constructor() { this.server = new Server({ name: 'Apple Dev MCP', version: '2.0.0', description: 'Complete Apple development guidance: Human Interface Guidelines (design) + Technical Documentation (API) for iOS, macOS, watchOS, tvOS, and visionOS', }, { capabilities: { tools: {}, }, }); } /** * Initialize the server asynchronously */ async initialize() { try { console.error('🔧 Starting initialization...'); // Only do minimal validation during startup to avoid DXT timeout // All heavy initialization is deferred until first request // Quick environment check (no external dependencies or imports) console.error('📋 Checking Node.js version...'); const requiredNodeVersion = '18.0.0'; const currentVersion = process.version.slice(1); if (this.compareVersions(currentVersion, requiredNodeVersion) < 0) { throw new Error(`Node.js ${requiredNodeVersion} or higher is required. Current version: ${process.version}`); } console.error(`✅ Node.js version OK: ${process.version}`); // Initialize components with lazy loading - don't access content directory yet console.error('🏗️ Creating server components...'); this.cache = new HIGCache(3600); console.error('✅ Cache created'); this.appleContentAPIClient = new AppleContentAPIClient(this.cache); console.error('✅ API client created'); this.toolProvider = new HIGToolProvider(this.cache, this.appleContentAPIClient); console.error('✅ Tool provider created'); console.error('🔌 Setting up request handlers...'); this.setupHandlers(); console.error('✅ Request handlers configured'); } catch (error) { console.error('💥 Initialization failed:', error); throw error; } } /** * Compare semantic versions */ compareVersions(version1, version2) { const v1Parts = version1.split('.').map(Number); const v2Parts = version2.split('.').map(Number); for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) { const v1Part = v1Parts[i] || 0; const v2Part = v2Parts[i] || 0; if (v1Part > v2Part) return 1; if (v1Part < v2Part) return -1; } return 0; } /** * Set up MCP request handlers with comprehensive error handling */ setupHandlers() { // Tool handlers this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'search_human_interface_guidelines', title: 'Search HIG Guidelines', description: 'Search Apple Human Interface Guidelines by keywords', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (keywords, component names, design concepts)', }, platform: { type: 'string', enum: ['iOS', 'macOS', 'watchOS', 'tvOS', 'visionOS', 'universal'], description: 'Optional: Filter by Apple platform', }, }, required: ['query'], }, }, // TODO: Future release - re-enable with static content integration // For now, users should search "accessibility" + component through regular HIG search // { // name: 'get_accessibility_requirements', // title: 'Get Accessibility Requirements', // description: 'Get accessibility requirements and guidelines for specific components', // inputSchema: { // type: 'object', // properties: { // component: { // type: 'string', // description: 'Component name (e.g., "Button", "Navigation Bar", "Tab Bar")', // }, // platform: { // type: 'string', // enum: ['iOS', 'macOS', 'watchOS', 'tvOS', 'visionOS'], // description: 'Target platform', // }, // }, // required: ['component', 'platform'], // }, // }, { name: 'search_technical_documentation', title: 'Search Technical Documentation', description: 'Search Apple technical documentation and API references', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (API names, symbols, frameworks)', }, framework: { type: 'string', description: 'Optional: Search within specific framework (e.g., "SwiftUI", "UIKit")', }, platform: { type: 'string', description: 'Optional: Filter by platform (iOS, macOS, etc.)', }, }, required: ['query'], }, }, { name: 'search_unified', title: 'Unified Search', description: 'Unified search across both HIG design guidelines and technical documentation', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (keywords, component names, design concepts)', }, platform: { type: 'string', enum: ['iOS', 'macOS', 'watchOS', 'tvOS', 'visionOS', 'universal'], description: 'Optional: Filter by Apple platform', }, }, required: ['query'], }, }, ], }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { try { const { name, arguments: args } = request.params; // Validate tool name if (!name || typeof name !== 'string') { throw new McpError(ErrorCode.InvalidRequest, 'Invalid or missing tool name'); } // Validate arguments if (args && typeof args !== 'object') { throw new McpError(ErrorCode.InvalidRequest, 'Tool arguments must be an object'); } // eslint-disable-next-line @typescript-eslint/no-explicit-any let result; switch (name) { case 'search_human_interface_guidelines': { result = await this.toolProvider.searchHumanInterfaceGuidelines(args); break; } // TODO: Future release - re-enable with static content integration // case 'get_accessibility_requirements': { // result = await this.toolProvider.getAccessibilityRequirements(args as any); // break; // } case 'search_technical_documentation': { result = await this.toolProvider.searchTechnicalDocumentation(args); break; } case 'search_unified': { result = await this.toolProvider.searchUnified(args); break; } default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } return { content: [{ type: 'text', text: JSON.stringify(result, null, 2), }], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; if (error instanceof McpError) { throw error; } throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${errorMessage}`); } }); } /** * Start the MCP server with proper error handling and logging */ async run() { try { // Minimal startup logging for fast DXT validation console.error('🚀 Apple Dev MCP Server starting...'); // Initialize the server components (minimal, fast startup) await this.initialize(); console.error('✅ Server initialized successfully'); const transport = new StdioServerTransport(); // Add error handling for transport transport.onclose = () => { console.error('🔌 Transport closed'); }; transport.onerror = (error) => { console.error('❌ Transport error:', error); }; console.error('🔗 Connecting to transport...'); await this.server.connect(transport); console.error('✅ Server connected and ready'); } catch (error) { console.error('💥 Server startup failed:', error); process.exit(1); } } } // Handle graceful shutdown process.on('SIGINT', async () => { process.exit(0); }); process.on('SIGTERM', async () => { process.exit(0); }); // Start the server immediately (remove conditional for DXT compatibility) console.error('🎯 Creating Apple Dev MCP Server instance...'); const server = new AppleHIGMCPServer(); server.run().catch((error) => { console.error('💀 Fatal server error:', error); process.exit(1); }); //# sourceMappingURL=server.js.map