#
tokens: 49233/50000 50/337 files (page 2/11)
lines: off (toggle) GitHub
raw markdown copy
This is page 2 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

--------------------------------------------------------------------------------
/src/mcp/tools/swift-package/swift_package_list.ts:
--------------------------------------------------------------------------------

```typescript
// Note: This tool shares the activeProcesses map with swift_package_run
// Since both are in the same workflow directory, they can share state

// Import the shared activeProcesses map from swift_package_run
// This maintains the same behavior as the original implementation
import { z } from 'zod';
import { ToolResponse, createTextContent } from '../../../types/common.ts';
import { createTypedTool } from '../../../utils/typed-tool-factory.ts';
import { getDefaultCommandExecutor } from '../../../utils/command.ts';

interface ProcessInfo {
  executableName?: string;
  startedAt: Date;
  packagePath: string;
}

const activeProcesses = new Map<number, ProcessInfo>();

/**
 * Process list dependencies for dependency injection
 */
export interface ProcessListDependencies {
  processMap?: Map<number, ProcessInfo>;
  arrayFrom?: typeof Array.from;
  dateNow?: typeof Date.now;
}

/**
 * Swift package list business logic - extracted for testability and separation of concerns
 * @param params - Parameters (unused, but maintained for consistency)
 * @param dependencies - Injectable dependencies for testing
 * @returns ToolResponse with process list information
 */
export async function swift_package_listLogic(
  params?: unknown,
  dependencies?: ProcessListDependencies,
): Promise<ToolResponse> {
  const processMap = dependencies?.processMap ?? activeProcesses;
  const arrayFrom = dependencies?.arrayFrom ?? Array.from;
  const dateNow = dependencies?.dateNow ?? Date.now;

  const processes = arrayFrom(processMap.entries());

  if (processes.length === 0) {
    return {
      content: [
        createTextContent('ℹ️ No Swift Package processes currently running.'),
        createTextContent('💡 Use swift_package_run to start an executable.'),
      ],
    };
  }

  const content = [createTextContent(`📋 Active Swift Package processes (${processes.length}):`)];

  for (const [pid, info] of processes) {
    // Use logical OR instead of nullish coalescing to treat empty strings as falsy
    // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
    const executableName = info.executableName || 'default';
    const runtime = Math.max(1, Math.round((dateNow() - info.startedAt.getTime()) / 1000));
    content.push(
      createTextContent(
        `  • PID ${pid}: ${executableName} (${info.packagePath}) - running ${runtime}s`,
      ),
    );
  }

  content.push(createTextContent('💡 Use swift_package_stop with a PID to terminate a process.'));

  return { content };
}

// Define schema as ZodObject (empty for this tool)
const swiftPackageListSchema = z.object({});

// Use z.infer for type safety
type SwiftPackageListParams = z.infer<typeof swiftPackageListSchema>;

export default {
  name: 'swift_package_list',
  description: 'Lists currently running Swift Package processes',
  schema: swiftPackageListSchema.shape, // MCP SDK compatibility
  handler: createTypedTool(
    swiftPackageListSchema,
    (params: SwiftPackageListParams) => {
      return swift_package_listLogic(params);
    },
    getDefaultCommandExecutor,
  ),
};

```

--------------------------------------------------------------------------------
/src/mcp/tools/logging/__tests__/index.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for logging workflow metadata
 */
import { describe, it, expect } from 'vitest';
import { workflow } from '../index.ts';

