xcode-cloud-mcp
by deeepanshu
README.md
# xcode-cloud-mcp (self-owned)
Local **Model Context Protocol** server so your agents can track **Xcode Cloud** builds via the **App Store Connect API**.
It also reads TestFlight beta-feedback crash reports. These are distinct from
Xcode Cloud CI failures: they are reports a TestFlight tester explicitly
submitted, including the raw crash log for a selected report.
This is your private copy — no third-party Xcode Cloud MCP package. Only well-known dependencies:
| Package | Why |
|---------|-----|
| `@modelcontextprotocol/sdk` | MCP stdio server |
| `jose` | ES256 JWT for App Store Connect |
| `zod` | Tool argument schemas |
## What agents can do
| Tool | Purpose |
|------|---------|
| `list_products` | Discover Xcode Cloud products |
| `list_apps` | Discover App Store Connect apps for TestFlight diagnostics |
| `list_workflows` | Workflows for a product |
| `get_workflow` | Single workflow details |
| `start_build` | Trigger a build (optional git ref) |
| `cancel_build` | Cancel an in-flight build |
| `list_build_runs` | Recent runs for a workflow |
| `get_build_run` | Status snapshot |
| `get_build_actions` | Per-action progress (build/test/archive) |
| `wait_for_build` | Poll until terminal status or timeout |
| `get_build_issues` | Errors / warnings |
| `list_action_artifacts` | Logs / result bundles metadata |
| `get_build_logs` | Download log text for an action |
| `get_test_results` | Tests for an action |
| `get_test_failures` | Failed tests across a run |
| `list_beta_feedback_crashes` | Privacy-minimized TestFlight crash-report summaries |
| `get_beta_crash_log` | Raw log for one selected TestFlight crash report |
### Typical agent flow
1. `list_products` → `list_workflows`
2. `start_build` **or** `list_build_runs` for an existing run
3. `wait_for_build` / `get_build_run` + `get_build_actions`
4. On failure: `get_build_issues`, `get_test_failures`, `get_build_logs`
### TestFlight crash-report flow
1. `list_apps` (optionally filter by bundle id)
2. `list_beta_feedback_crashes` with the selected `appId`
3. `get_beta_crash_log` for a specific `submissionId`
The listing intentionally omits tester email and the free-form feedback
comment. Raw crash logs are bounded by `maxChars` (80,000 by default) and
should be retrieved only when needed for diagnosis.
## Prerequisites
- Node.js 20+
- Apple Developer account with Xcode Cloud
- App Store Connect API key (Developer or App Manager is usually enough)
### Create an API key
1. [App Store Connect](https://appstoreconnect.apple.com) → **Users and Access** → **Integrations** → **App Store Connect API**
2. Generate a key → download the `.p8` (once only)
3. Note **Key ID** and **Issuer ID**
## Setup
```bash
cd ~/Desktop/projects/xcode-cloud-mcp
npm install
npm run build
```
Copy env template and fill in values:
```bash
cp .env.example .env
```
```env
APP_STORE_KEY_ID=YOUR_KEY_ID
APP_STORE_ISSUER_ID=YOUR_ISSUER_UUID
APP_STORE_PRIVATE_KEY_PATH=/Users/you/path/to/AuthKey_XXXXXXXXXX.p8
```
Prefer `APP_STORE_PRIVATE_KEY_PATH` over pasting PEM into config.
## Run locally (smoke)
```bash
export $(grep -v '^#' .env | xargs)
npm run dev
# process waits on stdio — that's correct for MCP
```
Quick API check without MCP (optional):
```bash
node --import tsx -e '
import { loadConfig } from "./src/config.ts";
import { AppStoreAuth } from "./src/auth.ts";
import { AppStoreConnectClient } from "./src/client.ts";
const c = loadConfig();
const client = new AppStoreConnectClient(c, new AppStoreAuth(c));
const doc = await client.get("/v1/ciProducts", { limit: 5 });
console.log(JSON.stringify(doc, null, 2));
'
```
## Grok config
Add to `~/.grok/config.toml` (user scope) or project `.grok/config.toml`:
```toml
[mcp_servers.xcode-cloud]
command = "node"
args = ["/Users/deepanshu/Desktop/projects/xcode-cloud-mcp/dist/index.js"]
startup_timeout_sec = 30
env = {
APP_STORE_KEY_ID = "YOUR_KEY_ID",
APP_STORE_ISSUER_ID = "YOUR_ISSUER_UUID",
APP_STORE_PRIVATE_KEY_PATH = "/Users/deepanshu/Desktop/projects/AuthKey_XXXXXXXXXX.p8"
}
```
Or via CLI:
```bash
grok mcp add xcode-cloud \
-e APP_STORE_KEY_ID=YOUR_KEY_ID \
-e APP_STORE_ISSUER_ID=YOUR_ISSUER_UUID \
-e APP_STORE_PRIVATE_KEY_PATH=/Users/deepanshu/Desktop/projects/AuthKey_XXXXXXXXXX.p8 \
-- node /Users/deepanshu/Desktop/projects/xcode-cloud-mcp/dist/index.js
```
Then `/mcps` → confirm tools, or:
```bash
grok mcp doctor xcode-cloud
```
## Cursor
`~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"xcode-cloud": {
"command": "node",
"args": ["/Users/deepanshu/Desktop/projects/xcode-cloud-mcp/dist/index.js"],
"env": {
"APP_STORE_KEY_ID": "YOUR_KEY_ID",
"APP_STORE_ISSUER_ID": "YOUR_ISSUER_UUID",
"APP_STORE_PRIVATE_KEY_PATH": "/Users/deepanshu/Desktop/projects/AuthKey_XXXXXXXXXX.p8"
}
}
}
}
```
## Security
- Never commit `.p8`, `.env`, or keys
- Restrict key role to the minimum needed
- Rotate keys if they leak
- This server speaks **only** to `api.appstoreconnect.apple.com` (override only via `ASC_API_BASE_URL`)
## Project layout
```
src/
index.ts # stdio entry
config.ts # env loading
auth.ts # JWT ES256
client.ts # ASC REST client
types.ts # CI resource shapes
format.ts # tool response helpers
tools/register.ts # MCP tools
```
## License
Private — for your use only.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues