Skip to main content
Glama
AppVisionOS

Apple Ads MCP

apple-search-ads-mcp

CI npm version npm downloads License: MIT Node MCP Glama MCP server

Un servidor del Protocolo de Contexto de Modelos (MCP) que envuelve la API completa de gestión de campañas de Apple Search Ads (ahora Apple Ads) v5. 74 herramientas tipadas, mapeo 1:1 a cada endpoint documentado de la v5: campañas, grupos de anuncios, anuncios, creatividades, páginas de producto personalizadas, palabras clave, palabras clave negativas, informes, informes de cuota de impresiones, órdenes de presupuesto, ACLs, búsqueda geo/app, metadatos de apps, auditorías de motivos de rechazo, además de un passthrough directo para cualquier endpoint futuro.

Ciclo de vida de la API: Apple Ads (Search Ads) v5 es la API de producción actual. La v5 dejará de estar disponible el 26 de enero de 2027 en favor de la nueva "Apple Ads Platform API" que llegará en el verano de 2026. Este servidor apunta a la v5.0 → v5.5.

Instalación rápida

git clone https://github.com/AppVisionOS/apple-search-ads-mcp.git
cd apple-search-ads-mcp
npm install
npm run build

Luego regístralo con Claude Code en una línea:

claude mcp add apple-search-ads --scope user \
  -e ASA_CLIENT_ID=SEARCHADS.xxxx \
  -e ASA_TEAM_ID=SEARCHADS.xxxx \
  -e ASA_KEY_ID=xxxx \
  -e ASA_PRIVATE_KEY_PATH=/absolute/path/to/asa-private.p8 \
  -e ASA_ORG_ID=1234567 \
  -- node $(pwd)/dist/index.js

Configuración

1. Obtener credenciales de API

La interfaz de usuario de Apple Ads divide las credenciales en dos pantallas. La pestaña API dentro de la Configuración de la cuenta solo gestiona el acceso para proveedores de servicios externos; para tu propio acceso programático, el flujo pasa primero por Gestión de usuarios.

a) Invitar a un usuario de API

En app-ads.apple.comConfiguración de la cuenta → Gestión de usuarios → Invitar usuario:

  • Correo electrónico: cualquier dirección que controles (puede ser la tuya; Apple requiere un Apple ID separado para el usuario de la API)

  • Rol: elige uno con permisos de API (p. ej., API Account Manager)

  • Envía la invitación y luego acéptala desde la bandeja de entrada invitada

b) Generar el par de claves localmente

Mientras se procesa la invitación, genera un par de claves ES256 en tu máquina. Asegúrate de que sea PKCS#8; los ejemplos .p8 de Apple y la salida antigua de openssl ecparam no son PKCS#8 y jose no puede cargarlos.

# CORRECT — produces PKCS#8 (-----BEGIN PRIVATE KEY-----)
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out asa-private.p8
openssl ec -in asa-private.p8 -pubout -out asa-public.pem

# If you already produced traditional EC (-----BEGIN EC PRIVATE KEY-----), convert it:
#   openssl pkcs8 -topk8 -nocrypt -in asa-private.p8 -out asa-private-pkcs8.p8

Guarda asa-private.p8 en un lugar seguro (p. ej., ~/.apple-search-ads/, chmod 600). Solo pegarás la mitad pública en Apple.

c) Generar el cliente de API

Cierra sesión y vuelve a iniciarla como el usuario de API invitado (no la cuenta de administrador). Ve a Configuración de la cuenta → API. Verás una pantalla de Credenciales de cliente con un área de texto para la Clave pública; esto solo aparece para los usuarios que tienen el rol de API.

  1. Pega el contenido de asa-public.pem (con los marcadores -----BEGIN PUBLIC KEY----- / -----END PUBLIC KEY-----).

  2. Haz clic en Generar cliente de API.

  3. Copia los tres valores que te muestra Apple: ID de cliente, ID de equipo, ID de clave; estos no vuelven a aparecer más tarde.

2. Instalación

npm install
npm run build

3. Configuración

Copia .env.example a .env y rellénalo, o pasa las variables de entorno a través de tu cliente MCP.

ASA_CLIENT_ID=SEARCHADS.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ASA_TEAM_ID=SEARCHADS.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ASA_KEY_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ASA_PRIVATE_KEY_PATH=/absolute/path/to/private-key.p8
ASA_ORG_ID=1234567   # optional default; can be overridden per call

