App Store Connect
About
MCP server for the App Store Connect API — manage iOS app pricing, subscriptions, IAPs, TestFlight, App Store metadata, screenshots, in-app events, and review submissions from Claude.
Details
- Author
- akoskomuves
- Categories
- Developer Tools, Other, Infrastructure
Jump to
Setup
Install App Store Connect in your MCP client (Claude Desktop, Cursor, Windsurf, and others).
Repository: https://github.com/akoskomuves/appstoreconnect-mcp
Follow the installation instructions in the repository README, then restart your MCP client.
AModel Context Protocolserver for theApple 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 issubscription 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; seeRoadmap.
npx @akoskomuves/appstoreconnect-mcp init
When something looks off later, run a read-only diagnostic:
npx @akoskomuves/appstoreconnect-mcp doctor
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" } } } }
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
Generate an App Store Connect API key atApp Store Connect → Users and Access → Integrations → Keys. Pricing writes need theAdminrole; read-only operations work withApp Manager.
The.p8file is a private key — never commit it. Recommended:~/.appstore/AuthKey_XXXXXXXXXX.p8outside any repo.
Only needed for theasc_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.
The server starts fine without these — only theasc_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. Runappstoreconnect-mcp doctorto verify the key loads as a valid ES256 PKCS#8.
Optional: vendor number (sales + finance reports)
Only used byasc_get_sales_report/asc_get_finance_report. Your vendor number is account-level, shown atApp Store Connect → Payments and Financial Reportsnext to your team name (a numeric string like85123456).
Without it the two report tools still work — they just needvendorNumberpassed per call (and their error message tells you where to find it). Note: downloading sales/finance reports requires an API key with theAdmin,Finance, orSalesrole.
- asc_list_apps— list apps (filter bybundleId)
- asc_get_app— fetch one app by ID
- asc_list_subscription_groups— groups for an app
- asc_list_subscriptions— auto-renewable subscriptions in a group
- asc_list_subscription_prices— current price schedule per subscription
- asc_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.
- asc_post_subscription_price— schedule a price change for one territory
- 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 territory
- asc_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.
- 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.
- 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.
- 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.
Thereview screenshotApple requires before an in-app purchase or subscription can be submitted, plus the optional promotionalimages— and, since v1.5, theApp Review attachmentsof 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 compositeasc_upload_tool that does all three from a local file. Wire gotcha handled for you: the IAP image relates viainAppPurchasewhile the IAP review screenshot usesinAppPurchaseV2.
- 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":
Availabilities (subscriptions, IAPs, plans)
Per-territory availability of in-app products — the sibling of App Availability with one key difference: territory linkage usesbare 3-letter ISO codes(plainterritories), 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 typeMONTHLY/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 aseparatesigning key from the ASC API key — issued at App Store Connect → Users and Access → Integrations → In-App Purchase. See theoptional config sectionfor 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.
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 offAppInfo, not the version— age rating is per-app metadata like categories, and/v1/appStoreVersions/{id}/ageRatingDeclarationreturns 404. And its IDisthe AppInfo ID, so passingappIdresolves the target automatically (if an app has several AppInfos across notarization tracks, the tool reports the candidates instead of guessing).
Applemergeson 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 explicitNONE/false. Overrides only ever raise the rating, never lower it.
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(full config) ·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/tagreference 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).
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 reviewableDRAFT—submitted:false) ·asc_patch_nomination(edit the draft;submitted:truesends it to Apple— no un-submit, onlyarchived:true) ·asc_delete_nomination.
- The pitch rides indescription+notes;publishStartDate/publishEndDateframe the relevance window;supplementalMaterialsUriscarry press-kit/TestFlight links;launchInSelectMarketsFirstis the wire key (Markets, not the UI's "storefronts" wording).
The Developer-portal surface (fastlanematch/sigh/certterritory).Role gate:needs anAdmin(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).
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).
- asc_list_territories— all 175 App Store territories
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.
Speaks theMCP 2026-07-28revision 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 (noinitializehandshake; capabilities come fromserver/discover), and the write-confirmation prompt uses multi-round-trip requests:ppp_apply_proposalreturns aninput_requiredresult, 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 withconfirm: 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 withconfirm: truenever ask, so they compute once and are unaffected.
A few details worth knowing before runningppp_apply_proposalagainst 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 rowscurrency-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 applyanyrun 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.
Theexamples/ppp-rebalance/directory contains aClaude Code skillthat 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."
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.
|v1.2.0✓ | IAP + subscriptionreview assets— the App Store review screenshot Apple requires before an IAP/subscription can be submitted, plus promotional images. Four resources (IAP/subscription × image/review-screenshot) on the v0.13 three-step upload flow, driven from one config table: compositeasc_upload_+ raw reserve/commit/delete + reads. Images to-many, review screenshots to-one (upload refuses a duplicate). Wire gotcha handled: IAP image relates viainAppPurchase, IAP review screenshot viainAppPurchaseV2. 22 new tools. | "Attach the review screenshot to my new subscription so I can submit it." | |v1.4.0✓ | Age rating declarations — read + PATCH the questionnaire App Review scores an app against (13 frequency enums, 11 booleans, rating overrides, Kids age band). Hangs off AppInfo, not the version; declaration id == appInfo id (resolved from appId). Apple merges on PATCH. 2 new tools. | "Answer the age-rating questionnaire so version 3.0 can be submitted." | |v1.5.0✓ | Ship-loop completeness:availabilities(subscription / IAP / plan-type per-territory availability — bare-ISO-code territory linkage, POST-only full replace) +App Review details(contact / demo account / notes per version) withreview attachmentsas the 5th upload-factory resource +release requests(release an approved version now) +standalone item submissions(IAP / subscription / group review without a version release) +billing grace periods. ~25 new tools. | "Fill in the review card, attach the demo video, submit the new subscription, and release the approved build." |
The v0.1→v1.0 roadmap is complete— every originally-planned surface has shipped. Post-1.0 work tracks Apple's API changes (new resources, contract drift — seescripts/audit-fieldsets.py+scripts/audit-required-attributes.py):v1.1.0adds win-back offers,v1.2.0adds IAP/subscription review assets (closing the "can't submit without a review screenshot" gap),v1.4.0adds age-rating declarations,v1.5.0closes the prepare→submit→release loop (availabilities, review details/attachments, standalone submissions, release requests, grace periods).
Out of scope(Fastlane / Xcode already do these well): provisioning profiles, certificates, devices, capabilities, Game Center config.
Think of this as the LLM companion for App Store Connect ops. Fastlane is for the build/release pipeline; this is for the post-release knowledge work — release lifecycle, translation, pricing, ASO, customer feedback, in-store promotion, and analytics.
Each new domain is one file undersrc/domains/<name>.tsplus aregistercall insrc/index.ts. Contributions welcome — seeCONTRIBUTING.md.
git clone https://github.com/akoskomuves/appstoreconnect-mcp.git cd appstoreconnect-mcp npm install npm run dev # tsx watch mode npm test npm run build
SeeCONTRIBUTING.mdfor the contributor flow (changesets, PR template, branch naming).
This is a web browser that enables your coding agent, such as Claude Code, to visit websites on your behalf and assist you in identifying bugs or creating UI test cases.
Manage apps, builds, and artifacts on Bitrise, a Continuous Integration and Delivery (CI/CD) platform.
Extentos is a multi-vendor development platform for adding smart-glasses capabilities to existing iOS and Android apps. The simplest analogy is Stripe for smart glasses
An MCP server tailored for React Native–first development using Gluestack UI
The Octopus MCP Server provides your AI assistant with powerful tools that allow it to inspect, query, and diagnose problems within your Octopus instance, transforming it into your ultimate DevOps wingmate.
MCP Server for PGYER platform, supports uploading, querying apps, etc.
The Railway MCP Server enables natural language interaction with your Railway projects and infrastructure. Ask your IDE or AI assistant to create projects, deploy templates, manage environments, pull variables, redeploy services, and more.
The Shipyard CLI provides an MCP server for agents to manage Shipyard environments directly: by pulling logs, comparing branches, running tests, and stopping/starting environments..
HashiCorp Terraform MCP server for Infrastructure as Code workflows, including provider and module discovery through the Terraform Registry.
MCP server for managing Unleash feature flags and automate best practices.
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.





