appstore-api-mcp 1.12.0 → 1.16.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/CHANGELOG.md +111 -0
- package/README.md +1 -0
- package/docs/SETUP.md +55 -0
- package/docs/TOOLS.md +166 -2
- package/package.json +1 -1
- package/src/cicd.js +481 -0
- package/src/client.js +19 -0
- package/src/guardrails.js +49 -1
- package/src/index.js +2824 -87
- package/src/ppp-countries.csv +176 -0
- package/src/ppp.js +183 -0
- package/src/subscriptions.js +45 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,117 @@ 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.16.0] - 2026-10-07
|
|
8
|
+
|
|
9
|
+
### Added — App Store marketing & growth (recent ASC features)
|
|
10
|
+
- **Custom Product Pages:** `list_custom_product_pages`, `get_custom_product_page`,
|
|
11
|
+
`create_custom_product_page`, `list/create_custom_product_page_version(s)`,
|
|
12
|
+
`list/create/update_custom_product_page_localization(s)`.
|
|
13
|
+
- **Product Page Optimization (A/B, appStoreVersionExperiments v2):**
|
|
14
|
+
`list_ppo_experiments`, `get_ppo_experiment`, `create_ppo_experiment`,
|
|
15
|
+
`start_ppo_experiment`, `stop_ppo_experiment`, `list/create_experiment_treatment(s)`,
|
|
16
|
+
`create_experiment_treatment_localization`. (Experiment *results* are UI-only —
|
|
17
|
+
the API exposes no metrics endpoint.)
|
|
18
|
+
- **Asset Library:** `get_asset_library`, `list_asset_library_images/videos`,
|
|
19
|
+
`upload_asset_library_image`, `assign_asset_placement` (reuse an asset on a
|
|
20
|
+
product page / event / treatment), `delete_asset_placement`.
|
|
21
|
+
- **Webhooks** (WWDC25): `list/get/create/update/delete_webhook`,
|
|
22
|
+
`list_webhook_deliveries`, `ping_webhook` — event callbacks for version/build/
|
|
23
|
+
TestFlight-feedback state, instead of polling.
|
|
24
|
+
- **Subscription offer codes** (promo-code replacement): `list_subscription_offer_codes`,
|
|
25
|
+
`create_subscription_offer_code`, `create_offer_code_one_time_use`,
|
|
26
|
+
`create_offer_code_custom`, `list_offer_code_codes`.
|
|
27
|
+
- **In-App Events:** `list/get/create_app_event`, `create_app_event_localization`.
|
|
28
|
+
- **Game Center challenges:** `list_game_center_challenges`, `get_game_center_challenge`.
|
|
29
|
+
- **Screenshots/previews** now attach to Custom Product Page and PPO treatment
|
|
30
|
+
localizations too: `create_screenshot_set` / `create_app_preview_set` accept
|
|
31
|
+
`customProductPageLocalizationId` / `treatmentLocalizationId`.
|
|
32
|
+
|
|
33
|
+
## [1.15.0] - 2026-10-05
|
|
34
|
+
|
|
35
|
+
### Added — submission flow (from 5 field reports of real submissions)
|
|
36
|
+
- **Build ↔ version ↔ review loop** (the biggest gap; previously `raw_request`):
|
|
37
|
+
`attach_build_to_version`, `get_app_store_version` (incl. attached build),
|
|
38
|
+
`update_app_store_version` (`usesIdfa`, releaseType…), `update_build`
|
|
39
|
+
(per-build `usesNonExemptEncryption` export compliance; `expired`),
|
|
40
|
+
`expire_build`, `get_build`, `wait_for_build_processing`, `next_build_number`.
|
|
41
|
+
- **Review submissions:** `list_review_submissions`, `get_review_submission`,
|
|
42
|
+
`add_review_submission_item`, `cancel_review_submission` (surfaces Apple's
|
|
43
|
+
"can't cancel empty/non-cancellable" cleanly), `get_app_store_review_detail`.
|
|
44
|
+
- **TestFlight:** `get_beta_review_status`, `set_beta_build_notes` ("What to Test").
|
|
45
|
+
- **Screenshots:** `find_incomplete_screenshots` (assets stuck != COMPLETE that
|
|
46
|
+
silently block submission), `reorder_screenshots`, `replace_screenshots` (bulk
|
|
47
|
+
delete + re-upload in order).
|
|
48
|
+
- **Subscriptions:** `list_subscription_groups`, `list_subscriptions`,
|
|
49
|
+
`list_subscription_offers` (**flags overlapping offer date ranges** — the
|
|
50
|
+
sandbox `countMismatch` cause), `create_subscription_group`,
|
|
51
|
+
`create_subscription`, `create_in_app_purchase`.
|
|
52
|
+
- **Territories:** `list_app_territories` (compact appAvailabilityV2 summary).
|
|
53
|
+
- **Diagnostics & orchestrators:** `diagnose_submission` (the exact blockers
|
|
54
|
+
behind submit's opaque 409), `swap_build` (wait→cancel→attach), `release_pipeline`
|
|
55
|
+
(attach→diagnose→submit), `bulk_upsert_localizations` (version + app-info, all
|
|
56
|
+
locales, creates missing ones, whitespace preserved verbatim).
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
- `list_builds` ignored `limit` (it paged via `getAll`). Now returns a single
|
|
60
|
+
page capped to `limit`; added `version` / `processingState` filters.
|
|
61
|
+
- `submit_for_review` now pre-flights and, on failure, surfaces the real blockers
|
|
62
|
+
(no attached build, build not VALID, unset export compliance / IDFA, assets
|
|
63
|
+
still processing) instead of Apple's opaque `409 ENTITY_STATE_INVALID`; cleans
|
|
64
|
+
up the half-created empty submission.
|
|
65
|
+
- `create_app_info_localization` upserts instead of failing "already exists"
|
|
66
|
+
(Apple auto-creates the name/subtitle row with the version localization).
|
|
67
|
+
- `release_readiness_check` now verifies a build is attached + VALID, checks
|
|
68
|
+
description/screenshots across **all** locales, and flags assets still processing.
|
|
69
|
+
- `upload_screenshot` returns a compact result (dropped ~700 tokens of signed
|
|
70
|
+
upload URLs per image).
|
|
71
|
+
- `list_app_store_version_localizations` gains `omitLongFields` (return lengths
|
|
72
|
+
instead of full description/promotionalText/whatsNew).
|
|
73
|
+
- `list_analytics_reports` accepts `appId` (uses the app's newest report request)
|
|
74
|
+
instead of crashing on a missing `requestId`.
|
|
75
|
+
|
|
76
|
+
## [1.14.0] - 2026-10-05
|
|
77
|
+
|
|
78
|
+
### Added
|
|
79
|
+
- **PPP regional pricing for IAPs & subscriptions.** Bulk-set in-app purchase and
|
|
80
|
+
subscription prices across ~174 territories from a US base price, scaled by
|
|
81
|
+
purchasing-power parity. Logic + the 175-country dataset are ported from the
|
|
82
|
+
MIT-licensed [appstore-ppp-prices](https://github.com/duceum/appstore-ppp-pricing-agent-skill).
|
|
83
|
+
- `list_purchasable_products` — IAPs (v2) + subscriptions with current US prices.
|
|
84
|
+
- `preview_ppp_prices` — read-only dry run: per-territory target table, scaled in
|
|
85
|
+
local currency via Apple's equalizations and snapped to each territory's real
|
|
86
|
+
price grid (ratio-preserving floors of 0.99 / 0.49).
|
|
87
|
+
- `apply_ppp_prices` — applies the prices (IAP = one atomic price-schedule POST;
|
|
88
|
+
subscriptions = per-territory with `preserveCurrentPrice` + future `startDate`,
|
|
89
|
+
clearing pending prices first). **Irreversible**: requires `confirm:true`, is
|
|
90
|
+
gated by `APPSTORE_MCP_ALLOW_PRICE_CHANGES` and read-only mode, and the docs
|
|
91
|
+
mandate previewing first.
|
|
92
|
+
- Per-tier coefficients use embedded defaults; override any tier (premium,
|
|
93
|
+
high_income, upper_middle, lower_middle, emerging) by passing a `coefficients`
|
|
94
|
+
map — the agent reasons about elasticity, no server-side LLM or extra key.
|
|
95
|
+
- New `client.getAllPages()` helper returns side-loaded `included` resources.
|
|
96
|
+
|
|
97
|
+
## [1.13.0] - 2026-06-13
|
|
98
|
+
|
|
99
|
+
### Added
|
|
100
|
+
- **iOS CI/CD → TestFlight bootstrap.** Turn a new iOS app into a fastlane +
|
|
101
|
+
GitHub Actions → TestFlight pipeline in one call:
|
|
102
|
+
- `ensure_asc_app` — find the App Store Connect app record for a bundle id
|
|
103
|
+
(find-only; the public API has no `POST /apps`, so it returns guidance when
|
|
104
|
+
the record doesn't exist yet).
|
|
105
|
+
- `bootstrap_ios_cicd` — scaffold the 7 pipeline files (Gemfile, fastlane
|
|
106
|
+
Appfile/Fastfile/.gitignore/SETUP.md, two GitHub Actions workflows) into the
|
|
107
|
+
app's repo, auto-detecting `appDir`/`bundleId`/`teamId`/`scheme`/`target`
|
|
108
|
+
from the `.xcodeproj`. Commits to a branch or opens a PR. Xcode automatic
|
|
109
|
+
("cloud") signing via `-allowProvisioningUpdates` — no `match` repo.
|
|
110
|
+
- `set_repo_ci_secrets` — push `ASC_KEY_ID` / `ASC_ISSUER_ID` / `ASC_KEY_P8`
|
|
111
|
+
(base64) to the repo's Actions secrets. The API key is read from this
|
|
112
|
+
server's own config, never passed as an argument or returned in output;
|
|
113
|
+
values are piped to `gh secret set` over stdin.
|
|
114
|
+
- `bootstrap_testflight` — one-call orchestrator running all three.
|
|
115
|
+
- GitHub operations shell out to the `gh` CLI (no extra token, no new
|
|
116
|
+
dependency). The bootstrap writers respect read-only / safe mode.
|
|
117
|
+
|
|
7
118
|
## [1.12.0] - 2026-06-03
|
|
8
119
|
|
|
9
120
|
### Added
|
package/README.md
CHANGED
|
@@ -422,6 +422,7 @@ More: **[docs/SECURITY.md](docs/SECURITY.md)**.
|
|
|
422
422
|
| `401`/`403` errors | Wrong issuer/key id, wrong `.p8`, or the key's role lacks permission. |
|
|
423
423
|
| `409` on metadata update | The version isn't in an editable state — create/select a `PREPARE_FOR_SUBMISSION` version. |
|
|
424
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). |
|
|
425
426
|
| Tools don't appear | MCP servers load at client startup — restart the client / start a new session. |
|
|
426
427
|
|
|
427
428
|
---
|
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,10 +352,91 @@ 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
|
|
358
|
-
- `appId` **(required)** — A/B tests with name, state, traffic proportion, start/end.
|
|
398
|
+
- `appId` **(required)** — A/B tests with name, state, traffic proportion, start/end (legacy v1 list).
|
|
399
|
+
|
|
400
|
+
## Marketing & growth
|
|
401
|
+
|
|
402
|
+
Recent App Store features — Custom Product Pages, Product Page Optimization (A/B),
|
|
403
|
+
Asset Library, Webhooks, subscription offer codes, In-App Events, Game Center challenges.
|
|
404
|
+
|
|
405
|
+
### Custom Product Pages
|
|
406
|
+
Alternate product pages (own screenshots/text, each with a unique marketing URL).
|
|
407
|
+
- `list_custom_product_pages(appId)`, `get_custom_product_page(id)`, `create_custom_product_page(appId, name)`
|
|
408
|
+
- `list_custom_product_page_versions(customProductPageId)`, `create_custom_product_page_version(customProductPageId, deepLink?)`
|
|
409
|
+
- `list_custom_product_page_localizations(versionId)`, `create_custom_product_page_localization(versionId, locale, promotionalText?)`, `update_custom_product_page_localization(localizationId, promotionalText)`
|
|
410
|
+
- Screenshots: `create_screenshot_set(customProductPageLocalizationId, displayType)` → `upload_screenshot`.
|
|
411
|
+
|
|
412
|
+
### Product Page Optimization (A/B, v2)
|
|
413
|
+
- `list_ppo_experiments(versionId)`, `get_ppo_experiment(id)`, `create_ppo_experiment(appId, name, trafficProportion, platform?)`
|
|
414
|
+
- `start_ppo_experiment(id)` / `stop_ppo_experiment(id)` (start/stop via a `started` flag; stop preserves results)
|
|
415
|
+
- `list_experiment_treatments(experimentId)`, `create_experiment_treatment(experimentId, name, appIconName?)`, `create_experiment_treatment_localization(treatmentId, locale)`
|
|
416
|
+
- Screenshots: `create_screenshot_set(treatmentLocalizationId, displayType)`.
|
|
417
|
+
- ⚠️ **Results (conversion %, confidence) are not in the API** — view them in the App Store Connect web UI.
|
|
418
|
+
|
|
419
|
+
### Asset Library
|
|
420
|
+
Upload media once, reuse across product pages / events / treatments.
|
|
421
|
+
- `get_asset_library(appId)`, `list_asset_library_images(appId, category?)`, `list_asset_library_videos(appId)`
|
|
422
|
+
- `upload_asset_library_image(appId, filePath, category?, referenceName?)` — category `CREATIVE_ASSETS` or `APP_SCREENSHOTS_AND_PREVIEWS`
|
|
423
|
+
- `assign_asset_placement({imageId|videoId}, <one destination localization id>, placementType?)`, `delete_asset_placement(placementId)`
|
|
424
|
+
|
|
425
|
+
### Webhooks
|
|
426
|
+
Event callbacks instead of polling (≤10 per app).
|
|
427
|
+
- `list_webhooks(appId)`, `get_webhook(id)`, `create_webhook(appId, name, url, eventTypes[], secret, enabled?)`, `update_webhook(id, …)`, `delete_webhook(id)`
|
|
428
|
+
- `list_webhook_deliveries(id)`, `ping_webhook(id)`
|
|
429
|
+
- Common `eventTypes`: `APP_STORE_VERSION_APP_VERSION_STATE_UPDATED`, `BUILD_UPLOAD_STATE_UPDATED`, `BUILD_BETA_DETAIL_EXTERNAL_BUILD_STATE_UPDATED`, `BETA_FEEDBACK_CRASH_SUBMISSION_CREATED`, `BETA_FEEDBACK_SCREENSHOT_SUBMISSION_CREATED`.
|
|
430
|
+
|
|
431
|
+
### Subscription offer codes (promo-code replacement)
|
|
432
|
+
- `list_subscription_offer_codes(subscriptionId)`, `create_subscription_offer_code(subscriptionId, name, offerMode, duration, numberOfPeriods, customerEligibilities[], offerEligibility, prices[])`
|
|
433
|
+
- `create_offer_code_one_time_use(offerCodeId, numberOfCodes, expirationDate, environment?)`, `create_offer_code_custom(offerCodeId, customCode, numberOfCodes, expirationDate?)`, `list_offer_code_codes(offerCodeId, kind?)`
|
|
434
|
+
|
|
435
|
+
### In-App Events
|
|
436
|
+
- `list_app_events(appId, eventState?)`, `get_app_event(id)`, `create_app_event(appId, referenceName, …)`, `create_app_event_localization(eventId, locale, …)`
|
|
437
|
+
|
|
438
|
+
### Game Center challenges (read)
|
|
439
|
+
- `list_game_center_challenges(appId)`, `get_game_center_challenge(id)`
|
|
359
440
|
|
|
360
441
|
## Code-signing health
|
|
361
442
|
|
|
@@ -451,7 +532,7 @@ clear error):
|
|
|
451
532
|
| --- | --- |
|
|
452
533
|
| `APPSTORE_MCP_READ_ONLY=true` | block all writes |
|
|
453
534
|
| `APPSTORE_MCP_ALLOW_RELEASE=false` | block `release_version` / `set_phased_release` |
|
|
454
|
-
| `APPSTORE_MCP_ALLOW_PRICE_CHANGES=false` | block `set_app_price` |
|
|
535
|
+
| `APPSTORE_MCP_ALLOW_PRICE_CHANGES=false` | block `set_app_price` / `apply_ppp_prices` |
|
|
455
536
|
| `APPSTORE_MCP_ALLOW_REVIEW_REPLIES=false` | block public review replies |
|
|
456
537
|
| `APPSTORE_MCP_ALLOW_EXTERNAL_TESTFLIGHT=false` | block `submit_beta_review` |
|
|
457
538
|
|
|
@@ -477,6 +558,46 @@ clear error):
|
|
|
477
558
|
> See **[RECIPES.md](RECIPES.md)** for copy-paste prompts that chain these into
|
|
478
559
|
> workflows (prepare-version, release-train-with-gates, review→notes, portfolio audit).
|
|
479
560
|
|
|
561
|
+
## Submission flow (build ↔ version ↔ review)
|
|
562
|
+
|
|
563
|
+
The steps a real submission needs beyond `submit_for_review` — attaching a build,
|
|
564
|
+
fixing the silent blockers behind Apple's opaque `409`, and swapping a build that's
|
|
565
|
+
already in review.
|
|
566
|
+
|
|
567
|
+
### Build & version
|
|
568
|
+
- `attach_build_to_version` — `versionId` + (`buildId` or `buildNumber`+`appId`). The mandatory pre-submit link.
|
|
569
|
+
- `get_app_store_version(versionId)` — state, releaseType, `usesIdfa`, and the **attached build** (or null).
|
|
570
|
+
- `update_app_store_version(versionId, …)` — `usesIdfa`, `releaseType`, `earliestReleaseDate`, `versionString`, `downloadable`.
|
|
571
|
+
- `update_build` — `usesNonExemptEncryption` (export compliance, **per-build, doesn't carry over**), `expired`. By `buildId` or `buildNumber`+`appId`.
|
|
572
|
+
- `expire_build`, `get_build`, `next_build_number(appId)`.
|
|
573
|
+
- `wait_for_build_processing(appId, buildNumber, timeoutSeconds?)` — polls to VALID/INVALID/FAILED.
|
|
574
|
+
|
|
575
|
+
### Review submissions
|
|
576
|
+
- `list_review_submissions(appId, state?)`, `get_review_submission(id)`.
|
|
577
|
+
- `add_review_submission_item(reviewSubmissionId, versionId)`.
|
|
578
|
+
- `cancel_review_submission` — by `submissionId` or `appId` (current in-flight); optional `waitSeconds`. Surfaces Apple's refusal on empty/non-cancellable submissions.
|
|
579
|
+
- `get_app_store_review_detail(versionId)` — contact info, demo account, notes.
|
|
580
|
+
|
|
581
|
+
### TestFlight
|
|
582
|
+
- `get_beta_review_status` (build's `betaReviewState`), `set_beta_build_notes(whatsNew, locale?)` ("What to Test", upsert).
|
|
583
|
+
|
|
584
|
+
### Screenshots
|
|
585
|
+
- `find_incomplete_screenshots(versionId)` — assets whose `assetDeliveryState != COMPLETE` (silent submit blockers).
|
|
586
|
+
- `reorder_screenshots(screenshotSetId, orderedIds)` — new uploads append last, so re-order after re-upload.
|
|
587
|
+
- `replace_screenshots(screenshotSetId, filePaths[])` — delete all + upload in order, one call.
|
|
588
|
+
|
|
589
|
+
### Subscriptions
|
|
590
|
+
- `list_subscription_groups(appId)`, `list_subscriptions(groupId)`.
|
|
591
|
+
- `list_subscription_offers(subscriptionId)` — **flags overlapping offer date ranges** (the sandbox `countMismatch` cause).
|
|
592
|
+
- `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.)
|
|
593
|
+
|
|
594
|
+
### Diagnostics & orchestrators
|
|
595
|
+
- `diagnose_submission(appId, versionId)` — read-only: the exact blockers behind submit's 409.
|
|
596
|
+
- `swap_build(appId, versionId, buildNumber|buildId)` — wait for processing → cancel in-flight review → attach.
|
|
597
|
+
- `release_pipeline(appId, versionId, buildNumber?, submit?)` — attach → diagnose → (if clean and `submit:true`) submit.
|
|
598
|
+
- `bulk_upsert_localizations(versionId, { locale: {…} })` — version + app-info fields across all locales, creates missing ones; `dryRun` supported.
|
|
599
|
+
- `list_app_territories(appId)` — compact availability summary.
|
|
600
|
+
|
|
480
601
|
## Build & ship (macOS + Xcode)
|
|
481
602
|
|
|
482
603
|
These run local Xcode tooling, so they only work on a Mac with Xcode installed.
|
|
@@ -503,6 +624,49 @@ in `list_builds` and can be submitted with `submit_for_review`.
|
|
|
503
624
|
|
|
504
625
|
---
|
|
505
626
|
|
|
627
|
+
## iOS CI/CD → TestFlight bootstrap
|
|
628
|
+
|
|
629
|
+
Turn a new iOS app into a fastlane + GitHub Actions → TestFlight pipeline in one
|
|
630
|
+
call. Signing is Xcode automatic ("cloud") signing via `-allowProvisioningUpdates`
|
|
631
|
+
— no `match` repo. The GitHub side shells out to the `gh` CLI (install + `gh auth
|
|
632
|
+
login` required; reuses your existing auth, no extra token). The App Store Connect
|
|
633
|
+
API key lives only in this server's environment and is exposed only through these
|
|
634
|
+
high-level actions — never as an argument, never in output.
|
|
635
|
+
|
|
636
|
+
> Create the App Store Connect API key **once at the TEAM level** (App Store
|
|
637
|
+
> Connect → Users and Access → Integrations) so the same three secret values work
|
|
638
|
+
> for every app/repo.
|
|
639
|
+
|
|
640
|
+
### ensure_asc_app
|
|
641
|
+
Find the App Store Connect app record for a bundle id. **Find-only:** the public
|
|
642
|
+
API cannot create app records (there is no `POST /apps`), so `created` is always
|
|
643
|
+
`false`. If missing, returns `found:false` plus guidance (register the bundle id,
|
|
644
|
+
create the record once in the web UI).
|
|
645
|
+
- `bundleId` **(required)**, `name`, `sku`, `platform`, `primaryLocale`
|
|
646
|
+
- **Returns:** `{ app_id, created, found, bundleId, name }`
|
|
647
|
+
|
|
648
|
+
### bootstrap_ios_cicd
|
|
649
|
+
Scaffold the 7 pipeline files (`Gemfile`, `fastlane/{Appfile,Fastfile,.gitignore,
|
|
650
|
+
SETUP.md}`, `.github/workflows/{ios-ci.yml,ios-testflight.yml}`). Auto-detects
|
|
651
|
+
`appDir`/`bundleId`/`teamId`/`scheme`/`target` from the repo's `.xcodeproj`.
|
|
652
|
+
- `repoDir` (local clone, default `.`), `repo` (`owner/name` for the PR), `owner`
|
|
653
|
+
- overrides: `appDir`, `bundleId`, `teamId`, `scheme`, `target`
|
|
654
|
+
- `mode`: `pr` (default — branch + push + open PR), `branch`, `commit`, `files`
|
|
655
|
+
- `branch`, `baseBranch`, `dryRun` (preview detected values + rendered files)
|
|
656
|
+
|
|
657
|
+
### set_repo_ci_secrets
|
|
658
|
+
Push `ASC_KEY_ID`, `ASC_ISSUER_ID`, `ASC_KEY_P8` (base64) to the repo's Actions
|
|
659
|
+
secrets via `gh secret set` (values piped over stdin; `gh` does the libsodium
|
|
660
|
+
sealed-box encryption). Key material is read from this server's env, never echoed.
|
|
661
|
+
- `repo` (`owner/name`), `owner`, `repoDir` (derive `owner/name` from `origin`)
|
|
662
|
+
|
|
663
|
+
### bootstrap_testflight
|
|
664
|
+
One-call orchestrator: `ensure_asc_app` → `bootstrap_ios_cicd` → `set_repo_ci_secrets`.
|
|
665
|
+
If the app record doesn't exist yet, it still scaffolds + sets secrets and tells
|
|
666
|
+
you to create the record in the web UI. Same options as `bootstrap_ios_cicd`.
|
|
667
|
+
|
|
668
|
+
---
|
|
669
|
+
|
|
506
670
|
## Rate limits
|
|
507
671
|
|
|
508
672
|
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.
|
|
3
|
+
"version": "1.16.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": {
|