AppStore MCP Server

Servidor MCP (Rust, rmcp) que expone la API de Apple App Store Connect: aplicaciones, compras dentro de la aplicación, suscripciones, precios, versiones/metadatos, TestFlight, aprovisionamiento y carga de activos, además de herramientas JSON:API genéricas.

Documentación

appstore-mcp

Un servidor MCP, escrito en Rust, que expone la API de App Store Connect de Apple a agentes de IA. Cubre el ciclo de vida completo del producto — apps y metadatos, compras dentro de la app, suscripciones y sus ofertas, precios y disponibilidad, versiones de App Store, envío a revisión de App Review, TestFlight, aprovisionamiento y firma, subida de assets, compras promocionadas, reseñas de clientes, lanzamiento gradual, usuarios y acceso, eventos dentro de la app, Xcode Cloud y análisis de informes — a través de 114 herramientas, y puede alcanzar cualquier otro endpoint de App Store Connect mediante dos herramientas genéricas JSON:API.

Construido sobre el SDK oficial rmcp a través de stdio.

📖 Referencia completa de herramientas → docs/TOOLS.md — el propósito y los parámetros de cada herramienta.

Cada herramienta está etiquetada con anotaciones MCP, para que un cliente pueda distinguir list_apps de remove_user. Puedes servir solo los dominios que necesites (ASC_TOOLS) o solo las herramientas que no pueden escribir (ASC_READ_ONLY) — consulta Elegir qué herramientas servir.

Diseño: cobertura híbrida

La API de App Store Connect tiene cientos de endpoints pero es uniformemente JSON:API. En lugar de una herramienta por endpoint, este servidor es híbrido:

  • Herramientas seleccionadas (112) para los flujos de trabajo comunes, de varios pasos o propensos a errores — apps y metadatos, compras dentro de la app, suscripciones y ofertas, versiones, precios, disponibilidad, envío a revisión de App Review, TestFlight, aprovisionamiento, subida de assets, compras promocionadas, reseñas de clientes, lanzamiento gradual, usuarios, eventos dentro de la app, Xcode Cloud, informes de análisis y páginas de producto personalizadas.
  • Dos herramientas genéricas de escape — appstore_request y appstore_list — que pueden llamar a cualquier endpoint con documentos JSON:API sin procesar.

Herramientas

GrupoHerramientas
Genéricasappstore_request, appstore_list
Apps y metadatoslist_apps, get_app, update_app, list_app_infos, update_app_info, set_age_rating, create_app_info_localization, update_app_info_localization
Compras dentro de la app (v2)list_in_app_purchases, create_in_app_purchase, update_in_app_purchase, delete_in_app_purchase, create_iap_localization, set_iap_price_schedule, upload_iap_review_screenshot
Suscripcioneslist_subscription_groups, create_subscription_group, create_subscription, update_subscription, create_subscription_localization, set_subscription_price
Versiones y metadatoslist_app_store_versions, create_app_store_version, create_version_localization, update_version_localization
Envío a revisión de App Reviewcreate_review_submission, add_review_submission_item, submit_review_submission, list_review_submissions, submit_in_app_purchase, set_app_review_detail, create_app_encryption_declaration, assign_build_encryption_declaration
Precioslist_territories, list_iap_price_points, list_subscription_price_points
Disponibilidadset_iap_availability, set_subscription_availability, set_app_availability
TestFlightlist_builds, list_beta_groups, create_beta_group, add_beta_tester, submit_build_for_beta_review, set_build_test_notes, set_build_beta_detail, set_beta_app_review_detail, expire_build, add_build_to_beta_group
Aprovisionamiento y firmalist_bundle_ids, create_bundle_id, enable_bundle_id_capability, disable_bundle_id_capability, list_certificates, create_certificate, list_devices, register_device, list_profiles, create_profile
Assetsupload_app_screenshot, upload_app_preview, create_screenshot_set, create_preview_set, delete_screenshot_set, delete_preview_set, reorder_screenshots
Ofertas de suscripcióncreate_introductory_offer, create_promotional_offer, create_winback_offer, list_winback_offers
Códigos de ofertacreate_offer_code, generate_one_time_use_codes, create_custom_offer_code, list_offer_codes
Compras promocionadascreate_promoted_purchase, update_promoted_purchase, set_promoted_purchase_order, list_promoted_purchases
Reseñas de clienteslist_customer_reviews, respond_to_review, delete_review_response
Lanzamiento gradualstart_phased_release, update_phased_release
Usuarios y accesolist_users, invite_user, update_user, remove_user
Eventos dentro de la appcreate_app_event, create_app_event_localization, upload_app_event_screenshot
Xcode Cloudlist_ci_products, list_ci_workflows, start_ci_build, get_ci_build_run, list_ci_build_actions
Informes de análisisrequest_analytics_report, list_analytics_reports, list_analytics_report_instances, list_analytics_report_segments, download_analytics_segment
Páginas de producto personalizadaslist_custom_product_pages, get_custom_product_page, create_custom_product_page, update_custom_product_page, delete_custom_product_page, list_custom_product_page_versions, create_custom_product_page_version, list_custom_product_page_localizations, create_custom_product_page_localization, update_custom_product_page_localization, create_cpp_screenshot_set, create_cpp_preview_set

