#
tokens: 48354/50000 7/337 files (page 10/11)
lines: off (toggle) GitHub
raw markdown copy
This is page 10 of 11. Use http://codebase.md/cameroncooke/xcodebuildmcp?lines=false&page={x} to view the full context.

# Directory Structure

```
├── .axe-version
├── .claude
│   └── agents
│       └── xcodebuild-mcp-qa-tester.md
├── .cursor
│   ├── BUGBOT.md
│   └── environment.json
├── .cursorrules
├── .github
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   └── feature_request.yml
│   └── workflows
│       ├── ci.yml
│       ├── claude-code-review.yml
│       ├── claude-dispatch.yml
│       ├── claude.yml
│       ├── droid-code-review.yml
│       ├── README.md
│       ├── release.yml
│       └── sentry.yml
├── .gitignore
├── .prettierignore
├── .prettierrc.js
├── .vscode
│   ├── extensions.json
│   ├── launch.json
│   ├── mcp.json
│   ├── settings.json
│   └── tasks.json
├── AGENTS.md
├── banner.png
├── build-plugins
│   ├── plugin-discovery.js
│   ├── plugin-discovery.ts
│   └── tsconfig.json
├── CHANGELOG.md
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── Dockerfile
├── docs
│   ├── ARCHITECTURE.md
│   ├── CODE_QUALITY.md
│   ├── CONTRIBUTING.md
│   ├── ESLINT_TYPE_SAFETY.md
│   ├── MANUAL_TESTING.md
│   ├── NODEJS_2025.md
│   ├── PLUGIN_DEVELOPMENT.md
│   ├── RELEASE_PROCESS.md
│   ├── RELOADEROO_FOR_XCODEBUILDMCP.md
│   ├── RELOADEROO_XCODEBUILDMCP_PRIMER.md
│   ├── RELOADEROO.md
│   ├── session_management_plan.md
│   ├── session-aware-migration-todo.md
│   ├── TEST_RUNNER_ENV_IMPLEMENTATION_PLAN.md
│   ├── TESTING.md
│   └── TOOLS.md
├── eslint.config.js
├── example_projects
│   ├── .vscode
│   │   └── launch.json
│   ├── iOS
│   │   ├── .cursor
│   │   │   └── rules
│   │   │       └── errors.mdc
│   │   ├── .vscode
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── MCPTest
│   │   │   ├── Assets.xcassets
│   │   │   │   ├── AccentColor.colorset
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── AppIcon.appiconset
│   │   │   │   │   └── Contents.json
│   │   │   │   └── Contents.json
│   │   │   ├── ContentView.swift
│   │   │   ├── MCPTestApp.swift
│   │   │   └── Preview Content
│   │   │       └── Preview Assets.xcassets
│   │   │           └── Contents.json
│   │   ├── MCPTest.xcodeproj
│   │   │   ├── project.pbxproj
│   │   │   └── xcshareddata
│   │   │       └── xcschemes
│   │   │           └── MCPTest.xcscheme
│   │   └── MCPTestUITests
│   │       └── MCPTestUITests.swift
│   ├── iOS_Calculator
│   │   ├── CalculatorApp
│   │   │   ├── Assets.xcassets
│   │   │   │   ├── AccentColor.colorset
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── AppIcon.appiconset
│   │   │   │   │   └── Contents.json
│   │   │   │   └── Contents.json
│   │   │   ├── CalculatorApp.swift
│   │   │   └── CalculatorApp.xctestplan
│   │   ├── CalculatorApp.xcodeproj
│   │   │   ├── project.pbxproj
│   │   │   └── xcshareddata
│   │   │       └── xcschemes
│   │   │           └── CalculatorApp.xcscheme
│   │   ├── CalculatorApp.xcworkspace
│   │   │   └── contents.xcworkspacedata
│   │   ├── CalculatorAppPackage
│   │   │   ├── .gitignore
│   │   │   ├── Package.swift
│   │   │   ├── Sources
│   │   │   │   └── CalculatorAppFeature
│   │   │   │       ├── BackgroundEffect.swift
│   │   │   │       ├── CalculatorButton.swift
│   │   │   │       ├── CalculatorDisplay.swift
│   │   │   │       ├── CalculatorInputHandler.swift
│   │   │   │       ├── CalculatorService.swift
│   │   │   │       └── ContentView.swift
│   │   │   └── Tests
│   │   │       └── CalculatorAppFeatureTests
│   │   │           └── CalculatorServiceTests.swift
│   │   ├── CalculatorAppTests
│   │   │   └── CalculatorAppTests.swift
│   │   └── Config
│   │       ├── Debug.xcconfig
│   │       ├── Release.xcconfig
│   │       ├── Shared.xcconfig
│   │       └── Tests.xcconfig
│   ├── macOS
│   │   ├── MCPTest
│   │   │   ├── Assets.xcassets
│   │   │   │   ├── AccentColor.colorset
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── AppIcon.appiconset
│   │   │   │   │   └── Contents.json
│   │   │   │   └── Contents.json
│   │   │   ├── ContentView.swift
│   │   │   ├── MCPTest.entitlements
│   │   │   ├── MCPTestApp.swift
│   │   │   └── Preview Content
│   │   │       └── Preview Assets.xcassets
│   │   │           └── Contents.json
│   │   └── MCPTest.xcodeproj
│   │       ├── project.pbxproj
│   │       └── xcshareddata
│   │           └── xcschemes
│   │               └── MCPTest.xcscheme
│   └── spm
│       ├── .gitignore
│       ├── Package.resolved
│       ├── Package.swift
│       ├── Sources
│       │   ├── long-server
│       │   │   └── main.swift
│       │   ├── quick-task
│       │   │   └── main.swift
│       │   ├── spm
│       │   │   └── main.swift
│       │   └── TestLib
│       │       └── TaskManager.swift
│       └── Tests
│           └── TestLibTests
│               └── SimpleTests.swift
├── LICENSE
├── mcp-install-dark.png
├── package-lock.json
├── package.json
├── README.md
├── scripts
│   ├── analysis
│   │   └── tools-analysis.ts
│   ├── bundle-axe.sh
│   ├── check-code-patterns.js
│   ├── release.sh
│   ├── tools-cli.ts
│   └── update-tools-docs.ts
├── server.json
├── smithery.yaml
├── src
│   ├── core
│   │   ├── __tests__
│   │   │   └── resources.test.ts
│   │   ├── dynamic-tools.ts
│   │   ├── plugin-registry.ts
│   │   ├── plugin-types.ts
│   │   └── resources.ts
│   ├── doctor-cli.ts
│   ├── index.ts
│   ├── mcp
│   │   ├── resources
│   │   │   ├── __tests__
│   │   │   │   ├── devices.test.ts
│   │   │   │   ├── doctor.test.ts
│   │   │   │   └── simulators.test.ts
│   │   │   ├── devices.ts
│   │   │   ├── doctor.ts
│   │   │   └── simulators.ts
│   │   └── tools
│   │       ├── device
│   │       │   ├── __tests__
│   │       │   │   ├── build_device.test.ts
│   │       │   │   ├── get_device_app_path.test.ts
│   │       │   │   ├── index.test.ts
│   │       │   │   ├── install_app_device.test.ts
│   │       │   │   ├── launch_app_device.test.ts
│   │       │   │   ├── list_devices.test.ts
│   │       │   │   ├── re-exports.test.ts
│   │       │   │   ├── stop_app_device.test.ts
│   │       │   │   └── test_device.test.ts
│   │       │   ├── build_device.ts
│   │       │   ├── clean.ts
│   │       │   ├── discover_projs.ts
│   │       │   ├── get_app_bundle_id.ts
│   │       │   ├── get_device_app_path.ts
│   │       │   ├── index.ts
│   │       │   ├── install_app_device.ts
│   │       │   ├── launch_app_device.ts
│   │       │   ├── list_devices.ts
│   │       │   ├── list_schemes.ts
│   │       │   ├── show_build_settings.ts
│   │       │   ├── start_device_log_cap.ts
│   │       │   ├── stop_app_device.ts
│   │       │   ├── stop_device_log_cap.ts
│   │       │   └── test_device.ts
│   │       ├── discovery
│   │       │   ├── __tests__
│   │       │   │   └── discover_tools.test.ts
│   │       │   ├── discover_tools.ts
│   │       │   └── index.ts
│   │       ├── doctor
│   │       │   ├── __tests__
│   │       │   │   ├── doctor.test.ts
│   │       │   │   └── index.test.ts
│   │       │   ├── doctor.ts
│   │       │   ├── index.ts
│   │       │   └── lib
│   │       │       └── doctor.deps.ts
│   │       ├── logging
│   │       │   ├── __tests__
│   │       │   │   ├── index.test.ts
│   │       │   │   ├── start_device_log_cap.test.ts
│   │       │   │   ├── start_sim_log_cap.test.ts
│   │       │   │   ├── stop_device_log_cap.test.ts
│   │       │   │   └── stop_sim_log_cap.test.ts
│   │       │   ├── index.ts
│   │       │   ├── start_device_log_cap.ts
│   │       │   ├── start_sim_log_cap.ts
│   │       │   ├── stop_device_log_cap.ts
│   │       │   └── stop_sim_log_cap.ts
│   │       ├── macos
│   │       │   ├── __tests__
│   │       │   │   ├── build_macos.test.ts
│   │       │   │   ├── build_run_macos.test.ts
│   │       │   │   ├── get_mac_app_path.test.ts
│   │       │   │   ├── index.test.ts
│   │       │   │   ├── launch_mac_app.test.ts
│   │       │   │   ├── re-exports.test.ts
│   │       │   │   ├── stop_mac_app.test.ts
│   │       │   │   └── test_macos.test.ts
│   │       │   ├── build_macos.ts
│   │       │   ├── build_run_macos.ts
│   │       │   ├── clean.ts
│   │       │   ├── discover_projs.ts
│   │       │   ├── get_mac_app_path.ts
│   │       │   ├── get_mac_bundle_id.ts
│   │       │   ├── index.ts
│   │       │   ├── launch_mac_app.ts
│   │       │   ├── list_schemes.ts
│   │       │   ├── show_build_settings.ts
│   │       │   ├── stop_mac_app.ts
│   │       │   └── test_macos.ts
│   │       ├── project-discovery
│   │       │   ├── __tests__
│   │       │   │   ├── discover_projs.test.ts
│   │       │   │   ├── get_app_bundle_id.test.ts
│   │       │   │   ├── get_mac_bundle_id.test.ts
│   │       │   │   ├── index.test.ts
│   │       │   │   ├── list_schemes.test.ts
│   │       │   │   └── show_build_settings.test.ts
│   │       │   ├── discover_projs.ts
│   │       │   ├── get_app_bundle_id.ts
│   │       │   ├── get_mac_bundle_id.ts
│   │       │   ├── index.ts
│   │       │   ├── list_schemes.ts
│   │       │   └── show_build_settings.ts
│   │       ├── project-scaffolding
│   │       │   ├── __tests__
│   │       │   │   ├── index.test.ts
│   │       │   │   ├── scaffold_ios_project.test.ts
│   │       │   │   └── scaffold_macos_project.test.ts
│   │       │   ├── index.ts
│   │       │   ├── scaffold_ios_project.ts
│   │       │   └── scaffold_macos_project.ts
│   │       ├── session-management
│   │       │   ├── __tests__
│   │       │   │   ├── index.test.ts
│   │       │   │   ├── session_clear_defaults.test.ts
│   │       │   │   ├── session_set_defaults.test.ts
│   │       │   │   └── session_show_defaults.test.ts
│   │       │   ├── index.ts
│   │       │   ├── session_clear_defaults.ts
│   │       │   ├── session_set_defaults.ts
│   │       │   └── session_show_defaults.ts
│   │       ├── simulator
│   │       │   ├── __tests__
│   │       │   │   ├── boot_sim.test.ts
│   │       │   │   ├── build_run_sim.test.ts
│   │       │   │   ├── build_sim.test.ts
│   │       │   │   ├── get_sim_app_path.test.ts
│   │       │   │   ├── index.test.ts
│   │       │   │   ├── install_app_sim.test.ts
│   │       │   │   ├── launch_app_logs_sim.test.ts
│   │       │   │   ├── launch_app_sim.test.ts
│   │       │   │   ├── list_sims.test.ts
│   │       │   │   ├── open_sim.test.ts
│   │       │   │   ├── record_sim_video.test.ts
│   │       │   │   ├── screenshot.test.ts
│   │       │   │   ├── stop_app_sim.test.ts
│   │       │   │   └── test_sim.test.ts
│   │       │   ├── boot_sim.ts
│   │       │   ├── build_run_sim.ts
│   │       │   ├── build_sim.ts
│   │       │   ├── clean.ts
│   │       │   ├── describe_ui.ts
│   │       │   ├── discover_projs.ts
│   │       │   ├── get_app_bundle_id.ts
│   │       │   ├── get_sim_app_path.ts
│   │       │   ├── index.ts
│   │       │   ├── install_app_sim.ts
│   │       │   ├── launch_app_logs_sim.ts
│   │       │   ├── launch_app_sim.ts
│   │       │   ├── list_schemes.ts
│   │       │   ├── list_sims.ts
│   │       │   ├── open_sim.ts
│   │       │   ├── record_sim_video.ts
│   │       │   ├── screenshot.ts
│   │       │   ├── show_build_settings.ts
│   │       │   ├── stop_app_sim.ts
│   │       │   └── test_sim.ts
│   │       ├── simulator-management
│   │       │   ├── __tests__
│   │       │   │   ├── erase_sims.test.ts
│   │       │   │   ├── index.test.ts
│   │       │   │   ├── reset_sim_location.test.ts
│   │       │   │   ├── set_sim_appearance.test.ts
│   │       │   │   ├── set_sim_location.test.ts
│   │       │   │   └── sim_statusbar.test.ts
│   │       │   ├── boot_sim.ts
│   │       │   ├── erase_sims.ts
│   │       │   ├── index.ts
│   │       │   ├── list_sims.ts
│   │       │   ├── open_sim.ts
│   │       │   ├── reset_sim_location.ts
│   │       │   ├── set_sim_appearance.ts
│   │       │   ├── set_sim_location.ts
│   │       │   └── sim_statusbar.ts
│   │       ├── swift-package
│   │       │   ├── __tests__
│   │       │   │   ├── active-processes.test.ts
│   │       │   │   ├── index.test.ts
│   │       │   │   ├── swift_package_build.test.ts
│   │       │   │   ├── swift_package_clean.test.ts
│   │       │   │   ├── swift_package_list.test.ts
│   │       │   │   ├── swift_package_run.test.ts
│   │       │   │   ├── swift_package_stop.test.ts
│   │       │   │   └── swift_package_test.test.ts
│   │       │   ├── active-processes.ts
│   │       │   ├── index.ts
│   │       │   ├── swift_package_build.ts
│   │       │   ├── swift_package_clean.ts
│   │       │   ├── swift_package_list.ts
│   │       │   ├── swift_package_run.ts
│   │       │   ├── swift_package_stop.ts
│   │       │   └── swift_package_test.ts
│   │       ├── ui-testing
│   │       │   ├── __tests__
│   │       │   │   ├── button.test.ts
│   │       │   │   ├── describe_ui.test.ts
│   │       │   │   ├── gesture.test.ts
│   │       │   │   ├── index.test.ts
│   │       │   │   ├── key_press.test.ts
│   │       │   │   ├── key_sequence.test.ts
│   │       │   │   ├── long_press.test.ts
│   │       │   │   ├── screenshot.test.ts
│   │       │   │   ├── swipe.test.ts
│   │       │   │   ├── tap.test.ts
│   │       │   │   ├── touch.test.ts
│   │       │   │   └── type_text.test.ts
│   │       │   ├── button.ts
│   │       │   ├── describe_ui.ts
│   │       │   ├── gesture.ts
│   │       │   ├── index.ts
│   │       │   ├── key_press.ts
│   │       │   ├── key_sequence.ts
│   │       │   ├── long_press.ts
│   │       │   ├── screenshot.ts
│   │       │   ├── swipe.ts
│   │       │   ├── tap.ts
│   │       │   ├── touch.ts
│   │       │   └── type_text.ts
│   │       └── utilities
│   │           ├── __tests__
│   │           │   ├── clean.test.ts
│   │           │   └── index.test.ts
│   │           ├── clean.ts
│   │           └── index.ts
│   ├── server
│   │   └── server.ts
│   ├── test-utils
│   │   └── mock-executors.ts
│   ├── types
│   │   └── common.ts
│   └── utils
│       ├── __tests__
│       │   ├── build-utils.test.ts
│       │   ├── environment.test.ts
│       │   ├── session-aware-tool-factory.test.ts
│       │   ├── session-store.test.ts
│       │   ├── simulator-utils.test.ts
│       │   ├── test-runner-env-integration.test.ts
│       │   └── typed-tool-factory.test.ts
│       ├── axe
│       │   └── index.ts
│       ├── axe-helpers.ts
│       ├── build
│       │   └── index.ts
│       ├── build-utils.ts
│       ├── capabilities.ts
│       ├── command.ts
│       ├── CommandExecutor.ts
│       ├── environment.ts
│       ├── errors.ts
│       ├── execution
│       │   └── index.ts
│       ├── FileSystemExecutor.ts
│       ├── log_capture.ts
│       ├── log-capture
│       │   └── index.ts
│       ├── logger.ts
│       ├── logging
│       │   └── index.ts
│       ├── plugin-registry
│       │   └── index.ts
│       ├── responses
│       │   └── index.ts
│       ├── schema-helpers.ts
│       ├── sentry.ts
│       ├── session-store.ts
│       ├── simulator-utils.ts
│       ├── template
│       │   └── index.ts
│       ├── template-manager.ts
│       ├── test
│       │   └── index.ts
│       ├── test-common.ts
│       ├── tool-registry.ts
│       ├── typed-tool-factory.ts
│       ├── validation
│       │   └── index.ts
│       ├── validation.ts
│       ├── version
│       │   └── index.ts
│       ├── video_capture.ts
│       ├── video-capture
│       │   └── index.ts
│       ├── xcode.ts
│       ├── xcodemake
│       │   └── index.ts
│       └── xcodemake.ts
├── tsconfig.json
├── tsconfig.test.json
├── tsup.config.ts
└── vitest.config.ts
```

# Files

--------------------------------------------------------------------------------
/docs/ARCHITECTURE.md:
--------------------------------------------------------------------------------

```markdown
# XcodeBuildMCP Architecture

## Table of Contents

1. [Overview](#overview)
2. [Core Architecture](#core-architecture)
3. [Design Principles](#design-principles)
4. [Component Details](#component-details)
5. [Registration System](#registration-system)
6. [Tool Naming Conventions & Glossary](#tool-naming-conventions--glossary)
7. [Testing Architecture](#testing-architecture)
8. [Build and Deployment](#build-and-deployment)
9. [Extension Guidelines](#extension-guidelines)
10. [Performance Considerations](#performance-considerations)
11. [Security Considerations](#security-considerations)

## Overview

XcodeBuildMCP is a Model Context Protocol (MCP) server that exposes Xcode operations as tools for AI assistants. The architecture emphasizes modularity, type safety, and selective enablement to support diverse development workflows.

### High-Level Objectives

- Expose Xcode-related tools (build, test, deploy, UI automation, etc.) through MCP
- Run as a long-lived stdio-based server for LLM agents, CLIs, or editors
- Enable fine-grained, opt-in activation of individual tools or tool groups
- Support incremental builds via experimental xcodemake with xcodebuild fallback

## Core Architecture

### Runtime Flow

1. **Initialization**
   - The `xcodebuildmcp` executable, as defined in `package.json`, points to the compiled `build/index.js` which executes the main logic from `src/index.ts`.
   - Sentry initialized for error tracking (optional)
   - Version information loaded from `package.json`

2. **Server Creation**
   - MCP server created with stdio transport
   - Plugin discovery system initialized

3. **Plugin Discovery (Build-Time)**
   - A build-time script (`build-plugins/plugin-discovery.ts`) scans the `src/mcp/tools/` and `src/mcp/resources/` directories
   - It generates `src/core/generated-plugins.ts` and `src/core/generated-resources.ts` with dynamic import maps
   - This approach improves startup performance by avoiding synchronous file system scans and enables code-splitting
   - Tool code is only loaded when needed, reducing initial memory footprint

4. **Plugin & Resource Loading (Runtime)**
   - At runtime, `loadPlugins()` and `loadResources()` use the generated loaders from the previous step
   - In **Static Mode**, all workflow loaders are executed at startup to register all tools
   - In **Dynamic Mode**, only the `discover_tools` tool is registered initially
   - The `enableWorkflows` function in `src/core/dynamic-tools.ts` uses generated loaders to dynamically import and register selected workflow tools on demand

5. **Tool Registration**
   - Discovered tools automatically registered with server using pre-generated maps
   - No manual registration or configuration required
   - Environment variables control dynamic tool discovery behavior

5. **Request Handling**
   - MCP client calls tool → server routes to tool handler
   - Zod validates parameters before execution
   - Tool handler uses shared utilities (build, simctl, etc.)
   - Returns standardized `ToolResponse`

6. **Response Streaming**
   - Server streams response back to client
   - Consistent error handling with `isError` flag

## Design Principles

### 1. **Plugin Autonomy**
Tools are self-contained units that export a standardized interface. They don't know about the server implementation, ensuring loose coupling and high testability.

### 2. **Pure Functions vs Stateful Components**
- Most utilities are stateless pure functions
- Stateful components (e.g., process tracking) isolated in specific tool modules
- Clear separation between computation and side effects

### 3. **Single Source of Truth**
- Version from `package.json` drives all version references
- Tool directory structure is authoritative tool source
- Environment variables provide consistent configuration interface

### 4. **Feature Isolation**
- Experimental features behind environment flags
- Optional dependencies (Sentry, xcodemake) gracefully degrade
- Tool directory structure enables workflow-specific organization

### 5. **Type Safety Throughout**
- TypeScript strict mode enabled
- Zod schemas for runtime validation
- Generic type constraints ensure compile-time safety

## Module Organization and Import Strategy

### Focused Facades Pattern

XcodeBuildMCP has migrated from a traditional "barrel file" export pattern (`src/utils/index.ts`) to a more structured **focused facades** pattern. Each distinct area of functionality within `src/utils` is exposed through its own `index.ts` file in a dedicated subdirectory.

**Example Structure:**

```
src/utils/
├── execution/
│   └── index.ts  # Facade for CommandExecutor, FileSystemExecutor
├── logging/
│   └── index.ts  # Facade for the logger
├── responses/
│   └── index.ts  # Facade for error types and response creators
├── validation/
│   └── index.ts  # Facade for validation utilities
├── axe/
│   └── index.ts  # Facade for axe UI automation helpers
├── plugin-registry/
│   └── index.ts  # Facade for plugin system utilities
├── xcodemake/
│   └── index.ts  # Facade for xcodemake utilities
├── template/
│   └── index.ts  # Facade for template management utilities
├── version/
│   └── index.ts  # Facade for version information
├── test/
│   └── index.ts  # Facade for test utilities
├── log-capture/
│   └── index.ts  # Facade for log capture utilities
└── index.ts      # Deprecated barrel file (legacy/external use only)
```

This approach offers several architectural benefits:

- **Clear Dependencies**: It makes the dependency graph explicit. Importing from `utils/execution` clearly indicates a dependency on command execution logic
- **Reduced Coupling**: Modules only import the functionality they need, reducing coupling between unrelated utility components
- **Prevention of Circular Dependencies**: It's much harder to create circular dependencies, which were a risk with the large barrel file
- **Improved Tree-Shaking**: Bundlers can more effectively eliminate unused code
- **Performance**: Eliminates loading of unused modules, reducing startup time and memory usage

### ESLint Enforcement

To maintain this architecture, an ESLint rule in `eslint.config.js` explicitly forbids importing from the deprecated barrel file within the `src/` directory.

**ESLint Rule Snippet** (`eslint.config.js`):

```javascript
'no-restricted-imports': ['error', {
  patterns: [{
    group: ['**/utils/index.js', '../utils/index.js', '../../utils/index.js', '../../../utils/index.js'],
    message: 'Barrel imports from utils/index.js are prohibited. Use focused facade imports instead (e.g., utils/logging/index.js, utils/execution/index.js).'
  }]
}],
```

This rule prevents regression to the previous barrel import pattern and ensures all new code follows the focused facade architecture.

## Component Details

### Entry Points

#### `src/index.ts`
Main server entry point responsible for:
- Sentry initialization (if enabled)
- xcodemake availability check
- Server creation and startup
- Process lifecycle management (SIGTERM, SIGINT)
- Error handling and logging

#### `src/doctor-cli.ts`
Standalone doctor tool for:
- Environment validation
- Dependency checking
- Configuration verification
- Troubleshooting assistance

### Server Layer

#### `src/server/server.ts`
MCP server wrapper providing:
- Server instance creation
- stdio transport configuration
- Request/response handling
- Error boundary implementation

### Tool Discovery System

#### `src/core/plugin-registry.ts`
Runtime plugin loading system that leverages build-time generated code:
- Uses `WORKFLOW_LOADERS` and `WORKFLOW_METADATA` maps from the generated `src/core/generated-plugins.ts` file
- `loadWorkflowGroups()` iterates through the loaders, dynamically importing each workflow module using `await loader()`
- Validates that each imported module contains the required `workflow` metadata export
- Aggregates all tools from the loaded workflows into a single map
- This system eliminates runtime file system scanning, providing significant startup performance boost

#### `src/core/plugin-types.ts`
Plugin type definitions:
- `PluginMeta` interface for plugin structure
- `WorkflowMeta` interface for workflow metadata
- `WorkflowGroup` interface for directory organization

### Tool Implementation

Each tool is implemented in TypeScript and follows a standardized pattern that separates the core business logic from the MCP handler boilerplate. This is achieved using the `createTypedTool` factory, which provides compile-time and runtime type safety.

**Standard Tool Pattern** (`src/mcp/tools/some-workflow/some_tool.ts`):

```typescript
import { z } from 'zod';
import { createTypedTool } from '../../../utils/typed-tool-factory.js';
import type { CommandExecutor } from '../../../utils/execution/index.js';
import { getDefaultCommandExecutor } from '../../../utils/execution/index.js';
import { log } from '../../../utils/logging/index.js';
import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.js';

