Skip to main content
Glama

MCP Xcode

by Stefan-Nitu
ARCHITECTURE.md15.9 kB
# Clean/Hexagonal Architecture Documentation ## Overview This document outlines the architectural principles and guidelines for organizing code following Clean Architecture (Uncle Bob) and Hexagonal Architecture (Ports & Adapters) patterns. These patterns ensure separation of concerns, testability, and maintainability by organizing code into distinct layers with clear responsibilities and dependencies. ## Core Principles ### 1. The Dependency Rule **The most critical rule**: Dependencies can only point inward. Nothing in an inner circle can know anything about an outer circle. This includes variables, functions, classes, or any other software entities. ### 2. Dependency Inversion High-level modules should not depend on low-level modules. Both should depend on abstractions (interfaces). The core business logic defines interfaces that the infrastructure implements. ### 3. Separation of Concerns Each layer has a single, well-defined responsibility. Business logic is completely isolated from technical implementation details. ## Layer Architecture ``` ┌─────────────────────────────────────────────────────────────┐ │ Presentation Layer │ │ (Controllers, CLI, API Routes, UI) │ ├─────────────────────────────────────────────────────────────┤ │ Infrastructure Layer │ │ (Database, File System, External APIs, Frameworks) │ ├─────────────────────────────────────────────────────────────┤ │ Application Layer │ │ (Use Cases, Application Services, DTOs) │ ├─────────────────────────────────────────────────────────────┤ │ Domain Layer │ │ (Entities, Value Objects, Domain Services) │ └─────────────────────────────────────────────────────────────┘ ↑ Dependencies Flow Inward ``` ## Layer Responsibilities and Guidelines ### Domain Layer (Core - Innermost) **Location**: `src/domain/` **Purpose**: Contains the enterprise-wide business rules and core business logic that represents the problem domain. **What Goes Here**: - **Entities**: Core business objects with unique identities (e.g., `XcodeProject`, `BuildTarget`, `TestResult`) - **Value Objects**: Immutable objects without identity (e.g., `BuildConfiguration`, `Version`, `FilePath`) - **Domain Services**: Business logic that doesn't naturally fit within a single entity - **Domain Events**: Important business events that have occurred - **Repository Interfaces**: Contracts for data persistence (interfaces only, no implementations) - **Domain Exceptions**: Business rule violations and domain-specific errors **Rules**: - NO dependencies on any other layer - NO frameworks or external libraries - NO infrastructure concerns (databases, file systems) - Pure business logic only - Must be testable in isolation **Example Structure**: ``` src/features/{feature}/domain/ ├── BuildRequest.ts # Domain entities ├── BuildResult.ts ├── BuildIssue.ts # Value objects ├── BuildDestination.ts └── PlatformInfo.ts src/shared/domain/ # Shared domain objects ├── Platform.ts ├── DeviceId.ts ├── AppPath.ts └── ProjectPath.ts src/domain/services/ # Domain services └── PlatformDetector.ts ``` ### Application Layer **Location**: `src/application/` **Purpose**: Orchestrates the flow of data and coordinates domain objects to perform specific tasks. Contains application-specific business rules. **What Goes Here**: - **Use Cases**: Application-specific business rules and workflows (e.g., `BuildProjectUseCase`, `RunTestsUseCase`) - **Application Services**: Services that coordinate multiple use cases - **DTOs (Data Transfer Objects)**: Objects for transferring data between layers - **Mappers**: Convert between domain entities and DTOs - **Port Interfaces**: Contracts for external services (following Hexagonal Architecture) - **Application Events**: Application-level events and their handlers - **Validation**: Input validation for use cases **Rules**: - Depends ONLY on Domain layer - Defines interfaces that Infrastructure will implement - No knowledge of how data is persisted or external systems work - Orchestrates but doesn't implement technical details - Each use case should represent a single user intent **Example Structure**: ``` src/application/ ├── useCases/ │ ├── buildProject/ │ │ ├── BuildProjectUseCase.ts │ │ ├── BuildProjectDTO.ts │ │ └── BuildProjectValidator.ts │ └── runTests/ │ ├── RunTestsUseCase.ts │ └── RunTestsDTO.ts ├── services/ │ └── ProjectManagementService.ts ├── ports/ │ ├── IFileSystemService.ts # Interface for infrastructure │ ├── IXcodeBuildService.ts # Interface for infrastructure │ └── INotificationService.ts # Interface for infrastructure └── mappers/ └── ProjectMapper.ts ``` ### Infrastructure Layer **Location**: `src/infrastructure/` **Purpose**: Implements all technical details and external system integrations. Provides concrete implementations of interfaces defined in Application and Domain layers. **What Goes Here**: - **Repository Implementations**: Concrete implementations of domain repository interfaces - **External Service Adapters**: Implementations of application port interfaces - **Database/Persistence**: ORM configurations, database clients, migrations - **File System Operations**: File reading/writing implementations - **Framework Integrations**: Express, Fastify, MCP framework specific code - **Third-party API Clients**: External service integrations - **Configuration**: Environment variables, config files - **Logging**: Concrete logging implementations - **Monitoring/Metrics**: Performance monitoring, telemetry **Rules**: - Depends on Domain and Application layers - Implements interfaces defined in inner layers - Can use any framework or library - Handles all I/O operations - Adapts external systems to match application needs **Example Structure**: ``` src/infrastructure/ ├── repositories/ │ └── FileSystemProjectRepository.ts # Implements IProjectRepository ├── services/ │ ├── XcodeBuildService.ts # Implements IXcodeBuildService │ ├── FileSystemService.ts # Implements IFileSystemService │ └── SlackNotificationService.ts # Implements INotificationService ├── persistence/ │ ├── database/ │ └── cache/ ├── external/ │ └── XcodeAPIClient.ts ├── config/ │ └── Configuration.ts └── logging/ └── WinstonLogger.ts ``` ### Presentation Layer **Location**: `src/presentation/` **Purpose**: Handles all user interaction and presents data to users. Translates user input into application use case calls. **What Goes Here**: - **Controllers**: MCP tool controllers that define tool metadata and orchestrate flow - **Presenters**: Complex output formatting for rich responses - **Formatters**: Shared formatting utilities (error formatting, etc.) - **Validators**: Reusable validation schemas - **View Models**: Data structures optimized for presentation - **Request/Response Models**: Protocol-specific request and response schemas **Special Pattern for MCP Servers**: In MCP servers, controllers serve dual purpose: 1. Define MCP tool metadata (name, description, input schema) 2. Orchestrate the flow (validate → use case → present) This eliminates the need for separate Tool classes. Controllers ARE the tools. **Rules**: - Depends on Application layer (through use cases) - No business logic - only presentation concerns - Translates between external format and application DTOs - Handles user input validation (format, not business rules) - Can depend on Infrastructure for technical needs **Example Structure**: ``` src/presentation/ ├── controllers/ # MCP tool controllers │ ├── BuildXcodeController.ts │ └── InstallAppController.ts ├── presenters/ # Complex formatting (optional) │ └── BuildXcodePresenter.ts ├── formatters/ # Shared formatting utilities │ ├── ErrorFormatter.ts │ └── strategies/ └── validation/ # Reusable validators └── ToolInputValidators.ts ``` **Controller Pattern**: ```typescript // Controllers combine MCP tool definition with orchestration export class BuildXcodeController { // MCP tool metadata name = 'build_xcode'; description = 'Build an Xcode project'; // MCP input schema (JSON Schema) get inputSchema() { /* ... */ } // Tool execution with validation and orchestration async execute(args: unknown) { // 1. Validate input (parse don't validate) const validated = schema.parse(args); // 2. Create domain objects const request = BuildRequest.create(...); // 3. Execute use case const result = await this.useCase.execute(request); // 4. Present result (use presenter for complex formatting) return this.presenter?.present(result) || this.formatSimple(result); } } ``` **When to Use Presenters**: - Use a separate Presenter class when formatting is complex (errors, warnings, truncation) - For simple responses, format directly in the controller - Always use ErrorFormatter for consistent error messages ## Feature-Based Organization The codebase is organized by features (vertical slices) rather than technical layers (horizontal slices). Each feature contains its complete stack: ``` src/features/ ├── build/ # Build feature │ ├── domain/ # Domain objects for build │ ├── use-cases/ # Build-specific use cases │ ├── infrastructure/ # Build adapters │ ├── controllers/ # Build MCP controllers │ ├── factories/ # Build dependency injection │ └── tests/ # Build tests (unit, integration, e2e) ├── simulator/ # Simulator management feature │ ├── domain/ │ ├── use-cases/ │ ├── infrastructure/ │ ├── controllers/ │ ├── factories/ │ └── tests/ └── app-management/ # App installation feature ├── domain/ ├── use-cases/ ├── infrastructure/ ├── controllers/ ├── factories/ └── tests/ ``` **Benefits**: - **Feature Cohesion**: All code for a feature is in one place - **Independent Development**: Teams can own entire features - **Easy Navigation**: Find all related code quickly - **Clear Boundaries**: Features have explicit interfaces **Shared Code**: ``` src/shared/ # Cross-feature shared code ├── domain/ # Shared domain objects ├── infrastructure/ # Shared adapters └── tests/ # Shared test utilities ``` ## Additional Directories ### Factories **Location**: `src/features/{feature}/factories/` **Purpose**: Creates and wires up all dependencies, implementing dependency injection patterns. **What Goes Here**: - Dependency injection containers - Factory classes for complex object creation - Application bootstrapping code **MCP-Specific Pattern**: Factories create controllers (which serve as MCP tools) with all their dependencies: ```typescript export function createBuildXcodeController(): BuildXcodeController { // Create infrastructure const executor = new ShellCommandExecutorAdapter(promisify(exec)); // Create use case const useCase = new BuildProjectUseCase(...); // Create presenter (if needed) const presenter = new BuildXcodePresenter(); // Return controller that serves as MCP tool return new BuildXcodeController(useCase, presenter); } ``` ### Utils/Shared **Location**: `src/utils/` or `src/shared/` **Purpose**: Cross-cutting concerns and utilities used across layers. **What Goes Here**: - Generic utilities (not business-specific) - Common types and interfaces - Helper functions - Constants **Rules**: - Should not contain business logic - Should be layer-agnostic - Minimize dependencies ## Best Practices ### 1. Interface Segregation - Define narrow, focused interfaces - Clients should not depend on methods they don't use - Prefer multiple specific interfaces over one general interface ### 2. Use Case Design - One use case per user intent - Use cases should be independent of each other - Name use cases clearly (e.g., `BuildProjectUseCase`, not `ProjectService`) ### 3. Testing Strategy - **Domain Layer**: Unit tests with no mocks needed - **Application Layer**: Unit tests with mocked infrastructure - **Infrastructure Layer**: Integration tests with real external systems - **Presentation Layer**: API/E2E tests ### 4. Dependency Injection - Use constructor injection - Dependencies flow from outer to inner layers - Factories handle wiring at application startup ### 5. Error Handling - Domain exceptions for business rule violations (data only, no messages) - Application layer returns domain errors without formatting - Infrastructure exceptions for technical failures - Presentation layer formats all errors for user display ### 6. Type Safety and Enums - Always use enum values for comparisons, not string literals - ✅ Good: `if (state === SimulatorState.Booted)` - ❌ Bad: `if (state === 'Booted')` - Parse external data into domain types at system boundaries - Use validation functions (e.g., `Platform.parse()`) to convert strings to enums - This ensures type safety and catches invalid values early ## Migration Strategy When refactoring existing code to Clean Architecture: 1. **Start with Domain**: Identify and extract pure business logic 2. **Create Use Cases**: Wrap existing functionality in use cases 3. **Define Interfaces**: Create contracts for external dependencies 4. **Implement Adapters**: Wrap existing infrastructure code 5. **Refactor Gradually**: Move code layer by layer, maintaining functionality ## Common Pitfalls to Avoid 1. **Leaking Domain Knowledge**: Don't let database schemas dictate domain models 2. **Anemic Domain Models**: Ensure entities contain behavior, not just data 3. **Use Case Bloat**: Keep use cases focused on a single responsibility 4. **Skipping Layers**: Don't bypass layers for convenience 5. **Framework Coupling**: Keep frameworks in Infrastructure/Presentation only 6. **Shared Mutable State**: Prefer immutability, especially in Domain layer 7. **Circular Dependencies**: Always respect the dependency rule ## Benefits of This Architecture 1. **Testability**: Each layer can be tested in isolation 2. **Maintainability**: Clear separation of concerns makes changes easier 3. **Flexibility**: Easy to swap implementations (database, frameworks) 4. **Team Scalability**: Teams can work on different layers independently 5. **Business Focus**: Domain logic is clear and framework-agnostic 6. **Longevity**: Business logic survives framework and technology changes

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/Stefan-Nitu/mcp-xcode'

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