appstore-connect-mcp

appstore-connect-mcp

Local-only MCP server for safe App Store Connect access, managing apps, builds, TestFlight, versions, metadata, screenshots, pricing, and review submission, enabling a build to go from uploaded to submitted for review.

Category
Visit Server

README

appstore-connect-mcp

A local-only Model Context Protocol server that gives Claude Code (or any MCP-capable coding agent) safe access to App Store Connect only: apps, builds, TestFlight, App Store versions, metadata, App Info, screenshots, review information, age rating, availability, pricing, content rights, and review submission — enough to take a build from "already uploaded" to "submitted for review" with a single human confirmation at the end.

This project deliberately does not touch Xcode. Xcode operations (build, test, archive, source editing, SwiftUI previews) are handled by Apple's own official Xcode MCP. Claude Code orchestrates both servers together. See CLAUDE.md for the full architecture and security rules.

Claude Code
├── xcode              → Apple's official Xcode MCP (xcrun mcpbridge)
└── appstore-connect-mcp   → this project (stdio)
  • macOS only
  • stdio transport only — no HTTP server, no open ports, no external deploy target
  • No database
  • Your .p8 private key is never written into this repo or committed. It lives outside the repo (e.g. ~/.config/appstore-connect-mcp/) and is read from disk only when signing a JWT.

Requirements

  • macOS
  • Xcode 26.3+ (for the Xcode MCP side of the workflow)
  • Node.js (managed via mise)
  • pnpm (managed via mise)
  • mise
  • Claude Code
  • An App Store Connect Team API Key (Key ID + Issuer ID + downloaded .p8)

Setup

