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 +2 -0
- package/CHANGELOG.md +97 -0
- package/README.md +3 -1
- package/docs/RECIPES.md +16 -1
- package/docs/SETUP.md +55 -0
- package/docs/TOOLS.md +147 -1
- package/package.json +1 -1
- package/src/cicd.js +481 -0
- package/src/client.js +19 -0
- package/src/guardrails.js +27 -1
- package/src/index.js +2403 -86
- package/src/ppp-countries.csv +176 -0
- package/src/ppp.js +183 -0
- package/src/subscriptions.js +45 -0
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
|
|
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.
|
|
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.
|
|
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": {
|