App Store Connect MCP Server
# App Store Connect MCP Server
[](https://www.npmjs.com/package/asc-mcp)
[](https://opensource.org/licenses/MIT)
[](https://nodejs.org)
A Model Context Protocol (MCP) server for Apple's App Store Connect API. Manage your iOS, macOS, tvOS, and visionOS apps directly from Claude, Cursor, or any MCP-compatible client.
## Features
- **App Store Localizations** - Full CRUD for version descriptions, keywords, and what's new
- **App Management** - List and inspect apps across all platforms
- **Version Control** - Create and manage app store versions
- **Beta Testing** - Manage TestFlight groups and testers
- **Screenshot Management** - Upload and organize app screenshots
- **Bundle ID Management** - Full CRUD for bundle identifiers
- **Device Management** - List and inspect registered devices
- **User Management** - List and inspect team users
- **Build Management** - List and inspect app builds
- **Category & Pricing** - Browse categories, check pricing and availability
- **Pricing & PPP** - Set per-territory pricing with Purchase Power Parity support
- **In-App Purchases** - Create and manage one-time purchases (lifetime/non-consumable, consumable, non-renewing): metadata, localization, pricing & PPP, availability, and review submission (review-screenshot upload excluded)
- **Analytics Reports** - Request and download app analytics reports (engagement, commerce, usage, performance)
- **Sales & Finance** - Download sales, trends, and financial reports
- **Performance & Diagnostics** - App/build power & performance metrics and diagnostic logs
- **Secure by Default** - ES256 JWT auth with automatic token refresh, credential redaction in logs
## Table of Contents
- [Quick Start](#quick-start)
- [Installation](#installation)
- [Configuration](#configuration)
- [Available Tools](#available-tools)
- [Usage Examples](#usage-examples)
- [Security](#security)
- [Troubleshooting](#troubleshooting)
- [Development](#development)
- [License](#license)
## Quick Start
```bash
# 1. Install
npm install -g asc-mcp
# 2. Set credentials (get from App Store Connect > Users and Access > Keys)
export APP_STORE_CONNECT_KEY_ID="YOUR_KEY_ID"
export APP_STORE_CONNECT_ISSUER_ID="YOUR_ISSUER_ID"
export APP_STORE_CONNECT_P8_PATH="/path/to/AuthKey.p8"
# 3. Add to your MCP client config and start using!
```
## Installation
### npm (recommended)
```bash
npm install -g asc-mcp
```
### Using npx
```bash
npx asc-mcp
```
### From Source
```bash
git clone https://github.com/SardorbekR/appstore-connect-mcp.git
cd appstore-connect-mcp
npm install
npm run build
```
## Configuration
### Prerequisites: Get Your Apple API Credentials
1. Sign in to [App Store Connect](https://appstoreconnect.apple.com)
2. Go to **Users and Access** → **Integrations** → **App Store Connect API**
3. Click **Generate API Key** (or use existing)
4. Select appropriate role (Admin or App Manager recommended)
5. **Download the .p8 file** - you can only download it once!
6. Note your **Key ID** (shown in the keys list)
7. Note your **Issuer ID** (shown at the top of the page)
### Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| `APP_STORE_CONNECT_KEY_ID` | Yes | Your API Key ID (e.g., `ABC123DEFG`) |
| `APP_STORE_CONNECT_ISSUER_ID` | Yes | Your Issuer ID (UUID format) |
| `APP_STORE_CONNECT_P8_PATH` | Yes* | Path to your `.p8` private key file |
| `APP_STORE_CONNECT_P8_CONTENT` | Yes* | Raw content of `.p8` key (alternative to path) |
*One of `P8_PATH` or `P8_CONTENT` is required.
### MCP Client Configuration
<details>
<summary><strong>Claude Desktop</strong></summary>
Add to your Claude Desktop config file:
**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"app-store-connect": {
"command": "asc-mcp",
"env": {
"APP_STORE_CONNECT_KEY_ID": "YOUR_KEY_ID",
"APP_STORE_CONNECT_ISSUER_ID": "YOUR_ISSUER_ID",
"APP_STORE_CONNECT_P8_PATH": "/absolute/path/to/AuthKey.p8"
}
}
}
}
```
</details>
<details>
<summary><strong>Cursor</strong></summary>
Add to your Cursor MCP settings (Settings → MCP Servers):
```json
{
"mcpServers": {
"app-store-connect": {
"command": "npx",
"args": ["-y", "asc-mcp"],
"env": {
"APP_STORE_CONNECT_KEY_ID": "YOUR_KEY_ID",
"APP_STORE_CONNECT_ISSUER_ID": "YOUR_ISSUER_ID",
"APP_STORE_CONNECT_P8_PATH": "/absolute/path/to/AuthKey.p8"
}
}
}
}
```
</details>
<details>
<summary><strong>VS Code with Continue</strong></summary>
Add to your Continue configuration:
```json
{
"mcpServers": {
"app-store-connect": {
"command": "asc-mcp",
"env": {
"APP_STORE_CONNECT_KEY_ID": "YOUR_KEY_ID",
"APP_STORE_CONNECT_ISSUER_ID": "YOUR_ISSUER_ID",
"APP_STORE_CONNECT_P8_PATH": "/absolute/path/to/AuthKey.p8"
}
}
}
}
```
</details>
<details>
<summary><strong>Using P8 Content Instead of Path</strong></summary>
For CI/CD or containerized environments, you can pass the key content directly:
```json
{
"mcpServers": {
"app-store-connect": {
"command": "asc-mcp",
"env": {
"APP_STORE_CONNECT_KEY_ID": "YOUR_KEY_ID",
"APP_STORE_CONNECT_ISSUER_ID": "YOUR_ISSUER_ID",
"APP_STORE_CONNECT_P8_CONTENT": "-----BEGIN PRIVATE KEY-----\nMIGT...your key here...AB12\n-----END PRIVATE KEY-----"
}
}
}
}
```
</details>
## Available Tools
### Apps
| Tool | Description | Parameters |
|------|-------------|------------|
| `list_apps` | List all apps in your account | `limit?` (number, 1-200) |
| `get_app` | Get details of a specific app | `appId` (string, required) |
### Versions
| Tool | Description | Parameters |
|------|-------------|------------|
| `list_app_versions` | List all versions for an app | `appId`, `platform?`, `versionState?`, `limit?` |
| `get_app_version` | Get version details | `versionId` |
| `create_app_version` | Create a new app version | `appId`, `platform`, `versionString`, `releaseType?` |
### Version Localizations
| Tool | Description | Parameters |
|------|-------------|------------|
| `list_version_localizations` | List localizations for a version | `versionId`, `limit?` |
| `get_version_localization` | Get localization details | `localizationId` |
| `create_version_localization` | Add a new locale | `versionId`, `locale`, `description?`, `keywords?`, `whatsNew?` |
| `update_version_localization` | Update localization | `localizationId`, `description?`, `keywords?`, `whatsNew?`, `promotionalText?` |
| `delete_version_localization` | Remove a locale | `localizationId` |
### App Info Localizations
| Tool | Description | Parameters |
|------|-------------|------------|
| `list_app_infos` | List app info records | `appId`, `limit?` |
| `list_app_info_localizations` | List name/subtitle localizations | `appInfoId`, `limit?` |
| `update_app_info_localization` | Update app name, subtitle | `localizationId`, `name?`, `subtitle?`, `privacyPolicyUrl?` |
### Beta Testing (TestFlight)
| Tool | Description | Parameters |
|------|-------------|------------|
| `list_beta_groups` | List beta groups for an app | `appId`, `limit?` |
| `list_beta_testers` | List testers in a group | `betaGroupId`, `limit?` |
| `add_beta_tester` | Add a tester to a group | `betaGroupId`, `email`, `firstName?`, `lastName?` |
| `remove_beta_tester` | Remove a tester from a group | `betaGroupId`, `betaTesterId` |
### Screenshots
| Tool | Description | Parameters |
|------|-------------|------------|
| `list_screenshot_sets` | List screenshot sets | `localizationId`, `limit?` |
| `list_screenshots` | List screenshots in a set | `screenshotSetId`, `limit?` |
| `upload_screenshot` | Upload a new screenshot | `screenshotSetId`, `fileName`, `fileSize`, `filePath` |
### Bundle IDs
| Tool | Description | Parameters |
|------|-------------|------------|
| `list_bundle_ids` | List all bundle IDs | `limit?`, `platform?` |
| `get_bundle_id` | Get bundle ID details | `bundleIdId` |
| `create_bundle_id` | Register a new bundle ID | `identifier`, `name`, `platform` |
| `update_bundle_id` | Update bundle ID name | `bundleIdId`, `name` |
| `delete_bundle_id` | Delete a bundle ID | `bundleIdId` |
### Devices
| Tool | Description | Parameters |
|------|-------------|------------|
| `list_devices` | List registered devices | `limit?`, `platform?`, `status?` |
| `get_device` | Get device details | `deviceId` |
### Users
| Tool | Description | Parameters |
|------|-------------|------------|
| `list_users` | List team users | `limit?`, `roles?` |
| `get_user` | Get user details | `userId` |
### Builds
| Tool | Description | Parameters |
|------|-------------|------------|
| `list_builds` | List builds for an app | `appId`, `limit?` |
| `get_build` | Get build details | `buildId` |
### Categories & Pricing
| Tool | Description | Parameters |
|------|-------------|------------|
| `list_app_categories` | List app categories | `limit?`, `platform?` |
| `get_app_price_schedule` | Get app pricing info | `appId` |
| `get_app_availability` | Get app territory availability | `appId` |
### Pricing (PPP)
| Tool | Description | Parameters |
|------|-------------|------------|
| `list_territories` | List all territories with currencies | `limit?` |
| `list_app_price_points` | List available price tiers for an app | `appId`, `territory?`, `limit?` |
| `get_price_point_equalizations` | Get PPP equivalent prices across countries | `pricePointId`, `territories?`, `limit?` |
| `set_app_prices` | Set per-territory manual pricing (replaces entire schedule) | `appId`, `baseTerritory`, `manualPrices` |
### In-App Purchases (Lifetime / Non-Consumable)
One-time purchases via Apple's In-App Purchases v2 API. `create_in_app_purchase` defaults to `NON_CONSUMABLE` — a "lifetime" unlock. To ship one: create → add a localization → set a price → set availability → submit for review.
> **Note:** App Review usually requires a review screenshot on the in-app purchase. Uploading IAP review screenshots is not yet covered by these tools — add one in App Store Connect if `submit_in_app_purchase_for_review` is rejected for a missing screenshot.
| Tool | Description | Parameters |
|------|-------------|------------|
| `list_in_app_purchases` | List an app's in-app purchases, optionally by type | `appId`, `inAppPurchaseType?`, `limit?` |
| `get_in_app_purchase` | Get a single in-app purchase's details and state | `inAppPurchaseId` |
| `create_in_app_purchase` | Create an IAP (defaults to NON_CONSUMABLE / lifetime) | `appId`, `name`, `productId`, `inAppPurchaseType?`, `familySharable?`, `reviewNote?` |
| `update_in_app_purchase` | Update name, Family Sharing, or review note (productId/type immutable) | `inAppPurchaseId`, `name?`, `familySharable?`, `reviewNote?` |
| `delete_in_app_purchase` | Delete an in-app purchase | `inAppPurchaseId` |
| `list_in_app_purchase_localizations` | List localized names/descriptions | `inAppPurchaseId`, `limit?` |
| `create_in_app_purchase_localization` | Add a localized display name (+ description) | `inAppPurchaseId`, `locale`, `name`, `description?` |
| `update_in_app_purchase_localization` | Update a localization | `localizationId`, `name?`, `description?` |
| `delete_in_app_purchase_localization` | Delete a localization | `localizationId` |
| `list_in_app_purchase_price_points` | List price points (customer price & proceeds) | `inAppPurchaseId`, `territory?`, `limit?`, `offset?` |
| `get_in_app_purchase_price_point_equalizations` | Apple's PPP-equivalent price points for a base price point | `pricePointId`, `territories?`, `limit?` |
| `set_in_app_purchase_price` | Set pricing (replaces schedule; one base territory to auto-equalize, or full PPP list) | `inAppPurchaseId`, `baseTerritory`, `manualPrices` |
| `list_in_app_purchase_prices` | Read current per-territory prices (manual; optional automatic) | `inAppPurchaseId`, `territory?`, `includeAutomatic?`, `limit?` |
| `get_in_app_purchase_availability` | Get territory availability | `inAppPurchaseId` |
| `set_in_app_purchase_availability` | Set territory availability | `inAppPurchaseId`, `availableInNewTerritories`, `territories?` |
| `submit_in_app_purchase_for_review` | Submit the IAP to App Review (independent of an app version) | `inAppPurchaseId` |
### Analytics Reports
| Tool | Description | Parameters |
|------|-------------|------------|
| `create_analytics_report_request` | Request an analytics report (ongoing or one-time snapshot) | `appId`, `accessType` |
| `list_analytics_report_requests` | List analytics report requests for an app | `appId` |
| `get_analytics_report_request` | Get an analytics report request | `requestId` |
| `delete_analytics_report_request` | Delete an analytics report request | `requestId` |
| `list_analytics_reports` | List reports available within a request | `requestId` |
| `list_analytics_report_instances` | List instances (by date) of a report | `reportId` |
| `list_analytics_report_segments` | List downloadable segments of a report instance | `instanceId` |
| `download_analytics_report_segment` | Download a report segment | `url` |
### Sales & Finance
| Tool | Description | Parameters |
|------|-------------|------------|
| `get_sales_report` | Download a sales/trends report | `vendorNumber`, `reportType`, `reportSubType`, `frequency`, `reportDate` |
| `get_finance_report` | Download a financial report | `vendorNumber`, `regionCode`, `reportDate`, `reportType` |
### Performance & Diagnostics
| Tool | Description | Parameters |
|------|-------------|------------|
| `get_app_perf_metrics` | Get an app's power & performance metrics | `appId`, `metricType` |
| `get_build_perf_metrics` | Get a build's performance metrics | `buildId`, `metricType` |
| `list_diagnostic_signatures` | List diagnostic signatures for a build | `buildId` |
| `list_diagnostic_logs` | List diagnostic logs for a signature | `signatureId` |
## Usage Examples
### List Your Apps
> "Show me all my apps in App Store Connect"
Claude will use `list_apps` to retrieve and display your apps.
### Update App Description
> "Update the English description for version 2.0 of MyApp to: 'A revolutionary app that simplifies your daily tasks.'"
Claude will:
1. Find the app using `list_apps`
2. Get the version using `list_app_versions`
3. Find the English localization using `list_version_localizations`
4. Update it using `update_version_localization`
### Add Japanese Localization
> "Add Japanese localization to MyApp version 2.0 with description '素晴らしいアプリです' and keywords 'アプリ,便利,簡単'"
Claude will use `create_version_localization` with locale `ja`.
### Add a Beta Tester
> "Add john@example.com as a beta tester to the Internal Testing group for MyApp"
Claude will:
1. Find the app and beta group using `list_beta_groups`
2. Add the tester using `add_beta_tester`
### Set PPP Pricing
> "Show me the equivalent prices for my $9.99 tier in India, Brazil, and Turkey"
Claude will:
1. Find the $9.99 price point using `list_app_price_points`
2. Get equivalent prices using `get_price_point_equalizations`
3. Show you the PPP-adjusted prices in each territory
> "Set my app to $9.99 in the US and use PPP pricing for India and Brazil"
Claude will use `set_app_prices` with the appropriate price point IDs for each territory.
### Create a Lifetime Purchase
> "Add a $99.99 lifetime unlock to MyApp with PPP pricing for India and Brazil"
Claude will:
1. Create a non-consumable IAP with `create_in_app_purchase`
2. Add a display name with `create_in_app_purchase_localization`
3. Find the $99.99 tier with `list_in_app_purchase_price_points`, then PPP equivalents with `get_in_app_purchase_price_point_equalizations`
4. Apply per-territory pricing with `set_in_app_purchase_price` and open availability with `set_in_app_purchase_availability`
5. Confirm the result with `list_in_app_purchase_prices`
### Check Version Status
> "What's the status of all versions of MyApp?"
Claude will use `list_app_versions` to show version states (PREPARE_FOR_SUBMISSION, IN_REVIEW, READY_FOR_SALE, etc.)
## Security
### Credential Handling
- **Private keys** are never logged or exposed in error messages
- **JWT tokens** are automatically redacted from any error output
- **Issuer IDs** (UUIDs) are redacted from logs
- Token caching minimizes key usage (15-min tokens, refreshed at 10 min)
### Path Validation
- P8 file paths are validated against directory traversal attacks (`..` not allowed)
- Only absolute paths are resolved
### Best Practices
1. **Never commit credentials** - Use environment variables or a secrets manager
2. **Restrict API key permissions** - Use minimal required role (App Manager for most operations)
3. **Rotate keys periodically** - Generate new API keys and revoke old ones
4. **Secure your .p8 file** - Set file permissions to `600` (owner read/write only)
```bash
chmod 600 /path/to/AuthKey.p8
```
## Troubleshooting
### "Configuration error: APP_STORE_CONNECT_KEY_ID environment variable is required"
Ensure all required environment variables are set:
- `APP_STORE_CONNECT_KEY_ID`
- `APP_STORE_CONNECT_ISSUER_ID`
- `APP_STORE_CONNECT_P8_PATH` or `APP_STORE_CONNECT_P8_CONTENT`
### "Failed to read private key"
1. Verify the path in `APP_STORE_CONNECT_P8_PATH` is correct and absolute
2. Check file permissions: `ls -la /path/to/AuthKey.p8`
3. Ensure the file is a valid `.p8` from Apple (starts with `-----BEGIN PRIVATE KEY-----`)
### "Authentication failed"
This usually means:
1. The API key was revoked in App Store Connect
2. The Key ID or Issuer ID doesn't match the .p8 file
3. The .p8 file is corrupted or incomplete
### "Rate limit exceeded"
The server includes built-in rate limiting (50 requests/minute). If you hit Apple's limits:
1. Wait for the indicated retry time
2. Batch your operations when possible
3. The server automatically retries with exponential backoff
### Tools Not Appearing in Claude
1. Verify the server is running: check Claude Desktop logs
2. Ensure the config file path is correct for your OS
3. Restart Claude Desktop after config changes
## Development
### Prerequisites
- Node.js 20+
- npm or pnpm
### Setup
```bash
# Clone the repository
git clone https://github.com/SardorbekR/appstore-connect-mcp.git
cd appstore-connect-mcp
# Install dependencies
npm install
# Build
npm run build
# Run tests
npm test
# Lint
npm run lint
# Type check
npm run typecheck
```
### Project Structure
```
src/
├── index.ts # MCP server entry point
├── auth/
│ └── jwt.ts # JWT token generation & caching
├── api/
│ ├── client.ts # HTTP client with retry logic
│ └── types.ts # TypeScript interfaces
├── tools/
│ ├── index.ts # Tool registry
│ ├── apps.tools.ts
│ ├── versions.tools.ts
│ ├── localizations.tools.ts
│ ├── app-info.tools.ts
│ ├── beta.tools.ts
│ ├── screenshots.tools.ts
│ ├── bundle-ids.tools.ts
│ ├── devices.tools.ts
│ ├── users.tools.ts
│ ├── builds.tools.ts
│ └── categories.tools.ts
└── utils/
├── errors.ts # Error classes with redaction
└── validation.ts # Zod schemas
```
### Running Locally
```bash
# Development mode with auto-reload
npm run dev
# Or run the built version
npm start
```
### Contributing
1. Fork the repository
2. Create a feature branch: `git checkout -b feature/my-feature`
3. Make your changes and add tests
4. Run `npm test` and `npm run lint`
5. Submit a pull request
## License
MIT License - see [LICENSE](LICENSE) for details.
## Links
- [Security Policy](SECURITY.md)
- [App Store Connect API Documentation](https://developer.apple.com/documentation/appstoreconnectapi)
- [Model Context Protocol](https://modelcontextprotocol.io)
TDQS
Scored across 52 tools
Each tool targets a distinct resource and action, with clear naming that avoids overlap. Even tools like get_app_perf_metrics and get_build_perf_metrics are differentiated by scope (app-level vs build-level).
All tools follow a consistent verb_noun pattern with underscores, using predictable verbs like list_, get_, create_, delete_, update_, upload_. No mixing of cases or irregular patterns.
52 tools is a large number, but the App Store Connect platform has diverse functionality (analytics, beta, pricing, screenshots, diagnostics). The count is borderline heavy but each tool serves a specific, justified purpose.
The tool set covers core CRUD operations for apps, versions, localizations, builds, beta testing, devices, users, and various reports. Minor gaps include lack of submission/review management and in-app purchase handling, but the core workflows are well-supported.