Skip to main content
Glama

scaffold_ios_project

Create a modern iOS Xcode project with workspace structure, SPM package for features, and proper iOS configuration from templates.

Instructions

Scaffold a new iOS project from templates. Creates a modern Xcode project with workspace structure, SPM package for features, and proper iOS configuration.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
projectNameYesName of the new project
outputPathYesPath where the project should be created
bundleIdentifierNoBundle identifier (e.g., com.example.myapp). If not provided, will use com.example.projectname
displayNameNoApp display name (shown on home screen/dock). If not provided, will use projectName
marketingVersionNoMarketing version (e.g., 1.0, 2.1.3). If not provided, will use 1.0
currentProjectVersionNoBuild number (e.g., 1, 42, 100). If not provided, will use 1
customizeNamesNoWhether to customize project names and identifiers. Default is true.
deploymentTargetNoiOS deployment target (e.g., 18.4, 17.0). If not provided, will use 18.4
targetedDeviceFamilyNoTarget device family. If not provided, will use iPhone+iPad
supportedOrientationsNoSupported orientations for iPhone. If not provided, will use all orientations
supportedOrientationsIpadNoSupported orientations for iPad. If not provided, will use all orientations

Implementation Reference

  • Zod schema defining input parameters for the scaffold_ios_project tool, extending base schema with iOS-specific fields like deploymentTarget, targetedDeviceFamily, and orientations.
    const ScaffoldiOSProjectSchema = BaseScaffoldSchema.extend({
      deploymentTarget: z
        .string()
        .optional()
        .describe('iOS deployment target (e.g., 18.4, 17.0). If not provided, will use 18.4'),
      targetedDeviceFamily: z
        .enum(['iPhone', 'iPad', 'iPhone+iPad'])
        .optional()
        .describe('Target device family. If not provided, will use iPhone+iPad'),
      supportedOrientations: z
        .array(z.enum(['Portrait', 'LandscapeLeft', 'LandscapeRight']))
        .optional()
        .describe('Supported orientations for iPhone. If not provided, will use all orientations'),
      supportedOrientationsIpad: z
        .array(z.enum(['Portrait', 'PortraitUpsideDown', 'LandscapeLeft', 'LandscapeRight']))
        .optional()
        .describe('Supported orientations for iPad. If not provided, will use all orientations'),
    });
  • Core handler function that performs the actual scaffolding: validates input, retrieves template, recursively processes files/directories with customizations, and returns the project path.
    async function scaffoldProject(params: ScaffoldProjectParams): Promise<string> {
      const { projectName, outputPath, platform, customizeNames = true } = params;
    
      log('info', `Scaffolding project: ${projectName} (${platform}) at ${outputPath}`);
    
      // Validate project name
      if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(projectName)) {
        throw new ValidationError(
          'Project name must start with a letter and contain only letters, numbers, and underscores',
        );
      }
    
      // Get template path from TemplateManager
      let templatePath: string;
      try {
        templatePath = await TemplateManager.getTemplatePath(platform);
      } catch (error) {
        throw new ValidationError(
          `Failed to get template for ${platform}: ${error instanceof Error ? error.message : String(error)}`,
        );
      }
    
      // Create output directory
      const projectPath = join(outputPath, customizeNames ? projectName : 'MyProject');
    
      if (existsSync(projectPath)) {
        throw new ValidationError(`Project directory already exists at ${projectPath}`);
      }
    
      try {
        // Process the template
        await processDirectory(templatePath, projectPath, params);
    
        return projectPath;
      } finally {
        // Clean up downloaded template if needed
        await TemplateManager.cleanup(templatePath);
      }
    }
  • Registers the scaffold_ios_project tool with the MCP server, providing name, description, schema, and an async handler that adds platform='iOS' and calls scaffoldProject, returning formatted success/error response.
    registerTool<ScaffoldiOSProjectParams>(
      server,
      'scaffold_ios_project',
      'Scaffold a new iOS project from templates. Creates a modern Xcode project with workspace structure, SPM package for features, and proper iOS configuration.',
      ScaffoldiOSProjectSchema.shape,
      async (params) => {
        try {
          const projectParams: ScaffoldProjectParams = { ...params, platform: 'iOS' };
          const projectPath = await scaffoldProject(projectParams);
    
          const response = {
            success: true,
            projectPath,
            platform: 'iOS',
            message: `Successfully scaffolded iOS project "${params.projectName}" at ${projectPath}`,
            nextSteps: [
              `Open the project: open ${projectPath}/${params.customizeNames ? params.projectName : 'MyProject'}.xcworkspace`,
              `Build for simulator: build_ios_sim_name_ws --workspace-path "${projectPath}/${params.customizeNames ? params.projectName : 'MyProject'}.xcworkspace" --scheme "${params.customizeNames ? params.projectName : 'MyProject'}" --simulator-name "iPhone 16"`,
              `Build and run on simulator: build_run_ios_sim_name_ws --workspace-path "${projectPath}/${params.customizeNames ? params.projectName : 'MyProject'}.xcworkspace" --scheme "${params.customizeNames ? params.projectName : 'MyProject'}" --simulator-name "iPhone 16"`,
            ],
          };
    
          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify(response, null, 2),
              },
            ],
          };
        } catch (error) {
          log(
            'error',
            `Failed to scaffold iOS project: ${error instanceof Error ? error.message : String(error)}`,
          );
    
          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify(
                  {
                    success: false,
                    error: error instanceof Error ? error.message : 'Unknown error occurred',
                  },
                  null,
                  2,
                ),
              },
            ],
          };
        }
      },
    );
  • Helper function to customize .xcconfig files with project details, bundle ID, versions, iOS-specific deployment target, device family, and orientations.
    function updateXCConfigFile(content: string, params: ScaffoldProjectParams): string {
      let result = content;
    
      // Update project identity settings
      result = result.replace(/PRODUCT_NAME = .+/g, `PRODUCT_NAME = ${params.projectName}`);
      result = result.replace(
        /PRODUCT_DISPLAY_NAME = .+/g,
        `PRODUCT_DISPLAY_NAME = ${params.displayName || params.projectName}`,
      );
      result = result.replace(
        /PRODUCT_BUNDLE_IDENTIFIER = .+/g,
        `PRODUCT_BUNDLE_IDENTIFIER = ${params.bundleIdentifier || `com.example.${params.projectName.toLowerCase().replace(/[^a-z0-9]/g, '')}`}`,
      );
      result = result.replace(
        /MARKETING_VERSION = .+/g,
        `MARKETING_VERSION = ${params.marketingVersion || '1.0'}`,
      );
      result = result.replace(
        /CURRENT_PROJECT_VERSION = .+/g,
        `CURRENT_PROJECT_VERSION = ${params.currentProjectVersion || '1'}`,
      );
    
      // Platform-specific updates
      if (params.platform === 'iOS') {
        const iosParams = params as ScaffoldiOSProjectParams & { platform: 'iOS' };
    
        // iOS deployment target
        if (iosParams.deploymentTarget) {
          result = result.replace(
            /IPHONEOS_DEPLOYMENT_TARGET = .+/g,
            `IPHONEOS_DEPLOYMENT_TARGET = ${iosParams.deploymentTarget}`,
          );
        }
    
        // Device family
        if (iosParams.targetedDeviceFamily) {
          const deviceFamilyValue = deviceFamilyToNumeric(iosParams.targetedDeviceFamily);
          result = result.replace(
            /TARGETED_DEVICE_FAMILY = .+/g,
            `TARGETED_DEVICE_FAMILY = ${deviceFamilyValue}`,
          );
        }
    
        // iPhone orientations
        if (iosParams.supportedOrientations && iosParams.supportedOrientations.length > 0) {
          // Filter out any empty strings and validate
          const validOrientations = iosParams.supportedOrientations.filter((o) => o && o.trim() !== '');
          if (validOrientations.length > 0) {
            const orientations = validOrientations.map(orientationToIOSConstant).join(' ');
            result = result.replace(
              /INFOPLIST_KEY_UISupportedInterfaceOrientations = .+/g,
              `INFOPLIST_KEY_UISupportedInterfaceOrientations = ${orientations}`,
            );
          }
        }
    
        // iPad orientations
        if (iosParams.supportedOrientationsIpad && iosParams.supportedOrientationsIpad.length > 0) {
          // Filter out any empty strings and validate
          const validOrientations = iosParams.supportedOrientationsIpad.filter(
            (o) => o && o.trim() !== '',
          );
          if (validOrientations.length > 0) {
            const orientations = validOrientations.map(orientationToIOSConstant).join(' ');
            result = result.replace(
              /INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = .+/g,
              `INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = ${orientations}`,
            );
          }
        }
      } else if (params.platform === 'macOS') {
        const macosParams = params as ScaffoldmacOSProjectParams & { platform: 'macOS' };
    
        // macOS deployment target
        if (macosParams.deploymentTarget) {
          result = result.replace(
            /MACOSX_DEPLOYMENT_TARGET = .+/g,
            `MACOSX_DEPLOYMENT_TARGET = ${macosParams.deploymentTarget}`,
          );
        }
    
        // Update entitlements path for macOS
        result = result.replace(
          /CODE_SIGN_ENTITLEMENTS = .+/g,
          `CODE_SIGN_ENTITLEMENTS = ${params.projectName}/${params.projectName}.entitlements`,
        );
      }
    
      // Update test bundle identifier and target name
      result = result.replace(/TEST_TARGET_NAME = .+/g, `TEST_TARGET_NAME = ${params.projectName}`);
    
      return result;
  • Recursive helper to process template directories, customizing file/directory names and contents based on params.
    async function processDirectory(
      sourceDir: string,
      destDir: string,
      params: ScaffoldProjectParams,
    ): Promise<void> {
      const entries = await readdir(sourceDir, { withFileTypes: true });
    
      for (const entry of entries) {
        const sourcePath = join(sourceDir, entry.name);
        let destName = entry.name;
    
        if (params.customizeNames) {
          // Replace MyProject in directory names
          destName = destName.replace(/MyProject/g, params.projectName);
        }
    
        const destPath = join(destDir, destName);
    
        if (entry.isDirectory()) {
          // Skip certain directories
          if (entry.name === '.git' || entry.name === 'xcuserdata') {
            continue;
          }
          await mkdir(destPath, { recursive: true });
          await processDirectory(sourcePath, destPath, params);
        } else if (entry.isFile()) {
          // Skip certain files
          if (entry.name === '.DS_Store' || entry.name.endsWith('.xcuserstate')) {
            continue;
          }
          await processFile(sourcePath, destPath, params);
        }
      }
Behavior2/5

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

With no annotations provided, the description carries full burden for behavioral disclosure. It states 'creates' which implies a write/mutation operation, but doesn't disclose important behavioral traits like whether this overwrites existing files, what permissions are needed, whether it's idempotent, or what happens on failure. The description mentions the outcome but not the process or potential side effects.

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

Conciseness4/5

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

The description is appropriately sized (two sentences) and front-loaded with the core purpose. Every sentence earns its place by specifying what gets created and the key components. It could be slightly more structured by separating creation details from configuration details, but it's efficient overall.

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 creation tool with 11 parameters, no annotations, and no output schema, the description is moderately complete. It explains what gets created but lacks information about return values, error conditions, or behavioral constraints. Given the complexity and absence of structured metadata, it should provide more context about the creation process and outcomes.

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 11 parameters thoroughly with descriptions and defaults. The description doesn't add any parameter-specific information beyond what's in the schema. According to scoring rules, when schema coverage is high (>80%), the baseline is 3 even with no param info in the description.

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 specific action ('scaffold a new iOS project from templates') and resource ('iOS project'), distinguishing it from sibling tools like 'scaffold_macos_project' which targets macOS instead of iOS. It provides concrete details about what gets created (Xcode project with workspace structure, SPM package, iOS configuration).

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

Usage Guidelines3/5

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

The description implies usage context (creating a new iOS project from templates) but doesn't explicitly state when to use this tool versus alternatives like 'scaffold_macos_project' or the various build/run tools. It mentions 'modern Xcode project' which suggests it's for new projects, but lacks explicit guidance on prerequisites or exclusions.

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