1. Xcode MCP (Apple's official server — not part of this repo)

claude mcp add --transport stdio xcode -- xcrun mcpbridge

In Xcode: Settings → Intelligence → Model Context Protocol → Allow external agents to use Xcode tools.

Verify:

claude mcp list

2. appstore-connect-mcp

mise install
mise run install   # pnpm install

3. Credentials

Create an App Store Connect Team API Key at Users and Access → Integrations → Team Keys, note its Key ID and Issuer ID, and download the .p8 file once (Apple only allows downloading it once).

Store the .p8 outside this repository:

mkdir -p ~/.config/appstore-connect-mcp
chmod 700 ~/.config/appstore-connect-mcp
mv ~/Downloads/AuthKey_XXXXXXXXXX.p8 ~/.config/appstore-connect-mcp/
chmod 600 ~/.config/appstore-connect-mcp/AuthKey_XXXXXXXXXX.p8

(The key's own contents are never printed anywhere by this project — including here.)

Then set three environment variables, either by copying .env.example to .env in this repo and filling it in, or by exporting them in your shell / setting them directly in your MCP client config (see below):

ASC_KEY_ID=<your Key ID>
ASC_ISSUER_ID=<your Issuer ID>
ASC_PRIVATE_KEY_PATH=~/.config/appstore-connect-mcp/AuthKey_XXXXXXXXXX.p8

.env, .p8, and anything matching credentials.*/secrets.* are gitignored — see .gitignore.

4. Verify

mise run test        # unit tests — no credentials needed
mise run typecheck
mise run build

To confirm your credentials actually authenticate, start the server (mise run dev) with the env vars set and call asc_apps_list from Claude Code — a successful response confirms the JWT and credentials are valid end-to-end.

Registering with Claude Code

Add appstore-connect-mcp as its own MCP server, alongside xcode:

claude mcp add appstore-connect-mcp -- mise run dev --cwd /absolute/path/to/appstore-connect-mcp

Replace /absolute/path/to/appstore-connect-mcp with wherever you cloned this repo — that path is environment-specific and shouldn't be assumed.

If your Claude Code client doesn't support claude mcp add (e.g. a desktop app without the CLI), add an entry to its MCP config JSON instead:

{
  "mcpServers": {
    "appstore-connect-mcp": {
      "command": "mise",
      "args": ["-C", "/absolute/path/to/appstore-connect-mcp", "run", "dev"],
      "env": {
        "ASC_KEY_ID": "...",
        "ASC_ISSUER_ID": "...",
        "ASC_PRIVATE_KEY_PATH": "/Users/you/.config/appstore-connect-mcp/AuthKey_XXXXXXXXXX.p8"
      }
    }
  }
}

Use mise -C <dir> run dev (rather than a bare cwd field) if your client doesn't apply cwd to the spawned process — -C is mise's own "change directory before running" flag and doesn't depend on the client supporting a cwd key.

After registering, confirm both servers are visible:

claude mcp list

You should see both xcode and appstore-connect-mcp.

Tools

App identification never requires the numeric Apple app id — pass a bundle id (io.tmkch.colorvia) or app name (Colorvia) and it's resolved for you.

Tool titles are tagged [Read], [Write], or [Destructive/Release] so risk level is visible at a glance (see CLAUDE.md for the philosophy behind this).

Tool Class Description
asc_apps_list Read List all apps on the account
asc_app_get Read Get one app's details
asc_builds_list Read List an app's uploaded builds
asc_build_get Read Get a single build
asc_build_status Read Get a build's processing state
asc_build_upload Write Upload a build (.ipa/.pkg) to App Store Connect
asc_build_upload_status Read Get the state of an in-progress build upload
asc_beta_groups_list Read List TestFlight beta groups
asc_beta_group_add_build Write Add a build to a TestFlight group
asc_beta_build_localization_get Read Get "What to Test" notes
asc_beta_build_localization_update Write Update "What to Test" notes
asc_beta_build_status Read Get TestFlight-specific build status
asc_versions_list Read List an app's App Store versions
asc_version_get Read Get a single App Store version
asc_version_create Write Create a new App Store version (supports dryRun)
asc_version_update Write Update a version's fields (supports dryRun)
asc_version_select_build Write Attach a build to a version (supports dryRun)
asc_metadata_get Read Get localized App Store metadata for a version
asc_metadata_update Write Update localized metadata (supports dryRun)
asc_metadata_create_locale Write Add a new metadata locale to a version (supports dryRun)
asc_version_ensure Write Return the version if it exists, else create it — race-safe (supports dryRun)
asc_build_latest Read Find the highest uploaded build number and suggest the next one
asc_build_wait Read Poll a build/build-upload until it's terminal (SUCCESS/FAILED/TIMEOUT/CANCELLED)
asc_screenshot_sets_list Read List screenshot sets (and their screenshots) for a version/locale
asc_screenshot_upload Write Upload a screenshot, creating its set if needed
asc_screenshot_delete Destructive/Release Permanently delete a screenshot
asc_review_get Read Get review contact/demo-account/notes — never returns the password
asc_review_update Write Update review contact/demo-account/notes (password write-only, supports dryRun)
asc_age_rating_get Read Get the age rating declaration
asc_age_rating_update Write Update the age rating declaration (supports dryRun)
asc_app_info_localizations_list Read List app-level localizations (name, subtitle, privacy policy)
asc_app_info_localization_get Read Get one app-level localization
asc_app_info_localization_create Write Add a new App Info locale (supports dryRun)
asc_app_info_localization_update Write Update an App Info locale (supports dryRun)
asc_availability_get Read Get which territories the app is available in
asc_availability_update Write Replace the full territory list, or ["ALL"] (supports dryRun)
asc_pricing_get Read Get whether pricing is configured and whether it's free
asc_pricing_set_free Write Set the app to free (supports dryRun)
asc_content_rights_get Read Get the content rights declaration
asc_content_rights_update Write Update the content rights declaration (supports dryRun)
asc_release_sync Write Sync .appstore/ SSOT config to App Store Connect, diff-based (supports dryRun)
asc_release_preflight Read Comprehensive App Review readiness check
asc_submission_status Read Get App Review status for a version
asc_submit_for_review Destructive/Release Submit a version for App Review — runs preflight first, always shows a native macOS confirmation dialog

There is intentionally no generic "call any App Store Connect endpoint" tool, and no tool that deletes apps or in-app purchases.

Not automatable through the public API

  • App Privacy Nutrition Labels (data collection / tracking responses) have no documented public App Store Connect API endpoint. asc_release_preflight always reports this check as manual_required — set it manually in App Store Connect under App Privacy.
  • Paid price pointsasc_pricing_set_free reliably handles free apps; setting a specific paid price is not exposed as a tool (Apple's price-schedule API has had several documented behavior changes on their developer forums, and a half-correct paid-pricing tool is worse than none — see src/tools/pricing/service.ts).
  • Export compliance (ITSAppUsesNonExemptEncryption) is an Xcode/Info.plist-side setting; asc_release_preflight reports it when detectable on the uploaded build, but doesn't set it.
  • Apple Developer Program agreement acceptance, tax/banking info, and creating the app record for the very first time all still require the App Store Connect web UI.

asc_build_upload / asc_screenshot_upload implementation notes

Both are implemented against Apple's actual published App Store Connect API schema — not guessed:

  • Builds use the BuildUpload / BuildUploadFile resources (App Store Connect API 4.1+): create the upload, create the file record, upload the file in the chunks specified by its uploadOperations, then commit with an MD5 sourceFileChecksums.file checksum. Processing then continues on Apple's servers — poll asc_build_upload_status, then asc_builds_list.
  • Screenshots use the older AppScreenshotSet / AppScreenshot resources (API 1.2+), which use the same chunked-upload pattern but commit with a plain sourceFileChecksum MD5 string instead of a nested checksum object. asc_screenshot_upload creates the screenshot set automatically if one doesn't already exist for the given locale/display type.

Both were verified field-by-field against developer.apple.com/documentation/appstoreconnectapi before implementation (see the header comments in src/tools/uploads/service.ts and src/tools/screenshots/service.ts for the exact resources referenced).

SSOT config (.appstore/)

Instead of calling metadata/App-Info/availability/age-rating/review/pricing/content-rights tools one at a time, an app repo can define a single source of truth that asc_release_sync reads and reconciles against App Store Connect (diff-based — only fields that actually differ get written):

.appstore/
├── appstore.yml
├── en-US/
│   ├── name.txt
│   ├── subtitle.txt
│   ├── description.txt
│   ├── keywords.txt
│   ├── promotional_text.txt
│   └── whats_new.txt
└── ja/
    └── ... (same files)
app:
  bundleId: io.tmkch.example

release:
  type: MANUAL

pricing:
  type: FREE

availability:
  territories: [JPN, USA, CAN, GBR]

review:
  signInRequired: false
  usernameEnv: APP_REVIEW_USERNAME
  passwordEnv: APP_REVIEW_PASSWORD

ageRating:
  userGeneratedContent: false
  unrestrictedWebAccess: false
  gambling: false

Secrets are referenced by environment variable name onlyreview.usernameEnv / review.passwordEnv, resolved from the environment at sync time. A literal review.password (or username) key in the YAML is rejected by the schema outright. All file access is confined to <projectRoot>/.appstore/ (see src/utils/projectPath.ts) — directory traversal via .. or an unsafe locale/filename is rejected before any read happens.

Screenshots are not part of this format yet — upload them with asc_screenshot_upload directly. App Privacy is always reported as manual_required (no public API — see above).

Release flow

Xcode MCP: Build → Test
      ↓
asc_build_latest → next build number
      ↓
Xcode MCP: set version/build number → Archive → Export
      ↓
asc_build_upload → asc_build_wait (poll until VALID)
      ↓
asc_version_ensure
      ↓
asc_release_sync (or individual metadata/appInfo/availability/ageRating/pricing/contentRights tools)
      ↓
asc_version_select_build
      ↓
asc_release_preflight → must be ready: true
      ↓
asc_submit_for_review → preflight re-checked → macOS confirmation dialog → human clicks Submit

See CLAUDE.md for the full orchestration rules (what never to skip, e.g. never submitting with failing tests or a still-processing build).

dryRun

Write tools that support it accept dryRun: true and return the change as a before/after diff instead of applying it:

{ "appId": "Colorvia", "version": "1.1", "locale": "en-US", "description": "...", "dryRun": true }
{ "dryRun": true, "action": "asc_metadata_update", "changes": [
  { "field": "description", "old": "...", "new": "..." }
] }

Development

mise run dev         # run the MCP server from source (stdio)
mise run build        # compile to dist/
mise run typecheck
mise run test          # unit tests — no App Store Connect credentials required

Unit tests never hit the real App Store Connect API — HTTP is stubbed at the fetch boundary, and JWT tests sign against a throwaway EC key generated in the test itself.

Project layout

src/
├── index.ts              # MCP entrypoint (stdio)
├── server/mcp.ts          # registers every tool group
├── auth/
│   ├── config.ts           # env var loading + .p8 file reading
│   └── jwt.ts               # ES256 Team API Key JWT signing/caching
├── asc/
│   ├── client.ts            # asc.get/post/patch/delete — not exposed as an MCP tool
│   ├── errors.ts             # AppStoreConnectError (HTTP/Reason/Suggestion), AscNetworkError
│   └── types.ts               # shared Apple JSON:API + summary types
├── tools/
│   ├── apps/ builds/ uploads/ testflight/ versions/ metadata/ screenshots/ submissions/
│   ├── review/ ageRating/ appInfo/ availability/ pricing/ contentRights/ buildWait/
│   ├── releaseSync/ releasePreflight/
│   └── each: service.ts (App Store Connect logic) + index.ts (MCP tool registration)
├── config/
│   ├── ssotSchema.ts        # zod schema for .appstore/appstore.yml
│   └── ssotLoader.ts          # reads/validates SSOT + locale files (path-confined)
├── security/confirm.ts     # osascript confirmation dialog (submit-for-review)
└── utils/
    ├── resolveApp.ts, resolveVersion.ts, resolveAppInfo.ts   # id resolution + caching
    ├── mcpResult.ts                        # toolJson/toolError helpers
    ├── dryRun.ts                            # shared dryRun diff builder
    ├── fileUpload.ts                         # chunked uploadOperations PUT + MD5 checksum
    └── projectPath.ts                         # path-traversal guard for SSOT file access
tests/                     # node:test unit tests, no live API calls

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured