XcodeBuildMCPdocs
Install

Output Formats

Choose human-readable output for agents, a structured JSON response for scripts, NDJSON fragments for live pipelines, or raw process output for debugging.

XcodeBuildMCP exposes the same tool results through the CLI and MCP server. The CLI controls presentation with --output text|json|jsonl|raw. MCP clients receive the rendered text block, and tools with structured results also attach structuredContent.

CLI output modes#

Every CLI tool accepts --output. The default is text.

shell
xcodebuildmcp simulator list --output json
xcodebuildmcp simulator build --scheme MyApp --project-path ./MyApp.xcodeproj --output jsonl
ModeWhat you getUse it when
textHuman-readable CLI output, with live progress for long-running tools.An agent or person is reading the result.
jsonOne structured JSON response printed to stdout after the tool finishes.A script, CI job, or custom integration needs stable fields.
jsonlNewline-delimited JSON (NDJSON), one JSON object per line as fragments arrive.A pipeline needs live progress without parsing terminal text.
rawRaw subprocess transcript on stderr, with final rendered text on stdout.You are debugging the underlying xcodebuild, xcrun, or helper process.

--output text#

text is the default. It uses the interactive CLI renderer in terminals, so build and test tools can stream progress while the command is running. In non-interactive contexts it still returns readable text.

shell
xcodebuildmcp simulator list
text
Available iOS Simulators:

• iPhone 17 Pro (Booted)
  UDID: 11111111-2222-3333-4444-555555555555
  Runtime: iOS 26.0

Next steps:
  xcodebuildmcp simulator build --scheme YOUR_SCHEME --simulator-id 11111111-2222-3333-4444-555555555555

Use text for normal agent workflows. Agents can read summaries, diagnostics, and next-step commands without needing a JSON parser.

CLI text renders file artifacts as labeled rows under Files: by default:

text
Files:
   ├ App Path: ~/Library/Developer/XcodeBuildMCP/.../MyApp.app
   └ Build Logs: ~/Library/Developer/XcodeBuildMCP/.../build.log

Use --file-path-render-style tree for the compact grouped tree used by MCP text responses, or set filePathRenderStyle: "tree" / XCODEBUILDMCP_FILE_PATH_RENDER_STYLE=tree. This setting only affects human-readable text; --output json and --output jsonl keep the same structured fields.

--output json#

json prints a structured JSON response to stdout after the tool finishes. It does not stream progress. Use it when you need stable fields for scripts or CI.

The response has these fields:

FieldTypeMeaning
schemastringSchema identifier, for example xcodebuildmcp.output.build-result.
schemaVersionstringVersion of that schema contract.
didErrorbooleanWhether the final result is an error.
errorstring | nullHuman-readable error text when didError is true.
dataobject | nullSchema-specific payload. Domain schemas use tool-specific data; xcodebuildmcp.output.error uses generic error metadata.

Structured output is a tagged union. Consumers should branch on schema, then parse data according to that schema:

typescript
type XcodeBuildMCPOutput =
  | {
      schema: "xcodebuildmcp.output.simulator-list"
      schemaVersion: "1"
      didError: boolean
      error: string | null
      data: { simulators: Simulator[] }
    }
  | {
      schema: "xcodebuildmcp.output.error"
      schemaVersion: "1"
      didError: true
      error: string
      data: { category: "runtime" | "validation" | "schema"; code: string }
    }

For example, list_sims normally returns xcodebuildmcp.output.simulator-list, but a validation or runtime failure before simulator listing begins returns xcodebuildmcp.output.error. Do not assume a tool always returns only its domain schema; always check schema first.

json
{
  "schema": "xcodebuildmcp.output.simulator-list",
  "schemaVersion": "1",
  "didError": false,
  "error": null,
  "data": {
    "simulators": [
      {
        "name": "iPhone 17 Pro",
        "simulatorId": "A1B2C3D4-E5F6-7890-ABCD-EF0123456789",
        "state": "Booted",
        "isAvailable": true,
        "runtime": "iOS 26.4"
      },
      {
        "name": "iPad Pro 13-inch (M5)",
        "simulatorId": "0F1E2D3C-4B5A-6978-8190-1122334455AA",
        "state": "Shutdown",
        "isAvailable": true,
        "runtime": "iOS 26.4"
      }
    ]
  }
}

data can be null when a tool succeeds but has no structured payload. If a tool produces no structured output, or if a pre-domain failure occurs (validation, schema, or runtime error before the tool executes), the response uses the generic error branch and the CLI exits with a non-zero status:

json
{
  "schema": "xcodebuildmcp.output.error",
  "schemaVersion": "1",
  "didError": true,
  "error": "Parameter validation failed: Invalid parameters...",
  "data": {
    "category": "validation",
    "code": "PARAMETER_VALIDATION_FAILED"
  }
}

Domain failures still use their domain schema. For example, an attempted build that fails returns xcodebuildmcp.output.build-result with didError: true and build diagnostics in data.

--output jsonl#

jsonl streams newline-delimited JSON (NDJSON) to stdout, one JSON object per line, written as the tool produces it. Each line has this shape:

json
{"event":"build-result.build-stage","operation":"BUILD","stage":"COMPILING","message":"Compiling MyApp"}

The final structured response is not emitted in jsonl mode. Your pipeline consumes fragments as the source of truth for live progress.

jsonl
{"event":"build-result.invocation","operation":"BUILD","request":{"scheme":"CalculatorApp","workspacePath":"example_projects/iOS_Calculator/CalculatorApp.xcworkspace","configuration":"Debug","platform":"iOS Simulator","simulatorName":"iPhone 17"}}
{"event":"build-result.build-stage","operation":"BUILD","stage":"RESOLVING_PACKAGES","message":"Resolving packages"}
{"event":"build-result.build-stage","operation":"BUILD","stage":"COMPILING","message":"Compiling CalculatorApp"}
{"event":"build-result.build-summary","operation":"BUILD","status":"SUCCEEDED","durationMs":3421}