describe('logging workflow metadata', () => {
  describe('Workflow Structure', () => {
    it('should export workflow object with required properties', () => {
      expect(workflow).toHaveProperty('name');
      expect(workflow).toHaveProperty('description');
      expect(workflow).toHaveProperty('platforms');
      expect(workflow).toHaveProperty('targets');
      expect(workflow).toHaveProperty('capabilities');
    });

    it('should have correct workflow name', () => {
      expect(workflow.name).toBe('Log Capture & Management');
    });

    it('should have correct description', () => {
      expect(workflow.description).toBe(
        'Log capture and management tools for iOS simulators and physical devices. Start, stop, and analyze application and system logs during development and testing.',
      );
    });

    it('should have correct platforms array', () => {
      expect(workflow.platforms).toEqual(['iOS', 'watchOS', 'tvOS', 'visionOS']);
    });

    it('should have correct targets array', () => {
      expect(workflow.targets).toEqual(['simulator', 'device']);
    });

    it('should have correct capabilities array', () => {
      expect(workflow.capabilities).toEqual([
        'log-capture',
        'log-analysis',
        'debugging',
        'monitoring',
      ]);
    });
  });

  describe('Workflow Validation', () => {
    it('should have valid string properties', () => {
      expect(typeof workflow.name).toBe('string');
      expect(typeof workflow.description).toBe('string');
      expect(workflow.name.length).toBeGreaterThan(0);
      expect(workflow.description.length).toBeGreaterThan(0);
    });

    it('should have valid array properties', () => {
      expect(Array.isArray(workflow.platforms)).toBe(true);
      expect(Array.isArray(workflow.targets)).toBe(true);
      expect(Array.isArray(workflow.capabilities)).toBe(true);

      expect(workflow.platforms.length).toBeGreaterThan(0);
      expect(workflow.targets.length).toBeGreaterThan(0);
      expect(workflow.capabilities.length).toBeGreaterThan(0);
    });

    it('should contain expected platform values', () => {
      expect(workflow.platforms).toContain('iOS');
      expect(workflow.platforms).toContain('watchOS');
      expect(workflow.platforms).toContain('tvOS');
      expect(workflow.platforms).toContain('visionOS');
    });

    it('should contain expected target values', () => {
      expect(workflow.targets).toContain('simulator');
      expect(workflow.targets).toContain('device');
    });

    it('should contain expected capability values', () => {
      expect(workflow.capabilities).toContain('log-capture');
      expect(workflow.capabilities).toContain('log-analysis');
      expect(workflow.capabilities).toContain('debugging');
      expect(workflow.capabilities).toContain('monitoring');
    });

    it('should not have projectTypes property', () => {
      expect(workflow).not.toHaveProperty('projectTypes');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/mcp/tools/simulator-management/reset_sim_location.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { ToolResponse } from '../../../types/common.ts';
import { log } from '../../../utils/logging/index.ts';
import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/execution/index.ts';
import { createTypedTool } from '../../../utils/typed-tool-factory.ts';

// Define schema as ZodObject
const resetSimulatorLocationSchema = z.object({
  simulatorUuid: z
    .string()
    .describe('UUID of the simulator to use (obtained from list_simulators)'),
});

// Use z.infer for type safety
type ResetSimulatorLocationParams = z.infer<typeof resetSimulatorLocationSchema>;

// Helper function to execute simctl commands and handle responses
async function executeSimctlCommandAndRespond(
  params: ResetSimulatorLocationParams,
  simctlSubCommand: string[],
  operationDescriptionForXcodeCommand: string,
  successMessage: string,
  failureMessagePrefix: string,
  operationLogContext: string,
  executor: CommandExecutor,
  extraValidation?: () => ToolResponse | undefined,
): Promise<ToolResponse> {
  if (extraValidation) {
    const validationResult = extraValidation();
    if (validationResult) {
      return validationResult;
    }
  }

  try {
    const command = ['xcrun', 'simctl', ...simctlSubCommand];
    const result = await executor(command, operationDescriptionForXcodeCommand, true, {});

    if (!result.success) {
      const fullFailureMessage = `${failureMessagePrefix}: ${result.error}`;
      log(
        'error',
        `${fullFailureMessage} (operation: ${operationLogContext}, simulator: ${params.simulatorUuid})`,
      );
      return {
        content: [{ type: 'text', text: fullFailureMessage }],
      };
    }

    log(
      'info',
      `${successMessage} (operation: ${operationLogContext}, simulator: ${params.simulatorUuid})`,
    );
    return {
      content: [{ type: 'text', text: successMessage }],
    };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    const fullFailureMessage = `${failureMessagePrefix}: ${errorMessage}`;
    log(
      'error',
      `Error during ${operationLogContext} for simulator ${params.simulatorUuid}: ${errorMessage}`,
    );
    return {
      content: [{ type: 'text', text: fullFailureMessage }],
    };
  }
}

export async function reset_sim_locationLogic(
  params: ResetSimulatorLocationParams,
  executor: CommandExecutor,
): Promise<ToolResponse> {
  log('info', `Resetting simulator ${params.simulatorUuid} location`);

  return executeSimctlCommandAndRespond(
    params,
    ['location', params.simulatorUuid, 'clear'],
    'Reset Simulator Location',
    `Successfully reset simulator ${params.simulatorUuid} location.`,
    'Failed to reset simulator location',
    'reset simulator location',
    executor,
  );
}

export default {
  name: 'reset_sim_location',
  description: "Resets the simulator's location to default.",
  schema: resetSimulatorLocationSchema.shape, // MCP SDK compatibility
  handler: createTypedTool(
    resetSimulatorLocationSchema,
    reset_sim_locationLogic,
    getDefaultCommandExecutor,
  ),
};

```

--------------------------------------------------------------------------------
/src/mcp/tools/macos/__tests__/re-exports.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for macos-project re-export files
 * These files re-export tools from macos-workspace to avoid duplication
 */
import { describe, it, expect } from 'vitest';

// Import all re-export tools
import testMacos from '../test_macos.ts';
import buildMacos from '../build_macos.ts';
import buildRunMacos from '../build_run_macos.ts';
import getMacAppPath from '../get_mac_app_path.ts';

describe('macos-project re-exports', () => {
  describe('test_macos re-export', () => {
    it('should re-export test_macos tool correctly', () => {
      expect(testMacos.name).toBe('test_macos');
      expect(typeof testMacos.handler).toBe('function');
      expect(testMacos.schema).toBeDefined();
      expect(typeof testMacos.description).toBe('string');
    });
  });

  describe('build_macos re-export', () => {
    it('should re-export build_macos tool correctly', () => {
      expect(buildMacos.name).toBe('build_macos');
      expect(typeof buildMacos.handler).toBe('function');
      expect(buildMacos.schema).toBeDefined();
      expect(typeof buildMacos.description).toBe('string');
    });
  });

  describe('build_run_macos re-export', () => {
    it('should re-export build_run_macos tool correctly', () => {
      expect(buildRunMacos.name).toBe('build_run_macos');
      expect(typeof buildRunMacos.handler).toBe('function');
      expect(buildRunMacos.schema).toBeDefined();
      expect(typeof buildRunMacos.description).toBe('string');
    });
  });

  describe('get_mac_app_path re-export', () => {
    it('should re-export get_mac_app_path tool correctly', () => {
      expect(getMacAppPath.name).toBe('get_mac_app_path');
      expect(typeof getMacAppPath.handler).toBe('function');
      expect(getMacAppPath.schema).toBeDefined();
      expect(typeof getMacAppPath.description).toBe('string');
    });
  });

  describe('All re-exports validation', () => {
    const reExports = [
      { tool: testMacos, name: 'test_macos' },
      { tool: buildMacos, name: 'build_macos' },
      { tool: buildRunMacos, name: 'build_run_macos' },
      { tool: getMacAppPath, name: 'get_mac_app_path' },
    ];

    it('should have all required tool properties', () => {
      reExports.forEach(({ tool, name }) => {
        expect(tool).toHaveProperty('name');
        expect(tool).toHaveProperty('description');
        expect(tool).toHaveProperty('schema');
        expect(tool).toHaveProperty('handler');
        expect(tool.name).toBe(name);
      });
    });

    it('should have callable handlers', () => {
      reExports.forEach(({ tool, name }) => {
        expect(typeof tool.handler).toBe('function');
        expect(tool.handler.length).toBeGreaterThanOrEqual(0);
      });
    });

    it('should have valid schemas', () => {
      reExports.forEach(({ tool, name }) => {
        expect(tool.schema).toBeDefined();
        expect(typeof tool.schema).toBe('object');
      });
    });

    it('should have non-empty descriptions', () => {
      reExports.forEach(({ tool, name }) => {
        expect(typeof tool.description).toBe('string');
        expect(tool.description.length).toBeGreaterThan(0);
      });
    });
  });
});

```

--------------------------------------------------------------------------------
/src/mcp/tools/macos/__tests__/index.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for macos-project workflow metadata
 */
import { describe, it, expect } from 'vitest';
import { workflow } from '../index.ts';

describe('macos-project workflow metadata', () => {
  describe('Workflow Structure', () => {
    it('should export workflow object with required properties', () => {
      expect(workflow).toHaveProperty('name');
      expect(workflow).toHaveProperty('description');
      expect(workflow).toHaveProperty('platforms');
      expect(workflow).toHaveProperty('targets');
      expect(workflow).toHaveProperty('projectTypes');
      expect(workflow).toHaveProperty('capabilities');
    });

    it('should have correct workflow name', () => {
      expect(workflow.name).toBe('macOS Development');
    });

    it('should have correct description', () => {
      expect(workflow.description).toBe(
        'Complete macOS development workflow for both .xcodeproj and .xcworkspace files. Build, test, deploy, and manage macOS applications.',
      );
    });

    it('should have correct platforms array', () => {
      expect(workflow.platforms).toEqual(['macOS']);
    });

    it('should have correct targets array', () => {
      expect(workflow.targets).toEqual(['native']);
    });

    it('should have correct projectTypes array', () => {
      expect(workflow.projectTypes).toEqual(['project', 'workspace']);
    });

    it('should have correct capabilities array', () => {
      expect(workflow.capabilities).toEqual(['build', 'test', 'deploy', 'debug', 'app-management']);
    });
  });

  describe('Workflow Validation', () => {
    it('should have valid string properties', () => {
      expect(typeof workflow.name).toBe('string');
      expect(typeof workflow.description).toBe('string');
      expect(workflow.name.length).toBeGreaterThan(0);
      expect(workflow.description.length).toBeGreaterThan(0);
    });

    it('should have valid array properties', () => {
      expect(Array.isArray(workflow.platforms)).toBe(true);
      expect(Array.isArray(workflow.targets)).toBe(true);
      expect(Array.isArray(workflow.projectTypes)).toBe(true);
      expect(Array.isArray(workflow.capabilities)).toBe(true);

      expect(workflow.platforms.length).toBeGreaterThan(0);
      expect(workflow.targets.length).toBeGreaterThan(0);
      expect(workflow.projectTypes.length).toBeGreaterThan(0);
      expect(workflow.capabilities.length).toBeGreaterThan(0);
    });

    it('should contain expected platform values', () => {
      expect(workflow.platforms).toContain('macOS');
    });

    it('should contain expected target values', () => {
      expect(workflow.targets).toContain('native');
    });

    it('should contain expected project type values', () => {
      expect(workflow.projectTypes).toContain('project');
    });

    it('should contain expected capability values', () => {
      expect(workflow.capabilities).toContain('build');
      expect(workflow.capabilities).toContain('test');
      expect(workflow.capabilities).toContain('deploy');
      expect(workflow.capabilities).toContain('debug');
      expect(workflow.capabilities).toContain('app-management');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/mcp/tools/device/build_device.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Device Shared Plugin: Build Device (Unified)
 *
 * Builds an app from a project or workspace for a physical Apple device.
 * Accepts mutually exclusive `projectPath` or `workspacePath`.
 */

import { z } from 'zod';
import { ToolResponse, XcodePlatform } from '../../../types/common.ts';
import { executeXcodeBuildCommand } from '../../../utils/build/index.ts';
import type { CommandExecutor } from '../../../utils/execution/index.ts';
import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts';
import { createSessionAwareTool } from '../../../utils/typed-tool-factory.ts';
import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts';

// Unified schema: XOR between projectPath and workspacePath
const baseSchemaObject = z.object({
  projectPath: z.string().optional().describe('Path to the .xcodeproj file'),
  workspacePath: z.string().optional().describe('Path to the .xcworkspace file'),
  scheme: z.string().describe('The scheme to build'),
  configuration: z.string().optional().describe('Build configuration (Debug, Release)'),
  derivedDataPath: z.string().optional().describe('Path to derived data directory'),
  extraArgs: z.array(z.string()).optional().describe('Additional arguments to pass to xcodebuild'),
  preferXcodebuild: z.boolean().optional().describe('Prefer xcodebuild over faster alternatives'),
});

const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject);

const buildDeviceSchema = baseSchema
  .refine((val) => val.projectPath !== undefined || val.workspacePath !== undefined, {
    message: 'Either projectPath or workspacePath is required.',
  })
  .refine((val) => !(val.projectPath !== undefined && val.workspacePath !== undefined), {
    message: 'projectPath and workspacePath are mutually exclusive. Provide only one.',
  });

export type BuildDeviceParams = z.infer<typeof buildDeviceSchema>;

/**
 * Business logic for building device project or workspace.
 * Exported for direct testing and reuse.
 */
export async function buildDeviceLogic(
  params: BuildDeviceParams,
  executor: CommandExecutor,
): Promise<ToolResponse> {
  const processedParams = {
    ...params,
    configuration: params.configuration ?? 'Debug', // Default config
  };

  return executeXcodeBuildCommand(
    processedParams,
    {
      platform: XcodePlatform.iOS,
      logPrefix: 'iOS Device Build',
    },
    params.preferXcodebuild ?? false,
    'build',
    executor,
  );
}

export default {
  name: 'build_device',
  description: 'Builds an app for a connected device.',
  schema: baseSchemaObject.omit({
    projectPath: true,
    workspacePath: true,
    scheme: true,
    configuration: true,
  } as const).shape,
  handler: createSessionAwareTool<BuildDeviceParams>({
    internalSchema: buildDeviceSchema as unknown as z.ZodType<BuildDeviceParams>,
    logicFunction: buildDeviceLogic,
    getExecutor: getDefaultCommandExecutor,
    requirements: [
      { allOf: ['scheme'], message: 'scheme is required' },
      { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' },
    ],
    exclusivePairs: [['projectPath', 'workspacePath']],
  }),
};

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "xcodebuildmcp",
  "version": "1.14.1",
  "mcpName": "com.xcodebuildmcp/XcodeBuildMCP",
  "iOSTemplateVersion": "v1.0.8",
  "macOSTemplateVersion": "v1.0.5",
  "main": "build/index.js",
  "type": "module",
  "bin": {
    "xcodebuildmcp": "build/index.js",
    "xcodebuildmcp-doctor": "build/doctor-cli.js"
  },
  "scripts": {
    "build": "node -e \"const fs = require('fs'); const pkg = require('./package.json'); fs.writeFileSync('src/version.ts', \\`export const version = '\\${pkg.version}';\\nexport const iOSTemplateVersion = '\\${pkg.iOSTemplateVersion}';\\nexport const macOSTemplateVersion = '\\${pkg.macOSTemplateVersion}';\\n\\`)\" && tsup",
    "dev": "npm run build && tsup --watch",
    "bundle:axe": "scripts/bundle-axe.sh",
    "lint": "eslint 'src/**/*.{js,ts}'",
    "lint:fix": "eslint 'src/**/*.{js,ts}' --fix",
    "format": "prettier --write 'src/**/*.{js,ts}'",
    "format:check": "prettier --check 'src/**/*.{js,ts}'",
    "typecheck": "npx tsc --noEmit",
    "inspect": "npx @modelcontextprotocol/inspector node build/index.js",
    "doctor": "node build/doctor-cli.js",
    "tools": "npx tsx scripts/tools-cli.ts",
    "tools:list": "npx tsx scripts/tools-cli.ts list",
    "tools:static": "npx tsx scripts/tools-cli.ts static",
    "tools:count": "npx tsx scripts/tools-cli.ts count --static",
    "tools:analysis": "npx tsx scripts/analysis/tools-analysis.ts",
    "docs:update": "npx tsx scripts/update-tools-docs.ts",
    "docs:update:dry-run": "npx tsx scripts/update-tools-docs.ts --dry-run --verbose",
    "test": "vitest run",
    "test:watch": "vitest",
    "test:ui": "vitest --ui",
    "test:coverage": "vitest run --coverage"
  },
  "files": [
    "build",
    "bundled",
    "plugins"
  ],
  "keywords": [
    "xcodebuild",
    "mcp",
    "modelcontextprotocol",
    "xcode",
    "ios",
    "macos",
    "simulator"
  ],
  "author": "Cameron Cooke",
  "license": "MIT",
  "description": "XcodeBuildMCP is a ModelContextProtocol server that provides tools for Xcode project management, simulator management, and app utilities.",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/cameroncooke/XcodeBuildMCP.git"
  },
  "homepage": "https://www.async-let.com/blog/xcodebuild-mcp/",
  "bugs": {
    "url": "https://github.com/cameroncooke/XcodeBuildMCP/issues"
  },
  "dependencies": {
    "@camsoft/mcp-sdk": "^1.17.1",
    "@sentry/cli": "^2.43.1",
    "@sentry/node": "^10.5.0",
    "uuid": "^11.1.0",
    "zod": "^3.24.2"
  },
  "devDependencies": {
    "@bacons/xcode": "^1.0.0-alpha.24",
    "@eslint/eslintrc": "^3.3.1",
    "@eslint/js": "^9.23.0",
    "@types/node": "^22.13.6",
    "@typescript-eslint/eslint-plugin": "^8.28.0",
    "@typescript-eslint/parser": "^8.28.0",
    "@vitest/coverage-v8": "^3.2.4",
    "@vitest/ui": "^3.2.4",
    "eslint": "^9.23.0",
    "eslint-config-prettier": "^10.1.1",
    "eslint-plugin-prettier": "^5.2.5",
    "playwright": "^1.53.0",
    "prettier": "^3.5.3",
    "ts-node": "^10.9.2",
    "tsup": "^8.5.0",
    "tsx": "^4.20.4",
    "typescript": "^5.8.2",
    "typescript-eslint": "^8.28.0",
    "vitest": "^3.2.4",
    "xcode": "^3.0.1"
  }
}

```

--------------------------------------------------------------------------------
/src/mcp/tools/simulator-management/sim_statusbar.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { ToolResponse } from '../../../types/common.ts';
import { log } from '../../../utils/logging/index.ts';
import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/execution/index.ts';
import { createTypedTool } from '../../../utils/typed-tool-factory.ts';

// Define schema as ZodObject
const simStatusbarSchema = z.object({
  simulatorUuid: z
    .string()
    .describe('UUID of the simulator to use (obtained from list_simulators)'),
  dataNetwork: z
    .enum([
      'clear',
      'hide',
      'wifi',
      '3g',
      '4g',
      'lte',
      'lte-a',
      'lte+',
      '5g',
      '5g+',
      '5g-uwb',
      '5g-uc',
    ])
    .describe(
      'Data network type to display in status bar. Use "clear" to reset all overrides. Valid values: clear, hide, wifi, 3g, 4g, lte, lte-a, lte+, 5g, 5g+, 5g-uwb, 5g-uc.',
    ),
});

// Use z.infer for type safety
type SimStatusbarParams = z.infer<typeof simStatusbarSchema>;

export async function sim_statusbarLogic(
  params: SimStatusbarParams,
  executor: CommandExecutor,
): Promise<ToolResponse> {
  log(
    'info',
    `Setting simulator ${params.simulatorUuid} status bar data network to ${params.dataNetwork}`,
  );

  try {
    let command: string[];
    let successMessage: string;

    if (params.dataNetwork === 'clear') {
      command = ['xcrun', 'simctl', 'status_bar', params.simulatorUuid, 'clear'];
      successMessage = `Successfully cleared status bar overrides for simulator ${params.simulatorUuid}`;
    } else {
      command = [
        'xcrun',
        'simctl',
        'status_bar',
        params.simulatorUuid,
        'override',
        '--dataNetwork',
        params.dataNetwork,
      ];
      successMessage = `Successfully set simulator ${params.simulatorUuid} status bar data network to ${params.dataNetwork}`;
    }

    const result = await executor(command, 'Set Status Bar', true, undefined);

    if (!result.success) {
      const failureMessage = `Failed to set status bar: ${result.error}`;
      log('error', `${failureMessage} (simulator: ${params.simulatorUuid})`);
      return {
        content: [{ type: 'text', text: failureMessage }],
        isError: true,
      };
    }

    log('info', `${successMessage} (simulator: ${params.simulatorUuid})`);
    return {
      content: [{ type: 'text', text: successMessage }],
    };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    const failureMessage = `Failed to set status bar: ${errorMessage}`;
    log('error', `Error setting status bar for simulator ${params.simulatorUuid}: ${errorMessage}`);
    return {
      content: [{ type: 'text', text: failureMessage }],
      isError: true,
    };
  }
}

export default {
  name: 'sim_statusbar',
  description:
    'Sets the data network indicator in the iOS simulator status bar. Use "clear" to reset all overrides, or specify a network type (hide, wifi, 3g, 4g, lte, lte-a, lte+, 5g, 5g+, 5g-uwb, 5g-uc).',
  schema: simStatusbarSchema.shape, // MCP SDK compatibility
  handler: createTypedTool(simStatusbarSchema, sim_statusbarLogic, getDefaultCommandExecutor),
};

```

--------------------------------------------------------------------------------
/src/utils/axe-helpers.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * AXe Helper Functions
 *
 * This utility module provides functions to work with the bundled AXe tool.
 * Always uses the bundled version to ensure consistency.
 */

import { existsSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import { createTextResponse } from './validation.ts';
import { ToolResponse } from '../types/common.ts';
import type { CommandExecutor } from './execution/index.ts';
import { getDefaultCommandExecutor } from './execution/index.ts';

// Get bundled AXe path - always use the bundled version for consistency
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// In the npm package, build/index.js is at the same level as bundled/
// So we go up one level from build/ to get to the package root
const bundledAxePath = join(__dirname, '..', 'bundled', 'axe');

/**
 * Get the path to the bundled axe binary
 */
export function getAxePath(): string | null {
  // Always use bundled version for consistency
  if (existsSync(bundledAxePath)) {
    return bundledAxePath;
  }
  return null;
}

/**
 * Get environment variables needed for bundled AXe to run
 */
export function getBundledAxeEnvironment(): Record<string, string> {
  // No special environment variables needed - bundled AXe binary
  // has proper @rpath configuration to find frameworks
  return {};
}

/**
 * Check if bundled axe tool is available
 */
export function areAxeToolsAvailable(): boolean {
  return getAxePath() !== null;
}

export function createAxeNotAvailableResponse(): ToolResponse {
  return createTextResponse(
    'Bundled axe tool not found. UI automation features are not available.\n\n' +
      'This is likely an installation issue with the npm package.\n' +
      'Please reinstall xcodebuildmcp or report this issue.',
    true,
  );
}

/**
 * Compare two semver strings a and b.
 * Returns 1 if a > b, -1 if a < b, 0 if equal.
 */
function compareSemver(a: string, b: string): number {
  const pa = a.split('.').map((n) => parseInt(n, 10));
  const pb = b.split('.').map((n) => parseInt(n, 10));
  const len = Math.max(pa.length, pb.length);
  for (let i = 0; i < len; i++) {
    const da = Number.isFinite(pa[i]) ? pa[i] : 0;
    const db = Number.isFinite(pb[i]) ? pb[i] : 0;
    if (da > db) return 1;
    if (da < db) return -1;
  }
  return 0;
}

/**
 * Determine whether the bundled AXe meets a minimum version requirement.
 * Runs `axe --version` and parses a semantic version (e.g., "1.1.0").
 * If AXe is missing or the version cannot be parsed, returns false.
 */
export async function isAxeAtLeastVersion(
  required: string,
  executor?: CommandExecutor,
): Promise<boolean> {
  const axePath = getAxePath();
  if (!axePath) return false;

  const exec = executor ?? getDefaultCommandExecutor();
  try {
    const res = await exec([axePath, '--version'], 'AXe Version', true);
    if (!res.success) return false;

    const output = res.output ?? '';
    const versionMatch = output.match(/(\d+\.\d+\.\d+)/);
    if (!versionMatch) return false;

    const current = versionMatch[1];
    return compareSemver(current, required) >= 0;
  } catch {
    return false;
  }
}

```

--------------------------------------------------------------------------------
/src/mcp/tools/simulator/__tests__/index.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for simulator-project workflow metadata
 */
import { describe, it, expect } from 'vitest';
import { workflow } from '../index.ts';

describe('simulator-project workflow metadata', () => {
  describe('Workflow Structure', () => {
    it('should export workflow object with required properties', () => {
      expect(workflow).toHaveProperty('name');
      expect(workflow).toHaveProperty('description');
      expect(workflow).toHaveProperty('platforms');
      expect(workflow).toHaveProperty('targets');
      expect(workflow).toHaveProperty('projectTypes');
      expect(workflow).toHaveProperty('capabilities');
    });

    it('should have correct workflow name', () => {
      expect(workflow.name).toBe('iOS Simulator Development');
    });

    it('should have correct description', () => {
      expect(workflow.description).toBe(
        'Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting simulators. Build, test, deploy, and interact with iOS apps on simulators.',
      );
    });

    it('should have correct platforms array', () => {
      expect(workflow.platforms).toEqual(['iOS']);
    });

    it('should have correct targets array', () => {
      expect(workflow.targets).toEqual(['simulator']);
    });

    it('should have correct projectTypes array', () => {
      expect(workflow.projectTypes).toEqual(['project', 'workspace']);
    });

    it('should have correct capabilities array', () => {
      expect(workflow.capabilities).toEqual(['build', 'test', 'deploy', 'debug', 'ui-automation']);
    });
  });

  describe('Workflow Validation', () => {
    it('should have valid string properties', () => {
      expect(typeof workflow.name).toBe('string');
      expect(typeof workflow.description).toBe('string');
      expect(workflow.name.length).toBeGreaterThan(0);
      expect(workflow.description.length).toBeGreaterThan(0);
    });

    it('should have valid array properties', () => {
      expect(Array.isArray(workflow.platforms)).toBe(true);
      expect(Array.isArray(workflow.targets)).toBe(true);
      expect(Array.isArray(workflow.projectTypes)).toBe(true);
      expect(Array.isArray(workflow.capabilities)).toBe(true);

      expect(workflow.platforms.length).toBeGreaterThan(0);
      expect(workflow.targets.length).toBeGreaterThan(0);
      expect(workflow.projectTypes.length).toBeGreaterThan(0);
      expect(workflow.capabilities.length).toBeGreaterThan(0);
    });

    it('should contain expected platform values', () => {
      expect(workflow.platforms).toContain('iOS');
    });

    it('should contain expected target values', () => {
      expect(workflow.targets).toContain('simulator');
    });

    it('should contain expected project type values', () => {
      expect(workflow.projectTypes).toContain('project');
    });

    it('should contain expected capability values', () => {
      expect(workflow.capabilities).toContain('build');
      expect(workflow.capabilities).toContain('test');
      expect(workflow.capabilities).toContain('deploy');
      expect(workflow.capabilities).toContain('debug');
      expect(workflow.capabilities).toContain('ui-automation');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/mcp/tools/utilities/__tests__/index.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for utilities workflow metadata
 */
import { describe, it, expect } from 'vitest';
import { workflow } from '../index.ts';

describe('utilities workflow metadata', () => {
  describe('Workflow Structure', () => {
    it('should export workflow object with required properties', () => {
      expect(workflow).toHaveProperty('name');
      expect(workflow).toHaveProperty('description');
      expect(workflow).toHaveProperty('platforms');
      expect(workflow).toHaveProperty('targets');
      expect(workflow).toHaveProperty('projectTypes');
      expect(workflow).toHaveProperty('capabilities');
    });

    it('should have correct workflow name', () => {
      expect(workflow.name).toBe('Project Utilities');
    });

    it('should have correct description', () => {
      expect(workflow.description).toBe(
        'Essential project maintenance utilities for cleaning and managing existing projects. Provides clean operations for both .xcodeproj and .xcworkspace files.',
      );
    });

    it('should have correct platforms array', () => {
      expect(workflow.platforms).toEqual(['iOS', 'macOS']);
    });

    it('should have correct targets array', () => {
      expect(workflow.targets).toEqual(['simulator', 'device', 'mac']);
    });

    it('should have correct projectTypes array', () => {
      expect(workflow.projectTypes).toEqual(['project', 'workspace']);
    });

    it('should have correct capabilities array', () => {
      expect(workflow.capabilities).toEqual(['project-cleaning', 'project-maintenance']);
    });
  });

  describe('Workflow Validation', () => {
    it('should have valid string properties', () => {
      expect(typeof workflow.name).toBe('string');
      expect(typeof workflow.description).toBe('string');
      expect(workflow.name.length).toBeGreaterThan(0);
      expect(workflow.description.length).toBeGreaterThan(0);
    });

    it('should have valid array properties', () => {
      expect(Array.isArray(workflow.platforms)).toBe(true);
      expect(Array.isArray(workflow.targets)).toBe(true);
      expect(Array.isArray(workflow.projectTypes)).toBe(true);
      expect(Array.isArray(workflow.capabilities)).toBe(true);

      expect(workflow.platforms.length).toBeGreaterThan(0);
      expect(workflow.targets.length).toBeGreaterThan(0);
      expect(workflow.projectTypes.length).toBeGreaterThan(0);
      expect(workflow.capabilities.length).toBeGreaterThan(0);
    });

    it('should contain expected platform values', () => {
      expect(workflow.platforms).toContain('iOS');
      expect(workflow.platforms).toContain('macOS');
    });

    it('should contain expected target values', () => {
      expect(workflow.targets).toContain('simulator');
      expect(workflow.targets).toContain('device');
      expect(workflow.targets).toContain('mac');
    });

    it('should contain expected project type values', () => {
      expect(workflow.projectTypes).toContain('project');
      expect(workflow.projectTypes).toContain('workspace');
    });

    it('should contain expected capability values', () => {
      expect(workflow.capabilities).toContain('project-cleaning');
      expect(workflow.capabilities).toContain('project-maintenance');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/mcp/tools/device/__tests__/re-exports.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for device-project re-export files
 * These files re-export tools from device-workspace to avoid duplication
 */
import { describe, it, expect } from 'vitest';

// Import all re-export tools
import launchAppDevice from '../launch_app_device.ts';
import stopAppDevice from '../stop_app_device.ts';
import listDevices from '../list_devices.ts';
import installAppDevice from '../install_app_device.ts';

describe('device-project re-exports', () => {
  describe('launch_app_device re-export', () => {
    it('should re-export launch_app_device tool correctly', () => {
      expect(launchAppDevice.name).toBe('launch_app_device');
      expect(typeof launchAppDevice.handler).toBe('function');
      expect(launchAppDevice.schema).toBeDefined();
      expect(typeof launchAppDevice.description).toBe('string');
    });
  });

  describe('stop_app_device re-export', () => {
    it('should re-export stop_app_device tool correctly', () => {
      expect(stopAppDevice.name).toBe('stop_app_device');
      expect(typeof stopAppDevice.handler).toBe('function');
      expect(stopAppDevice.schema).toBeDefined();
      expect(typeof stopAppDevice.description).toBe('string');
    });
  });

  describe('list_devices re-export', () => {
    it('should re-export list_devices tool correctly', () => {
      expect(listDevices.name).toBe('list_devices');
      expect(typeof listDevices.handler).toBe('function');
      expect(listDevices.schema).toBeDefined();
      expect(typeof listDevices.description).toBe('string');
    });
  });

  describe('install_app_device re-export', () => {
    it('should re-export install_app_device tool correctly', () => {
      expect(installAppDevice.name).toBe('install_app_device');
      expect(typeof installAppDevice.handler).toBe('function');
      expect(installAppDevice.schema).toBeDefined();
      expect(typeof installAppDevice.description).toBe('string');
    });
  });

  describe('All re-exports validation', () => {
    const reExports = [
      { tool: launchAppDevice, name: 'launch_app_device' },
      { tool: stopAppDevice, name: 'stop_app_device' },
      { tool: listDevices, name: 'list_devices' },
      { tool: installAppDevice, name: 'install_app_device' },
    ];

    it('should have all required tool properties', () => {
      reExports.forEach(({ tool, name }) => {
        expect(tool).toHaveProperty('name');
        expect(tool).toHaveProperty('description');
        expect(tool).toHaveProperty('schema');
        expect(tool).toHaveProperty('handler');
        expect(tool.name).toBe(name);
      });
    });

    it('should have callable handlers', () => {
      reExports.forEach(({ tool, name }) => {
        expect(typeof tool.handler).toBe('function');
        expect(tool.handler.length).toBeGreaterThanOrEqual(0);
      });
    });

    it('should have valid schemas', () => {
      reExports.forEach(({ tool, name }) => {
        expect(tool.schema).toBeDefined();
        expect(typeof tool.schema).toBe('object');
      });
    });

    it('should have non-empty descriptions', () => {
      reExports.forEach(({ tool, name }) => {
        expect(typeof tool.description).toBe('string');
        expect(tool.description.length).toBeGreaterThan(0);
      });
    });
  });
});

```

--------------------------------------------------------------------------------
/src/mcp/tools/simulator-management/set_sim_appearance.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { ToolResponse } from '../../../types/common.ts';
import { log } from '../../../utils/logging/index.ts';
import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/execution/index.ts';
import { createTypedTool } from '../../../utils/typed-tool-factory.ts';

// Define schema as ZodObject
const setSimAppearanceSchema = z.object({
  simulatorUuid: z
    .string()
    .describe('UUID of the simulator to use (obtained from list_simulators)'),
  mode: z.enum(['dark', 'light']).describe('The appearance mode to set (either "dark" or "light")'),
});

// Use z.infer for type safety
type SetSimAppearanceParams = z.infer<typeof setSimAppearanceSchema>;

// Helper function to execute simctl commands and handle responses
async function executeSimctlCommandAndRespond(
  params: SetSimAppearanceParams,
  simctlSubCommand: string[],
  operationDescriptionForXcodeCommand: string,
  successMessage: string,
  failureMessagePrefix: string,
  operationLogContext: string,
  extraValidation?: () => ToolResponse | undefined,
  executor: CommandExecutor = getDefaultCommandExecutor(),
): Promise<ToolResponse> {
  if (extraValidation) {
    const validationResult = extraValidation();
    if (validationResult) {
      return validationResult;
    }
  }

  try {
    const command = ['xcrun', 'simctl', ...simctlSubCommand];
    const result = await executor(command, operationDescriptionForXcodeCommand, true, undefined);

    if (!result.success) {
      const fullFailureMessage = `${failureMessagePrefix}: ${result.error}`;
      log(
        'error',
        `${fullFailureMessage} (operation: ${operationLogContext}, simulator: ${params.simulatorUuid})`,
      );
      return {
        content: [{ type: 'text', text: fullFailureMessage }],
      };
    }

    log(
      'info',
      `${successMessage} (operation: ${operationLogContext}, simulator: ${params.simulatorUuid})`,
    );
    return {
      content: [{ type: 'text', text: successMessage }],
    };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    const fullFailureMessage = `${failureMessagePrefix}: ${errorMessage}`;
    log(
      'error',
      `Error during ${operationLogContext} for simulator ${params.simulatorUuid}: ${errorMessage}`,
    );
    return {
      content: [{ type: 'text', text: fullFailureMessage }],
    };
  }
}

export async function set_sim_appearanceLogic(
  params: SetSimAppearanceParams,
  executor: CommandExecutor,
): Promise<ToolResponse> {
  log('info', `Setting simulator ${params.simulatorUuid} appearance to ${params.mode} mode`);

  return executeSimctlCommandAndRespond(
    params,
    ['ui', params.simulatorUuid, 'appearance', params.mode],
    'Set Simulator Appearance',
    `Successfully set simulator ${params.simulatorUuid} appearance to ${params.mode} mode`,
    'Failed to set simulator appearance',
    'set simulator appearance',
    undefined,
    executor,
  );
}

export default {
  name: 'set_sim_appearance',
  description: 'Sets the appearance mode (dark/light) of an iOS simulator.',
  schema: setSimAppearanceSchema.shape, // MCP SDK compatibility
  handler: createTypedTool(
    setSimAppearanceSchema,
    set_sim_appearanceLogic,
    getDefaultCommandExecutor,
  ),
};

```

--------------------------------------------------------------------------------
/src/mcp/tools/simulator/install_app_sim.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { ToolResponse } from '../../../types/common.ts';
import { log } from '../../../utils/logging/index.ts';
import { validateFileExists } from '../../../utils/validation/index.ts';
import type { CommandExecutor, FileSystemExecutor } from '../../../utils/execution/index.ts';
import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts';
import { createSessionAwareTool } from '../../../utils/typed-tool-factory.ts';

const installAppSimSchemaObject = z.object({
  simulatorId: z.string().describe('UUID of the simulator to target'),
  appPath: z.string().describe('Path to the .app bundle to install'),
});

type InstallAppSimParams = z.infer<typeof installAppSimSchemaObject>;

const publicSchemaObject = installAppSimSchemaObject.omit({
  simulatorId: true,
} as const);

export async function install_app_simLogic(
  params: InstallAppSimParams,
  executor: CommandExecutor,
  fileSystem?: FileSystemExecutor,
): Promise<ToolResponse> {
  const appPathExistsValidation = validateFileExists(params.appPath, fileSystem);
  if (!appPathExistsValidation.isValid) {
    return appPathExistsValidation.errorResponse!;
  }

  log('info', `Starting xcrun simctl install request for simulator ${params.simulatorId}`);

  try {
    const command = ['xcrun', 'simctl', 'install', params.simulatorId, params.appPath];
    const result = await executor(command, 'Install App in Simulator', true, undefined);

    if (!result.success) {
      return {
        content: [
          {
            type: 'text',
            text: `Install app in simulator operation failed: ${result.error}`,
          },
        ],
      };
    }

    let bundleId = '';
    try {
      const bundleIdResult = await executor(
        ['defaults', 'read', `${params.appPath}/Info`, 'CFBundleIdentifier'],
        'Extract Bundle ID',
        false,
        undefined,
      );
      if (bundleIdResult.success) {
        bundleId = bundleIdResult.output.trim();
      }
    } catch (error) {
      log('warning', `Could not extract bundle ID from app: ${error}`);
    }

    return {
      content: [
        {
          type: 'text',
          text: `App installed successfully in simulator ${params.simulatorId}`,
        },
        {
          type: 'text',
          text: `Next Steps:
1. Open the Simulator app: open_sim({})
2. Launch the app: launch_app_sim({ simulatorId: "${params.simulatorId}"${
            bundleId ? `, bundleId: "${bundleId}"` : ', bundleId: "YOUR_APP_BUNDLE_ID"'
          } })`,
        },
      ],
    };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    log('error', `Error during install app in simulator operation: ${errorMessage}`);
    return {
      content: [
        {
          type: 'text',
          text: `Install app in simulator operation failed: ${errorMessage}`,
        },
      ],
    };
  }
}

export default {
  name: 'install_app_sim',
  description: 'Installs an app in an iOS simulator.',
  schema: publicSchemaObject.shape,
  handler: createSessionAwareTool<InstallAppSimParams>({
    internalSchema: installAppSimSchemaObject,
    logicFunction: install_app_simLogic,
    getExecutor: getDefaultCommandExecutor,
    requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }],
  }),
};

```

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

```typescript
/**
 * Tests for project-scaffolding workflow metadata
 */
import { describe, it, expect } from 'vitest';
import { workflow } from '../index.ts';

describe('project-scaffolding workflow metadata', () => {
  describe('Workflow Structure', () => {
    it('should export workflow object with required properties', () => {
      expect(workflow).toHaveProperty('name');
      expect(workflow).toHaveProperty('description');
      expect(workflow).toHaveProperty('platforms');
      expect(workflow).toHaveProperty('targets');
      expect(workflow).toHaveProperty('projectTypes');
      expect(workflow).toHaveProperty('capabilities');
    });

    it('should have correct workflow name', () => {
      expect(workflow.name).toBe('Project Scaffolding');
    });

    it('should have correct description', () => {
      expect(workflow.description).toBe(
        'Tools for creating new iOS and macOS projects from templates. Bootstrap new applications with best practices, standard configurations, and modern project structures.',
      );
    });

    it('should have correct platforms array', () => {
      expect(workflow.platforms).toEqual(['iOS', 'macOS']);
    });

    it('should have correct targets array', () => {
      expect(workflow.targets).toEqual(['simulator', 'device', 'mac']);
    });

    it('should have correct projectTypes array', () => {
      expect(workflow.projectTypes).toEqual(['project']);
    });

    it('should have correct capabilities array', () => {
      expect(workflow.capabilities).toEqual([
        'project-creation',
        'template-generation',
        'project-initialization',
      ]);
    });
  });

  describe('Workflow Validation', () => {
    it('should have valid string properties', () => {
      expect(typeof workflow.name).toBe('string');
      expect(typeof workflow.description).toBe('string');
      expect(workflow.name.length).toBeGreaterThan(0);
      expect(workflow.description.length).toBeGreaterThan(0);
    });

    it('should have valid array properties', () => {
      expect(Array.isArray(workflow.platforms)).toBe(true);
      expect(Array.isArray(workflow.targets)).toBe(true);
      expect(Array.isArray(workflow.projectTypes)).toBe(true);
      expect(Array.isArray(workflow.capabilities)).toBe(true);

      expect(workflow.platforms.length).toBeGreaterThan(0);
      expect(workflow.targets.length).toBeGreaterThan(0);
      expect(workflow.projectTypes.length).toBeGreaterThan(0);
      expect(workflow.capabilities.length).toBeGreaterThan(0);
    });

    it('should contain expected platform values', () => {
      expect(workflow.platforms).toContain('iOS');
      expect(workflow.platforms).toContain('macOS');
    });

    it('should contain expected target values', () => {
      expect(workflow.targets).toContain('simulator');
      expect(workflow.targets).toContain('device');
      expect(workflow.targets).toContain('mac');
    });

    it('should contain expected project type values', () => {
      expect(workflow.projectTypes).toContain('project');
    });

    it('should contain expected capability values', () => {
      expect(workflow.capabilities).toContain('project-creation');
      expect(workflow.capabilities).toContain('template-generation');
      expect(workflow.capabilities).toContain('project-initialization');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/mcp/resources/__tests__/devices.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect } from 'vitest';

import devicesResource, { devicesResourceLogic } from '../devices.ts';
import { createMockExecutor } from '../../../test-utils/mock-executors.ts';

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

    it('should export correct description', () => {
      expect(devicesResource.description).toBe(
        'Connected physical Apple devices with their UUIDs, names, and connection status',
      );
    });

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

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

  describe('Handler Functionality', () => {
    it('should handle successful device data retrieval with xctrace fallback', async () => {
      const mockExecutor = createMockExecutor({
        success: true,
        output: `iPhone (12345-ABCDE-FGHIJ-67890) (13.0)
iPad (98765-KLMNO-PQRST-43210) (14.0)
My Device (11111-22222-33333-44444) (15.0)`,
      });

      const result = await devicesResourceLogic(mockExecutor);

      expect(result.contents).toHaveLength(1);
      expect(result.contents[0].text).toContain('Device listing (xctrace output)');
      expect(result.contents[0].text).toContain('iPhone');
      expect(result.contents[0].text).toContain('iPad');
    });

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

      const result = await devicesResourceLogic(mockExecutor);

      expect(result.contents).toHaveLength(1);
      expect(result.contents[0].text).toContain('Failed to list devices');
      expect(result.contents[0].text).toContain('Command failed');
    });

    it('should handle spawn errors', async () => {
      const mockExecutor = createMockExecutor(new Error('spawn xcrun ENOENT'));

      const result = await devicesResourceLogic(mockExecutor);

      expect(result.contents).toHaveLength(1);
      expect(result.contents[0].text).toContain('Error retrieving device data');
      expect(result.contents[0].text).toContain('spawn xcrun ENOENT');
    });

    it('should handle empty device data with xctrace fallback', async () => {
      const mockExecutor = createMockExecutor({
        success: true,
        output: '',
      });

      const result = await devicesResourceLogic(mockExecutor);

      expect(result.contents).toHaveLength(1);
      expect(result.contents[0].text).toContain('Device listing (xctrace output)');
      expect(result.contents[0].text).toContain('Xcode 15 or later');
    });

    it('should handle device data with next steps guidance', async () => {
      const mockExecutor = createMockExecutor({
        success: true,
        output: `iPhone 15 Pro (12345-ABCDE-FGHIJ-67890) (17.0)`,
      });

      const result = await devicesResourceLogic(mockExecutor);

      expect(result.contents).toHaveLength(1);
      expect(result.contents[0].text).toContain('Device listing (xctrace output)');
      expect(result.contents[0].text).toContain('iPhone 15 Pro');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/core/plugin-registry.ts:
--------------------------------------------------------------------------------

```typescript
import type { PluginMeta, WorkflowGroup, WorkflowMeta } from './plugin-types.ts';
import { WORKFLOW_LOADERS, WorkflowName, WORKFLOW_METADATA } from './generated-plugins.ts';

export async function loadPlugins(): Promise<Map<string, PluginMeta>> {
  const plugins = new Map<string, PluginMeta>();

  // Load all workflows and collect all their tools
  const workflowGroups = await loadWorkflowGroups();

  for (const [, workflow] of workflowGroups.entries()) {
    for (const tool of workflow.tools) {
      if (tool?.name && typeof tool.handler === 'function') {
        plugins.set(tool.name, tool);
      }
    }
  }

  return plugins;
}

/**
 * Load workflow groups with metadata validation using generated loaders
 */
export async function loadWorkflowGroups(): Promise<Map<string, WorkflowGroup>> {
  const workflows = new Map<string, WorkflowGroup>();

  for (const [workflowName, loader] of Object.entries(WORKFLOW_LOADERS)) {
    try {
      // Dynamic import with code-splitting
      const workflowModule = (await loader()) as {
        workflow?: WorkflowMeta;
        [key: string]: unknown;
      };

      if (!workflowModule.workflow) {
        throw new Error(`Workflow metadata missing in ${workflowName}/index.js`);
      }

      // Validate required fields
      const workflowMeta = workflowModule.workflow as WorkflowMeta;
      if (!workflowMeta.name || typeof workflowMeta.name !== 'string') {
        throw new Error(
          `Invalid workflow.name in ${workflowName}/index.js: must be a non-empty string`,
        );
      }

      if (!workflowMeta.description || typeof workflowMeta.description !== 'string') {
        throw new Error(
          `Invalid workflow.description in ${workflowName}/index.js: must be a non-empty string`,
        );
      }

      workflows.set(workflowName, {
        workflow: workflowMeta,
        tools: await loadWorkflowTools(workflowModule),
        directoryName: workflowName,
      });
    } catch (error) {
      throw new Error(
        `Failed to load workflow '${workflowName}': ${error instanceof Error ? error.message : 'Unknown error'}`,
      );
    }
  }

  return workflows;
}

/**
 * Load workflow tools from the workflow module
 */
async function loadWorkflowTools(workflowModule: Record<string, unknown>): Promise<PluginMeta[]> {
  const tools: PluginMeta[] = [];

  // Load individual tool files from the workflow module
  for (const [key, value] of Object.entries(workflowModule)) {
    if (key !== 'workflow' && value && typeof value === 'object') {
      const tool = value as PluginMeta;
      if (tool.name && typeof tool.handler === 'function') {
        tools.push(tool);
      }
    }
  }

  return tools;
}

/**
 * Get workflow metadata by directory name using generated loaders
 */
export async function getWorkflowMetadata(directoryName: string): Promise<WorkflowMeta | null> {
  try {
    // First try to get from generated metadata (fast path)
    const metadata = WORKFLOW_METADATA[directoryName as WorkflowName];
    if (metadata) {
      return metadata;
    }

    // Fall back to loading the actual module
    const loader = WORKFLOW_LOADERS[directoryName as WorkflowName];
    if (loader) {
      const workflowModule = (await loader()) as { workflow?: WorkflowMeta };
      return workflowModule.workflow ?? null;
    }

    return null;
  } catch {
    return null;
  }
}

```

--------------------------------------------------------------------------------
/src/mcp/tools/simulator/__tests__/test_sim.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for test_sim plugin (session-aware version)
 * Follows CLAUDE.md guidance: dependency injection, no vi-mocks, literal validation.
 */

import { describe, it, expect, beforeEach } from 'vitest';
import { z } from 'zod';
import { sessionStore } from '../../../../utils/session-store.ts';
import testSim from '../test_sim.ts';

describe('test_sim tool', () => {
  beforeEach(() => {
    sessionStore.clear();
  });

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

    it('should have concise description', () => {
      expect(testSim.description).toBe('Runs tests on an iOS simulator.');
    });

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

    it('should expose only non-session fields in public schema', () => {
      const schema = z.object(testSim.schema);

      expect(schema.safeParse({}).success).toBe(true);
      expect(
        schema.safeParse({
          derivedDataPath: '/tmp/derived',
          extraArgs: ['--quiet'],
          preferXcodebuild: true,
          testRunnerEnv: { FOO: 'BAR' },
        }).success,
      ).toBe(true);

      expect(schema.safeParse({ derivedDataPath: 123 }).success).toBe(false);
      expect(schema.safeParse({ extraArgs: ['--ok', 42] }).success).toBe(false);
      expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false);
      expect(schema.safeParse({ testRunnerEnv: { FOO: 123 } }).success).toBe(false);

      const schemaKeys = Object.keys(testSim.schema).sort();
      expect(schemaKeys).toEqual(
        ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'testRunnerEnv'].sort(),
      );
    });
  });

  describe('Handler Requirements', () => {
    it('should require scheme when not provided', async () => {
      const result = await testSim.handler({});

      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('scheme is required');
    });

    it('should require project or workspace when scheme default exists', async () => {
      sessionStore.setDefaults({ scheme: 'MyScheme' });

      const result = await testSim.handler({});

      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('Provide a project or workspace');
    });

    it('should require simulator identifier when scheme and project defaults exist', async () => {
      sessionStore.setDefaults({
        scheme: 'MyScheme',
        projectPath: '/path/to/project.xcodeproj',
      });

      const result = await testSim.handler({});

      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('Provide simulatorId or simulatorName');
    });

    it('should error when both simulatorId and simulatorName provided explicitly', async () => {
      sessionStore.setDefaults({
        scheme: 'MyScheme',
        workspacePath: '/path/to/workspace.xcworkspace',
      });

      const result = await testSim.handler({
        simulatorId: 'SIM-UUID',
        simulatorName: 'iPhone 16',
      });

      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('Mutually exclusive parameters provided');
      expect(result.content[0].text).toContain('simulatorId');
      expect(result.content[0].text).toContain('simulatorName');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/mcp/tools/swift-package/swift_package_stop.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts';
import { getProcess, removeProcess, type ProcessInfo } from './active-processes.ts';
import { ToolResponse } from '../../../types/common.ts';

// Define schema as ZodObject
const swiftPackageStopSchema = z.object({
  pid: z.number().describe('Process ID (PID) of the running executable'),
});

// Use z.infer for type safety
type SwiftPackageStopParams = z.infer<typeof swiftPackageStopSchema>;

/**
 * Process manager interface for dependency injection
 */
export interface ProcessManager {
  getProcess: (pid: number) => ProcessInfo | undefined;
  removeProcess: (pid: number) => boolean;
}

/**
 * Default process manager implementation
 */
const defaultProcessManager: ProcessManager = {
  getProcess,
  removeProcess,
};

/**
 * Get the default process manager instance
 */
export function getDefaultProcessManager(): ProcessManager {
  return defaultProcessManager;
}

/**
 * Create a mock process manager for testing
 */
export function createMockProcessManager(overrides?: Partial<ProcessManager>): ProcessManager {
  return {
    getProcess: () => undefined,
    removeProcess: () => true,
    ...overrides,
  };
}

/**
 * Business logic for stopping a Swift Package executable
 */
export async function swift_package_stopLogic(
  params: SwiftPackageStopParams,
  processManager: ProcessManager = getDefaultProcessManager(),
  timeout: number = 5000,
): Promise<ToolResponse> {
  const processInfo = processManager.getProcess(params.pid);
  if (!processInfo) {
    return createTextResponse(
      `⚠️ No running process found with PID ${params.pid}. Use swift_package_run to check active processes.`,
      true,
    );
  }

  try {
    processInfo.process.kill('SIGTERM');

    // Give it time to terminate gracefully (configurable for testing)
    await new Promise((resolve) => {
      let terminated = false;

      processInfo.process.on('exit', () => {
        terminated = true;
        resolve(true);
      });

      setTimeout(() => {
        if (!terminated) {
          processInfo.process.kill('SIGKILL');
        }
        resolve(true);
      }, timeout);
    });

    processManager.removeProcess(params.pid);

    return {
      content: [
        {
          type: 'text',
          text: `✅ Stopped executable (was running since ${processInfo.startedAt.toISOString()})`,
        },
        {
          type: 'text',
          text: `💡 Process terminated. You can now run swift_package_run again if needed.`,
        },
      ],
    };
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    return createErrorResponse('Failed to stop process', message);
  }
}

export default {
  name: 'swift_package_stop',
  description: 'Stops a running Swift Package executable started with swift_package_run',
  schema: swiftPackageStopSchema.shape, // MCP SDK compatibility
  async handler(args: Record<string, unknown>): Promise<ToolResponse> {
    // Validate parameters using Zod
    const parseResult = swiftPackageStopSchema.safeParse(args);
    if (!parseResult.success) {
      return createErrorResponse(
        'Parameter validation failed',
        parseResult.error.errors.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
      );
    }

    return swift_package_stopLogic(parseResult.data);
  },
};

```

--------------------------------------------------------------------------------
/src/core/resources.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Resource Management - MCP Resource handlers and URI management
 *
 * This module manages MCP resources, providing a unified interface for exposing
 * data through the Model Context Protocol resource system. Resources allow clients
 * to access data via URI references without requiring tool calls.
 *
 * Responsibilities:
 * - Loading resources from the plugin-based resource system
 * - Managing resource registration with the MCP server
 * - Providing fallback compatibility for clients without resource support
 */

import { McpServer } from '@camsoft/mcp-sdk/server/mcp.js';
import { ReadResourceResult } from '@camsoft/mcp-sdk/types.js';
import { log } from '../utils/logging/index.ts';
import type { CommandExecutor } from '../utils/execution/index.ts';
import { RESOURCE_LOADERS } from './generated-resources.ts';

/**
 * Resource metadata interface
 */
export interface ResourceMeta {
  uri: string;
  name: string;
  description: string;
  mimeType: string;
  handler: (
    uri: URL,
    executor?: CommandExecutor,
  ) => Promise<{
    contents: Array<{ text: string }>;
  }>;
}

/**
 * Load all resources using generated loaders
 * @returns Map of resource URI to resource metadata
 */
export async function loadResources(): Promise<Map<string, ResourceMeta>> {
  const resources = new Map<string, ResourceMeta>();

  for (const [resourceName, loader] of Object.entries(RESOURCE_LOADERS)) {
    try {
      const resource = (await loader()) as ResourceMeta;

      if (!resource.uri || !resource.handler || typeof resource.handler !== 'function') {
        throw new Error(`Invalid resource structure for ${resourceName}`);
      }

      resources.set(resource.uri, resource);
      log('info', `Loaded resource: ${resourceName} (${resource.uri})`);
    } catch (error) {
      log(
        'error',
        `Failed to load resource ${resourceName}: ${error instanceof Error ? error.message : String(error)}`,
      );
    }
  }

  return resources;
}

/**
 * Register all resources with the MCP server if client supports resources
 * @param server The MCP server instance
 * @returns true if resources were registered, false if skipped due to client limitations
 */
export async function registerResources(server: McpServer): Promise<boolean> {
  const resources = await loadResources();

  for (const [uri, resource] of Array.from(resources)) {
    // Create a handler wrapper that matches ReadResourceCallback signature
    const readCallback = async (resourceUri: URL): Promise<ReadResourceResult> => {
      const result = await resource.handler(resourceUri);
      // Transform the content to match MCP SDK expectations
      return {
        contents: result.contents.map((content) => ({
          uri: resourceUri.toString(),
          text: content.text,
          mimeType: resource.mimeType,
        })),
      };
    };

    server.resource(
      resource.name,
      uri,
      {
        mimeType: resource.mimeType,
        title: resource.description,
      },
      readCallback,
    );

    log('info', `Registered resource: ${resource.name} at ${uri}`);
  }

  log('info', `Registered ${resources.size} resources`);
  return true;
}

/**
 * Get all available resource URIs
 * @returns Array of resource URI strings
 */
export async function getAvailableResources(): Promise<string[]> {
  const resources = await loadResources();
  return Array.from(resources.keys());
}

```

--------------------------------------------------------------------------------
/example_projects/iOS_Calculator/CalculatorAppPackage/Sources/CalculatorAppFeature/ContentView.swift:
--------------------------------------------------------------------------------

```swift
import SwiftUI

public struct ContentView: View {
    @State private var calculatorService = CalculatorService()
    @State private var backgroundGradient = BackgroundState.normal
    
    private var inputHandler: CalculatorInputHandler {
        CalculatorInputHandler(service: calculatorService)
    }
    
    public var body: some View {
        GeometryReader { geometry in
            ZStack {
                // Dynamic gradient background
                AnimatedBackground(backgroundGradient: backgroundGradient)
                
                VStack(spacing: 0) {
                    Spacer()
                    
                    // Display Section
                    CalculatorDisplay(
                        expressionDisplay: calculatorService.expressionDisplay,
                        display: calculatorService.display,
                        onDeleteLastDigit: {
                            inputHandler.deleteLastDigit()
                        }
                    )
                    
                    // Button Grid
                    LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 12), count: 4), spacing: 12) {
                        ForEach(calculatorButtons, id: \.self) { button in
                            CalculatorButton(
                                title: button,
                                buttonType: buttonType(for: button),
                                isWideButton: button == "0"
                            ) {
                                handleButtonPress(button)
                            }
                        }
                    }
                    .padding(.horizontal, 20)
                    .padding(.bottom, max(geometry.safeAreaInsets.bottom, 20))
                }
            }
        }
    }
    
    // Calculator button layout (proper grid with = button in correct position)
    private var calculatorButtons: [String] {
        [
            "C", "±", "%", "÷",
            "7", "8", "9", "×", 
            "4", "5", "6", "-",
            "1", "2", "3", "+",
            "",  "0", ".", "="
        ]
    }
    
    private func buttonType(for button: String) -> CalculatorButtonType {
        switch button {
        case "C", "±", "%":
            return .function
        case "÷", "×", "-", "+", "=":
            return .operation
        case "":
            return .hidden
        default:
            return .number
        }
    }
    
    private func handleButtonPress(_ button: String) {
        // Process input through the input handler
        inputHandler.handleInput(button)
        
        // Handle background state changes with modern animation
        withAnimation(.easeInOut(duration: 0.3)) {
            if button == "=" {
                backgroundGradient = calculatorService.hasError ? .error : .calculated
                
                // Reset to normal after a delay using structured concurrency
                Task {
                    try await Task.sleep(for: .seconds(1.5))
                    await MainActor.run {
                        withAnimation(.easeInOut(duration: 0.5)) {
                            backgroundGradient = .normal
                        }
                    }
                }
            } else if button == "C" {
                backgroundGradient = .normal
            }
        }
    }
    
    public init() {}
}


#Preview {
    ContentView()
}

```

--------------------------------------------------------------------------------
/src/utils/xcode.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Xcode Utilities - Core infrastructure for interacting with Xcode tools
 *
 * This utility module provides the foundation for all Xcode interactions across the codebase.
 * It offers platform-specific utilities, and common functionality that can be used by any module
 * requiring Xcode tool integration.
 *
 * Responsibilities:
 * - Constructing platform-specific destination strings (constructDestinationString)
 *
 * This file serves as the foundation layer for more specialized utilities like build-utils.ts,
 * which build upon these core functions to provide higher-level abstractions.
 */

import { log } from './logger.ts';
import { XcodePlatform } from '../types/common.ts';

// Re-export XcodePlatform for use in other modules
export { XcodePlatform };

/**
 * Constructs a destination string for xcodebuild from platform and simulator parameters
 * @param platform The target platform
 * @param simulatorName Optional simulator name
 * @param simulatorId Optional simulator UUID
 * @param useLatest Whether to use the latest simulator version (primarily for named simulators)
 * @param arch Optional architecture for macOS builds (arm64 or x86_64)
 * @returns Properly formatted destination string for xcodebuild
 */
export function constructDestinationString(
  platform: XcodePlatform,
  simulatorName?: string,
  simulatorId?: string,
  useLatest: boolean = true,
  arch?: string,
): string {
  const isSimulatorPlatform = [
    XcodePlatform.iOSSimulator,
    XcodePlatform.watchOSSimulator,
    XcodePlatform.tvOSSimulator,
    XcodePlatform.visionOSSimulator,
  ].includes(platform);

  // If ID is provided for a simulator, it takes precedence and uniquely identifies it.
  if (isSimulatorPlatform && simulatorId) {
    return `platform=${platform},id=${simulatorId}`;
  }

  // If name is provided for a simulator
  if (isSimulatorPlatform && simulatorName) {
    return `platform=${platform},name=${simulatorName}${useLatest ? ',OS=latest' : ''}`;
  }

  // If it's a simulator platform but neither ID nor name is provided (should be prevented by callers now)
  if (isSimulatorPlatform && !simulatorId && !simulatorName) {
    // Throw error as specific simulator is needed unless it's a generic build action
    // Allow fallback for generic simulator builds if needed, but generally require specifics for build/run
    log(
      'warning',
      `Constructing generic destination for ${platform} without name or ID. This might not be specific enough.`,
    );
    // Example: return `platform=${platform},name=Any ${platform} Device`; // Or similar generic target
    throw new Error(`Simulator name or ID is required for specific ${platform} operations`);
  }

  // Handle non-simulator platforms
  switch (platform) {
    case XcodePlatform.macOS:
      return arch ? `platform=macOS,arch=${arch}` : 'platform=macOS';
    case XcodePlatform.iOS:
      return 'generic/platform=iOS';
    case XcodePlatform.watchOS:
      return 'generic/platform=watchOS';
    case XcodePlatform.tvOS:
      return 'generic/platform=tvOS';
    case XcodePlatform.visionOS:
      return 'generic/platform=visionOS';
    // No default needed as enum covers all cases unless extended
    // default:
    //   throw new Error(`Unsupported platform for destination string: ${platform}`);
  }
  // Fallback just in case (shouldn't be reached with enum)
  log('error', `Reached unexpected point in constructDestinationString for platform: ${platform}`);
  return `platform=${platform}`;
}

```

--------------------------------------------------------------------------------
/src/mcp/tools/swift-package/swift_package_test.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import path from 'node:path';
import type { CommandExecutor } from '../../../utils/execution/index.ts';
import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts';
import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts';
import { log } from '../../../utils/logging/index.ts';
import { ToolResponse } from '../../../types/common.ts';
import { createTypedTool } from '../../../utils/typed-tool-factory.ts';

// Define schema as ZodObject
const swiftPackageTestSchema = z.object({
  packagePath: z.string().describe('Path to the Swift package root (Required)'),
  testProduct: z.string().optional().describe('Optional specific test product to run'),
  filter: z.string().optional().describe('Filter tests by name (regex pattern)'),
  configuration: z
    .enum(['debug', 'release'])
    .optional()
    .describe('Swift package configuration (debug, release)'),
  parallel: z.boolean().optional().describe('Run tests in parallel (default: true)'),
  showCodecov: z.boolean().optional().describe('Show code coverage (default: false)'),
  parseAsLibrary: z
    .boolean()
    .optional()
    .describe('Add -parse-as-library flag for @main support (default: false)'),
});

// Use z.infer for type safety
type SwiftPackageTestParams = z.infer<typeof swiftPackageTestSchema>;

export async function swift_package_testLogic(
  params: SwiftPackageTestParams,
  executor: CommandExecutor,
): Promise<ToolResponse> {
  const resolvedPath = path.resolve(params.packagePath);
  const swiftArgs = ['test', '--package-path', resolvedPath];

  if (params.configuration && params.configuration.toLowerCase() === 'release') {
    swiftArgs.push('-c', 'release');
  } else if (params.configuration && params.configuration.toLowerCase() !== 'debug') {
    return createTextResponse("Invalid configuration. Use 'debug' or 'release'.", true);
  }

  if (params.testProduct) {
    swiftArgs.push('--test-product', params.testProduct);
  }

  if (params.filter) {
    swiftArgs.push('--filter', params.filter);
  }

  if (params.parallel === false) {
    swiftArgs.push('--no-parallel');
  }

  if (params.showCodecov) {
    swiftArgs.push('--show-code-coverage');
  }

  if (params.parseAsLibrary) {
    swiftArgs.push('-Xswiftc', '-parse-as-library');
  }

  log('info', `Running swift ${swiftArgs.join(' ')}`);
  try {
    const result = await executor(['swift', ...swiftArgs], 'Swift Package Test', true, undefined);
    if (!result.success) {
      const errorMessage = result.error ?? result.output ?? 'Unknown error';
      return createErrorResponse('Swift package tests failed', errorMessage);
    }

    return {
      content: [
        { type: 'text', text: '✅ Swift package tests completed.' },
        {
          type: 'text',
          text: '💡 Next: Execute your app with swift_package_run if tests passed',
        },
        { type: 'text', text: result.output },
      ],
      isError: false,
    };
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    log('error', `Swift package test failed: ${message}`);
    return createErrorResponse('Failed to execute swift test', message);
  }
}

export default {
  name: 'swift_package_test',
  description: 'Runs tests for a Swift Package with swift test',
  schema: swiftPackageTestSchema.shape, // MCP SDK compatibility
  handler: createTypedTool(
    swiftPackageTestSchema,
    swift_package_testLogic,
    getDefaultCommandExecutor,
  ),
};

```

--------------------------------------------------------------------------------
/src/mcp/tools/device/__tests__/index.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for device-project workflow metadata
 */
import { describe, it, expect } from 'vitest';
import { workflow } from '../index.ts';

describe('device-project workflow metadata', () => {
  describe('Workflow Structure', () => {
    it('should export workflow object with required properties', () => {
      expect(workflow).toHaveProperty('name');
      expect(workflow).toHaveProperty('description');
      expect(workflow).toHaveProperty('platforms');
      expect(workflow).toHaveProperty('targets');
      expect(workflow).toHaveProperty('projectTypes');
      expect(workflow).toHaveProperty('capabilities');
    });

    it('should have correct workflow name', () => {
      expect(workflow.name).toBe('iOS Device Development');
    });

    it('should have correct description', () => {
      expect(workflow.description).toBe(
        'Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting physical devices (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro). Build, test, deploy, and debug apps on real hardware.',
      );
    });

    it('should have correct platforms array', () => {
      expect(workflow.platforms).toEqual(['iOS', 'watchOS', 'tvOS', 'visionOS']);
    });

    it('should have correct targets array', () => {
      expect(workflow.targets).toEqual(['device']);
    });

    it('should have correct projectTypes array', () => {
      expect(workflow.projectTypes).toEqual(['project', 'workspace']);
    });

    it('should have correct capabilities array', () => {
      expect(workflow.capabilities).toEqual([
        'build',
        'test',
        'deploy',
        'debug',
        'log-capture',
        'device-management',
      ]);
    });
  });

  describe('Workflow Validation', () => {
    it('should have valid string properties', () => {
      expect(typeof workflow.name).toBe('string');
      expect(typeof workflow.description).toBe('string');
      expect(workflow.name.length).toBeGreaterThan(0);
      expect(workflow.description.length).toBeGreaterThan(0);
    });

    it('should have valid array properties', () => {
      expect(Array.isArray(workflow.platforms)).toBe(true);
      expect(Array.isArray(workflow.targets)).toBe(true);
      expect(Array.isArray(workflow.projectTypes)).toBe(true);
      expect(Array.isArray(workflow.capabilities)).toBe(true);

      expect(workflow.platforms.length).toBeGreaterThan(0);
      expect(workflow.targets.length).toBeGreaterThan(0);
      expect(workflow.projectTypes.length).toBeGreaterThan(0);
      expect(workflow.capabilities.length).toBeGreaterThan(0);
    });

    it('should contain expected platform values', () => {
      expect(workflow.platforms).toContain('iOS');
      expect(workflow.platforms).toContain('watchOS');
      expect(workflow.platforms).toContain('tvOS');
      expect(workflow.platforms).toContain('visionOS');
    });

    it('should contain expected target values', () => {
      expect(workflow.targets).toContain('device');
    });

    it('should contain expected project type values', () => {
      expect(workflow.projectTypes).toContain('project');
      expect(workflow.projectTypes).toContain('workspace');
    });

    it('should contain expected capability values', () => {
      expect(workflow.capabilities).toContain('build');
      expect(workflow.capabilities).toContain('test');
      expect(workflow.capabilities).toContain('deploy');
      expect(workflow.capabilities).toContain('debug');
      expect(workflow.capabilities).toContain('log-capture');
      expect(workflow.capabilities).toContain('device-management');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/mcp/tools/swift-package/__tests__/index.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for swift-package workflow metadata
 */
import { describe, it, expect } from 'vitest';
import { workflow } from '../index.ts';

describe('swift-package workflow metadata', () => {
  describe('Workflow Structure', () => {
    it('should export workflow object with required properties', () => {
      expect(workflow).toHaveProperty('name');
      expect(workflow).toHaveProperty('description');
      expect(workflow).toHaveProperty('platforms');
      expect(workflow).toHaveProperty('targets');
      expect(workflow).toHaveProperty('projectTypes');
      expect(workflow).toHaveProperty('capabilities');
    });

    it('should have correct workflow name', () => {
      expect(workflow.name).toBe('Swift Package Manager');
    });

    it('should have correct description', () => {
      expect(workflow.description).toBe(
        'Swift Package Manager operations for building, testing, running, and managing Swift packages and dependencies. Complete SPM workflow support.',
      );
    });

    it('should have correct platforms array', () => {
      expect(workflow.platforms).toEqual(['iOS', 'macOS', 'watchOS', 'tvOS', 'visionOS', 'Linux']);
    });

    it('should have correct targets array', () => {
      expect(workflow.targets).toEqual(['package']);
    });

    it('should have correct projectTypes array', () => {
      expect(workflow.projectTypes).toEqual(['swift-package']);
    });

    it('should have correct capabilities array', () => {
      expect(workflow.capabilities).toEqual([
        'build',
        'test',
        'run',
        'clean',
        'dependency-management',
        'package-management',
      ]);
    });
  });

  describe('Workflow Validation', () => {
    it('should have valid string properties', () => {
      expect(typeof workflow.name).toBe('string');
      expect(typeof workflow.description).toBe('string');
      expect(workflow.name.length).toBeGreaterThan(0);
      expect(workflow.description.length).toBeGreaterThan(0);
    });

    it('should have valid array properties', () => {
      expect(Array.isArray(workflow.platforms)).toBe(true);
      expect(Array.isArray(workflow.targets)).toBe(true);
      expect(Array.isArray(workflow.projectTypes)).toBe(true);
      expect(Array.isArray(workflow.capabilities)).toBe(true);

      expect(workflow.platforms.length).toBeGreaterThan(0);
      expect(workflow.targets.length).toBeGreaterThan(0);
      expect(workflow.projectTypes.length).toBeGreaterThan(0);
      expect(workflow.capabilities.length).toBeGreaterThan(0);
    });

    it('should contain expected platform values', () => {
      expect(workflow.platforms).toContain('iOS');
      expect(workflow.platforms).toContain('macOS');
      expect(workflow.platforms).toContain('watchOS');
      expect(workflow.platforms).toContain('tvOS');
      expect(workflow.platforms).toContain('visionOS');
      expect(workflow.platforms).toContain('Linux');
    });

    it('should contain expected target values', () => {
      expect(workflow.targets).toContain('package');
    });

    it('should contain expected project type values', () => {
      expect(workflow.projectTypes).toContain('swift-package');
    });

    it('should contain expected capability values', () => {
      expect(workflow.capabilities).toContain('build');
      expect(workflow.capabilities).toContain('test');
      expect(workflow.capabilities).toContain('run');
      expect(workflow.capabilities).toContain('clean');
      expect(workflow.capabilities).toContain('dependency-management');
      expect(workflow.capabilities).toContain('package-management');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/utils/errors.ts:
--------------------------------------------------------------------------------

```typescript
import { ToolResponse } from '../types/common.ts';

/**
 * Error Utilities - Type-safe error hierarchy for the application
 *
 * This utility module defines a structured error hierarchy for the application,
 * providing specialized error types for different failure scenarios. Using these
 * typed errors enables more precise error handling, improves debugging, and
 * provides better error messages to users.
 *
 * Responsibilities:
 * - Providing a base error class (XcodeBuildMCPError) for all application errors
 * - Defining specialized error subtypes for different error categories:
 *   - ValidationError: Parameter validation failures
 *   - SystemError: Underlying system/OS issues
 *   - ConfigurationError: Application configuration problems
 *   - SimulatorError: iOS simulator-specific failures
 *   - AxeError: axe-specific errors
 *
 * The structured hierarchy allows error consumers to handle errors with the
 * appropriate level of specificity using instanceof checks or catch clauses.
 */

/**
 * Custom error types for XcodeBuildMCP
 */

/**
 * Base error class for XcodeBuildMCP errors
 */
export class XcodeBuildMCPError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'XcodeBuildMCPError';
    // This is necessary for proper inheritance in TypeScript
    Object.setPrototypeOf(this, XcodeBuildMCPError.prototype);
  }
}

/**
 * Error thrown when validation of parameters fails
 */
export class ValidationError extends XcodeBuildMCPError {
  constructor(
    message: string,
    public paramName?: string,
  ) {
    super(message);
    this.name = 'ValidationError';
    Object.setPrototypeOf(this, ValidationError.prototype);
  }
}

/**
 * Error thrown for system-level errors (file access, permissions, etc.)
 */
export class SystemError extends XcodeBuildMCPError {
  constructor(
    message: string,
    public originalError?: Error,
  ) {
    super(message);
    this.name = 'SystemError';
    Object.setPrototypeOf(this, SystemError.prototype);
  }
}

/**
 * Error thrown for configuration issues
 */
export class ConfigurationError extends XcodeBuildMCPError {
  constructor(message: string) {
    super(message);
    this.name = 'ConfigurationError';
    Object.setPrototypeOf(this, ConfigurationError.prototype);
  }
}

/**
 * Error thrown for simulator-specific errors
 */
export class SimulatorError extends XcodeBuildMCPError {
  constructor(
    message: string,
    public simulatorName?: string,
    public simulatorId?: string,
  ) {
    super(message);
    this.name = 'SimulatorError';
    Object.setPrototypeOf(this, SimulatorError.prototype);
  }
}

/**
 * Error thrown for axe-specific errors
 */
export class AxeError extends XcodeBuildMCPError {
  constructor(
    message: string,
    public command?: string, // The axe command that failed
    public axeOutput?: string, // Output from axe
    public simulatorId?: string,
  ) {
    super(message);
    this.name = 'AxeError';
    Object.setPrototypeOf(this, AxeError.prototype);
  }
}

// Helper to create a standard error response
export function createErrorResponse(message: string, details?: string): ToolResponse {
  const detailText = details ? `\nDetails: ${details}` : '';
  return {
    content: [
      {
        type: 'text',
        text: `Error: ${message}${detailText}`,
      },
    ],
    isError: true,
  };
}

/**
 * Error class for missing dependencies
 */
export class DependencyError extends ConfigurationError {
  constructor(
    message: string,
    public details?: string,
  ) {
    super(message);
    this.name = 'DependencyError';
    Object.setPrototypeOf(this, DependencyError.prototype);
  }
}

```

--------------------------------------------------------------------------------
/src/mcp/tools/simulator-management/__tests__/reset_sim_location.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect } from 'vitest';
import { z } from 'zod';
import resetSimLocationPlugin, { reset_sim_locationLogic } from '../reset_sim_location.ts';
import { createMockExecutor } from '../../../../test-utils/mock-executors.ts';

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

    it('should have correct description field', () => {
      expect(resetSimLocationPlugin.description).toBe(
        "Resets the simulator's location to default.",
      );
    });

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

    it('should have correct schema validation', () => {
      const schema = z.object(resetSimLocationPlugin.schema);

      expect(
        schema.safeParse({
          simulatorUuid: 'abc123',
        }).success,
      ).toBe(true);

      expect(
        schema.safeParse({
          simulatorUuid: 123,
        }).success,
      ).toBe(false);

      expect(schema.safeParse({}).success).toBe(false);
    });
  });

  describe('Handler Behavior (Complete Literal Returns)', () => {
    it('should successfully reset simulator location', async () => {
      const mockExecutor = createMockExecutor({
        success: true,
        output: 'Location reset successfully',
      });

      const result = await reset_sim_locationLogic(
        {
          simulatorUuid: 'test-uuid-123',
        },
        mockExecutor,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Successfully reset simulator test-uuid-123 location.',
          },
        ],
      });
    });

    it('should handle command failure', async () => {
      const mockExecutor = createMockExecutor({
        success: false,
        error: 'Command failed',
      });

      const result = await reset_sim_locationLogic(
        {
          simulatorUuid: 'test-uuid-123',
        },
        mockExecutor,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Failed to reset simulator location: Command failed',
          },
        ],
      });
    });

    it('should handle exception during execution', async () => {
      const mockExecutor = createMockExecutor(new Error('Network error'));

      const result = await reset_sim_locationLogic(
        {
          simulatorUuid: 'test-uuid-123',
        },
        mockExecutor,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Failed to reset simulator location: Network error',
          },
        ],
      });
    });

    it('should call correct command', async () => {
      let capturedCommand: string[] = [];
      let capturedLogPrefix: string | undefined;

      const mockExecutor = createMockExecutor({
        success: true,
        output: 'Location reset successfully',
      });

      // Create a wrapper to capture the command arguments
      const capturingExecutor = async (command: string[], logPrefix?: string) => {
        capturedCommand = command;
        capturedLogPrefix = logPrefix;
        return mockExecutor(command, logPrefix);
      };

      await reset_sim_locationLogic(
        {
          simulatorUuid: 'test-uuid-123',
        },
        capturingExecutor,
      );

      expect(capturedCommand).toEqual(['xcrun', 'simctl', 'location', 'test-uuid-123', 'clear']);
      expect(capturedLogPrefix).toBe('Reset Simulator Location');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/utils/sentry.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Sentry instrumentation for XcodeBuildMCP
 *
 * This file initializes Sentry as early as possible in the application lifecycle.
 * It should be imported at the top of the main entry point file.
 */

import * as Sentry from '@sentry/node';
import { version } from '../version.ts';
import { execSync } from 'child_process';

// Inlined system info functions to avoid circular dependencies
function getXcodeInfo(): { version: string; path: string; selectedXcode: string; error?: string } {
  try {
    const xcodebuildOutput = execSync('xcodebuild -version', { encoding: 'utf8' }).trim();
    const version = xcodebuildOutput.split('\n').slice(0, 2).join(' - ');
    const path = execSync('xcode-select -p', { encoding: 'utf8' }).trim();
    const selectedXcode = execSync('xcrun --find xcodebuild', { encoding: 'utf8' }).trim();

    return { version, path, selectedXcode };
  } catch (error) {
    return {
      version: 'Not available',
      path: 'Not available',
      selectedXcode: 'Not available',
      error: error instanceof Error ? error.message : String(error),
    };
  }
}

function getEnvironmentVariables(): Record<string, string> {
  const relevantVars = [
    'INCREMENTAL_BUILDS_ENABLED',
    'PATH',
    'DEVELOPER_DIR',
    'HOME',
    'USER',
    'TMPDIR',
    'NODE_ENV',
    'SENTRY_DISABLED',
  ];

  const envVars: Record<string, string> = {};
  relevantVars.forEach((varName) => {
    envVars[varName] = process.env[varName] ?? '';
  });

  Object.keys(process.env).forEach((key) => {
    if (key.startsWith('XCODEBUILDMCP_')) {
      envVars[key] = process.env[key] ?? '';
    }
  });

  return envVars;
}

function checkBinaryAvailability(binary: string): { available: boolean; version?: string } {
  try {
    execSync(`which ${binary}`, { stdio: 'ignore' });
  } catch {
    return { available: false };
  }

  let version: string | undefined;
  const versionCommands: Record<string, string> = {
    axe: 'axe --version',
    mise: 'mise --version',
  };

  if (binary in versionCommands) {
    try {
      version = execSync(versionCommands[binary], {
        encoding: 'utf8',
        stdio: ['ignore', 'pipe', 'ignore'],
      }).trim();
    } catch {
      // Version command failed, but binary exists
    }
  }

  return { available: true, version };
}

Sentry.init({
  dsn:
    process.env.SENTRY_DSN ??
    'https://798607831167c7b9fe2f2912f5d3c665@o4509258288332800.ingest.de.sentry.io/4509258293837904',

  // Setting this option to true will send default PII data to Sentry
  // For example, automatic IP address collection on events
  sendDefaultPii: true,

  // Set release version to match application version
  release: `xcodebuildmcp@${version}`,

  // Always report under production environment
  environment: 'production',

  // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring
  // We recommend adjusting this value in production
  tracesSampleRate: 1.0,
});

const axeAvailable = checkBinaryAvailability('axe');
const miseAvailable = checkBinaryAvailability('mise');
const envVars = getEnvironmentVariables();
const xcodeInfo = getXcodeInfo();

// Add additional context that might be helpful for debugging
const tags: Record<string, string> = {
  nodeVersion: process.version,
  platform: process.platform,
  arch: process.arch,
  axeAvailable: axeAvailable.available ? 'true' : 'false',
  axeVersion: axeAvailable.version ?? 'Unknown',
  miseAvailable: miseAvailable.available ? 'true' : 'false',
  miseVersion: miseAvailable.version ?? 'Unknown',
  ...Object.fromEntries(Object.entries(envVars).map(([k, v]) => [`env_${k}`, v ?? ''])),
  xcodeVersion: xcodeInfo.version ?? 'Unknown',
  xcodePath: xcodeInfo.path ?? 'Unknown',
};

Sentry.setTags(tags);

```

--------------------------------------------------------------------------------
/src/core/__tests__/resources.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect, beforeEach } from 'vitest';
import { McpServer } from '@camsoft/mcp-sdk/server/mcp.js';

import { registerResources, getAvailableResources, loadResources } from '../resources.ts';

describe('resources', () => {
  let mockServer: McpServer;
  let registeredResources: Array<{
    name: string;
    uri: string;
    metadata: { mimeType: string; title: string };
    handler: any;
  }>;

  beforeEach(() => {
    registeredResources = [];
    // Create a mock MCP server using simple object structure
    mockServer = {
      resource: (
        name: string,
        uri: string,
        metadata: { mimeType: string; title: string },
        handler: any,
      ) => {
        registeredResources.push({ name, uri, metadata, handler });
      },
    } as unknown as McpServer;
  });

  describe('Exports', () => {
    it('should export registerResources function', () => {
      expect(typeof registerResources).toBe('function');
    });

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

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

  describe('loadResources', () => {
    it('should load resources from generated loaders', async () => {
      const resources = await loadResources();

      // Should have at least the simulators resource
      expect(resources.size).toBeGreaterThan(0);
      expect(resources.has('xcodebuildmcp://simulators')).toBe(true);
    });

    it('should validate resource structure', async () => {
      const resources = await loadResources();

      for (const [uri, resource] of resources) {
        expect(resource.uri).toBe(uri);
        expect(typeof resource.description).toBe('string');
        expect(typeof resource.mimeType).toBe('string');
        expect(typeof resource.handler).toBe('function');
      }
    });
  });

  describe('registerResources', () => {
    it('should register all loaded resources with the server and return true', async () => {
      const result = await registerResources(mockServer);

      expect(result).toBe(true);

      // Should have registered at least one resource
      expect(registeredResources.length).toBeGreaterThan(0);

      // Check simulators resource was registered
      const simulatorsResource = registeredResources.find(
        (r) => r.uri === 'xcodebuildmcp://simulators',
      );
      expect(typeof simulatorsResource?.handler).toBe('function');
      expect(simulatorsResource?.metadata.title).toBe(
        'Available iOS simulators with their UUIDs and states',
      );
      expect(simulatorsResource?.metadata.mimeType).toBe('text/plain');
      expect(simulatorsResource?.name).toBe('simulators');
    });

    it('should register resources with correct handlers', async () => {
      const result = await registerResources(mockServer);

      expect(result).toBe(true);

      const simulatorsResource = registeredResources.find(
        (r) => r.uri === 'xcodebuildmcp://simulators',
      );
      expect(typeof simulatorsResource?.handler).toBe('function');
    });
  });

  describe('getAvailableResources', () => {
    it('should return array of available resource URIs', async () => {
      const resources = await getAvailableResources();

      expect(Array.isArray(resources)).toBe(true);
      expect(resources.length).toBeGreaterThan(0);
      expect(resources).toContain('xcodebuildmcp://simulators');
    });

    it('should return unique URIs', async () => {
      const resources = await getAvailableResources();
      const uniqueResources = [...new Set(resources)];

      expect(resources.length).toBe(uniqueResources.length);
    });
  });
});

```

--------------------------------------------------------------------------------
/example_projects/iOS_Calculator/CalculatorAppPackage/Sources/CalculatorAppFeature/CalculatorButton.swift:
--------------------------------------------------------------------------------

```swift
import SwiftUI

// MARK: - Calculator Button Component
struct CalculatorButton: View {
    let title: String
    let buttonType: CalculatorButtonType
    let isWideButton: Bool
    let action: () -> Void
    
    @State private var isPressed = false
    
    var body: some View {
        if buttonType == .hidden {
            // Empty space for layout
            Color.clear
                .frame(height: 80)
        } else {
            Button(action: {
                withAnimation(.easeInOut(duration: 0.1)) {
                    isPressed = true
                }
                action()
                
                Task {
                    try await Task.sleep(for: .seconds(0.1))
                    await MainActor.run {
                        withAnimation(.easeInOut(duration: 0.1)) {
                            isPressed = false
                        }
                    }
                }
            }) {
                ZStack {
                    // Frosted glass background
                    RoundedRectangle(cornerRadius: 20)
                        .fill(.ultraThinMaterial)
                        .overlay(
                            RoundedRectangle(cornerRadius: 20)
                                .stroke(buttonType.borderColor, lineWidth: 1)
                        )
                        .overlay(
                            // Subtle inner glow
                            RoundedRectangle(cornerRadius: 20)
                                .fill(
                                    RadialGradient(
                                        colors: [buttonType.glowColor.opacity(0.3), Color.clear],
                                        center: .topLeading,
                                        startRadius: 0,
                                        endRadius: 50
                                    )
                                )
                        )
                        .scaleEffect(isPressed ? 0.95 : 1.0)
                        .shadow(color: buttonType.shadowColor.opacity(0.3), radius: isPressed ? 2 : 8, x: 0, y: isPressed ? 1 : 4)
                    
                    // Button text
                    Text(title)
                        .font(.system(size: 32, weight: .medium, design: .rounded))
                        .foregroundColor(buttonType.textColor)
                        .scaleEffect(isPressed ? 0.9 : 1.0)
                }
            }
            .frame(height: 80)
            .gridCellColumns(isWideButton ? 2 : 1)
            .buttonStyle(PlainButtonStyle())
        }
    }
}

// MARK: - Button Type Configuration
enum CalculatorButtonType {
    case number, operation, function, hidden
    
    var textColor: Color {
        switch self {
        case .number:
            return .white
        case .operation:
            return .white
        case .function:
            return .white
        case .hidden:
            return .clear
        }
    }
    
    var borderColor: Color {
        switch self {
        case .number:
            return .white.opacity(0.3)
        case .operation:
            return .orange.opacity(0.6)
        case .function:
            return .gray.opacity(0.5)
        case .hidden:
            return .clear
        }
    }
    
    var glowColor: Color {
        switch self {
        case .number:
            return .blue
        case .operation:
            return .orange
        case .function:
            return .gray
        case .hidden:
            return .clear
        }
    }
    
    var shadowColor: Color {
        switch self {
        case .number:
            return .blue
        case .operation:
            return .orange
        case .function:
            return .gray
        case .hidden:
            return .clear
        }
    }
}
```

--------------------------------------------------------------------------------
/src/mcp/tools/project-discovery/get_mac_bundle_id.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Project Discovery Plugin: Get macOS Bundle ID
 *
 * Extracts the bundle identifier from a macOS app bundle (.app).
 */

import { z } from 'zod';
import { log } from '../../../utils/logging/index.ts';
import { ToolResponse } from '../../../types/common.ts';
import {
  CommandExecutor,
  getDefaultFileSystemExecutor,
  getDefaultCommandExecutor,
} from '../../../utils/command.ts';
import { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts';
import { createTypedTool } from '../../../utils/typed-tool-factory.ts';

/**
 * Sync wrapper for CommandExecutor to handle synchronous commands
 */
async function executeSyncCommand(command: string, executor: CommandExecutor): Promise<string> {
  const result = await executor(['/bin/sh', '-c', command], 'macOS Bundle ID Extraction');
  if (!result.success) {
    throw new Error(result.error ?? 'Command failed');
  }
  return result.output || '';
}

// Define schema as ZodObject
const getMacBundleIdSchema = z.object({
  appPath: z
    .string()
    .describe(
      'Path to the macOS .app bundle to extract bundle ID from (full path to the .app directory)',
    ),
});

// Use z.infer for type safety
type GetMacBundleIdParams = z.infer<typeof getMacBundleIdSchema>;

/**
 * Business logic for extracting macOS bundle ID
 */
export async function get_mac_bundle_idLogic(
  params: GetMacBundleIdParams,
  executor: CommandExecutor,
  fileSystemExecutor: FileSystemExecutor,
): Promise<ToolResponse> {
  const appPath = params.appPath;

  if (!fileSystemExecutor.existsSync(appPath)) {
    return {
      content: [
        {
          type: 'text',
          text: `File not found: '${appPath}'. Please check the path and try again.`,
        },
      ],
      isError: true,
    };
  }

  log('info', `Starting bundle ID extraction for macOS app: ${appPath}`);

  try {
    let bundleId;

    try {
      bundleId = await executeSyncCommand(
        `defaults read "${appPath}/Contents/Info" CFBundleIdentifier`,
        executor,
      );
    } catch {
      try {
        bundleId = await executeSyncCommand(
          `/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "${appPath}/Contents/Info.plist"`,
          executor,
        );
      } catch (innerError) {
        throw new Error(
          `Could not extract bundle ID from Info.plist: ${innerError instanceof Error ? innerError.message : String(innerError)}`,
        );
      }
    }

    log('info', `Extracted macOS bundle ID: ${bundleId}`);

    return {
      content: [
        {
          type: 'text',
          text: `✅ Bundle ID: ${bundleId}`,
        },
        {
          type: 'text',
          text: `Next Steps:
- Launch: launch_mac_app({ appPath: "${appPath}" })
- Build again: build_macos({ scheme: "SCHEME_NAME" })`,
        },
      ],
      isError: false,
    };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    log('error', `Error extracting macOS bundle ID: ${errorMessage}`);

    return {
      content: [
        {
          type: 'text',
          text: `Error extracting macOS bundle ID: ${errorMessage}`,
        },
        {
          type: 'text',
          text: `Make sure the path points to a valid macOS app bundle (.app directory).`,
        },
      ],
      isError: true,
    };
  }
}

export default {
  name: 'get_mac_bundle_id',
  description:
    "Extracts the bundle identifier from a macOS app bundle (.app). IMPORTANT: You MUST provide the appPath parameter. Example: get_mac_bundle_id({ appPath: '/path/to/your/app.app' }) Note: In some environments, this tool may be prefixed as mcp0_get_macos_bundle_id.",
  schema: getMacBundleIdSchema.shape, // MCP SDK compatibility
  handler: createTypedTool(
    getMacBundleIdSchema,
    (params: GetMacBundleIdParams) =>
      get_mac_bundle_idLogic(params, getDefaultCommandExecutor(), getDefaultFileSystemExecutor()),
    getDefaultCommandExecutor,
  ),
};

```

--------------------------------------------------------------------------------
/src/mcp/tools/simulator-management/set_sim_location.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { ToolResponse } from '../../../types/common.ts';
import { log } from '../../../utils/logging/index.ts';
import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/execution/index.ts';
import { createTypedTool } from '../../../utils/typed-tool-factory.ts';

// Define schema as ZodObject
const setSimulatorLocationSchema = z.object({
  simulatorUuid: z
    .string()
    .describe('UUID of the simulator to use (obtained from list_simulators)'),
  latitude: z.number().describe('The latitude for the custom location.'),
  longitude: z.number().describe('The longitude for the custom location.'),
});

// Use z.infer for type safety
type SetSimulatorLocationParams = z.infer<typeof setSimulatorLocationSchema>;

// Helper function to execute simctl commands and handle responses
async function executeSimctlCommandAndRespond(
  params: SetSimulatorLocationParams,
  simctlSubCommand: string[],
  operationDescriptionForXcodeCommand: string,
  successMessage: string,
  failureMessagePrefix: string,
  operationLogContext: string,
  executor: CommandExecutor = getDefaultCommandExecutor(),
  extraValidation?: () => ToolResponse | null,
): Promise<ToolResponse> {
  if (extraValidation) {
    const validationResult = extraValidation();
    if (validationResult) {
      return validationResult;
    }
  }

  try {
    const command = ['xcrun', 'simctl', ...simctlSubCommand];
    const result = await executor(command, operationDescriptionForXcodeCommand, true, {});

    if (!result.success) {
      const fullFailureMessage = `${failureMessagePrefix}: ${result.error}`;
      log(
        'error',
        `${fullFailureMessage} (operation: ${operationLogContext}, simulator: ${params.simulatorUuid})`,
      );
      return {
        content: [{ type: 'text', text: fullFailureMessage }],
      };
    }

    log(
      'info',
      `${successMessage} (operation: ${operationLogContext}, simulator: ${params.simulatorUuid})`,
    );
    return {
      content: [{ type: 'text', text: successMessage }],
    };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    const fullFailureMessage = `${failureMessagePrefix}: ${errorMessage}`;
    log(
      'error',
      `Error during ${operationLogContext} for simulator ${params.simulatorUuid}: ${errorMessage}`,
    );
    return {
      content: [{ type: 'text', text: fullFailureMessage }],
    };
  }
}

export async function set_sim_locationLogic(
  params: SetSimulatorLocationParams,
  executor: CommandExecutor,
): Promise<ToolResponse> {
  const extraValidation = (): ToolResponse | null => {
    if (params.latitude < -90 || params.latitude > 90) {
      return {
        content: [
          {
            type: 'text',
            text: 'Latitude must be between -90 and 90 degrees',
          },
        ],
      };
    }
    if (params.longitude < -180 || params.longitude > 180) {
      return {
        content: [
          {
            type: 'text',
            text: 'Longitude must be between -180 and 180 degrees',
          },
        ],
      };
    }
    return null;
  };

  log(
    'info',
    `Setting simulator ${params.simulatorUuid} location to ${params.latitude},${params.longitude}`,
  );

  return executeSimctlCommandAndRespond(
    params,
    ['location', params.simulatorUuid, 'set', `${params.latitude},${params.longitude}`],
    'Set Simulator Location',
    `Successfully set simulator ${params.simulatorUuid} location to ${params.latitude},${params.longitude}`,
    'Failed to set simulator location',
    'set simulator location',
    executor,
    extraValidation,
  );
}

export default {
  name: 'set_sim_location',
  description: 'Sets a custom GPS location for the simulator.',
  schema: setSimulatorLocationSchema.shape, // MCP SDK compatibility
  handler: createTypedTool(
    setSimulatorLocationSchema,
    set_sim_locationLogic,
    getDefaultCommandExecutor,
  ),
};

```

--------------------------------------------------------------------------------
/src/mcp/tools/macos/build_macos.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * macOS Shared Plugin: Build macOS (Unified)
 *
 * Builds a macOS app using xcodebuild from a project or workspace.
 * Accepts mutually exclusive `projectPath` or `workspacePath`.
 */

import { z } from 'zod';
import { log } from '../../../utils/logging/index.ts';
import { executeXcodeBuildCommand } from '../../../utils/build/index.ts';
import { ToolResponse, XcodePlatform } from '../../../types/common.ts';
import type { CommandExecutor } from '../../../utils/execution/index.ts';
import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts';
import { createSessionAwareTool } from '../../../utils/typed-tool-factory.ts';
import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts';

// Types for dependency injection
export interface BuildUtilsDependencies {
  executeXcodeBuildCommand: typeof executeXcodeBuildCommand;
}

// Default implementations
const defaultBuildUtilsDependencies: BuildUtilsDependencies = {
  executeXcodeBuildCommand,
};

// Unified schema: XOR between projectPath and workspacePath
const baseSchemaObject = z.object({
  projectPath: z.string().optional().describe('Path to the .xcodeproj file'),
  workspacePath: z.string().optional().describe('Path to the .xcworkspace file'),
  scheme: z.string().describe('The scheme to use'),
  configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'),
  derivedDataPath: z
    .string()
    .optional()
    .describe('Path where build products and other derived data will go'),
  arch: z
    .enum(['arm64', 'x86_64'])
    .optional()
    .describe('Architecture to build for (arm64 or x86_64). For macOS only.'),
  extraArgs: z.array(z.string()).optional().describe('Additional xcodebuild arguments'),
  preferXcodebuild: z
    .boolean()
    .optional()
    .describe('If true, prefers xcodebuild over the experimental incremental build system'),
});

const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject);

const publicSchemaObject = baseSchemaObject.omit({
  projectPath: true,
  workspacePath: true,
  scheme: true,
  configuration: true,
  arch: true,
} as const);

const buildMacOSSchema = baseSchema
  .refine((val) => val.projectPath !== undefined || val.workspacePath !== undefined, {
    message: 'Either projectPath or workspacePath is required.',
  })
  .refine((val) => !(val.projectPath !== undefined && val.workspacePath !== undefined), {
    message: 'projectPath and workspacePath are mutually exclusive. Provide only one.',
  });

export type BuildMacOSParams = z.infer<typeof buildMacOSSchema>;

/**
 * Business logic for building macOS apps from project or workspace with dependency injection.
 * Exported for direct testing and reuse.
 */
export async function buildMacOSLogic(
  params: BuildMacOSParams,
  executor: CommandExecutor,
  buildUtilsDeps: BuildUtilsDependencies = defaultBuildUtilsDependencies,
): Promise<ToolResponse> {
  log('info', `Starting macOS build for scheme ${params.scheme} (internal)`);

  const processedParams = {
    ...params,
    configuration: params.configuration ?? 'Debug',
    preferXcodebuild: params.preferXcodebuild ?? false,
  };

  return buildUtilsDeps.executeXcodeBuildCommand(
    processedParams,
    {
      platform: XcodePlatform.macOS,
      arch: params.arch,
      logPrefix: 'macOS Build',
    },
    processedParams.preferXcodebuild ?? false,
    'build',
    executor,
  );
}

export default {
  name: 'build_macos',
  description: 'Builds a macOS app.',
  schema: publicSchemaObject.shape,
  handler: createSessionAwareTool<BuildMacOSParams>({
    internalSchema: buildMacOSSchema as unknown as z.ZodType<BuildMacOSParams>,
    logicFunction: buildMacOSLogic,
    getExecutor: getDefaultCommandExecutor,
    requirements: [
      { allOf: ['scheme'], message: 'scheme is required' },
      { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' },
    ],
    exclusivePairs: [['projectPath', 'workspacePath']],
  }),
};

```

--------------------------------------------------------------------------------
/src/mcp/tools/project-discovery/get_app_bundle_id.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Project Discovery Plugin: Get App Bundle ID
 *
 * Extracts the bundle identifier from an app bundle (.app) for any Apple platform
 * (iOS, iPadOS, watchOS, tvOS, visionOS).
 */

import { z } from 'zod';
import { log } from '../../../utils/logging/index.ts';
import { ToolResponse } from '../../../types/common.ts';
import {
  CommandExecutor,
  getDefaultFileSystemExecutor,
  getDefaultCommandExecutor,
} from '../../../utils/command.ts';
import { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts';
import { createTypedTool } from '../../../utils/typed-tool-factory.ts';

// Define schema as ZodObject
const getAppBundleIdSchema = z.object({
  appPath: z
    .string()
    .describe(
      'Path to the .app bundle to extract bundle ID from (full path to the .app directory)',
    ),
});

// Use z.infer for type safety
type GetAppBundleIdParams = z.infer<typeof getAppBundleIdSchema>;

/**
 * Sync wrapper for CommandExecutor to handle synchronous commands
 */
async function executeSyncCommand(command: string, executor: CommandExecutor): Promise<string> {
  const result = await executor(['/bin/sh', '-c', command], 'Bundle ID Extraction');
  if (!result.success) {
    throw new Error(result.error ?? 'Command failed');
  }
  return result.output || '';
}

/**
 * Business logic for extracting bundle ID from app.
 * Separated for testing and reusability.
 */
export async function get_app_bundle_idLogic(
  params: GetAppBundleIdParams,
  executor: CommandExecutor,
  fileSystemExecutor: FileSystemExecutor,
): Promise<ToolResponse> {
  // Zod validation is handled by createTypedTool, so params.appPath is guaranteed to be a string
  const appPath = params.appPath;

  if (!fileSystemExecutor.existsSync(appPath)) {
    return {
      content: [
        {
          type: 'text',
          text: `File not found: '${appPath}'. Please check the path and try again.`,
        },
      ],
      isError: true,
    };
  }

  log('info', `Starting bundle ID extraction for app: ${appPath}`);

  try {
    let bundleId;

    try {
      bundleId = await executeSyncCommand(
        `defaults read "${appPath}/Info" CFBundleIdentifier`,
        executor,
      );
    } catch {
      try {
        bundleId = await executeSyncCommand(
          `/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "${appPath}/Info.plist"`,
          executor,
        );
      } catch (innerError) {
        throw new Error(
          `Could not extract bundle ID from Info.plist: ${innerError instanceof Error ? innerError.message : String(innerError)}`,
        );
      }
    }

    log('info', `Extracted app bundle ID: ${bundleId}`);

    return {
      content: [
        {
          type: 'text',
          text: `✅ Bundle ID: ${bundleId}`,
        },
        {
          type: 'text',
          text: `Next Steps:
- Simulator: install_app_sim + launch_app_sim
- Device: install_app_device + launch_app_device`,
        },
      ],
      isError: false,
    };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    log('error', `Error extracting app bundle ID: ${errorMessage}`);

    return {
      content: [
        {
          type: 'text',
          text: `Error extracting app bundle ID: ${errorMessage}`,
        },
        {
          type: 'text',
          text: `Make sure the path points to a valid app bundle (.app directory).`,
        },
      ],
      isError: true,
    };
  }
}

export default {
  name: 'get_app_bundle_id',
  description:
    "Extracts the bundle identifier from an app bundle (.app) for any Apple platform (iOS, iPadOS, watchOS, tvOS, visionOS). IMPORTANT: You MUST provide the appPath parameter. Example: get_app_bundle_id({ appPath: '/path/to/your/app.app' })",
  schema: getAppBundleIdSchema.shape, // MCP SDK compatibility
  handler: createTypedTool(
    getAppBundleIdSchema,
    (params: GetAppBundleIdParams) =>
      get_app_bundle_idLogic(params, getDefaultCommandExecutor(), getDefaultFileSystemExecutor()),
    getDefaultCommandExecutor,
  ),
};

```

--------------------------------------------------------------------------------
/src/mcp/resources/__tests__/doctor.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect } from 'vitest';

import doctorResource, { doctorResourceLogic } from '../doctor.ts';
import { createMockExecutor } from '../../../test-utils/mock-executors.ts';

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

    it('should export correct description', () => {
      expect(doctorResource.description).toBe(
        'Comprehensive development environment diagnostic information and configuration status',
      );
    });

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

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

  describe('Handler Functionality', () => {
    it('should handle successful environment data retrieval', async () => {
      const mockExecutor = createMockExecutor({
        success: true,
        output: 'Mock command output',
      });

      const result = await doctorResourceLogic(mockExecutor);

      expect(result.contents).toHaveLength(1);
      expect(result.contents[0].text).toContain('XcodeBuildMCP Doctor');
      expect(result.contents[0].text).toContain('## System Information');
      expect(result.contents[0].text).toContain('## Node.js Information');
      expect(result.contents[0].text).toContain('## Dependencies');
      expect(result.contents[0].text).toContain('## Environment Variables');
      expect(result.contents[0].text).toContain('## Feature Status');
    });

    it('should handle spawn errors by showing doctor info', async () => {
      const mockExecutor = createMockExecutor(new Error('spawn xcrun ENOENT'));

      const result = await doctorResourceLogic(mockExecutor);

      expect(result.contents).toHaveLength(1);
      expect(result.contents[0].text).toContain('XcodeBuildMCP Doctor');
      expect(result.contents[0].text).toContain('Error: spawn xcrun ENOENT');
    });

    it('should include required doctor sections', async () => {
      // Set dynamic tools environment variable to include discover_tools text
      const originalValue = process.env.XCODEBUILDMCP_DYNAMIC_TOOLS;
      process.env.XCODEBUILDMCP_DYNAMIC_TOOLS = 'true';

      try {
        const mockExecutor = createMockExecutor({
          success: true,
          output: 'Mock output',
        });

        const result = await doctorResourceLogic(mockExecutor);

        expect(result.contents[0].text).toContain('## Troubleshooting Tips');
        expect(result.contents[0].text).toContain('brew tap cameroncooke/axe');
        expect(result.contents[0].text).toContain('INCREMENTAL_BUILDS_ENABLED=1');
        expect(result.contents[0].text).toContain('discover_tools');
      } finally {
        // Restore original environment variable
        if (originalValue === undefined) {
          delete process.env.XCODEBUILDMCP_DYNAMIC_TOOLS;
        } else {
          process.env.XCODEBUILDMCP_DYNAMIC_TOOLS = originalValue;
        }
      }
    });

    it('should provide feature status information', async () => {
      const mockExecutor = createMockExecutor({
        success: true,
        output: 'Mock output',
      });

      const result = await doctorResourceLogic(mockExecutor);

      expect(result.contents[0].text).toContain('### UI Automation (axe)');
      expect(result.contents[0].text).toContain('### Incremental Builds');
      expect(result.contents[0].text).toContain('### Mise Integration');
      expect(result.contents[0].text).toContain('## Tool Availability Summary');
    });

    it('should handle error conditions gracefully', async () => {
      const mockExecutor = createMockExecutor({
        success: false,
        output: '',
        error: 'Command failed',
      });

      const result = await doctorResourceLogic(mockExecutor);

      expect(result.contents).toHaveLength(1);
      expect(result.contents[0].text).toContain('XcodeBuildMCP Doctor');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

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

/**
 * XcodeBuildMCP - Main entry point
 *
 * This file serves as the entry point for the XcodeBuildMCP server, importing and registering
 * all tool modules with the MCP server. It follows the platform-specific approach for Xcode tools.
 *
 * Responsibilities:
 * - Creating and starting the MCP server
 * - Registering all platform-specific tool modules
 * - Configuring server options and logging
 * - Handling server lifecycle events
 */

// Import Sentry instrumentation
import './utils/sentry.ts';

// Import server components
import { createServer, startServer } from './server/server.ts';
import { McpServer } from '@camsoft/mcp-sdk/server/mcp.js';

// Import MCP types for logging
import { SetLevelRequestSchema } from '@camsoft/mcp-sdk/types.js';

// Import utilities
import { log, setLogLevel, type LogLevel } from './utils/logger.ts';

// Import version
import { version } from './version.ts';

// Import xcodemake utilities
import { isXcodemakeEnabled, isXcodemakeAvailable } from './utils/xcodemake.ts';

// Import process for stdout configuration
import process from 'node:process';

// Import resource management
import { registerResources } from './core/resources.ts';
import {
  registerDiscoveryTools,
  registerAllToolsStatic,
  registerSelectedWorkflows,
} from './utils/tool-registry.ts';

/**
 * Main function to start the server
 */
async function main(): Promise<void> {
  try {
    // Check if xcodemake is enabled and available
    if (isXcodemakeEnabled()) {
      log('info', 'xcodemake is enabled, checking if available...');
      const available = await isXcodemakeAvailable();
      if (available) {
        log('info', 'xcodemake is available and will be used for builds');
      } else {
        log(
          'warn',
          'xcodemake is enabled but could not be made available, falling back to xcodebuild',
        );
      }
    } else {
      log('debug', 'xcodemake is disabled, using standard xcodebuild');
    }

    // Create the server
    const server = createServer();

    // Register logging/setLevel handler
    server.server.setRequestHandler(SetLevelRequestSchema, async (request) => {
      const { level } = request.params;
      setLogLevel(level as LogLevel);
      log('info', `Client requested log level: ${level}`);
      return {}; // Empty result as per MCP spec
    });

    // Make server available globally for dynamic tools
    (globalThis as { mcpServer?: McpServer }).mcpServer = server;

    // Check if dynamic tools mode is explicitly disabled
    const isDynamicModeEnabled = process.env.XCODEBUILDMCP_DYNAMIC_TOOLS === 'true';

    if (isDynamicModeEnabled) {
      // DYNAMIC MODE: Start with discovery tools only
      log('info', '🚀 Initializing server in dynamic tools mode...');
      await registerDiscoveryTools(server);
      log('info', '💡 Use discover_tools to enable additional workflows based on your task.');
    } else {
      // STATIC MODE: Check for selective workflows
      const enabledWorkflows = process.env.XCODEBUILDMCP_ENABLED_WORKFLOWS;

      if (enabledWorkflows) {
        const workflowNames = enabledWorkflows.split(',');
        log('info', `🚀 Initializing server with selected workflows: ${workflowNames.join(', ')}`);
        await registerSelectedWorkflows(server, workflowNames);
      } else {
        log('info', '🚀 Initializing server in static tools mode...');
        await registerAllToolsStatic(server);
      }
    }

    await registerResources(server);

    // Start the server
    await startServer(server);

    // Clean up on exit
    process.on('SIGTERM', async () => {
      await server.close();
      process.exit(0);
    });

    process.on('SIGINT', async () => {
      await server.close();
      process.exit(0);
    });

    // Log successful startup
    log('info', `XcodeBuildMCP server (version ${version}) started successfully`);
  } catch (error) {
    console.error('Fatal error in main():', error);
    process.exit(1);
  }
}

// Start the server
main().catch((error) => {
  console.error('Unhandled exception:', error);
  // Give Sentry a moment to send the error before exiting
  setTimeout(() => process.exit(1), 1000);
});

```

--------------------------------------------------------------------------------
/src/mcp/tools/session-management/__tests__/session_set_defaults.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect, beforeEach } from 'vitest';
import { sessionStore } from '../../../../utils/session-store.ts';
import plugin, { sessionSetDefaultsLogic } from '../session_set_defaults.ts';

describe('session-set-defaults tool', () => {
  beforeEach(() => {
    sessionStore.clear();
  });

  describe('Export Field Validation (Literal)', () => {
    it('should have correct name', () => {
      expect(plugin.name).toBe('session-set-defaults');
    });

    it('should have correct description', () => {
      expect(plugin.description).toBe(
        'Set the session defaults needed by many tools. Most tools require one or more session defaults to be set before they can be used. Agents should set the relevant defaults at the beginning of a session.',
      );
    });

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

    it('should have schema object', () => {
      expect(plugin.schema).toBeDefined();
      expect(typeof plugin.schema).toBe('object');
    });
  });

  describe('Handler Behavior', () => {
    it('should set provided defaults and return updated state', async () => {
      const result = await sessionSetDefaultsLogic({
        scheme: 'MyScheme',
        simulatorName: 'iPhone 16',
        useLatestOS: true,
        arch: 'arm64',
      });

      expect(result.isError).toBe(false);
      expect(result.content[0].text).toContain('Defaults updated:');

      const current = sessionStore.getAll();
      expect(current.scheme).toBe('MyScheme');
      expect(current.simulatorName).toBe('iPhone 16');
      expect(current.useLatestOS).toBe(true);
      expect(current.arch).toBe('arm64');
    });

    it('should validate parameter types via Zod', async () => {
      const result = await plugin.handler({
        useLatestOS: 'yes' as unknown as boolean,
      });

      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('Parameter validation failed');
      expect(result.content[0].text).toContain('useLatestOS');
    });

    it('should clear workspacePath when projectPath is set', async () => {
      sessionStore.setDefaults({ workspacePath: '/old/App.xcworkspace' });
      await sessionSetDefaultsLogic({ projectPath: '/new/App.xcodeproj' });
      const current = sessionStore.getAll();
      expect(current.projectPath).toBe('/new/App.xcodeproj');
      expect(current.workspacePath).toBeUndefined();
    });

    it('should clear projectPath when workspacePath is set', async () => {
      sessionStore.setDefaults({ projectPath: '/old/App.xcodeproj' });
      await sessionSetDefaultsLogic({ workspacePath: '/new/App.xcworkspace' });
      const current = sessionStore.getAll();
      expect(current.workspacePath).toBe('/new/App.xcworkspace');
      expect(current.projectPath).toBeUndefined();
    });

    it('should clear simulatorName when simulatorId is set', async () => {
      sessionStore.setDefaults({ simulatorName: 'iPhone 16' });
      await sessionSetDefaultsLogic({ simulatorId: 'SIM-UUID' });
      const current = sessionStore.getAll();
      expect(current.simulatorId).toBe('SIM-UUID');
      expect(current.simulatorName).toBeUndefined();
    });

    it('should clear simulatorId when simulatorName is set', async () => {
      sessionStore.setDefaults({ simulatorId: 'SIM-UUID' });
      await sessionSetDefaultsLogic({ simulatorName: 'iPhone 16' });
      const current = sessionStore.getAll();
      expect(current.simulatorName).toBe('iPhone 16');
      expect(current.simulatorId).toBeUndefined();
    });

    it('should reject when both projectPath and workspacePath are provided', async () => {
      const res = await plugin.handler({
        projectPath: '/app/App.xcodeproj',
        workspacePath: '/app/App.xcworkspace',
      });
      expect(res.isError).toBe(true);
      expect(res.content[0].text).toContain('Parameter validation failed');
      expect(res.content[0].text).toContain('projectPath and workspacePath are mutually exclusive');
    });

    it('should reject when both simulatorId and simulatorName are provided', async () => {
      const res = await plugin.handler({
        simulatorId: 'SIM-1',
        simulatorName: 'iPhone 16',
      });
      expect(res.isError).toBe(true);
      expect(res.content[0].text).toContain('Parameter validation failed');
      expect(res.content[0].text).toContain('simulatorId and simulatorName are mutually exclusive');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/mcp/tools/device/launch_app_device.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Device Workspace Plugin: Launch App Device
 *
 * Launches an app on a physical Apple device (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro).
 * Requires deviceId and bundleId.
 */

import { z } from 'zod';
import { ToolResponse } from '../../../types/common.ts';
import { log } from '../../../utils/logging/index.ts';
import type { CommandExecutor } from '../../../utils/execution/index.ts';
import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts';
import { createSessionAwareTool } from '../../../utils/typed-tool-factory.ts';
import { promises as fs } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';

// Type for the launch JSON response
type LaunchDataResponse = {
  result?: {
    process?: {
      processIdentifier?: number;
    };
  };
};

// Define schema as ZodObject
const launchAppDeviceSchema = z.object({
  deviceId: z.string().describe('UDID of the device (obtained from list_devices)'),
  bundleId: z
    .string()
    .describe('Bundle identifier of the app to launch (e.g., "com.example.MyApp")'),
});

// Use z.infer for type safety
type LaunchAppDeviceParams = z.infer<typeof launchAppDeviceSchema>;

export async function launch_app_deviceLogic(
  params: LaunchAppDeviceParams,
  executor: CommandExecutor,
): Promise<ToolResponse> {
  const { deviceId, bundleId } = params;

  log('info', `Launching app ${bundleId} on device ${deviceId}`);

  try {
    // Use JSON output to capture process ID
    const tempJsonPath = join(tmpdir(), `launch-${Date.now()}.json`);

    const result = await executor(
      [
        'xcrun',
        'devicectl',
        'device',
        'process',
        'launch',
        '--device',
        deviceId,
        '--json-output',
        tempJsonPath,
        '--terminate-existing',
        bundleId,
      ],
      'Launch app on device',
      true, // useShell
      undefined, // env
    );

    if (!result.success) {
      return {
        content: [
          {
            type: 'text',
            text: `Failed to launch app: ${result.error}`,
          },
        ],
        isError: true,
      };
    }

    // Parse JSON to extract process ID
    let processId: number | undefined;
    try {
      const jsonContent = await fs.readFile(tempJsonPath, 'utf8');
      const parsedData: unknown = JSON.parse(jsonContent);

      // Type guard to validate the parsed data structure
      if (
        parsedData &&
        typeof parsedData === 'object' &&
        'result' in parsedData &&
        parsedData.result &&
        typeof parsedData.result === 'object' &&
        'process' in parsedData.result &&
        parsedData.result.process &&
        typeof parsedData.result.process === 'object' &&
        'processIdentifier' in parsedData.result.process &&
        typeof parsedData.result.process.processIdentifier === 'number'
      ) {
        const launchData = parsedData as LaunchDataResponse;
        processId = launchData.result?.process?.processIdentifier;
      }

      // Clean up temp file
      await fs.unlink(tempJsonPath).catch(() => {});
    } catch (error) {
      log('warn', `Failed to parse launch JSON output: ${error}`);
    }

    let responseText = `✅ App launched successfully\n\n${result.output}`;

    if (processId) {
      responseText += `\n\nProcess ID: ${processId}`;
      responseText += `\n\nNext Steps:`;
      responseText += `\n1. Interact with your app on the device`;
      responseText += `\n2. Stop the app: stop_app_device({ deviceId: "${deviceId}", processId: ${processId} })`;
    }

    return {
      content: [
        {
          type: 'text',
          text: responseText,
        },
      ],
    };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    log('error', `Error launching app on device: ${errorMessage}`);
    return {
      content: [
        {
          type: 'text',
          text: `Failed to launch app on device: ${errorMessage}`,
        },
      ],
      isError: true,
    };
  }
}

export default {
  name: 'launch_app_device',
  description: 'Launches an app on a connected device.',
  schema: launchAppDeviceSchema.omit({ deviceId: true } as const).shape,
  handler: createSessionAwareTool<LaunchAppDeviceParams>({
    internalSchema: launchAppDeviceSchema as unknown as z.ZodType<LaunchAppDeviceParams>,
    logicFunction: launch_app_deviceLogic,
    getExecutor: getDefaultCommandExecutor,
    requirements: [{ allOf: ['deviceId'], message: 'deviceId is required' }],
  }),
};

```

--------------------------------------------------------------------------------
/src/mcp/tools/simulator-management/__tests__/set_sim_appearance.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect } from 'vitest';
import { z } from 'zod';
import setSimAppearancePlugin, { set_sim_appearanceLogic } from '../set_sim_appearance.ts';
import { createMockExecutor } from '../../../../test-utils/mock-executors.ts';

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

    it('should have correct description field', () => {
      expect(setSimAppearancePlugin.description).toBe(
        'Sets the appearance mode (dark/light) of an iOS simulator.',
      );
    });

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

    it('should have correct schema validation', () => {
      const schema = z.object(setSimAppearancePlugin.schema);

      expect(
        schema.safeParse({
          simulatorUuid: 'abc123',
          mode: 'dark',
        }).success,
      ).toBe(true);

      expect(
        schema.safeParse({
          simulatorUuid: 'abc123',
          mode: 'light',
        }).success,
      ).toBe(true);

      expect(
        schema.safeParse({
          simulatorUuid: 'abc123',
          mode: 'invalid',
        }).success,
      ).toBe(false);

      expect(
        schema.safeParse({
          simulatorUuid: 123,
          mode: 'dark',
        }).success,
      ).toBe(false);
    });
  });

  describe('Handler Behavior (Complete Literal Returns)', () => {
    it('should handle successful appearance change', async () => {
      const mockExecutor = createMockExecutor({
        success: true,
        output: '',
        error: '',
      });

      const result = await set_sim_appearanceLogic(
        {
          simulatorUuid: 'test-uuid-123',
          mode: 'dark',
        },
        mockExecutor,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Successfully set simulator test-uuid-123 appearance to dark mode',
          },
        ],
      });
    });

    it('should handle appearance change failure', async () => {
      const mockExecutor = createMockExecutor({
        success: false,
        error: 'Invalid device: invalid-uuid',
      });

      const result = await set_sim_appearanceLogic(
        {
          simulatorUuid: 'invalid-uuid',
          mode: 'light',
        },
        mockExecutor,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Failed to set simulator appearance: Invalid device: invalid-uuid',
          },
        ],
      });
    });

    it('should handle missing simulatorUuid via Zod validation', async () => {
      const mockExecutor = createMockExecutor({
        success: true,
        output: '',
        error: '',
      });

      // Test the handler directly to trigger Zod validation
      const result = await setSimAppearancePlugin.handler({ mode: 'dark' });

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Error: Parameter validation failed\nDetails: Invalid parameters:\nsimulatorUuid: Required',
          },
        ],
        isError: true,
      });
    });

    it('should handle exception during execution', async () => {
      const mockExecutor = createMockExecutor(new Error('Network error'));

      const result = await set_sim_appearanceLogic(
        {
          simulatorUuid: 'test-uuid-123',
          mode: 'dark',
        },
        mockExecutor,
      );

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Failed to set simulator appearance: Network error',
          },
        ],
      });
    });

    it('should call correct command', async () => {
      const commandCalls: any[] = [];
      const mockExecutor = (...args: any[]) => {
        commandCalls.push(args);
        return Promise.resolve({
          success: true,
          output: '',
          error: '',
          process: { pid: 12345 },
        });
      };

      await set_sim_appearanceLogic(
        {
          simulatorUuid: 'test-uuid-123',
          mode: 'dark',
        },
        mockExecutor,
      );

      expect(commandCalls).toEqual([
        [
          ['xcrun', 'simctl', 'ui', 'test-uuid-123', 'appearance', 'dark'],
          'Set Simulator Appearance',
          true,
          undefined,
        ],
      ]);
    });
  });
});

```

--------------------------------------------------------------------------------
/src/mcp/tools/simulator/__tests__/boot_sim.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for boot_sim plugin (session-aware version)
 * Follows CLAUDE.md guidance: dependency injection, no vi-mocks, literal validation.
 */

import { describe, it, expect, beforeEach } from 'vitest';
import { z } from 'zod';
import { createMockExecutor } from '../../../../test-utils/mock-executors.ts';
import { sessionStore } from '../../../../utils/session-store.ts';
import bootSim, { boot_simLogic } from '../boot_sim.ts';

describe('boot_sim tool', () => {
  beforeEach(() => {
    sessionStore.clear();
  });

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

    it('should have concise description', () => {
      expect(bootSim.description).toBe('Boots an iOS simulator.');
    });

    it('should expose empty public schema', () => {
      const schema = z.object(bootSim.schema);
      expect(schema.safeParse({}).success).toBe(true);
      expect(Object.keys(bootSim.schema)).toHaveLength(0);
    });
  });

  describe('Handler Requirements', () => {
    it('should require simulatorId when not provided', async () => {
      const result = await bootSim.handler({});

      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('Missing required session defaults');
      expect(result.content[0].text).toContain('simulatorId is required');
      expect(result.content[0].text).toContain('session-set-defaults');
    });
  });

  describe('Logic Behavior (Literal Results)', () => {
    it('should handle successful boot', async () => {
      const mockExecutor = createMockExecutor({
        success: true,
        output: 'Simulator booted successfully',
      });

      const result = await boot_simLogic({ simulatorId: 'test-uuid-123' }, mockExecutor);

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: `✅ Simulator booted successfully. To make it visible, use: open_sim()

Next steps:
1. Open the Simulator app (makes it visible): open_sim()
2. Install an app: install_app_sim({ simulatorId: "test-uuid-123", appPath: "PATH_TO_YOUR_APP" })
3. Launch an app: launch_app_sim({ simulatorId: "test-uuid-123", bundleId: "YOUR_APP_BUNDLE_ID" })`,
          },
        ],
      });
    });

    it('should handle command failure', async () => {
      const mockExecutor = createMockExecutor({
        success: false,
        error: 'Simulator not found',
      });

      const result = await boot_simLogic({ simulatorId: 'invalid-uuid' }, mockExecutor);

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Boot simulator operation failed: Simulator not found',
          },
        ],
      });
    });

    it('should handle exception with Error object', async () => {
      const mockExecutor = async () => {
        throw new Error('Connection failed');
      };

      const result = await boot_simLogic({ simulatorId: 'test-uuid-123' }, mockExecutor);

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Boot simulator operation failed: Connection failed',
          },
        ],
      });
    });

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

      const result = await boot_simLogic({ simulatorId: 'test-uuid-123' }, mockExecutor);

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Boot simulator operation failed: String error',
          },
        ],
      });
    });

    it('should verify command generation with mock executor', async () => {
      const calls: Array<{
        command: string[];
        description: string;
        allowStderr: boolean;
        timeout?: number;
      }> = [];
      const mockExecutor = async (
        command: string[],
        description: string,
        allowStderr: boolean,
        timeout?: number,
      ) => {
        calls.push({ command, description, allowStderr, timeout });
        return {
          success: true,
          output: 'Simulator booted successfully',
          error: undefined,
          process: { pid: 12345 },
        };
      };

      await boot_simLogic({ simulatorId: 'test-uuid-123' }, mockExecutor);

      expect(calls).toHaveLength(1);
      expect(calls[0]).toEqual({
        command: ['xcrun', 'simctl', 'boot', 'test-uuid-123'],
        description: 'Boot Simulator',
        allowStderr: true,
        timeout: undefined,
      });
    });
  });
});

