Skip to main content
Glama

build_project

Builds Xcode projects by specifying configuration and scheme to compile code for testing or deployment.

Instructions

Builds the active Xcode project using the specified configuration and scheme.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
configurationYesBuild configuration to use (e.g., 'Debug' or 'Release').
schemeYesName of the build scheme to be built. Must be one of the schemes available in the project.

Implementation Reference

  • The core handler function that runs the xcodebuild command to build the Xcode project, processes JSON output, optionally filters warnings, generates reports, and returns success status with filtered output.
    private async buildProject(
        projectPath: string, 
        scheme: string, 
        configuration: string, 
        destination: string = "platform=iOS Simulator,name=iPhone 15 Pro",
        includeWarnings: boolean = false
    ) {
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        const logPath = path.join(this.buildLogsDir, `build-${timestamp}.log`);
        const projectDir = path.dirname(projectPath);
        const reportsPath = path.join(projectDir, 'Build', `Reports-${timestamp}`);
        const xcresultPath = `${reportsPath}.xcresult`;
        
        try {
            await mkdir(path.join(projectDir, 'Build'), { recursive: true });
        } catch (error) {
            console.error(`Failed to prepare build directory: ${error}`);
        }
    
        const command = `which xcodebuild && xcodebuild -project "${projectPath}" \
            -scheme "${scheme}" \
            -configuration "${configuration}" \
            -destination '${destination}' \
            -resultBundlePath "${xcresultPath}" \
            -UseModernBuildSystem=YES \
            -json \
            clean build 2>&1 | tee ${logPath}`;
        
        try {
            const { stdout, stderr } = await execAsync(command, { maxBuffer: 100 * 1024 * 1024 });
            
            try {
                const jsonOutput = stdout.split('\n')
                    .filter(line => line.trim())
                    .map(line => {
                        try {
                            return JSON.parse(line);
                        } catch (e) {
                            return line;
                        }
                    });
    
                // Filter warnings if needed
                const filteredOutput = includeWarnings ? jsonOutput : this.filterBuildOutput(jsonOutput);
                
                await writeFile(logPath + '.json', JSON.stringify(filteredOutput, null, 2));
                
                // Use filtered output for response
                const outputText = filteredOutput
                    .map(line => typeof line === 'string' ? line : JSON.stringify(line))
                    .join('\n');
    
                // Process xcresult if it exists
                if (fs.existsSync(xcresultPath)) {
                    try {
                        const reportOutput = await execAsync(`xcrun xcresulttool get --format json --path "${xcresultPath}"`);
                        await writeFile(path.join(this.buildLogsDir, `report-${timestamp}.json`), reportOutput.stdout);
                        
                        const summaryOutput = await execAsync(`xcrun xcresulttool get --format human-readable --path "${xcresultPath}"`);
                        await writeFile(path.join(this.buildLogsDir, `report-${timestamp}.txt`), summaryOutput.stdout);
                    } catch (reportError) {
                        console.error('Failed to process build results:', reportError);
                    }
                }
    
                const success = !stdout.includes('** BUILD FAILED **');
                return { success, output: outputText, logPath };
                
            } catch (parseError) {
                console.error('Failed to parse JSON output:', parseError);
                return { success: false, output: stdout + stderr, logPath };
            }
    
        } catch (error) {
            console.error('Build error:', error);
            if (error instanceof Error) {
                const execError = error as { stderr?: string };
                const errorOutput = error.message + (execError.stderr ? `\n${execError.stderr}` : '');
                await writeFile(logPath, errorOutput);
                return { success: false, output: errorOutput, logPath };
            }
            throw error;
        }
    }
  • src/index.ts:330-344 (registration)
    The tool call handler case that validates arguments, invokes the buildProject method, and formats the MCP response.
    case "build_project": {
        if (!isBuildOptions(request.params.arguments)) {
            throw new McpError(ErrorCode.InvalidParams, "Invalid build arguments provided");
        }
        const { projectPath, scheme, configuration = "Debug", destination, includeWarnings = false } = request.params.arguments;
        const result = await this.buildProject(projectPath, scheme, configuration, destination, includeWarnings);
        this.latestBuildLog = result.logPath;
        return {
            content: [{
                type: "text",
                text: result.output
            }],
            isError: !result.success
        };
    }
  • Tool registration including name, description, and input schema definition for the build_project tool.
    name: "build_project",
    description: "Build an Xcode project",
    inputSchema: {
        type: "object",
        properties: {
            projectPath: {
                type: "string",
                description: "Path to the .xcodeproj or .xcworkspace"
            },
            scheme: {
                type: "string",
                description: "Build scheme name"
            },
            configuration: {
                type: "string",
                description: "Build configuration (e.g., Debug, Release)",
                default: "Debug"
            },
            includeWarnings: {
                type: "boolean",
                description: "Include warning messages in output",
                default: false
            }
        },
        required: ["projectPath", "scheme"]
    }
  • Type guard function to validate build options arguments used in the tool handler.
    function isBuildOptions(args: unknown): args is BuildOptions {
        if (!isBuildArguments(args)) return false;
        const a = args as Partial<BuildOptions>;
        return a.includeWarnings === undefined || typeof a.includeWarnings === 'boolean';
    }
  • Helper function to filter build output, excluding warnings unless includeWarnings is true.
    private filterBuildOutput(jsonOutput: any[]): any[] {
        const significantErrors = jsonOutput.filter(line => {
            if (typeof line === 'string') {
                // Include build system lines
                if (line.startsWith('/usr/bin/xcodebuild') || 
                    line.includes('** BUILD')) {
                    return true;
                }
    
                // Include only actual error messages and their notes
                if (line.match(/^\/.+:\d+:\d+: error:/) ||  // Matches error lines with file paths
                    line.includes('note: found this candidate')) {
                    return true;
                }
    
                return false;
            }
            
            if (typeof line === 'object' && line?.type === 'diagnostic') {
                return line.diagnostic?.severity === 'error';
            }
            
            return false;
        });
    
        return significantErrors;
    }
Behavior2/5

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

No annotations are provided, so the description carries full burden. It states the tool performs a build operation, implying mutation (e.g., compiling code), but doesn't disclose behavioral traits such as execution time, side effects (e.g., generating binaries), error handling, or dependencies on other tools. This leaves significant gaps for a mutation tool.

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 a single, efficient sentence that front-loads the core purpose ('Builds the active Xcode project') and specifies key parameters. There is zero waste, making it appropriately sized and easy to parse.

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

Completeness2/5

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

Given the tool's complexity (a build operation with no annotations and no output schema), the description is incomplete. It lacks details on behavioral aspects (e.g., what 'builds' entails, output location, success/failure indicators) and doesn't compensate for the absence of structured data, making it inadequate for safe agent use.

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%, with both parameters ('configuration' and 'scheme') well-documented in the schema. The description adds no additional meaning beyond implying these parameters are required for the build, matching the baseline score when the schema does the heavy lifting.

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

Purpose4/5

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

The description clearly states the action ('Builds') and target ('the active Xcode project'), specifying the verb and resource. It distinguishes from siblings like 'run_tests' or 'compile_asset_catalog' by focusing on full project builds, but doesn't explicitly differentiate from all alternatives like 'run_xcrun' which might also involve building.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention prerequisites (e.g., needing an active project set via 'set_project_path'), exclusions, or compare to siblings like 'run_tests' for testing builds. Usage is implied by the action but not explicitly defined.

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/PolarVista/Xcode-mcp-server'

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