Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
79d6b5c
feat(xcodebuild): Support prepared test artifacts
cameroncooke Jul 16, 2026
65b862b
feat(manifest): Support conditional next steps
cameroncooke Jul 16, 2026
e359897
fix(testing): Stabilize prepared test execution
cameroncooke Jul 16, 2026
88da782
test(snapshot): Align device test schema versions
cameroncooke Jul 16, 2026
e7b8d53
fix(test): Extract failures from managed result bundles
cameroncooke Jul 16, 2026
f95b573
fix(test): Preserve explicit test destinations
cameroncooke Jul 16, 2026
c3683ea
meta: Merge main into feature branch
cameroncooke Jul 16, 2026
6c45d0b
test(snapshot): isolate snapshot preflight state
cameroncooke Jul 17, 2026
a925f49
fix(testing): address prepared test review findings
cameroncooke Jul 17, 2026
2ee73fa
fix(testing): preserve prepared test selectors
cameroncooke Jul 17, 2026
ae6d5ba
fix(testing): only mark successful test products complete
cameroncooke Jul 17, 2026
a233718
fix: stabilize prepared test snapshot fixtures
cameroncooke Jul 18, 2026
9a22305
fix: honor test product retention options
cameroncooke Jul 18, 2026
f0a9b1e
fix: reject device destinations without prepared tests
cameroncooke Jul 18, 2026
cda637e
fix: isolate session destinations for device builds
cameroncooke Jul 18, 2026
191b74e
fix: mark failed prepared builds complete
cameroncooke Jul 18, 2026
115fdde
fix: remove unstable snapshot fixture churn
cameroncooke Jul 18, 2026
e6912a4
fix(snapshot): Tolerate simulator lifecycle races
cameroncooke Jul 18, 2026
50ffcd0
test(snapshot): Align prepared test fixtures
cameroncooke Jul 18, 2026
650356a
fix(snapshot): Handle teardown shutdown races
cameroncooke Jul 18, 2026
3e8ab0e
fix: Align prepared test review fixes
cameroncooke Jul 18, 2026
dced3b4
fix(snapshot): Stabilize isolated fixture output
cameroncooke Jul 18, 2026
94d4ae3
fix(test): Sanitize managed test product paths
cameroncooke Jul 18, 2026
bdbfb28
fix: preserve test product retention cap
cameroncooke Jul 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