// 1. Define the Zod schema for parameters
const someToolSchema = z.object({
  requiredParam: z.string().describe('Description for AI'),
  optionalParam: z.boolean().optional().describe('Optional parameter'),
});

// 2. Infer the parameter type from the schema
type SomeToolParams = z.infer<typeof someToolSchema>;

// 3. Implement the core logic in a separate, testable function
// This function receives strongly-typed parameters and an injected executor.
export async function someToolLogic(
  params: SomeToolParams,
  executor: CommandExecutor,
): Promise<ToolResponse> {
  log('info', `Executing some_tool with param: ${params.requiredParam}`);
  
  try {
    const result = await executor(['some', 'command'], 'Some Tool Operation');
    
    if (!result.success) {
      return createErrorResponse('Operation failed', result.error);
    }
    
    return createTextResponse(`✅ Success: ${result.output}`);
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    return createErrorResponse('Tool execution failed', errorMessage);
  }
}

// 4. Export the tool definition for auto-discovery
export default {
  name: 'some_tool',
  description: 'Tool description for AI agents. Example: some_tool({ requiredParam: "value" })',
  schema: someToolSchema.shape, // Expose shape for MCP SDK
  
  // 5. Create the handler using the type-safe factory
  handler: createTypedTool(
    someToolSchema,
    someToolLogic,
    getDefaultCommandExecutor,
  ),
};
```

This pattern ensures that:
- The `someToolLogic` function is highly testable via dependency injection
- Zod handles all runtime parameter validation automatically
- The handler is type-safe, preventing unsafe access to parameters
- Import paths use focused facades for clear dependency management
```

### MCP Resources System

XcodeBuildMCP provides dual interfaces: traditional MCP tools and efficient MCP resources for supported clients. Resources are located in `src/mcp/resources/` and are automatically discovered **at build time**. The build process generates `src/core/generated-resources.ts`, which contains dynamic loaders for each resource, improving startup performance. For more details on creating resources, see the [Plugin Development Guide](docs/PLUGIN_DEVELOPMENT.md).

#### Resource Architecture

```
src/mcp/resources/
├── simulators.ts           # Simulator data resource
└── __tests__/              # Resource-specific tests
```

#### Client Capability Detection

The system automatically detects client MCP capabilities:

```typescript
// src/core/resources.ts
export function supportsResources(server?: unknown): boolean {
  // Detects client capabilities via getClientCapabilities()
  // Conservative fallback: assumes resource support
}
```

#### Resource Implementation Pattern

Resources can reuse existing tool logic for consistency:

```typescript
// src/mcp/resources/some_resource.ts
import { log } from '../../utils/logging/index.js';
import { getDefaultCommandExecutor, CommandExecutor } from '../../utils/execution/index.js';
import { getSomeResourceLogic } from '../tools/some-workflow/get_some_resource.js';

// Testable resource logic separated from MCP handler
export async function someResourceResourceLogic(
  executor: CommandExecutor = getDefaultCommandExecutor(),
): Promise<{ contents: Array<{ text: string }> }> {
  try {
    log('info', 'Processing some resource request');

    const result = await getSomeResourceLogic({}, executor);

    if (result.isError) {
      const errorText = result.content[0]?.text;
      throw new Error(
        typeof errorText === 'string' ? errorText : 'Failed to retrieve some resource data',
      );
    }

    return {
      contents: [
        {
          text:
            typeof result.content[0]?.text === 'string'
              ? result.content[0].text
              : 'No data for that resource is available',
        },
      ],
    };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    log('error', `Error in some_resource resource handler: ${errorMessage}`);

    return {
      contents: [
        {
          text: `Error retrieving resource data: ${errorMessage}`,
        },
      ],
    };
  }
}

export default {
  uri: 'xcodebuildmcp://some_resource',
  name: 'some_resource',
  description: 'Returns some resource information',
  mimeType: 'text/plain',
  async handler(_uri: URL): Promise<{ contents: Array<{ text: string }> }> {
    return someResourceResourceLogic();
  },
};
```

## Registration System

XcodeBuildMCP supports two primary operating modes for tool registration, controlled by the `XCODEBUILDMCP_DYNAMIC_TOOLS` environment variable.

### Static Mode (Default)

- **Environment**: `XCODEBUILDMCP_DYNAMIC_TOOLS` is `false` or not set.
- **Behavior**: All available tools are loaded and registered with the MCP server at startup.
- **Use Case**: This mode is ideal for environments where the full suite of tools is desired immediately, providing a comprehensive and predictable toolset for the AI assistant.

### Dynamic Mode (AI-Powered Workflow Selection)

- **Environment**: `XCODEBUILDMCP_DYNAMIC_TOOLS=true`
- **Behavior**: At startup, only the `discover_tools` tool is registered. This tool is designed to analyze a natural language task description from the user.
- **Workflow**:
    1. The client sends a task description (e.g., "I want to build and test my iOS app") to the `discover_tools` tool.
    2. The tool uses the client's LLM via an MCP sampling request to determine the most relevant workflow group (e.g., `simulator-workspace`).
    3. The server then dynamically loads and registers all tools from the selected workflow group.
    4. The client is notified of the newly available tools.
- **Use Case**: This mode is beneficial for conserving the LLM's context window by only loading a relevant subset of tools, leading to more focused and efficient interactions.

## Tool Naming Conventions & Glossary

Tools follow a consistent naming pattern to ensure predictability and clarity. Understanding this convention is crucial for both using and developing tools.

### Naming Pattern

The standard naming convention for tools is:

`{action}_{target}_{specifier}_{projectType}`

- **action**: The primary verb describing the tool's function (e.g., `build`, `test`, `get`, `list`).
- **target**: The main subject of the action (e.g., `sim` for simulator, `dev` for device, `mac` for macOS).
- **specifier**: A variant that specifies *how* the target is identified (e.g., `id` for UUID, `name` for by-name).
- **projectType**: The type of Xcode project the tool operates on (e.g., `ws` for workspace, `proj` for project).

Not all parts are required for every tool. For example, `swift_package_build` has an action and a target, but no specifier or project type.

### Examples

- `build_sim_id_ws`: **Build** for a **simulator** identified by its **ID (UUID)** from a **workspace**.
- `test_dev_proj`: **Test** on a **device** from a **project**.
- `get_mac_app_path_ws`: **Get** the app path for a **macOS** application from a **workspace**.
- `list_sims`: **List** all **simulators**.

### Glossary

| Term/Abbreviation | Meaning | Description |
|---|---|---|
| `ws` | Workspace | Refers to an `.xcworkspace` file. Used for projects with multiple `.xcodeproj` files or dependencies managed by CocoaPods or SPM. |
| `proj` | Project | Refers to an `.xcodeproj` file. Used for single-project setups. |
| `sim` | Simulator | Refers to the iOS, watchOS, tvOS, or visionOS simulator. |
| `dev` | Device | Refers to a physical Apple device (iPhone, iPad, etc.). |
| `mac` | macOS | Refers to a native macOS application target. |
| `id` | Identifier | Refers to the unique identifier (UUID/UDID) of a simulator or device. |
| `name` | Name | Refers to the human-readable name of a simulator (e.g., "iPhone 15 Pro"). |
| `cap` | Capture | Used in logging tools, e.g., `start_sim_log_cap`. |

## Testing Architecture

### Framework and Configuration

- **Test Runner**: Vitest 3.x
- **Environment**: Node.js
- **Configuration**: `vitest.config.ts`
- **Test Pattern**: `*.test.ts` files alongside implementation

### Testing Principles

XcodeBuildMCP uses a strict **Dependency Injection (DI)** pattern for testing, which completely bans the use of traditional mocking libraries like Vitest's `vi.mock` or `vi.fn`. This ensures that tests are robust, maintainable, and verify the actual integration between components.

For detailed guidelines, see the [Testing Guide](docs/TESTING.md).

### Test Structure Example

Tests inject mock "executors" for external interactions like command-line execution or file system access. This allows for deterministic testing of tool logic without mocking the implementation itself. The project provides helper functions like `createMockExecutor` and `createMockFileSystemExecutor` in `src/test-utils/mock-executors.ts` to facilitate this pattern.

```typescript
import { describe, it, expect } from 'vitest';
import { someToolLogic } from '../tool-file.js'; // Import the logic function
import { createMockExecutor } from '../../../test-utils/mock-executors.js';

describe('Tool Name', () => {
  it('should execute successfully', async () => {
    // 1. Create a mock executor to simulate command-line results
    const mockExecutor = createMockExecutor({
      success: true,
      output: 'Command output'
    });

    // 2. Call the tool's logic function, injecting the mock executor
    const result = await someToolLogic({ requiredParam: 'value' }, mockExecutor);
    
    // 3. Assert the final result
    expect(result).toEqual({
      content: [{ type: 'text', text: 'Expected output' }],
      isError: false
    });
  });
});
```

## Build and Deployment

### Build Process

1. **Version Generation**
   ```bash
   npm run build
   ```
   - Reads version from `package.json`
   - Generates `src/version.ts`

2. **Plugin & Resource Loader Generation**
   - The `build-plugins/plugin-discovery.ts` script is executed
   - It scans `src/mcp/tools/` and `src/mcp/resources/` to find all workflows and resources
   - It generates `src/core/generated-plugins.ts` and `src/core/generated-resources.ts` with dynamic import maps
   - This eliminates runtime file system scanning and enables code-splitting

3. **TypeScript Compilation**
   - `tsup` compiles the TypeScript source, including the newly generated files, into JavaScript
   - Compiles TypeScript with tsup

4. **Build Configuration** (`tsup.config.ts`)
   - Entry points: `index.ts`, `doctor-cli.ts`
   - Output format: ESM
   - Target: Node 18+
   - Source maps enabled

5. **Distribution Structure**
   ```
   build/
   ├── index.js          # Main server executable
   ├── doctor-cli.js # Doctor tool
   └── *.js.map         # Source maps
   ```

### npm Package

- **Name**: `xcodebuildmcp`
- **Executables**:
  - `xcodebuildmcp` → Main server
  - `xcodebuildmcp-doctor` → Doctor tool
- **Dependencies**: Minimal runtime dependencies
- **Platform**: macOS only (due to Xcode requirement)

### Bundled Resources

```
bundled/
├── axe              # UI automation binary
└── Frameworks/      # Facebook device frameworks
    ├── FBControlCore.framework
    ├── FBDeviceControl.framework
    └── FBSimulatorControl.framework
```

## Extension Guidelines

This project is designed to be extensible. For comprehensive instructions on creating new tools, workflow groups, and resources, please refer to the dedicated [**Plugin Development Guide**](docs/PLUGIN_DEVELOPMENT.md).

The guide covers:
- The auto-discovery system architecture.
- The dependency injection pattern required for all new tools.
- How to organize tools into workflow groups.
- Testing guidelines and patterns.

## Performance Considerations

### Startup Performance

- **Build-Time Plugin Discovery**: The server avoids expensive and slow file system scans at startup by using pre-generated loader maps. This is the single most significant performance optimization
- **Code-Splitting**: In Dynamic Mode, tool code is only loaded into memory when its workflow is enabled, reducing the initial memory footprint and parse time
- **Focused Facades**: Using targeted imports instead of a large barrel file improves module resolution speed for the Node.js runtime
- **Lazy Loading**: Tools only initialized when registered
- **Selective Registration**: Fewer tools = faster startup
- **Minimal Dependencies**: Fast module resolution

### Runtime Performance

- **Stateless Operations**: Most tools complete quickly
- **Process Management**: Long-running processes tracked separately
- **Incremental Builds**: xcodemake provides significant speedup
- **Parallel Execution**: Tools can run concurrently

### Memory Management

- **Process Cleanup**: Proper process termination handling
- **Log Rotation**: Captured logs have size limits
- **Resource Disposal**: Explicit cleanup in lifecycle hooks

### Optimization Strategies

1. **Use Tool Groups**: Enable only needed workflows
2. **Enable Incremental Builds**: Set `INCREMENTAL_BUILDS_ENABLED=true`
3. **Limit Log Capture**: Use structured logging when possible

## Security Considerations

### Input Validation

- All tool inputs validated with Zod schemas
- Command injection prevented via proper escaping
- Path traversal protection in file operations

### Process Isolation

- Tools run with user permissions
- No privilege escalation
- Sandboxed execution environment

### Error Handling

- Sensitive information scrubbed from errors
- Stack traces limited to application code
- Sentry integration respects privacy settings