Use jsonl when the user benefits from live progress, but your caller still wants machine-readable records.

--output raw#

raw is for debugging subprocess behavior. Transcript fragments, such as the command line and raw process output, are written to stderr as the tool produces them. The final non-transcript rendering still goes to stdout.

shell
xcodebuildmcp simulator build \
  --scheme MyApp \
  --project-path ./MyApp.xcodeproj \
  --output raw
text
$ xcodebuild -project ./MyApp.xcodeproj -scheme MyApp -destination platform=iOS Simulator,name=iPhone 17 Pro build
CompileSwift normal arm64 /repo/MyApp/ContentView.swift
Ld /tmp/DerivedData/MyApp.app/MyApp normal arm64

Do not script against raw. The transcript is intentionally close to the underlying tools, so it can change when Xcode changes.

Structured content for MCP clients#

MCP mode always returns the normal content array. When a tool sets structured output, XcodeBuildMCP also attaches structuredContent with the same shape as --output json. Pre-domain failures — validation errors, schema mismatches, or runtime errors before the tool executes — also produce structuredContent using schema: "xcodebuildmcp.output.error".

json
{
  "content": [
    {
      "type": "text",
      "text": "Build and run succeeded for CalculatorApp."
    }
  ],
  "structuredContent": {
    "schema": "xcodebuildmcp.output.build-run-result",
    "schemaVersion": "1",
    "didError": false,
    "error": null,
    "data": {
      "request": {
        "scheme": "CalculatorApp",
        "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace",
        "configuration": "Debug",
        "platform": "iOS Simulator",
        "simulatorName": "iPhone 17"
      },
      "summary": {
        "status": "SUCCEEDED",
        "durationMs": 3421,
        "target": "simulator"
      },
      "artifacts": {
        "appPath": "~/Library/Developer/XcodeBuildMCP/DerivedData/Build/Products/Debug-iphonesimulator/CalculatorApp.app",
        "bundleId": "io.sentry.calculatorapp",
        "processId": 91827,
        "simulatorId": "A1B2C3D4-E5F6-7890-ABCD-EF0123456789",
        "buildLogPath": "~/Library/Developer/XcodeBuildMCP/logs/build_run_sim_20260424.log",
        "runtimeLogPath": "~/Library/Developer/XcodeBuildMCP/logs/io.sentry.calculatorapp_20260424.log"
      },
      "diagnostics": {
        "warnings": [],
        "errors": []
      }
    }
  }
}

Tools that declare an output schema also advertise it during MCP registration. For tools with structured output, the advertised schema is a union of the tool's domain envelope and the generic xcodebuildmcp.output.error envelope. Clients can use that schema to validate structuredContent or render typed UI, but should still branch on the returned schema value at runtime.

Per-test results#

Test tools (test_sim, test_device, test_macos, swift_package_test) include a testCases array on the structured test-result response. Each entry has suite (optional), test, status (passed, failed, or skipped), and durationMs (optional).

The array is always present in JSON output and MCP structuredContent when the run produced any per-case results. The showTestTiming config option (or XCODEBUILDMCP_SHOW_TEST_TIMING=1) is purely a text-rendering toggle: with it on, --output text and MCP content[] text include a Test Results: block before the summary; with it off, the structured data is still there for programmatic consumers.

json
{
  "schema": "xcodebuildmcp.output.test-result",
  "schemaVersion": "1",
  "didError": false,
  "error": null,
  "data": {
    "summary": {
      "status": "FAILED",
      "durationMs": 22100,
      "counts": { "passed": 21, "failed": 2, "skipped": 0 },
      "target": "simulator"
    },
    "testCases": [
      { "suite": "CalculatorAppTests", "test": "testAddition", "status": "passed", "durationMs": 1 },
      { "suite": "CalculatorAppTests", "test": "testCalculatorServiceFailure", "status": "failed", "durationMs": 4 },
      { "suite": "IntentionalFailureTests", "test": "test", "status": "failed", "durationMs": 3 }
    ],
    "diagnostics": {
      "warnings": [],
      "errors": [],
      "testFailures": [
        {
          "suite": "CalculatorAppTests",
          "test": "testCalculatorServiceFailure",
          "message": "XCTAssertEqual failed: (\"0\") is not equal to (\"999\")",
          "location": "CalculatorAppTests/CalculatorAppTests.swift:52"
        }
      ]
    }
  }
}

Parameterized Swift Testing groups currently surface as a single aggregate entry because xcodebuild does not emit per-case names or durations for them. Counts in summary.counts may exceed the testCases array length when parameterization is in use.

Response schema reference#

Canonical JSON schemas live in the source repository under schemas/structured-output/. Concrete examples:

Examples#

json
{
"schema": "xcodebuildmcp.output.simulator-list",
"schemaVersion": "1",
"didError": false,
"error": null,
"data": {
  "simulators": [
    {
      "name": "iPhone 17 Pro",
      "simulatorId": "A1B2C3D4-E5F6-7890-ABCD-EF0123456789",
      "state": "Booted",
      "isAvailable": true,
      "runtime": "iOS 26.4"
    },
    {
      "name": "iPad Pro 13-inch (M5)",
      "simulatorId": "0F1E2D3C-4B5A-6978-8190-1122334455AA",
      "state": "Shutdown",
      "isAvailable": true,
      "runtime": "iOS 26.4"
    }
  ]
}
}