Skip to main content
Glama

build_run_ios_sim_id_ws

Build and run an iOS app from a workspace on a specific simulator using its UUID. Provide workspace path, scheme, and simulator ID to execute the process.

Instructions

Builds and runs an iOS app from a workspace on a simulator specified by UUID. IMPORTANT: Requires workspacePath, scheme, and simulatorId. Example: build_run_ios_sim_id_ws({ workspacePath: '/path/to/workspace', scheme: 'MyScheme', simulatorId: 'SIMULATOR_UUID' })

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
workspacePathYesPath to the .xcworkspace file (Required)
schemeYesThe scheme to use (Required)
simulatorIdYesUUID of the simulator to use (obtained from listSimulators) (Required)
configurationNoBuild configuration (Debug, Release, etc.)
derivedDataPathNoPath where build products and other derived data will go
extraArgsNoAdditional xcodebuild arguments
useLatestOSNoWhether to use the latest OS version for the named simulator
preferXcodebuildNoIf true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails.

Implementation Reference

  • Core handler implementing the tool logic: performs build via _handleIOSSimulatorBuildLogic, then handles simulator boot/install/launch sequence for iOS app from workspace on specified simulator ID.
    async function _handleIOSSimulatorBuildAndRunLogic(params: {
      workspacePath?: string;
      projectPath?: string;
      scheme: string;
      configuration: string;
      simulatorName?: string;
      simulatorId?: string;
      useLatestOS: boolean;
      derivedDataPath?: string;
      extraArgs?: string[];
      preferXcodebuild?: boolean;
    }): Promise<ToolResponse> {
      log('info', `Starting iOS Simulator build and run for scheme ${params.scheme} (internal)`);
    
      try {
        // --- Build Step ---
        const buildResult = await _handleIOSSimulatorBuildLogic(params);
    
        if (buildResult.isError) {
          return buildResult; // Return the build error
        }
    
        // --- Get App Path Step ---
        // Create the command array for xcodebuild with -showBuildSettings option
        const command = ['xcodebuild', '-showBuildSettings'];
    
        // Add the workspace or project
        if (params.workspacePath) {
          command.push('-workspace', params.workspacePath);
        } else if (params.projectPath) {
          command.push('-project', params.projectPath);
        }
    
        // Add the scheme and configuration
        command.push('-scheme', params.scheme);
        command.push('-configuration', params.configuration);
    
        // Handle destination for simulator
        let destinationString = '';
        if (params.simulatorId) {
          destinationString = `platform=iOS Simulator,id=${params.simulatorId}`;
        } else if (params.simulatorName) {
          destinationString = `platform=iOS Simulator,name=${params.simulatorName}${params.useLatestOS ? ',OS=latest' : ''}`;
        } else {
          return createTextResponse(
            'Either simulatorId or simulatorName must be provided for iOS simulator build',
            true,
          );
        }
    
        command.push('-destination', destinationString);
    
        // Add derived data path if provided
        if (params.derivedDataPath) {
          command.push('-derivedDataPath', params.derivedDataPath);
        }
    
        // Add extra args if provided
        if (params.extraArgs && params.extraArgs.length > 0) {
          command.push(...params.extraArgs);
        }
    
        // Execute the command directly
        const result = await executeCommand(command, 'Get App Path');
    
        // If there was an error with the command execution, return it
        if (!result.success) {
          return createTextResponse(
            `Build succeeded, but failed to get app path: ${result.error || 'Unknown error'}`,
            true,
          );
        }
    
        // Parse the output to extract the app path
        const buildSettingsOutput = result.output;
    
        // Extract CODESIGNING_FOLDER_PATH from build settings to get app path
        const appPathMatch = buildSettingsOutput.match(/CODESIGNING_FOLDER_PATH = (.+\.app)/);
        if (!appPathMatch || !appPathMatch[1]) {
          return createTextResponse(
            `Build succeeded, but could not find app path in build settings.`,
            true,
          );
        }
    
        const appBundlePath = appPathMatch[1].trim();
        log('info', `App bundle path for run: ${appBundlePath}`);
    
        // --- Find/Boot Simulator Step ---
        let simulatorUuid = params.simulatorId;
        if (!simulatorUuid && params.simulatorName) {
          try {
            log('info', `Finding simulator UUID for name: ${params.simulatorName}`);
            const simulatorsOutput = execSync('xcrun simctl list devices available --json').toString();
            const simulatorsJson = JSON.parse(simulatorsOutput);
            let foundSimulator = null;
    
            // Find the simulator in the available devices list
            for (const runtime in simulatorsJson.devices) {
              const devices = simulatorsJson.devices[runtime];
              for (const device of devices) {
                if (device.name === params.simulatorName && device.isAvailable) {
                  foundSimulator = device;
                  break;
                }
              }
              if (foundSimulator) break;
            }
    
            if (foundSimulator) {
              simulatorUuid = foundSimulator.udid;
              log('info', `Found simulator for run: ${foundSimulator.name} (${simulatorUuid})`);
            } else {
              return createTextResponse(
                `Build succeeded, but could not find an available simulator named '${params.simulatorName}'. Use list_simulators({}) to check available devices.`,
                true,
              );
            }
          } catch (error) {
            const errorMessage = error instanceof Error ? error.message : String(error);
            return createTextResponse(
              `Build succeeded, but error finding simulator: ${errorMessage}`,
              true,
            );
          }
        }
    
        if (!simulatorUuid) {
          return createTextResponse(
            'Build succeeded, but no simulator specified and failed to find a suitable one.',
            true,
          );
        }
    
        // Ensure simulator is booted
        try {
          log('info', `Checking simulator state for UUID: ${simulatorUuid}`);
          const simulatorStateOutput = execSync('xcrun simctl list devices').toString();
          const simulatorLine = simulatorStateOutput
            .split('\n')
            .find((line) => line.includes(simulatorUuid));
    
          const isBooted = simulatorLine ? simulatorLine.includes('(Booted)') : false;
    
          if (!simulatorLine) {
            return createTextResponse(
              `Build succeeded, but could not find simulator with UUID: ${simulatorUuid}`,
              true,
            );
          }
    
          if (!isBooted) {
            log('info', `Booting simulator ${simulatorUuid}`);
            execSync(`xcrun simctl boot "${simulatorUuid}"`);
            // Wait a moment for the simulator to fully boot
            await new Promise((resolve) => setTimeout(resolve, 2000));
          } else {
            log('info', `Simulator ${simulatorUuid} is already booted`);
          }
        } catch (error) {
          const errorMessage = error instanceof Error ? error.message : String(error);
          log('error', `Error checking/booting simulator: ${errorMessage}`);
          return createTextResponse(
            `Build succeeded, but error checking/booting simulator: ${errorMessage}`,
            true,
          );
        }
    
        // --- Open Simulator UI Step ---
        try {
          log('info', 'Opening Simulator app');
          execSync('open -a Simulator');
          // Give the Simulator app time to open
          await new Promise((resolve) => setTimeout(resolve, 2000));
        } catch (error) {
          const errorMessage = error instanceof Error ? error.message : String(error);
          log('warning', `Warning: Could not open Simulator app: ${errorMessage}`);
          // Don't fail the whole operation for this
        }
    
        // --- Install App Step ---
        try {
          log('info', `Installing app at path: ${appBundlePath} to simulator: ${simulatorUuid}`);
          execSync(`xcrun simctl install "${simulatorUuid}" "${appBundlePath}"`);
          // Wait a moment for installation to complete
          await new Promise((resolve) => setTimeout(resolve, 1000));
        } catch (error) {
          const errorMessage = error instanceof Error ? error.message : String(error);
          log('error', `Error installing app: ${errorMessage}`);
          return createTextResponse(
            `Build succeeded, but error installing app on simulator: ${errorMessage}`,
            true,
          );
        }
    
        // --- Get Bundle ID Step ---
        let bundleId;
        try {
          log('info', `Extracting bundle ID from app: ${appBundlePath}`);
    
          // Try PlistBuddy first (more reliable)
          try {
            bundleId = execSync(
              `/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "${appBundlePath}/Info.plist"`,
            )
              .toString()
              .trim();
          } catch (plistError: unknown) {
            // Fallback to defaults if PlistBuddy fails
            const errorMessage = plistError instanceof Error ? plistError.message : String(plistError);
            log('warning', `PlistBuddy failed, trying defaults: ${errorMessage}`);
            bundleId = execSync(`defaults read "${appBundlePath}/Info" CFBundleIdentifier`)
              .toString()
              .trim();
          }
    
          if (!bundleId) {
            throw new Error('Could not extract bundle ID from Info.plist');
          }
    
          log('info', `Bundle ID for run: ${bundleId}`);
        } catch (error) {
          const errorMessage = error instanceof Error ? error.message : String(error);
          log('error', `Error getting bundle ID: ${errorMessage}`);
          return createTextResponse(
            `Build and install succeeded, but error getting bundle ID: ${errorMessage}`,
            true,
          );
        }
    
        // --- Launch App Step ---
        try {
          log('info', `Launching app with bundle ID: ${bundleId} on simulator: ${simulatorUuid}`);
          execSync(`xcrun simctl launch "${simulatorUuid}" "${bundleId}"`);
        } catch (error) {
          const errorMessage = error instanceof Error ? error.message : String(error);
          log('error', `Error launching app: ${errorMessage}`);
          return createTextResponse(
            `Build and install succeeded, but error launching app on simulator: ${errorMessage}`,
            true,
          );
        }
    
        // --- Success ---
        log('info', '✅ iOS simulator build & run succeeded.');
    
        const target = params.simulatorId
          ? `simulator UUID ${params.simulatorId}`
          : `simulator name '${params.simulatorName}'`;
    
        return {
          content: [
            {
              type: 'text',
              text: `✅ iOS simulator build and run succeeded for scheme ${params.scheme} targeting ${target}.
              
    The app (${bundleId}) is now running in the iOS Simulator. 
    If you don't see the simulator window, it may be hidden behind other windows. The Simulator app should be open.
    
    Next Steps:
    - Option 1: Capture structured logs only (app continues running):
      start_simulator_log_capture({ simulatorUuid: '${simulatorUuid}', bundleId: '${bundleId}' })
    - Option 2: Capture both console and structured logs (app will restart):
      start_simulator_log_capture({ simulatorUuid: '${simulatorUuid}', bundleId: '${bundleId}', captureConsole: true })
    - Option 3: Launch app with logs in one step (for a fresh start):
      launch_app_with_logs_in_simulator({ simulatorUuid: '${simulatorUuid}', bundleId: '${bundleId}' })
    
    When done with any option, use: stop_sim_log_cap({ logSessionId: 'SESSION_ID' })`,
            },
          ],
        };
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        log('error', `Error in iOS Simulator build and run: ${errorMessage}`);
        return createTextResponse(`Error in iOS Simulator build and run: ${errorMessage}`, true);
      }
    }
  • Direct MCP tool registration for 'build_run_ios_sim_id_ws', including schema, description, and handler wrapper that delegates to core logic.
    registerTool<Params>(
      server,
      'build_run_ios_sim_id_ws',
      "Builds and runs an iOS app from a workspace on a simulator specified by UUID. IMPORTANT: Requires workspacePath, scheme, and simulatorId. Example: build_run_ios_sim_id_ws({ workspacePath: '/path/to/workspace', scheme: 'MyScheme', simulatorId: 'SIMULATOR_UUID' })",
      {
        workspacePath: workspacePathSchema,
        scheme: schemeSchema,
        simulatorId: simulatorIdSchema,
        configuration: configurationSchema,
        derivedDataPath: derivedDataPathSchema,
        extraArgs: extraArgsSchema,
        useLatestOS: useLatestOSSchema,
        preferXcodebuild: preferXcodebuildSchema,
      },
      async (params: Params) => {
        // Validate required parameters
        const workspaceValidation = validateRequiredParam('workspacePath', params.workspacePath);
        if (!workspaceValidation.isValid) return workspaceValidation.errorResponse!;
    
        const schemeValidation = validateRequiredParam('scheme', params.scheme);
        if (!schemeValidation.isValid) return schemeValidation.errorResponse!;
    
        const simulatorIdValidation = validateRequiredParam('simulatorId', params.simulatorId);
        if (!simulatorIdValidation.isValid) return simulatorIdValidation.errorResponse!;
    
        // Provide defaults
        return _handleIOSSimulatorBuildAndRunLogic({
          ...params,
          configuration: params.configuration ?? 'Debug',
          useLatestOS: params.useLatestOS ?? true, // May be ignored
          preferXcodebuild: params.preferXcodebuild ?? false,
        });
      },
    );
  • Top-level registration entry that invokes registerIOSSimulatorBuildAndRunByIdWorkspaceTool(server) to register the tool during server initialization.
    {
      register: registerIOSSimulatorBuildAndRunByIdWorkspaceTool,
      groups: [ToolGroup.IOS_SIMULATOR_WORKFLOW, ToolGroup.APP_DEPLOYMENT],
      envVar: 'XCODEBUILDMCP_TOOL_IOS_SIMULATOR_BUILD_AND_RUN_BY_ID_WORKSPACE',
    },
  • Zod schema definitions for tool parameters: workspacePath, scheme, simulatorId, etc., imported and composed in the tool registration.
    export const workspacePathSchema = z.string().describe('Path to the .xcworkspace file (Required)');
    export const projectPathSchema = z.string().describe('Path to the .xcodeproj file (Required)');
    export const schemeSchema = z.string().describe('The scheme to use (Required)');
    export const configurationSchema = z
      .string()
      .optional()
      .describe('Build configuration (Debug, Release, etc.)');
    export const derivedDataPathSchema = z
      .string()
      .optional()
      .describe('Path where build products and other derived data will go');
    export const extraArgsSchema = z
      .array(z.string())
      .optional()
      .describe('Additional xcodebuild arguments');
    export const simulatorNameSchema = z
      .string()
      .describe("Name of the simulator to use (e.g., 'iPhone 16') (Required)");
    export const simulatorIdSchema = z
      .string()
      .describe('UUID of the simulator to use (obtained from listSimulators) (Required)');
    export const useLatestOSSchema = z
      .boolean()
      .optional()
      .describe('Whether to use the latest OS version for the named simulator');
    export const appPathSchema = z
      .string()
      .describe('Path to the .app bundle (full path to the .app directory)');
    export const bundleIdSchema = z
      .string()
      .describe("Bundle identifier of the app (e.g., 'com.example.MyApp')");
    export const launchArgsSchema = z
      .array(z.string())
      .optional()
      .describe('Additional arguments to pass to the app');
    export const preferXcodebuildSchema = z
      .boolean()
      .optional()
      .describe(
        'If true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails.',
      );
  • Helper function that performs the actual xcodebuild for simulator build, called by the main handler.
    async function _handleIOSSimulatorBuildLogic(params: {
      workspacePath?: string;
      projectPath?: string;
      scheme: string;
      configuration: string;
      simulatorName?: string;
      simulatorId?: string;
      useLatestOS: boolean;
      derivedDataPath?: string;
      extraArgs?: string[];
      preferXcodebuild?: boolean;
    }): Promise<ToolResponse> {
      log('info', `Starting iOS Simulator build for scheme ${params.scheme} (internal)`);
    
      return executeXcodeBuildCommand(
        {
          ...params,
        },
        {
          platform: XcodePlatform.iOSSimulator,
          simulatorName: params.simulatorName,
          simulatorId: params.simulatorId,
          useLatestOS: params.useLatestOS,
          logPrefix: 'iOS Simulator Build',
        },
        params.preferXcodebuild,
        'build',
      );
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden but only states the tool 'builds and runs' without disclosing behavioral traits like whether this is a destructive operation, what permissions are needed, time/performance characteristics, error handling, or what happens to existing builds. The example helps but doesn't cover behavioral aspects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is perfectly front-loaded with the core purpose in the first sentence, followed by important requirements and a concrete example. Every sentence earns its place with zero wasted words, making it highly efficient for an AI agent.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex build/run tool with 8 parameters and no annotations or output schema, the description is adequate but has clear gaps. It covers the basic purpose and requirements well, but lacks information about what the tool returns, error conditions, performance characteristics, or dependencies on other tools like 'list_sims' for obtaining simulatorId.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all 8 parameters thoroughly. The description adds minimal value by mentioning three required parameters in the text, but doesn't provide additional semantic context beyond what's in the schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verbs ('builds and runs') and resources ('iOS app from a workspace on a simulator specified by UUID'), distinguishing it from siblings like 'build_ios_sim_id_ws' (which only builds) and 'build_run_ios_sim_id_proj' (which uses a project instead of workspace).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use this tool by listing required parameters (workspacePath, scheme, simulatorId) and provides an example, but doesn't explicitly mention when not to use it or name alternatives like 'build_run_ios_sim_id_proj' for project-based builds versus workspace-based.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SampsonKY/XcodeBuildMCP'

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