```

--------------------------------------------------------------------------------
/src/utils/simulator-utils.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Simulator utility functions for name to UUID resolution
 */

import type { CommandExecutor } from './execution/index.ts';
import { ToolResponse } from '../types/common.ts';
import { log } from './logging/index.ts';
import { createErrorResponse } from './responses/index.ts';

/**
 * UUID regex pattern to check if a string looks like a UUID
 */
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

/**
 * Determines the simulator UUID from either a UUID or name.
 *
 * Behavior:
 * - If simulatorUuid provided: return it directly
 * - Else if simulatorName looks like a UUID (regex): treat it as UUID and return it
 * - Else: resolve name → UUID via simctl and return the match (isAvailable === true)
 *
 * @param params Object containing optional simulatorUuid or simulatorName
 * @param executor Command executor for running simctl commands
 * @returns Object with uuid, optional warning, or error
 */
export async function determineSimulatorUuid(
  params: { simulatorUuid?: string; simulatorName?: string },
  executor: CommandExecutor,
): Promise<{ uuid?: string; warning?: string; error?: ToolResponse }> {
  // If UUID is provided directly, use it
  if (params.simulatorUuid) {
    log('info', `Using provided simulator UUID: ${params.simulatorUuid}`);
    return { uuid: params.simulatorUuid };
  }

  // If name is provided, check if it's actually a UUID
  if (params.simulatorName) {
    // Check if the "name" is actually a UUID string
    if (UUID_REGEX.test(params.simulatorName)) {
      log(
        'info',
        `Simulator name '${params.simulatorName}' appears to be a UUID, using it directly`,
      );
      return {
        uuid: params.simulatorName,
        warning: `The simulatorName '${params.simulatorName}' appears to be a UUID. Consider using simulatorUuid parameter instead.`,
      };
    }

    // Resolve name to UUID via simctl
    log('info', `Looking up simulator UUID for name: ${params.simulatorName}`);

    const listResult = await executor(
      ['xcrun', 'simctl', 'list', 'devices', 'available', '-j'],
      'List available simulators',
    );

    if (!listResult.success) {
      return {
        error: createErrorResponse(
          'Failed to list simulators',
          listResult.error ?? 'Unknown error',
        ),
      };
    }

    try {
      interface SimulatorDevice {
        udid: string;
        name: string;
        isAvailable: boolean;
      }

      interface DevicesData {
        devices: Record<string, SimulatorDevice[]>;
      }

      const devicesData = JSON.parse(listResult.output ?? '{}') as DevicesData;

      // Search through all runtime sections for the named device
      for (const runtime of Object.keys(devicesData.devices)) {
        const devices = devicesData.devices[runtime];
        if (!Array.isArray(devices)) continue;

        // Look for exact name match with isAvailable === true
        const device = devices.find(
          (d) => d.name === params.simulatorName && d.isAvailable === true,
        );

        if (device) {
          log('info', `Found simulator '${params.simulatorName}' with UUID: ${device.udid}`);
          return { uuid: device.udid };
        }
      }

      // If no available device found, check if device exists but is unavailable
      for (const runtime of Object.keys(devicesData.devices)) {
        const devices = devicesData.devices[runtime];
        if (!Array.isArray(devices)) continue;

        const unavailableDevice = devices.find(
          (d) => d.name === params.simulatorName && d.isAvailable === false,
        );

        if (unavailableDevice) {
          return {
            error: createErrorResponse(
              `Simulator '${params.simulatorName}' exists but is not available`,
              'The simulator may need to be downloaded or is incompatible with the current Xcode version',
            ),
          };
        }
      }

      // Device not found at all
      return {
        error: createErrorResponse(
          `Simulator '${params.simulatorName}' not found`,
          'Please check the simulator name or use "xcrun simctl list devices" to see available simulators',
        ),
      };
    } catch (parseError) {
      return {
        error: createErrorResponse(
          'Failed to parse simulator list',
          parseError instanceof Error ? parseError.message : String(parseError),
        ),
      };
    }
  }

  // Neither UUID nor name provided
  return {
    error: createErrorResponse(
      'No simulator identifier provided',
      'Either simulatorUuid or simulatorName is required',
    ),
  };
}

```

