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_requestyappstore_list— que pueden llamar a cualquier endpoint con documentos JSON:API sin procesar.
Herramientas
| Grupo | Herramientas |
|---|---|
| Genéricas | appstore_request, appstore_list |
| Apps y metadatos | list_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 |
| Suscripciones | list_subscription_groups, create_subscription_group, create_subscription, update_subscription, create_subscription_localization, set_subscription_price |
| Versiones y metadatos | list_app_store_versions, create_app_store_version, create_version_localization, update_version_localization |
| Envío a revisión de App Review | create_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 |
| Precios | list_territories, list_iap_price_points, list_subscription_price_points |
| Disponibilidad | set_iap_availability, set_subscription_availability, set_app_availability |
| TestFlight | list_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 firma | list_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 |
| Assets | upload_app_screenshot, upload_app_preview, create_screenshot_set, create_preview_set, delete_screenshot_set, delete_preview_set, reorder_screenshots |
| Ofertas de suscripción | create_introductory_offer, create_promotional_offer, create_winback_offer, list_winback_offers |
| Códigos de oferta | create_offer_code, generate_one_time_use_codes, create_custom_offer_code, list_offer_codes |
| Compras promocionadas | create_promoted_purchase, update_promoted_purchase, set_promoted_purchase_order, list_promoted_purchases |
| Reseñas de clientes | list_customer_reviews, respond_to_review, delete_review_response |
| Lanzamiento gradual | start_phased_release, update_phased_release |
| Usuarios y acceso | list_users, invite_user, update_user, remove_user |
| Eventos dentro de la app | create_app_event, create_app_event_localization, upload_app_event_screenshot |
| Xcode Cloud | list_ci_products, list_ci_workflows, start_ci_build, get_ci_build_run, list_ci_build_actions |
| Informes de análisis | request_analytics_report, list_analytics_reports, list_analytics_report_instances, list_analytics_report_segments, download_analytics_segment |
| Páginas de producto personalizadas | list_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:
| Variable | Requerida | Descripció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_KEY | una de | Contenido PEM de .p8 en línea. |
ASC_PRIVATE_KEY_PATH | una de | Ruta al archivo .p8 descargado. |
ASC_BASE_URL | opcional | Anula el origen de la API. |
ASC_LOG | opcional | Filtro 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:
| Variable | Por defecto | Descripción |
|---|---|---|
ASC_TOOLS | todos | Grupos de herramientas separados por comas para servir, o el ajuste predefinido core. |
ASC_READ_ONLY | 0 | Sirve solo herramientas que no pueden modificar la cuenta. |
ASC_TOOL_DISCOVERY | 0 | Expone 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.
| Variable | Por defecto | Descripción |
|---|---|---|
ASC_TIMEOUT_SECS | 60 | Tiempo de espera de toda la solicitud. 0 lo desactiva. |
ASC_CONNECT_TIMEOUT_SECS | 10 | Tiempo de espera de conexión. 0 lo desactiva. |
ASC_TRANSFER_TIMEOUT_SECS | 300 | Tiempo de espera para subidas de assets y descargas de informes. |
ASC_MAX_RETRIES | 3 | Reintentos después del primer intento. 0 los desactiva. |
ASC_MAX_RESPONSE_BYTES | 60000 | Límite de tamaño del resultado de la herramienta. 0 lo desactiva. |
ASC_COMPACT_RESPONSES | 1 | Elimina 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_pointspara obtener elidparaset_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 unappScreenshotSet/appPreviewSetexistente; créalos con las herramientas genéricas si es necesario. - Paginación.
appstore_listdevuelve una página por defecto. Pasamax_pages(hasta 20) para seguirlinks.nexty fusionar las páginas en un solo resultado —meta.hasMorete indica si queda algo más. - Datos de analítica.
request_analytics_report→list_analytics_reports→list_analytics_report_instances→list_analytics_report_segmentste da una URL prefirmada de segmento;download_analytics_segmentla 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) oappstore_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
appssolo permite GET y UPDATE —POST /v1/appsdevuelve403 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 concreate_bundle_id. Todas las demás herramientas operan sobre una app existente. - El
productIdde 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