xcode-cloud-mcp
<p align="center">
<a href="https://www.npmjs.com/package/@thatfactory/xcode-cloud-mcp"><img alt="NPM" src="https://img.shields.io/badge/NPM-ready-CB3837.svg?logo=npm&logoColor=white"></a>
<a href="https://developers.openai.com/codex/mcp"><img alt="Codex MCP" src="https://img.shields.io/badge/Codex-MCP-1F70C1.svg?logo=icloud&logoColor=white"></a>
<a href="https://docs.anthropic.com/en/docs/claude-code/mcp"><img alt="Claude MCP" src="https://img.shields.io/badge/Claude-MCP-D97757.svg?logo=claude&logoColor=white"></a>
<a href="https://en.wikipedia.org/wiki/MIT_License"><img alt="License" src="https://img.shields.io/badge/License-MIT-67ac5b.svg?logo=googledocs&logoColor=white"></a>
<a href="https://github.com/thatfactory/xcode-cloud-mcp/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/thatfactory/xcode-cloud-mcp/actions/workflows/ci.yml/badge.svg"></a>
<a href="https://github.com/thatfactory/xcode-cloud-mcp/actions/workflows/nightly.yml"><img alt="Nightly" src="https://github.com/thatfactory/xcode-cloud-mcp/actions/workflows/nightly.yml/badge.svg"></a>
</p>
# xcode-cloud-mcp
Minimal MCP server for discovering Xcode Cloud products, inspecting and editing workflows, monitoring build runs, and retrieving build issues, logs, test summaries, and UI test artifacts through the App Store Connect API.
## Features
| Feature | Tool(s) | Example use | Example return |
| --- | --- | --- | --- |
| Discover products | `list_products` | "Show me the Xcode Cloud products available in this account." | `Demo App`, `productType: APP`, `createdDate: 2026-03-30T10:00:00Z` |
| Discover workflows | `list_workflows` | "List the workflows for product `def456`." | `Feature Branch`, `description`, `isEnabled: true`, `containerFilePath: Chauffeur.xcodeproj` |
| Inspect workflow configuration | `get_workflow_details` | "Show me the full workflow details for `abc123`, including environment and actions." | `general`, `environment`, `startConditions`, `actions`, `postActions` |
| Monitor running or recent builds | `list_build_runs` | "Show me the running builds for workflow `abc123` so I can monitor them." | `number: 93`, `executionProgress: RUNNING`, `completionStatus: null`, `startedDate: ...` |
| Enable or disable a workflow | `set_workflow_enabled` | "Disable workflow `abc123` while we are testing new settings." | `operation.type: set_workflow_enabled`, `workflow.general.isEnabled: false` |
| Update name, description, or clean mode | `update_workflow_general` | "Rename workflow `abc123` to `Feature Branch v2` and adjust its description." | `changedFields: [name, description]`, updated `workflow.general` |
| Update start conditions explicitly | `update_workflow_start_conditions` | "Change workflow `abc123` so pull-request builds no longer auto-cancel." | updated `workflow.startConditions.pullRequest.autoCancel: false` |
| Replace the workflow action list | `update_workflow_actions` | "Remove the archive action from workflow `abc123`, then add it back once the experiment is done." | `actionCount: 4` after removal, then `actionCount: 5` after restore |
| See build health quickly | `get_build_issues` | "What went wrong in the latest failing build for workflow `abc123`?" | `issueCounts: { errors: 1, testFailures: 3, warnings: 2 }` |
| Read compact build log summaries | `get_build_logs` | "Retrieve logs of build `81` and summarize the failure." | `failedTests`, `highlights`, `excerpt`, `savedLogsDirectory` |
| Materialize logs for local grep | `materialize_build_logs` | "Download the logs for build `81` so I can grep them locally." | `savedLogsDirectory: /var/folders/...`, `savedLogs: [...]` |
| Summarize test outcomes | `get_test_results` | "Summarize the test results for the latest failing build." | `testFailures`, `issueCounts`, `summary` |
| Jump straight to failed tests | `get_failed_tests` | "What tests failed in build `81`?" | `displayExpiryDateReturnsFormattedDateWhenExpiryDateExists()`, assertion message, saved log paths |
| Retrieve UI test artifacts | `get_test_artifacts` | "Show me the screenshots and videos from the latest failing UI test run." | `screenshots`, `videos`, `resultBundles`, `downloadUrl` |
| Clean up local temp files | `cleanup_saved_logs` | "Remove saved logs older than 24 hours." | `removedDirectories: [...]`, `retainedDirectories: [...]` |
Build lookup is workflow-scoped. Retrieval tools accept a direct `buildRunId`, or a `workflowId` plus `buildNumber`, or a `workflowId` plus `buildSelector: "latest" | "latestFailing"`.
`list_products` and `list_workflows` automatically paginate through all results.
`list_build_runs` supports `status: "all" | "failed" | "succeeded" | "running" | "pending"` and an optional `limit`, which defaults to `20`, so agents can poll active workflows without post-processing every run locally or inflating MCP response size.
## Requirements
- Node.js `20+`
- App Store Connect API credentials with access to Xcode Cloud
## Environment Variables
Primary names:
- `APPSTORE_CONNECT_API_KEY_ID`
- `APPSTORE_CONNECT_API_ISSUER_ID`
- `APPSTORE_CONNECT_API_KEY_CONTENT`
Compatibility aliases:
- `APP_STORE_KEY_ID`
- `APP_STORE_ISSUER_ID`
- `APP_STORE_PRIVATE_KEY`
The private key can be passed as literal multi-line PEM content or as a string with escaped `\n`.
## Claude Setup
```bash
claude mcp add xcode-cloud \
--env APPSTORE_CONNECT_API_KEY_ID="$APPSTORE_CONNECT_API_KEY_ID" \
--env APPSTORE_CONNECT_API_ISSUER_ID="$APPSTORE_CONNECT_API_ISSUER_ID" \
--env APPSTORE_CONNECT_API_KEY_CONTENT="$APPSTORE_CONNECT_API_KEY_CONTENT" \
-- npx -y @thatfactory/xcode-cloud-mcp
```
## Codex Setup
```bash
codex mcp add xcode-cloud \
--env APPSTORE_CONNECT_API_KEY_ID="$APPSTORE_CONNECT_API_KEY_ID" \
--env APPSTORE_CONNECT_API_ISSUER_ID="$APPSTORE_CONNECT_API_ISSUER_ID" \
--env APPSTORE_CONNECT_API_KEY_CONTENT="$APPSTORE_CONNECT_API_KEY_CONTENT" \
-- npx -y @thatfactory/xcode-cloud-mcp
```
## Available Tools
- `list_products()`
- `list_workflows(productId)`
- `get_workflow_details(workflowId)`
- `list_build_runs(workflowId, limit?, status?)`
- `set_workflow_enabled(workflowId, enabled)`
- `update_workflow_general(workflowId, name?, description?, clean?)`
- `update_workflow_start_conditions(workflowId, branchStartCondition?, manualBranchStartCondition?, pullRequestStartCondition?, manualPullRequestStartCondition?, scheduledStartCondition?, tagStartCondition?, manualTagStartCondition?)`
- `update_workflow_actions(workflowId, actions)`
- `configure_manual_release_candidate(workflowId, scheme, branch, buildDistributionAudience)`
- `get_build_issues(buildRunId? workflowId? buildNumber? buildSelector?)`
- `get_build_logs(buildRunId? workflowId? buildNumber? buildSelector?, maxCharacters?)`
- `materialize_build_logs(buildRunId? workflowId? buildNumber? buildSelector?)`
- `get_test_results(buildRunId? workflowId? buildNumber? buildSelector?)`
- `get_failed_tests(buildRunId? workflowId? buildNumber? buildSelector?)`
- `get_test_artifacts(buildRunId? workflowId? buildNumber? buildSelector?)`
- `cleanup_saved_logs(buildRunId?, maxAgeHours?)`
## Log Retrieval Behavior
`get_build_logs` keeps the MCP response compact on purpose:
- it downloads and extracts text-like build log artifacts to a temporary local directory
- it returns `savedLogsDirectory` and `savedLogs` so local agents can inspect the extracted files with `rg`, `grep`, or `cat`
- it returns a compact `failedTests` summary, `highlights`, and a capped `excerpt`
- even if a caller passes a very large `maxCharacters`, the inline excerpt is clamped to avoid oversized MCP responses
Recommended agent workflow:
1. Call `get_failed_tests` or `get_build_logs`.
2. Read `savedLogsDirectory`.
3. Use `rg` inside that directory to inspect the exact failing test or assertion.
4. If needed, call `cleanup_saved_logs` when the investigation is done.
Temporary logs are written under the system temp directory in a path like:
```text
/tmp/xcode-cloud-mcp/build-logs/<buildRunId>
```
On macOS this typically resolves to a path under `/var/folders/.../T/`.
Cleanup policy:
- each new call for the same `buildRunId` deletes and recreates that build-specific temp directory first
- older build directories are pruned automatically when they are older than 24 hours
- you can also call `cleanup_saved_logs` directly for one `buildRunId` or for all directories older than a chosen retention window
## Example Prompts
```text
Retrieve logs of the latest failing build for workflow abc123.
```
```text
Retrieve logs of build 81, then inspect the returned savedLogsDirectory and grep for Expectation failed.
```
```text
Get the failed tests for build 81, then open the saved logs directory and inspect the failing test in context.
```
```text
Retrieve logs of build number 42 for workflow abc123.
```
```text
Show me the latest failing UI test artifacts for workflow abc123.
```
```text
List the workflows for product def456 and then summarize the latest build.
```
```text
Show me the full workflow details for workflow abc123, including environment, start conditions, actions, and whether it is enabled.
```
```text
Disable workflow abc123, remove the archive action, then restore the original action list after the experiment.
```
## Workflow Details Behavior
`get_workflow_details` returns the live workflow configuration exposed by App Store Connect, grouped into:
- `general`
- `environment`
- `startConditions`
- `actions`
- `postActions`
Notes:
- `environment` includes repository, `xcodeVersion`, and `macOsVersion` when App Store Connect returns them.
- `actions` includes action type, scheme, platform, destination, required-to-pass state, and test-plan details when present.
- `postActions` is currently returned as an empty array with a note because Apple does not expose workflow post-actions in its public API. This does not mean no post-actions are configured; `testFlightDistribution` reports `UNSUPPORTED_BY_APPLE_API` and an actionable next step.
## Workflow Update Behavior
The workflow update tools are intentionally explicit:
- `set_workflow_enabled` only toggles `isEnabled`
- `update_workflow_general` only changes `name`, `description`, and `clean`
- `update_workflow_start_conditions` only changes the start-condition objects you pass
- `update_workflow_actions` replaces the full `actions` array, so callers should fetch the current workflow first and then send the final desired action list
Important restriction:
- if the workflow has `Restrict Editing` enabled in Xcode Cloud, edits can fail even when the App Store Connect API key has `App Manager` access
- for MCP edits to work reliably, disable the `Restrict Editing` checkbox for that workflow before using the write tools
- if Apple still rejects the request after that, use a stronger API key role such as `Admin`
## Local Development
Install dependencies:
```bash
npm install
```
Run tests:
```bash
npm test
```
Build the package:
```bash
npm run build
```
### Manual TestFlight release candidates
`configure_manual_release_candidate` converts an existing workflow to one macOS archive action (`ARCHIVE`, `MACOS`, `ANY_MAC`) with manual starts from an exact branch. It replaces the entire action list and all seven start conditions in one PATCH, clearing automatic branch, pull-request, tag, and scheduled starts plus manual tag and pull-request starts. Fetch `get_workflow_details` first if you need to retain the original configuration. The preset preserves the workflow's enabled state, name, environment, and clean-build setting; enable a disabled workflow separately when ready.
```json
{
"workflowId": "abc123",
"scheme": "Headroom",
"branch": "main",
"buildDistributionAudience": "APP_STORE_ELIGIBLE"
}
```
The audience is required and cannot be null in this preset:
- `APP_STORE_ELIGIBLE`: Deployment Preparation = TestFlight and App Store.
- `INTERNAL_ONLY`: Deployment Preparation = TestFlight internal testing only.
The general `update_workflow_actions` tool accepts only these two audience values or null/omission (Deployment Preparation = None). Use the preset when requesting a TestFlight release candidate: null/omission is rejected before any API mutation. Workflow details retain the raw `buildDistributionAudience` and add the human-readable `deploymentPreparation` value for each action.
Creating an archive, making it eligible for TestFlight, and assigning its processed build to tester groups are separate steps. This preset configures eligibility; it does not start a build, guarantee successful upload/processing, or assign testers. To distribute automatically, edit the workflow in Xcode or App Store Connect, add a TestFlight Internal Testing post-action, and select the internal group.
Apple's [OpenAPI specification](https://developer.apple.com/sample-code/app-store-connect/app-store-connect-openapi-specification.zip), version 4.4.1, inspected on 2026-09-07, exposes no post-action field or relationship in `CiWorkflow`, `CiWorkflowCreateRequest`, or `CiWorkflowUpdateRequest`, and no workflow post-action endpoint. Consequently, workflow responses explicitly report `testFlightDistribution.status: "UNSUPPORTED_BY_APPLE_API"` and group assignment as `UNKNOWN`; the legacy empty `postActions` array is not evidence that no post-actions exist. See Apple's [BuildAudienceType](https://developer.apple.com/documentation/appstoreconnectapi/buildaudiencetype) and [TestFlight distribution guide](https://developer.apple.com/documentation/xcode/distributing-your-xcode-cloud-builds-through-testflight).
A separate build-start/wait/processing/beta-group orchestration could use the TestFlight API, but is outside this server's current scope and would not be a native Xcode Cloud post-action.
TDQS
Scored across 16 tools
Several tools overlap significantly in purpose: get_build_logs, materialize_build_logs, and get_test_results all resolve a build and download/extract logs locally, with get_build_logs and materialize_build_logs being nearly indistinguishable. get_failed_tests and get_test_artifacts also resolve builds and return test-related metadata, adding further ambiguity. The workflow update tools (update_workflow_general, update_workflow_actions, update_workflow_start_conditions, configure_manual_release_candidate) are more distinct, but the log/test cluster is confusing.
Most tool names follow a consistent verb_noun pattern (get_failed_tests, list_products, update_workflow_general). There is a minor deviation with 'configure_manual_release_candidate' using 'configure' instead of 'update' or 'set', and 'set_workflow_enabled' uses 'set' while others use 'update', but overall the pattern is predictable.
16 tools is slightly heavy for the Xcode Cloud domain but still within a reasonable range. Many tools are needed to cover workflows, builds, tests, and logs, though some redundancy (e.g., multiple log-extraction tools) suggests the count could be trimmed.
The server covers key operations: listing products/workflows, getting workflow details, updating various workflow aspects, listing/analyzing builds, retrieving test results and logs, and cleaning up logs. Minor gaps exist, such as no tool to start a build or manage products, but the core workflows for monitoring and configuration are well-covered.