apple-asc-mcp

mcp
Security Audit
Fail
Health Warn
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 8 GitHub stars
Code Fail
  • crypto private key — Private key handling in src/auth.ts
  • network request — Outbound network request in src/client.ts
  • process.env — Environment variable access in src/config.ts
  • crypto private key — Private key handling in src/config.ts
  • spawnSync — Synchronous process spawning in src/diagnose.ts
  • crypto private key — Private key handling in src/diagnose.ts
  • crypto private key — Private key handling in src/index.ts
Permissions Pass
  • Permissions — No dangerous permissions requested

No AI report is available for this listing yet.

SUMMARY

MCP server that lets Claude Code drive App Store Connect end-to-end (build, upload, metadata, screenshots, submit for review).

README.md

apple-asc-mcp

CI
npm version
License: MIT
Node ≥20

A Model Context Protocol server that lets Claude Code — or any MCP client (Codex, Cursor, Windsurf, …) — drive an App Store release end-to-end:

xcodebuild → archive → export → upload → metadata → screenshots → submit for review

Claude does the work; you review the result in App Store Connect and tap Submit for Review.

Status: 0.2.0 (pre-1.0). API coverage is current as of Apple's OpenAPI v4.3 + WWDC 2025, and every
tool's request shape has been cross-checked against Apple's published OpenAPI spec. The core
release/monetization paths are live-validated against a real App Store Connect app — auth, metadata,
screenshot upload, app-pricing discovery, the full in-app-purchase and subscription flows (create →
localize → price → availability), age rating, content rights, users, and the delete tools all work
against real Apple traffic. The remaining domains (webhooks, Xcode Cloud, reporting, Game Center,
alternative distribution, in-app events, custom product pages, A/B experiments) are spec-confirmed but
not yet exercised live
— their shapes are correct per the spec but unproven against Apple's API. They
carry [VERIFY] notes where a detail is still inferred. (App Privacy "nutrition label" tools were removed
— that data isn't in the public App Store Connect API.) This is a 0.x release: expect shapes to move as
tools meet live traffic, and pin your version. See Known limitations.


Table of contents


What it does

158 MCP tools split across:

  • Discovery (11) — asc_whoami, list/get apps, builds, versions, localizations, categories, territories, plus asc_release_status: a one-shot snapshot that tells Claude what's blocking submission.
  • Build & upload (5) — xc_archive, xc_export_ipa, asc_validate_ipa, asc_upload_ipa (defaults to the new REST /v1/buildUploads flow from WWDC 2025; falls back to xcrun altool on demand), asc_wait_for_build_processing.
  • Versioning & metadata (10) — create/update versions, attach builds, upsert per-locale description/keywords/promo text/what's new, set categories, set App Review demo credentials, and control the 7-day phased release (asc_set_phased_release).
  • App pricing (3) — list price points, read the price schedule, set the app's price (free or paid; base territory + auto-equalize).
  • Compliance declarations (3) — set the content-rights declaration, read/set the age-rating questionnaire.
  • Submission gates (5) — set app territory availability (where it's sold) and manage export-compliance encryption declarations (list/create/assign-to-build).
  • Screenshots & previews (7) — idempotent set-find-or-create, full reservation → multipart PUT → MD5 commit. Same code path covers iPhone/iPad/Watch/TV/Vision Pro/Mac and iMessage variants.
  • Review submission (4) — modern reviewSubmissions flow.
  • Customer reviews (4) — list/read App Store reviews and post/edit/delete the developer response.
  • TestFlight (4) — list beta groups, set "What to test", distribute, submit for beta review.
  • In-App Purchases (10) — create products, upsert per-locale name/description, price (base territory + auto-equalize), set availability, attach a review screenshot, submit, and delete. Consumable / Non-Consumable / Non-Renewing Subscription.
  • Subscriptions (14) — subscription groups + group localizations, create auto-renewable subscriptions, per-locale name/description, pricing (base + auto-equalize), availability, introductory offers (free trial / pay-as-you-go / pay-up-front), review screenshot, group-level submission, and delete (subscription + empty group).
  • Subscription promotional offers (4) — create/list/delete promotional offers (discounts for existing/lapsed subscribers) and add per-territory offer prices.
  • Subscription offer codes & win-back (7) — offer codes (NEW/EXISTING/EXPIRED eligibility) with custom or one-time-use redeemable codes, and win-back offers (create/list/delete) for lapsed subscribers.
  • Provisioning (13) — bundle IDs + capabilities, signing certificates, test devices, and provisioning profiles (code-signing automation, same auth as the rest).
  • Webhooks (6) — create/list/update/delete app-event webhooks, ping, list deliveries.
  • Users & access (6) — list/get/update team members, list/invite/cancel invitations (Admin key required).
  • Xcode Cloud (6) — list products/workflows, get/start/list build runs (CI/CD).
  • Reporting (4) — download Sales & Finance reports (gzipped TSV, parsed) and request App Store analytics reports.
  • Game Center (7) — per-app detail, achievements + leaderboards with per-locale text.
  • Alternative distribution (5) — EU-DMA distribution keys, packages, and marketplace domains.
  • In-App Events (7) — create/list/update/delete time-boxed product-page events, per-locale text, and event card / details-page art (image + video clip). Schedule per territory; submit via asc_submit_for_review.
  • Custom Product Pages (5) — create/list/get/delete per-audience variant pages with their own promo text and (reusing the screenshot/preview tools) their own visuals.
  • Product Page Optimization (8) — A/B experiments: create/list/get/update (start/stop) experiments, add treatments (alternate icon + visuals), per-locale treatment localizations, and delete. Treatment visuals reuse the screenshot/preview tools.

See the tool reference for the full table.

Quick start

Prerequisites

  • Node 20+. macOS 13+ with Xcode is only needed for the build/archive/validate tools (xc_archive, xc_export_ipa, asc_validate_ipa) and the altool upload fallback — everything else runs cross-platform.
  • An App Store Connect API key (.p8 + Key ID + Issuer ID). If you don't have one yet, get it first via the Apple setup walkthrough, then come back to Step 2.

Step 1 — Install

npm install -g apple-asc-mcp

Or skip the install and run it on demand with npx:

npx apple-asc-mcp --diagnose

Step 2 — Set your credentials

If you haven't created an API key yet, follow the Apple setup walkthrough to generate it and place the .p8. Then export the three credentials in your current shell:

export APP_STORE_CONNECT_KEY_ID=ABCDEFGHIJ
export APP_STORE_CONNECT_ISSUER_ID=11111111-2222-3333-4444-555555555555
export APP_STORE_CONNECT_PRIVATE_KEY_PATH=~/.appstoreconnect/private_keys/AuthKey_ABCDEFGHIJ.p8

These last only for the current terminal. To make them permanent, append the three lines to your shell's startup file, then reload it:

# zsh (default on macOS)
cat >> ~/.zshrc <<'EOF'
export APP_STORE_CONNECT_KEY_ID=ABCDEFGHIJ
export APP_STORE_CONNECT_ISSUER_ID=11111111-2222-3333-4444-555555555555
export APP_STORE_CONNECT_PRIVATE_KEY_PATH=~/.appstoreconnect/private_keys/AuthKey_ABCDEFGHIJ.p8
EOF
source ~/.zshrc

# bash — same, but use ~/.bashrc (Linux) or ~/.bash_profile (macOS)

Step 3 — Verify your setup

apple-asc-mcp --diagnose

You should see all checks pass. If anything fails, the diagnose output names the file/env var to fix.

Step 4 — Wire into Claude Code

Claude Code launches the server in its own environment, which does not inherit the exports from Step 2. So pass the credentials to the registration directly:

claude mcp add --scope user apple-asc-mcp \
  -e APP_STORE_CONNECT_KEY_ID=ABCDEFGHIJ \
  -e APP_STORE_CONNECT_ISSUER_ID=11111111-2222-3333-4444-555555555555 \
  -- apple-asc-mcp

--scope user registers the server globally, for every project. Without it, claude mcp add defaults to local scope and the server only shows up in the directory you ran the command from.

The .p8 is found automatically when it lives at ~/.appstoreconnect/private_keys/AuthKey_<KEYID>.p8 (see Place the .p8). If you keep it somewhere else, add -e APP_STORE_CONNECT_PRIVATE_KEY_PATH=/absolute/path/to/AuthKey_ABCDEFGHIJ.p8.

(The Step 2 exports still matter — they're what let apple-asc-mcp --diagnose find your credentials from the terminal. The -e flags are the separate copy Claude Code's server process needs.)

Using a different MCP client (Codex, Cursor, Windsurf, …)? See Use with any MCP client — those clients pass the same three credentials in their own config's env block.

That's it. In Claude Code:

> Use asc_release_status for bundleId com.example.myapp to see what's blocking submission.
> Now create version 1.4.0 attached to the latest valid build.
> Set the en-US "What's New": "• Faster sync\n• HealthKit integration\n• Bug fixes"
> Submit for review.

Apple setup walkthrough

Create an App Store Connect API key

  1. Sign in to App Store Connect with an Account Holder or Admin role.
  2. Go to Users and Access → Integrations → App Store Connect API → Team Keys.
  3. Click (+). Pick a name (e.g. "MCP release automation") and a role. Recommended: App Manager — it can manage versions, metadata, submissions, and TestFlight, but cannot edit users or pricing. For pricing/IAP automation, you'll need Admin or Finance.
  4. Click Generate. Apple will show a Download API Key link once. Click it. The file AuthKey_<KEYID>.p8 is now in your Downloads folder. You cannot re-download it. If you lose it, you have to revoke the key and create a new one.
  5. Note the Issuer ID (UUID at the top of the Integrations page) and the Key ID (10 characters next to your new key).

Place the .p8

The recommended location, which both altool and this MCP look in by default:

mkdir -p ~/.appstoreconnect/private_keys
mv ~/Downloads/AuthKey_*.p8 ~/.appstoreconnect/private_keys/
chmod 600 ~/.appstoreconnect/private_keys/*.p8

Verify

apple-asc-mcp --diagnose

You should see all checks pass. If anything fails, the diagnose output names the file/env var to fix.

Configuration

Variable Required Notes
APP_STORE_CONNECT_KEY_ID yes 10-character Key ID shown next to the key in App Store Connect
APP_STORE_CONNECT_ISSUER_ID yes UUID at the top of the Integrations page
APP_STORE_CONNECT_PRIVATE_KEY_PATH one of these Path to the AuthKey_<KEYID>.p8 file
APP_STORE_CONNECT_PRIVATE_KEY PEM contents inline (alternative to the path)
APP_STORE_CONNECT_PREFER_REST_UPLOAD no true (default) uses /v1/buildUploads; false falls back to altool

If neither path nor inline PEM is set, the server looks in altool's canonical locations:

~/.appstoreconnect/private_keys/AuthKey_<KEYID>.p8
~/.private_keys/AuthKey_<KEYID>.p8
./private_keys/AuthKey_<KEYID>.p8
./AuthKey_<KEYID>.p8

Wire into Claude Code

Pass the credentials to the registration — Claude Code launches the server in its own environment and won't pick up your shell exports:

claude mcp add --scope user apple-asc-mcp \
  -e APP_STORE_CONNECT_KEY_ID=ABCDEFGHIJ \
  -e APP_STORE_CONNECT_ISSUER_ID=11111111-2222-3333-4444-555555555555 \
  -- apple-asc-mcp

--scope user registers the server globally, for every project. Without it, claude mcp add defaults to local scope and the server only shows up in the directory you ran the command from.

The .p8 is found automatically when it lives at ~/.appstoreconnect/private_keys/AuthKey_<KEYID>.p8 (see Place the .p8). If you keep it somewhere else, add -e APP_STORE_CONNECT_PRIVATE_KEY_PATH=/absolute/path/to/AuthKey_ABCDEFGHIJ.p8.

…or write the same thing to the top-level mcpServers in ~/.claude.json directly (that's what --scope user does):

{
  "mcpServers": {
    "apple-asc-mcp": {
      "command": "apple-asc-mcp",
      "env": {
        "APP_STORE_CONNECT_KEY_ID": "ABCDEFGHIJ",
        "APP_STORE_CONNECT_ISSUER_ID": "11111111-2222-3333-4444-555555555555",
        "APP_STORE_CONNECT_PRIVATE_KEY_PATH": "/Users/me/.appstoreconnect/private_keys/AuthKey_ABCDEFGHIJ.p8"
      }
    }
  }
}

Verify it loaded with:

> Use asc_whoami to check the App Store Connect connection.

Use with any MCP client

This is a standard stdio MCP server — nothing about it is Claude-specific. Any MCP-capable client speaks to it the same way; only the registration differs. Point the client at the appstore-connect-mcp command (or npx -y appstore-connect-mcp) and pass the three credential env vars.

OpenAI Codex — add to ~/.codex/config.toml:

[mcp_servers.appstore-connect]
command = "npx"
args = ["-y", "appstore-connect-mcp"]
env = { APP_STORE_CONNECT_KEY_ID = "ABCDEFGHIJ", APP_STORE_CONNECT_ISSUER_ID = "11111111-2222-3333-4444-555555555555", APP_STORE_CONNECT_PRIVATE_KEY_PATH = "/Users/me/.appstoreconnect/private_keys/AuthKey_ABCDEFGHIJ.p8" }

(Recent Codex also has a codex mcp add helper — see codex mcp --help.)

Cursor / Windsurf / VS Code / Zed / Cline and most others use the same JSON shape as the ~/.claude.json block above — a mcpServers (or editor-specific) entry with command, args, and env. Drop the same three env vars in.

Heads-up on the tool count. This server exposes ~150 tools. Claude Code lazy-loads tool schemas, so the full surface costs almost nothing in context until a tool is used. Clients that don't lazy-load register every schema up front — that's token-heavy, and a few clients/models cap how many tools they'll accept. The server works regardless; if your client struggles with large tool sets, that's the cause. (The macOS/Xcode requirement for xc_archive / xc_export_ipa / asc_validate_ipa is an environment constraint, independent of the client.)

End-to-end release flow

The flow Claude follows when you say "ship 1.4.0 to the App Store":

  1. Snapshot. asc_release_status appId=… returns the editable version, latest VALID build, missing localizations, missing screenshots, and a blockers list. Always start here.
  2. Build & upload (if a fresh build is needed).
    • xc_archive { workspacePath, scheme, archivePath }
    • xc_export_ipa { archivePath, exportPath, exportOptions: { method: "app-store-connect", teamID, signingStyle, … } }
    • asc_validate_ipa { ipaPath, … } (cheap pre-flight; catches signing / entitlement / ITMS errors before upload)
    • asc_upload_ipa { ipaPath, bundleVersion, platform: "IOS" }
    • asc_wait_for_build_processing { appId, bundleVersion } polls until VALID
  3. Version.
    • asc_create_version { appId, versionString: "1.4.0", platform: "IOS", buildId }, or
    • asc_attach_build_to_version { versionId, buildId } if the version already exists.
  4. Localizations. For each locale, asc_set_version_localization with description, keywords, whatsNew, promotionalText, marketingUrl, supportUrl.
  5. App-level info. asc_get_editable_app_infoasc_set_app_categoriesasc_set_app_info_localization (name, subtitle, privacy URL).
  6. Screenshots. Per (locale, displayType): asc_find_or_create_screenshot_set → loop asc_upload_screenshot for each PNG/JPG → optional asc_reorder_screenshots. Same shape for asc_upload_preview.
  7. Review details. asc_set_review_details with contact info and demo credentials (required if your app has a login).
  8. Submit. asc_submit_for_review { appId, versionId, platform } creates the reviewSubmission, adds the version as an item, and PATCHes submitted: true.
  9. Track. asc_get_review_submission { submissionId }. Use asc_release_to_store after Apple approves to push a PENDING_DEVELOPER_RELEASE build live.

For TestFlight: asc_set_beta_whats_newasc_distribute_to_beta_groups (internal-only) or asc_submit_for_beta_review (external testers).

See examples/release.example.json for a sample metadata payload.

Tool reference

Discovery & status

Tool Required inputs Purpose
asc_whoami Verify auth works; lists 1 app to confirm
asc_list_apps List visible apps; filter by bundleId or name
asc_get_app appId or bundleId Single app, with sideloaded versions/builds/appInfos
asc_list_builds appId Builds; filter by processingState, version, expired
asc_get_build buildId Single build, with preReleaseVersion + appStoreVersion
asc_list_versions appId Versions for an app; filter by platform, appVersionState
asc_get_version versionId Single version + build + localizations + review detail
asc_list_version_localizations versionId Per-locale marketing copy under a version
asc_list_categories Discover category ids for asc_set_app_categories
asc_list_territories Territory codes (USA, GBR, …) for the pricing/availability tools
asc_release_status appId or bundleId One-shot "what's blocking submission" snapshot

Build, validate, upload (macOS for the first three)

Tool Required inputs Purpose
xc_archive scheme, archivePath, one of workspacePath/projectPath xcodebuild ... archive
xc_export_ipa archivePath, exportPath, one of exportOptionsPlist/exportOptions xcodebuild -exportArchive (auto-generates plist if you pass exportOptions)
asc_validate_ipa ipaPath Pre-flight via xcrun altool --validate-app
asc_upload_ipa ipaPath, bundleVersion REST /v1/buildUploads (default) or altool
asc_wait_for_build_processing appId, bundleVersion Polls until VALID/INVALID/timeout

Version & metadata

Tool Required inputs Purpose
asc_create_version appId, versionString New App Store Version; optionally attach a build
asc_update_version versionId Edit versionString/copyright/releaseType/etc.
asc_attach_build_to_version versionId, buildId Set or swap the binary on a version
asc_set_version_localization versionId, locale Upsert description, keywords, whatsNew, etc.
asc_release_to_store versionId Manually release a PENDING_DEVELOPER_RELEASE build
asc_set_phased_release versionId Enable / pause / resume / complete the 7-day phased rollout
asc_get_editable_app_info appId Find the editable AppInfo (state=PREPARE_FOR_SUBMISSION)
asc_set_app_categories appInfoId, primaryCategoryId Set primary/secondary categories
asc_set_app_info_localization appInfoId, locale Upsert name, subtitle, privacy URL
asc_set_review_details versionId Contact info + demo credentials

App pricing

The app's own price (free or paid), distinct from in-app-purchase pricing. Apple uses fixed, server-defined price points per territory — you pick a tier, you don't type an amount. asc_set_app_price resolves a customerPrice like "4.99" to the matching point in the base territory (or free: true for the $0 tier); territories you don't list auto-equalize.

Tool Required inputs Purpose
asc_list_app_price_points appId Valid price tiers for a territory
asc_get_app_price_schedule appId The app's current price schedule
asc_set_app_price appId Set price (free, customerPrice, or pricePointId); auto-equalizes

Validation status. Like the rest of the server, these are not yet exercised against live Apple traffic; the price-schedule shape is inferred from research/api-reference.md §12. If a call 400s, attach the JSON:API error body to an issue.

Compliance declarations

Submission gates beyond metadata. (App-privacy "nutrition label" data usages are in the next section.)

Tool Required inputs Purpose
asc_set_content_rights appId, usesThirdPartyContent Declare third-party content rights on the app
asc_get_age_rating appId Read the age-rating questionnaire
asc_set_age_rating appId Set questionnaire answers (resolves the editable AppInfo + declaration)

Validation status. [VERIFY] — and note Apple overhauled the age-rating questionnaire in 2024–25 (new bands/questions). asc_set_age_rating PATCHes only the fields you pass and takes an additionalDeclarations escape hatch for questions not in the typed list.

Submission gates

Final blockers beyond version metadata: where the app is sold, and export compliance.

Tool Required inputs Purpose
asc_get_app_availability appId Territories the app is available in
asc_set_app_availability appId Set territories (or availableInAllTerritories) + auto-add-new flag
asc_list_encryption_declarations appId Existing export-compliance declarations
asc_create_encryption_declaration appId, appDescription, containsProprietaryCryptography, containsThirdPartyCryptography, availableOnFrenchStore Export-compliance declaration (HTTPS-only apps answer false to both crypto flags)
asc_assign_encryption_declaration buildId, declarationId Attach a declaration to a build

Validation status. Write shapes cross-checked against the App Store Connect OpenAPI spec: set_app_availability inlines territoryAvailabilities resources, and the encryption declaration is appDescription + two cryptography flags. Not yet exercised against live Apple traffic — if a call 400s, attach the JSON:API error body to an issue.

Screenshots & previews

Tool Required inputs Purpose
asc_list_screenshot_sets localizationId List sets under a localization
asc_find_or_create_screenshot_set one of localizationId / customProductPageLocalizationId / experimentTreatmentLocalizationId, + displayType Idempotent set get/create (default page, Custom Product Page, or experiment treatment)
asc_upload_screenshot setId, filePath Reservation → multipart PUT → MD5 commit
asc_delete_screenshot screenshotId Remove one screenshot
asc_reorder_screenshots setId, screenshotIds[] Change display order
asc_find_or_create_preview_set one of localizationId / customProductPageLocalizationId / experimentTreatmentLocalizationId, + previewType Idempotent video set get/create
asc_upload_preview setId, filePath Same flow as screenshots, for video

Review submission

Tool Required inputs Purpose
asc_submit_for_review appId, versionId End-to-end: create submission → add item → PATCH submitted=true
asc_get_review_submission submissionId State + items
asc_list_review_submissions appId Recent submissions
asc_cancel_review_submission submissionId Pull back if not yet picked up

Customer reviews

Read App Store customer reviews and manage the developer response. (Distinct from the review-submission flow above.) Reading needs no special role; posting responses may need a higher one.

Tool Required inputs Purpose
asc_list_customer_reviews appId List reviews (filter territory/rating, sort by date/rating)
asc_get_customer_review reviewId A single review + its response
asc_respond_to_review reviewId, responseBody Upsert the developer response
asc_delete_review_response responseId Remove a response

Validation status. [VERIFY] — the response relationship path (/v1/customerReviews/{id}/response) is inferred. If a call 400s, attach the JSON:API error body to an issue.

TestFlight

Tool Required inputs Purpose
asc_list_beta_groups appId Internal + external groups
asc_set_beta_whats_new buildId, locale, whatsNew Per-locale "What to Test"
asc_distribute_to_beta_groups buildId, groupIds[] Push a build to one or more groups
asc_submit_for_beta_review buildId Required before external distribution

In-App Purchases

Covers Consumable, Non-Consumable, and Non-Renewing Subscription products. Auto-renewable subscriptions and offers are deliberately a later phase.

Tool Required inputs Purpose
asc_list_in_app_purchases appId List IAPs; filter by inAppPurchaseType/state
asc_get_in_app_purchase inAppPurchaseId Single IAP + localizations + price schedule + availability
asc_create_in_app_purchase appId, name, productId, inAppPurchaseType Create the product (productId is immutable)
asc_set_iap_localization inAppPurchaseId, locale Upsert customer-facing name (≤30) + description (≤45)
asc_list_iap_price_points inAppPurchaseId Discover valid price tiers for a territory
asc_set_iap_price inAppPurchaseId Base territory + customerPrice/pricePointId; other territories auto-equalize
asc_set_iap_availability inAppPurchaseId Territory availability (codes or availableInAllTerritories)
asc_upload_iap_review_screenshot inAppPurchaseId, filePath Reservation → PUT → MD5 commit (App Review screenshot)
asc_submit_iap_for_review inAppPurchaseId Standalone IAP submission (or bundle via asc_submit_for_review)
asc_delete_in_app_purchase inAppPurchaseId Permanently delete an IAP (only while still editable)

Typical flow:

> Create a non-consumable IAP "Pro Upgrade" productId com.example.app.pro for appId 12345.
> Set its en-US name "Pro Upgrade" and description "Unlock every feature, forever."
> Price it at $4.99 (base territory USA) — other territories auto-equalize.
> Make it available in all territories, upload ~/screens/iap-review.png as the review screenshot, and submit it for review.

Pricing model. Apple uses fixed, server-defined price points per territory — you pick a tier, you don't type an amount. asc_set_iap_price resolves a customerPrice like "4.99" to the matching point in the base territory; pass an explicit pricePointId (from asc_list_iap_price_points) for precision. Territories you don't list are auto-derived from the base.

Validation status. Like the rest of the server, the IAP tools have not been exercised against live Apple traffic. Four spec details are marked [VERIFY] in src/tools/iap.ts (the inAppPurchaseV2 vs inAppPurchase relationship keys, the standalone inAppPurchaseSubmissions submit path, and price-schedule auto-equalization). If one 400s, attach the JSON:API error body to an issue.

Subscriptions

Auto-renewable subscriptions. A subscription lives inside a subscription group; a customer can hold only one active subscription per group, and groupLevel ranks the upgrade/downgrade tiers. Submission happens at the group level.

Tool Required inputs Purpose
asc_list_subscription_groups appId Groups + their subscriptions
asc_create_subscription_group appId, referenceName Create a group (internal name)
asc_set_subscription_group_localization subscriptionGroupId, locale Upsert customer-facing group name (+ optional customAppName)
asc_create_subscription groupId, name, productId, subscriptionPeriod Create an auto-renewable subscription in a group
asc_get_subscription subscriptionId Single subscription + localizations/prices/availability/offers
asc_set_subscription_localization subscriptionId, locale Upsert customer-facing name (≤30) + description (≤45)
asc_list_subscription_price_points subscriptionId Discover valid price tiers for a territory
asc_set_subscription_price subscriptionId Base territory + customerPrice/pricePointId; auto-equalizes
asc_set_subscription_availability subscriptionId Territory availability
asc_set_subscription_intro_offer subscriptionId, offerMode, duration Free trial / pay-as-you-go / pay-up-front offer
asc_upload_subscription_review_screenshot subscriptionId, filePath Reservation → PUT → MD5 commit
asc_submit_subscription_for_review subscriptionGroupId Submit the whole group for review
asc_delete_subscription subscriptionId Permanently delete a subscription (only while still editable)
asc_delete_subscription_group subscriptionGroupId Delete an empty subscription group

Typical flow:

> Create a subscription group "Pro" for appId 12345 and set its en-US name "Pro".
> Add a monthly auto-renewable subscription "Pro Monthly" productId com.example.app.pro.monthly, groupLevel 1.
> Set its en-US name "Pro Monthly" and description "Everything in Pro, billed monthly."
> Price it at $9.99 (base USA), give it a 1-month free trial, make it available everywhere.
> Upload ~/screens/sub-review.png and submit the Pro group for review.

Validation status. Same [VERIFY] caveats apply (see src/tools/subscriptions.ts): the subscription/subscriptionGroup relationship keys, subscription-price auto-equalization (subscriptions have no price-schedule resource — prices are created per territory), the introductory-offer territory/price-point shape, and the group-level subscriptionGroupSubmissions submit path.

Subscription promotional offers

Discounts for existing or lapsed subscribers (distinct from introductory offers, which target new subscribers). Each offer has a developer-defined offerCode (referenced by StoreKit at purchase) and a discounted price per territory.

Tool Required inputs Purpose
asc_list_promotional_offers subscriptionId Offers on a subscription
asc_create_promotional_offer subscriptionId, name, offerCode, offerMode, duration, + price Create an offer (a price — customerPrice/pricePointId in baseTerritory — is required for every mode)
asc_add_promotional_offer_price promotionalOfferId, subscriptionId, territory Add a discounted price for another territory
asc_delete_promotional_offer promotionalOfferId Remove an offer

Validation status. [VERIFY] — the offerCode attribute, the inline subscriptionPromotionalOfferPrices shape, and the standalone add-price path are inferred. If a call 400s, attach the JSON:API error body to an issue.

Subscription offer codes & win-back

Redeemable codes and lapsed-subscriber offers — the remaining subscription-offer types (introductory offers live under Subscriptions; promotional offers above).

Tool Required inputs Purpose
asc_list_offer_codes subscriptionId Offer codes on a subscription
asc_create_offer_code subscriptionId, name, customerEligibilities, offerEligibility, offerMode, duration, + price Create an offer code (NEW/EXISTING/EXPIRED; a price is required)
asc_create_offer_code_custom_codes offerCodeId, customCode, numberOfCodes A memorable code usable N times
asc_create_offer_code_one_time_codes offerCodeId, numberOfCodes, expirationDate A batch of unique single-use codes
asc_list_win_back_offers subscriptionId Win-back offers on a subscription
asc_create_win_back_offer subscriptionId, referenceName, offerId, offerMode, duration, priority, eligibility window, startDate, + price Offer for lapsed subscribers

Validation status. Write shapes cross-checked against the App Store Connect OpenAPI spec: offer codes require offerEligibility and a price; win-back offers require the customer-eligibility window (months paid + a months-since-last-subscribed range), a priority, and a startDate. One-time-use code values are downloaded from App Store Connect, not exposed per-code via the API. Not yet exercised live — if a call 400s, attach the JSON:API error body to an issue.

Provisioning / code signing

Bundle IDs, capabilities, certificates, test devices, and provisioning profiles — the developer-portal side, on the same auth as everything else.

Tool Required inputs Purpose
asc_list_bundle_ids / asc_create_bundle_id / asc_delete_bundle_id (varies) Register/list/delete bundle IDs (platform IOS/MAC_OS/UNIVERSAL)
asc_enable_bundle_capability / asc_disable_bundle_capability bundleIdResourceId/capabilityId Toggle capabilities (push, iCloud, IAP, …)
asc_list_certificates / asc_create_certificate / asc_revoke_certificate (varies) Signing certs (create from a CSR)
asc_list_devices / asc_register_device (varies) Register test devices by UDID
asc_list_profiles / asc_create_profile / asc_delete_profile (varies) Provisioning profiles (bundleId + certs + devices)

Validation status. [VERIFY] — endpoints follow research/api-reference.md §14, but the CapabilityType/CertificateType/ProfileType enum spellings drift over time, so those inputs are free strings (common values documented in each tool). If a call 400s, attach the JSON:API error body to an issue.

Webhooks

Subscribe an HTTPS endpoint to app events (2024+).

Tool Required inputs Purpose
asc_list_webhooks / asc_create_webhook / asc_update_webhook / asc_delete_webhook (varies) Manage webhooks (url, eventTypes, secret, enabled)
asc_ping_webhook webhookId Send a test delivery
asc_list_webhook_deliveries webhookId Recent delivery attempts (debugging)

Users & access

Team members and invitations. Requires an Admin key (lower roles get 403).

Tool Required inputs Purpose
asc_list_users / asc_get_user / asc_update_user (varies) View/edit members (roles, app visibility)
asc_list_user_invitations / asc_invite_user / asc_cancel_user_invitation (varies) Manage pending invitations

Xcode Cloud

CI/CD products, workflows, and build runs.

Tool Required inputs Purpose
asc_list_ci_products CI products
asc_list_ci_workflows / asc_get_ci_workflow ciProductId / workflowId Workflows
asc_start_ci_build workflowId Trigger a build run (optional git ref)
asc_list_ci_build_runs / asc_get_ci_build_run workflowId / buildRunId Build-run status/progress

Reporting

Sales & Finance reports are gzipped TSV (fetched via the raw-bytes path, gunzipped, and parsed to rows); Analytics reports are asynchronous. Needs the ACCESS_TO_REPORTS role (Finance reports need Finance/Admin).

Tool Required inputs Purpose
asc_get_sales_report vendorNumber Sales & Trends report → parsed rows
asc_get_finance_report vendorNumber, regionCode, reportDate Finance report → parsed rows
asc_request_analytics_report appId Request an analytics report set (async)
asc_list_analytics_reports requestId Generated reports for a request

Game Center

Tool Required inputs Purpose
asc_get_game_center_detail appId The app's Game Center detail (anchors the rest)
asc_list_achievements / asc_create_achievement / asc_set_achievement_localization (varies) Achievements + per-locale text
asc_list_leaderboards / asc_create_leaderboard / asc_set_leaderboard_localization (varies) Leaderboards + per-locale text

Alternative distribution (EU DMA)

Distributing outside the App Store in the EU — the newest, most speculative surface.

Tool Required inputs Purpose
asc_get_alt_distribution_key / asc_create_alt_distribution_key appId (+ publicKey) App's alt-distribution signing key
asc_list_alt_distribution_packages appId Alt-distribution build packages
asc_list_marketplace_domains / asc_create_marketplace_domain (varies) Marketplace domains

Validation status. All six domains are [VERIFY] — not yet exercised against live Apple traffic. The reporting filter names, the Xcode Cloud git-reference relationship, Game Center attribute shapes, and the entire alternative-distribution surface are inferred. If a call 400s, attach the JSON:API error body to an issue.

In-App Events

Time-boxed, promotable events on the product page. Flow: create → localize → upload card/details art → schedule per territory → asc_submit_for_review with {type:"appEvent", id}.

Tool Required inputs Purpose
asc_list_app_events appId Events on an app (referenceName, badge, eventState)
asc_create_app_event appId, referenceName Create the event (+ badge / priority / purpose / schedule)
asc_update_app_event appEventId Set territorySchedules (publish/start/end per territory) + other fields
asc_set_app_event_localization appEventId, locale Upsert name / short / long description per locale
asc_upload_app_event_screenshot appEventLocalizationId, filePath, assetType Card / details-page image (reservation → PUT → commit)
asc_upload_app_event_video_clip appEventLocalizationId, filePath, assetType Card / details-page video clip
asc_delete_app_event appEventId Delete an event

Custom Product Pages

Per-audience variant pages, each with its own URL, promo text, and visuals. Flow: create → localize the draft version → attach screenshots/previews (the screenshot/preview set tools take customProductPageLocalizationId) → asc_submit_for_review with {type:"appCustomProductPageVersion", id}.

Tool Required inputs Purpose
asc_list_custom_product_pages appId Pages (name, shareable url, visible)
asc_create_custom_product_page appId, name Create a page; returns the draft versionId
asc_get_custom_product_page customProductPageId url + visibility + draft version + localizations
asc_set_custom_product_page_localization versionId, locale Upsert promotional text; the returned localization id feeds the screenshot tools
asc_delete_custom_product_page customProductPageId Delete a page

Validation status. Create shapes confirmed against the OpenAPI spec; territorySchedules is fully typed. Not yet exercised live — video-clip formats and the exact pre-submission requirements (Apple wants a schedule before an event submits) are the likely first surprises.

Product Page Optimization (A/B experiments)

Test alternate screenshots/previews/icons against the baseline. Flow: create experiment → add treatment(s) → add a treatment localization → attach that treatment's visuals (the screenshot/preview set tools take experimentTreatmentLocalizationId) → asc_update_experiment with started: true.

Tool Required inputs Purpose
asc_list_experiments appId Experiments (name, state, trafficProportion, dates)
asc_create_experiment appId, name, trafficProportion Create an experiment (v2, app-level)
asc_get_experiment experimentId Experiment + its treatments
asc_update_experiment experimentId Start (started: true) / stop / rename / re-weight
asc_create_experiment_treatment experimentId, name Add a variant (+ optional appIconName)
asc_set_experiment_treatment_localization treatmentId, locale Find/create the localization; its id feeds the screenshot tools
asc_delete_experiment_treatment treatmentId Remove a treatment
asc_delete_experiment experimentId Delete an experiment

Validation status. Shapes confirmed against the OpenAPI spec (v2 app-level model). Not yet exercised live — the exact gating on started: true (treatments must be complete and, if reviewRequired, approved first) is the likely first surprise.

Troubleshooting

Run apple-asc-mcp --diagnose first — it diagnoses about 80% of setup issues directly.

Authentication errors

Symptom Likely cause Fix
401 NOT_AUTHORIZED Clock skew on your machine Sync NTP. Apple rejects JWTs with iat in the future.
401 NOT_AUTHORIZED, was working yesterday Key revoked or expired in App Store Connect Re-create the key, download the new .p8, update env vars
401 NOT_AUTHORIZED, only on certain endpoints Key role too low Promote to App Manager (or Admin/Finance for pricing/IAP)
401, JWT looks fine Token cached past exp We mint 18-min tokens with a 60s refresh lead; if you fork the code, don't loosen this — Apple's hard cap is 1199 seconds
Could not import the .p8 private key as PKCS#8 / ES256 The .p8 was edited / re-saved and lost its line breaks Re-download from App Store Connect — keep the file byte-identical
credentials are not configured inside Claude Code (or another MCP client), but apple-asc-mcp --diagnose passes in the terminal The server runs in the client's environment and doesn't inherit your shell exports Put the three credentials in the registration's env — re-add with claude mcp add --scope user apple-asc-mcp -e APP_STORE_CONNECT_KEY_ID=… -e APP_STORE_CONNECT_ISSUER_ID=… -- apple-asc-mcp. See Wire into Claude Code.
The server works in one project but is missing everywhere else It was registered without --scope user, so it landed in the default local scope (that one directory only) From that directory run claude mcp remove apple-asc-mcp, then re-add with --scope user. See Wire into Claude Code.

Upload errors (ITMS-90xxx)

altool and the REST upload surface return Apple's structured error codes. The most common:

Code Meaning Common fix
ITMS-90049 Bundle is missing a required key in Info.plist Apple's error includes the key — usually CFBundleIcons, LSApplicationCategoryType, or NSPhotoLibraryUsageDescription
ITMS-90189 Redundant binary upload — same CFBundleVersion already exists Bump build number; build numbers must be unique per (bundleId, version)
ITMS-90683 Missing purpose string for a usage-permission API Add NS<Capability>UsageDescription to Info.plist
ITMS-90161 Invalid provisioning profile Profile doesn't include the App ID, or uses a wrong certificate. Re-export with -allowProvisioningUpdates and a valid ExportOptions.plist.
ITMS-90685 CFBundleVersion value not greater than the previous upload App Store wants strictly increasing build numbers within a marketing version
ITMS-90809 Use of deprecated SDK / API Apple's error names the API; address it or set manageAppVersionAndBuildNumber=false if it was a tooling-side rewrite
ITMS-91065 Missing privacy manifest declarations Add a PrivacyInfo.xcprivacy to your bundle declaring required-reason API usage
STATE_ERROR.SCREENSHOTS_REQUIRED Submitting a version with no screenshots in the primary locale Upload at least one set (e.g. APP_IPHONE_67) for the primary locale before submitting
STATE_ERROR.MISSING_METADATA Required localization fields empty Fill description and keywords for the primary locale

Build stuck in PROCESSING

  • Normal: 5–30 minutes. Larger bundles, watch/visionOS extensions, on-demand resources, and Apple Silicon-only Mac builds can take 60+ minutes.
  • If still PROCESSING after 60 minutes, check App Store Connect → My Apps → TestFlight → Build Activity for an error. The REST API will eventually move it to INVALID if processing fails server-side.
  • asc_wait_for_build_processing defaults to a 45-minute timeout. Bump timeoutMinutes if needed.

Screenshot rejected at commit time

If assetDeliveryState.state comes back FAILED after asc_upload_screenshot:

  • Wrong dimensions for the display type (most common). All screenshots in a set must share dimensions; see the table in §7.1 of the API reference.
  • Transparency / alpha channel — Apple rejects PNGs with alpha.
  • Interlaced PNG — also rejected.

The fix is always: regenerate the asset, delete the failed screenshot, and re-upload.

"No editable App Store Version"

Means the previous version is still in flight (e.g. IN_REVIEW). You can't create a new editable version until the current one resolves. Either wait, or asc_cancel_review_submission if you need to pull back.

Logs

Detailed request/response logs (with secrets redacted) live at ~/logs/apple-asc-mcp/<date>.log. Attaching the relevant slice to a bug report is the single most useful thing you can do.

Security & secret handling

  • The .p8 is a long-lived team-wide credential. Anyone with this file plus the Key ID and Issuer ID can act as the role you assigned. Treat it like a production API key:
    • chmod 600 ~/.appstoreconnect/private_keys/*.p8
    • Never commit it to git. A .gitignore entry for *.p8 is included in this repo.
    • Don't paste it into chat logs. The MCP redacts known secret arg names (password, apiKey, etc.) from its own logs, but it can't redact what you paste.
  • Pick the minimum role. App Manager is enough for everything in this MCP except pricing changes (Finance/Admin).
  • Rotate when in doubt. If you suspect compromise, revoke the key in App Store Connect → Integrations and create a new one. Old .p8 files become inert immediately on revocation.
  • Per-MCP-instance keys are fine. You can create multiple API keys for the same team — one per machine, one per CI environment, one per developer. Track them by name.
  • JWT scoping. Apple supports a scope claim limiting a JWT to specific endpoints. This MCP doesn't set it because the tool surface spans many endpoints; if you want defense-in-depth, fork and add a scope allowlist to auth.ts.

Comparison with fastlane / Transporter

Honest answer: fastlane is more battle-tested. It's been wrapping App Store Connect since 2014, supports IAPs and pricing in depth, and has a huge community. If you have a deterministic CI pipeline and a Ruby allergy isn't a problem, fastlane (pilot + deliver + match) is a fine choice.

Where this MCP wins:

  • Conversational. "Ship 1.4.0 with the screenshots in ~/screens/, demo creds from 1Password" reads like an instruction; the equivalent fastlane setup is hundreds of lines of Ruby.
  • One-shot snapshots. asc_release_status returns the entire submission-readiness picture in one call, designed for an LLM to reason over. fastlane gives you many small lanes you can call but no equivalent aggregator.
  • Modern API surface. Uses reviewSubmissions (the current submission flow) and /v1/buildUploads (REST-native binary upload from WWDC 2025) — both of which fastlane supports but which most blog posts haven't caught up to.
  • No Ruby. Single Node binary; works wherever Node 20 runs.

Where fastlane wins:

  • Coverage. IAPs, subscriptions, custom product pages, age ratings, Game Center — fastlane handles all of these. This MCP focuses on the release path and deliberately punts IAP/subscription metadata to v2.
  • Plugins. A decade of ecosystem.
  • Battle-testing. This MCP is alpha; fastlane has shipped tens of thousands of apps.

A reasonable hybrid: use fastlane for IAP-heavy workflows you've already automated, use this MCP for the conversational "drive a release" loop.

Known limitations

These are spec/platform constraints, not TODOs — they cannot be worked around in code:

  • No POST /v1/apps. Apple's REST API does not let you create a new App record from scratch. The first time you upload a build for a brand-new bundle ID, App Store Connect materializes the App row server-side; before that, you must register the bundle ID and create the App in App Store Connect's web UI.
  • xc_archive, xc_export_ipa, and asc_validate_ipa require macOS with Xcode. The REST upload path is cross-platform, but you still need a Mac to produce the IPA.
  • Bitcode is dead. uploadBitcode / compileBitcode keys in ExportOptions.plist are no-ops since Xcode 14.

These items are working but not yet validated against live Apple traffic — flagged in the code with [VERIFY] comments:

  • The Platform enum spelling on POST /v1/buildUploads (we use IOS / MAC_OS / TV_OS / VISION_OS, matching every other endpoint in the API; WWDC 2025 transcripts used a generic placeholder).
  • The full BuildUploadFile.assetType enum. We hardcode "BUILD" for the main IPA, which Apple's session described, but other values may exist for accompanying assets (dSYMs, etc.).

If you hit a 400 from the REST upload, set APP_STORE_CONNECT_PREFER_REST_UPLOAD=false to fall back to xcrun altool and please file an issue with the JSON:API error body — that's the fastest way to nail down the spec details.

Out of scope for v1 (will probably ship in v2):

  • In-App Purchases / Subscriptions (the API has 30+ endpoints for this; needs its own design pass).
  • App Pricing (appPriceSchedules with the v3 appPricePoints model — needs Finance/Admin role and a fair amount of territory bookkeeping).
  • App Custom Product Pages and A/B Test Experiments.
  • Age Rating Declaration full questionnaire automation (we expose read; the questionnaire is a moving target).
  • Provisioning profile / certificate management (bundleIds, profiles, certificates, devices). The API supports these and they fit the same auth/client; just not on the critical path for "ship a release."

Development

git clone https://github.com/warunacds/apple-asc-mcp
cd apple-asc-mcp
npm install

npm run build       # tsc → dist/
npm run dev         # tsx, hot path for iteration
npm run typecheck   # tsc --noEmit
npm test            # node --test --import tsx
npm run diagnose    # run the preflight against your env

Logs at ~/logs/apple-asc-mcp/<date>.log (mirrored to stderr).

Layout

src/
  index.ts          MCP server entry (stdio transport) + --diagnose CLI
  diagnose.ts       Preflight check runner
  auth.ts           ES256 JWT minter with caching
  client.ts         JSON:API REST client w/ retry, pagination, structured errors
  upload.ts         Asset reservation runner: chunked PUT + MD5 (used for screenshots, previews, builds)
  xcode.ts          xcodebuild & altool subprocess wrappers
  config.ts         Env + .p8 loader
  log.ts            Logs to stderr + ~/logs/apple-asc-mcp/
  tools/            One file per tool family; all flow into tools/index.ts → ALL_TOOLS
test/               node:test against in-process mock servers (no real Apple traffic)
research/           Reference docs from the design phase
examples/           Sample metadata + ExportOptions.plist

See CONTRIBUTING.md for the bar for new tools and tests.

License

MIT © 2026 apple-asc-mcp contributors

Sources

Full design notes are in research/api-reference.md and research/upload-pipeline.md.

Reviews (0)

No results found