appstore-api-mcp 1.11.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.example CHANGED
@@ -33,3 +33,5 @@ ASC_PRIVATE_KEY_PATH=/absolute/path/to/AuthKey_XXXXXXXXXX.p8
33
33
  # APPSTORE_MCP_ALLOW_EXTERNAL_TESTFLIGHT=false # block submit_beta_review
34
34
  # Optional: where metadata snapshots are written (default ~/.appstore-api-mcp/snapshots)
35
35
  # APPSTORE_MCP_SNAPSHOT_DIR=
36
+ # Optional: auto-snapshot an app's text metadata before the first listing edit (a safety net for "revert")
37
+ # APPSTORE_MCP_AUTO_SNAPSHOT=true
package/CHANGELOG.md CHANGED
@@ -4,6 +4,103 @@ All notable changes to this project are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/) and the project uses
5
5
  [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [1.15.0] - 2026-10-05
8
+
9
+ ### Added — submission flow (from 5 field reports of real submissions)
10
+ - **Build ↔ version ↔ review loop** (the biggest gap; previously `raw_request`):
11
+ `attach_build_to_version`, `get_app_store_version` (incl. attached build),
12
+ `update_app_store_version` (`usesIdfa`, releaseType…), `update_build`
13
+ (per-build `usesNonExemptEncryption` export compliance; `expired`),
14
+ `expire_build`, `get_build`, `wait_for_build_processing`, `next_build_number`.
15
+ - **Review submissions:** `list_review_submissions`, `get_review_submission`,
16
+ `add_review_submission_item`, `cancel_review_submission` (surfaces Apple's
17
+ "can't cancel empty/non-cancellable" cleanly), `get_app_store_review_detail`.
18
+ - **TestFlight:** `get_beta_review_status`, `set_beta_build_notes` ("What to Test").
19
+ - **Screenshots:** `find_incomplete_screenshots` (assets stuck != COMPLETE that
20
+ silently block submission), `reorder_screenshots`, `replace_screenshots` (bulk
21
+ delete + re-upload in order).
22
+ - **Subscriptions:** `list_subscription_groups`, `list_subscriptions`,
23
+ `list_subscription_offers` (**flags overlapping offer date ranges** — the
24
+ sandbox `countMismatch` cause), `create_subscription_group`,
25
+ `create_subscription`, `create_in_app_purchase`.
26
+ - **Territories:** `list_app_territories` (compact appAvailabilityV2 summary).
27
+ - **Diagnostics & orchestrators:** `diagnose_submission` (the exact blockers
28
+ behind submit's opaque 409), `swap_build` (wait→cancel→attach), `release_pipeline`
29
+ (attach→diagnose→submit), `bulk_upsert_localizations` (version + app-info, all
30
+ locales, creates missing ones, whitespace preserved verbatim).
31
+
32
+ ### Fixed
33
+ - `list_builds` ignored `limit` (it paged via `getAll`). Now returns a single
34
+ page capped to `limit`; added `version` / `processingState` filters.
35
+ - `submit_for_review` now pre-flights and, on failure, surfaces the real blockers
36
+ (no attached build, build not VALID, unset export compliance / IDFA, assets
37
+ still processing) instead of Apple's opaque `409 ENTITY_STATE_INVALID`; cleans
38
+ up the half-created empty submission.
39
+ - `create_app_info_localization` upserts instead of failing "already exists"
40
+ (Apple auto-creates the name/subtitle row with the version localization).
41
+ - `release_readiness_check` now verifies a build is attached + VALID, checks
42
+ description/screenshots across **all** locales, and flags assets still processing.
43
+ - `upload_screenshot` returns a compact result (dropped ~700 tokens of signed
44
+ upload URLs per image).
45
+ - `list_app_store_version_localizations` gains `omitLongFields` (return lengths
46
+ instead of full description/promotionalText/whatsNew).
47
+ - `list_analytics_reports` accepts `appId` (uses the app's newest report request)
48
+ instead of crashing on a missing `requestId`.
49
+
50
+ ## [1.14.0] - 2026-10-05
51
+
52
+ ### Added
53
+ - **PPP regional pricing for IAPs & subscriptions.** Bulk-set in-app purchase and
54
+ subscription prices across ~174 territories from a US base price, scaled by
55
+ purchasing-power parity. Logic + the 175-country dataset are ported from the
56
+ MIT-licensed [appstore-ppp-prices](https://github.com/duceum/appstore-ppp-pricing-agent-skill).
57
+ - `list_purchasable_products` — IAPs (v2) + subscriptions with current US prices.
58
+ - `preview_ppp_prices` — read-only dry run: per-territory target table, scaled in
59
+ local currency via Apple's equalizations and snapped to each territory's real
60
+ price grid (ratio-preserving floors of 0.99 / 0.49).
61
+ - `apply_ppp_prices` — applies the prices (IAP = one atomic price-schedule POST;
62
+ subscriptions = per-territory with `preserveCurrentPrice` + future `startDate`,
63
+ clearing pending prices first). **Irreversible**: requires `confirm:true`, is
64
+ gated by `APPSTORE_MCP_ALLOW_PRICE_CHANGES` and read-only mode, and the docs
65
+ mandate previewing first.
66
+ - Per-tier coefficients use embedded defaults; override any tier (premium,
67
+ high_income, upper_middle, lower_middle, emerging) by passing a `coefficients`
68
+ map — the agent reasons about elasticity, no server-side LLM or extra key.
69
+ - New `client.getAllPages()` helper returns side-loaded `included` resources.
70
+
71
+ ## [1.13.0] - 2026-06-13
72
+
73
+ ### Added
74
+ - **iOS CI/CD → TestFlight bootstrap.** Turn a new iOS app into a fastlane +
75
+ GitHub Actions → TestFlight pipeline in one call:
76
+ - `ensure_asc_app` — find the App Store Connect app record for a bundle id
77
+ (find-only; the public API has no `POST /apps`, so it returns guidance when
78
+ the record doesn't exist yet).
79
+ - `bootstrap_ios_cicd` — scaffold the 7 pipeline files (Gemfile, fastlane
80
+ Appfile/Fastfile/.gitignore/SETUP.md, two GitHub Actions workflows) into the
81
+ app's repo, auto-detecting `appDir`/`bundleId`/`teamId`/`scheme`/`target`
82
+ from the `.xcodeproj`. Commits to a branch or opens a PR. Xcode automatic
83
+ ("cloud") signing via `-allowProvisioningUpdates` — no `match` repo.
84
+ - `set_repo_ci_secrets` — push `ASC_KEY_ID` / `ASC_ISSUER_ID` / `ASC_KEY_P8`
85
+ (base64) to the repo's Actions secrets. The API key is read from this
86
+ server's own config, never passed as an argument or returned in output;
87
+ values are piped to `gh secret set` over stdin.
88
+ - `bootstrap_testflight` — one-call orchestrator running all three.
89
+ - GitHub operations shell out to the `gh` CLI (no extra token, no new
90
+ dependency). The bootstrap writers respect read-only / safe mode.
91
+
92
+ ## [1.12.0] - 2026-06-03
93
+
94
+ ### Added
95
+ - **App preview videos:** `list_app_preview_sets`, `list_app_previews`,
96
+ `get_app_preview` (incl. `videoUrl`), `create_app_preview_set`,
97
+ `upload_app_preview`, `delete_app_preview`. Snapshots can back up previews
98
+ (`includePreviews:true`) and **`restore_app_previews`** re-uploads them.
99
+ - **Auto-snapshot safety net:** `APPSTORE_MCP_AUTO_SNAPSHOT=true` makes the server
100
+ save a text-metadata snapshot of an app before the **first** listing edit of the
101
+ session — so "revert" works even if you forgot to snapshot. Plus a server-side
102
+ habit nudge to snapshot before bulk/risky edits, and a revert recipe.
103
+
7
104
  ## [1.11.0] - 2026-06-03
8
105
 
9
106
  ### Added
package/README.md CHANGED
@@ -261,11 +261,12 @@ Full parameter reference: **[docs/TOOLS.md](docs/TOOLS.md)**.
261
261
  | `list_screenshot_sets` / `create_screenshot_set` | Manage per-device screenshot sets |
262
262
  | `list_screenshots` / `upload_screenshot` / `delete_screenshot` | Manage screenshots (upload handles the full reserve→upload→commit flow) |
263
263
  | `get_screenshot` | 👁️ Fetch a live screenshot **as an image the agent can see** — review/compare what's on a listing |
264
+ | `list_app_preview_sets` / `list_app_previews` / `get_app_preview` / `create_app_preview_set` / `upload_app_preview` / `delete_app_preview` | 🎬 **App preview videos** — list, inspect (incl. `videoUrl`), upload, delete |
264
265
  | `audit_apps` | 🩺 **Fleet ASO audit** — scan all apps for missing subtitle/keywords/description, under-used keyword field, single-locale listings, missing screenshots. Read-only |
265
266
  | `apps_review_status` | 🗂️ **Fleet review board** — every app's current version + state (waiting / in-review / rejected / ready) in one call |
266
267
  | `submit_for_review` / `release_version` / `set_phased_release` | 🚀 Submit a version to Apple review (full flow), release an approved build, and control phased rollout |
267
268
  | `doctor` | 🩺 Diagnose setup: Node, creds, key works, role capabilities, vendor number, Mac build tools, write mode |
268
- | `snapshot_app_metadata` / `diff_app_metadata_snapshot` / `restore_app_metadata` / `restore_screenshots` | 💾 Back up / compare / restore an app's metadata. Text is always saved; pass `includeScreenshots:true` to also download the images so **deleted screenshots can be re-uploaded** |
269
+ | `snapshot_app_metadata` / `diff_app_metadata_snapshot` / `restore_app_metadata` / `restore_screenshots` / `restore_app_previews` | 💾 Back up / compare / restore an app's metadata. Text always saved; `includeScreenshots:true` and `includePreviews:true` also download the images/videos so **deleted screenshots and previews can be re-uploaded**. Set `APPSTORE_MCP_AUTO_SNAPSHOT=true` to auto-snapshot text before the first edit |
269
270
  | `release_readiness_check` | ✅ One-call **go/no-go report** — build, metadata, ASO, screenshots, compliance, TestFlight, reviews |
270
271
  | `aso_opportunity_report` / `portfolio_growth_report` | 📈 Rank the easiest **ASO wins** across all apps; portfolio snapshot of units sold per app |
271
272
  | `add_build_to_beta_group` / `submit_beta_review` | ✈️ Assign a build to a TestFlight group; submit for beta review |
@@ -421,6 +422,7 @@ More: **[docs/SECURITY.md](docs/SECURITY.md)**.
421
422
  | `401`/`403` errors | Wrong issuer/key id, wrong `.p8`, or the key's role lacks permission. |
422
423
  | `409` on metadata update | The version isn't in an editable state — create/select a `PREPARE_FOR_SUBMISSION` version. |
423
424
  | `npx` can't find the package | Ensure Node ≥ 18 and that the package name is published/correct. |
425
+ | `SyntaxError: Unexpected token '&&='` or `npm … not to run on Node.js v14` at startup | `npx` is using an **old Node** still on your `PATH`, not your Node ≥ 18. Check with `npx node --version`. Fix: point `command` at an absolute modern npx (e.g. `/opt/homebrew/opt/node@22/bin/npx`) or add a `PATH` to the server's `env`. See [SETUP.md → multiple Node versions](docs/SETUP.md#multiple-node-versions-the-most-common-startup-crash). |
424
426
  | Tools don't appear | MCP servers load at client startup — restart the client / start a new session. |
425
427
 
426
428
  ---
package/docs/RECIPES.md CHANGED
@@ -134,7 +134,22 @@ Uses: `get_app_store_version_localization`, `list_app_info_localizations`,
134
134
  `update_app_store_version_localization` (with `dryRun`),
135
135
  `bulk_update_version_localizations`, `aso_opportunity_report`.
136
136
 
137
- ## 7. Build & ship (Mac only)
137
+ ## 7. Safety net — snapshot before risky edits, then revert if needed
138
+
139
+ ```text
140
+ Before we change anything on AppName, take a full snapshot (include screenshots
141
+ and previews). Then make the edits I describe. If I say "revert", restore the
142
+ metadata, screenshots, and previews from that snapshot.
143
+ ```
144
+
145
+ Uses: `snapshot_app_metadata` (with `includeScreenshots:true` / `includePreviews:true`),
146
+ then `restore_app_metadata` + `restore_screenshots` + `restore_app_previews`.
147
+
148
+ > Or set `APPSTORE_MCP_AUTO_SNAPSHOT=true` so the server auto-snapshots text
149
+ > metadata before the first edit — then "revert" works even if you forgot to
150
+ > snapshot. (Screenshots/previews still need the explicit include flags.)
151
+
152
+ ## 8. Build & ship (Mac only)
138
153
 
139
154
  ```text
140
155
  Bump AppName's build number, archive it, and upload the new build to App Store
package/docs/SETUP.md CHANGED
@@ -112,3 +112,58 @@ Then set `ASC_PRIVATE_KEY_BASE64` instead of `ASC_PRIVATE_KEY_PATH`.
112
112
  Create or select a version in `PREPARE_FOR_SUBMISSION` state.
113
113
  - **Token/clock errors** — JWTs are time-based; make sure your system clock is correct.
114
114
  - **`npx` fails to resolve the package** — verify Node ≥ 18 and the published package name.
115
+
116
+ ### Multiple Node versions (the most common startup crash)
117
+
118
+ > **Symptom:** the server fails to connect, and its logs show one of:
119
+ > ```
120
+ > npm v10.x is known not to run on Node.js v14.x
121
+ > SyntaxError: Unexpected token '&&='
122
+ > ```
123
+
124
+ Having Node ≥ 18 *installed* isn't enough — `npx` has to actually **run on it**.
125
+ If you have more than one Node on your machine (nvm, Homebrew, system Node, Xcode's
126
+ bundled Node…), `npx` may resolve to an **old** one earlier on your `PATH`. Modern
127
+ npm then crashes before the server ever reads your credentials, so it looks like an
128
+ auth/config problem when it isn't.
129
+
130
+ **Diagnose** — ask which Node `npx` actually uses (not just `node --version`):
131
+
132
+ ```bash
133
+ npx node --version # if this prints v14.x / anything < 18, that's the bug
134
+ which -a node npx # shows every node/npx on your PATH, in resolution order
135
+ ```
136
+
137
+ **Fix — option A (recommended): pin an absolute, modern npx as the `command`.**
138
+ Find it with `which npx` under your good Node (e.g. `/opt/homebrew/opt/node@22/bin/npx`),
139
+ then use that full path instead of bare `npx`:
140
+
141
+ ```json
142
+ {
143
+ "mcpServers": {
144
+ "appstore-api": {
145
+ "command": "/opt/homebrew/opt/node@22/bin/npx",
146
+ "args": ["-y", "appstore-api-mcp"],
147
+ "env": {
148
+ "ASC_KEY_ID": "YOUR_KEY_ID",
149
+ "ASC_ISSUER_ID": "YOUR_ISSUER_ID",
150
+ "ASC_PRIVATE_KEY_PATH": "/Users/you/.appstoreconnect/AuthKey_XXXXXXXXXX.p8"
151
+ }
152
+ }
153
+ }
154
+ }
155
+ ```
156
+
157
+ **Fix — option B: add a `PATH` to the server's `env`** so its child process resolves
158
+ the right Node first (handy when `command` must stay as bare `npx`):
159
+
160
+ ```json
161
+ "env": {
162
+ "ASC_KEY_ID": "YOUR_KEY_ID",
163
+ "ASC_ISSUER_ID": "YOUR_ISSUER_ID",
164
+ "ASC_PRIVATE_KEY_PATH": "/Users/you/.appstoreconnect/AuthKey_XXXXXXXXXX.p8",
165
+ "PATH": "/opt/homebrew/opt/node@22/bin:/opt/homebrew/bin:/usr/bin:/bin"
166
+ }
167
+ ```
168
+
169
+ After either fix, restart the client (or start a new session) so the server relaunches.
package/docs/TOOLS.md CHANGED
@@ -352,6 +352,46 @@ The agent does the translating; this writes them.
352
352
  Creates a new price schedule from a price point. **Changes live pricing — confirm first.**
353
353
  - `appId` **(required)**, `baseTerritory` **(required)**, `pricePointId` **(required)**, `startDate` (YYYY-MM-DD, optional)
354
354
 
355
+ ## PPP regional pricing (IAP & subscriptions)
356
+
357
+ Bulk-set **in-app purchase / subscription** prices across ~174 territories from a
358
+ US base price, scaled by purchasing-power-parity (PPP). Distinct from
359
+ `set_app_price` (which sets the paid-*app* price). The target price is scaled in
360
+ each territory's **local currency** (via Apple's equalizations) and snapped to a
361
+ real price-point on that territory's grid — not a dollar figure equalized to a
362
+ coarse subset. Ported from the MIT-licensed
363
+ [appstore-ppp-prices](https://github.com/duceum/appstore-ppp-pricing-agent-skill).
364
+
365
+ > **Always `preview_ppp_prices` → show the user the table → `apply_ppp_prices`.**
366
+ > Prices are customer-facing across ~174 territories and **cannot be undone.**
367
+
368
+ Coefficients come from an embedded 175-country table (tiers: `premium`,
369
+ `high_income`, `upper_middle`, `lower_middle`, `emerging`; USA is always the 1.00
370
+ base). There is **no server-side LLM** — reason about the app's elasticity (games =
371
+ high → discount poorer markets more; AI/productivity = low → discount less) and
372
+ pass per-tier overrides. Floors: 0.99 (premium/high_income), 0.49 (others), with
373
+ ratio preservation across multiple products.
374
+
375
+ ### list_purchasable_products
376
+ Every IAP + subscription with its current US price, tagged `IAP`/`SUB`.
377
+ - `appId` **(required)** — use the returned `productId` with the tools below.
378
+
379
+ ### preview_ppp_prices
380
+ Dry run (read-only): the full per-territory price table without writing.
381
+ - `appId`, `productId` **(required)**
382
+ - `usPrice` (override the US base), `coefficients` (per-tier overrides, each 0.1–2.0),
383
+ `exclude` (territory codes, e.g. `["RUS","BLR"]`)
384
+
385
+ ### apply_ppp_prices
386
+ Applies the prices. **Irreversible.** Requires `confirm:true`; blocked in read-only
387
+ mode and when `APPSTORE_MCP_ALLOW_PRICE_CHANGES=false`.
388
+ - `appId`, `productId`, `confirm:true` **(required)**
389
+ - `usPrice`, `coefficients`, `exclude` (same as preview)
390
+ - `preserveCurrentPrice` (subscriptions; default **true** — existing subscribers keep
391
+ their price), `startDate` (subscriptions; `YYYY-MM-DD`, default 2 days out)
392
+ - IAPs are set in one atomic request; subscriptions per-territory (pending price
393
+ changes are cleared first).
394
+
355
395
  ## Product Page Optimization
356
396
 
357
397
  ### list_app_store_version_experiments
@@ -419,6 +459,29 @@ Re-upload screenshots from a snapshot that was taken with `includeScreenshots:tr
419
459
  - `replace` — delete the set's current screenshots first (true restore)
420
460
  - `dryRun` — preview what would be uploaded
421
461
 
462
+ ### restore_app_previews
463
+ Re-upload app preview **videos** from a snapshot taken with `includePreviews:true`.
464
+ - `appId` **(required)**, `snapshotFile` **(required)**, `replace`, `dryRun`
465
+
466
+ > **Auto-snapshot:** set `APPSTORE_MCP_AUTO_SNAPSHOT=true` and the server saves a
467
+ > text-metadata snapshot of an app before the **first** listing edit of the session
468
+ > — a built-in safety net so you can always revert. (Screenshots/previews still
469
+ > need `includeScreenshots` / `includePreviews` to be restorable.)
470
+
471
+ ## App previews (video)
472
+
473
+ ### list_app_preview_sets / list_app_previews / get_app_preview
474
+ - sets: `localizationId`; previews: `previewSetId`; one preview: `previewId` (includes `videoUrl` when available).
475
+
476
+ ### create_app_preview_set
477
+ - `localizationId` **(required)**, `previewType` **(required)** (e.g. IPHONE_67).
478
+
479
+ ### upload_app_preview
480
+ - `previewSetId` **(required)**, `filePath` **(required)** (.mp4/.mov), `fileName`, `previewFrameTimeCode` (e.g. `00:00:05:00`).
481
+
482
+ ### delete_app_preview
483
+ - `previewId` **(required)**.
484
+
422
485
  ## Safe mode (guardrails)
423
486
 
424
487
  Set these env vars to enforce limits at the **server** (blocked calls return a
@@ -428,7 +491,7 @@ clear error):
428
491
  | --- | --- |
429
492
  | `APPSTORE_MCP_READ_ONLY=true` | block all writes |
430
493
  | `APPSTORE_MCP_ALLOW_RELEASE=false` | block `release_version` / `set_phased_release` |
431
- | `APPSTORE_MCP_ALLOW_PRICE_CHANGES=false` | block `set_app_price` |
494
+ | `APPSTORE_MCP_ALLOW_PRICE_CHANGES=false` | block `set_app_price` / `apply_ppp_prices` |
432
495
  | `APPSTORE_MCP_ALLOW_REVIEW_REPLIES=false` | block public review replies |
433
496
  | `APPSTORE_MCP_ALLOW_EXTERNAL_TESTFLIGHT=false` | block `submit_beta_review` |
434
497
 
@@ -454,6 +517,46 @@ clear error):
454
517
  > See **[RECIPES.md](RECIPES.md)** for copy-paste prompts that chain these into
455
518
  > workflows (prepare-version, release-train-with-gates, review→notes, portfolio audit).
456
519
 
520
+ ## Submission flow (build ↔ version ↔ review)
521
+
522
+ The steps a real submission needs beyond `submit_for_review` — attaching a build,
523
+ fixing the silent blockers behind Apple's opaque `409`, and swapping a build that's
524
+ already in review.
525
+
526
+ ### Build & version
527
+ - `attach_build_to_version` — `versionId` + (`buildId` or `buildNumber`+`appId`). The mandatory pre-submit link.
528
+ - `get_app_store_version(versionId)` — state, releaseType, `usesIdfa`, and the **attached build** (or null).
529
+ - `update_app_store_version(versionId, …)` — `usesIdfa`, `releaseType`, `earliestReleaseDate`, `versionString`, `downloadable`.
530
+ - `update_build` — `usesNonExemptEncryption` (export compliance, **per-build, doesn't carry over**), `expired`. By `buildId` or `buildNumber`+`appId`.
531
+ - `expire_build`, `get_build`, `next_build_number(appId)`.
532
+ - `wait_for_build_processing(appId, buildNumber, timeoutSeconds?)` — polls to VALID/INVALID/FAILED.
533
+
534
+ ### Review submissions
535
+ - `list_review_submissions(appId, state?)`, `get_review_submission(id)`.
536
+ - `add_review_submission_item(reviewSubmissionId, versionId)`.
537
+ - `cancel_review_submission` — by `submissionId` or `appId` (current in-flight); optional `waitSeconds`. Surfaces Apple's refusal on empty/non-cancellable submissions.
538
+ - `get_app_store_review_detail(versionId)` — contact info, demo account, notes.
539
+
540
+ ### TestFlight
541
+ - `get_beta_review_status` (build's `betaReviewState`), `set_beta_build_notes(whatsNew, locale?)` ("What to Test", upsert).
542
+
543
+ ### Screenshots
544
+ - `find_incomplete_screenshots(versionId)` — assets whose `assetDeliveryState != COMPLETE` (silent submit blockers).
545
+ - `reorder_screenshots(screenshotSetId, orderedIds)` — new uploads append last, so re-order after re-upload.
546
+ - `replace_screenshots(screenshotSetId, filePaths[])` — delete all + upload in order, one call.
547
+
548
+ ### Subscriptions
549
+ - `list_subscription_groups(appId)`, `list_subscriptions(groupId)`.
550
+ - `list_subscription_offers(subscriptionId)` — **flags overlapping offer date ranges** (the sandbox `countMismatch` cause).
551
+ - `create_subscription_group`, `create_subscription`, `create_in_app_purchase`. (First-time products still need ticking for review on the version page — the API can't submit them.)
552
+
553
+ ### Diagnostics & orchestrators
554
+ - `diagnose_submission(appId, versionId)` — read-only: the exact blockers behind submit's 409.
555
+ - `swap_build(appId, versionId, buildNumber|buildId)` — wait for processing → cancel in-flight review → attach.
556
+ - `release_pipeline(appId, versionId, buildNumber?, submit?)` — attach → diagnose → (if clean and `submit:true`) submit.
557
+ - `bulk_upsert_localizations(versionId, { locale: {…} })` — version + app-info fields across all locales, creates missing ones; `dryRun` supported.
558
+ - `list_app_territories(appId)` — compact availability summary.
559
+
457
560
  ## Build & ship (macOS + Xcode)
458
561
 
459
562
  These run local Xcode tooling, so they only work on a Mac with Xcode installed.
@@ -480,6 +583,49 @@ in `list_builds` and can be submitted with `submit_for_review`.
480
583
 
481
584
  ---
482
585
 
586
+ ## iOS CI/CD → TestFlight bootstrap
587
+
588
+ Turn a new iOS app into a fastlane + GitHub Actions → TestFlight pipeline in one
589
+ call. Signing is Xcode automatic ("cloud") signing via `-allowProvisioningUpdates`
590
+ — no `match` repo. The GitHub side shells out to the `gh` CLI (install + `gh auth
591
+ login` required; reuses your existing auth, no extra token). The App Store Connect
592
+ API key lives only in this server's environment and is exposed only through these
593
+ high-level actions — never as an argument, never in output.
594
+
595
+ > Create the App Store Connect API key **once at the TEAM level** (App Store
596
+ > Connect → Users and Access → Integrations) so the same three secret values work
597
+ > for every app/repo.
598
+
599
+ ### ensure_asc_app
600
+ Find the App Store Connect app record for a bundle id. **Find-only:** the public
601
+ API cannot create app records (there is no `POST /apps`), so `created` is always
602
+ `false`. If missing, returns `found:false` plus guidance (register the bundle id,
603
+ create the record once in the web UI).
604
+ - `bundleId` **(required)**, `name`, `sku`, `platform`, `primaryLocale`
605
+ - **Returns:** `{ app_id, created, found, bundleId, name }`
606
+
607
+ ### bootstrap_ios_cicd
608
+ Scaffold the 7 pipeline files (`Gemfile`, `fastlane/{Appfile,Fastfile,.gitignore,
609
+ SETUP.md}`, `.github/workflows/{ios-ci.yml,ios-testflight.yml}`). Auto-detects
610
+ `appDir`/`bundleId`/`teamId`/`scheme`/`target` from the repo's `.xcodeproj`.
611
+ - `repoDir` (local clone, default `.`), `repo` (`owner/name` for the PR), `owner`
612
+ - overrides: `appDir`, `bundleId`, `teamId`, `scheme`, `target`
613
+ - `mode`: `pr` (default — branch + push + open PR), `branch`, `commit`, `files`
614
+ - `branch`, `baseBranch`, `dryRun` (preview detected values + rendered files)
615
+
616
+ ### set_repo_ci_secrets
617
+ Push `ASC_KEY_ID`, `ASC_ISSUER_ID`, `ASC_KEY_P8` (base64) to the repo's Actions
618
+ secrets via `gh secret set` (values piped over stdin; `gh` does the libsodium
619
+ sealed-box encryption). Key material is read from this server's env, never echoed.
620
+ - `repo` (`owner/name`), `owner`, `repoDir` (derive `owner/name` from `origin`)
621
+
622
+ ### bootstrap_testflight
623
+ One-call orchestrator: `ensure_asc_app` → `bootstrap_ios_cicd` → `set_repo_ci_secrets`.
624
+ If the app record doesn't exist yet, it still scaffolds + sets secrets and tells
625
+ you to create the record in the web UI. Same options as `bootstrap_ios_cicd`.
626
+
627
+ ---
628
+
483
629
  ## Rate limits
484
630
 
485
631
  App Store Connect allows ~3,500 requests/hour and returns `429` when exceeded.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appstore-api-mcp",
3
- "version": "1.11.0",
3
+ "version": "1.15.0",
4
4
  "description": "MCP server for Apple App Store Connect — edit listings (keywords, descriptions, titles, screenshots), track analytics (downloads, proceeds, subscriptions, retention), run a fleet-wide ASO audit, preview changes with dry-run, and reach the full API. Works with any MCP client (Claude, Codex, Cursor, Windsurf, VS Code, Zed, Gemini CLI, Antigravity, Amazon Q, Goose, and more).",
5
5
  "type": "module",
6
6
  "bin": {