--------------------------------------------------------------------------------
/src/mcp/tools/simulator-management/erase_sims.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { ToolResponse, type ToolResponseContent } from '../../../types/common.ts';
import { log } from '../../../utils/logging/index.ts';
import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/execution/index.ts';
import { createTypedTool } from '../../../utils/typed-tool-factory.ts';

const eraseSimsBaseSchema = z.object({
  simulatorUdid: z.string().uuid().optional().describe('UDID of the simulator to erase.'),
  all: z.boolean().optional().describe('When true, erases all simulators.'),
  shutdownFirst: z
    .boolean()
    .optional()
    .describe('If true, shuts down the target (UDID or all) before erasing.'),
});

const eraseSimsSchema = eraseSimsBaseSchema.refine(
  (v) => {
    const selectors = (v.simulatorUdid ? 1 : 0) + (v.all === true ? 1 : 0);
    return selectors === 1;
  },
  { message: 'Provide exactly one of: simulatorUdid or all=true.' },
);

type EraseSimsParams = z.infer<typeof eraseSimsSchema>;

export async function erase_simsLogic(
  params: EraseSimsParams,
  executor: CommandExecutor,
): Promise<ToolResponse> {
  try {
    if (params.simulatorUdid) {
      const udid = params.simulatorUdid;
      log(
        'info',
        `Erasing simulator ${udid}${params.shutdownFirst ? ' (shutdownFirst=true)' : ''}`,
      );

      if (params.shutdownFirst) {
        try {
          await executor(
            ['xcrun', 'simctl', 'shutdown', udid],
            'Shutdown Simulator',
            true,
            undefined,
          );
        } catch {
          // ignore shutdown errors; proceed to erase attempt
        }
      }

      const result = await executor(
        ['xcrun', 'simctl', 'erase', udid],
        'Erase Simulator',
        true,
        undefined,
      );
      if (result.success) {
        return { content: [{ type: 'text', text: `Successfully erased simulator ${udid}` }] };
      }

      // Add tool hint if simulator is booted and shutdownFirst was not requested
      const errText = result.error ?? 'Unknown error';
      if (/Unable to erase contents and settings.*Booted/i.test(errText) && !params.shutdownFirst) {
        return {
          content: [
            { type: 'text', text: `Failed to erase simulator: ${errText}` },
            {
              type: 'text',
              text: `Tool hint: The simulator appears to be Booted. Re-run erase_sims with { simulatorUdid: '${udid}', shutdownFirst: true } to shut it down before erasing.`,
            },
          ],
        };
      }

      return {
        content: [{ type: 'text', text: `Failed to erase simulator: ${errText}` }],
      };
    }

    if (params.all === true) {
      log('info', `Erasing ALL simulators${params.shutdownFirst ? ' (shutdownFirst=true)' : ''}`);
      if (params.shutdownFirst) {
        try {
          await executor(
            ['xcrun', 'simctl', 'shutdown', 'all'],
            'Shutdown All Simulators',
            true,
            undefined,
          );
        } catch {
          // ignore and continue to erase
        }
      }

      const result = await executor(
        ['xcrun', 'simctl', 'erase', 'all'],
        'Erase All Simulators',
        true,
        undefined,
      );
      if (!result.success) {
        const errText = result.error ?? 'Unknown error';
        const content: ToolResponseContent[] = [
          { type: 'text', text: `Failed to erase all simulators: ${errText}` },
        ];
        if (
          /Unable to erase contents and settings.*Booted/i.test(errText) &&
          !params.shutdownFirst
        ) {
          content.push({
            type: 'text',
            text: 'Tool hint: One or more simulators appear to be Booted. Re-run erase_sims with { all: true, shutdownFirst: true } to shut them down before erasing.',
          });
        }
        return { content };
      }
      return { content: [{ type: 'text', text: 'Successfully erased all simulators' }] };
    }

    return {
      content: [{ type: 'text', text: 'Invalid parameters: provide simulatorUdid or all=true.' }],
    };
  } catch (error: unknown) {
    const message = error instanceof Error ? error.message : String(error);
    log('error', `Error erasing simulators: ${message}`);
    return { content: [{ type: 'text', text: `Failed to erase simulators: ${message}` }] };
  }
}