Consulta docs/TOOLS.md para la descripción y los parámetros de cada herramienta. Las imágenes de páginas de producto personalizadas se suben con las herramientas existentes upload_app_screenshot / upload_app_preview.

Instalación

Los binarios precompilados para macOS (universal), Linux (x86-64) y Windows (x86-64) se adjuntan a cada Release de GitHub. Elige el canal para tu cliente; todos necesitan credenciales (consulta Credenciales).

Claude Desktop — paquete de un clic

Descarga appstore-mcp.mcpb de la última release y ábrelo con Claude Desktop (Configuración → Extensiones → Instalar extensión…, o arrastra el archivo a la ventana). Te pedirá tu Issuer ID, Key ID y archivo de clave .p8. El paquete incluye los binarios de las tres plataformas y selecciona el correcto automáticamente.

Claude Code — marketplace de plugins

/plugin marketplace add forgeopslabs/appstore-mcp
/plugin install appstore-mcp@forgeopslabs

El plugin lanza el binario appstore-mcp desde tu PATH, así que instálalo primero — descarga el binario para tu sistema operativo desde la última release y colócalo en tu PATH, o cargo install --git https://github.com/forgeopslabs/appstore-mcp. Configura ASC_ISSUER_ID, ASC_KEY_ID y ASC_PRIVATE_KEY_PATH en el entorno desde el que inicias Claude Code.

Codex

Codex configura los servidores MCP directamente (sin marketplace). Con appstore-mcp en tu PATH:

codex mcp add appstore \
  --env ASC_ISSUER_ID=... --env ASC_KEY_ID=... \
  --env ASC_PRIVATE_KEY_PATH=/path/AuthKey_XXXXXX.p8 \
  -- appstore-mcp

o en ~/.codex/config.toml:

[mcp_servers.appstore]
command = "appstore-mcp"
args = []
env = { ASC_ISSUER_ID = "...", ASC_KEY_ID = "...", ASC_PRIVATE_KEY_PATH = "/path/AuthKey_XXXXXX.p8" }

Registro MCP

Publicado como io.github.forgeopslabs/appstore-mcp (metadatos en server.json) para que cualquier cliente compatible con MCP pueda descubrirlo.

Desde el código fuente

cargo build --release    # -> target/release/appstore-mcp

Credenciales

Genera una Team Key en App Store Connect → Usuarios y acceso → Integraciones → API de App Store Connect, y descarga el archivo .p8. Luego configura:

VariableRequeridaDescripción
ASC_ISSUER_ID✅UUID del emisor que se muestra sobre la tabla de claves.
ASC_KEY_ID✅El Key ID de la clave de API.
ASC_PRIVATE_KEYuna deContenido PEM de .p8 en línea.
ASC_PRIVATE_KEY_PATHuna deRuta al archivo .p8 descargado.
ASC_BASE_URLopcionalAnula el origen de la API.
ASC_LOGopcionalFiltro de registro (a stderr). Por defecto info.

Consulta .env.example. El servidor autentica cada solicitud con un JWT ES256 de corta duración firmado con tu clave (almacenado en caché y renovado automáticamente).

El servidor se inicia incluso sin credenciales para que un cliente pueda listar sus herramientas; las llamadas a herramientas devuelven entonces un error de configuración accionable hasta que se establezcan las credenciales.

Elegir qué herramientas servir

Cien definiciones de herramientas cuestan contexto en cada sesión, y un cliente que ve una lista plana no puede distinguir una lectura de un borrado. Dos controles lo solucionan:

VariablePor defectoDescripción
ASC_TOOLStodosGrupos de herramientas separados por comas para servir, o el ajuste predefinido core.
ASC_READ_ONLY0Sirve solo herramientas que no pueden modificar la cuenta.
ASC_TOOL_DISCOVERY0Expone solo search_tools, get_tool_details y call_discovered_tool; las herramientas de dominio filtradas permanecen disponibles mediante descubrimiento.
ASC_TOOLS=core                     # 41 tools: generic, apps, versions, assets, testflight, submission
ASC_TOOLS=testflight,provisioning  # just what a build-distribution agent needs
ASC_READ_ONLY=1                    # 35 read-only tools; writes are withheld entirely
ASC_TOOL_DISCOVERY=1               # 3 visible tools; discover domain tools on demand