```

--------------------------------------------------------------------------------
/src/mcp/tools/project-scaffolding/__tests__/scaffold_ios_project.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Vitest test for scaffold_ios_project plugin
 *
 * Tests the plugin structure and iOS scaffold tool functionality
 * including parameter validation, file operations, template processing, and response formatting.
 *
 * Plugin location: plugins/utilities/scaffold_ios_project.js
 */

import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { z } from 'zod';
import scaffoldIosProject, { scaffold_ios_projectLogic } from '../scaffold_ios_project.ts';
import {
  createMockExecutor,
  createMockFileSystemExecutor,
} from '../../../../test-utils/mock-executors.ts';

describe('scaffold_ios_project plugin', () => {
  let mockCommandExecutor: any;
  let mockFileSystemExecutor: any;
  let originalEnv: string | undefined;

  beforeEach(() => {
    // Create mock executor using approved utility
    mockCommandExecutor = createMockExecutor({
      success: true,
      output: 'Command executed successfully',
    });

    mockFileSystemExecutor = createMockFileSystemExecutor({
      existsSync: (path) => {
        // Mock template directories exist but project files don't
        return (
          path.includes('xcodebuild-mcp-template') ||
          path.includes('XcodeBuildMCP-iOS-Template') ||
          path.includes('/template') ||
          path.endsWith('template') ||
          path.includes('extracted') ||
          path.includes('/mock/template/path')
        );
      },
      readFile: async () => 'template content with MyProject placeholder',
      readdir: async () => [
        { name: 'Package.swift', isDirectory: () => false, isFile: () => true } as any,
        { name: 'MyProject.swift', isDirectory: () => false, isFile: () => true } as any,
      ],
      mkdir: async () => {},
      rm: async () => {},
      cp: async () => {},
      writeFile: async () => {},
      stat: async () => ({ isDirectory: () => true }),
    });

    // Store original environment for cleanup
    originalEnv = process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH;
    // Set local template path to avoid download and chdir issues
    process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = '/mock/template/path';
  });

  afterEach(() => {
    // Restore original environment
    if (originalEnv !== undefined) {
      process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = originalEnv;
    } else {
      delete process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH;
    }
  });

  describe('Export Field Validation (Literal)', () => {
    it('should have correct name field', () => {
      expect(scaffoldIosProject.name).toBe('scaffold_ios_project');
    });

    it('should have correct description field', () => {
      expect(scaffoldIosProject.description).toBe(
        'Scaffold a new iOS project from templates. Creates a modern Xcode project with workspace structure, SPM package for features, and proper iOS configuration.',
      );
    });

    it('should have handler as function', () => {
      expect(typeof scaffoldIosProject.handler).toBe('function');
    });

    it('should have valid schema with required fields', () => {
      const schema = z.object(scaffoldIosProject.schema);

      // Test valid input
      expect(
        schema.safeParse({
          projectName: 'MyTestApp',
          outputPath: '/path/to/output',
          bundleIdentifier: 'com.test.myapp',
          displayName: 'My Test App',
          marketingVersion: '1.0',
          currentProjectVersion: '1',
          customizeNames: true,
          deploymentTarget: '18.4',
          targetedDeviceFamily: ['iphone', 'ipad'],
          supportedOrientations: ['portrait', 'landscape-left'],
          supportedOrientationsIpad: ['portrait', 'landscape-left', 'landscape-right'],
        }).success,
      ).toBe(true);

      // Test minimal valid input
      expect(
        schema.safeParse({
          projectName: 'MyTestApp',
          outputPath: '/path/to/output',
        }).success,
      ).toBe(true);

      // Test invalid input - missing projectName
      expect(
        schema.safeParse({
          outputPath: '/path/to/output',
        }).success,
      ).toBe(false);

      // Test invalid input - missing outputPath
      expect(
        schema.safeParse({
          projectName: 'MyTestApp',
        }).success,
      ).toBe(false);

      // Test invalid input - wrong type for customizeNames
      expect(
        schema.safeParse({
          projectName: 'MyTestApp',
          outputPath: '/path/to/output',
          customizeNames: 'true',
        }).success,
      ).toBe(false);

      // Test invalid input - wrong enum value for targetedDeviceFamily
      expect(
        schema.safeParse({
          projectName: 'MyTestApp',
          outputPath: '/path/to/output',
          targetedDeviceFamily: ['invalid-device'],
        }).success,
      ).toBe(false);

      // Test invalid input - wrong enum value for supportedOrientations
      expect(
        schema.safeParse({
          projectName: 'MyTestApp',
          outputPath: '/path/to/output',
          supportedOrientations: ['invalid-orientation'],
        }).success,
      ).toBe(false);
    });
  });

  describe('Command Generation Tests', () => {
    it('should generate correct curl command for iOS template download', async () => {
      // Temporarily disable local template to force download
      delete process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH;

      // Track commands executed
      let capturedCommands: string[][] = [];
      const trackingCommandExecutor = createMockExecutor({
        success: true,
        output: 'Command executed successfully',
      });
      // Wrap to capture commands
      const capturingExecutor = async (command: string[], ...args: any[]) => {
        capturedCommands.push(command);
        return trackingCommandExecutor(command, ...args);
      };

      await scaffold_ios_projectLogic(
        {
          projectName: 'TestIOSApp',
          outputPath: '/tmp/test-projects',
        },
        capturingExecutor,
        mockFileSystemExecutor,
      );

      // Verify curl command was executed
      const curlCommand = capturedCommands.find((cmd) => cmd.includes('curl'));
      expect(curlCommand).toBeDefined();
      expect(curlCommand).toEqual([
        'curl',
        '-L',
        '-f',
        '-o',
        expect.stringMatching(/template\.zip$/),
        expect.stringMatching(
          /https:\/\/github\.com\/cameroncooke\/XcodeBuildMCP-iOS-Template\/releases\/download\/v\d+\.\d+\.\d+\/XcodeBuildMCP-iOS-Template-\d+\.\d+\.\d+\.zip/,
        ),
      ]);

      // Restore environment variable
      process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = '/mock/template/path';
    });

    it.skip('should generate correct unzip command for iOS template extraction', async () => {
      // Temporarily disable local template to force download
      delete process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH;

      // Create a mock that returns false for local template paths to force download
      const downloadMockFileSystemExecutor = createMockFileSystemExecutor({
        existsSync: (path) => {
          // Only return true for extracted template directories, false for local template paths
          return (
            path.includes('xcodebuild-mcp-template') ||
            path.includes('XcodeBuildMCP-iOS-Template') ||
            path.includes('extracted')
          );
        },
        readFile: async () => 'template content with MyProject placeholder',
        readdir: async () => [
          { name: 'Package.swift', isDirectory: () => false, isFile: () => true } as any,
          { name: 'MyProject.swift', isDirectory: () => false, isFile: () => true } as any,
        ],
        mkdir: async () => {},
        rm: async () => {},
        cp: async () => {},
        writeFile: async () => {},
        stat: async () => ({ isDirectory: () => true }),
      });

      // Track commands executed
      let capturedCommands: string[][] = [];
      const trackingCommandExecutor = createMockExecutor({
        success: true,
        output: 'Command executed successfully',
      });
      // Wrap to capture commands
      const capturingExecutor = async (command: string[], ...args: any[]) => {
        capturedCommands.push(command);
        return trackingCommandExecutor(command, ...args);
      };

      await scaffold_ios_projectLogic(
        {
          projectName: 'TestIOSApp',
          outputPath: '/tmp/test-projects',
        },
        capturingExecutor,
        downloadMockFileSystemExecutor,
      );

      // Verify unzip command was executed
      const unzipCommand = capturedCommands.find((cmd) => cmd.includes('unzip'));
      expect(unzipCommand).toBeDefined();
      expect(unzipCommand).toEqual(['unzip', '-q', expect.stringMatching(/template\.zip$/)]);

      // Restore environment variable
      process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = '/mock/template/path';
    });

    it('should generate correct commands when using custom template version', async () => {
      // Temporarily disable local template to force download
      delete process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH;

      // Set custom template version
      const originalVersion = process.env.XCODEBUILD_MCP_IOS_TEMPLATE_VERSION;
      process.env.XCODEBUILD_MCP_IOS_TEMPLATE_VERSION = 'v2.0.0';

      // Track commands executed
      let capturedCommands: string[][] = [];
      const trackingCommandExecutor = createMockExecutor({
        success: true,
        output: 'Command executed successfully',
      });
      // Wrap to capture commands
      const capturingExecutor = async (command: string[], ...args: any[]) => {
        capturedCommands.push(command);
        return trackingCommandExecutor(command, ...args);
      };

      await scaffold_ios_projectLogic(
        {
          projectName: 'TestIOSApp',
          outputPath: '/tmp/test-projects',
        },
        capturingExecutor,
        mockFileSystemExecutor,
      );

      // Verify curl command uses custom version
      const curlCommand = capturedCommands.find((cmd) => cmd.includes('curl'));
      expect(curlCommand).toBeDefined();
      expect(curlCommand).toEqual([
        'curl',
        '-L',
        '-f',
        '-o',
        expect.stringMatching(/template\.zip$/),
        'https://github.com/cameroncooke/XcodeBuildMCP-iOS-Template/releases/download/v2.0.0/XcodeBuildMCP-iOS-Template-2.0.0.zip',
      ]);

      // Restore original version
      if (originalVersion) {
        process.env.XCODEBUILD_MCP_IOS_TEMPLATE_VERSION = originalVersion;
      } else {
        delete process.env.XCODEBUILD_MCP_IOS_TEMPLATE_VERSION;
      }

      // Restore environment variable
      process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = '/mock/template/path';
    });

    it.skip('should generate correct commands with no command executor passed', async () => {
      // Temporarily disable local template to force download
      delete process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH;

      // Create a mock that returns false for local template paths to force download
      const downloadMockFileSystemExecutor = createMockFileSystemExecutor({
        existsSync: (path) => {
          // Only return true for extracted template directories, false for local template paths
          return (
            path.includes('xcodebuild-mcp-template') ||
            path.includes('XcodeBuildMCP-iOS-Template') ||
            path.includes('extracted')
          );
        },
        readFile: async () => 'template content with MyProject placeholder',
        readdir: async () => [
          { name: 'Package.swift', isDirectory: () => false, isFile: () => true } as any,
          { name: 'MyProject.swift', isDirectory: () => false, isFile: () => true } as any,
        ],
        mkdir: async () => {},
        rm: async () => {},
        cp: async () => {},
        writeFile: async () => {},
        stat: async () => ({ isDirectory: () => true }),
      });

      // Track commands executed - using default executor path
      let capturedCommands: string[][] = [];
      const trackingCommandExecutor = createMockExecutor({
        success: true,
        output: 'Command executed successfully',
      });
      // Wrap to capture commands
      const capturingExecutor = async (command: string[], ...args: any[]) => {
        capturedCommands.push(command);
        return trackingCommandExecutor(command, ...args);
      };

      await scaffold_ios_projectLogic(
        {
          projectName: 'TestIOSApp',
          outputPath: '/tmp/test-projects',
        },
        capturingExecutor,
        downloadMockFileSystemExecutor,
      );

      // Verify both curl and unzip commands were executed in sequence
      expect(capturedCommands.length).toBeGreaterThanOrEqual(2);

      const curlCommand = capturedCommands.find((cmd) => cmd.includes('curl'));
      const unzipCommand = capturedCommands.find((cmd) => cmd.includes('unzip'));

      expect(curlCommand).toBeDefined();
      expect(unzipCommand).toBeDefined();
      expect(curlCommand[0]).toBe('curl');
      expect(unzipCommand[0]).toBe('unzip');

      // Restore environment variable
      process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = '/mock/template/path';
    });
  });

  describe('Handler Behavior (Complete Literal Returns)', () => {
    it('should return success response for valid scaffold iOS project request', async () => {
      const result = await scaffold_ios_projectLogic(
        {
          projectName: 'TestIOSApp',
          outputPath: '/tmp/test-projects',
          bundleIdentifier: 'com.test.iosapp',
        },
        mockCommandExecutor,
        mockFileSystemExecutor,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: JSON.stringify(
              {
                success: true,
                projectPath: '/tmp/test-projects',
                platform: 'iOS',
                message: 'Successfully scaffolded iOS project "TestIOSApp" in /tmp/test-projects',
                nextSteps: [
                  'Important: Before working on the project make sure to read the README.md file in the workspace root directory.',
                  'Build for simulator: build_sim({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject", simulatorName: "iPhone 16" })',
                  'Build and run on simulator: build_run_sim({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject", simulatorName: "iPhone 16" })',
                ],
              },
              null,
              2,
            ),
          },
        ],
      });
    });

    it('should return success response with all optional parameters', async () => {
      const result = await scaffold_ios_projectLogic(
        {
          projectName: 'TestIOSApp',
          outputPath: '/tmp/test-projects',
          bundleIdentifier: 'com.test.iosapp',
          displayName: 'Test iOS App',
          marketingVersion: '2.0',
          currentProjectVersion: '5',
          customizeNames: true,
          deploymentTarget: '17.0',
          targetedDeviceFamily: ['iphone'],
          supportedOrientations: ['portrait'],
          supportedOrientationsIpad: ['portrait', 'landscape-left'],
        },
        mockCommandExecutor,
        mockFileSystemExecutor,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: JSON.stringify(
              {
                success: true,
                projectPath: '/tmp/test-projects',
                platform: 'iOS',
                message: 'Successfully scaffolded iOS project "TestIOSApp" in /tmp/test-projects',
                nextSteps: [
                  'Important: Before working on the project make sure to read the README.md file in the workspace root directory.',
                  'Build for simulator: build_sim({ workspacePath: "/tmp/test-projects/TestIOSApp.xcworkspace", scheme: "TestIOSApp", simulatorName: "iPhone 16" })',
                  'Build and run on simulator: build_run_sim({ workspacePath: "/tmp/test-projects/TestIOSApp.xcworkspace", scheme: "TestIOSApp", simulatorName: "iPhone 16" })',
                ],
              },
              null,
              2,
            ),
          },
        ],
      });
    });

    it('should return success response with customizeNames false', async () => {
      const result = await scaffold_ios_projectLogic(
        {
          projectName: 'TestIOSApp',
          outputPath: '/tmp/test-projects',
          customizeNames: false,
        },
        mockCommandExecutor,
        mockFileSystemExecutor,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: JSON.stringify(
              {
                success: true,
                projectPath: '/tmp/test-projects',
                platform: 'iOS',
                message: 'Successfully scaffolded iOS project "TestIOSApp" in /tmp/test-projects',
                nextSteps: [
                  'Important: Before working on the project make sure to read the README.md file in the workspace root directory.',
                  'Build for simulator: build_sim({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject", simulatorName: "iPhone 16" })',
                  'Build and run on simulator: build_run_sim({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject", simulatorName: "iPhone 16" })',
                ],
              },
              null,
              2,
            ),
          },
        ],
      });
    });

    it('should return error response for invalid project name', async () => {
      const result = await scaffold_ios_projectLogic(
        {
          projectName: '123InvalidName',
          outputPath: '/tmp/test-projects',
        },
        mockCommandExecutor,
        mockFileSystemExecutor,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: JSON.stringify(
              {
                success: false,
                error:
                  'Project name must start with a letter and contain only letters, numbers, and underscores',
              },
              null,
              2,
            ),
          },
        ],
        isError: true,
      });
    });

    it('should return error response for existing project files', async () => {
      // Update mock to return true for existing files
      mockFileSystemExecutor = createMockFileSystemExecutor({
        existsSync: () => true,
        readFile: async () => 'template content with MyProject placeholder',
        readdir: async () => [
          { name: 'Package.swift', isDirectory: () => false, isFile: () => true } as any,
          { name: 'MyProject.swift', isDirectory: () => false, isFile: () => true } as any,
        ],
      });

      const result = await scaffold_ios_projectLogic(
        {
          projectName: 'TestIOSApp',
          outputPath: '/tmp/test-projects',
        },
        mockCommandExecutor,
        mockFileSystemExecutor,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: JSON.stringify(
              {
                success: false,
                error: 'Xcode project files already exist in /tmp/test-projects',
              },
              null,
              2,
            ),
          },
        ],
        isError: true,
      });
    });

    it('should return error response for template download failure', async () => {
      // Temporarily disable local template to force download
      delete process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH;

      // Mock command executor to fail for curl commands
      const failingMockCommandExecutor = createMockExecutor({
        success: false,
        output: '',
        error: 'Template download failed',
      });

      const result = await scaffold_ios_projectLogic(
        {
          projectName: 'TestIOSApp',
          outputPath: '/tmp/test-projects',
        },
        failingMockCommandExecutor,
        mockFileSystemExecutor,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: JSON.stringify(
              {
                success: false,
                error:
                  'Failed to get template for iOS: Failed to download template: Template download failed',
              },
              null,
              2,
            ),
          },
        ],
        isError: true,
      });

      // Restore environment variable
      process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = '/mock/template/path';
    });

    it.skip('should return error response for template extraction failure', async () => {
      // Temporarily disable local template to force download
      delete process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH;

      // Create a mock that returns false for local template paths to force download
      const downloadMockFileSystemExecutor = createMockFileSystemExecutor({
        existsSync: (path) => {
          // Only return true for extracted template directories, false for local template paths
          return (
            path.includes('xcodebuild-mcp-template') ||
            path.includes('XcodeBuildMCP-iOS-Template') ||
            path.includes('extracted')
          );
        },
        readFile: async () => 'template content with MyProject placeholder',
        readdir: async () => [
          { name: 'Package.swift', isDirectory: () => false, isFile: () => true } as any,
          { name: 'MyProject.swift', isDirectory: () => false, isFile: () => true } as any,
        ],
        mkdir: async () => {},
        rm: async () => {},
        cp: async () => {},
        writeFile: async () => {},
        stat: async () => ({ isDirectory: () => true }),
      });

      // Mock command executor to fail for unzip commands
      const failingMockCommandExecutor = createMockExecutor({
        success: false,
        output: '',
        error: 'Extraction failed',
      });

      const result = await scaffold_ios_projectLogic(
        {
          projectName: 'TestIOSApp',
          outputPath: '/tmp/test-projects',
        },
        failingMockCommandExecutor,
        downloadMockFileSystemExecutor,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: JSON.stringify(
              {
                success: false,
                error:
                  'Failed to get template for iOS: Failed to extract template: Extraction failed',
              },
              null,
              2,
            ),
          },
        ],
        isError: true,
      });

      // Restore environment variable
      process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = '/mock/template/path';
    });
  });
});

```

--------------------------------------------------------------------------------
/src/mcp/tools/discovery/__tests__/discover_tools.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for discover_tools plugin
 * Following CLAUDE.md testing standards with literal validation
 * Using dependency injection for deterministic testing
 */

import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { z } from 'zod';
import discoverTools, { discover_toolsLogic } from '../discover_tools.ts';

// Mock dependencies interface for dependency injection
interface MockDependencies {
  getAvailableWorkflows?: () => string[];
  generateWorkflowDescriptions?: () => string;
  enableWorkflows?: (server: any, workflows: string[], additive?: boolean) => Promise<void>;
}

// Track function calls manually for verification
interface CallTracker {
  getAvailableWorkflowsCalls: Array<any[]>;
  generateWorkflowDescriptionsCalls: Array<any[]>;
  enableWorkflowsCalls: Array<any[]>;
}

function createMockDependencies(
  config: {
    availableWorkflows?: string[];
    workflowDescriptions?: string;
    enableWorkflowsError?: Error;
    getAvailableWorkflowsError?: Error;
  },
  callTracker: CallTracker,
): MockDependencies {
  const workflowNames = config.availableWorkflows ?? ['simulator-workspace'];
  const descriptions =
    config.workflowDescriptions ??
    `Available workflows:
1. simulator-workspace: iOS Simulator Workspace - iOS development for workspaces`;

  return {
    getAvailableWorkflows: config.getAvailableWorkflowsError
      ? () => {
          callTracker.getAvailableWorkflowsCalls.push([]);
          throw config.getAvailableWorkflowsError;
        }
      : () => {
          callTracker.getAvailableWorkflowsCalls.push([]);
          return workflowNames;
        },
    generateWorkflowDescriptions: () => {
      callTracker.generateWorkflowDescriptionsCalls.push([]);
      return descriptions;
    },
    enableWorkflows: config.enableWorkflowsError
      ? async (server: any, workflows: string[], additive?: boolean) => {
          callTracker.enableWorkflowsCalls.push([server, workflows, additive]);
          throw config.enableWorkflowsError;
        }
      : async (server: any, workflows: string[], additive?: boolean) => {
          callTracker.enableWorkflowsCalls.push([server, workflows, additive]);
          return undefined;
        },
  };
}

