App Store MCP
<div align="center">
# App Store MCP
### Manage App Store Connect releases, metadata, TestFlight, screenshots, and reviews from an AI agent.


[](LICENSE)
[Quick start](#quick-start) · [Capabilities](#capabilities) · [Apple credentials](#apple-credentials) · [MCP clients](#mcp-clients) · [Tool reference](#tool-reference)
</div>
App Store MCP is a stdio [Model Context Protocol](https://modelcontextprotocol.io/) server using the official [App Store Connect API](https://developer.apple.com/documentation/appstoreconnectapi/). Its shape follows the sibling Play Store MCP project, but uses Apple's resources and publishing workflow. It does not scrape the App Store Connect website. Tools return Apple's JSON:API response, including resource IDs and `links.next` for pagination.
## Quick start
1. Install Node.js 20 or newer and create an [App Store Connect API key](#apple-credentials).
2. Configure the server in your MCP client. For a package installed from npm after publication:
```sh
codex mcp add app-store \
--env APP_STORE_CONNECT_KEY_ID=YOUR_KEY_ID \
--env APP_STORE_CONNECT_ISSUER_ID=YOUR_ISSUER_UUID \
--env APP_STORE_CONNECT_PRIVATE_KEY_PATH=/absolute/path/to/AuthKey_YOUR_KEY_ID.p8 \
-- npx -y app-store-connect-mcp
codex mcp list
```
The package is currently local and has **not** been published. For local use, replace `npx -y app-store-connect-mcp` with `node /absolute/path/to/app-store-mcp/dist/index.js` after `npm install && npm run build`.
3. Ask your agent to call `list_apps`, then use the returned app `id` in other tools. The App Store Connect app ID is an opaque resource ID, not the bundle ID.
## Capabilities
| Area | Tools |
| --- | --- |
| Apps and builds | Discover apps and versions; upload IPA binaries; inspect upload status and processed builds. |
| Reviews | Read customer reviews and publish or replace developer replies. |
| Metadata | Read and edit app-level name/subtitle and version-level descriptions, keywords, URLs, and release notes. |
| Screenshots | Create screenshot sets, upload local PNG/JPEG files with Apple's reserve/upload/commit workflow, and list screenshots. |
| TestFlight | List and create beta groups and testers, then assign processed builds and existing testers to groups. |
| Publishing | Create a version, attach a processed build, create a review submission, add the version, submit it, and manually release an approved version. |
Apple does not use Google Play's transactional app edits. Metadata changes affect App Store Connect immediately, subject to the version state. Sending a review submission starts Apple's review process; it does not guarantee approval. `release_version` is only for an approved version in `PENDING_DEVELOPER_RELEASE` and cannot be canceled. [Apple's manual release documentation](https://developer.apple.com/documentation/appstoreconnectapi/post-v1-appstoreversionreleaserequests)
The `upload_ipa` tool uses Apple's [Build uploads API](https://developer.apple.com/documentation/appstoreconnectapi/build-uploads): it creates a build upload, reserves the IPA file, uploads Apple-specified byte ranges, and commits an MD5 checksum. Provide the version and build number found inside the IPA; the server does not inspect the archive. Apple processes uploads asynchronously. Poll `get_build_upload`, then `list_builds`, and attach only a processed build. Xcode and Transporter remain alternatives for binary delivery.
## Tool reference
| Tool | Action |
| --- | --- |
| `list_apps` | List apps; optionally filter by bundle ID. |
| `get_app` | Read one app. |
| `list_versions` | List an app's App Store versions, optionally by platform. |
| `get_version` | Read a version and its assigned build. |
| `list_builds` | List processed builds for an app. |
| `get_build` | Read a build and processing state. |
| `list_build_uploads` | List build upload records for an app. |
| `get_build_upload` | Read upload processing state. |
| `upload_ipa` | **Uploads:** send a local iOS IPA via Apple Build uploads API. |
| `list_reviews` | List an app's reviews, optionally by rating. |
| `get_review` | Read one customer review. |
| `get_review_response` | Read the developer response for a review. |
| `reply_to_review` | **Publishes:** create or replace a public review response. |
| `list_localizations` | List version-level metadata localizations. |
| `get_localization` | Read a version localization. |
| `create_localization` | **Changes metadata:** add a version localization. |
| `update_localization` | **Changes metadata:** edit description, keywords, release notes, and URLs. |
| `list_app_infos` | List app-level information records. |
| `list_app_info_localizations` | List localized names, subtitles, and privacy URLs. |
| `create_app_info_localization` | **Changes metadata:** add an app-level locale. |
| `update_app_info_localization` | **Changes metadata:** edit localized name, subtitle, or privacy URLs. |
| `list_screenshot_sets` | List screenshot sets for a version locale. |
| `create_screenshot_set` | **Changes metadata:** create a display-type screenshot set. |
| `list_screenshots` | List screenshots in a set. |
| `upload_screenshot` | **Uploads:** add a local PNG or JPEG to a screenshot set. |
| `list_beta_groups` | List an app's TestFlight groups. |
| `create_beta_group` | **Changes TestFlight access:** create a group. |
| `list_beta_testers` | List an app's TestFlight testers. |
| `create_beta_tester` | **Changes TestFlight access:** add a tester to a group. |
| `add_build_to_beta_group` | **Changes TestFlight access:** give a group access to a build. |
| `add_tester_to_beta_group` | **Changes TestFlight access:** add an existing tester to a group. |
| `create_version` | **Creates draft:** create an App Store version. |
| `assign_build_to_version` | **Changes draft:** attach a processed build. |
| `list_review_submissions` | List App Review submissions. |
| `create_review_submission` | **Creates draft:** create a submission container. |
| `add_version_to_submission` | **Changes draft:** add a version to a submission. |
| `submit_review` | **Submits:** send the submission and all its items to App Review; requires `confirmSubmit: true`. |
| `release_version` | **Publishes:** manually release an approved version; requires `confirmPublish: true`. |
List tools accept `limit` and the `links.next` URL returned by Apple. Use resource IDs from prior responses. Adding a new locale requires both `create_localization` and `create_app_info_localization`; Apple rejects submissions when those locale sets differ. Screenshot display types are Apple's values, such as `APP_IPHONE_67` and `APP_IPAD_PRO_3GEN_129`. The screenshot upload uses Apple's `appScreenshots` flow; Apple also offers newer asset library APIs, which this package does not yet expose. [Localization requirements](https://developer.apple.com/documentation/appstoreconnectapi/post-v1-appstoreversionlocalizations), [screenshot upload workflow](https://developer.apple.com/documentation/appstoreconnectapi/uploading-assets-to-app-store-connect)
## Apple credentials
The Account Holder must [request access to the App Store Connect API](https://developer.apple.com/help/app-store-connect/get-started/app-store-connect-api/) before keys can be used. Create either a **team key** under **Users and Access → Integrations → App Store Connect API → Team Keys**, or an **individual key** from your profile. Team keys apply to **all apps** in the account; individual keys inherit the user's app access. Choose the narrowest role that supports your intended tools. [Apple key and role documentation](https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api), [role permissions](https://developer.apple.com/help/app-store-connect/reference/account-management/role-permissions/)
Download the `.p8` private key once and keep it outside this repository. Set:
- `APP_STORE_CONNECT_KEY_ID`: the 10-character key ID.
- `APP_STORE_CONNECT_PRIVATE_KEY_PATH`: absolute path to the `.p8` file.
- `APP_STORE_CONNECT_ISSUER_ID`: issuer UUID for a **team key**. Omit this for an **individual key**.
The server signs short-lived ES256 JWTs with `aud=appstoreconnect-v1`; team tokens use `iss`, individual tokens use `sub=user`. It refreshes tokens automatically. [Apple token specification](https://developer.apple.com/documentation/appstoreconnectapi/generating-tokens-for-api-requests)
The key's role controls what works. App Manager is a practical role for version metadata and publishing; Customer Support covers review response work. TestFlight access depends on the assigned role and app permissions. Verify the least-privilege role against a test app before production use. The server does not print keys, tokens, or raw Apple error bodies.
## MCP clients
After publication, the command is `npx -y app-store-connect-mcp`. Until then, build locally and use `node /absolute/path/to/app-store-mcp/dist/index.js` in place of that command.
### Codex CLI or app
```sh
codex mcp add app-store \
--env APP_STORE_CONNECT_KEY_ID=YOUR_KEY_ID \
--env APP_STORE_CONNECT_ISSUER_ID=YOUR_ISSUER_UUID \
--env APP_STORE_CONNECT_PRIVATE_KEY_PATH=/absolute/path/to/AuthKey_YOUR_KEY_ID.p8 \
-- npx -y app-store-connect-mcp
codex mcp list
```
For an individual key, omit the issuer environment option. Replace `npx -y app-store-connect-mcp` with the local `node` command until publication.
### Claude CLI (Claude Code)
```sh
claude mcp add \
--env APP_STORE_CONNECT_KEY_ID=YOUR_KEY_ID \
--env APP_STORE_CONNECT_ISSUER_ID=YOUR_ISSUER_UUID \
--env APP_STORE_CONNECT_PRIVATE_KEY_PATH=/absolute/path/to/AuthKey_YOUR_KEY_ID.p8 \
--transport stdio app-store \
-- npx -y app-store-connect-mcp
claude mcp get app-store
```
For an individual key, omit the issuer option. Until publication, replace the `npx` command with `node /absolute/path/to/app-store-mcp/dist/index.js`. Claude CLI saves the server for the current project by default; add `--scope user` before `app-store` to use it across projects. See [Claude Code MCP setup](https://code.claude.com/docs/en/mcp).
### Claude Desktop
Add this entry under the existing `mcpServers` object, keeping any other servers:
```json
{
"app-store": {
"command": "npx",
"args": ["-y", "app-store-connect-mcp"],
"env": {
"APP_STORE_CONNECT_KEY_ID": "YOUR_KEY_ID",
"APP_STORE_CONNECT_ISSUER_ID": "YOUR_ISSUER_UUID",
"APP_STORE_CONNECT_PRIVATE_KEY_PATH": "/absolute/path/to/AuthKey_YOUR_KEY_ID.p8"
}
}
}
```
For local use, set `command` to `node` and `args` to `["/absolute/path/to/app-store-mcp/dist/index.js"]`. For an individual key, remove the issuer field.
## License
MIT. See [LICENSE](LICENSE).
TDQS
Scored across 38 tools
Most tools target a distinct resource+action, and the MUTATES/SUBMITS prefixes plus hints like 'poll get_build_upload before assigning the build' clarify sequencing and side effects. A few pairs remain easy to confuse: list_builds vs list_build_uploads, create_localization vs create_app_info_localization, and add_build_to_beta_group vs assign_build_to_version, where the target (beta group vs version) must be read carefully.
All names use consistent snake_case verb_noun form (list_apps, get_build, create_beta_tester, upload_screenshot, add_tester_to_beta_group, submit_review). Verb choice tracks semantics predictably (list/get for reads, create/update/upload for writes, add_* for membership), so an agent can infer behavior from the name alone.
At 38 tools the surface is heavy, sitting well above the comfortable 3-15 range, though this reflects a genuinely large domain (apps, versions, builds, TestFlight, metadata assets, review submission). Each tool maps to a real App Store Connect operation, but the breadth makes discovery and correct selection harder than it needs to be.
The happy path from upload_ipa through assign_build_to_version to submit_review and release_version is fully covered, including TestFlight and screenshot/localization metadata. However, the surface has no delete or remove operations at all (no delete_screenshot, delete_localization, remove build/tester from a beta group, cancel review submission), and there is no update_app_info for app-level fields like privacy URLs, leaving agents with dead ends.