export default {
  name: 'erase_sims',
  description:
    'Erases simulator content and settings. Provide exactly one of: simulatorUdid or all=true. Optional: shutdownFirst to shut down before erasing.',
  schema: eraseSimsBaseSchema.shape,
  handler: createTypedTool(eraseSimsSchema, erase_simsLogic, getDefaultCommandExecutor),
};

```

--------------------------------------------------------------------------------
/src/mcp/tools/project-discovery/show_build_settings.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Project Discovery Plugin: Show Build Settings (Unified)
 *
 * Shows build settings from either a project or workspace using xcodebuild.
 * Accepts mutually exclusive `projectPath` or `workspacePath`.
 */

import { z } from 'zod';
import { log } from '../../../utils/logging/index.ts';
import type { CommandExecutor } from '../../../utils/execution/index.ts';
import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts';
import { createTextResponse } from '../../../utils/responses/index.ts';
import { ToolResponse } from '../../../types/common.ts';
import { createSessionAwareTool } from '../../../utils/typed-tool-factory.ts';
import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts';

// Unified schema: XOR between projectPath and workspacePath
const baseSchemaObject = z.object({
  projectPath: z.string().optional().describe('Path to the .xcodeproj file'),
  workspacePath: z.string().optional().describe('Path to the .xcworkspace file'),
  scheme: z.string().describe('Scheme name to show build settings for (Required)'),
});

const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject);

const showBuildSettingsSchema = baseSchema
  .refine((val) => val.projectPath !== undefined || val.workspacePath !== undefined, {
    message: 'Either projectPath or workspacePath is required.',
  })
  .refine((val) => !(val.projectPath !== undefined && val.workspacePath !== undefined), {
    message: 'projectPath and workspacePath are mutually exclusive. Provide only one.',
  });

export type ShowBuildSettingsParams = z.infer<typeof showBuildSettingsSchema>;

/**
 * Business logic for showing build settings from a project or workspace.
 * Exported for direct testing and reuse.
 */
export async function showBuildSettingsLogic(
  params: ShowBuildSettingsParams,
  executor: CommandExecutor,
): Promise<ToolResponse> {
  log('info', `Showing build settings for scheme ${params.scheme}`);

  try {
    // Create the command array for xcodebuild
    const command = ['xcodebuild', '-showBuildSettings']; // -showBuildSettings as an option, not an action

    const hasProjectPath = typeof params.projectPath === 'string';
    const path = hasProjectPath ? params.projectPath : params.workspacePath;

    if (hasProjectPath) {
      command.push('-project', params.projectPath!);
    } else {
      command.push('-workspace', params.workspacePath!);
    }

    // Add the scheme
    command.push('-scheme', params.scheme);

    // Execute the command directly
    const result = await executor(command, 'Show Build Settings', true);

    if (!result.success) {
      return createTextResponse(`Failed to show build settings: ${result.error}`, true);
    }

    // Create response based on which type was used (similar to workspace version with next steps)
    const content: Array<{ type: 'text'; text: string }> = [
      {
        type: 'text',
        text: hasProjectPath
          ? `✅ Build settings for scheme ${params.scheme}:`
          : '✅ Build settings retrieved successfully',
      },
      {
        type: 'text',
        text: result.output || 'Build settings retrieved successfully.',
      },
    ];

    // Add next steps for workspace (similar to original workspace implementation)
    if (!hasProjectPath && path) {
      content.push({
        type: 'text',
        text: `Next Steps:
- Build the workspace: build_macos({ workspacePath: "${path}", scheme: "${params.scheme}" })
- For iOS: build_sim({ workspacePath: "${path}", scheme: "${params.scheme}", simulatorName: "iPhone 16" })
- List schemes: list_schemes({ workspacePath: "${path}" })`,
      });
    }

    return {
      content,
      isError: false,
    };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    log('error', `Error showing build settings: ${errorMessage}`);
    return createTextResponse(`Error showing build settings: ${errorMessage}`, true);
  }
}

const publicSchemaObject = baseSchemaObject.omit({
  projectPath: true,
  workspacePath: true,
  scheme: true,
} as const);

export default {
  name: 'show_build_settings',
  description: 'Shows xcodebuild build settings.',
  schema: publicSchemaObject.shape,
  handler: createSessionAwareTool<ShowBuildSettingsParams>({
    internalSchema: showBuildSettingsSchema as unknown as z.ZodType<ShowBuildSettingsParams>,
    logicFunction: showBuildSettingsLogic,
    getExecutor: getDefaultCommandExecutor,
    requirements: [
      { allOf: ['scheme'], message: 'scheme is required' },
      { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' },
    ],
    exclusivePairs: [['projectPath', 'workspacePath']],
  }),
};

```

--------------------------------------------------------------------------------
/src/mcp/tools/project-discovery/list_schemes.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Project Discovery Plugin: List Schemes (Unified)
 *
 * Lists available schemes for either a project or workspace using xcodebuild.
 * Accepts mutually exclusive `projectPath` or `workspacePath`.
 */