ASA_PRIVATE_KEY (contenido PEM en línea, con escapes \n si se inyecta vía JSON) es compatible como alternativa a ASA_PRIVATE_KEY_PATH.

4. Conectar a tu cliente MCP

{
  "mcpServers": {
    "apple-search-ads": {
      "command": "node",
      "args": ["/absolute/path/to/apple-search-ads-mcp/dist/index.js"],
      "env": {
        "ASA_CLIENT_ID": "SEARCHADS.xxxx...",
        "ASA_TEAM_ID": "SEARCHADS.xxxx...",
        "ASA_KEY_ID": "xxxx...",
        "ASA_PRIVATE_KEY_PATH": "/absolute/path/to/private-key.p8",
        "ASA_ORG_ID": "1234567"
      }
    }
  }
}

Autenticación

El servidor maneja el flujo de credenciales de cliente OAuth 2.0 con una aserción de cliente JWT ES256:

  1. Firma un JWT con tu clave privada .p8 (encabezado: kid=ID de clave, alg=ES256; carga útil: iss=ID de equipo, sub=ID de cliente, aud=https://appleid.apple.com).

  2. Haz un POST a https://appleid.apple.com/auth/oauth2/token con grant_type=client_credentials y scope=searchadsorg.

  3. Usa el token de acceso de 1 hora devuelto con Authorization: Bearer … y X-AP-Context: orgId=… en cada llamada a la API.

El token se almacena en caché en memoria hasta unos 30 segundos antes de su vencimiento, por lo que firmas una aserción e intercambias un token por hora. En caso de 401, el servidor fuerza la actualización y reintenta una vez. En caso de 429/5xx, espera (respetando Retry-After) hasta 3 veces.

Inventario de herramientas (74 herramientas)

Cuenta y acceso (2)

org_acls, me_user: llama sin un contexto de organización para descubrir qué puede hacer tu token.

Descubrimiento (3)

search_apps, search_geo, geo_lookup

Metadatos de apps (6)

apps_get, apps_locale_details, apps_eligibilities_find, apps_assets_find, creative_app_preview_devices, countries_or_regions_list

Páginas de producto personalizadas (3)

cpp_list, cpp_get, cpp_locale_details

Campañas (6)

campaigns_create, campaigns_get, campaigns_list, campaigns_find, campaigns_update, campaigns_delete

Grupos de anuncios (7)

adgroups_create, adgroups_get, adgroups_list, adgroups_find_in_campaign, adgroups_find_org_wide, adgroups_update, adgroups_delete

Creatividades (4)

creatives_create, creatives_list, creatives_get, creatives_find: las creatividades envuelven una referencia a una Página de producto personalizada, Página de producto predeterminada o Conjunto de creatividades. Los anuncios se vinculan a las creatividades mediante creativeId.

Anuncios (7)

ads_create, ads_get, ads_list, ads_find_in_campaign, ads_find_org_wide, ads_update, ads_delete

Palabras clave de segmentación (7)

targeting_keywords_create, targeting_keywords_get, targeting_keywords_list, targeting_keywords_find, targeting_keywords_update, targeting_keywords_delete (en lote), targeting_keywords_delete_single

Palabras clave negativas: alcance de grupo de anuncios (6)

adgroup_negative_keywords_create, adgroup_negative_keywords_get, adgroup_negative_keywords_list, adgroup_negative_keywords_find, adgroup_negative_keywords_update, adgroup_negative_keywords_delete

Palabras clave negativas: alcance de campaña (6)

campaign_negative_keywords_create, campaign_negative_keywords_get, campaign_negative_keywords_list, campaign_negative_keywords_find, campaign_negative_keywords_update, campaign_negative_keywords_delete

Informes (7)

Herramienta

Endpoint

reports_campaigns

POST /reports/campaigns

reports_adgroups

POST /reports/campaigns/{id}/adgroups

reports_keywords_in_campaign

POST /reports/campaigns/{id}/keywords

reports_keywords_in_adgroup

POST /reports/campaigns/{id}/adgroups/{id}/keywords

reports_search_terms_in_campaign

POST /reports/campaigns/{id}/searchterms

reports_search_terms_in_adgroup

POST /reports/campaigns/{id}/adgroups/{id}/searchterms

reports_ads_in_campaign

POST /reports/campaigns/{id}/ads

Todos aceptan startTime, endTime, granularity opcional (HOURLY/DAILY/WEEKLY/MONTHLY), groupBy opcional (adminArea / ageRange / countryCode / countryOrRegion / deviceClass / gender / locality), selector, returnRowTotals, returnGrandTotals, returnRecordsWithNoMetrics, timeZone (UTC | ORTZ).

Informes de cuota de impresiones (3): asíncronos

custom_reports_create → devuelve reportId. Consulta con custom_reports_get hasta que state=COMPLETED. Lista con custom_reports_list.

Órdenes de presupuesto (4): solo cuentas LOC

budget_orders_create, budget_orders_get, budget_orders_list, budget_orders_update. La v5 no tiene eliminación para órdenes de presupuesto.

Auditoría de motivos de rechazo (2)

product_page_reasons_find, product_page_reasons_get: inspección de solo lectura de por qué los revisores de Apple rechazaron las creatividades.

Puerta de escape (1)

apple_search_ads_request: llama a cualquier ruta con cualquier método. La autenticación y el contexto de la organización se siguen manejando por ti.

Selectores

Las herramientas *_find aceptan la gramática de selectores de Apple:

{
  "conditions": [
    { "field": "status", "operator": "EQUALS", "values": ["ENABLED"] },
    { "field": "countriesOrRegions", "operator": "CONTAINS_ANY", "values": ["US", "GB"] }
  ],
  "fields": ["id", "name", "status"],
  "orderBy": [{ "field": "name", "sortOrder": "ASCENDING" }],
  "pagination": { "limit": 100, "offset": 0 }
}

Operadores: EQUALS, NOT_EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, GREATER_THAN, LESS_THAN, IN, NOT_IN, CONTAINS_ALL, CONTAINS_ANY, BETWEEN. values es siempre una matriz.

Ejemplo de extremo a extremo

Un flujo de trabajo que puedes realizar completamente a través de Claude:

  1. org_acls → elige el orgId.

  2. search_apps para tu app → obtén el adamId.

  3. campaigns_create con ese adamId, presupuesto diario, segmentación en EE. UU., adChannelType=SEARCH, supplySources=["APPSTORE_SEARCH_RESULTS"], billingEvent=TAPS.

  4. adgroups_create dentro de la campaña con defaultBidAmount={amount:"1.00",currency:"USD"} y pricingModel=CPC.

  5. targeting_keywords_create con un lote de filas {text, matchType, bidAmount}.

  6. cpp_list → elige un productPageId → creatives_create con type=CUSTOM_PRODUCT_PAGE para crear un creativeId → ads_create para vincularlo al grupo de anuncios.

  7. Espera unos días. reports_campaigns para resultados generales, luego reports_search_terms_in_campaign para obtener nuevas palabras clave / negativas.

  8. custom_reports_create para cuota de impresiones / cuota de voz en tus búsquedas principales.

Notas sobre la superficie conocida (peculiaridades de la v5)

  • No hay CRUD de conjuntos de creatividades heredados. Apple lo eliminó en la v5; crea una fila en creatives en su lugar y haz referencia a ella desde ads.creativeId.

  • No hay endpoint de categorías de apps. Usa apps_get y lee primaryGenre / secondaryGenre.

  • No hay segmentación geográfica por código postal. Las entidades geográficas en la v5 son solo Country / AdminArea / Locality.

  • No hay búsqueda en toda la organización para palabras clave o búsqueda de palabras clave con alcance de grupo de anuncios. Apple limita la búsqueda de palabras clave de segmentación al nivel de campaña (/campaigns/{id}/adgroups/targetingkeywords/find) y las agrupa en todos los grupos de anuncios; filtra por adGroupId en el selector para restringir.

  • No hay DELETE en órdenes de presupuesto. Actualízalas, no las elimines.

  • Las audiencias, la previsión y los eventos de conversión NO están en la v5; esos residen en APIs de Apple separadas (AdServices Attribution, etc.).

Desarrollo local

npm run dev        # tsc --watch
npm run typecheck  # one-shot type check
npm run build      # compile to dist/

Usa apple_search_ads_request para depurar cualquier endpoint directamente; devuelve el sobre sin procesar para que puedas ver la forma exacta de la respuesta que devolvió Apple.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/AppVisionOS/apple-search-ads-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server