describe('discover_tools', () => {
  let mockServer: Record<string, unknown>;
  let originalGlobalThis: Record<string, unknown>;
  let callTracker: CallTracker;
  let requestCalls: Array<any[]>;
  let sendToolListChangedCalls: Array<any[]>;

  beforeEach(() => {
    // Save original globalThis
    originalGlobalThis = globalThis.mcpServer;
    // Initialize call trackers
    callTracker = {
      getAvailableWorkflowsCalls: [],
      generateWorkflowDescriptionsCalls: [],
      enableWorkflowsCalls: [],
    };
    requestCalls = [];
    sendToolListChangedCalls = [];
    // Create mock server
    mockServer = {
      server: {
        _clientCapabilities: { sampling: true },
        getClientCapabilities: () => ({ sampling: true }),
        createMessage: async (...args: any[]) => {
          requestCalls.push(args);
          throw new Error('Mock createMessage not configured');
        },
        request: async (...args: any[]) => {
          requestCalls.push(args);
          throw new Error('Mock request not configured');
        },
      },
      sendToolListChanged: (...args: any[]) => {
        sendToolListChangedCalls.push(args);
      },
    };
    // Set up global server
    (globalThis as any).mcpServer = mockServer;
    // Reset all mocks
  });

  afterEach(() => {
    // Restore original globalThis
    globalThis.mcpServer = originalGlobalThis;
  });

  describe('Export Field Validation (Literal)', () => {
    it('should have correct name', () => {
      expect(discoverTools.name).toBe('discover_tools');
    });

    it('should have correct description', () => {
      expect(discoverTools.description).toBe(
        'Analyzes a natural language task description and enables the most relevant development workflow. Prioritizes project/workspace workflows (simulator/device/macOS) and also supports task-based workflows (simulator-management, logging) and Swift packages.',
      );
    });

    it('should have handler function', () => {
      expect(typeof discover_toolsLogic).toBe('function');
    });

    it('should have correct schema with task_description string field', () => {
      const schema = z.object(discoverTools.schema);

      // Valid inputs
      expect(schema.safeParse({ task_description: 'Build my iOS app' }).success).toBe(true);
      expect(schema.safeParse({ task_description: 'Test my React Native app' }).success).toBe(true);

      // Invalid inputs
      expect(schema.safeParse({ task_description: 123 }).success).toBe(false);
      expect(schema.safeParse({ task_description: null }).success).toBe(false);
      expect(schema.safeParse({ task_description: undefined }).success).toBe(false);
      expect(schema.safeParse({}).success).toBe(false);
    });
  });

  describe('Capability Detection', () => {
    it('should return error when client lacks sampling capability', async () => {
      // Mock server without sampling capability
      mockServer.server._clientCapabilities = {};
      (mockServer.server as any).getClientCapabilities = () => ({});

      const result = await discover_toolsLogic({ task_description: 'Build my app' }, undefined);

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Your client does not support the sampling feature required for dynamic tool discovery. Please use XCODEBUILDMCP_DYNAMIC_TOOLS=false to use the standard tool set.',
          },
        ],
        isError: true,
      });
    });

    it('should proceed when client has sampling capability', async () => {
      const mockDeps = createMockDependencies(
        {
          availableWorkflows: ['simulator-workspace'],
          workflowDescriptions: `Available workflows:
1. simulator-workspace: iOS Simulator Workspace - iOS development for workspaces`,
        },
        callTracker,
      );

      // Configure mock request to return successful response
      (mockServer.server as any).createMessage = async (...args: any[]) => {
        requestCalls.push(args);
        return {
          content: [{ type: 'text', text: '["simulator-workspace"]' }],
        };
      };

      const result = await discover_toolsLogic(
        { task_description: 'Build my iOS app' },
        undefined,
        mockDeps,
      );

      expect(result.isError).toBeFalsy();
      expect(callTracker.getAvailableWorkflowsCalls).toHaveLength(1);
    });
  });

  describe('Workflow Loading', () => {
    it('should load workflow groups and build descriptions', async () => {
      const mockDeps = createMockDependencies(
        {
          availableWorkflows: ['simulator-workspace', 'macos-project'],
          workflowDescriptions: `Available workflows:
1. simulator-workspace: iOS Simulator Workspace - Complete iOS development workflow for .xcworkspace files targeting simulators
2. macos-project: macOS Project - Complete macOS development workflow for .xcodeproj files`,
        },
        callTracker,
      );

      // Configure mock request to capture calls and return response
      (mockServer.server as any).createMessage = async (...args: any[]) => {
        requestCalls.push(args);
        return {
          content: [{ type: 'text', text: '["simulator-workspace"]' }],
        };
      };

      await discover_toolsLogic({ task_description: 'Build my iOS app' }, undefined, mockDeps);

      // Verify workflow groups were loaded
      expect(callTracker.getAvailableWorkflowsCalls).toHaveLength(1);

      // Verify LLM prompt includes workflow descriptions
      expect(requestCalls).toHaveLength(1);
      const requestCall = requestCalls[0];
      const prompt = requestCall[0].messages[0].content.text;

      expect(prompt).toContain('simulator-workspace');
      expect(prompt).toContain(
        'Complete iOS development workflow for .xcworkspace files targeting simulators',
      );
      expect(prompt).toContain('macos-project');
      expect(prompt).toContain('Complete macOS development workflow for .xcodeproj files');
    });
  });

  describe('LLM Interaction', () => {
    let mockDeps: MockDependencies;
    let localCallTracker: CallTracker;

    beforeEach(() => {
      // Reset local call tracker for this describe block
      localCallTracker = {
        getAvailableWorkflowsCalls: [],
        generateWorkflowDescriptionsCalls: [],
        enableWorkflowsCalls: [],
      };
      mockDeps = createMockDependencies(
        {
          availableWorkflows: ['simulator-workspace'],
          workflowDescriptions: `Available workflows:
1. simulator-workspace: iOS Simulator Workspace - iOS development for workspaces`,
        },
        localCallTracker,
      );
    });

    it('should send correct sampling request to LLM', async () => {
      // Reset request calls for this test
      requestCalls.length = 0;

      // Configure mock request to capture calls and return response
      (mockServer.server as any).createMessage = async (...args: any[]) => {
        requestCalls.push(args);
        return {
          content: [{ type: 'text', text: '["simulator-workspace"]' }],
        };
      };

      await discover_toolsLogic(
        { task_description: 'Build my iOS app and test it' },
        undefined,
        mockDeps,
      );

      expect(requestCalls).toHaveLength(1);
      const requestCall = requestCalls[0];
      expect(requestCall[0]).toEqual({
        messages: [
          {
            role: 'user',
            content: {
              type: 'text',
              text: expect.stringContaining('Build my iOS app and test it'),
            },
          },
        ],
        maxTokens: 200,
      });
      // Note: Schema parameter was removed in TypeScript fix - request method now only accepts one parameter
    });

    it('should handle array content format in LLM response', async () => {
      // Reset call trackers for this test
      localCallTracker.enableWorkflowsCalls.length = 0;

      // Configure mock request to return response
      (mockServer.server as any).createMessage = async (...args: any[]) => {
        return {
          content: [{ type: 'text', text: '["simulator-workspace"]' }],
        };
      };

      const result = await discover_toolsLogic(
        { task_description: 'Build my app' },
        undefined,
        mockDeps,
      );

      expect(result.isError).toBeFalsy();
      expect(localCallTracker.enableWorkflowsCalls).toHaveLength(1);
      expect(localCallTracker.enableWorkflowsCalls[0]).toEqual([
        mockServer,
        ['simulator-workspace'],
        false,
      ]);
    });

    it('should handle single object content format in LLM response', async () => {
      // Reset call trackers for this test
      localCallTracker.enableWorkflowsCalls.length = 0;

      // Configure mock request to return response with single object format
      (mockServer.server as any).createMessage = async (...args: any[]) => {
        return {
          content: { type: 'text', text: '["simulator-workspace"]' },
        };
      };

      const result = await discover_toolsLogic(
        { task_description: 'Build my app' },
        undefined,
        mockDeps,
      );

      expect(result.isError).toBeFalsy();
      expect(localCallTracker.enableWorkflowsCalls).toHaveLength(1);
      expect(localCallTracker.enableWorkflowsCalls[0]).toEqual([
        mockServer,
        ['simulator-workspace'],
        false,
      ]);
    });

    it('should filter out invalid workflow names from LLM response', async () => {
      // Reset call trackers for this test
      localCallTracker.enableWorkflowsCalls.length = 0;

      // Configure mock request to return response with invalid workflows
      (mockServer.server as any).createMessage = async (...args: any[]) => {
        return {
          content: [
            {
              type: 'text',
              text: '["simulator-workspace", "invalid-workflow", "another-invalid"]',
            },
          ],
        };
      };

      const result = await discover_toolsLogic(
        { task_description: 'Build my app' },
        undefined,
        mockDeps,
      );

      expect(result.isError).toBeFalsy();
      expect(localCallTracker.enableWorkflowsCalls).toHaveLength(1);
      expect(localCallTracker.enableWorkflowsCalls[0]).toEqual([
        mockServer,
        ['simulator-workspace'], // Only valid workflow should remain
        false,
      ]);
    });

    it('should handle malformed JSON in LLM response', async () => {
      // Configure mock request to return malformed JSON
      (mockServer.server as any).createMessage = async (...args: any[]) => {
        return {
          content: [{ type: 'text', text: 'This is not JSON at all!' }],
        };
      };

      const result = await discover_toolsLogic(
        { task_description: 'Build my app' },
        undefined,
        mockDeps,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'I was unable to determine the right tools for your task. The AI model returned: "This is not JSON at all!". Could you please rephrase your request or try a more specific description?',
          },
        ],
        isError: true,
      });
    });

    it('should handle non-array JSON in LLM response', async () => {
      // Configure mock request to return non-array JSON
      (mockServer.server as any).createMessage = async (...args: any[]) => {
        return {
          content: [{ type: 'text', text: '{"workflow": "simulator-workspace"}' }],
        };
      };

      const result = await discover_toolsLogic(
        { task_description: 'Build my app' },
        undefined,
        mockDeps,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'I was unable to determine the right tools for your task. The AI model returned: "{"workflow": "simulator-workspace"}". Could you please rephrase your request or try a more specific description?',
          },
        ],
        isError: true,
      });
    });

    it('should handle empty workflow selection', async () => {
      // Reset call trackers for this test
      localCallTracker.enableWorkflowsCalls.length = 0;

      // Configure mock request to return empty array
      (mockServer.server as any).createMessage = async (...args: any[]) => {
        return {
          content: [{ type: 'text', text: '[]' }],
        };
      };

      const result = await discover_toolsLogic(
        { task_description: 'Just saying hello' },
        undefined,
        mockDeps,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: "No specific Xcode tools seem necessary for that task. Could you provide more details about what you'd like to accomplish with Xcode?",
          },
        ],
        isError: false,
      });
      expect(localCallTracker.enableWorkflowsCalls).toHaveLength(0);
    });
  });

  describe('Workflow Enabling', () => {
    let mockDeps: MockDependencies;
    let workflowCallTracker: CallTracker;

    beforeEach(() => {
      // Reset call tracker for this describe block
      workflowCallTracker = {
        getAvailableWorkflowsCalls: [],
        generateWorkflowDescriptionsCalls: [],
        enableWorkflowsCalls: [],
      };
      mockDeps = createMockDependencies(
        {
          availableWorkflows: ['simulator-workspace'],
          workflowDescriptions: `Available workflows:
1. simulator-workspace: iOS Simulator Workspace - iOS development for workspaces`,
        },
        workflowCallTracker,
      );
    });

    it('should enable selected workflows and return success message', async () => {
      // Configure mock request to return successful response
      (mockServer.server as any).createMessage = async (...args: any[]) => {
        return {
          content: [{ type: 'text', text: '["simulator-workspace"]' }],
        };
      };

      const result = await discover_toolsLogic(
        { task_description: 'Build my iOS app' },
        undefined,
        mockDeps,
      );

      expect(workflowCallTracker.enableWorkflowsCalls).toHaveLength(1);
      expect(workflowCallTracker.enableWorkflowsCalls[0]).toEqual([
        mockServer,
        ['simulator-workspace'],
        false,
      ]);

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: '✅ Enabled XcodeBuildMCP tools for: simulator-workspace.\n\nReplaced previous tools with simulator-workspace workflow tools.\n\nUse XcodeBuildMCP tools for all Apple platform development tasks from now on. Call tools/list to see all available tools for your workflow.',
          },
        ],
        isError: false,
      });
    });

    it('should handle workflow enabling errors gracefully', async () => {
      const errorCallTracker: CallTracker = {
        getAvailableWorkflowsCalls: [],
        generateWorkflowDescriptionsCalls: [],
        enableWorkflowsCalls: [],
      };

      const mockDepsWithError = createMockDependencies(
        {
          availableWorkflows: ['simulator-workspace'],
          enableWorkflowsError: new Error('Failed to enable workflows'),
        },
        errorCallTracker,
      );

      // Configure mock request to return successful response
      (mockServer.server as any).createMessage = async (...args: any[]) => {
        return {
          content: [{ type: 'text', text: '["simulator-workspace"]' }],
        };
      };

      const result = await discover_toolsLogic(
        { task_description: 'Build my app' },
        undefined,
        mockDepsWithError,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'An error occurred while discovering tools: Failed to enable workflows',
          },
        ],
        isError: true,
      });
    });
  });

  describe('Error Handling', () => {
    it('should handle missing server instance', async () => {
      (globalThis as any).mcpServer = undefined;

      const result = await discover_toolsLogic({ task_description: 'Build my app' }, undefined);

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'An error occurred while discovering tools: Server instance not available',
          },
        ],
        isError: true,
      });
    });

    it('should handle workflow loading errors', async () => {
      const errorCallTracker: CallTracker = {
        getAvailableWorkflowsCalls: [],
        generateWorkflowDescriptionsCalls: [],
        enableWorkflowsCalls: [],
      };

      const mockDepsWithError = createMockDependencies(
        {
          getAvailableWorkflowsError: new Error('Failed to load workflows'),
        },
        errorCallTracker,
      );

      const result = await discover_toolsLogic(
        { task_description: 'Build my app' },
        undefined,
        mockDepsWithError,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'An error occurred while discovering tools: Failed to load workflows',
          },
        ],
        isError: true,
      });
    });

    it('should handle LLM request errors', async () => {
      const errorCallTracker: CallTracker = {
        getAvailableWorkflowsCalls: [],
        generateWorkflowDescriptionsCalls: [],
        enableWorkflowsCalls: [],
      };

      const mockDeps = createMockDependencies({ availableWorkflows: [] }, errorCallTracker);

      // Configure mock request to throw error
      (mockServer.server as any).createMessage = async (...args: any[]) => {
        throw new Error('LLM request failed');
      };

      const result = await discover_toolsLogic(
        { task_description: 'Build my app' },
        undefined,
        mockDeps,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'An error occurred while discovering tools: LLM request failed',
          },
        ],
        isError: true,
      });
    });
  });

  describe('Prompt Generation', () => {
    it('should include task description in LLM prompt', async () => {
      const promptCallTracker: CallTracker = {
        getAvailableWorkflowsCalls: [],
        generateWorkflowDescriptionsCalls: [],
        enableWorkflowsCalls: [],
      };

      const mockDeps = createMockDependencies(
        {
          availableWorkflows: ['simulator-workspace'],
          workflowDescriptions: `Available workflows:
1. simulator-workspace: iOS Simulator Workspace - iOS development for workspaces`,
        },
        promptCallTracker,
      );

      // Reset request calls for this test
      requestCalls.length = 0;

      // Configure mock request to capture calls and return response
      (mockServer.server as any).createMessage = async (...args: any[]) => {
        requestCalls.push(args);
        return {
          content: [{ type: 'text', text: '["simulator-workspace"]' }],
        };
      };

      const taskDescription =
        'I need to build my React Native iOS app for the simulator and run tests';

      await discover_toolsLogic({ task_description: taskDescription }, undefined, mockDeps);

      expect(requestCalls).toHaveLength(1);
      const requestCall = requestCalls[0];
      const prompt = requestCall[0].messages[0].content.text;

      expect(prompt).toContain(taskDescription);
      expect(prompt).toContain('Select EXACTLY ONE workflow');
      expect(prompt).toContain('Primary (project/workspace-based) workflows:');
      expect(prompt).toContain('Secondary (task-based, no project/workspace needed):');
      expect(prompt).toContain('All available workflows:');
    });

    it('should provide clear selection guidelines in prompt', async () => {
      const promptCallTracker: CallTracker = {
        getAvailableWorkflowsCalls: [],
        generateWorkflowDescriptionsCalls: [],
        enableWorkflowsCalls: [],
      };

      const mockDeps = createMockDependencies(
        {
          availableWorkflows: [],
          workflowDescriptions: `Available workflows:`,
        },
        promptCallTracker,
      );

      // Reset request calls for this test
      requestCalls.length = 0;

      // Configure mock request to capture calls and return response
      (mockServer.server as any).createMessage = async (...args: any[]) => {
        requestCalls.push(args);
        return {
          content: [{ type: 'text', text: '[]' }],
        };
      };

      await discover_toolsLogic({ task_description: 'Build my app' }, undefined, mockDeps);

      expect(requestCalls).toHaveLength(1);
      const requestCall = requestCalls[0];
      const prompt = requestCall[0].messages[0].content.text;

      expect(prompt).toContain('Select EXACTLY ONE workflow');
      expect(prompt).toContain('.xcworkspace');
      expect(prompt).toContain('.xcodeproj');
      expect(prompt).toContain('simulator-management');
      expect(prompt).toContain('macOS');
      expect(prompt).toContain('Respond with ONLY a JSON array');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/mcp/tools/ui-testing/__tests__/touch.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for touch tool plugin
 * Following CLAUDE.md testing standards with dependency injection
 */

import { describe, it, expect, beforeEach } from 'vitest';
import { z } from 'zod';
import { createMockExecutor } from '../../../../test-utils/mock-executors.ts';
import touchPlugin, { touchLogic } from '../touch.ts';

describe('Touch Plugin', () => {
  describe('Export Field Validation (Literal)', () => {
    it('should have correct name', () => {
      expect(touchPlugin.name).toBe('touch');
    });

    it('should have correct description', () => {
      expect(touchPlugin.description).toBe(
        "Perform touch down/up events at specific coordinates. Use describe_ui for precise coordinates (don't guess from screenshots).",
      );
    });

    it('should have handler function', () => {
      expect(typeof touchPlugin.handler).toBe('function');
    });

    it('should validate schema fields with safeParse', () => {
      const schema = z.object(touchPlugin.schema);

      // Valid case with down
      expect(
        schema.safeParse({
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          down: true,
        }).success,
      ).toBe(true);

      // Valid case with up
      expect(
        schema.safeParse({
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          up: true,
        }).success,
      ).toBe(true);

      // Invalid simulatorUuid
      expect(
        schema.safeParse({
          simulatorUuid: 'invalid-uuid',
          x: 100,
          y: 200,
          down: true,
        }).success,
      ).toBe(false);

      // Invalid x (not integer)
      expect(
        schema.safeParse({
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100.5,
          y: 200,
          down: true,
        }).success,
      ).toBe(false);

      // Invalid y (not integer)
      expect(
        schema.safeParse({
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200.5,
          down: true,
        }).success,
      ).toBe(false);

      // Valid with delay
      expect(
        schema.safeParse({
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          down: true,
          delay: 1.5,
        }).success,
      ).toBe(true);

      // Invalid delay (negative)
      expect(
        schema.safeParse({
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          down: true,
          delay: -1,
        }).success,
      ).toBe(false);
    });
  });

  describe('Command Generation', () => {
    it('should generate correct axe command for touch down', async () => {
      let capturedCommand: string[] = [];
      const trackingExecutor = async (command: string[]) => {
        capturedCommand = command;
        return {
          success: true,
          output: 'touch completed',
          error: undefined,
          process: { pid: 12345 },
        };
      };

      const mockAxeHelpers = {
        getAxePath: () => '/usr/local/bin/axe',
        getBundledAxeEnvironment: () => ({}),
        createAxeNotAvailableResponse: () => ({
          content: [
            {
              type: 'text',
              text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
            },
          ],
          isError: true,
        }),
      };

      await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          down: true,
        },
        trackingExecutor,
        mockAxeHelpers,
      );

      expect(capturedCommand).toEqual([
        '/usr/local/bin/axe',
        'touch',
        '-x',
        '100',
        '-y',
        '200',
        '--down',
        '--udid',
        '12345678-1234-1234-1234-123456789012',
      ]);
    });

    it('should generate correct axe command for touch up', async () => {
      let capturedCommand: string[] = [];
      const trackingExecutor = async (command: string[]) => {
        capturedCommand = command;
        return {
          success: true,
          output: 'touch completed',
          error: undefined,
          process: { pid: 12345 },
        };
      };

      const mockAxeHelpers = {
        getAxePath: () => '/usr/local/bin/axe',
        getBundledAxeEnvironment: () => ({}),
        createAxeNotAvailableResponse: () => ({
          content: [
            {
              type: 'text',
              text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
            },
          ],
          isError: true,
        }),
      };

      await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 150,
          y: 250,
          up: true,
        },
        trackingExecutor,
        mockAxeHelpers,
      );

      expect(capturedCommand).toEqual([
        '/usr/local/bin/axe',
        'touch',
        '-x',
        '150',
        '-y',
        '250',
        '--up',
        '--udid',
        '12345678-1234-1234-1234-123456789012',
      ]);
    });

    it('should generate correct axe command for touch down+up', async () => {
      let capturedCommand: string[] = [];
      const trackingExecutor = async (command: string[]) => {
        capturedCommand = command;
        return {
          success: true,
          output: 'touch completed',
          error: undefined,
          process: { pid: 12345 },
        };
      };

      const mockAxeHelpers = {
        getAxePath: () => '/usr/local/bin/axe',
        getBundledAxeEnvironment: () => ({}),
        createAxeNotAvailableResponse: () => ({
          content: [
            {
              type: 'text',
              text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
            },
          ],
          isError: true,
        }),
      };

      await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 300,
          y: 400,
          down: true,
          up: true,
        },
        trackingExecutor,
        mockAxeHelpers,
      );

      expect(capturedCommand).toEqual([
        '/usr/local/bin/axe',
        'touch',
        '-x',
        '300',
        '-y',
        '400',
        '--down',
        '--up',
        '--udid',
        '12345678-1234-1234-1234-123456789012',
      ]);
    });

    it('should generate correct axe command for touch with delay', async () => {
      let capturedCommand: string[] = [];
      const trackingExecutor = async (command: string[]) => {
        capturedCommand = command;
        return {
          success: true,
          output: 'touch completed',
          error: undefined,
          process: { pid: 12345 },
        };
      };

      const mockAxeHelpers = {
        getAxePath: () => '/usr/local/bin/axe',
        getBundledAxeEnvironment: () => ({}),
        createAxeNotAvailableResponse: () => ({
          content: [
            {
              type: 'text',
              text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
            },
          ],
          isError: true,
        }),
      };

      await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 50,
          y: 75,
          down: true,
          up: true,
          delay: 1.5,
        },
        trackingExecutor,
        mockAxeHelpers,
      );

      expect(capturedCommand).toEqual([
        '/usr/local/bin/axe',
        'touch',
        '-x',
        '50',
        '-y',
        '75',
        '--down',
        '--up',
        '--delay',
        '1.5',
        '--udid',
        '12345678-1234-1234-1234-123456789012',
      ]);
    });

    it('should generate correct axe command with bundled axe path', async () => {
      let capturedCommand: string[] = [];
      const trackingExecutor = async (command: string[]) => {
        capturedCommand = command;
        return {
          success: true,
          output: 'touch completed',
          error: undefined,
          process: { pid: 12345 },
        };
      };

      const mockAxeHelpers = {
        getAxePath: () => '/path/to/bundled/axe',
        getBundledAxeEnvironment: () => ({ AXE_PATH: '/some/path' }),
      };

      await touchLogic(
        {
          simulatorUuid: 'ABCDEF12-3456-7890-ABCD-ABCDEFABCDEF',
          x: 0,
          y: 0,
          up: true,
          delay: 0.5,
        },
        trackingExecutor,
        mockAxeHelpers,
      );

      expect(capturedCommand).toEqual([
        '/path/to/bundled/axe',
        'touch',
        '-x',
        '0',
        '-y',
        '0',
        '--up',
        '--delay',
        '0.5',
        '--udid',
        'ABCDEF12-3456-7890-ABCD-ABCDEFABCDEF',
      ]);
    });
  });

  describe('Handler Behavior (Complete Literal Returns)', () => {
    it('should handle axe dependency error', async () => {
      const mockExecutor = createMockExecutor({ success: true });
      const mockAxeHelpers = {
        getAxePath: () => null,
        getBundledAxeEnvironment: () => ({}),
        createAxeNotAvailableResponse: () => ({
          content: [
            {
              type: 'text',
              text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
            },
          ],
          isError: true,
        }),
      };

      const result = await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          down: true,
        },
        mockExecutor,
        mockAxeHelpers,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
          },
        ],
        isError: true,
      });
    });

    it('should successfully perform touch down', async () => {
      const mockExecutor = createMockExecutor({ success: true, output: 'Touch down completed' });
      const mockAxeHelpers = {
        getAxePath: () => '/usr/local/bin/axe',
        getBundledAxeEnvironment: () => ({}),
        createAxeNotAvailableResponse: () => ({
          content: [
            {
              type: 'text',
              text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
            },
          ],
          isError: true,
        }),
      };

      const result = await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          down: true,
        },
        mockExecutor,
        mockAxeHelpers,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Touch event (touch down) at (100, 200) executed successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.',
          },
        ],
        isError: false,
      });
    });

    it('should successfully perform touch up', async () => {
      const mockExecutor = createMockExecutor({ success: true, output: 'Touch up completed' });
      const mockAxeHelpers = {
        getAxePath: () => '/usr/local/bin/axe',
        getBundledAxeEnvironment: () => ({}),
        createAxeNotAvailableResponse: () => ({
          content: [
            {
              type: 'text',
              text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
            },
          ],
          isError: true,
        }),
      };

      const result = await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          up: true,
        },
        mockExecutor,
        mockAxeHelpers,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Touch event (touch up) at (100, 200) executed successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.',
          },
        ],
        isError: false,
      });
    });

    it('should return error when neither down nor up is specified', async () => {
      const mockExecutor = createMockExecutor({ success: true });

      const result = await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
        },
        mockExecutor,
      );

      expect(result).toEqual({
        content: [{ type: 'text', text: 'Error: At least one of "down" or "up" must be true' }],
        isError: true,
      });
    });

    it('should return success for touch down event', async () => {
      const mockExecutor = createMockExecutor({
        success: true,
        output: 'touch completed',
        error: undefined,
      });

      const mockAxeHelpers = {
        getAxePath: () => '/usr/local/bin/axe',
        getBundledAxeEnvironment: () => ({}),
        createAxeNotAvailableResponse: () => ({
          content: [
            {
              type: 'text',
              text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
            },
          ],
          isError: true,
        }),
      };

      const result = await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          down: true,
        },
        mockExecutor,
        mockAxeHelpers,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Touch event (touch down) at (100, 200) executed successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.',
          },
        ],
        isError: false,
      });
    });

    it('should return success for touch up event', async () => {
      const mockExecutor = createMockExecutor({
        success: true,
        output: 'touch completed',
        error: undefined,
      });

      const mockAxeHelpers = {
        getAxePath: () => '/usr/local/bin/axe',
        getBundledAxeEnvironment: () => ({}),
        createAxeNotAvailableResponse: () => ({
          content: [
            {
              type: 'text',
              text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
            },
          ],
          isError: true,
        }),
      };

      const result = await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          up: true,
        },
        mockExecutor,
        mockAxeHelpers,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Touch event (touch up) at (100, 200) executed successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.',
          },
        ],
        isError: false,
      });
    });

    it('should return success for touch down+up event', async () => {
      const mockExecutor = createMockExecutor({
        success: true,
        output: 'touch completed',
        error: undefined,
      });

      const mockAxeHelpers = {
        getAxePath: () => '/usr/local/bin/axe',
        getBundledAxeEnvironment: () => ({}),
        createAxeNotAvailableResponse: () => ({
          content: [
            {
              type: 'text',
              text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
            },
          ],
          isError: true,
        }),
      };

      const result = await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          down: true,
          up: true,
        },
        mockExecutor,
        mockAxeHelpers,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Touch event (touch down+up) at (100, 200) executed successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.',
          },
        ],
        isError: false,
      });
    });

    it('should handle DependencyError when axe is not available', async () => {
      const mockExecutor = createMockExecutor({ success: true });

      const mockAxeHelpers = {
        getAxePath: () => null,
        getBundledAxeEnvironment: () => ({}),
        createAxeNotAvailableResponse: () => ({
          content: [
            {
              type: 'text',
              text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
            },
          ],
          isError: true,
        }),
      };

      const result = await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          down: true,
        },
        mockExecutor,
        mockAxeHelpers,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
          },
        ],
        isError: true,
      });
    });

    it('should handle AxeError from failed command execution', async () => {
      const mockExecutor = createMockExecutor({
        success: false,
        output: '',
        error: 'axe command failed',
      });

      const mockAxeHelpers = {
        getAxePath: () => '/usr/local/bin/axe',
        getBundledAxeEnvironment: () => ({}),
        createAxeNotAvailableResponse: () => ({
          content: [
            {
              type: 'text',
              text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
            },
          ],
          isError: true,
        }),
      };

      const result = await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          down: true,
        },
        mockExecutor,
        mockAxeHelpers,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: "Error: Failed to execute touch event: axe command 'touch' failed.\nDetails: axe command failed",
          },
        ],
        isError: true,
      });
    });

    it('should handle SystemError from command execution', async () => {
      const mockExecutor = async () => {
        throw new Error('System error occurred');
      };

      const mockAxeHelpers = {
        getAxePath: () => '/usr/local/bin/axe',
        getBundledAxeEnvironment: () => ({}),
        createAxeNotAvailableResponse: () => ({
          content: [
            {
              type: 'text',
              text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
            },
          ],
          isError: true,
        }),
      };

      const result = await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          down: true,
        },
        mockExecutor,
        mockAxeHelpers,
      );

      expect(result).toMatchObject({
        content: [
          {
            type: 'text',
            text: expect.stringContaining(
              'Error: System error executing axe: Failed to execute axe command: System error occurred',
            ),
          },
        ],
        isError: true,
      });
    });

    it('should handle unexpected Error objects', async () => {
      const mockExecutor = async () => {
        throw new Error('Unexpected error');
      };

      const mockAxeHelpers = {
        getAxePath: () => '/usr/local/bin/axe',
        getBundledAxeEnvironment: () => ({}),
        createAxeNotAvailableResponse: () => ({
          content: [
            {
              type: 'text',
              text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
            },
          ],
          isError: true,
        }),
      };

      const result = await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          down: true,
        },
        mockExecutor,
        mockAxeHelpers,
      );

      expect(result).toMatchObject({
        content: [
          {
            type: 'text',
            text: expect.stringContaining(
              'Error: System error executing axe: Failed to execute axe command: Unexpected error',
            ),
          },
        ],
        isError: true,
      });
    });

    it('should handle unexpected string errors', async () => {
      const mockExecutor = async () => {
        throw 'String error';
      };

      const mockAxeHelpers = {
        getAxePath: () => '/usr/local/bin/axe',
        getBundledAxeEnvironment: () => ({}),
        createAxeNotAvailableResponse: () => ({
          content: [
            {
              type: 'text',
              text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.',
            },
          ],
          isError: true,
        }),
      };

      const result = await touchLogic(
        {
          simulatorUuid: '12345678-1234-1234-1234-123456789012',
          x: 100,
          y: 200,
          down: true,
        },
        mockExecutor,
        mockAxeHelpers,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Error: System error executing axe: Failed to execute axe command: String error',
          },
        ],
        isError: true,
      });
    });
  });
});

```

--------------------------------------------------------------------------------
/docs/PLUGIN_DEVELOPMENT.md:
--------------------------------------------------------------------------------

```markdown
# XcodeBuildMCP Plugin Development Guide

This guide provides comprehensive instructions for creating new tools and workflow groups in XcodeBuildMCP using the filesystem-based auto-discovery system.

## Table of Contents