import { z } from 'zod';
import { log } from '../../../utils/logging/index.ts';
import type { CommandExecutor } from '../../../utils/execution/index.ts';
import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts';
import { createTextResponse } from '../../../utils/responses/index.ts';
import { ToolResponse } from '../../../types/common.ts';
import { createSessionAwareTool } from '../../../utils/typed-tool-factory.ts';
import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts';

// Unified schema: XOR between projectPath and workspacePath
const baseSchemaObject = z.object({
  projectPath: z.string().optional().describe('Path to the .xcodeproj file'),
  workspacePath: z.string().optional().describe('Path to the .xcworkspace file'),
});

const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject);

const listSchemesSchema = baseSchema
  .refine((val) => val.projectPath !== undefined || val.workspacePath !== undefined, {
    message: 'Either projectPath or workspacePath is required.',
  })
  .refine((val) => !(val.projectPath !== undefined && val.workspacePath !== undefined), {
    message: 'projectPath and workspacePath are mutually exclusive. Provide only one.',
  });

export type ListSchemesParams = z.infer<typeof listSchemesSchema>;

/**
 * Business logic for listing schemes in a project or workspace.
 * Exported for direct testing and reuse.
 */
export async function listSchemesLogic(
  params: ListSchemesParams,
  executor: CommandExecutor,
): Promise<ToolResponse> {
  log('info', 'Listing schemes');

  try {
    // For listing schemes, we can't use executeXcodeBuild directly since it's not a standard action
    // We need to create a custom command with -list flag
    const command = ['xcodebuild', '-list'];

    const hasProjectPath = typeof params.projectPath === 'string';
    const projectOrWorkspace = hasProjectPath ? 'project' : 'workspace';
    const path = hasProjectPath ? params.projectPath : params.workspacePath;

    if (hasProjectPath) {
      command.push('-project', params.projectPath!);
    } else {
      command.push('-workspace', params.workspacePath!);
    }

    const result = await executor(command, 'List Schemes', true);

    if (!result.success) {
      return createTextResponse(`Failed to list schemes: ${result.error}`, true);
    }

    // Extract schemes from the output
    const schemesMatch = result.output.match(/Schemes:([\s\S]*?)(?=\n\n|$)/);

    if (!schemesMatch) {
      return createTextResponse('No schemes found in the output', true);
    }

    const schemeLines = schemesMatch[1].trim().split('\n');
    const schemes = schemeLines.map((line) => line.trim()).filter((line) => line);

    // Prepare next steps with the first scheme if available
    let nextStepsText = '';
    if (schemes.length > 0) {
      const firstScheme = schemes[0];

      // Note: After Phase 2, these will be unified tool names too
      nextStepsText = `Next Steps:
1. Build the app: build_macos({ ${projectOrWorkspace}Path: "${path}", scheme: "${firstScheme}" })
   or for iOS: build_sim({ ${projectOrWorkspace}Path: "${path}", scheme: "${firstScheme}", simulatorName: "iPhone 16" })
2. Show build settings: show_build_settings({ ${projectOrWorkspace}Path: "${path}", scheme: "${firstScheme}" })`;
    }

    return {
      content: [
        {
          type: 'text',
          text: `✅ Available schemes:`,
        },
        {
          type: 'text',
          text: schemes.join('\n'),
        },
        {
          type: 'text',
          text: nextStepsText,
        },
      ],
      isError: false,
    };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    log('error', `Error listing schemes: ${errorMessage}`);
    return createTextResponse(`Error listing schemes: ${errorMessage}`, true);
  }
}

const publicSchemaObject = baseSchemaObject.omit({
  projectPath: true,
  workspacePath: true,
} as const);

export default {
  name: 'list_schemes',
  description: 'Lists schemes for a project or workspace.',
  schema: publicSchemaObject.shape,
  handler: createSessionAwareTool<ListSchemesParams>({
    internalSchema: listSchemesSchema as unknown as z.ZodType<ListSchemesParams>,
    logicFunction: listSchemesLogic,
    getExecutor: getDefaultCommandExecutor,
    requirements: [
      { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' },
    ],
    exclusivePairs: [['projectPath', 'workspacePath']],
  }),
};

```

--------------------------------------------------------------------------------
/src/mcp/tools/simulator/__tests__/open_sim.test.ts:
--------------------------------------------------------------------------------

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

import { describe, it, expect } from 'vitest';
import { z } from 'zod';
import { createMockExecutor, type CommandExecutor } from '../../../../test-utils/mock-executors.ts';
import openSim, { open_simLogic } from '../open_sim.ts';

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

    it('should have correct description field', () => {
      expect(openSim.description).toBe('Opens the iOS Simulator app.');
    });

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

    it('should have correct schema validation', () => {
      const schema = z.object(openSim.schema);

      // Schema is empty, so any object should pass
      expect(schema.safeParse({}).success).toBe(true);

      expect(
        schema.safeParse({
          anyProperty: 'value',
        }).success,
      ).toBe(true);

      // Empty schema should accept anything
      expect(
        schema.safeParse({
          enabled: true,
        }).success,
      ).toBe(true);
    });
  });

  describe('Handler Behavior (Complete Literal Returns)', () => {
    it('should return exact successful open simulator response', async () => {
      const mockExecutor = createMockExecutor({
        success: true,
        output: '',
      });

      const result = await open_simLogic({}, mockExecutor);

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Simulator app opened successfully',
          },
          {
            type: 'text',
            text: `Next Steps:
1. Boot a simulator if needed: boot_sim({ simulatorUuid: 'UUID_FROM_LIST_SIMULATORS' })
2. Launch your app and interact with it
3. Log capture options:
   - Option 1: Capture structured logs only (app continues running):
     start_sim_log_cap({ simulatorUuid: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID' })
   - Option 2: Capture both console and structured logs (app will restart):
     start_sim_log_cap({ simulatorUuid: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID', captureConsole: true })
   - Option 3: Launch app with logs in one step:
     launch_app_logs_sim({ simulatorUuid: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID' })`,
          },
        ],
      });
    });

    it('should return exact command failure response', async () => {
      const mockExecutor = createMockExecutor({
        success: false,
        error: 'Command failed',
      });

      const result = await open_simLogic({}, mockExecutor);

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Open simulator operation failed: Command failed',
          },
        ],
      });
    });

    it('should return exact exception handling response', async () => {
      const mockExecutor: CommandExecutor = async () => {
        throw new Error('Test error');
      };

      const result = await open_simLogic({}, mockExecutor);

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Open simulator operation failed: Test error',
          },
        ],
      });
    });

    it('should return exact string error handling response', async () => {
      const mockExecutor: CommandExecutor = async () => {
        throw 'String error';
      };

      const result = await open_simLogic({}, mockExecutor);

      expect(result).toEqual({
        content: [
          {
            type: 'text',
            text: 'Open simulator operation failed: String error',
          },
        ],
      });
    });

    it('should verify command generation with mock executor', async () => {
      const calls: Array<{
        command: string[];
        description: string;
        hideOutput: boolean;
        workingDirectory: string | undefined;
      }> = [];

      const mockExecutor: CommandExecutor = async (
        command,
        description,
        hideOutput,
        workingDirectory,
      ) => {
        calls.push({ command, description, hideOutput, workingDirectory });
        return {
          success: true,
          output: '',
          error: undefined,
          process: { pid: 12345 },
        };
      };

      await open_simLogic({}, mockExecutor);

      expect(calls).toHaveLength(1);
      expect(calls[0]).toEqual({
        command: ['open', '-a', 'Simulator'],
        description: 'Open Simulator',
        hideOutput: true,
        workingDirectory: undefined,
      });
    });
  });
});

```

--------------------------------------------------------------------------------
/docs/session-aware-migration-todo.md:
--------------------------------------------------------------------------------

```markdown
# Session-Aware Migration TODO

_Audit date: October 6, 2025_

Reference: `docs/session_management_plan.md`