Grupos: generic, apps, iap, subscriptions, versions, pricing, availability, submission, testflight, provisioning, assets, offers, offer-codes, promotions, reviews, users, events, xcode-cloud, analytics, custom-product-pages — más all y core. Un nombre no reconocido se advierte en stderr y no sirve nada en lugar de volver silenciosamente a todo.

En modo de solo lectura, appstore_request se mantiene pero rechaza cualquier método que no sea GET, por lo que la vía de escape aún alcanza endpoints sin una herramienta seleccionada sin convertirse en una forma de eludir la restricción.

Cada herramienta servida anuncia anotaciones MCP (readOnlyHint, destructiveHint, idempotentHint), que los clientes usan para decidir qué requiere confirmación. Nueve herramientas están marcadas como destructivas: las siete herramientas delete_*/remove_*, expire_build, disable_bundle_id_capability y appstore_request (que pueden alcanzar cualquier endpoint DELETE).

Modo descubrimiento. Busca por tarea, inspecciona el esquema de entrada completo y las anotaciones de seguridad de una herramienta coincidente, luego pasa su nombre y argumentos exactos a call_discovered_tool. ASC_TOOLS y ASC_READ_ONLY filtran el catálogo privado antes de la búsqueda o ejecución. Las llamadas directas a nombres de herramientas ocultas fallan. Esto es descubrimiento del lado del servidor: los esquemas inspeccionados aparecen en los resultados de las herramientas, no como nuevas herramientas MCP de primera clase inyectadas por el host. La herramienta de ejecución genérica se marca como destructiva siempre que las escrituras estén habilitadas; los hosts no pueden aplicar reglas de aprobación distintas a cada herramienta oculta. Inspecciona la herramienta subyacente y aprueba las escrituras antes de llamarla. El modo predeterminado preserva la superficie de herramientas anotadas individualmente existente.

Ajuste

Los valores predeterminados se eligen para que una llamada a herramienta no se cuelgue y una sola respuesta no sature el contexto de un agente. Todos son opcionales.

VariablePor defectoDescripción
ASC_TIMEOUT_SECS60Tiempo de espera de toda la solicitud. 0 lo desactiva.
ASC_CONNECT_TIMEOUT_SECS10Tiempo de espera de conexión. 0 lo desactiva.
ASC_TRANSFER_TIMEOUT_SECS300Tiempo de espera para subidas de assets y descargas de informes.
ASC_MAX_RETRIES3Reintentos después del primer intento. 0 los desactiva.
ASC_MAX_RESPONSE_BYTES60000Límite de tamaño del resultado de la herramienta. 0 lo desactiva.
ASC_COMPACT_RESPONSES1Elimina enlaces JSON:API redundantes de las respuestas.

Reintentos. Un 429 se reproduce para cualquier método, ya que Apple rechazó la solicitud sin aplicarla. Un 5xx o un tiempo de espera a mitad de vuelo se reproduce solo para GET/PATCH/PUT/DELETE — nunca POST, que de otro modo podría crear un recurso duplicado (y Apple reserva permanentemente identificadores como un ID de producto). La retroalimentación es exponencial con jitter y respeta Retry-After.

Forma de la respuesta. Los resultados se serializan de forma compacta — el JSON indentado medía 1,72× los bytes para contenido idéntico, por lo que el mismo presupuesto ahora lleva alrededor de un 40% más de los datos que solicitaste. Los enlaces self por recurso y las relaciones solo de enlace se eliminan: no se pierde contenido direccionable, y links.next sobrevive para la paginación. Si una respuesta aún supera el presupuesto, included se elimina primero, luego los elementos data finales, y el resultado lleva una clave _truncated que indica qué faltó y cómo acotar la consulta. Ten en cuenta que seguir links.next después de un recorte omitiría los elementos eliminados — vuelve a solicitar con un limit más pequeño en su lugar.

Compilar y ejecutar

cargo build --release
ASC_ISSUER_ID=... ASC_KEY_ID=... ASC_PRIVATE_KEY_PATH=/path/AuthKey_XXX.p8 \
  ./target/release/appstore-mcp

El servidor habla MCP a través de stdio. Los registros van a stderr; stdout es el canal del protocolo.

Uso con un cliente MCP

Ejemplo de configuración de cliente (p. ej., el mcpServers de Claude Desktop):