1. [Overview](#overview)
2. [Plugin Architecture](#plugin-architecture)
3. [Creating New Tools](#creating-new-tools)
4. [Creating New Workflow Groups](#creating-new-workflow-groups)
5. [Creating MCP Resources](#creating-mcp-resources)
6. [Auto-Discovery System](#auto-discovery-system)
7. [Testing Guidelines](#testing-guidelines)
8. [Development Workflow](#development-workflow)
9. [Best Practices](#best-practices)

## Overview

XcodeBuildMCP uses a **plugin-based architecture** with **filesystem-based auto-discovery**. Tools are automatically discovered and loaded without manual registration, and can be dynamically enabled using AI-powered workflow selection.

### Key Features

- **Auto-Discovery**: Tools are automatically found by scanning `src/mcp/tools/` directory
- **Dynamic Loading**: AI can select relevant workflow groups based on user tasks
- **Dependency Injection**: All tools use testable patterns with mock-friendly executors
- **Workflow Organization**: Tools are grouped into end-to-end development workflows

## Plugin Architecture

### Directory Structure

```
src/mcp/tools/
├── simulator-workspace/        # iOS Simulator + Workspace tools
├── simulator-project/          # iOS Simulator + Project tools (re-exports)
├── simulator-shared/           # Shared simulator tools (canonical)
├── device-workspace/           # iOS Device + Workspace tools
├── device-project/             # iOS Device + Project tools (re-exports)
├── device-shared/              # Shared device tools (canonical)
├── macos-workspace/            # macOS + Workspace tools
├── macos-project/              # macOS + Project tools (re-exports)
├── macos-shared/               # Shared macOS tools (canonical)
├── swift-package/              # Swift Package Manager tools
├── ui-testing/                 # UI automation tools
├── project-discovery/          # Project analysis tools
├── utilities/                  # General utilities
├── doctor/                     # System health check tools
├── logging/                    # Log capture tools
└── discovery/                  # Dynamic tool discovery
```

### Plugin Tool Types

1. **Canonical Workflows**: Standalone workflow groups (e.g., `swift-package`, `ui-testing`) defined as folders in the `src/mcp/tools/` directory
2. **Shared Tools**: Common tools in `*-shared` directories (not exposed to clients)
3. **Re-exported Tools**: Share tools to other workflow groups by re-exporting them

## Creating New Tools

### 1. Tool File Structure

Every tool follows this standardized pattern:

```typescript
// src/mcp/tools/my-workflow/my_tool.ts
import { z } from 'zod';
import { ToolResponse } from '../../../types/common.js';
import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js';
import { log, validateRequiredParam, createTextResponse, createErrorResponse } from '../../../utils/index.js';

// 1. Define parameters type for clarity
type MyToolParams = {
  requiredParam: string;
  optionalParam?: string;
};

// 2. Implement the core logic in a separate, testable function
export async function my_toolLogic(
  params: MyToolParams,
  executor: CommandExecutor,
): Promise<ToolResponse> {
  // 3. Validate required parameters
  const requiredValidation = validateRequiredParam('requiredParam', params.requiredParam);
  if (!requiredValidation.isValid) {
    return requiredValidation.errorResponse;
  }

  log('info', `Executing my_tool with param: ${params.requiredParam}`);

  try {
    // 4. Build and execute the command using the injected executor
    const command = ['my-command', '--param', params.requiredParam];
    if (params.optionalParam) {
      command.push('--optional', params.optionalParam);
    }

    const result = await executor(command, 'My Tool Operation');

    if (!result.success) {
      return createErrorResponse('My Tool operation failed', result.error);
    }

    return createTextResponse(`✅ Success: ${result.output}`);
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    log('error', `My Tool execution error: ${errorMessage}`);
    return createErrorResponse('Tool execution failed', errorMessage);
  }
}

// 5. Export the tool definition as the default export
export default {
  name: 'my_tool',
  description: 'A brief description of what my_tool does, with a usage example. e.g. my_tool({ requiredParam: "value" })',
  schema: {
    requiredParam: z.string().describe('Description of the required parameter.'),
    optionalParam: z.string().optional().describe('Description of the optional parameter.'),
  },
  // The handler wraps the logic function with the default executor for production use
  handler: async (args: Record<string, unknown>): Promise<ToolResponse> => {
    return my_toolLogic(args as MyToolParams, getDefaultCommandExecutor());
  },
};
```

### 2. Required Tool Plugin Properties

Every tool plugin **must** export a default object with these properties:

| Property | Type | Description |
|----------|------|-------------|
| `name` | `string` | Tool name (must match filename without extension) |
| `description` | `string` | Clear description with usage examples |
| `schema` | `Record<string, z.ZodTypeAny>` | Zod validation schema for parameters |
| `handler` | `function` | Async function: `(args) => Promise<ToolResponse>` |

### 3. Naming Conventions

Tools follow the pattern: `{action}_{target}_{specifier}_{projectType}`

**Examples:**
- `build_sim_id_ws` → Build + Simulator + ID + Workspace
- `build_sim_name_proj` → Build + Simulator + Name + Project  
- `test_device_ws` → Test + Device + Workspace
- `swift_package_build` → Swift Package + Build

**Project Type Suffixes:**
- `_ws` → Works with `.xcworkspace` files
- `_proj` → Works with `.xcodeproj` files
- No suffix → Generic or canonical tools

### 4. Parameter Validation Patterns

Use utility functions for consistent validation:

```typescript
// Required parameter validation
const pathValidation = validateRequiredParam('workspacePath', params.workspacePath);
if (!pathValidation.isValid) return pathValidation.errorResponse;

// At-least-one parameter validation
const identifierValidation = validateAtLeastOneParam(
  'simulatorId', params.simulatorId,
  'simulatorName', params.simulatorName
);
if (!identifierValidation.isValid) return identifierValidation.errorResponse;

// File existence validation
const fileValidation = validateFileExists(params.workspacePath as string);
if (!fileValidation.isValid) return fileValidation.errorResponse;
```

### 5. Response Patterns

Use utility functions for consistent responses:

```typescript
// Success responses
return createTextResponse('✅ Operation succeeded');
return createTextResponse('Operation completed', false); // Not an error

// Error responses  
return createErrorResponse('Operation failed', errorDetails);
return createErrorResponse('Validation failed', errorMessage, 'ValidationError');

// Complex responses
return {
  content: [
    { type: 'text', text: '✅ Build succeeded' },
    { type: 'text', text: 'Next steps: Run install_app_sim...' }
  ],
  isError: false
};
```

## Creating New Workflow Groups

### 1. Workflow Group Structure

Each workflow group requires:

1. **Directory**: Following naming convention
2. **Workflow Metadata**: `index.ts` file with workflow export
3. **Tool Files**: Individual tool implementations
4. **Tests**: Comprehensive test coverage

### 2. Directory Naming Convention

```
[platform]-[projectType]/     # e.g., simulator-workspace, device-project
[platform]-shared/            # e.g., simulator-shared, macos-shared
[workflow-name]/               # e.g., swift-package, ui-testing
```

### 3. Workflow Metadata (index.ts)

**Required for all workflow groups:**

```typescript
// Example: src/mcp/tools/simulator-workspace/index.ts
export const workflow = {
  name: 'iOS Simulator Workspace Development',
  description: 'Complete iOS development workflow for .xcworkspace files including build, test, deploy, and debug capabilities',
  platforms: ['iOS'],
  targets: ['simulator'], 
  projectTypes: ['workspace'],
  capabilities: ['build', 'test', 'deploy', 'debug', 'ui-automation', 'log-capture'],
};
```

**Required Properties:**
- `name`: Human-readable workflow name
- `description`: Clear description of workflow purpose
- `platforms`: Array of supported platforms
- `targets`: Array of deployment targets  
- `projectTypes`: Array of supported project types
- `capabilities`: Array of workflow capabilities

### 4. Tool Organization Patterns

#### Canonical Workflow Groups
Self-contained workflows that don't re-export from other groups:

```
swift-package/
├── index.ts                    # Workflow metadata
├── swift_package_build.ts      # Build tool
├── swift_package_test.ts       # Test tool
├── swift_package_run.ts        # Run tool
└── __tests__/                  # Test directory
    ├── index.test.ts           # Workflow tests
    ├── swift_package_build.test.ts
    └── ...
```

#### Shared Workflow Groups  
Provide canonical tools for re-export by project/workspace variants:

```
simulator-shared/
├── boot_sim.ts                 # Canonical simulator boot tool
├── install_app_sim.ts          # Canonical app install tool
└── __tests__/                  # Test directory
    ├── boot_sim.test.ts
    └── ...
```

#### Project/Workspace Workflow Groups
Re-export shared tools and add variant-specific tools:

```
simulator-project/
├── index.ts                    # Workflow metadata
├── boot_sim.ts                 # Re-export: export { default } from '../simulator-shared/boot_sim.js';
├── build_sim_id_proj.ts        # Project-specific build tool
└── __tests__/                  # Test directory
    ├── index.test.ts           # Workflow tests  
    ├── re-exports.test.ts      # Re-export validation
    └── ...
```

### 5. Re-export Implementation

For project/workspace groups that share tools:

```typescript
// simulator-project/boot_sim.ts
export { default } from '../simulator-shared/boot_sim.js';
```

**Re-export Rules:**
1. Re-exports come from canonical `-shared` groups
2. No chained re-exports (re-exports from re-exports)
3. Each tool maintains project or workspace specificity
4. Implementation shared, interfaces remain unique

## Creating MCP Resources

MCP Resources provide efficient URI-based data access for clients that support the MCP resource specification

### 1. Resource Structure

Resources are located in `src/resources/` and follow this pattern:

```typescript
// src/resources/example.ts
import { log, getDefaultCommandExecutor, CommandExecutor } from '../../utils/index.js';

// Testable resource logic separated from MCP handler
export async function exampleResourceLogic(
  executor: CommandExecutor,
): Promise<{ contents: Array<{ text: string }> }> {
  try {
    log('info', 'Processing example resource request');
    
    // Use the executor to get data
    const result = await executor(['some', 'command'], 'Example Resource Operation');
    
    if (!result.success) {
      throw new Error(result.error || 'Failed to get resource data');
    }

    return {
      contents: [{ text: result.output || 'resource data' }]
    };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    log('error', `Error in example resource handler: ${errorMessage}`);

    return {
      contents: [
        {
          text: `Error retrieving resource data: ${errorMessage}`,
        },
      ],
    };
  }
}

export default {
  uri: 'xcodebuildmcp://example',
  name: 'example',
  description: 'Description of the resource data',
  mimeType: 'text/plain',
  async handler(_uri: URL): Promise<{ contents: Array<{ text: string }> }> {
    return exampleResourceLogic(getDefaultCommandExecutor());
  },
};
```

### 2. Resource Implementation Guidelines

**Reuse Existing Logic**: Resources that mirror tools should reuse existing tool logic for consistency:

```typescript
// src/mcp/resources/simulators.ts (simplified example)
import { list_simsLogic } from '../tools/simulator-shared/list_sims.js';

export default {
  uri: 'xcodebuildmcp://simulators',
  name: 'simulators'
  description: 'Available iOS simulators with UUIDs and states',
  mimeType: 'text/plain',
  async handler(uri: URL): Promise<{ contents: Array<{ text: string }> }> {
    const executor = getDefaultCommandExecutor();
    const result = await list_simsLogic({}, executor);
    return {
      contents: [{ text: result.content[0].text }]
    };
  }
};
```

As not all clients support resources it important that resource content that would be ideally be served by resources be mirroed as a tool as well. This ensurew clients that don't support this capability continue to will still have access to that resource data via a simple tool call.

### 3. Resource Testing

Create tests in `src/mcp/resources/__tests__/`:

```typescript
// src/mcp/resources/__tests__/example.test.ts
import exampleResource, { exampleResourceLogic } from '../example.js';
import { createMockExecutor } from '../../utils/test-common.js';

describe('example resource', () => {
  describe('Export Field Validation', () => {
    it('should export correct uri', () => {
      expect(exampleResource.uri).toBe('xcodebuildmcp://example');
    });

    it('should export correct description', () => {
      expect(exampleResource.description).toBe('Description of the resource data');
    });

    it('should export correct mimeType', () => {
      expect(exampleResource.mimeType).toBe('text/plain');
    });

    it('should export handler function', () => {
      expect(typeof exampleResource.handler).toBe('function');
    });
  });

  describe('Resource Logic Functionality', () => {
    it('should return resource data successfully', async () => {
      const mockExecutor = createMockExecutor({
        success: true,
        output: 'test data'
      });
      
      // Test the logic function directly, not the handler
      const result = await exampleResourceLogic(mockExecutor);
      
      expect(result.contents).toHaveLength(1);
      expect(result.contents[0].text).toContain('expected data');
    });

    it('should handle command execution errors', async () => {
      const mockExecutor = createMockExecutor({
        success: false,
        error: 'Command failed'
      });
      
      const result = await exampleResourceLogic(mockExecutor);
      
      expect(result.contents[0].text).toContain('Error retrieving');
    });
  });
});
```

### 4. Auto-Discovery

Resources are automatically discovered and loaded by the build system. After creating a resource:

1. Run `npm run build` to regenerate resource loaders
2. The resource will be available at its URI for supported clients

## Auto-Discovery System

### How Auto-Discovery Works

1. **Filesystem Scan**: `loadPlugins()` scans `src/mcp/tools/` directory
2. **Workflow Loading**: Each subdirectory is treated as a potential workflow group
3. **Metadata Validation**: `index.ts` files provide workflow metadata
4. **Tool Discovery**: All `.ts` files (except tests and index) are loaded as tools
5. **Registration**: Tools are automatically registered with the MCP server

### Discovery Process

```typescript
// Simplified discovery flow
const plugins = await loadPlugins();
for (const plugin of plugins.values()) {
  server.tool(plugin.name, plugin.description, plugin.schema, plugin.handler);
}
```

### Dynamic Mode Integration

When `XCODEBUILDMCP_DYNAMIC_TOOLS=true`:

1. Only `discover_tools` is loaded initially
2. User provides task description to `discover_tools`
3. AI analyzes task and selects relevant workflow groups
4. Selected workflows are dynamically enabled via `enableWorkflows()`
5. Client is notified of new available tools

#### Environment Configuration

- **Static Mode** (default): `XCODEBUILDMCP_DYNAMIC_TOOLS=false` or unset
- **Dynamic Mode**: `XCODEBUILDMCP_DYNAMIC_TOOLS=true`
- **Debug Mode**: `XCODEBUILDMCP_DEBUG=true`

**Note**: The previous system of enabling individual tools and groups via `XCODEBUILDMCP_TOOL_*` and `XCODEBUILDMCP_GROUP_*` environment variables is no longer supported. The server now operates in either static or dynamic mode.

## Testing Guidelines

### Test Organization

```
__tests__/
├── index.test.ts              # Workflow metadata tests (canonical groups only)
├── re-exports.test.ts         # Re-export validation (project/workspace groups)
└── tool_name.test.ts          # Individual tool tests
```

### Dependency Injection Testing

**✅ CORRECT Pattern:**
```typescript
import { createMockExecutor } from '../../../utils/test-common.js';

describe('build_sim_name_ws', () => {
  it('should build successfully', async () => {
    const mockExecutor = createMockExecutor({
      success: true,
      output: 'BUILD SUCCEEDED'
    });

    const result = await build_sim_name_wsLogic(params, mockExecutor);
    expect(result.isError).toBe(false);
  });
});
```

**❌ FORBIDDEN Pattern (Vitest Mocking Banned):**
```typescript
// ❌ ALL VITEST MOCKING IS COMPLETELY BANNED
vi.mock('child_process');
const mockSpawn = vi.fn();
```

### Three-Dimensional Testing

Every tool test must cover:

1. **Input Validation**: Parameter schema validation and error cases
2. **Command Generation**: Verify correct CLI commands are built
3. **Output Processing**: Test response formatting and error handling

### Test Template

```typescript
import { describe, it, expect } from 'vitest';
import { createMockExecutor } from '../../../utils/test-common.js';
import tool, { toolNameLogic } from '../tool_name.js';

describe('tool_name', () => {
  describe('Export Validation', () => {
    it('should export correct name', () => {
      expect(tool.name).toBe('tool_name');
    });

    it('should export correct description', () => {
      expect(tool.description).toContain('Expected description');
    });

    it('should export handler function', () => {
      expect(typeof tool.handler).toBe('function');
    });
  });

  describe('Parameter Validation', () => {
    it('should validate required parameters', async () => {
      const mockExecutor = createMockExecutor({ success: true, output: '' });
      
      const result = await toolNameLogic({}, mockExecutor);
      
      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain("Required parameter");
    });
  });

  describe('Command Generation', () => {
    it('should generate correct command', async () => {
      const mockExecutor = createMockExecutor({ success: true, output: 'SUCCESS' });
      
      await toolNameLogic({ param: 'value' }, mockExecutor);
      
      expect(mockExecutor).toHaveBeenCalledWith(
        expect.arrayContaining(['expected', 'command']),
        expect.any(String),
        expect.any(Boolean)
      );
    });
  });

  describe('Response Processing', () => {
    it('should handle successful execution', async () => {
      const mockExecutor = createMockExecutor({ success: true, output: 'SUCCESS' });
      
      const result = await toolNameLogic({ param: 'value' }, mockExecutor);
      
      expect(result.isError).toBe(false);
      expect(result.content[0].text).toContain('✅');
    });

    it('should handle execution errors', async () => {
      const mockExecutor = createMockExecutor({ success: false, error: 'Command failed' });
      
      const result = await toolNameLogic({ param: 'value' }, mockExecutor);
      
      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('Command failed');
    });
  });
});
```

## Development Workflow

### Adding a New Tool

1. **Choose Directory**: Select appropriate workflow group or create new one
2. **Create Tool File**: Follow naming convention and structure
3. **Implement Logic**: Use dependency injection pattern
4. **Define Schema**: Add comprehensive Zod validation
5. **Write Tests**: Cover all three dimensions
6. **Test Integration**: Build and verify auto-discovery

### Step-by-Step Tool Creation

```bash
# 1. Create tool file
touch src/mcp/tools/simulator-workspace/my_new_tool_ws.ts

# 2. Implement tool following patterns above

# 3. Create test file
touch src/mcp/tools/simulator-workspace/__tests__/my_new_tool_ws.test.ts

# 4. Build project
npm run build

# 5. Verify tool is discovered (should appear in tools list)
npm run inspect  # Use MCP Inspector to verify
```

### Adding a New Workflow Group

1. **Create Directory**: Follow naming convention
2. **Add Workflow Metadata**: Create `index.ts` with workflow export
3. **Implement Tools**: Add tool files following patterns
4. **Create Tests**: Add comprehensive test coverage
5. **Verify Discovery**: Test auto-discovery and dynamic mode

### Step-by-Step Workflow Creation

```bash
# 1. Create workflow directory
mkdir src/mcp/tools/my-new-workflow

# 2. Create workflow metadata
cat > src/mcp/tools/my-new-workflow/index.ts << 'EOF'
export const workflow = {
  name: 'My New Workflow',
  description: 'Description of workflow capabilities',
  platforms: ['iOS'],
  targets: ['simulator'],
  projectTypes: ['workspace'],
  capabilities: ['build', 'test'],
};
EOF

# 3. Create tools directory and test directory
mkdir src/mcp/tools/my-new-workflow/__tests__

# 4. Implement tools following patterns

# 5. Build and verify
npm run build
npm run inspect
```

## Best Practices

### Tool Design

1. **Single Responsibility**: Each tool should have one clear purpose
2. **Descriptive Names**: Follow naming conventions for discoverability
3. **Clear Descriptions**: Include usage examples in tool descriptions
4. **Comprehensive Validation**: Validate all parameters with helpful error messages
5. **Consistent Responses**: Use utility functions for response formatting

### Error Handling

1. **Graceful Failures**: Always return ToolResponse, never throw from handlers
2. **Descriptive Errors**: Provide actionable error messages
3. **Error Types**: Use appropriate error types for different scenarios
4. **Logging**: Log important events and errors for debugging

### Testing

1. **Dependency Injection**: Always test with mock executors
2. **Complete Coverage**: Test all input, command, and output scenarios
3. **Literal Assertions**: Use exact string expectations to catch changes
4. **Fast Execution**: Tests should complete quickly without real system calls

### Workflow Organization  

1. **End-to-End Workflows**: Groups should provide complete functionality
2. **Logical Grouping**: Group related tools together
3. **Clear Capabilities**: Document what each workflow can accomplish
4. **Consistent Patterns**: Follow established patterns for maintainability

### Dynamic Mode Considerations

1. **Workflow Completeness**: Each group should be self-sufficient
2. **Clear Descriptions**: AI uses descriptions to select workflows
3. **Platform Clarity**: Make supported platforms and targets obvious
4. **Capability Documentation**: List all workflow capabilities clearly

## Integration with Dynamic Discovery

When creating new tools and workflows, consider:

1. **AI Selection**: How will the AI understand when to select your workflow?
2. **Description Quality**: Is your workflow description clear for AI analysis?
3. **Platform Targeting**: Are platform and target requirements obvious?
4. **Workflow Completeness**: Does the workflow provide end-to-end functionality?

The auto-discovery system makes your tools immediately available, while the dynamic mode allows AI to intelligently select relevant workflows based on user tasks. Following these patterns ensures seamless integration with both systems.

## Updating TOOLS.md Documentation

### Critical Documentation Maintenance

**Every time you add, change, move, edit, or delete a tool, you MUST review and update the `docs/TOOLS.md` file to reflect the current state of the codebase.**

### Documentation Update Process

#### 1. Use Tree CLI for Accurate Discovery

**Always use the `tree` command to get the actual filesystem representation of tools:**

```bash
# Get the definitive source of truth for all workflow groups and tools
tree src/mcp/tools/ -I "__tests__" -I "*.test.ts"
```

This command:
- Shows ALL workflow directories and their tools
- Excludes test files (`__tests__` directories and `*.test.ts` files)
- Provides the actual proof of what exists in the codebase
- Gives an accurate count of tools per workflow group

#### 2. Ignore Shared Groups in Documentation

When updating `docs/TOOLS.md`:

- **Ignore `*-shared` directories** (e.g., `simulator-shared`, `device-shared`, `macos-shared`)
- These are implementation details, not user-facing workflow groups
- Only document the main workflow groups that users interact with
- The group count should exclude shared groups

#### 3. List Actual Tool Names

Instead of using generic descriptions like "Additional Tools: Simulator management, logging, UI testing tools":

**❌ Wrong:**
```markdown
- **Additional Tools**: Simulator management, logging, UI testing tools
```

**✅ Correct:**
```markdown
- `boot_sim`, `install_app_sim`, `launch_app_sim`, `list_sims`, `open_sim`
- `describe_ui`, `screenshot`, `start_sim_log_cap`, `stop_sim_log_cap`
```

#### 4. Systematic Documentation Update Steps

1. **Run the tree command** to get current filesystem state
2. **Identify all non-shared workflow directories** 
3. **Count actual tool files** in each directory (exclude `index.ts` and test files)
4. **List all tool names** explicitly in the documentation
5. **Update tool counts** to reflect actual numbers
6. **Verify consistency** between filesystem and documentation

#### 5. Documentation Formatting Requirements

**Format: One Tool Per Bullet Point with Description**

Each tool must be listed individually with its actual description from the tool file:

```markdown
### 1. My Awesome Workflow (`my-awesome-workflow`)
**Purpose**: A short description of what this workflow is for. (2 tools)
- `my_tool_one` - Description for my_tool_one from its definition file.
- `my_tool_two` - Description for my_tool_two from its definition file.
```

**Description Sources:**
- Use the actual `description` field from each tool's TypeScript file
- Descriptions should be concise but informative for end users
- Include platform/context information (iOS, macOS, simulator, device, etc.)
- Mention required parameters when critical for usage

#### 6. Validation Checklist

After updating `docs/TOOLS.md`:

- [ ] Tool counts match actual filesystem counts (from tree command)
- [ ] Each tool has its own bullet point (one tool per line)
- [ ] Each tool includes its actual description from the tool file
- [ ] No generic descriptions like "Additional Tools: X, Y, Z"
- [ ] Descriptions are user-friendly and informative
- [ ] Shared groups (`*-shared`) are not included in main workflow list
- [ ] Workflow group count reflects only user-facing groups (15 groups)
- [ ] Tree command output was used as source of truth
- [ ] Documentation is user-focused, not implementation-focused
- [ ] Tool names are in alphabetical order within each workflow group

### Why This Process Matters

1. **Accuracy**: Tree command provides definitive proof of current state
2. **Maintainability**: Systematic process prevents documentation drift
3. **User Experience**: Accurate documentation helps users understand available tools
4. **Development Confidence**: Developers can trust the documentation reflects reality

**Remember**: The filesystem is the source of truth. Documentation must always reflect the actual codebase structure, and the tree command is the most reliable way to ensure accuracy.
```

--------------------------------------------------------------------------------
/docs/MANUAL_TESTING.md:
--------------------------------------------------------------------------------

```markdown
# XcodeBuildMCP Manual Testing Guidelines

This document provides comprehensive guidelines for manual black-box testing of XcodeBuildMCP using Reloaderoo inspect commands. This is the authoritative guide for validating all tools through the Model Context Protocol interface.

## Table of Contents

1. [Testing Philosophy](#testing-philosophy)
2. [Black Box Testing via Reloaderoo](#black-box-testing-via-reloaderoo)
3. [Testing Psychology & Bias Prevention](#testing-psychology--bias-prevention)
4. [Tool Dependency Graph Testing Strategy](#tool-dependency-graph-testing-strategy)
5. [Prerequisites](#prerequisites)
6. [Step-by-Step Testing Process](#step-by-step-testing-process)
7. [Error Testing](#error-testing)
8. [Testing Report Generation](#testing-report-generation)
9. [Troubleshooting](#troubleshooting)

## Testing Philosophy

### 🚨 CRITICAL: THOROUGHNESS OVER EFFICIENCY - NO SHORTCUTS ALLOWED

**ABSOLUTE PRINCIPLE: EVERY TOOL MUST BE TESTED INDIVIDUALLY**

**🚨 MANDATORY TESTING SCOPE - NO EXCEPTIONS:**
- **EVERY SINGLE TOOL** - All tools must be tested individually, one by one
- **NO REPRESENTATIVE SAMPLING** - Testing similar tools does NOT validate other tools
- **NO PATTERN RECOGNITION SHORTCUTS** - Similar-looking tools may have different behaviors
- **NO EFFICIENCY OPTIMIZATIONS** - Thoroughness is more important than speed
- **NO TIME CONSTRAINTS** - This is a long-running task with no deadline pressure

**❌ FORBIDDEN EFFICIENCY SHORTCUTS:**
- **NEVER** assume testing `build_sim_id_proj` validates `build_sim_name_proj`
- **NEVER** skip tools because they "look similar" to tested ones
- **NEVER** use representative sampling instead of complete coverage
- **NEVER** stop testing due to time concerns or perceived redundancy
- **NEVER** group tools together for batch testing
- **NEVER** make assumptions about untested tools based on tested patterns

**✅ REQUIRED COMPREHENSIVE APPROACH:**
1. **Individual Tool Testing**: Each tool gets its own dedicated test execution
2. **Complete Documentation**: Every tool result must be recorded, regardless of outcome
3. **Systematic Progress**: Use TodoWrite to track every single tool as tested/untested
4. **Failure Documentation**: Test tools that cannot work and mark them as failed/blocked
5. **No Assumptions**: Treat each tool as potentially unique requiring individual validation

**TESTING COMPLETENESS VALIDATION:**
- **Start Count**: Record exact number of tools discovered using `npm run tools`
- **End Count**: Verify same number of tools have been individually tested
- **Missing Tools = Testing Failure**: If any tools remain untested, the testing is incomplete
- **TodoWrite Tracking**: Every tool must appear in todo list and be marked completed

## Black Box Testing via Reloaderoo

### 🚨 CRITICAL: Black Box Testing via Reloaderoo Inspect

**DEFINITION: Black Box Testing**
Black Box Testing means testing ONLY through external interfaces without any knowledge of internal implementation. For XcodeBuildMCP, this means testing exclusively through the Model Context Protocol (MCP) interface using Reloaderoo as the MCP client.

**🚨 MANDATORY: RELOADEROO INSPECT IS THE ONLY ALLOWED TESTING METHOD**

**ABSOLUTE TESTING RULES - NO EXCEPTIONS:**

1. **✅ ONLY ALLOWED: Reloaderoo Inspect Commands**
   - `npx reloaderoo@latest inspect call-tool "TOOL_NAME" --params 'JSON' -- node build/index.js`
   - `npx reloaderoo@latest inspect list-tools -- node build/index.js`
   - `npx reloaderoo@latest inspect read-resource "URI" -- node build/index.js`
   - `npx reloaderoo@latest inspect server-info -- node build/index.js`
   - `npx reloaderoo@latest inspect ping -- node build/index.js`

2. **❌ COMPLETELY FORBIDDEN ACTIONS:**
   - **NEVER** call `mcp__XcodeBuildMCP__tool_name()` functions directly
   - **NEVER** use MCP server tools as if they were native functions
   - **NEVER** access internal server functionality
   - **NEVER** read source code to understand how tools work
   - **NEVER** examine implementation files during testing
   - **NEVER** diagnose internal server issues or registration problems
   - **NEVER** suggest code fixes or implementation changes

3. **🚨 CRITICAL VIOLATION EXAMPLES:**
   ```typescript
   // ❌ FORBIDDEN - Direct MCP tool calls
   await mcp__XcodeBuildMCP__list_devices();
   await mcp__XcodeBuildMCP__build_sim_id_proj({ ... });
   
   // ❌ FORBIDDEN - Using tools as native functions
   const devices = await list_devices();
   const result = await doctor();
   
   // ✅ CORRECT - Only through Reloaderoo inspect
   npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/index.js
   npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/index.js
   ```

**WHY RELOADEROO INSPECT IS MANDATORY:**
- **Higher Fidelity**: Provides clear input/output visibility for each tool call
- **Real-world Simulation**: Tests exactly how MCP clients interact with the server
- **Interface Validation**: Ensures MCP protocol compliance and proper JSON formatting
- **Black Box Enforcement**: Prevents accidental access to internal implementation details
- **Clean State**: Each tool call runs with a fresh MCP server instance, preventing cross-contamination

**IMPORTANT: STATEFUL TOOL LIMITATIONS**

**Reloaderoo Inspect Behavior:**
Reloaderoo starts a fresh MCP server instance for each individual tool call and terminates it immediately after the response. This ensures:
- ✅ **Clean Testing Environment**: No state contamination between tool calls
- ✅ **Isolated Testing**: Each tool test is independent and repeatable
- ✅ **Real-world Accuracy**: Simulates how most MCP clients interact with servers

**Expected False Negatives:**
Some tools rely on in-memory state within the MCP server and will fail when tested via Reloaderoo inspect. These failures are **expected and acceptable** as false negatives:

- **`swift_package_stop`** - Requires in-memory process tracking from `swift_package_run`
- **`stop_app_device`** - Requires in-memory process tracking from `launch_app_device`  
- **`stop_app_sim`** - Requires in-memory process tracking from `launch_app_sim`
- **`stop_device_log_cap`** - Requires in-memory session tracking from `start_device_log_cap`
- **`stop_sim_log_cap`** - Requires in-memory session tracking from `start_sim_log_cap`
- **`stop_mac_app`** - Requires in-memory process tracking from `launch_mac_app`

**Testing Protocol for Stateful Tools:**
1. **Test the tool anyway** - Execute the Reloaderoo inspect command
2. **Expect failure** - Tool will likely fail due to missing state
3. **Mark as false negative** - Document the failure as expected due to stateful limitations
4. **Continue testing** - Do not attempt to fix or investigate the failure
5. **Report as finding** - Note in testing report that stateful tools failed as expected

**COMPLETE COVERAGE REQUIREMENTS:**
- ✅ **Test ALL tools individually** - No exceptions, every tool gets manual verification
- ✅ **Follow dependency graphs** - Test tools in correct order based on data dependencies
- ✅ **Capture key outputs** - Record UUIDs, paths, schemes needed by dependent tools
- ✅ **Test real workflows** - Complete end-to-end workflows from discovery to execution
- ✅ **Use tool-summary.js script** - Accurate tool/resource counting and discovery
- ✅ **Document all observations** - Record exactly what you see via testing
- ✅ **Report discrepancies as findings** - Note unexpected results without investigation

**MANDATORY INDIVIDUAL TOOL TESTING PROTOCOL:**

**Step 1: Create Complete Tool Inventory**
```bash
# Use the official tool summary script to get accurate tool count and list
npm run tools > /tmp/summary_output.txt
TOTAL_TOOLS=$(grep "Tools:" /tmp/summary_output.txt | awk '{print $2}')
echo "TOTAL TOOLS TO TEST: $TOTAL_TOOLS"

# Generate detailed tool list and extract tool names
npm run tools:list > /tmp/tools_detailed.txt
grep "^   • " /tmp/tools_detailed.txt | sed 's/^   • //' > /tmp/tool_names.txt
```

**Step 2: Create TodoWrite Task List for Every Tool**
```bash
# Create individual todo items for each tool discovered
# Use the actual tool count from step 1
# Example for first few tools:
# 1. [ ] Test tool: doctor  
# 2. [ ] Test tool: list_devices
# 3. [ ] Test tool: list_sims
# ... (continue for ALL $TOTAL_TOOLS tools)
```

**Step 3: Test Each Tool Individually**
For EVERY tool in the list:
```bash
# Test each tool individually - NO BATCHING
npx reloaderoo@latest inspect call-tool "TOOL_NAME" --params 'APPROPRIATE_PARAMS' -- node build/index.js

# Mark tool as completed in TodoWrite IMMEDIATELY after testing
# Record result (success/failure/blocked) for each tool
```

**Step 4: Validate Complete Coverage**
```bash
# Verify all tools tested
COMPLETED_TOOLS=$(count completed todo items)
if [ $COMPLETED_TOOLS -ne $TOTAL_TOOLS ]; then
    echo "ERROR: Testing incomplete. $COMPLETED_TOOLS/$TOTAL_TOOLS tested"
    exit 1
fi
```

**CRITICAL: NO TOOL LEFT UNTESTED**
- **Every tool name from the JSON list must be individually tested**
- **Every tool must have a TodoWrite entry that gets marked completed**
- **Tools that fail due to missing parameters should be tested anyway and marked as blocked**
- **Tools that require setup (like running processes) should be tested and documented as requiring dependencies**
- **NO ASSUMPTIONS**: Test tools even if they seem redundant or similar to others

**BLACK BOX TESTING ENFORCEMENT:**
- ✅ **Test only through Reloaderoo MCP interface** - Simulates real-world MCP client usage
- ✅ **Use task lists** - Track progress with TodoWrite tool for every single tool
- ✅ **Tick off each tool** - Mark completed in task list after manual verification
- ✅ **Manual oversight** - Human verification of each tool's input and output
- ❌ **Never examine source code** - No reading implementation files during testing
- ❌ **Never diagnose internal issues** - No investigation of build processes or tool registration
- ❌ **Never suggest implementation fixes** - Report issues as findings, don't solve them
- ❌ **Never use scripts for tool testing** - Each tool must be manually executed and verified

## Testing Psychology & Bias Prevention

**COMMON ANTI-PATTERNS TO AVOID:**

**1. Efficiency Bias (FORBIDDEN)**
- **Symptom**: "These tools look similar, I'll test one to validate the others"
- **Correction**: Every tool is unique and must be tested individually
- **Enforcement**: Count tools at start, verify same count tested at end

**2. Pattern Recognition Override (FORBIDDEN)**  
- **Symptom**: "I see the pattern, the rest will work the same way"
- **Correction**: Patterns may hide edge cases, bugs, or different implementations
- **Enforcement**: No assumptions allowed, test every tool regardless of apparent similarity

**3. Time Pressure Shortcuts (FORBIDDEN)**
- **Symptom**: "This is taking too long, let me speed up by sampling"
- **Correction**: This is explicitly a long-running task with no time constraints
- **Enforcement**: Thoroughness is the ONLY priority, efficiency is irrelevant

**4. False Confidence (FORBIDDEN)**
- **Symptom**: "The architecture is solid, so all tools must work"
- **Correction**: Architecture validation does not guarantee individual tool functionality
- **Enforcement**: Test tools to discover actual issues, not to confirm assumptions

**MANDATORY MINDSET:**
- **Every tool is potentially broken** until individually tested
- **Every tool may have unique edge cases** not covered by similar tools
- **Every tool deserves individual attention** regardless of apparent redundancy
- **Testing completion means EVERY tool tested**, not "enough tools to validate patterns"
- **The goal is discovering problems**, not confirming everything works

**TESTING COMPLETENESS CHECKLIST:**
- [ ] Generated complete tool list using `npm run tools:list`
- [ ] Created TodoWrite entry for every single tool
- [ ] Tested every tool individually via Reloaderoo inspect
- [ ] Marked every tool as completed in TodoWrite
- [ ] Verified tool count: tested_count == total_count
- [ ] Documented all results, including failures and blocked tools
- [ ] Created final report covering ALL tools, not just successful ones

## Tool Dependency Graph Testing Strategy

**CRITICAL: Tools must be tested in dependency order:**

1. **Foundation Tools** (provide data for other tools):
   - `doctor` - System info
   - `list_devices` - Device UUIDs
   - `list_sims` - Simulator UUIDs  
   - `discover_projs` - Project/workspace paths

2. **Discovery Tools** (provide metadata for build tools):
   - `list_schemes` - Scheme names
   - `show_build_settings` - Build settings

3. **Build Tools** (create artifacts for install tools):
   - `build_*` tools - Create app bundles
   - `get_*_app_path_*` tools - Locate built app bundles
   - `get_*_bundle_id` tools - Extract bundle IDs

4. **Installation Tools** (depend on built artifacts):
   - `install_app_*` tools - Install built apps
   - `launch_app_*` tools - Launch installed apps

5. **Testing Tools** (depend on projects/schemes):
   - `test_*` tools - Run test suites

6. **UI Automation Tools** (depend on running apps):
   - `describe_ui`, `screenshot`, `tap`, etc.

**MANDATORY: Record Key Outputs**

Must capture and document these values for dependent tools:
- **Device UUIDs** from `list_devices`
- **Simulator UUIDs** from `list_sims`
- **Project/workspace paths** from `discover_projs`
- **Scheme names** from `list_schems_*`
- **App bundle paths** from `get_*_app_path_*`
- **Bundle IDs** from `get_*_bundle_id`

## Prerequisites

1. **Build the server**: `npm run build`
2. **Install jq**: `brew install jq` (required for JSON parsing)
3. **System Requirements**: macOS with Xcode installed, connected devices/simulators optional

## Step-by-Step Testing Process

**Note**: All tool and resource discovery now uses the official `tool-summary.js` script (available as `npm run tools`, `npm run tools:list`, and `npm run tools:all`) instead of direct reloaderoo calls. This ensures accurate counts and lists without hardcoded values.

### Step 1: Programmatic Discovery and Official Testing Lists

#### Generate Official Tool and Resource Lists using tool-summary.js

```bash
# Use the official tool summary script to get accurate counts and lists
npm run tools > /tmp/summary_output.txt

# Extract tool and resource counts from summary
TOOL_COUNT=$(grep "Tools:" /tmp/summary_output.txt | awk '{print $2}')
RESOURCE_COUNT=$(grep "Resources:" /tmp/summary_output.txt | awk '{print $2}')
echo "Official tool count: $TOOL_COUNT"
echo "Official resource count: $RESOURCE_COUNT"

# Generate detailed tool list for testing checklist
npm run tools:list > /tmp/tools_detailed.txt

# Extract tool names from the detailed output
grep "^   • " /tmp/tools_detailed.txt | sed 's/^   • //' > /tmp/tool_names.txt
echo "Tool names saved to /tmp/tool_names.txt"

# Generate detailed resource list for testing checklist  
npm run tools:all > /tmp/tools_and_resources.txt

# Extract resource URIs from the detailed output
sed -n '/📚 Available Resources:/,/✅ Tool summary complete!/p' /tmp/tools_and_resources.txt | grep "^   • " | sed 's/^   • //' | cut -d' ' -f1 > /tmp/resource_uris.txt
echo "Resource URIs saved to /tmp/resource_uris.txt"
```

#### Create Tool Testing Checklist

```bash
# Generate markdown checklist from actual tool list
echo "# Official Tool Testing Checklist" > /tmp/tool_testing_checklist.md
echo "" >> /tmp/tool_testing_checklist.md
echo "Total Tools: $TOOL_COUNT" >> /tmp/tool_testing_checklist.md
echo "" >> /tmp/tool_testing_checklist.md

# Add each tool as unchecked item
while IFS= read -r tool_name; do
    echo "- [ ] $tool_name" >> /tmp/tool_testing_checklist.md
done < /tmp/tool_names.txt

echo "Tool testing checklist created at /tmp/tool_testing_checklist.md"
```

#### Create Resource Testing Checklist

```bash
# Generate markdown checklist from actual resource list
echo "# Official Resource Testing Checklist" > /tmp/resource_testing_checklist.md
echo "" >> /tmp/resource_testing_checklist.md
echo "Total Resources: $RESOURCE_COUNT" >> /tmp/resource_testing_checklist.md
echo "" >> /tmp/resource_testing_checklist.md

# Add each resource as unchecked item
while IFS= read -r resource_uri; do
    echo "- [ ] $resource_uri" >> /tmp/resource_testing_checklist.md
done < /tmp/resource_uris.txt

echo "Resource testing checklist created at /tmp/resource_testing_checklist.md"
```

### Step 2: Tool Schema Discovery for Parameter Testing

#### Extract Tool Schema Information

```bash
# Get schema for specific tool to understand required parameters
TOOL_NAME="list_devices"
jq --arg tool "$TOOL_NAME" '.tools[] | select(.name == $tool) | .inputSchema' /tmp/tools.json

# Get tool description for usage guidance
jq --arg tool "$TOOL_NAME" '.tools[] | select(.name == $tool) | .description' /tmp/tools.json

# Generate parameter template for tool testing
jq --arg tool "$TOOL_NAME" '.tools[] | select(.name == $tool) | .inputSchema.properties // {}' /tmp/tools.json
```

#### Batch Schema Extraction

```bash
# Create schema reference file for all tools
echo "# Tool Schema Reference" > /tmp/tool_schemas.md
echo "" >> /tmp/tool_schemas.md

while IFS= read -r tool_name; do
    echo "## $tool_name" >> /tmp/tool_schemas.md
    echo "" >> /tmp/tool_schemas.md
    
    # Get description
    description=$(jq -r --arg tool "$tool_name" '.tools[] | select(.name == $tool) | .description' /tmp/tools.json)
    echo "**Description:** $description" >> /tmp/tool_schemas.md
    echo "" >> /tmp/tool_schemas.md
    
    # Get required parameters
    required=$(jq -r --arg tool "$tool_name" '.tools[] | select(.name == $tool) | .inputSchema.required // [] | join(", ")' /tmp/tools.json)
    if [ "$required" != "" ]; then
        echo "**Required Parameters:** $required" >> /tmp/tool_schemas.md
    else
        echo "**Required Parameters:** None" >> /tmp/tool_schemas.md
    fi
    echo "" >> /tmp/tool_schemas.md
    
    # Get all parameters
    echo "**All Parameters:**" >> /tmp/tool_schemas.md
    jq --arg tool "$tool_name" '.tools[] | select(.name == $tool) | .inputSchema.properties // {} | keys[]' /tmp/tools.json | while read param; do
        echo "- $param" >> /tmp/tool_schemas.md
    done
    echo "" >> /tmp/tool_schemas.md
    
done < /tmp/tool_names.txt

echo "Tool schema reference created at /tmp/tool_schemas.md"
```

### Step 3: Manual Tool-by-Tool Testing

#### 🚨 CRITICAL: STEP-BY-STEP BLACK BOX TESTING PROCESS

**ABSOLUTE RULE: ALL TESTING MUST BE DONE MANUALLY, ONE TOOL AT A TIME USING RELOADEROO INSPECT**

**SYSTEMATIC TESTING PROCESS:**

1. **Create TodoWrite Task List**
   - Add all tools (from `npm run tools` count) to task list before starting
   - Mark each tool as "pending" initially
   - Update status to "in_progress" when testing begins
   - Mark "completed" only after manual verification

2. **Test Each Tool Individually**
   - Execute ONLY via `npx reloaderoo@latest inspect call-tool "TOOL_NAME" --params 'JSON' -- node build/index.js`
   - Wait for complete response before proceeding to next tool
   - Read and verify each tool's output manually
   - Record key outputs (UUIDs, paths, schemes) for dependent tools

3. **Manual Verification Requirements**
   - ✅ **Read each response** - Manually verify tool output makes sense
   - ✅ **Check for errors** - Identify any tool failures or unexpected responses  
   - ✅ **Record UUIDs/paths** - Save outputs needed for dependent tools
   - ✅ **Update task list** - Mark each tool complete after verification
   - ✅ **Document issues** - Record any problems found during testing

4. **FORBIDDEN SHORTCUTS:**
   - ❌ **NO SCRIPTS** - Scripts hide what's happening and prevent proper verification
   - ❌ **NO AUTOMATION** - Every tool call must be manually executed and verified
   - ❌ **NO BATCHING** - Cannot test multiple tools simultaneously
   - ❌ **NO MCP DIRECT CALLS** - Only Reloaderoo inspect commands allowed

#### Phase 1: Infrastructure Validation

**Manual Commands (execute individually):**

```bash
# Test server connectivity
npx reloaderoo@latest inspect ping -- node build/index.js

# Get server information  
npx reloaderoo@latest inspect server-info -- node build/index.js

# Verify tool count manually
npx reloaderoo@latest inspect list-tools -- node build/index.js 2>/dev/null | jq '.tools | length'

# Verify resource count manually
npx reloaderoo@latest inspect list-resources -- node build/index.js 2>/dev/null | jq '.resources | length'
```

#### Phase 2: Resource Testing

```bash
# Test each resource systematically
while IFS= read -r resource_uri; do
    echo "Testing resource: $resource_uri"
    npx reloaderoo@latest inspect read-resource "$resource_uri" -- node build/index.js 2>/dev/null
    echo "---"
done < /tmp/resource_uris.txt
```

#### Phase 3: Foundation Tools (Data Collection)

**CRITICAL: Capture ALL key outputs for dependent tools**

```bash
echo "=== FOUNDATION TOOL TESTING & DATA COLLECTION ==="

# 1. Test doctor (no dependencies)
echo "Testing doctor..."
npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/index.js 2>/dev/null

# 2. Collect device data
echo "Collecting device UUIDs..."
npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/index.js 2>/dev/null > /tmp/devices_output.json
DEVICE_UUIDS=$(jq -r '.content[0].text' /tmp/devices_output.json | grep -E "UDID: [A-F0-9-]+" | sed 's/.*UDID: //' | head -2)
echo "Device UUIDs captured: $DEVICE_UUIDS"

# 3. Collect simulator data  
echo "Collecting simulator UUIDs..."
npx reloaderoo@latest inspect call-tool "list_sims" --params '{}' -- node build/index.js 2>/dev/null > /tmp/sims_output.json
SIMULATOR_UUIDS=$(jq -r '.content[0].text' /tmp/sims_output.json | grep -E "\([A-F0-9-]+\)" | sed 's/.*(\([A-F0-9-]*\)).*/\1/' | head -3)
echo "Simulator UUIDs captured: $SIMULATOR_UUIDS"

# 4. Collect project data
echo "Collecting project paths..."
npx reloaderoo@latest inspect call-tool "discover_projs" --params '{"workspaceRoot": "/Volumes/Developer/XcodeBuildMCP"}' -- node build/index.js 2>/dev/null > /tmp/projects_output.json
PROJECT_PATHS=$(jq -r '.content[1].text' /tmp/projects_output.json | grep -E "\.xcodeproj$" | sed 's/.*- //' | head -3)
WORKSPACE_PATHS=$(jq -r '.content[2].text' /tmp/projects_output.json | grep -E "\.xcworkspace$" | sed 's/.*- //' | head -2)
echo "Project paths captured: $PROJECT_PATHS"
echo "Workspace paths captured: $WORKSPACE_PATHS"

# Save key data for dependent tools
echo "$DEVICE_UUIDS" > /tmp/device_uuids.txt
echo "$SIMULATOR_UUIDS" > /tmp/simulator_uuids.txt  
echo "$PROJECT_PATHS" > /tmp/project_paths.txt
echo "$WORKSPACE_PATHS" > /tmp/workspace_paths.txt
```

#### Phase 4: Discovery Tools (Metadata Collection)

```bash
echo "=== DISCOVERY TOOL TESTING & METADATA COLLECTION ==="

# Collect schemes for each project
while IFS= read -r project_path; do
    if [ -n "$project_path" ]; then
        echo "Getting schemes for: $project_path"
        npx reloaderoo@latest inspect call-tool "list_schems_proj" --params "{\"projectPath\": \"$project_path\"}" -- node build/index.js 2>/dev/null > /tmp/schemes_$$.json
        SCHEMES=$(jq -r '.content[1].text' /tmp/schemes_$$.json 2>/dev/null || echo "NoScheme")
        echo "$project_path|$SCHEMES" >> /tmp/project_schemes.txt
        echo "Schemes captured for $project_path: $SCHEMES"
    fi
done < /tmp/project_paths.txt

# Collect schemes for each workspace
while IFS= read -r workspace_path; do
    if [ -n "$workspace_path" ]; then
        echo "Getting schemes for: $workspace_path"
        npx reloaderoo@latest inspect call-tool "list_schemes" --params "{\"workspacePath\": \"$workspace_path\"}" -- node build/index.js 2>/dev/null > /tmp/ws_schemes_$$.json
        SCHEMES=$(jq -r '.content[1].text' /tmp/ws_schemes_$$.json 2>/dev/null || echo "NoScheme")
        echo "$workspace_path|$SCHEMES" >> /tmp/workspace_schemes.txt
        echo "Schemes captured for $workspace_path: $SCHEMES"
    fi
done < /tmp/workspace_paths.txt
```

#### Phase 5: Manual Individual Tool Testing (All Tools)

**CRITICAL: Test every single tool manually, one at a time**

**Manual Testing Process:**

1. **Create task list** with TodoWrite tool for all tools (using count from `npm run tools`)
2. **Test each tool individually** with proper parameters
3. **Mark each tool complete** in task list after manual verification
4. **Record results** and observations for each tool
5. **NO SCRIPTS** - Each command executed manually

**STEP-BY-STEP MANUAL TESTING COMMANDS:**

```bash
# STEP 1: Test foundation tools (no parameters required)
# Execute each command individually, wait for response, verify manually
npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/index.js
# [Wait for response, read output, mark tool complete in task list]

npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/index.js
# [Record device UUIDs from response for dependent tools]

npx reloaderoo@latest inspect call-tool "list_sims" --params '{}' -- node build/index.js
# [Record simulator UUIDs from response for dependent tools]

# STEP 2: Test project discovery (use discovered project paths)
npx reloaderoo@latest inspect call-tool "list_schems_proj" --params '{"projectPath": "/actual/path/from/discover_projs.xcodeproj"}' -- node build/index.js
# [Record scheme names from response for build tools]

# STEP 3: Test workspace tools (use discovered workspace paths)  
npx reloaderoo@latest inspect call-tool "list_schemes" --params '{"workspacePath": "/actual/path/from/discover_projs.xcworkspace"}' -- node build/index.js
# [Record scheme names from response for build tools]

# STEP 4: Test simulator tools (use captured simulator UUIDs from step 1)
npx reloaderoo@latest inspect call-tool "boot_sim" --params '{"simulatorUuid": "ACTUAL_UUID_FROM_LIST_SIMS"}' -- node build/index.js
# [Verify simulator boots successfully]

# STEP 5: Test build tools (requires project + scheme + simulator from previous steps)
npx reloaderoo@latest inspect call-tool "build_sim_id_proj" --params '{"projectPath": "/actual/project.xcodeproj", "scheme": "ActualSchemeName", "simulatorId": "ACTUAL_SIMULATOR_UUID"}' -- node build/index.js
# [Verify build succeeds and record app bundle path]
```

**CRITICAL: EACH COMMAND MUST BE:**
1. **Executed individually** - One command at a time, manually typed or pasted
2. **Verified manually** - Read the complete response before continuing
3. **Tracked in task list** - Mark tool complete only after verification
4. **Use real data** - Replace placeholder values with actual captured data
5. **Wait for completion** - Allow each command to finish before proceeding

### TESTING VIOLATIONS AND ENFORCEMENT

**🚨 CRITICAL VIOLATIONS THAT WILL TERMINATE TESTING:**

1. **Direct MCP Tool Usage Violation:**
   ```typescript
   // ❌ IMMEDIATE TERMINATION - Using MCP tools directly
   await mcp__XcodeBuildMCP__list_devices();
   const result = await list_sims();
   ```

2. **Script-Based Testing Violation:**
   ```bash
   # ❌ IMMEDIATE TERMINATION - Using scripts to test tools
   for tool in $(cat tool_list.txt); do
     npx reloaderoo inspect call-tool "$tool" --params '{}' -- node build/index.js
   done
   ```

3. **Batching/Automation Violation:**
   ```bash
   # ❌ IMMEDIATE TERMINATION - Testing multiple tools simultaneously
   npx reloaderoo inspect call-tool "list_devices" & npx reloaderoo inspect call-tool "list_sims" &
   ```

4. **Source Code Examination Violation:**
   ```typescript
   // ❌ IMMEDIATE TERMINATION - Reading implementation during testing
   const toolImplementation = await Read('/src/mcp/tools/device-shared/list_devices.ts');
   ```

**ENFORCEMENT PROCEDURE:**
1. **First Violation**: Immediate correction and restart of testing process
2. **Documentation Update**: Add explicit prohibition to prevent future violations  
3. **Method Validation**: Ensure all future testing uses only Reloaderoo inspect commands
4. **Progress Reset**: Restart testing from foundation tools if direct MCP usage detected

**VALID TESTING SEQUENCE EXAMPLE:**
```bash
# ✅ CORRECT - Step-by-step manual execution via Reloaderoo
# Tool 1: Test doctor
npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/index.js
# [Read response, verify, mark complete in TodoWrite]

# Tool 2: Test list_devices  
npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/index.js
# [Read response, capture UUIDs, mark complete in TodoWrite]

# Tool 3: Test list_sims
npx reloaderoo@latest inspect call-tool "list_sims" --params '{}' -- node build/index.js
# [Read response, capture UUIDs, mark complete in TodoWrite]

# Tool X: Test stateful tool (expected to fail)
npx reloaderoo@latest inspect call-tool "swift_package_stop" --params '{"pid": 12345}' -- node build/index.js
# [Tool fails as expected - no in-memory state available]
# [Mark as "false negative - stateful tool limitation" in TodoWrite]
# [Continue to next tool without investigation]

# Continue individually for all tools (use count from npm run tools)...
```

**HANDLING STATEFUL TOOL FAILURES:**
```bash
# ✅ CORRECT Response to Expected Stateful Tool Failure
# Tool fails with "No process found" or similar state-related error
# Response: Mark tool as "tested - false negative (stateful)" in task list
# Do NOT attempt to diagnose, fix, or investigate the failure
# Continue immediately to next tool in sequence
```

## Error Testing

```bash
# Test error handling systematically
echo "=== Error Testing ==="

# Test with invalid JSON parameters
echo "Testing invalid parameter types..."
npx reloaderoo@latest inspect call-tool list_schems_proj --params '{"projectPath": 123}' -- node build/index.js 2>/dev/null

# Test with non-existent paths
echo "Testing non-existent paths..."
npx reloaderoo@latest inspect call-tool list_schems_proj --params '{"projectPath": "/nonexistent/path.xcodeproj"}' -- node build/index.js 2>/dev/null

# Test with invalid UUIDs
echo "Testing invalid UUIDs..."
npx reloaderoo@latest inspect call-tool boot_sim --params '{"simulatorUuid": "invalid-uuid"}' -- node build/index.js 2>/dev/null
```

## Testing Report Generation

```bash
# Create comprehensive testing session report
cat > TESTING_SESSION_$(date +%Y-%m-%d).md << EOF
# Manual Testing Session - $(date +%Y-%m-%d)

## Environment
- macOS Version: $(sw_vers -productVersion)
- XcodeBuildMCP Version: $(jq -r '.version' package.json 2>/dev/null || echo "unknown")
- Testing Method: Reloaderoo @latest via npx

## Official Counts (Programmatically Verified)
- Total Tools: $TOOL_COUNT
- Total Resources: $RESOURCE_COUNT

## Test Results
[Document test results here]

## Issues Found
[Document any discrepancies or failures]

## Performance Notes
[Document response times and performance observations]
EOF

echo "Testing session template created: TESTING_SESSION_$(date +%Y-%m-%d).md"
```

### Key Commands Reference

```bash
# Essential testing commands
npx reloaderoo@latest inspect ping -- node build/index.js
npx reloaderoo@latest inspect server-info -- node build/index.js
npx reloaderoo@latest inspect list-tools -- node build/index.js | jq '.tools | length'
npx reloaderoo@latest inspect list-resources -- node build/index.js | jq '.resources | length'
npx reloaderoo@latest inspect call-tool TOOL_NAME --params '{}' -- node build/index.js
npx reloaderoo@latest inspect read-resource "xcodebuildmcp://RESOURCE" -- node build/index.js

# Schema extraction
jq --arg tool "TOOL_NAME" '.tools[] | select(.name == $tool) | .inputSchema' /tmp/tools.json
jq --arg tool "TOOL_NAME" '.tools[] | select(.name == $tool) | .description' /tmp/tools.json
```

## Troubleshooting

### Common Issues

#### 1. Reloaderoo Command Timeouts
**Symptoms**: Commands hang or timeout after extended periods
**Cause**: Server startup issues or MCP protocol communication problems
**Resolution**: 
- Verify server builds successfully: `npm run build`
- Test direct server startup: `node build/index.js`
- Check for TypeScript compilation errors

#### 2. Tool Parameter Validation Errors
**Symptoms**: Tools return parameter validation errors
**Cause**: Missing or incorrect required parameters
**Resolution**:
- Check tool schema: `jq --arg tool "TOOL_NAME" '.tools[] | select(.name == $tool) | .inputSchema' /tmp/tools.json`
- Verify parameter types and required fields
- Use captured dependency data (UUIDs, paths, schemes)

#### 3. "No Such Tool" Errors
**Symptoms**: Reloaderoo reports tool not found
**Cause**: Tool name mismatch or server registration issues
**Resolution**:
- Verify tool exists in list: `npx reloaderoo@latest inspect list-tools -- node build/index.js | jq '.tools[].name'`
- Check exact tool name spelling and case sensitivity
- Ensure server built successfully

#### 4. Empty or Malformed Responses
**Symptoms**: Tools return empty responses or JSON parsing errors
**Cause**: Tool implementation issues or server errors
**Resolution**:
- Document as testing finding - do not investigate implementation
- Mark tool as "failed - empty response" in task list
- Continue with next tool in sequence

This systematic approach ensures comprehensive, accurate testing using programmatic discovery and validation of all XcodeBuildMCP functionality through the MCP interface exclusively.
```

--------------------------------------------------------------------------------
/scripts/check-code-patterns.js:
--------------------------------------------------------------------------------

```javascript
#!/usr/bin/env node

/**
 * XcodeBuildMCP Code Pattern Violations Checker
 * 
 * Validates that all code files follow XcodeBuildMCP-specific architectural patterns.
 * This script focuses on domain-specific rules that ESLint cannot express.
 * 
 * USAGE:
 *   node scripts/check-code-patterns.js [--pattern=vitest|execsync|handler|handler-testing|all]
 *   node scripts/check-code-patterns.js --help
 * 
 * ARCHITECTURAL RULES ENFORCED:
 * 1. NO vitest mocking patterns (vi.mock, vi.fn, .mockResolvedValue, etc.)
 * 2. NO execSync usage in production code (use CommandExecutor instead)
 * 3. ONLY dependency injection with createMockExecutor() and createMockFileSystemExecutor()
 * 4. NO handler signature violations (handlers must have exact MCP SDK signatures)
 * 5. NO handler testing violations (test logic functions, not handlers directly)
 * 
 * For comprehensive code quality documentation, see docs/CODE_QUALITY.md
 * 
 * Note: General code quality rules (TypeScript, ESLint) are handled by other tools.
 * This script only enforces XcodeBuildMCP-specific architectural patterns.
 */

import { readFileSync, readdirSync, statSync } from 'fs';
import { join, relative } from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const projectRoot = join(__dirname, '..');

// Parse command line arguments
const args = process.argv.slice(2);
const patternFilter = args.find(arg => arg.startsWith('--pattern='))?.split('=')[1] || 'all';
const showHelp = args.includes('--help') || args.includes('-h');

if (showHelp) {
  console.log(`
XcodeBuildMCP Code Pattern Violations Checker

USAGE:
  node scripts/check-code-patterns.js [options]

OPTIONS:
  --pattern=TYPE    Check specific pattern type (vitest|execsync|handler|handler-testing|server-typing|all) [default: all]
  --help, -h        Show this help message

PATTERN TYPES:
  vitest           Check only vitest mocking violations (vi.mock, vi.fn, etc.)
  execsync         Check only execSync usage in production code
  handler          Check only handler signature violations
  handler-testing  Check only handler testing violations (testing handlers instead of logic functions)
  server-typing    Check only improper server typing violations (Record<string, unknown> casts)
  all              Check all pattern violations (default)

  Note: General code quality (TypeScript, etc.) is handled by ESLint

EXAMPLES:
  node scripts/check-code-patterns.js
  node scripts/check-code-patterns.js --pattern=vitest
  node scripts/check-code-patterns.js --pattern=handler
  node scripts/check-code-patterns.js --pattern=handler-testing
  node scripts/check-code-patterns.js --pattern=server-typing
`);
  process.exit(0);
}

// Patterns for execSync usage in production code (FORBIDDEN)
// Note: execSync is allowed in test files for mocking, but not in production code
const EXECSYNC_PATTERNS = [
  /\bexecSync\s*\(/,                 // Direct execSync usage
  /\bexecSyncFn\s*[=:]/,             // execSyncFn parameter or assignment
  /^import\s+(?!type\s)[^}]*from\s+['"]child_process['"]/m,    // Importing from child_process (except type-only imports)
  /^import\s+{[^}]*(?:exec|spawn|execSync)[^}]*}\s+from\s+['"](?:node:)?child_process['"]/m, // Named imports of functions
];

// CRITICAL: ALL VITEST MOCKING PATTERNS ARE COMPLETELY FORBIDDEN
// ONLY dependency injection with approved mock utilities is allowed
const VITEST_GENERIC_PATTERNS = [
  /vi\.mock\s*\(/,                   // vi.mock() - BANNED
  /vi\.fn\s*\(/,                     // vi.fn() - BANNED
  /vi\.mocked\s*\(/,                 // vi.mocked() - BANNED
  /vi\.spyOn\s*\(/,                  // vi.spyOn() - BANNED
  /vi\.clearAllMocks\s*\(/,          // vi.clearAllMocks() - BANNED
  /\.mockResolvedValue/,             // .mockResolvedValue - BANNED
  /\.mockRejectedValue/,             // .mockRejectedValue - BANNED
  /\.mockReturnValue/,               // .mockReturnValue - BANNED
  /\.mockImplementation/,            // .mockImplementation - BANNED
  /\.mockClear/,                     // .mockClear - BANNED
  /\.mockReset/,                     // .mockReset - BANNED
  /\.mockRestore/,                   // .mockRestore - BANNED
  /\.toHaveBeenCalled/,              // .toHaveBeenCalled - BANNED
  /\.toHaveBeenCalledWith/,          // .toHaveBeenCalledWith - BANNED
  /MockedFunction/,                  // MockedFunction type - BANNED
  /as MockedFunction/,               // Type casting to MockedFunction - BANNED
  /\bexecSync\b/,                    // execSync usage - BANNED (use executeCommand instead)
  /\bexecSyncFn\b/,                  // execSyncFn usage - BANNED (use executeCommand instead)
];

// APPROVED mock utilities - ONLY these are allowed
const APPROVED_MOCK_PATTERNS = [
  /\bcreateMockExecutor\b/,
  /\bcreateMockFileSystemExecutor\b/,
  /\bcreateNoopExecutor\b/,
  /\bcreateNoopFileSystemExecutor\b/,
  /\bcreateCommandMatchingMockExecutor\b/,
  /\bcreateMockEnvironmentDetector\b/,
];

// REFINED PATTERNS - Only flag ACTUAL vitest violations, not approved dependency injection patterns
// Manual executors and mock objects are APPROVED when used for dependency injection
const UNAPPROVED_MOCK_PATTERNS = [
  // ONLY ACTUAL VITEST PATTERNS (vi.* usage) - Everything else is approved
  /\bmock[A-Z][a-zA-Z0-9]*\s*=\s*vi\./,              // mockSomething = vi.fn() - vitest assignments only

  // No other patterns - manual executors and mock objects are approved for dependency injection
];

// Function to check if a line contains unapproved mock patterns
function hasUnapprovedMockPattern(line) {
  // Skip lines that contain approved patterns
  const hasApprovedPattern = APPROVED_MOCK_PATTERNS.some(pattern => pattern.test(line));
  if (hasApprovedPattern) {
    return false;
  }

  // Check for unapproved patterns
  return UNAPPROVED_MOCK_PATTERNS.some(pattern => pattern.test(line));
}

// Combined pattern checker for backward compatibility
const VITEST_MOCKING_PATTERNS = VITEST_GENERIC_PATTERNS;

// CRITICAL: ARCHITECTURAL VIOLATIONS - Utilities bypassing CommandExecutor (BANNED)
const UTILITY_BYPASS_PATTERNS = [
  /spawn\s*\(/,                      // Direct Node.js spawn usage in utilities - BANNED
  /exec\s*\(/,                       // Direct Node.js exec usage in utilities - BANNED  
  /execSync\s*\(/,                   // Direct Node.js execSync usage in utilities - BANNED
  /child_process\./,                 // Direct child_process module usage in utilities - BANNED
];

// TypeScript patterns are now handled by ESLint - removed from domain-specific checks
// ESLint has comprehensive TypeScript rules with proper test file exceptions

// CRITICAL: HANDLER SIGNATURE VIOLATIONS ARE FORBIDDEN
// MCP SDK requires handlers to have exact signatures: 
// Tools: (args: Record<string, unknown>) => Promise<ToolResponse>
// Resources: (uri: URL) => Promise<{ contents: Array<{ text: string }> }>
const HANDLER_SIGNATURE_VIOLATIONS = [
  /async\s+handler\s*\([^)]*:\s*[^,)]+,\s*[^)]+\s*:/ms,  // Handler with multiple parameters separated by comma - BANNED
  /async\s+handler\s*\(\s*args\?\s*:/ms,                   // Handler with optional args parameter - BANNED (should be required)
  /async\s+handler\s*\([^)]*,\s*[^)]*CommandExecutor/ms,  // Handler with CommandExecutor parameter - BANNED
  /async\s+handler\s*\([^)]*,\s*[^)]*FileSystemExecutor/ms, // Handler with FileSystemExecutor parameter - BANNED
  /async\s+handler\s*\([^)]*,\s*[^)]*Dependencies/ms,      // Handler with Dependencies parameter - BANNED
  /async\s+handler\s*\([^)]*,\s*[^)]*executor\s*:/ms,      // Handler with executor parameter - BANNED
  /async\s+handler\s*\([^)]*,\s*[^)]*dependencies\s*:/ms,  // Handler with dependencies parameter - BANNED
];

// CRITICAL: HANDLER TESTING IN TESTS IS FORBIDDEN
// Tests must ONLY call logic functions with dependency injection, NEVER handlers directly
// Handlers are thin wrappers for MCP SDK - testing them violates dependency injection architecture
const HANDLER_TESTING_VIOLATIONS = [
  /\.handler\s*\(/,                     // Direct handler calls in tests - BANNED
  /await\s+\w+\.handler\s*\(/,          // Awaited handler calls - BANNED
  /const\s+result\s*=\s*await\s+\w+\.handler/, // Handler result assignment - BANNED
  /expect\s*\(\s*await\s+\w+\.handler/, // Handler expectation calls - BANNED
];

// CRITICAL: IMPROPER SERVER TYPING PATTERNS ARE FORBIDDEN  
// Server instances must use proper MCP SDK types, not generic Record<string, unknown> casts
const IMPROPER_SERVER_TYPING_VIOLATIONS = [
  /as Record<string, unknown>.*server/,           // Casting server to Record - BANNED
  /server.*as Record<string, unknown>/,           // Casting server to Record - BANNED
  /mcpServer\?\s*:\s*Record<string, unknown>/,    // Typing server as Record - BANNED
  /server\.server\?\?\s*server.*as Record/,       // Complex server casting - BANNED
  /interface\s+MCPServerInterface\s*{/,           // Custom MCP interfaces when SDK types exist - BANNED
];

// ALLOWED PATTERNS for cleanup (not mocking)
const ALLOWED_CLEANUP_PATTERNS = [
  // All cleanup patterns removed - no exceptions allowed
];

// Patterns that indicate TRUE dependency injection approach
const DEPENDENCY_INJECTION_PATTERNS = [
  /createMockExecutor/,              // createMockExecutor usage
  /createMockFileSystemExecutor/,    // createMockFileSystemExecutor usage
  /executor\?\s*:\s*CommandExecutor/, // executor?: CommandExecutor parameter
];

function findTestFiles(dir) {
  const testFiles = [];

  function traverse(currentDir) {
    const items = readdirSync(currentDir);

    for (const item of items) {
      const fullPath = join(currentDir, item);
      const stat = statSync(fullPath);

      if (stat.isDirectory()) {
        // Skip node_modules and other non-relevant directories
        if (!item.startsWith('.') && item !== 'node_modules' && item !== 'dist' && item !== 'build') {
          traverse(fullPath);
        }
      } else if (item.endsWith('.test.ts') || item.endsWith('.test.js')) {
        testFiles.push(fullPath);
      }
    }
  }

  traverse(dir);
  return testFiles;
}

function findToolAndResourceFiles(dir) {
  const toolFiles = [];

  function traverse(currentDir) {
    const items = readdirSync(currentDir);

    for (const item of items) {
      const fullPath = join(currentDir, item);
      const stat = statSync(fullPath);

      if (stat.isDirectory()) {
        // Skip test directories and other non-relevant directories
        if (!item.startsWith('.') && item !== '__tests__' && item !== 'node_modules' && item !== 'dist' && item !== 'build') {
          traverse(fullPath);
        }
      } else if ((item.endsWith('.ts') || item.endsWith('.js')) && !item.includes('.test.') && item !== 'index.ts' && item !== 'index.js') {
        toolFiles.push(fullPath);
      }
    }
  }

  traverse(dir);
  return toolFiles;
}

function findUtilityFiles(dir) {
  const utilityFiles = [];

  function traverse(currentDir) {
    const items = readdirSync(currentDir);

    for (const item of items) {
      const fullPath = join(currentDir, item);
      const stat = statSync(fullPath);

      if (stat.isDirectory()) {
        // Skip test directories and other non-relevant directories
        if (!item.startsWith('.') && item !== '__tests__' && item !== 'node_modules' && item !== 'dist' && item !== 'build') {
          traverse(fullPath);
        }
      } else if ((item.endsWith('.ts') || item.endsWith('.js')) && !item.includes('.test.') && item !== 'index.ts' && item !== 'index.js') {
        // Only include key utility files that should use CommandExecutor
        // Exclude command.ts itself as it's the core implementation that is allowed to use spawn()
        if (fullPath.includes('/utils/') && (
          fullPath.includes('log_capture.ts') ||
          fullPath.includes('build.ts') ||
          fullPath.includes('simctl.ts')
        ) && !fullPath.includes('command.ts')) {
          utilityFiles.push(fullPath);
        }
      }
    }
  }

  traverse(dir);
  return utilityFiles;
}

// Helper function to determine if a file is a test file
function isTestFile(filePath) {
  return filePath.includes('__tests__') || filePath.endsWith('.test.ts') || filePath.endsWith('.test.js');
}

// Helper function to determine if a file is a production file
function isProductionFile(filePath) {
  return !isTestFile(filePath) && (filePath.endsWith('.ts') || filePath.endsWith('.js'));
}

// Helper function to determine if a file is allowed to use child_process
function isAllowedChildProcessFile(filePath) {
  // These files need direct child_process access for their core functionality
  return filePath.includes('command.ts') || // Core CommandExecutor implementation
    filePath.includes('swift_package_run.ts'); // Needs spawn for background process management
}

function analyzeTestFile(filePath) {
  try {
    const content = readFileSync(filePath, 'utf8');
    const relativePath = relative(projectRoot, filePath);

    // Check for vitest mocking patterns using new robust approach
    const vitestMockingDetails = [];
    const lines = content.split('\n');

    // 1. Check generic vi.* patterns (always violations)
    lines.forEach((line, index) => {
      VITEST_GENERIC_PATTERNS.forEach(pattern => {
        if (pattern.test(line)) {
          vitestMockingDetails.push({
            line: index + 1,
            content: line.trim(),
            pattern: pattern.source,
            type: 'vitest-generic'
          });
        }
      });

      // 2. Check for unapproved mock patterns
      if (hasUnapprovedMockPattern(line)) {
        // Find which specific pattern matched for better reporting
        const matchedPattern = UNAPPROVED_MOCK_PATTERNS.find(pattern => pattern.test(line));
        vitestMockingDetails.push({
          line: index + 1,
          content: line.trim(),
          pattern: matchedPattern ? matchedPattern.source : 'unapproved mock pattern',
          type: 'unapproved-mock'
        });
      }
    });

    const hasVitestMockingPatterns = vitestMockingDetails.length > 0;

    // TypeScript patterns now handled by ESLint
    const hasTypescriptAntipatterns = false;

    // Check for handler testing violations (FORBIDDEN - ARCHITECTURAL VIOLATION)
    const hasHandlerTestingViolations = HANDLER_TESTING_VIOLATIONS.some(pattern => pattern.test(content));

    // Check for improper server typing violations (FORBIDDEN - ARCHITECTURAL VIOLATION)
    const hasImproperServerTypingViolations = IMPROPER_SERVER_TYPING_VIOLATIONS.some(pattern => pattern.test(content));

    // Check for dependency injection patterns (TRUE DI)
    const hasDIPatterns = DEPENDENCY_INJECTION_PATTERNS.some(pattern => pattern.test(content));

    // Extract specific pattern occurrences for details
    const execSyncDetails = []; // Not applicable to test files
    const typescriptAntipatternDetails = []; // Unused - TypeScript handled by ESLint
    const handlerTestingDetails = [];
    const improperServerTypingDetails = [];

    lines.forEach((line, index) => {

      // TypeScript anti-patterns now handled by ESLint - removed from domain checks

      HANDLER_TESTING_VIOLATIONS.forEach(pattern => {
        if (pattern.test(line)) {
          handlerTestingDetails.push({
            line: index + 1,
            content: line.trim(),
            pattern: pattern.source
          });
        }
      });

      IMPROPER_SERVER_TYPING_VIOLATIONS.forEach(pattern => {
        if (pattern.test(line)) {
          improperServerTypingDetails.push({
            line: index + 1,
            content: line.trim(),
            pattern: pattern.source
          });
        }
      });
    });

    return {
      filePath: relativePath,
      hasExecSyncPatterns: false, // Not applicable to test files
      hasVitestMockingPatterns,
      hasTypescriptAntipatterns,
      hasHandlerTestingViolations,
      hasImproperServerTypingViolations,
      hasDIPatterns,
      execSyncDetails,
      vitestMockingDetails,
      typescriptAntipatternDetails,
      handlerTestingDetails,
      improperServerTypingDetails,
      needsConversion: hasVitestMockingPatterns || hasHandlerTestingViolations || hasImproperServerTypingViolations,
      isConverted: hasDIPatterns && !hasVitestMockingPatterns && !hasHandlerTestingViolations && !hasImproperServerTypingViolations,
      isMixed: (hasVitestMockingPatterns || hasHandlerTestingViolations || hasImproperServerTypingViolations) && hasDIPatterns
    };
  } catch (error) {
    console.error(`Error reading file ${filePath}: ${error.message}`);
    return null;
  }
}

function analyzeToolOrResourceFile(filePath) {
  try {
    const content = readFileSync(filePath, 'utf8');
    const relativePath = relative(projectRoot, filePath);

    // Check for execSync patterns in production code (excluding allowed files)
    const hasExecSyncPatterns = isProductionFile(filePath) &&
      !isAllowedChildProcessFile(filePath) &&
      EXECSYNC_PATTERNS.some(pattern => pattern.test(content));

    // Check for vitest mocking patterns using new robust approach
    const vitestMockingDetails = [];
    const lines = content.split('\n');

    // 1. Check generic vi.* patterns (always violations)
    lines.forEach((line, index) => {
      VITEST_GENERIC_PATTERNS.forEach(pattern => {
        if (pattern.test(line)) {
          vitestMockingDetails.push({
            line: index + 1,
            content: line.trim(),
            pattern: pattern.source,
            type: 'vitest-generic'
          });
        }
      });

      // 2. Check for unapproved mock patterns
      if (hasUnapprovedMockPattern(line)) {
        // Find which specific pattern matched for better reporting
        const matchedPattern = UNAPPROVED_MOCK_PATTERNS.find(pattern => pattern.test(line));
        vitestMockingDetails.push({
          line: index + 1,
          content: line.trim(),
          pattern: matchedPattern ? matchedPattern.source : 'unapproved mock pattern',
          type: 'unapproved-mock'
        });
      }
    });

    const hasVitestMockingPatterns = vitestMockingDetails.length > 0;

    // TypeScript patterns now handled by ESLint
    const hasTypescriptAntipatterns = false;

    // Check for dependency injection patterns (TRUE DI)
    const hasDIPatterns = DEPENDENCY_INJECTION_PATTERNS.some(pattern => pattern.test(content));

    // Check for handler signature violations (FORBIDDEN)
    const hasHandlerSignatureViolations = HANDLER_SIGNATURE_VIOLATIONS.some(pattern => pattern.test(content));

    // Check for improper server typing violations (FORBIDDEN - ARCHITECTURAL VIOLATION)
    const hasImproperServerTypingViolations = IMPROPER_SERVER_TYPING_VIOLATIONS.some(pattern => pattern.test(content));

    // Check for utility bypass patterns (ARCHITECTURAL VIOLATION)
    const hasUtilityBypassPatterns = UTILITY_BYPASS_PATTERNS.some(pattern => pattern.test(content));

    // Extract specific pattern occurrences for details
    const execSyncDetails = [];
    const typescriptAntipatternDetails = []; // Unused - TypeScript handled by ESLint
    const handlerSignatureDetails = [];
    const improperServerTypingDetails = [];
    const utilityBypassDetails = [];

    lines.forEach((line, index) => {
      if (isProductionFile(filePath) && !isAllowedChildProcessFile(filePath)) {
        EXECSYNC_PATTERNS.forEach(pattern => {
          if (pattern.test(line)) {
            execSyncDetails.push({
              line: index + 1,
              content: line.trim(),
              pattern: pattern.source
            });
          }
        });
      }

      // TypeScript anti-patterns now handled by ESLint - removed from domain checks

      IMPROPER_SERVER_TYPING_VIOLATIONS.forEach(pattern => {
        if (pattern.test(line)) {
          improperServerTypingDetails.push({
            line: index + 1,
            content: line.trim(),
            pattern: pattern.source
          });
        }
      });

      UTILITY_BYPASS_PATTERNS.forEach(pattern => {
        if (pattern.test(line)) {
          utilityBypassDetails.push({
            line: index + 1,
            content: line.trim(),
            pattern: pattern.source
          });
        }
      });
    });
    if (hasHandlerSignatureViolations) {
      // Use regex to find the violation and its line number
      const lines = content.split('\n');
      const fullContent = content;

      HANDLER_SIGNATURE_VIOLATIONS.forEach(pattern => {
        let match;
        const globalPattern = new RegExp(pattern.source, pattern.flags + 'g');
        while ((match = globalPattern.exec(fullContent)) !== null) {
          // Find which line this match is on
          const beforeMatch = fullContent.substring(0, match.index);
          const lineNumber = beforeMatch.split('\n').length;

          handlerSignatureDetails.push({
            line: lineNumber,
            content: match[0].replace(/\s+/g, ' ').trim(),
            pattern: pattern.source
          });
        }
      });
    }

    return {
      filePath: relativePath,
      hasExecSyncPatterns,
      hasVitestMockingPatterns,
      hasTypescriptAntipatterns,
      hasDIPatterns,
      hasHandlerSignatureViolations,
      hasImproperServerTypingViolations,
      hasUtilityBypassPatterns,
      execSyncDetails,
      vitestMockingDetails,
      typescriptAntipatternDetails,
      handlerSignatureDetails,
      improperServerTypingDetails,
      utilityBypassDetails,
      needsConversion: hasExecSyncPatterns || hasVitestMockingPatterns || hasHandlerSignatureViolations || hasImproperServerTypingViolations || hasUtilityBypassPatterns,
      isConverted: hasDIPatterns && !hasExecSyncPatterns && !hasVitestMockingPatterns && !hasHandlerSignatureViolations && !hasImproperServerTypingViolations && !hasUtilityBypassPatterns,
      isMixed: (hasExecSyncPatterns || hasVitestMockingPatterns || hasHandlerSignatureViolations || hasImproperServerTypingViolations || hasUtilityBypassPatterns) && hasDIPatterns
    };
  } catch (error) {
    console.error(`Error reading file ${filePath}: ${error.message}`);
    return null;
  }
}

function main() {
  console.log('🔍 XcodeBuildMCP Code Pattern Violations Checker\n');
  console.log(`🎯 Checking pattern type: ${patternFilter.toUpperCase()}\n`);
  console.log('CODE GUIDELINES ENFORCED:');
  console.log('✅ ONLY ALLOWED: createMockExecutor() and createMockFileSystemExecutor()');
  console.log('❌ BANNED: vitest mocking patterns (vi.mock, vi.fn, .mockResolvedValue, etc.)');
  console.log('❌ BANNED: execSync usage in production code (use CommandExecutor instead)');
  console.log('ℹ️  TypeScript patterns: Handled by ESLint with proper test exceptions');
  console.log('❌ BANNED: handler signature violations (handlers must have exact MCP SDK signatures)');
  console.log('❌ BANNED: handler testing violations (test logic functions, not handlers directly)');
  console.log('❌ BANNED: improper server typing (use McpServer type, not Record<string, unknown>)\n');

  const testFiles = findTestFiles(join(projectRoot, 'src'));
  const testResults = testFiles.map(analyzeTestFile).filter(Boolean);

  // Also check tool and resource files for TypeScript anti-patterns AND handler signature violations
  const toolFiles = findToolAndResourceFiles(join(projectRoot, 'src', 'mcp', 'tools'));
  const resourceFiles = findToolAndResourceFiles(join(projectRoot, 'src', 'mcp', 'resources'));
  const allToolAndResourceFiles = [...toolFiles, ...resourceFiles];
  const toolResults = allToolAndResourceFiles.map(analyzeToolOrResourceFile).filter(Boolean);

  // Check utility files for architectural violations (bypassing CommandExecutor)
  const utilityFiles = findUtilityFiles(join(projectRoot, 'src'));
  const utilityResults = utilityFiles.map(analyzeToolOrResourceFile).filter(Boolean);

  // Combine test, tool, and utility file results for analysis
  const results = [...testResults, ...toolResults, ...utilityResults];
  const handlerResults = toolResults;
  const utilityBypassResults = utilityResults.filter(r => r.hasUtilityBypassPatterns);

  // Filter results based on pattern type
  let filteredResults;
  let filteredHandlerResults = [];

  switch (patternFilter) {
    case 'vitest':
      filteredResults = results.filter(r => r.hasVitestMockingPatterns);
      console.log(`Filtering to show only vitest mocking violations (${filteredResults.length} files)`);
      break;
    case 'execsync':
      filteredResults = results.filter(r => r.hasExecSyncPatterns);
      console.log(`Filtering to show only execSync violations (${filteredResults.length} files)`);
      break;
    // TypeScript case removed - now handled by ESLint
    case 'handler':
      filteredResults = [];
      filteredHandlerResults = handlerResults.filter(r => r.hasHandlerSignatureViolations);
      console.log(`Filtering to show only handler signature violations (${filteredHandlerResults.length} files)`);
      break;
    case 'handler-testing':
      filteredResults = results.filter(r => r.hasHandlerTestingViolations);
      console.log(`Filtering to show only handler testing violations (${filteredResults.length} files)`);
      break;
    case 'server-typing':
      filteredResults = results.filter(r => r.hasImproperServerTypingViolations);
      console.log(`Filtering to show only improper server typing violations (${filteredResults.length} files)`);
      break;
    case 'all':
    default:
      filteredResults = results.filter(r => r.needsConversion);
      filteredHandlerResults = handlerResults.filter(r => r.hasHandlerSignatureViolations);
      console.log(`Showing all pattern violations (${filteredResults.length} test files + ${filteredHandlerResults.length} handler files)`);
      break;
  }

  const needsConversion = filteredResults;
  const converted = results.filter(r => r.isConverted);
  const mixed = results.filter(r => r.isMixed);
  const execSyncOnly = results.filter(r => r.hasExecSyncPatterns && !r.hasVitestMockingPatterns && true && !r.hasHandlerTestingViolations && !r.hasImproperServerTypingViolations && !r.hasDIPatterns);
  const vitestMockingOnly = results.filter(r => r.hasVitestMockingPatterns && !r.hasExecSyncPatterns && true && !r.hasHandlerTestingViolations && !r.hasImproperServerTypingViolations && !r.hasDIPatterns);
  const typescriptOnly = results.filter(r => r.false && !r.hasExecSyncPatterns && !r.hasVitestMockingPatterns && !r.hasHandlerTestingViolations && !r.hasImproperServerTypingViolations && !r.hasDIPatterns);
  const handlerTestingOnly = results.filter(r => r.hasHandlerTestingViolations && !r.hasExecSyncPatterns && !r.hasVitestMockingPatterns && true && !r.hasImproperServerTypingViolations && !r.hasDIPatterns);
  const improperServerTypingOnly = results.filter(r => r.hasImproperServerTypingViolations && !r.hasExecSyncPatterns && !r.hasVitestMockingPatterns && !r.hasHandlerTestingViolations && !r.hasDIPatterns);
  const noPatterns = results.filter(r => !r.hasExecSyncPatterns && !r.hasVitestMockingPatterns && true && !r.hasHandlerTestingViolations && !r.hasImproperServerTypingViolations && !r.hasDIPatterns);

  console.log(`📊 CODE PATTERN VIOLATION ANALYSIS`);
  console.log(`=================================`);
  console.log(`Total files analyzed: ${results.length}`);
  console.log(`🚨 FILES WITH VIOLATIONS: ${needsConversion.length}`);
  console.log(`  └─ execSync production violations: ${execSyncOnly.length}`);
  console.log(`  └─ vitest mocking violations: ${vitestMockingOnly.length}`);
  // TypeScript anti-patterns now handled by ESLint
  console.log(`  └─ handler testing violations: ${handlerTestingOnly.length}`);
  console.log(`  └─ improper server typing violations: ${improperServerTypingOnly.length}`);
  console.log(`🚨 ARCHITECTURAL VIOLATIONS: ${utilityBypassResults.length}`);
  console.log(`✅ COMPLIANT (best practices): ${converted.length}`);
  console.log(`⚠️  MIXED VIOLATIONS: ${mixed.length}`);
  console.log(`📝 No patterns detected: ${noPatterns.length}`);
  console.log('');

  if (needsConversion.length > 0) {
    console.log(`❌ FILES THAT NEED CONVERSION (${needsConversion.length}):`);
    console.log(`=====================================`);
    needsConversion.forEach((result, index) => {
      console.log(`${index + 1}. ${result.filePath}`);

      if (result.execSyncDetails && result.execSyncDetails.length > 0) {
        console.log(`   🚨 EXECSYNC PATTERNS (${result.execSyncDetails.length}):`);
        result.execSyncDetails.slice(0, 2).forEach(detail => {
          console.log(`   Line ${detail.line}: ${detail.content}`);
        });
        if (result.execSyncDetails.length > 2) {
          console.log(`   ... and ${result.execSyncDetails.length - 2} more execSync patterns`);
        }
        console.log(`   🔧 FIX: Replace execSync with CommandExecutor dependency injection`);
      }

      if (result.vitestMockingDetails.length > 0) {
        console.log(`   🧪 VITEST MOCKING PATTERNS (${result.vitestMockingDetails.length}):`);
        result.vitestMockingDetails.slice(0, 2).forEach(detail => {
          console.log(`   Line ${detail.line}: ${detail.content}`);
        });
        if (result.vitestMockingDetails.length > 2) {
          console.log(`   ... and ${result.vitestMockingDetails.length - 2} more vitest patterns`);
        }
      }

      // TypeScript violations now handled by ESLint - removed from domain checks

      if (result.handlerTestingDetails && result.handlerTestingDetails.length > 0) {
        console.log(`   🚨 HANDLER TESTING VIOLATIONS (${result.handlerTestingDetails.length}):`);
        result.handlerTestingDetails.slice(0, 2).forEach(detail => {
          console.log(`   Line ${detail.line}: ${detail.content}`);
        });
        if (result.handlerTestingDetails.length > 2) {
          console.log(`   ... and ${result.handlerTestingDetails.length - 2} more handler testing violations`);
        }
        console.log(`   🔧 FIX: Replace handler calls with logic function calls using dependency injection`);
      }

      if (result.improperServerTypingDetails && result.improperServerTypingDetails.length > 0) {
        console.log(`   🔧 IMPROPER SERVER TYPING VIOLATIONS (${result.improperServerTypingDetails.length}):`);
        result.improperServerTypingDetails.slice(0, 2).forEach(detail => {
          console.log(`   Line ${detail.line}: ${detail.content}`);
        });
        if (result.improperServerTypingDetails.length > 2) {
          console.log(`   ... and ${result.improperServerTypingDetails.length - 2} more server typing violations`);
        }
        console.log(`   🔧 FIX: Import McpServer from SDK and use proper typing instead of Record<string, unknown>`);
      }

      console.log('');
    });
  }

  // Utility bypass violations reporting  
  if (utilityBypassResults.length > 0) {
    console.log(`🚨 CRITICAL: UTILITY ARCHITECTURAL VIOLATIONS (${utilityBypassResults.length}):`);
    console.log(`=======================================================`);
    console.log('⚠️  These utilities bypass CommandExecutor and break our testing architecture!');
    console.log('');
    utilityBypassResults.forEach((result, index) => {
      console.log(`${index + 1}. ${result.filePath}`);

      if (result.utilityBypassDetails.length > 0) {
        console.log(`   🚨 BYPASS PATTERNS (${result.utilityBypassDetails.length}):`);
        result.utilityBypassDetails.forEach(detail => {
          console.log(`   Line ${detail.line}: ${detail.content}`);
        });
      }

      console.log('   🔧 FIX: Refactor to accept CommandExecutor and use it instead of direct spawn/exec calls');
      console.log('');
    });
  }

  // Handler signature violations reporting
  if (filteredHandlerResults.length > 0) {
    console.log(`🚨 HANDLER SIGNATURE VIOLATIONS (${filteredHandlerResults.length}):`);
    console.log(`===========================================`);
    filteredHandlerResults.forEach((result, index) => {
      console.log(`${index + 1}. ${result.filePath}`);

      if (result.handlerSignatureDetails.length > 0) {
        console.log(`   🛠️  HANDLER VIOLATIONS (${result.handlerSignatureDetails.length}):`);
        result.handlerSignatureDetails.forEach(detail => {
          console.log(`   Line ${detail.line}: ${detail.content}`);
        });
      }

      console.log('');
    });
  }

  if (mixed.length > 0) {
    console.log(`⚠️  FILES WITH MIXED PATTERNS (${mixed.length}):`);
    console.log(`===================================`);
    mixed.forEach((result, index) => {
      console.log(`${index + 1}. ${result.filePath}`);
      console.log(`   ⚠️  Contains both setTimeout and dependency injection patterns`);
      console.log('');
    });
  }

  if (converted.length > 0) {
    console.log(`✅ SUCCESSFULLY CONVERTED FILES (${converted.length}):`);
    console.log(`====================================`);
    converted.forEach((result, index) => {
      console.log(`${index + 1}. ${result.filePath}`);
    });
    console.log('');
  }

  // Summary for next steps
  const hasViolations = needsConversion.length > 0 || filteredHandlerResults.length > 0 || utilityBypassResults.length > 0;

  if (needsConversion.length > 0) {
    console.log(`🚨 CRITICAL ACTION REQUIRED (TEST FILES):`);
    console.log(`=======================================`);
    console.log(`1. IMMEDIATELY remove ALL vitest mocking from ${needsConversion.length} files`);
    console.log(`2. BANNED: vi.mock(), vi.fn(), .mockResolvedValue(), .toHaveBeenCalled(), etc.`);
    console.log(`3. BANNED: Testing handlers directly (.handler()) - test logic functions with dependency injection`);
    console.log(`4. ONLY ALLOWED: createMockExecutor() and createMockFileSystemExecutor()`);
    console.log(`4. Update plugin implementations to accept executor?: CommandExecutor parameter`);
    console.log(`5. Run this script again after each fix to track progress`);
    console.log('');

    // Show top files by total violation count
    const sortedByPatterns = needsConversion
      .sort((a, b) => {
        const totalA = (a.execSyncDetails?.length || 0) + a.vitestMockingDetails.length + (a.handlerTestingDetails?.length || 0) + (a.improperServerTypingDetails?.length || 0);
        const totalB = (b.execSyncDetails?.length || 0) + b.vitestMockingDetails.length + (b.handlerTestingDetails?.length || 0) + (b.improperServerTypingDetails?.length || 0);
        return totalB - totalA;
      })
      .slice(0, 5);

    console.log(`🚨 TOP 5 FILES WITH MOST VIOLATIONS:`);
    sortedByPatterns.forEach((result, index) => {
      const totalPatterns = (result.execSyncDetails?.length || 0) + result.vitestMockingDetails.length + (result.handlerTestingDetails?.length || 0) + (result.improperServerTypingDetails?.length || 0);
      console.log(`${index + 1}. ${result.filePath} (${totalPatterns} violations: ${result.execSyncDetails?.length || 0} execSync + ${result.vitestMockingDetails.length} vitest + ${result.handlerTestingDetails?.length || 0} handler + ${result.improperServerTypingDetails?.length || 0} server)`);
    });
    console.log('');
  }

  if (utilityBypassResults.length > 0) {
    console.log(`🚨 CRITICAL ACTION REQUIRED (UTILITY FILES):`);
    console.log(`==========================================`);
    console.log(`1. IMMEDIATELY fix ALL architectural violations in ${utilityBypassResults.length} files`);
    console.log(`2. Refactor utilities to accept CommandExecutor parameter`);
    console.log(`3. Replace direct spawn/exec calls with executor calls`);
    console.log(`4. These violations break our entire testing strategy`);
    console.log(`5. Run this script again after each fix to track progress`);
    console.log('');
  }

  if (filteredHandlerResults.length > 0) {
    console.log(`🚨 CRITICAL ACTION REQUIRED (HANDLER FILES):`);
    console.log(`==========================================`);
    console.log(`1. IMMEDIATELY fix ALL handler signature violations in ${filteredHandlerResults.length} files`);
    console.log(`2. Tools: Handler must be: async handler(args: Record<string, unknown>): Promise<ToolResponse>`);
    console.log(`3. Resources: Handler must be: async handler(uri: URL): Promise<{ contents: Array<{ text: string }> }>`);
    console.log(`4. Inject dependencies INSIDE handler body: const executor = getDefaultCommandExecutor()`);
    console.log(`5. Run this script again after each fix to track progress`);
    console.log('');
  }

  if (!hasViolations && mixed.length === 0) {
    console.log(`🎉 ALL FILES COMPLY WITH PROJECT STANDARDS!`);
    console.log(`==========================================`);
    console.log(`✅ All files use ONLY createMockExecutor() and createMockFileSystemExecutor()`);
    console.log(`✅ All files follow TypeScript best practices (no unsafe casts)`);
    console.log(`✅ All handler signatures comply with MCP SDK requirements`);
    console.log(`✅ All utilities properly use CommandExecutor dependency injection`);
    console.log(`✅ No violations detected!`);
  }

  // Exit with appropriate code
  process.exit(hasViolations || mixed.length > 0 ? 1 : 0);
}

main();
```
Page 10/11FirstPrevNextLast