## Utilities
- [x] `src/mcp/tools/utilities/clean.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `configuration`.

## Project Discovery
- [x] `src/mcp/tools/project-discovery/list_schemes.ts` — session defaults: `projectPath`, `workspacePath`.
- [x] `src/mcp/tools/project-discovery/show_build_settings.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`.

## Device Workflows
- [x] `src/mcp/tools/device/build_device.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `configuration`.
- [x] `src/mcp/tools/device/test_device.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `deviceId`, `configuration`.
- [x] `src/mcp/tools/device/get_device_app_path.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `configuration`.
- [x] `src/mcp/tools/device/install_app_device.ts` — session defaults: `deviceId`.
- [x] `src/mcp/tools/device/launch_app_device.ts` — session defaults: `deviceId`.
- [x] `src/mcp/tools/device/stop_app_device.ts` — session defaults: `deviceId`.

## Device Logging
- [x] `src/mcp/tools/logging/start_device_log_cap.ts` — session defaults: `deviceId`.

## macOS Workflows
- [x] `src/mcp/tools/macos/build_macos.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `configuration`, `arch`.
- [x] `src/mcp/tools/macos/build_run_macos.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `configuration`, `arch`.
- [x] `src/mcp/tools/macos/test_macos.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `configuration`.
- [x] `src/mcp/tools/macos/get_mac_app_path.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `configuration`, `arch`.

## Simulator Build/Test/Path
- [x] `src/mcp/tools/simulator/test_sim.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `simulatorId`, `simulatorName`, `configuration`, `useLatestOS`.
- [x] `src/mcp/tools/simulator/get_sim_app_path.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `simulatorId`, `simulatorName`, `configuration`, `useLatestOS`, `arch`.

## Simulator Runtime Actions
- [x] `src/mcp/tools/simulator/boot_sim.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [x] `src/mcp/tools/simulator/install_app_sim.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [x] `src/mcp/tools/simulator/launch_app_sim.ts` — session defaults: `simulatorId`, `simulatorName` (hydrate `simulatorUuid`).
- [x] `src/mcp/tools/simulator/launch_app_logs_sim.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [x] `src/mcp/tools/simulator/stop_app_sim.ts` — session defaults: `simulatorId`, `simulatorName` (hydrate `simulatorUuid`).
- [x] `src/mcp/tools/simulator/record_sim_video.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).

## Simulator Management
- [ ] `src/mcp/tools/simulator-management/erase_sims.ts` — session defaults: `simulatorId` (covers `simulatorUdid`).
- [ ] `src/mcp/tools/simulator-management/set_sim_location.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [ ] `src/mcp/tools/simulator-management/reset_sim_location.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [ ] `src/mcp/tools/simulator-management/set_sim_appearance.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [ ] `src/mcp/tools/simulator-management/sim_statusbar.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).

## Simulator Logging
- [ ] `src/mcp/tools/logging/start_sim_log_cap.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).

## AXe UI Testing Tools
- [ ] `src/mcp/tools/ui-testing/button.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [ ] `src/mcp/tools/ui-testing/describe_ui.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [ ] `src/mcp/tools/ui-testing/gesture.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [ ] `src/mcp/tools/ui-testing/key_press.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [ ] `src/mcp/tools/ui-testing/key_sequence.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [ ] `src/mcp/tools/ui-testing/long_press.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [ ] `src/mcp/tools/ui-testing/screenshot.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [ ] `src/mcp/tools/ui-testing/swipe.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [ ] `src/mcp/tools/ui-testing/tap.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [ ] `src/mcp/tools/ui-testing/touch.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
- [ ] `src/mcp/tools/ui-testing/type_text.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).

```

--------------------------------------------------------------------------------
/src/mcp/tools/simulator-management/__tests__/erase_sims.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect } from 'vitest';
import { z } from 'zod';
import eraseSims, { erase_simsLogic } from '../erase_sims.ts';
import { createMockExecutor } from '../../../../test-utils/mock-executors.ts';

describe('erase_sims tool (UDID or ALL only)', () => {
  describe('Export Field Validation (Literal)', () => {
    it('should have correct name', () => {
      expect(eraseSims.name).toBe('erase_sims');
    });

    it('should have correct description', () => {
      expect(eraseSims.description).toContain('Provide exactly one of: simulatorUdid or all=true');
      expect(eraseSims.description).toContain('shutdownFirst');
    });

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

    it('should validate schema fields (shape only)', () => {
      const schema = z.object(eraseSims.schema);
      // Valid
      expect(
        schema.safeParse({ simulatorUdid: '123e4567-e89b-12d3-a456-426614174000' }).success,
      ).toBe(true);
      expect(schema.safeParse({ all: true }).success).toBe(true);
      // Shape-level schema does not enforce selection rules; handler validation covers that.
    });
  });

  describe('Single mode', () => {
    it('erases a simulator successfully', async () => {
      const mock = createMockExecutor({ success: true, output: 'OK' });
      const res = await erase_simsLogic({ simulatorUdid: 'UD1' }, mock);
      expect(res).toEqual({
        content: [{ type: 'text', text: 'Successfully erased simulator UD1' }],
      });
    });

    it('returns failure when erase fails', async () => {
      const mock = createMockExecutor({ success: false, error: 'Booted device' });
      const res = await erase_simsLogic({ simulatorUdid: 'UD1' }, mock);
      expect(res).toEqual({
        content: [{ type: 'text', text: 'Failed to erase simulator: Booted device' }],
      });
    });

    it('adds tool hint when booted error occurs without shutdownFirst', async () => {
      const bootedError =
        'An error was encountered processing the command (domain=com.apple.CoreSimulator.SimError, code=405):\nUnable to erase contents and settings in current state: Booted\n';
      const mock = createMockExecutor({ success: false, error: bootedError });
      const res = await erase_simsLogic({ simulatorUdid: 'UD1' }, mock);
      expect((res.content?.[1] as any).text).toContain('Tool hint');
      expect((res.content?.[1] as any).text).toContain('shutdownFirst: true');
    });

    it('performs shutdown first when shutdownFirst=true', async () => {
      const calls: any[] = [];
      const exec = async (cmd: string[]) => {
        calls.push(cmd);
        return { success: true, output: 'OK', error: '', process: { pid: 1 } as any };
      };
      const res = await erase_simsLogic({ simulatorUdid: 'UD1', shutdownFirst: true }, exec as any);
      expect(calls).toEqual([
        ['xcrun', 'simctl', 'shutdown', 'UD1'],
        ['xcrun', 'simctl', 'erase', 'UD1'],
      ]);
      expect(res).toEqual({
        content: [{ type: 'text', text: 'Successfully erased simulator UD1' }],
      });
    });
  });

  describe('All mode', () => {
    it('erases all simulators successfully', async () => {
      const exec = createMockExecutor({ success: true, output: 'OK' });
      const res = await erase_simsLogic({ all: true }, exec);
      expect(res).toEqual({
        content: [{ type: 'text', text: 'Successfully erased all simulators' }],
      });
    });

    it('returns failure when erase all fails', async () => {
      const exec = createMockExecutor({ success: false, error: 'Denied' });
      const res = await erase_simsLogic({ all: true }, exec);
      expect(res).toEqual({
        content: [{ type: 'text', text: 'Failed to erase all simulators: Denied' }],
      });
    });

    it('performs shutdown all when shutdownFirst=true', async () => {
      const calls: any[] = [];
      const exec = async (cmd: string[]) => {
        calls.push(cmd);
        return { success: true, output: 'OK', error: '', process: { pid: 1 } as any };
      };
      const res = await erase_simsLogic({ all: true, shutdownFirst: true }, exec as any);
      expect(calls).toEqual([
        ['xcrun', 'simctl', 'shutdown', 'all'],
        ['xcrun', 'simctl', 'erase', 'all'],
      ]);
      expect(res).toEqual({
        content: [{ type: 'text', text: 'Successfully erased all simulators' }],
      });
    });

    it('adds tool hint on booted error without shutdownFirst (all mode)', async () => {
      const bootedError = 'Unable to erase contents and settings in current state: Booted';
      const exec = createMockExecutor({ success: false, error: bootedError });
      const res = await erase_simsLogic({ all: true }, exec);
      expect((res.content?.[1] as any).text).toContain('Tool hint');
      expect((res.content?.[1] as any).text).toContain('shutdownFirst: true');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/mcp/tools/ui-testing/type_text.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * UI Testing Plugin: Type Text
 *
 * Types text into the iOS Simulator using keyboard input.
 * Supports standard US keyboard characters.
 */

import { z } from 'zod';
import { ToolResponse } from '../../../types/common.ts';
import { log } from '../../../utils/logging/index.ts';
import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts';
import { DependencyError, AxeError, SystemError } from '../../../utils/errors.ts';
import type { CommandExecutor } from '../../../utils/execution/index.ts';
import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts';
import {
  createAxeNotAvailableResponse,
  getAxePath,
  getBundledAxeEnvironment,
} from '../../../utils/axe-helpers.ts';
import { createTypedTool } from '../../../utils/typed-tool-factory.ts';

const LOG_PREFIX = '[AXe]';

// Define schema as ZodObject
const typeTextSchema = z.object({
  simulatorUuid: z.string().uuid('Invalid Simulator UUID format'),
  text: z.string().min(1, 'Text cannot be empty'),
});

// Use z.infer for type safety
type TypeTextParams = z.infer<typeof typeTextSchema>;

interface AxeHelpers {
  getAxePath: () => string | null;
  getBundledAxeEnvironment: () => Record<string, string>;
}

export async function type_textLogic(
  params: TypeTextParams,
  executor: CommandExecutor,
  axeHelpers?: AxeHelpers,
): Promise<ToolResponse> {
  const toolName = 'type_text';

  // Params are already validated by the factory, use directly
  const { simulatorUuid, text } = params;
  const commandArgs = ['type', text];

  log(
    'info',
    `${LOG_PREFIX}/${toolName}: Starting type "${text.substring(0, 20)}..." on ${simulatorUuid}`,
  );

  try {
    await executeAxeCommand(commandArgs, simulatorUuid, 'type', executor, axeHelpers);
    log('info', `${LOG_PREFIX}/${toolName}: Success for ${simulatorUuid}`);
    return createTextResponse('Text typing simulated successfully.');
  } catch (error) {
    log(
      'error',
      `${LOG_PREFIX}/${toolName}: Failed - ${error instanceof Error ? error.message : String(error)}`,
    );
    if (error instanceof DependencyError) {
      return createAxeNotAvailableResponse();
    } else if (error instanceof AxeError) {
      return createErrorResponse(
        `Failed to simulate text typing: ${error.message}`,
        error.axeOutput,
      );
    } else if (error instanceof SystemError) {
      return createErrorResponse(
        `System error executing axe: ${error.message}`,
        error.originalError?.stack,
      );
    }
    return createErrorResponse(
      `An unexpected error occurred: ${error instanceof Error ? error.message : String(error)}`,
    );
  }
}

export default {
  name: 'type_text',
  description:
    'Type text (supports US keyboard characters). Use describe_ui to find text field, tap to focus, then type.',
  schema: typeTextSchema.shape, // MCP SDK compatibility
  handler: createTypedTool(typeTextSchema, type_textLogic, getDefaultCommandExecutor), // Safe factory
};

// Helper function for executing axe commands (inlined from src/tools/axe/index.ts)
async function executeAxeCommand(
  commandArgs: string[],
  simulatorUuid: string,
  commandName: string,
  executor: CommandExecutor = getDefaultCommandExecutor(),
  axeHelpers?: AxeHelpers,
): Promise<void> {
  // Use provided helpers or defaults
  const helpers = axeHelpers ?? { getAxePath, getBundledAxeEnvironment };

  // Get the appropriate axe binary path
  const axeBinary = helpers.getAxePath();
  if (!axeBinary) {
    throw new DependencyError('AXe binary not found');
  }

  // Add --udid parameter to all commands
  const fullArgs = [...commandArgs, '--udid', simulatorUuid];

  // Construct the full command array with the axe binary as the first element
  const fullCommand = [axeBinary, ...fullArgs];

  try {
    // Determine environment variables for bundled AXe
    const axeEnv = axeBinary !== 'axe' ? helpers.getBundledAxeEnvironment() : undefined;

    const result = await executor(fullCommand, `${LOG_PREFIX}: ${commandName}`, false, axeEnv);

    if (!result.success) {
      throw new AxeError(
        `axe command '${commandName}' failed.`,
        commandName,
        result.error ?? result.output,
        simulatorUuid,
      );
    }

    // Check for stderr output in successful commands
    if (result.error) {
      log(
        'warn',
        `${LOG_PREFIX}: Command '${commandName}' produced stderr output but exited successfully. Output: ${result.error}`,
      );
    }

    // Function now returns void - the calling code creates its own response
  } catch (error) {
    if (error instanceof Error) {
      if (error instanceof AxeError) {
        throw error;
      }

      // Otherwise wrap it in a SystemError
      throw new SystemError(`Failed to execute axe command: ${error.message}`, error);
    }

    // For any other type of error
    throw new SystemError(`Failed to execute axe command: ${String(error)}`);
  }
}

```

--------------------------------------------------------------------------------
/src/utils/__tests__/typed-tool-factory.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for the createTypedTool factory
 */

import { describe, it, expect } from 'vitest';
import { z } from 'zod';
import { createTypedTool } from '../typed-tool-factory.ts';
import { createMockExecutor } from '../../test-utils/mock-executors.ts';
import { ToolResponse } from '../../types/common.ts';

// Test schema and types
const testSchema = z.object({
  requiredParam: z.string().describe('A required string parameter'),
  optionalParam: z.number().optional().describe('An optional number parameter'),
});

type TestParams = z.infer<typeof testSchema>;

// Mock logic function for testing
async function testLogic(params: TestParams): Promise<ToolResponse> {
  return {
    content: [{ type: 'text', text: `Logic executed with: ${params.requiredParam}` }],
    isError: false,
  };
}

describe('createTypedTool', () => {
  describe('Type Safety and Validation', () => {
    it('should accept valid parameters and call logic function', async () => {
      const mockExecutor = createMockExecutor({ success: true, output: 'test' });
      const handler = createTypedTool(testSchema, testLogic, () => mockExecutor);

      const result = await handler({
        requiredParam: 'valid-value',
        optionalParam: 42,
      });

      expect(result.isError).toBe(false);
      expect(result.content[0].text).toContain('Logic executed with: valid-value');
    });

    it('should reject parameters with missing required fields', async () => {
      const mockExecutor = createMockExecutor({ success: true, output: 'test' });
      const handler = createTypedTool(testSchema, testLogic, () => mockExecutor);

      const result = await handler({
        // Missing requiredParam
        optionalParam: 42,
      });

      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('Parameter validation failed');
      expect(result.content[0].text).toContain('requiredParam');
    });

    it('should reject parameters with wrong types', async () => {
      const mockExecutor = createMockExecutor({ success: true, output: 'test' });
      const handler = createTypedTool(testSchema, testLogic, () => mockExecutor);

      const result = await handler({
        requiredParam: 123, // Should be string, not number
        optionalParam: 42,
      });

      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('Parameter validation failed');
      expect(result.content[0].text).toContain('requiredParam');
    });

    it('should accept parameters with only required fields', async () => {
      const mockExecutor = createMockExecutor({ success: true, output: 'test' });
      const handler = createTypedTool(testSchema, testLogic, () => mockExecutor);

      const result = await handler({
        requiredParam: 'valid-value',
        // optionalParam omitted
      });

      expect(result.isError).toBe(false);
      expect(result.content[0].text).toContain('Logic executed with: valid-value');
    });

    it('should provide detailed validation error messages', async () => {
      const mockExecutor = createMockExecutor({ success: true, output: 'test' });
      const handler = createTypedTool(testSchema, testLogic, () => mockExecutor);

      const result = await handler({
        requiredParam: 123, // Wrong type
        optionalParam: 'should-be-number', // Wrong type
      });

      expect(result.isError).toBe(true);
      const errorText = result.content[0].text;
      expect(errorText).toContain('Parameter validation failed');
      expect(errorText).toContain('requiredParam');
      expect(errorText).toContain('optionalParam');
    });
  });

  describe('Error Handling', () => {
    it('should re-throw non-Zod errors from logic function', async () => {
      const mockExecutor = createMockExecutor({ success: true, output: 'test' });

      // Logic function that throws a non-Zod error
      async function errorLogic(): Promise<ToolResponse> {
        throw new Error('Unexpected error');
      }

      const handler = createTypedTool(testSchema, errorLogic, () => mockExecutor);

      await expect(handler({ requiredParam: 'valid' })).rejects.toThrow('Unexpected error');
    });
  });

  describe('Executor Integration', () => {
    it('should pass the provided executor to logic function', async () => {
      const mockExecutor = createMockExecutor({ success: true, output: 'test' });

      async function executorTestLogic(params: TestParams, executor: any): Promise<ToolResponse> {
        // Verify executor is passed correctly
        expect(executor).toBe(mockExecutor);
        return {
          content: [{ type: 'text', text: 'Executor passed correctly' }],
          isError: false,
        };
      }

      const handler = createTypedTool(testSchema, executorTestLogic, () => mockExecutor);

      const result = await handler({ requiredParam: 'valid' });

      expect(result.isError).toBe(false);
      expect(result.content[0].text).toBe('Executor passed correctly');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/utils/logger.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Logger Utility - Simple logging implementation for the application
 *
 * This utility module provides a lightweight logging system that directs log
 * messages to stderr rather than stdout, ensuring they don't interfere with
 * the MCP protocol communication which uses stdout.
 *
 * Responsibilities:
 * - Formatting log messages with timestamps and level indicators
 * - Directing all logs to stderr to avoid MCP protocol interference
 * - Supporting different log levels (info, warning, error, debug)
 * - Providing a simple, consistent logging interface throughout the application
 * - Sending error-level logs to Sentry for monitoring and alerting
 *
 * While intentionally minimal, this logger provides the essential functionality
 * needed for operational monitoring and debugging throughout the application.
 * It's used by virtually all other modules for status reporting and error logging.
 */

import { createRequire } from 'node:module';
// Note: Removed "import * as Sentry from '@sentry/node'" to prevent native module loading at import time

const SENTRY_ENABLED =
  process.env.SENTRY_DISABLED !== 'true' && process.env.XCODEBUILDMCP_SENTRY_DISABLED !== 'true';

// Log levels in order of severity (lower number = more severe)
const LOG_LEVELS = {
  emergency: 0,
  alert: 1,
  critical: 2,
  error: 3,
  warning: 4,
  notice: 5,
  info: 6,
  debug: 7,
} as const;

export type LogLevel = keyof typeof LOG_LEVELS;

/**
 * Optional context for logging to control Sentry capture
 */
export interface LogContext {
  sentry?: boolean;
}

// Client-requested log level (null means no filtering)
let clientLogLevel: LogLevel | null = null;

function isTestEnv(): boolean {
  return (
    process.env.VITEST === 'true' ||
    process.env.NODE_ENV === 'test' ||
    process.env.XCODEBUILDMCP_SILENCE_LOGS === 'true'
  );
}

type SentryModule = typeof import('@sentry/node');

const require = createRequire(import.meta.url);
let cachedSentry: SentryModule | null = null;

function loadSentrySync(): SentryModule | null {
  if (!SENTRY_ENABLED || isTestEnv()) return null;
  if (cachedSentry) return cachedSentry;
  try {
    cachedSentry = require('@sentry/node') as SentryModule;
    return cachedSentry;
  } catch {
    // If @sentry/node is not installed in some environments, fail silently.
    return null;
  }
}

function withSentry(cb: (s: SentryModule) => void): void {
  const s = loadSentrySync();
  if (!s) return;
  try {
    cb(s);
  } catch {
    // no-op: avoid throwing inside logger
  }
}

if (!SENTRY_ENABLED) {
  if (process.env.SENTRY_DISABLED === 'true') {
    log('info', 'Sentry disabled due to SENTRY_DISABLED environment variable');
  } else if (process.env.XCODEBUILDMCP_SENTRY_DISABLED === 'true') {
    log('info', 'Sentry disabled due to XCODEBUILDMCP_SENTRY_DISABLED environment variable');
  }
}

/**
 * Set the minimum log level for client-requested filtering
 * @param level The minimum log level to output
 */
export function setLogLevel(level: LogLevel): void {
  clientLogLevel = level;
  log('debug', `Log level set to: ${level}`);
}

/**
 * Get the current client-requested log level
 * @returns The current log level or null if no filtering is active
 */
export function getLogLevel(): LogLevel | null {
  return clientLogLevel;
}

/**
 * Check if a log level should be output based on client settings
 * @param level The log level to check
 * @returns true if the message should be logged
 */
function shouldLog(level: string): boolean {
  // Suppress logging during tests to keep test output clean
  if (isTestEnv()) {
    return false;
  }

  // If no client level set, log everything
  if (clientLogLevel === null) {
    return true;
  }

  // Check if the level is valid
  const levelKey = level.toLowerCase() as LogLevel;
  if (!(levelKey in LOG_LEVELS)) {
    return true; // Log unknown levels
  }

  // Only log if the message level is at or above the client's requested level
  return LOG_LEVELS[levelKey] <= LOG_LEVELS[clientLogLevel];
}

/**
 * Log a message with the specified level
 * @param level The log level (emergency, alert, critical, error, warning, notice, info, debug)
 * @param message The message to log
 * @param context Optional context to control Sentry capture and other behavior
 */
export function log(level: string, message: string, context?: LogContext): void {
  // Check if we should log this level
  if (!shouldLog(level)) {
    return;
  }

  const timestamp = new Date().toISOString();
  const logMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}`;

  // Default: error level goes to Sentry
  // But respect explicit override from context
  const captureToSentry = SENTRY_ENABLED && (context?.sentry ?? level === 'error');

  if (captureToSentry) {
    withSentry((s) => s.captureMessage(logMessage));
  }

  // It's important to use console.error here to ensure logs don't interfere with MCP protocol communication
  // see https://modelcontextprotocol.io/docs/tools/debugging#server-side-logging
  console.error(logMessage);
}

```
Page 2/11FirstPrevNextLast