Server data from the Official MCP Registry
App Store Connect for MCP clients: PPP pricing, subscriptions, TestFlight, reviews, reports & more
About
App Store Connect for MCP clients: PPP pricing, subscriptions, TestFlight, reviews, reports & more
Security Report
Valid MCP server (1 strong, 1 medium validity signals). No known CVEs in dependencies. Package registry verified. Imported from the Official MCP Registry.
4 files analyzed · 1 issue found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
What You'll Need
Set these up before or after installing:
Environment variable: ASC_ISSUER_ID
Environment variable: ASC_KEY_ID
Environment variable: ASC_PRIVATE_KEY_PATH
Environment variable: ASC_VENDOR_NUMBER
How to Install
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-akoskomuves-appstoreconnect-mcp": {
"env": {
"ASC_KEY_ID": "your-asc-key-id-here",
"ASC_ISSUER_ID": "your-asc-issuer-id-here",
"ASC_VENDOR_NUMBER": "your-asc-vendor-number-here",
"ASC_PRIVATE_KEY_PATH": "your-asc-private-key-path-here"
},
"args": [
"-y",
"@akoskomuves/appstoreconnect-mcp"
],
"command": "npx"
}
}
}Documentation
View on GitHubFrom the project's GitHub README.
appstoreconnect-mcp
A Model Context Protocol server for the Apple App Store Connect API. Drives apps, subscriptions, pricing, and more from any MCP-compatible client (Claude Code, Claude Desktop, Cursor, Windsurf).
The first published surface is subscription pricing — including a Purchasing Power Parity rebalance flow that's already been used to schedule 120 production price changes across 65 territories on a real iOS app. New ASC domains (TestFlight, sales, screenshots, IAPs) are designed to plug in one file at a time; see Roadmap.
Install (zero-config)
npx @akoskomuves/appstoreconnect-mcp init
The wizard:
- Opens App Store Connect → Keys so you can download a
.p8(skipped if you already have one). - Copies the key to
~/.appstore/withchmod 600. - Asks for your Issuer ID and (auto-detected) Key ID.
- Verifies auth with a real API call before writing anything.
- Detects which MCP clients you have installed — Claude Code, Claude Desktop, Cursor, Windsurf — and registers itself in the ones you pick.
When something looks off later, run a read-only diagnostic:
npx @akoskomuves/appstoreconnect-mcp doctor
Manual install
If you'd rather wire it up by hand, add to ~/.claude.json (Claude Code), claude_desktop_config.json (Claude Desktop), or your client's equivalent:
{
"mcpServers": {
"appstoreconnect": {
"command": "npx",
"args": ["-y", "@akoskomuves/appstoreconnect-mcp"],
"env": {
"ASC_ISSUER_ID": "...",
"ASC_KEY_ID": "...",
"ASC_PRIVATE_KEY_PATH": "~/.appstore/AuthKey_XXXXXXXXXX.p8"
}
}
}
}
Or via Claude Code's CLI:
claude mcp add appstoreconnect \
-e ASC_ISSUER_ID=... \
-e ASC_KEY_ID=... \
-e ASC_PRIVATE_KEY_PATH=~/.appstore/AuthKey_XXXXXXXXXX.p8 \
-- npx -y @akoskomuves/appstoreconnect-mcp
Configure
Generate an App Store Connect API key at App Store Connect → Users and Access → Integrations → Keys. Pricing writes need the Admin role; read-only operations work with App Manager.
| Variable | What |
|---|---|
ASC_ISSUER_ID | Issuer UUID from the Keys page |
ASC_KEY_ID | 10-character Key ID |
ASC_PRIVATE_KEY_PATH | Path to your downloaded AuthKey_XXXXXXXXXX.p8 file (~ is expanded) |
The .p8 file is a private key — never commit it. Recommended: ~/.appstore/AuthKey_XXXXXXXXXX.p8 outside any repo.
Optional: In-App Purchase signing key
Only needed for the asc_sign_* tools (subscription offer redemption signing). Issue a second key at App Store Connect → Users and Access → Integrations → In-App Purchase — this is a separate key from the ASC API key above, generated on a different tab of the same page.
| Variable | What |
|---|---|
ASC_IAP_ISSUER_ID | Issuer UUID from the In-App Purchase keys tab (different from ASC_ISSUER_ID) |
ASC_IAP_KEY_ID | 10-character Key ID for the IAP key |
ASC_IAP_PRIVATE_KEY_PATH | Path to the IAP signing .p8 (~ is expanded) |
The server starts fine without these — only the asc_sign_* tools refuse with a setup message if they're missing. Set one or two but not all three and the server rejects with a clear error. Run appstoreconnect-mcp doctor to verify the key loads as a valid ES256 PKCS#8.
Optional: vendor number (sales + finance reports)
Only used by asc_get_sales_report / asc_get_finance_report. Your vendor number is account-level, shown at App Store Connect → Payments and Financial Reports next to your team name (a numeric string like 85123456).
| Variable | What |
|---|---|
ASC_VENDOR_NUMBER | Default vendor number for sales/finance report downloads |
Without it the two report tools still work — they just need vendorNumber passed per call (and their error message tells you where to find it). Note: downloading sales/finance reports requires an API key with the Admin, Finance, or Sales role.
Tools
Apps
asc_list_apps— list apps (filter bybundleId)asc_get_app— fetch one app by ID
Subscriptions
asc_list_subscription_groups— groups for an appasc_get_subscription_group— fetch one group by IDasc_list_subscriptions— auto-renewable subscriptions in a groupasc_get_subscription— fetch one subscription by IDasc_list_subscription_prices— current price schedule per subscription. One row per territory (~175 unfiltered) — passterritoryIdto narrow to one marketasc_list_subscription_price_points— valid price points for a subscription in a territory. PassnearAmountto narrow the response to the nearest tiers around a target price.
Subscription products (writes)
Creating the hierarchy itself — the step that used to send you to the App Store Connect web UI. Four names, only two of which customers ever see:
| Resource | Attribute | Who sees it |
|---|---|---|
SubscriptionGroup | referenceName | internal only |
SubscriptionGroupLocalization | name | customer — heading above the plan choices |
Subscription | name | internal only |
SubscriptionLocalization | name | customer — the individual plan |
asc_post_subscription_group— create the container every subscription must live in. A customer can hold only one active subscription per group, so mutually exclusive plans (Monthly vs Yearly) belong in the same groupasc_patch_subscription_group— rename (referenceNameis the only mutable attribute; groups cannot move between apps)asc_delete_subscription_group— lists the group's subscriptions first and refuses client-side naming the specific products that block the delete, instead of letting Apple return a bare 409asc_post_subscription— create an auto-renewable subscription.productIdis permanent: it cannot be changed, and Apple never lets it be reused on the account — not even after the subscription is deleted.subscriptionPeriodis optional at create but required before submission; the new product starts inMISSING_METADATAand the success message lists the remaining stepsasc_patch_subscription—name,subscriptionPeriod,familySharable,reviewNote,groupLevel.productIdhas no codepath here by construction. Nested offer/price arrays are deliberately unsupported — their wire semantic is replace, so a caller passing one offer would silently delete the rest; use the dedicated offer and price toolsasc_delete_subscription— pre-checks state and refuses for products under review or ever approved, pointing atasc_post_subscription_availabilityas the way to stop selling a live product
Subscription group localizations
The customer-facing group heading — what the App Store renders above the plan choices, and what shows in Settings → Subscriptions. A group with no localizations cannot be submitted.
asc_list_subscription_group_localizations/asc_get_subscription_group_localizationasc_post_subscription_group_localization—name+locale, plus optionalcustomAppName(overrides how the app name reads inside the subscription sheet; usually omit). Wire gotcha handled: the parent relationship key issubscriptionGroup, whileSubscriptioncalls the same parentgroupasc_patch_subscription_group_localization—name/customAppName; locale is the immutable lookup keyasc_delete_subscription_group_localization
Subscription pricing (writes)
asc_post_subscription_price— set the price for one territory, either the opening price or a scheduled change. Order matters, and both steps are confirmed live: territory availability → undated baseline → dated changes.- Omit
startDatefor a subscription's first price in a territory. The opening row is the undated baseline; a dated first price is a price change with nothing to change from. Apple says so outright — "Invalid startDate. Create a starting price before creating future prices." — and distance doesn't help (1, 8 and 29 days out all 409). - Availability must exist first. Without it even a correctly shaped undated POST fails, with a 409 that blames the price point (
ENTITY_ERROR.RELATIONSHIP.INVALID→/data/relationships/subscriptionPricePoint/id). The price point is fine; Apple just can't price a territory the product isn't sold in. The tool translates both of these rather than passing the raw 409 through. preserveCurrentPricedefaults to true on a dated change and is omitted on a baseline, where there's no existing cohort to grandfather. The created row readspreserved: falseuntil a newer price supersedes it; that is expected, not a grandfathering failure
- Omit
asc_delete_subscription_price— cancel a pending scheduled change
App pricing (paid non-subscription apps)
asc_list_app_prices— current price schedule for an app, splitting manual overrides from auto-derived prices and surfacing the base territoryasc_list_app_price_points— valid Apple price tiers for an app in a given territory (~600+ tiers per territory). PassnearAmount(target price) and optionalnearCount(default 10) to narrow the response to the nearest tiers — Apple does not support a near-amount filter server-side, so the full list is still paginated but only the nearest tiers are surfaced.asc_post_app_price_schedule— replace the entire price schedule (whole-schedule replace, NOT a merge — matches Apple's API). Pre-flight refuses unless at least one entry targets the base territory with nostartDate, and requires explicitacknowledgeReplacesAll: true. A separateacknowledgeDeletesScheduledIfBaseChangesack is required when changing the base territory (Apple wipes pending scheduled changes on base-change). Apps have no grandfather mechanism — new schedules activate atomically at each entry'sstartDate.
In-app purchases (consumables, non-consumables, non-renewing subs)
asc_list_iaps— list IAPs for an app (v2 surface only — auto-renewable subscriptions are covered by the Subscriptions tools above). Filterable byinAppPurchaseTypeandstate. If this returns zero rows for an app you know has IAPs, the IAPs may be legacy-only and need to be migrated in the App Store Connect web UI before they appear here.asc_get_iap— fetch a single IAP by ID.asc_list_iap_prices— current price schedule for an IAP (same shape as app prices: manual overrides + auto-derived + base territory).asc_list_iap_price_points— valid Apple price tiers for an IAP in a given territory. SamenearAmount/nearCountnarrowing as the app and subscription price-point tools.asc_post_iap_price_schedule— replace the entire IAP price schedule (same whole-schedule replace semantics asasc_post_app_price_schedule:acknowledgeReplacesAll: true, base-territory entry with nostartDate, base-change ack required). No grandfather mechanism — same as apps.
Subscription introductory offers
Introductory offers target new subscribers — the discounted "first window" before the regular price kicks in.
asc_list_subscription_introductory_offers— list intro offers (free trial / pay-as-you-go / pay-up-front) configured for a subscription, across territories. Apple's "all territories" wildcard (a single offer with noterritory) surfaces asTERR=(all)in the table. PassterritoryIdto narrow to one market — wildcard offers are always kept, since they are live everywhere.asc_get_subscription_introductory_offer— fetch one offer by ID.asc_post_subscription_introductory_offer— create an offer. ThreeofferModes:FREE_TRIAL(no price; omitpricePointId),PAY_AS_YOU_GO(charge the offer price each period fornumberOfPeriodsperiods),PAY_UP_FRONT(single charge for the whole duration; Apple still requiresnumberOfPeriods— defaults to 1 when omitted). PassterritoryIdto target one market, or omit it for Apple's "all territories" wildcard (uses the literal price point in every market — no auto-FX). Server-side validation refusesPAY_*withoutpricePointId,PAY_AS_YOU_GOwithoutnumberOfPeriods, andendDate ≤ startDate— Apple's error is surfaced inline otherwise.asc_patch_subscription_introductory_offer— narrow update path: onlystartDate,endDate, andpricePointIdcan change after creation. To change mode / duration / periods, delete and re-create.asc_delete_subscription_introductory_offer— delete a pending or active offer. Apple refuses to delete one that is currently redeemable; PATCHendDateto today to stop it instead.
Subscription promotional offers
Promotional offers target existing or lapsed subscribers — opposite eligibility from intro offers, set by the resource type itself (no per-offer flag). Apple caps active promo offers at 10 per subscription. After creation, only the per-territory prices can be edited — name, offerCode, offerMode, duration, and numberOfPeriods are immutable.
asc_list_subscription_promotional_offers— list promo offers configured for a subscription.asc_get_subscription_promotional_offer— fetch a single offer, including its per-territory prices.asc_list_subscription_promotional_offer_prices— list per-territory price rows attached to an offer (territory + currency + amount + price-point ID).asc_post_subscription_promotional_offer— create an offer (name+offerCode+ mode + duration + all per-territory prices) in one atomic POST. Pre-flights Apple's 10-offer cap andofferCodecollisions, refusing with a clear remedy message instead of letting Apple 409.asc_patch_subscription_promotional_offer_prices— update the offer's per-territory prices. Apple's wire semantic is replace (the new prices array becomes the post-state, dropping any territory not listed); the tool'smode: 'replace' | 'add' | 'remove'parameter hides the footgun —'add'reads current prices and merges,'remove'reads and filters.asc_delete_subscription_promotional_offer— DELETE → 204.
Subscription win-back offers
Win-back offers target lapsed subscribers — customers who previously subscribed and churned — and Apple surfaces them automatically to eligible customers based on the offer's eligibility rules (or through your own StoreKit messaging). This is the third offer type alongside introductory and promotional. Richer than promo offers: they add eligibility targeting, a schedule, priority, and an auto-asset intent. referenceName, offerId, duration, offerMode, periodCount, targetSubscriptionPlanType, and the prices are immutable after creation.
asc_list_subscription_win_back_offers— list win-back offers configured for a subscription.asc_get_subscription_win_back_offer— fetch a single offer, including its subscription and per-territory prices.asc_list_subscription_win_back_offer_prices— list per-territory price rows attached to an offer (territory + currency + amount + price-point ID).asc_post_subscription_win_back_offer— create an offer (identity + eligibility rules + schedule + priority + all per-territory prices) in one atomic POST. Eligibility is expressed ascustomerEligibilityPaidSubscriptionDurationInMonths,customerEligibilityTimeSinceLastSubscribedInMonths(an{ minimum, maximum? }range), and an optionalcustomerEligibilityWaitBetweenOffersInMonths. Pre-flightsofferIdcollisions and validates the range +endDate > startDate.asc_patch_subscription_win_back_offer— update the mutable attributes only: eligibility,startDate/endDate,priority, andpromotionIntent. To change identity, mode, duration, periods, or prices, delete and re-create.asc_delete_subscription_win_back_offer— DELETE → 204.
IAP & subscription review assets
The review screenshot Apple requires before an in-app purchase or subscription can be submitted, plus the optional promotional images — and, since v1.5, the App Review attachments of a version (files for the reviewer, e.g. a demo video). Five resources, each on the same three-step upload flow as app screenshots (reserve → PUT chunks → commit), with a composite asc_upload_* tool that does all three from a local file. Wire gotcha handled for you: the IAP image relates via inAppPurchase while the IAP review screenshot uses inAppPurchaseV2.
- Images (to-many, per IAP / subscription):
asc_list_{iap,subscription}_images·asc_get_*·asc_upload_*(composite) ·asc_post_*/asc_patch_*(raw reserve/commit) ·asc_delete_*. - Review screenshots (to-one, per IAP / subscription):
asc_get_{iap,subscription}_review_screenshot(returns the single one, or null) ·asc_upload_*·asc_post_*/asc_patch_*·asc_delete_*. Because it's to-one, the upload/reserve tools refuse if one already exists — delete it first. - App Review attachments (to-many, per version's review detail):
asc_list_review_attachments·asc_get_review_attachment·asc_upload_review_attachment(composite) ·asc_post_*/asc_patch_*·asc_delete_*. The parent id is theappStoreReviewDetailid fromasc_get_app_store_review_detail.
App Review details, submissions & release
The last manual steps between "metadata is ready" and "build is live":
asc_get_app_store_review_detail/asc_post_…/asc_patch_…— the What-to-tell-App-Review card of a version: contact person, demo account (name/password/required), notes. To-one per version; Apple merges on PATCH.asc_post_app_store_version_release_request— ⚠️ release an approved (PENDING_DEVELOPER_RELEASE) version to the public App Store now — the "Release this version" click, automated. Only for manually-released versions; no undo.asc_post_iap_submission/asc_post_subscription_submission/asc_post_subscription_group_submission— submit an IAP / subscription / subscription group's pending metadata changes for review standalone, without a version release. Wire gotcha handled: the IAP one relates viainAppPurchaseV2.asc_get_subscription_grace_period/asc_patch_…— billing grace period per app:optIn/sandboxOptIn, duration (3 / 16 / 28 days),renewalType(all renewals vs paid-to-paid only). Keeps lapsed subscribers entitled while Apple retries payment.
Availabilities (subscriptions, IAPs, plans)
Per-territory availability of in-app products — the sibling of App Availability with one key difference: territory linkage uses bare 3-letter ISO codes (plain territories), not the opaque composites apps use.
- Subscriptions:
asc_get_subscription_availability·asc_list_subscription_available_territories·asc_post_subscription_availability(POST-only full replacement — send the complete territory list; ⚠️ removed territories go off sale). - IAPs:
asc_get_iap_availability(reads through the v2 parent path) ·asc_list_iap_available_territories·asc_post_iap_availability(same replace semantics). - Subscription plans (per plan type
MONTHLY/UPFRONT):asc_list_subscription_plan_availabilities·asc_list_subscription_plan_available_territories·asc_post_…·asc_patch_…(the one availability resource with a PATCH).
Subscription offer signing (in-app redemption)
The cryptographic signer that makes promo/intro offers redeemable in your iOS app via StoreKit. Uses a separate signing key from the ASC API key — issued at App Store Connect → Users and Access → Integrations → In-App Purchase. See the optional config section for env vars. Built on Apple's official @apple/app-store-server-library.
asc_sign_promotional_offer_legacy— legacy ECDSA-concatenated signature used by StoreKit 1'sSKPaymentDiscountand the original StoreKit 2Product.PurchaseOption.promotionalOffer(offerID:keyID:nonce:signature:timestamp:)API. Returns the base64 signature plus the nonce, timestamp, and keyId for the caller to pass to StoreKit. Auto-generates a UUID nonce and current timestamp; both overridable for testing.asc_sign_promotional_offer— JWS v2 format introduced at WWDC 2025 (back-deployed to iOS 15). Use with StoreKit 2's newer promotional-offer purchase options. Returns the JWS compact serialization directly.transactionId(the customer'sappTransactionId) is optional but strongly recommended.asc_sign_introductory_offer_eligibility— JWS v2 withaud="introductory-offer-eligibility". Lets you override StoreKit's default introductory-offer eligibility check (e.g. grant a returning customer another trial). New in WWDC 2025.
All signatures are valid for 24 hours from signing time — re-sign per redemption attempt rather than pre-signing and caching.
Age rating
The questionnaire App Review scores an app against — it gates submission, and there was previously no way to set it from here.
asc_get_age_rating_declaration— read the answers. Shows only the non-default ones (a typical declaration has 29 attributes, nearly all atNONE/false) plus every override, so the few that actually drive the rating stand out.asc_patch_age_rating_declaration— answer the questionnaire. Content questions take a frequency (NONE/INFREQUENT_OR_MILD/FREQUENT_OR_INTENSE); the rest are booleans, plus the rating overrides and the Kids age band.
Two things about this resource are easy to get wrong, so the tools handle them for you. It hangs off AppInfo, not the version — age rating is per-app metadata like categories, and /v1/appStoreVersions/{id}/ageRatingDeclaration returns 404. And its ID is the AppInfo ID, so passing appId resolves the target automatically (if an app has several AppInfos across notarization tracks, the tool reports the candidates instead of guessing).
Apple merges on write: omitted keys keep their current value, so a partial update is safe — but you can't clear an answer by leaving it out, you have to send the explicit NONE/false. Overrides only ever raise the rating, never lower it.
Xcode Cloud (CI/CD)
The build side of the ship loop: watch runs, read failures, kick builds. Hierarchy: products → workflows → build runs → actions (build/test/archive/analyze) → issues / test results / artifacts. A finished run links the TestFlight builds it produced, handing off to the TestFlight tools.
- Reads:
asc_list_ci_products·asc_list_ci_workflows/asc_get_ci_workflow(compact config summary: flags, start-condition patterns, actions, resolved Xcode/macOS —raw:truefor Apple's full ~90k-char document) ·asc_list_ci_build_runs(by workflow or product) /asc_get_ci_build_run·asc_list_ci_build_actions·asc_list_ci_issues·asc_list_ci_test_results·asc_list_ci_artifacts/asc_get_ci_artifact(pre-signed, time-limiteddownloadUrl— fetch it without the ASC bearer) ·asc_list_ci_build_run_builds(the TestFlight handoff) ·asc_list_ci_environment_versions(Xcode/macOS catalogs). - SCM reads:
asc_list_scm_providers·asc_list_scm_repositories·asc_list_scm_git_references(branch/tag reference ids — what build-start takes) ·asc_list_scm_pull_requests. - Triggers:
asc_post_ci_build_run(start a build — optional branch/tag override +clean; uses the team's compute hours) ·asc_patch_ci_workflow(pause/resume viaisEnabled,clean, name, description — start conditions and actions stay Xcode-owned by design).
Featuring nominations
Pitch a release to Apple's editorial team for App Store featuring (Today tab, curated collections). Drafts are private; submission is one-way.
asc_list_nominations(filter by app / state / type) ·asc_get_nomination·asc_post_nomination(defaults to a reviewable DRAFT —submitted:false) ·asc_patch_nomination(edit the draft;submitted:truesends it to Apple — no un-submit, onlyarchived:true) ·asc_delete_nomination.- The pitch rides in
description+notes;publishStartDate/publishEndDateframe the relevance window;supplementalMaterialsUriscarry press-kit/TestFlight links;launchInSelectMarketsFirstis the wire key (Markets, not the UI's "storefronts" wording).
Provisioning & code signing
The Developer-portal surface (fastlane match/sigh/cert territory). Role gate: needs an Admin (or Account Holder) API key — App Manager/Developer keys get 403 here (the tools explain it).
- Bundle IDs:
asc_list_bundle_ids(identifier filter) ·asc_get_bundle_id(with capabilities + profiles) ·asc_post_bundle_id(identifier immutable — check the reverse-DNS string) ·asc_patch_bundle_id(rename only) ·asc_delete_bundle_id(refused while an app is attached). - Capabilities:
asc_post_bundle_id_capability·asc_patch_bundle_id_capability·asc_delete_bundle_id_capability— capability changes invalidate existing profiles; regenerate them after. - Certificates:
asc_list_certificates·asc_get_certificate(base64 DER content) ·asc_post_certificate(from a PEM CSR — the private key never goes to Apple) ·asc_delete_certificate(⚠️ DELETE = revoke; CI signing with it breaks immediately). - Profiles:
asc_list_profiles·asc_get_profile(profileContent= the actual base64.mobileprovision) ·asc_post_profile·asc_delete_profile. No PATCH — profiles are immutable; rotate by delete + re-create. - Devices:
asc_list_devices·asc_post_device(⚠️ effectively permanent — devices can only be disabled, never deleted, and count against the 100-per-class yearly limit) ·asc_patch_device(rename, ENABLED/DISABLED).
Sandbox testers
StoreKit test accounts, for exercising the monetization surface end-to-end. Testers are created in the ASC UI; the API manages their settings.
asc_list_sandbox_testers·asc_patch_sandbox_tester(territory,interruptPurchases, acceleratedsubscriptionRenewalRate— a subscription month renews every 3–60 minutes) ·asc_post_sandbox_testers_clear_purchase_history(sandbox-only wipe so purchase flows can be re-tested; resets intro-offer eligibility too).
Territories
asc_list_territories— all 175 App Store territories
PPP rebalancing
ppp_load_index— return the bundled Apple Music Individual-plan price snapshot used as the PPP signalppp_compute_proposal— compute a proposed per-territory price schedule (read-only dry-run; uses Apple Music ratios as implied PPP-FX, snaps to valid Apple price points, applies a configurable round strategy and floor). PassresourceType: "subscription"(default) withsubscriptionId,resourceType: "app"withappIdfor paid apps,resourceType: "iap"withiapId,resourceType: "introductoryOffer"withsubscriptionIdplusofferMode/duration(andnumberOfPeriodsforPAY_AS_YOU_GO;PAY_UP_FRONTdefaults it to 1), orresourceType: "promotionalOffer"withsubscriptionIdplusofferMode/duration/promoOfferName/promoOfferCode(andnumberOfPeriodsforPAY_AS_YOU_GO;PAY_UP_FRONTdefaults it to 1).ppp_apply_proposal— recompute and apply the proposal against ASC after confirming via MCP elicitation (orconfirm: truefor unattended use). Refuses if any row drops by more thanmaxDropPct(default 90%); skips territories where ASC billing currency ≠ Apple Music currency.- For subscriptions: per-territory
subscriptionPricesPOSTs, paced atmaxConcurrency(default 2), retrying 429s automatically; existing subscribers grandfathered whenpreserveCurrentPrice: true(default). - For apps and IAPs: a single whole-schedule-replace POST (one HTTP call, atomic). Apps/IAPs have no grandfather mechanism — new prices activate at each entry's
startDate. RequiresacknowledgeDeletesScheduledIfBaseChanges: truewhen changing the base territory (Apple wipes pending scheduled changes on base-change). - For introductory offers: per-territory
subscriptionIntroductoryOffersPOSTs, paced atmaxConcurrency. The Δ column compares the snapped offer price against the current regular sub price in that territory, so-50%means the offer is half off the sub.FREE_TRIALis rejected (no price to compute — useasc_post_subscription_introductory_offerwithterritoryIdomitted for a single global free trial). Intro offers are additions, not replacements — Apple returns 409 if an active offer already exists for a(sub, territory)cell, and those rows show asfailedin the result table. - For promotional offers: one atomic POST to
/v1/subscriptionPromotionalOfferscreates the offer + all per-territory PPP-snapped prices in a single request. Create-only — refuses ifofferCodecollides with an existing offer or the sub is at Apple's 10-offer cap.FREE_TRIALrejected (no price to compute). Same Δ-vs-current-sub-price reporting as intro offers.
- For subscriptions: per-territory
Response shape
Every list/get tool returns a compact text table by default — designed for an LLM to read without burning context. Every tool also accepts:
raw: true— return the full JSON:API payload (data,included,links,meta) for debugging or advanced use.maxItems: number— cap auto-pagination (default 500–1000 depending on the tool). The MCP followslinks.nextand merges + dedupesincludedresources across pages.
Sparse fieldsets (fields[type]=...) are applied per tool to avoid pulling unused attributes. The whole 175-territory price schedule comes back in one paginated call (200/page) at roughly 1/10th the size of the unfiltered payload.
Protocol support
Speaks the MCP 2026-07-28 revision and the 2025-era protocol from the same build — your client picks. There is nothing to configure either way.
On 2026-07-28 the server is stateless (no initialize handshake; capabilities come from server/discover), and the write-confirmation prompt uses multi-round-trip requests: ppp_apply_proposal returns an input_required result, your client shows the acknowledgement, and the same tool call is re-issued with your answer. Clients that don't support elicitation are told to re-run with confirm: true, exactly as before.
The proposal is recomputed on re-entry rather than carried across the round trip, so prices are re-read from App Store Connect immediately before anything is written — never reused from before you paused to consider. The cost is that an interactive apply computes twice: on a 64-territory subscription that is roughly 95s rather than 48s. Unattended runs with confirm: true never ask, so they compute once and are unaffected.
Production behavior
A few details worth knowing before running ppp_apply_proposal against a live App Store Connect account:
- Rate limit handling. Apple throttles POST endpoints around 50/min.
client.requesthonoursRetry-Afterheaders and falls back to exponential backoff (2s → 60s, capped, up to 6 retries). A 60-territory rebalance pacing through retries finishes in about 2 minutes wall time with zero manual intervention. - Currency-mismatch skip. If the bundled Apple Music index lists a territory in one currency (say BHD) but ASC bills your subscription in another (USD), the PPP-FX ratio breaks dimensionally. The proposal marks those rows
currency-mismatch (asc=USD, am=BHD)and excludes them from the apply set. Common in Gulf USD-billed markets (BHR, KWT, OMN). Set those manually if you want to. - Sanity floor.
floorFactor(default 0.15) is a hard lower bound on per-territory drops as a fraction of the current price — guards against a stale index entry collapsing a price to near-zero. For a more conservative rebalance, pass 0.30 or 0.50. - Sanity ceiling on drops.
maxDropPct(default 90%) refuses to apply any run where a single row drops more than this. If you've ever seen Apple Music tank a market price aggressively, this catches the resulting outlier before you write it to ASC. - Refresh the snapshot when you care.
data/apple-music-prices.jsonis a hand-curated snapshot. Each entry is dated; the snapshot date is shown in proposal output. Pull request a refresh when Apple Music prices move and the project will fold it in.
Anonymous error reports (opt-in, off by default)
This server holds your App Store Connect credentials, so the bar for anything leaving your machine is high. Telemetry is off unless you explicitly turn it on, and there is no "enabled by default, opt out later" step.
appstoreconnect-mcp init asks once. Change it any time:
appstoreconnect-mcp telemetry status
appstoreconnect-mcp telemetry on
appstoreconnect-mcp telemetry off
What is sent
| Sent | Tool name (asc_patch_subscription_localization), HTTP status (409), Apple's error code (ENTITY_ERROR.ATTRIBUTE.INVALID.UNMODIFIABLE), Apple's generic title, the JSON pointer (/data/attributes/state), package version, Node version, OS + arch, and a random install UUID. Plus one liveness ping per day. |
| Never sent | Apple's error detail text, request URLs or paths, app IDs, bundle IDs, app names, prices, subscription names, any request or response body, your issuer ID, key ID, or any credential. Geolocation is explicitly disabled ($geoip_disable), so no IP-derived city, postal code or coordinates are recorded either. |
The scrubber is an allow-list, not a blocklist: a field Apple adds tomorrow is absent by construction rather than by review. It is enforced by tests in tests/telemetry-scrubbing.test.ts, which assert on what is absent as hard as on what is present.
Why
So a bug like "Apple started rejecting every asc_patch_subscription_localization with a 409" shows up as a signal instead of waiting for someone to file an issue. That is a real example — it was found by hand, and this is the automated version of it.
Turning it off everywhere
DO_NOT_TRACK=1is honoured and beats an explicit opt-in.ASC_MCP_TELEMETRY=0hard-disables;=1enables for that run without recording consent on disk.ASC_MCP_TELEMETRY_HOST/ASC_MCP_TELEMETRY_KEYpoint a fork at its own collector.
Transport is fire-and-forget behind a 3s timeout: it never blocks a tool call, never throws, and never writes to stdout (that stream is the MCP protocol channel).
PPP rebalancing skill
The examples/ppp-rebalance/ directory contains a Claude Code skill that wraps these tools into a Purchasing Power Parity workflow (dry-run → schedule → rollback) with the gotchas baked in.
mkdir -p ~/.claude/skills && \
ln -s "$PWD/examples/ppp-rebalance" ~/.claude/skills/ppp-rebalance
Then ask Claude: "Rebalance my subscription prices using the ppp-rebalance skill."
Roadmap
v0.1–v1.0 cover monetization + beta distribution + the full App Store product-page surface + live promotional events + territory / rollout / export compliance + push notifications + revenue/analytics reporting + customer feedback + product-page A/B testing + runtime health/accessibility + pre-orders/real-FX + EU DMA alternative distribution: the full pricing/IAP/offers surface (subscriptions, paid apps, IAPs, intro offers, promo offers, offer-code campaigns, signers), TestFlight (builds, beta groups, beta testers, beta localizations, beta review submissions), the per-locale product-page copy (release notes, descriptions, keywords, promotional text), the release lifecycle (App Store Version write + V2 Review Submission), App Info / category / tag / search-keyword surfaces (v0.12), screenshot + preview asset upload + Custom Product Pages (v0.13), In-App Events + Promoted Purchases (v0.14), App Availability + Phased Release + Encryption Declarations (v0.15), the TestFlight feedback loop — beta feedback screenshots/crashes, build notifications, public-link recruitment criteria (v0.16), Webhooks — per-app event push with delivery history, redelivery, and test pings (v0.17), sales/finance report downloads + the Analytics Reports chain (v0.18), customer reviews — read, respond, summarizations (v0.19), App Store Version Experiments — product-page A/B tests with treatments + variant assets (v0.20), diagnostics/perf-power/accessibility surfaces (v0.21), per-territory pre-orders + real-FX PPP (v0.22), and EU DMA / alternative distribution (v1.0). The planned roadmap is complete. The rest is fertile ground for LLM-driven ops because so much App Store work is judgment-heavy text — review responses, pricing positioning — that a model can draft and a human approves.
Documentation truncated — see the full README on GitHub.
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Fetch
Freeby Modelcontextprotocol · Developer Tools
Web content fetching and conversion for efficient LLM usage
Paperclip
Freeby Paperclipai · Developer Tools
Trending hip-hop artist momentum scores across four cultural dimensions.
Netdata
Freeby Netdata · Developer Tools
Real-time infrastructure monitoring with metrics, logs, alerts, and ML-based anomaly detection.
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
mcp-creator-python
Freeby mcp-marketplace · Developer Tools
Create, build, and publish Python MCP servers to PyPI — conversationally.
MCP Marketplace
Freeby mcp-marketplace · Developer Tools
Search and install MCP servers from inside your AI client.
