Releases: akoskomuves/appstoreconnect-mcp
Release list
v1.2.0
Minor Changes
-
f7d559e: Add IAP + subscription review assets — 22 tools across a new
review-assetsdomain, closing a real correctness gap: Apple requires an App Store review screenshot on an in-app purchase / subscription before it can be submitted, and there was no tool to attach one.Four resources, each with the v0.13 three-step asset-upload flow (composite
asc_upload_*+ raw reserve/commit/delete + reads):- IAP promotional images (
inAppPurchaseImages, to-many) — list / get / upload / post / patch / delete - IAP review screenshot (
inAppPurchaseAppStoreReviewScreenshots, to-one) — get / upload / post / patch / delete - Subscription promotional images (
subscriptionImages, to-many) — full set - Subscription review screenshot (
subscriptionAppStoreReviewScreenshots, to-one) — full set
Apple-contract details captured: the reserve relationship key differs per resource (IAP image uses
inAppPurchase, IAP review screenshot usesinAppPurchaseV2— sameinAppPurchasestarget), theuploadedwire key (SwiftisUploaded), and the to-one review-screenshot cardinality (the upload/reserve tools pre-flight and refuse a second one with a delete-first remedy). Reserve/commit wire shapes pinned by unit tests; read paths live-smoked against the real API. - IAP promotional images (
v1.1.0
Minor Changes
-
0ee436f: Add subscription win-back offers — the third subscription offer type alongside introductory and promotional offers.
Win-back offers target lapsed subscribers and are surfaced automatically by Apple to eligible churned customers. Six new tools in a new
win-back-offersdomain:asc_list_subscription_win_back_offers/asc_get_subscription_win_back_offer/asc_list_subscription_win_back_offer_prices(reads)asc_post_subscription_win_back_offer— create with eligibility rules, schedule, priority, and per-territory prices in one atomic POST (pre-flightsofferIduniqueness)asc_patch_subscription_win_back_offer— update the mutable attributes (eligibility,startDate/endDate,priority,promotionIntent)asc_delete_subscription_win_back_offer
Immutability mirrors Apple's contract:
referenceName,offerId,duration,offerMode,periodCount,targetSubscriptionPlanType, and the prices are fixed after creation (delete + re-create to change them). Wire shapes for create/patch are pinned by unit tests.
v1.0.2
Patch Changes
-
e91d8b3: Fix
LocaleSchemaregex to accept BCP-47 script subtags likezh-Hansandzh-Hant.The previous regex
/^[a-z]{2,3}(-[A-Z]{2})?$/only allowed a 2-letter region
suffix (e.g.en-US), which incorrectly rejected 4-letter script subtags
(Hans,Hant). This caused input validation errors for Traditional and
Simplified Chinese locales (zh-Hant,zh-Hans) even though the tool's own
error message listed them as valid examples.The new regex
/^[a-z]{2,3}(-[A-Z][a-z]{3})?(-[A-Z]{2})?$/covers all three
forms Apple uses: language-only (ja), language + script (zh-Hant), and
language + region (en-US).
v1.0.1
Patch Changes
- 557ff93: Add
mcpNameto package.json and aserver.jsonmanifest for publishing to the official MCP Registry (registry.modelcontextprotocol.io). No functional change to the server — this is metadata for registry namespace verification (io.github.akoskomuves/appstoreconnect-mcp).
v1.0.0
Major Changes
-
ef99375: v1.0 — EU DMA / alternative distribution, completing the roadmap. Every planned surface from v0.1 to v1.0 has now shipped: ~230 tools across 40 domains covering monetization, TestFlight, the full product page, release lifecycle, territory/rollout/export compliance, webhooks, revenue + analytics reporting, customer feedback, A/B testing, runtime health, accessibility, pre-orders, real-FX PPP, and alternative distribution.
19 new tools in 1 sub-domain —
src/domains/alternative-distribution.ts. Entitlement-gated: every endpoint 403s unless the account holds Apple's EU alternative-distribution / marketplace entitlement; the error formatter explains this rather than presenting it as a bug.- Web-distribution domains (3): list / register / delete (
⚠️ delete stops installs from that domain). No PATCH exists — delete + re-create. - Signing keys (4): list, per-app to-one get, register (takes the PUBLIC PEM only — the tool description forbids pasting private keys), delete (
⚠️ artifacts stop validating). - Packages (5): relationships-only create per App Store Version, to-one get, versions list (state COMPLETED/REPLACED with pre-signed time-limited download URLs printed verbatim — same hygiene as analytics segments: download promptly, never with the ASC bearer), variants + deltas lists.
- Marketplace search details (4): to-one get, create / patch / delete of the catalogUrl MarketplaceKit queries.
- Marketplace webhooks (4): list / create / patch / delete with a write-only HMAC secret (v0.17 semantics).
Apple-contract gotchas pinned by tests:
catalogURL→catalogUrl+endpointURL→endpointUrl(URL-strip family), relationships-only package create, optional-app-relationship omission on key create, write-only marketplace secret.1.0 stability statement: 486 unit tests; every release v0.12–v1.0 live-smoked against a real App Store Connect account; all 73 sparse fieldsets validated against Apple's official OpenAPI spec (
scripts/audit-fieldsets.py); security posture — Apple-only egress (pre-signed URLs fetched bearer-free), write-only secrets, public-key-only intake, customer-facing writes (review responses, experiment starts, accessibility publishes, territory pulls, pricing) explicitly flagged for human approval in their descriptions.Live smoke (2026-06-12, unentitled account — the expected boundary): team-level lists (domains, keys, marketplace webhooks) return clean empty 200s; the per-app key to-one returns 200 +
data:nullwhen absent (the v0.19 pattern — tool now reports it plainly); calling marketplaceSearchDetail on a NON-marketplace app surfaces an Apple-side 500 UNEXPECTED_ERROR (documented in the tool description: it means "not a marketplace app", not an outage). Writes not exercisable without the entitlement — body shapes pinned by unit tests. - Web-distribution domains (3): list / register / delete (
v0.21.0
Minor Changes
-
379eb20: v0.22 — Per-territory pre-orders + real-FX PPP. Two of the three remaining v1.0+ items.
Pre-orders (1 new tool) —
asc_patch_territory_availability: PATCH/v1/territoryAvailabilities/{id}with available / releaseDate / preOrderEnabled, completing the v0.15 availability surface (list → patch → end-pre-order). The ID is the Apple-opaque base64 composite fromasc_list_territory_availabilities(the v0.15 discovery) — bare ISO codes rejected.⚠️ available=falsepulls the app from sale in that territory; the description requires confirmed intent.Real-FX (PPP engine upgrade, no new tool) —
fxRatesonppp_compute_proposalANDppp_apply_proposal(and threaded through every offer-apply path):- Semantics: USD value of ONE unit of each currency (e.g.
{"BHD": 2.65, "KWD": 3.25}); USD implied 1. - Rescues the currency-mismatch territories the engine previously skipped (USD-billed Gulf storefronts where Apple Music prices in BHD/KWD/OMR) with a dimension-correct conversion:
factor = (indexLocal × usd(idx)) / (anchorLocal × usd(anchor)), target converted into the billing currency. Implemented as the pure, unit-testedfxAdjustedTargetin the PPP engine. - fx-adjusted rows are flagged (
*on FACTOR + summary count);reasonstays exactlychange/unchangedso apply filters are unaffected. - The skip message now says how to rescue ("pass fxRates") or what's missing ("fxRates missing a needed currency").
- Security stance: rates are USER-SUPPLIED only. This server's egress remains Apple-API-only — no third-party FX feed is ever fetched.
Tests: FX math (mismatch rescue, all-same-currency reduction, non-USD billing conversion, missing/invalid-rate refusals) + territory-availability patch body.
Live smoke (2026-06-12, WikiCatch): territory-availability read verified (175 territories, opaque composite IDs feeding the new PATCH tool); real-FX rescue verified against the real bundled index (BHR: 1.49 BHD × 2.65 USD/BHD vs the USD anchor → factor 0.359, dimension-correct target). The PATCH write itself was NOT drilled — it is store-affecting on a shipped app; its body shape is pinned by unit tests.
- Semantics: USD value of ONE unit of each currency (e.g.
v0.20.0
Minor Changes
-
251cc79: v0.21 — Runtime health + accessibility: diagnostic signatures, perf/power metrics, accessibility declarations. Two of the three remaining v1.0+ "niche read surfaces" turn out richer than planned — accessibility declarations are full CRUD with a customer-facing publish gate.
8 new tools across 2 sub-domains.
Diagnostics + perf/power (4 tools, all read-only) —
src/domains/diagnostics.ts.asc_list_diagnostic_signatures— per-build problem hotspots (DISK_WRITES / HANGS / LAUNCHES), impact-weighted, heaviest-first digest.asc_get_diagnostic_logs— the anonymized call stacks behind one signature. NOT JSON:API:application/vnd.apple.diagnostic-logs+jsonvia an Accept override onrequestText.asc_get_app_perf_power_metrics/asc_get_build_perf_power_metrics— Xcode-metrics percentile curves (8 metric types × device classes), app-rolling or single-build. NOT JSON:API:application/vnd.apple.xcode-metrics+json. maxChars-capped raw JSON.- All three populate only with enough opted-in usage — empty/404 on small apps is the documented normal case.
Accessibility declarations (4 tools) —
src/domains/accessibility-declarations.ts. The "Accessibility Nutrition Label" on the product page, per (app, deviceFamily).asc_list_accessibility_declarations— deviceFamily/state filters; digest renders the nine support flags as a Y/N/— matrix (— = not declared ≠ declared-unsupported).asc_post_accessibility_declaration— DRAFT create (deviceFamily required + any subset of nine flags; omitted flags OMITTED on the wire, not false).asc_patch_accessibility_declaration— flag edits +⚠️ publish=true is CUSTOMER-FACING (label goes live; previous PUBLISHED becomes REPLACED). Description instructs explicit human approval.asc_delete_accessibility_declaration— DRAFTs only.
Apple-contract gotchas pinned by tests:
- The largest is-prefix strip family yet (10 members): nine
isSupports*→supports*+isPublish→publish. - Omitted ≠ false on support flags — undeclared flags are dropped from the body.
- Two non-JSON:API content types requiring Accept overrides (
vnd.apple.diagnostic-logs+json,vnd.apple.xcode-metrics+json). - Static fieldset literal kept audit-compatible with
scripts/audit-fieldsets.py(validated against Apple's official OpenAPI spec — 71 fieldset usages clean).
Live smoke (2026-06-12, WikiCatch): CLEAN — zero corrections (second clean round in a row). Both non-JSON:API content types verified live (real xcode-metrics documents returned through the Accept overrides; empty
productDatais the small-app expected shape), diagnostic-signatures empty-list read clean, and the accessibility drill (DRAFT create with two flags → third-flag patch → delete, publish never sent) confirmed all ten strip-family wire keys against the live API with the flag-matrix digest rendering correctly.Schemas (5 new):
DiagnosticSignatureIdSchema,DiagnosticTypeSchema,PerfMetricTypeSchema,AccessibilityDeclarationIdSchema,AccessibilityDeclarationStateSchema.Digests (2 new):
digestDiagnosticSignatures(heaviest-first),digestAccessibilityDeclarations(flag matrix).
v0.19.0
Minor Changes
-
396ca2d: v0.20 — App Store Version Experiments V2 (product-page A/B tests). The highest-leverage ASO item from the v1.0+ bucket, pulled forward: test alternate screenshots, previews, and app icons against the live product page with a controlled traffic split.
12 new tools in 1 sub-domain —
src/domains/version-experiments.ts. V2 surface only (app-attached, rolls across versions); the deprecated version-attached v1 experiments are not exposed.Experiment lifecycle (5 tools)
asc_list_version_experiments— GET/v1/apps/{id}/appStoreVersionExperimentsV2(the URL-version QUIRK: a /v1 path listing V2 resources; CRUD lives at/v2/appStoreVersionExperiments). Digest shows the 9-state lifecycle, traffic %, review-required flag, start/end dates.asc_get_version_experiment— with treatments + latestControlVersion included.asc_post_version_experiment— ALL THREE attrs required (name, platform, trafficProportion 1–99). Created in PREPARE_FOR_SUBMISSION; publicly invisible until submitted AND started.asc_patch_version_experiment— name / trafficProportion / started=true (⚠️ customer-facing: begins serving treatments to real traffic; requires APPROVED; the description instructs explicit human approval first). Wire-key gotcha pinned: SwiftisStarted→ wirestarted.asc_delete_version_experiment.
Treatments + localizations (7 tools)
- Treatment CRUD-minus-get (
asc_list/post/patch/delete_experiment_treatment[s]) — name + optionalappIconName(alternate-icon testing; the icon must ship in the binary). Create emits theappStoreVersionExperimentV2relationship, never the deprecated v1 sibling (pinned by test). - Treatment localization list/create/delete — per-locale containers for variant assets. Variant screenshots/previews reuse the existing v0.13 asset tools (
asc_post_app_screenshot_setetc. already accept parentTypeappStoreVersionExperimentTreatmentLocalizations) — no new upload machinery. - Submission rides the existing v0.11 V2 review-submission flow.
Apple-contract gotchas pinned by tests: the
isStarted→startedstrip, all-required experiment create attrs, V2-only treatment relationship, /v1-list-vs-/v2-CRUD path split.Live smoke (2026-06-12, WikiCatch): CLEAN — full create → get → patch → treatment → localization → lists → delete drill verified every tool path with zero spec corrections (first fully-clean round since v0.14).
started=trueand review submission deliberately NOT exercised (customer-facing). The read list also rendered a real historical experiment correctly (a STOPPED screenshots test from April), confirming digest output against genuine data.Schemas (5 new):
VersionExperimentIdSchema,ExperimentTreatmentIdSchema,TreatmentLocalizationIdSchema,TrafficProportionSchema,ExperimentStateSchema(9-value enum).Digests (3 new):
digestVersionExperiments,digestExperimentTreatments(PROMOTED column = winner pushed live),digestTreatmentLocalizations.
v0.18.1
Patch Changes
- 3683434: Fix:
asc_list_app_infos(andasc_get_app_info's digest) 400ed on every call — Apple REMOVED thekidsAgeBandattribute from the AppInfo contract after v0.12 shipped, and the hard-coded sparse fieldset still requested it ("'kidsAgeBand' is not a valid field name"). The field is dropped from the fieldset, digest column, and schema/tool descriptions. Live-verified: the old fieldset reproduces the 400, the fixed one returns data. Reported from real usage during a release workflow on 2026-06-12.
v0.18.0
Minor Changes
-
b959be2: v0.19 — Customer reviews + review summarizations. The last planned roadmap row before v1.0+: read what customers say, reply publicly (with guardrails), and pull Apple's AI-aggregated review summary.
6 new tools in 1 sub-domain —
src/domains/customer-reviews.ts.asc_list_customer_reviews— app-wide (/v1/apps/{id}/customerReviews) or version-scoped (/v1/appStoreVersions/{id}/customerReviews); pass exactly one of appId / appStoreVersionId. Filters: rating (STRINGS "1".."5" per the wire contract), territory, andhasPublishedResponse(wireexists[publishedResponse]) —falseis the unanswered queue. Sorts: ±createdDate, ±rating. Digest shows star bars, title/body previews, and the developer-response state viainclude=response.asc_get_customer_review— full review body + response included.asc_get_customer_review_response— the review's single developer reply (state PUBLISHED / PENDING_PUBLISH).asc_post_customer_review_response—⚠️ PUBLIC-FACING WRITE: publishes the reply on the App Store under the review; one response per review (re-posting REPLACES). The tool description instructs models to draft → show the human → post only on explicit approval. responseBody capped at 5,970 chars per Apple's UI limit.asc_delete_customer_review_response— removes the public reply; the review itself is untouched.asc_list_customer_review_summarizations— Apple's AI-aggregated summary per (platform, territory, locale), the same text shown on the product page.filter[platform]REQUIRED by Apple; empty lists are common (feature rollout + review-volume gated).
Apple-contract gotchas pinned by tests:
- Exists-param strip variant: Swift
isExistsPublishedResponse→ wireexists[publishedResponse]— a new member of the is-prefix strip family, on a query param rather than an attribute. filter[rating]values are strings, not numbers.- CustomerReviewResponseV1CreateRequest: responseBody attribute REQUIRED + review relationship.
include=responsebaked into the list (v0.16 lesson — relationship objects don't materialize without include).- No sentiment filter exists on Apple's side — rating is the documented proxy.
Live-smoke spec corrections caught on 2026-06-12 (real WikiCatch reviews via
scripts/smoke-reviews.ts, strictly read-only — the respond tool is public-facing and was NOT drilled):- A review with no developer response returns 200 +
data: nullon/v1/customerReviews/{id}/response— not a 404. The tool detects it and reports "no response yet" instead of dumping{"data": null}. - Verified live: app-wide list (2 real reviews with star bars + previews), the
exists[publishedResponse]=falseunanswered queue, rating filters, single-review get with include, version-scoped list, and a clean empty-list summarizations read (Apple hasn't generated one for this app). Respond/delete paths covered by body-builder tests only — intentionally not exercised against the live store.
Schemas (5 new):
CustomerReviewIdSchema,CustomerReviewResponseIdSchema,ReviewRatingFilterSchema,ReviewSortSchema,SummarizationPlatformSchema.Digests (2 new):
digestCustomerReviews(star bars + response state),digestCustomerReviewSummarizations(full summary text blocks).
Patch Changes
- ebf09b0: Document the
ASC_VENDOR_NUMBERenv var (introduced in v0.18 as the vendor-number fallback forasc_get_sales_report/asc_get_finance_report): README gets an "Optional: vendor number" config section (where to find it in App Store Connect, role requirements for the report endpoints), andappstoreconnect-mcp --helplists it alongside the other optional env vars.