- Added `xcodebuildmcp purge` to report and explicitly clean XcodeBuildMCP-managed workspace storage, including opt-in DerivedData cleanup with dry-run and confirmation safeguards.
- Added `extraArgs` as a first-class session-default value. Repo config or runtime defaults can now carry common `xcodebuild` flags (for example `-skipPackagePluginValidation` or `-disableAutomaticPackageResolution`) so they don't need repeating on every build or test call. Per-call `extraArgs` replace matching configured flags or build settings and append after non-matching defaults, while an explicit empty array (`extraArgs: []`) clears the defaults for a single call. The session management tools show, set, sync, and clear `extraArgs` alongside the other defaults.
- Added reusable test preparation to the existing simulator, device, and macOS build tools. Builds can now return a portable `.xctestproducts` artifact, and test tools can run that artifact—or an advanced `.xctestrun` input—without rebuilding before producing a new `.xcresult` ([#450](https://github.com/getsentry/XcodeBuildMCP/issues/450)).

### Changed

- Tool manifests can now conditionally expose static next steps using handler-activated condition keys, while runtime-generated parameters continue to flow through the existing next-step parameter contract.

### Fixed

Expand Down Expand Up @@ -694,4 +699,3 @@ Please note that the UI automation features are an early preview and currently i
## [v1.0.1] - 2025-04-02
- Initial release of XcodeBuildMCP
- Basic support for building iOS and macOS applications

6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ xcodebuildmcp tools

# Build for simulator
xcodebuildmcp simulator build --scheme MyApp --project-path ./MyApp.xcodeproj

# Prepare portable test products without running tests
xcodebuildmcp simulator build --scheme MyApp --project-path ./MyApp.xcodeproj --simulator-name "iPhone 17" --build-for-testing --test-products-path ./MyApp.xctestproducts

# Run previously prepared test products
xcodebuildmcp simulator test --test-products-path ./MyApp.xctestproducts --simulator-name "iPhone 17"
```

Check for updates and upgrade in place:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ import OSLog
import CalculatorAppFeature

private let logger = Logger(subsystem: "io.sentry.calculatorapp", category: "lifecycle")
private let snapshotScrollSurfaceArgument = "--snapshot-scroll-surface"

@main
struct CalculatorApp: App {
@Environment(\.scenePhase) private var scenePhase

var body: some Scene {
WindowGroup {
ContentView()
if ProcessInfo.processInfo.arguments.contains(snapshotScrollSurfaceArgument) {
SnapshotScrollSurface()
} else {
ContentView()
}
}
.onChange(of: scenePhase) { _, newPhase in
switch newPhase {
Expand All @@ -25,6 +30,21 @@ struct CalculatorApp: App {
}
}

private struct SnapshotScrollSurface: View {
var body: some View {
ScrollView {
LazyVStack(spacing: 16) {
ForEach(0..<30, id: \.self) { index in
Text("Snapshot row \(index)")
.frame(maxWidth: .infinity, minHeight: 44)
}
}
.padding()
}
.accessibilityIdentifier("snapshot-scroll-surface")
}
}

#Preview {
ContentView()
}
Expand Down
5 changes: 5 additions & 0 deletions manifests/tools/batch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ annotations:
readOnlyHint: true
destructiveHint: false
openWorldHint: false
nextSteps:
- label: Refresh after UI action
toolId: snapshot_ui
when: success
condition: ui_action_needs_refresh
7 changes: 6 additions & 1 deletion manifests/tools/build_device.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ names:
description: Build for device.
outputSchema:
schema: xcodebuildmcp.output.build-result
version: "2"
version: '3'
predicates:
- hideWhenXcodeAgentMode
annotations:
Expand All @@ -19,3 +19,8 @@ nextSteps:
toolId: get_device_app_path
priority: 1
when: success
condition: app_build_succeeded
- label: Run prepared tests
toolId: test_device
when: success
condition: prepared_tests_available
7 changes: 6 additions & 1 deletion manifests/tools/build_macos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ names:
description: Build macOS app.
outputSchema:
schema: xcodebuildmcp.output.build-result
version: "2"
version: '3'
predicates:
- hideWhenXcodeAgentMode
annotations:
Expand All @@ -19,3 +19,8 @@ nextSteps:
toolId: get_mac_app_path
priority: 1
when: success
condition: app_build_succeeded
- label: Run prepared tests
toolId: test_macos
when: success
condition: prepared_tests_available
7 changes: 6 additions & 1 deletion manifests/tools/build_sim.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ names:
description: Build for iOS sim (compile-only, no launch).
outputSchema:
schema: xcodebuildmcp.output.build-result
version: "2"
version: '3'
Comment thread
github-actions[bot] marked this conversation as resolved.
predicates:
- hideWhenXcodeAgentMode
annotations:
Expand All @@ -19,3 +19,8 @@ nextSteps:
toolId: get_sim_app_path
priority: 1
when: success
condition: app_build_succeeded
- label: Run prepared tests
toolId: test_sim
when: success
condition: prepared_tests_available
5 changes: 5 additions & 0 deletions manifests/tools/button.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ annotations:
readOnlyHint: true
destructiveHint: false
openWorldHint: false
nextSteps:
- label: Refresh after UI action
toolId: snapshot_ui
when: success
condition: ui_action_needs_refresh
5 changes: 5 additions & 0 deletions manifests/tools/drag.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ annotations:
readOnlyHint: true
destructiveHint: false
openWorldHint: false
nextSteps:
- label: Refresh after UI action
toolId: snapshot_ui
when: success
condition: ui_action_needs_refresh
5 changes: 5 additions & 0 deletions manifests/tools/gesture.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ annotations:
readOnlyHint: true
destructiveHint: false
openWorldHint: false
nextSteps:
- label: Refresh after UI action
toolId: snapshot_ui
when: success
condition: ui_action_needs_refresh
5 changes: 5 additions & 0 deletions manifests/tools/key_press.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ annotations:
readOnlyHint: true
destructiveHint: false
openWorldHint: false
nextSteps:
- label: Refresh after UI action
toolId: snapshot_ui
when: success
condition: ui_action_needs_refresh
5 changes: 5 additions & 0 deletions manifests/tools/key_sequence.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ annotations:
readOnlyHint: true
destructiveHint: false
openWorldHint: false
nextSteps:
- label: Refresh after UI action
toolId: snapshot_ui
when: success
condition: ui_action_needs_refresh
5 changes: 5 additions & 0 deletions manifests/tools/long_press.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ annotations:
readOnlyHint: true
destructiveHint: false
openWorldHint: false
nextSteps:
- label: Refresh after UI action
toolId: snapshot_ui
when: success
condition: ui_action_needs_refresh
5 changes: 5 additions & 0 deletions manifests/tools/swipe.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,8 @@ annotations:
readOnlyHint: true
destructiveHint: false
openWorldHint: false
nextSteps:
- label: Refresh after UI action
toolId: snapshot_ui
when: success
condition: ui_action_needs_refresh
5 changes: 5 additions & 0 deletions manifests/tools/tap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ annotations:
readOnlyHint: true
destructiveHint: false
openWorldHint: false
nextSteps:
- label: Refresh after UI action
toolId: snapshot_ui
when: success
condition: ui_action_needs_refresh
2 changes: 1 addition & 1 deletion manifests/tools/test_device.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ names:
description: Test on device.
outputSchema:
schema: xcodebuildmcp.output.test-result
version: "2"
version: "3"
predicates:
- hideWhenXcodeAgentMode
annotations:
Expand Down
2 changes: 1 addition & 1 deletion manifests/tools/test_macos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ names:
description: Test macOS target.
outputSchema:
schema: xcodebuildmcp.output.test-result
version: "2"
version: "3"
predicates:
- hideWhenXcodeAgentMode
annotations:
Expand Down
2 changes: 1 addition & 1 deletion manifests/tools/test_sim.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ names:
description: Test on iOS sim.
outputSchema:
schema: xcodebuildmcp.output.test-result
version: "2"
version: "3"
predicates:
- hideWhenXcodeAgentMode
annotations:
Expand Down
5 changes: 5 additions & 0 deletions manifests/tools/touch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ annotations:
readOnlyHint: true
destructiveHint: false
openWorldHint: false
nextSteps:
- label: Refresh after UI action
toolId: snapshot_ui
when: success
condition: ui_action_needs_refresh
5 changes: 5 additions & 0 deletions manifests/tools/type_text.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ annotations:
readOnlyHint: true
destructiveHint: false
openWorldHint: false
nextSteps:
- label: Refresh after UI action
toolId: snapshot_ui
when: success
condition: ui_action_needs_refresh
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@
"knip": "knip",
"test": "vitest run",
"test:schema-fixtures": "vitest run src/snapshot-tests/__tests__/json-fixture-schema.test.ts",
"test:snapshot": "npm run build && node build/cli.js daemon stop 2>/dev/null; vitest run --config vitest.snapshot.config.ts",
"test:snapshot": "npm run build && vitest run --config vitest.snapshot.config.ts",
"test:snapshots": "npm run test:snapshot",
"test:snapshot:device": "npm run build && node build/cli.js daemon stop 2>/dev/null; vitest run --config vitest.snapshot.config.ts src/snapshot-tests/__tests__/device.snapshot.test.ts && vitest run --config vitest.snapshot.config.ts src/snapshot-tests/__tests__/cli-json-fixture-parity.snapshot.test.ts src/snapshot-tests/__tests__/mcp-json-fixture-parity.snapshot.test.ts -t 'device workflow'",
"test:snapshot:update": "npm run build && node build/cli.js daemon stop 2>/dev/null; UPDATE_SNAPSHOTS=1 vitest run --config vitest.snapshot.config.ts",
"test:snapshot:device": "npm run build && vitest run --config vitest.snapshot.config.ts src/snapshot-tests/__tests__/device.snapshot.test.ts && vitest run --config vitest.snapshot.config.ts src/snapshot-tests/__tests__/cli-json.snapshot.test.ts src/snapshot-tests/__tests__/mcp-json.snapshot.test.ts -t 'device workflow'",
"test:snapshot:update": "npm run build && UPDATE_SNAPSHOTS=1 vitest run --config vitest.snapshot.config.ts",
"test:snapshots:update": "npm run test:snapshot:update",
"test:smoke": "npm run build && vitest run --config vitest.smoke.config.ts",
"test:watch": "vitest",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.build-result/3.schema.json",
"type": "object",
"additionalProperties": false,
"allOf": [
{
"$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency"
}
],
"$defs": {
"buildInvocationRequest": {
"type": "object",
"additionalProperties": false,
"properties": {
"buildForTesting": { "type": "boolean" },
"scheme": { "type": "string" },
"workspacePath": { "type": "string" },
"projectPath": { "type": "string" },
"packagePath": { "type": "string" },
"targetName": { "type": "string" },
"configuration": { "type": "string" },
"platform": { "type": "string" },
"target": {
"enum": ["simulator", "device", "macos", "swift-package"]
},
"simulatorName": { "type": "string" },
"simulatorId": { "type": "string" },
"deviceId": { "type": "string" },
"executableName": { "type": "string" },
"arch": { "type": "string" },
"derivedDataPath": { "type": "string" },
"testProductsPath": { "type": "string" },
"xctestrunPath": { "type": "string" },
"onlyTesting": {
"type": "array",
"items": { "type": "string" }
},
"skipTesting": {
"type": "array",
"items": { "type": "string" }
}
}
}
},
"properties": {
"schema": {
"const": "xcodebuildmcp.output.build-result"
},
"schemaVersion": {
"const": "3"
},
"didError": {
"type": "boolean"
},
"error": {
"type": ["string", "null"]
},
"data": {
"anyOf": [
{
"type": "object",
"additionalProperties": false,
"properties": {
"request": {
"$ref": "#/$defs/buildInvocationRequest"
},
"summary": {
"type": "object",
"additionalProperties": false,
"properties": {
"status": {
"enum": ["SUCCEEDED", "FAILED"]
},
"durationMs": {
"type": "integer",
"minimum": 0
},
"target": {
"enum": ["simulator", "device", "macos", "swift-package"]
}
},
"required": ["status"]
},
"artifacts": {
"type": "object",
"additionalProperties": false,
"properties": {
"appPath": { "type": "string" },
"bundleId": { "type": "string" },
"buildLogPath": { "type": "string" },
"packagePath": { "type": "string" },
"workspacePath": { "type": "string" },
"scheme": { "type": "string" },
"configuration": { "type": "string" },
"platform": { "type": "string" },
"testProductsPath": { "type": "string" },
"xctestrunPaths": {
"type": "array",
"items": { "type": "string" }
}
},
"required": [],
"minProperties": 1
},
"diagnostics": {
"$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/basicDiagnostics"
}
},
"required": ["summary", "artifacts", "diagnostics"]
},
{
"type": "null"
}
]
},
"nextSteps": {
"$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/nextSteps"
}
},
"required": ["schema", "schemaVersion", "didError", "error", "data"]
}
Loading
Loading