{
  "mcpServers": {
    "appstore": {
      "command": "/absolute/path/to/appstore-mcp/target/release/appstore-mcp",
      "env": {
        "ASC_ISSUER_ID": "00000000-0000-0000-0000-000000000000",
        "ASC_KEY_ID": "ABCD123456",
        "ASC_PRIVATE_KEY_PATH": "/absolute/path/to/AuthKey_ABCD123456.p8"
      }
    }
  }
}

Inspeccionar con el MCP Inspector

npx @modelcontextprotocol/inspector ./target/release/appstore-mcp

Notas de uso

  • Los IDs son opacos. Primero usa list/get para resolver los IDs de apps, compras dentro de la app (IAP), suscripciones, conjuntos y puntos de precio, y luego pásalos a las herramientas de crear/actualizar.
  • Los precios necesitan un punto de precio. Usa list_iap_price_points / list_subscription_price_points para obtener el id para set_iap_price_schedule / set_subscription_price.
  • Cargas de archivos (upload_*) toman una ruta de archivo local y ejecutan el flujo completo de reserva → carga fragmentada → confirmación MD5 en una sola llamada. El archivo se transmite en streaming, por lo que la memoria máxima es un fragmento en lugar del tamaño del archivo, y un fragmento que falla se reintenta por sí solo. Las capturas de pantalla/vistas previas requieren un appScreenshotSet / appPreviewSet existente; créalos con las herramientas genéricas si es necesario.
  • Paginación. appstore_list devuelve una página por defecto. Pasa max_pages (hasta 20) para seguir links.next y fusionar las páginas en un solo resultado — meta.hasMore te indica si queda algo más.
  • Datos de analítica. request_analytics_report → list_analytics_reports → list_analytics_report_instances → list_analytics_report_segments te da una URL prefirmada de segmento; download_analytics_segment la obtiene, la descomprime con gunzip y devuelve las filas como JSON. Apple puede tardar hasta 48 horas en generar el primer informe para una nueva solicitud.
  • Cualquier cosa no listada es accesible mediante appstore_request (método raw + ruta + cuerpo JSON:API) o appstore_list (GET paginado). Ejemplo: appstore_request { "method": "GET", "path": "/v1/apps/123/customerReviews" }.
  • No cubierto: los endpoints de informes de ventas/finanzas devuelven TSV comprimido con gzip (no JSON:API) y están fuera del alcance de estas herramientas.

Limitaciones (impuestas por Apple)

  • No puedes crear una app mediante la API. El recurso apps solo permite GET y UPDATE — POST /v1/apps devuelve 403 FORBIDDEN_ERROR. Crea el registro de la app en el sitio web de App Store Connect (Apps → ➕ → Nueva App); puedes pre-crear su bundle ID con create_bundle_id. Todas las demás herramientas operan sobre una app existente.
  • El productId de una compra dentro de la app eliminada está permanentemente reservado por Apple y no se puede reutilizar.

Desarrollo

cargo test                              # 200+ tests, no network or credentials needed
cargo clippy --all-targets -- -D warnings
cargo fmt --check

Las pruebas vienen en tres capas: pruebas unitarias puras para los constructores de cuerpos de solicitud, decisiones de reintento y modelado de respuestas; pruebas wiremock que manejan el cliente HTTP real contra una API simulada (reintentos, tiempos de espera, paginación, el protocolo de carga en tres pasos, descargas de segmentos); y tests/tool_surface.rs, que verifica los invariantes de lo que un cliente realmente ve — nombres únicos, descripciones reales, esquemas de objetos, anotaciones correctas y que ASC_TOOLS/ASC_READ_ONLY retienen exactamente lo que afirman.

La versión mínima compatible de Rust es 1.88, verificada por su propio trabajo de CI.

Regenera la referencia de herramientas después de agregar/cambiar herramientas (necesita el binario de lanzamiento; no se requieren credenciales):

cargo build --release && python3 scripts/gen_tools_doc.py   # rewrites docs/TOOLS.md

Pruebas de integración en vivo

scripts/integration_test.py maneja el servidor compilado contra la API real. Solo lectura por defecto; --write agrega un ciclo de vida de IAP autolimpiante.

cargo build --release
# Credentials via env (ASC_ISSUER_ID/ASC_KEY_ID/ASC_PRIVATE_KEY_PATH) or local
# appstore-connect.txt + AuthKey_*.p8 in the repo root (both gitignored).
python3 scripts/integration_test.py --app <APP_ID>          # read-only sweep
python3 scripts/integration_test.py --app <APP_ID> --write  # + write lifecycle

Licencia

MIT