# App Store Connect MCP Server [![npm version](https://img.shields.io/npm/v/asc-mcp.svg)](https://www.npmjs.com/package/asc-mcp) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Node.js Version](https://img.shields.io/node/v/asc-mcp.svg)](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
Claude Desktop 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" } } } } ```
Cursor 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" } } } } ```
VS Code with Continue 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" } } } } ```
Using P8 Content Instead of Path 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-----" } } } } ```
## 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)