seo-console-mcp

Un servidor MCP y CLI con licencia MIT que ofrece 43 herramientas para Search Console, App Store, Google Play y WordPress.org. Una fila faltante se reporta como desconocida, nunca como cero.

Documentación

seo-mcp

seo-mcp es un servidor Model Context Protocol por stdio para Google Search Console, PageSpeed Insights y auditorías SEO on-page. Proporciona a los clientes MCP cuarenta y tres herramientas que cubren propiedades verificadas de Search Console y otros lugares donde los productos se descubren: App Store, Google Play, WordPress.org, Google Ads y Core Web Vitals de usuarios reales, mientras mantiene la auditoría HTML, PageSpeed, IndexNow, ideas de palabras clave y las herramientas de WordPress.org utilizables sin credenciales de cuenta de servicio de Google. Cada herramienta también se ejecuta desde la línea de comandos, por lo que un resultado puede escribirse en un archivo en lugar de en el contexto de un modelo, y snapshot registra Search Console, App Store, Google Play y WordPress.org en un momento dado para que una ejecución posterior pueda compararse con él.

https://github.com/user-attachments/assets/66bbd628-d267-421f-9400-633b696bbd53

Requisitos

  • Node.js 20.18.1 o superior
  • gcloud solo si usas el asistente de configuración

Lo que necesitas depende de las herramientas que uses. El asistente de configuración cubre Search Console y PageSpeed; las herramientas de App Store, Google Play y Chrome UX Report necesitan cada una una credencial que creas tú mismo.

HerramientasNecesitaDe dónde proviene
seo_audit, audit_site, keyword_ideas (sin siteUrl), wporg_pluginnadaendpoints públicos
pagespeedSEO_MCP_PAGESPEED_KEY opcionalasistente de configuración --pagespeed-key, o una clave API de Google Cloud
crux_field_data, crux_historySEO_MCP_CRUX_KEY (o la clave de PageSpeed si puede llamar a la API de CrUX)clave API de Google Cloud
indexnow_submitSEO_MCP_INDEXNOW_KEYcualquier clave que alojes en /<key>.txt
Herramientas de Search Console, propiedades snapshotclave de cuenta de servicioasistente de configuración, luego agrega la cuenta a la propiedad
snapshot, list_snapshots, compare_snapshotsSEO_MCP_SNAPSHOT_DIR opcionaldonde viven los archivos de instantáneas, por defecto en ~/.config/seo-mcp/snapshots
verifyCLOUDFLARE_API_TOKENCloudflare, Zone.DNS:Edit
app_store_listing, app_store_discovery, app_store_reviewsSEO_MCP_ASC_KEY_PATH, SEO_MCP_ASC_KEY_ID, SEO_MCP_ASC_ISSUER_IDclave de equipo de App Store Connect, cualquier rol que pueda leer la app
app_store_saleslo anterior más SEO_MCP_ASC_VENDOR_NUMBERclave de equipo creada con Admin, Finance o Sales and Reports
play_store_statsSEO_MCP_PLAY_BUCKET, SEO_MCP_PLAY_CREDENTIALScuenta de servicio con acceso de lectura al bucket de informes
play_vitalsSEO_MCP_PLAY_CREDENTIALScuenta de servicio invitada en Play Console con acceso a calidad de la app
las herramientas ads_GOOGLE_ADS_DEVELOPER_TOKEN, GOOGLE_ADS_CLIENT_ID, GOOGLE_ADS_CLIENT_SECRET, GOOGLE_ADS_REFRESH_TOKEN, GOOGLE_ADS_CUSTOMER_IDun token de desarrollador de Google Ads y un cliente OAuth con token de actualización

Instalación y compilación

npm install
npm run build

Ejecuta el servidor local con:

node /absolute/path/to/seo-mcp/dist/index.js

El paquete se publica en npm como seo-console-mcp; instala un comando llamado seo-mcp. Un cliente MCP puede iniciarlo mediante:

npx -y seo-console-mcp

El servidor en ejecución usa stdout exclusivamente para el protocolo de cable MCP. Los diagnósticos se escriben en stderr.

Asistente de configuración

Desde un checkout local:

npm run setup

O con npx:

npx -y seo-console-mcp setup

Para una elección de proyecto desatendida o una ubicación de clave personalizada:

seo-mcp setup --project my-seo-project --key /absolute/path/seo-mcp.key.json

El asistente también ofrece una clave API opcional de PageSpeed Insights para una cuota más alta. Es opcional: usa --pagespeed-key para crearla sin aviso, o --no-pagespeed-key para omitir el aviso explícitamente. Las ejecuciones no interactivas la omiten a menos que se proporcione --pagespeed-key.

El asistente es seguro de volver a ejecutar. Este:

  1. Verifica gcloud. Si está ausente, imprime instrucciones manuales y sale correctamente sin cambiar nada.
  2. Usa la cuenta autenticada activa o ejecuta gcloud auth login.
  3. Usa el proyecto actual, un --project proporcionado, o pide un ID de proyecto. Crea el proyecto si no existe y lo selecciona.
  4. Habilita searchconsole.googleapis.com, pagespeedonline.googleapis.com y siteverification.googleapis.com.
  5. Reutiliza o crea la cuenta de servicio seo-mcp.
  6. Reutiliza una clave existente o crea seo-mcp.key.json.
  7. Opcionalmente crea una clave API a nivel de proyecto restringida a PageSpeed Insights.
  8. Imprime el paso requerido de permiso de Search Console y las configuraciones de cliente listas para copiar.

El asistente nunca imprime el contenido de la clave de la cuenta de servicio. Cuando se solicita la creación de la clave de PageSpeed y tiene éxito, imprime esa clave una vez en la configuración final del cliente. El nombre de archivo *.key.json generado es ignorado por Git.

Otorgar acceso a Search Console a la cuenta de servicio

La API de Search Console no tiene un endpoint para agregar un usuario a una propiedad, por lo que la cuenta de servicio debe convertirse en propietaria verificada del dominio en sí. Hay dos formas de hacerlo.

Automatizado (DNS de Cloudflare)

Si el DNS del dominio está en Cloudflare, verify hace todo: pide a Google un token de verificación, escribe el registro TXT a través de la API de Cloudflare, espera la verificación y registra la propiedad.

export CLOUDFLARE_API_TOKEN=...   # a token scoped to Zone.DNS:Edit for the zone
seo-mcp verify getpsst.app another-domain.com

El token también se puede pasar con --cf-token, y la ruta de la clave con --credentials (de lo contrario, se usa GOOGLE_APPLICATION_CREDENTIALS / SEO_MCP_CREDENTIALS). El comando es idempotente: el registro TXT se deja en su lugar (Google lo vuelve a verificar), por lo que volver a ejecutar un dominio es seguro. Deja el registro en el DNS o se pierde la propiedad.

verify lee el token de CLOUDFLARE_API_TOKEN o CF_API_TOKEN (o --cf-token) y nunca lo almacena ni lo registra, por lo que cualquier almacén de secretos que pueda exportar una variable de entorno funciona. El token necesita Zone -> DNS -> Edit y Zone -> Zone -> Read (la plantilla "Edit zone DNS"), con ámbito en las zonas que verificas. Para mantenerlo fuera del historial del shell:

macOS (Keychain):

security add-generic-password -a "$USER" -s cloudflare-dns-edit -l "Cloudflare DNS Edit" -U -w   # store once, hidden prompt
CLOUDFLARE_API_TOKEN=$(security find-generic-password -s cloudflare-dns-edit -w) seo-mcp verify example.com

Linux (libsecret, o pass):

secret-tool store --label="Cloudflare DNS Edit" service cloudflare-dns-edit   # store once, hidden prompt
CLOUDFLARE_API_TOKEN=$(secret-tool lookup service cloudflare-dns-edit) seo-mcp verify example.com

Windows (PowerShell SecretManagement):

Set-Secret -Name cloudflare-dns-edit -Secret (Read-Host -AsSecureString)   # store once, hidden prompt
$env:CLOUDFLARE_API_TOKEN = Get-Secret -Name cloudflare-dns-edit -AsPlainText; seo-mcp verify example.com

Manual

Agrega la cuenta de servicio como propietaria en la interfaz de Search Console:

Search Console -> your property -> Settings -> Users and permissions -> Add user
  seo-mcp@PROJECT_ID.iam.gserviceaccount.com  ->  Owner

Usa el correo electrónico exacto de la cuenta de servicio impreso por el asistente. Se necesita acceso de propietario porque submit_sitemap es una operación de escritura.

Respaldo manual de Google Cloud

Si gcloud no está disponible, crea las credenciales manualmente o ejecuta estos comandos después de instalarlo:

gcloud auth login
gcloud projects create YOUR_PROJECT_ID
gcloud config set project YOUR_PROJECT_ID
gcloud services enable searchconsole.googleapis.com pagespeedonline.googleapis.com siteverification.googleapis.com
gcloud iam service-accounts create seo-mcp --display-name="SEO MCP"
gcloud iam service-accounts keys create ./seo-mcp.key.json \
  --iam-account=seo-mcp@YOUR_PROJECT_ID.iam.gserviceaccount.com

Si el proyecto ya existe, omite gcloud projects create. Luego otorga acceso a Search Console a la cuenta de servicio (ver arriba) y configura la ruta absoluta de la clave en el cliente MCP.

Autenticación

Las herramientas de Search Console usan google.auth.GoogleAuth con ambos ámbitos:

  • https://www.googleapis.com/auth/webmasters
  • https://www.googleapis.com/auth/webmasters.readonly

El orden de búsqueda de credenciales es:

  1. --credentials /absolute/path/key.json
  2. SEO_MCP_CREDENTIALS
  3. GOOGLE_APPLICATION_CREDENTIALS
  4. ~/.config/seo-mcp/seo-mcp.key.json (o $XDG_CONFIG_HOME/seo-mcp/...) si existe. Esta es la ubicación predeterminada donde escribe el asistente de configuración, por lo que una instalación estándar no necesita configuración.

Por ejemplo:

node dist/index.js --credentials /absolute/path/seo-mcp.key.json

pagespeed es público y no usa la cuenta de servicio. Establece SEO_MCP_PAGESPEED_KEY o pasa apiKey a esa herramienta para una cuota más alta de PageSpeed Insights. seo_audit, audit_site y indexnow_submit tampoco necesitan credenciales de Google; indexnow_submit en su lugar toma una clave de IndexNow mediante key o SEO_MCP_INDEXNOW_KEY. keyword_ideas solo las necesita cuando se pasa siteUrl para la referencia cruzada de Search Console. App Store Sales and Trends lee SEO_MCP_ASC_VENDOR_NUMBER. Las herramientas de Chrome UX Report leen SEO_MCP_CRUX_KEY, con respaldo a SEO_MCP_PAGESPEED_KEY cuando a la misma clave se le permite llamar a chromeuxreport.googleapis.com. snapshot, list_snapshots y compare_snapshots mantienen sus documentos en SEO_MCP_SNAPSHOT_DIR, por defecto en ~/.config/seo-mcp/snapshots, y no pueden leer ni escribir fuera de él. La tabla bajo Requisitos asigna cada herramienta a lo que necesita.

Modelo de seguridad

  • Verificar un dominio convierte a la cuenta de servicio en Propietaria verificada. Los propietarios pueden cambiar la configuración de Search Console y enviar solicitudes de eliminación (deindexación), por lo que trata la clave como una credencial sensible aunque la mayoría de las herramientas aquí solo leen.
  • Mantén la clave local. Vive en la ruta GOOGLE_APPLICATION_CREDENTIALS (se recomienda chmod 600). Nunca la incluyas en un paquete publicado, una imagen de contenedor o un almacén de secretos de CI. Si se filtra, cualquiera que la tenga tiene control de propietario sobre cada propiedad verificada.
  • Deja el registro TXT google-site-verification en el DNS. Google lo vuelve a verificar; eliminarlo revoca la propiedad.
  • Ningún secreto se registra. El asistente y verify imprimen solo rutas de credenciales, nunca contenidos de claves o tokens.
  • Revocar es fácil. Renuncia a la propiedad desde la interfaz de Search Console (o siteVerification.webResource.delete), y rota la clave con gcloud iam service-accounts keys delete.
  • seo_audit solo obtiene hosts públicos. La URL objetivo y cada salto de redirección se resuelve y se rechaza si aterriza en una dirección de bucle local, privada, de enlace local u otra no pública, por lo que un modelo no puede ser dirigido a obtener servicios internos o metadatos de la nube. La dirección se valida nuevamente en el momento de la conexión (el socket se fija a la dirección validada), por lo que un host de rebinding de DNS no puede presentar una dirección pública en la validación y una privada en la conexión. Establece SEO_MCP_ALLOW_PRIVATE_HOSTS=1 para auditar hosts internos o de staging en los que confíes. Esto no sustituye el aislamiento a nivel de red; ejecuta el servidor detrás de controles de salida si auditas URLs no confiables en un host con servicios internos alcanzables.

Plugin de Claude Code

Este repositorio también es un plugin de Claude Code que agrupa el servidor MCP y agrega tres comandos de barra sobre él. Desde Claude Code:

/plugin marketplace add ibrahimhajjaj/seo-console-mcp
/plugin install seo-console@verdelic

Registra el servidor MCP (mediante npx -y seo-console-mcp) y agrega:

  • /seo-console:triage <siteUrl>: triaje completo de propiedad con un plan de acción priorizado
  • /seo-console:content <siteUrl>: contenido para crear o mejorar, respaldado por datos de Search Console
  • /seo-console:launch <siteUrl>: verificación de preparación SEO previa al lanzamiento / lanzamiento

El servidor encuentra tu clave de cuenta de servicio automáticamente en la ubicación predeterminada (~/.config/seo-mcp/seo-mcp.key.json, donde el asistente de configuración la escribe), por lo que no se necesita configuración para una instalación estándar. Para una clave en otro lugar, establece GOOGLE_APPLICATION_CREDENTIALS (y SEO_MCP_PAGESPEED_KEY para una cuota más alta de PageSpeed) en el entorno donde se ejecuta Claude Code. Las herramientas seo_audit y pagespeed funcionan sin credenciales en absoluto.

Para probarlo desde un checkout local sin un marketplace: claude --plugin-dir ..

Claude Code (solo servidor MCP)

Registra la compilación local para el usuario actual:

claude mcp add --scope user seo-mcp --env GOOGLE_APPLICATION_CREDENTIALS=/abs/path/seo-mcp.key.json -- node /abs/path/seo-mcp/dist/index.js

El separador -- es obligatorio. Separa las opciones de Claude Code del comando del servidor MCP.

O con npx (sin compilación local):

claude mcp add --scope user seo-mcp --env GOOGLE_APPLICATION_CREDENTIALS=/abs/path/seo-mcp.key.json -- npx -y seo-console-mcp

El ámbito de usuario hace que el servidor esté disponible en todos tus proyectos. Usa --scope project cuando el registro deba compartirse a través del .mcp.json del proyecto actual en su lugar.

.mcp.json del proyecto:

{
  "mcpServers": {
    "seo-mcp": {
      "type": "stdio",
      "command": "node",
      "args": ["/abs/path/seo-mcp/dist/index.js"],
      "env": {
        "GOOGLE_APPLICATION_CREDENTIALS": "/abs/path/seo-mcp.key.json"
      }
    }
  }
}

Claude Desktop

Agrega la misma entrada de servidor bajo mcpServers en el archivo de configuración de Claude Desktop, luego reinicia Claude Desktop:

{
  "mcpServers": {
    "seo-mcp": {
      "command": "node",
      "args": ["/abs/path/seo-mcp/dist/index.js"],
      "env": {
        "GOOGLE_APPLICATION_CREDENTIALS": "/abs/path/seo-mcp.key.json"
      }
    }
  }
}

Para ejecutar sin una compilación local, usa "command": "npx" y "args": ["-y", "seo-console-mcp"].

Recursos

seo://properties devuelve las propiedades de Google Search Console disponibles para la cuenta de servicio como JSON. Llama a Search Console en cada lectura, por lo que el resultado siempre está actualizado.

Prompts

Los clientes MCP muestran estos prompts como puntos de partida que un usuario puede elegir para flujos de trabajo SEO comunes:

  • seo_triage confirma una propiedad, analiza el rendimiento reciente y las oportunidades, audita el sitio y produce un plan de acción de impacto versus esfuerzo.
  • content_opportunities agrupa recomendaciones respaldadas por evidencia en contenido para crear y contenido existente para mejorar.
  • launch_seo_check produce una lista de verificación de aprobación/rechazo para la preparación técnica y de indexación antes del lanzamiento.

Herramientas

Cada herramienta valida su entrada con Zod. Los fallos de herramientas devuelven un resultado de error MCP en lugar de terminar el servidor. El estado, mensaje y razón de la API de Google se incluyen cuando están disponibles. Un 403 de Search Console también explica cómo otorgar acceso a la propiedad a la cuenta de servicio.

list_properties

Lista cada propiedad de Google Search Console a la que la cuenta de servicio puede acceder, devolviendo el siteUrl y permissionLevel exactos de cada propiedad. No recibe ninguna entrada. Se requieren credenciales de cuenta de servicio, a diferencia de pagespeed, seo_audit, audit_site y indexnow_submit.

Esta herramienta no acepta parámetros.

search_analytics

Consulta searchanalytics.query y devuelve una tabla clasificada compacta junto con filas estructuradas.

{
  "siteUrl": "sc-domain:example.com",
  "startDate": "2026-06-01",
  "endDate": "2026-06-28",
  "dimensions": ["query", "page"],
  "rowLimit": 100,
  "maxTableRows": 25,
  "dimensionFilterGroups": [
    {
      "groupType": "and",
      "filters": [
        { "dimension": "query", "operator": "contains", "expression": "seo" }
      ]
    }
  ],
  "type": "web"
}
ParámetroTipoObligatorioPredeterminadoDescripción
siteUrlstringsíPropiedad de Search Console, como https://example.com/ o sc-domain:example.com
startDatestringnoFecha de inicio en formato YYYY-MM-DD; el valor predeterminado es hace 28 días
endDatestringnoFecha de fin en formato YYYY-MM-DD; el valor predeterminado es hoy
dimensionslista de uno de query, page, country, device, date, searchAppearanceno["query"]Dimensiones utilizadas para agrupar resultados
rowLimitnumberno25Número máximo de filas a devolver
startRownumberno0Fila de inicio basada en cero, para paginar un resultado grande
maxTableRowsnumberno25Límite de filas mostradas en la tabla de texto; las filas estructuradas siempre están completas. 0 = solo resumen.
dimensionFilterGroupslista JSONnoFiltros de dimensión de Search Console
typeuno de web, image, video, news, discover, googleNewsnoTipo de resultado. discover es el feed de Discover y googleNews es la aplicación Google News y news.google.com, no la pestaña Noticias en Búsqueda. Ambos admiten menos dimensiones que web: ninguno informa una dimensión de consulta
dataStateuno de full, allnofull = datos finalizados (predeterminado, retraso de ~2-3 días); all = incluir datos parciales recientes
aggregationTypeuno de auto, byProperty, byPagenoCómo agrega Search Console las filas

maxTableRows limita solo la tabla de texto; las filas estructuradas permanecen completas, por lo que 0 devuelve los totales sin tabla en lugar de un resultado vacío. discover y googleNews admiten menos dimensiones que web: ninguno informa una dimensión de query.

keyword_ideas

Expande una semilla a través del Autocompletado de Google y devuelve ideas de palabras clave normalizadas y deduplicadas agrupadas por familia de descubrimiento. Utiliza el endpoint público de autocompletado, no necesita una clave API adicional y funciona sin credenciales de Google a menos que se proporcione siteUrl. Con una propiedad de Search Console, etiqueta las ideas que ya están posicionadas con su posición promedio, clics e impresiones durante la ventana de retroceso seleccionada.

{
  "seed": "technical seo",
  "siteUrl": "sc-domain:example.com",
  "language": "en",
  "country": "us",
  "expansions": ["alphabet", "questions", "prepositions", "comparisons"],
  "days": 90,
  "limit": 100
}
ParámetroTipoObligatorioPredeterminadoDescripción
seedstringsíPalabra clave semilla a expandir
siteUrlstringnoPropiedad opcional de Search Console utilizada para identificar consultas ya posicionadas
languagestringno"en"Idioma de la interfaz de autocompletado pasado como hl
countrystringnoPaís de autocompletado pasado como gl
expansionslista de uno de alphabet, questions, prepositions, comparisonsno["alphabet","questions","prepositions","comparisons"]Familias de expansión de sugerencias a ejecutar además de la semilla simple
daysnumberno90Ventana de retroceso de Search Console en días
limitnumberno100Número máximo de ideas de palabras clave a devolver

Las cuatro familias de expansión se ejecutan de forma predeterminada. days tiene un valor predeterminado de 90 y un máximo de 480; limit tiene un valor predeterminado de 100 y un máximo de 500. Los fallos individuales de autocompletado se cuentan sin descartar las sugerencias exitosas.

search_opportunities

Encuentra consultas de alta impresión a una distancia notable de posiciones más fuertes. Agrupa por consulta y página, se predetermina a las posiciones 5 a 20 y devuelve oportunidades clasificadas por posición ponderada por impresiones.

{
  "siteUrl": "sc-domain:example.com",
  "startDate": "2026-06-01",
  "endDate": "2026-06-28",
  "minPosition": 5,
  "maxPosition": 20,
  "minImpressions": 100,
  "limit": 25
}
ParámetroTipoObligatorioPredeterminadoDescripción
siteUrlstringsíPropiedad de Search Console a analizar
startDatestringnoFecha de inicio en formato YYYY-MM-DD; el valor predeterminado es la ventana más reciente de 28 días
endDatestringnoFecha de fin en formato YYYY-MM-DD; el valor predeterminado es hoy
minPositionnumbernoPosición promedio más baja a incluir; el valor predeterminado es 5
maxPositionnumbernoPosición promedio más alta a incluir; el valor predeterminado es 20
minImpressionsnumbernoImpresiones mínimas requeridas; el valor predeterminado es 10
limitnumbernoNúmero máximo de oportunidades a devolver; el valor predeterminado es 50

compare_search_periods

Compara una ventana seleccionada con la ventana inmediatamente anterior de igual duración. Devuelve los mayores ganadores y perdedores de clics agrupados por consulta o página.

{
  "siteUrl": "sc-domain:example.com",
  "startDate": "2026-06-01",
  "endDate": "2026-06-28",
  "by": "query",
  "limit": 25
}
ParámetroTipoObligatorioPredeterminadoDescripción
siteUrlstringsíPropiedad de Search Console a analizar
startDatestringnoFecha de inicio en formato YYYY-MM-DD; el valor predeterminado es la ventana más reciente de 28 días
endDatestringnoFecha de fin en formato YYYY-MM-DD; el valor predeterminado es hoy
byuno de query, pageno"query"Dimensión utilizada para comparar el rendimiento
limitnumbernoNúmero máximo de ganadores y perdedores a devolver; el valor predeterminado es 50 de cada uno

ctr_gaps

Encuentra consultas o páginas de alta impresión cuyo CTR está por debajo del promedio de filas en la misma posición redondeada. La estimación de clics perdidos ayuda a priorizar las reescrituras de títulos y descripciones.

{
  "siteUrl": "sc-domain:example.com",
  "by": "page",
  "minImpressions": 250,
  "limit": 25
}
ParámetroTipoObligatorioPredeterminadoDescripción
siteUrlstringsíPropiedad de Search Console a analizar
startDatestringnoFecha de inicio en formato YYYY-MM-DD; el valor predeterminado es la ventana más reciente de 28 días
endDatestringnoFecha de fin en formato YYYY-MM-DD; el valor predeterminado es hoy
byuno de query, pageno"query"Dimensión utilizada para identificar brechas de CTR
minImpressionsnumbernoImpresiones mínimas requeridas; el valor predeterminado es 100
limitnumbernoNúmero máximo de brechas a devolver; el valor predeterminado es 50

query_cannibalization

Encuentra consultas para las cuales varias páginas reciben impresiones de Search Console. Los resultados agrupan las páginas en competencia y clasifican los grupos por impresiones totales.

{
  "siteUrl": "sc-domain:example.com",
  "startDate": "2026-06-01",
  "endDate": "2026-06-28",
  "minImpressions": 25
}
ParámetroTipoObligatorioPredeterminadoDescripción
siteUrlstringsíPropiedad de Search Console a analizar
startDatestringnoFecha de inicio en formato YYYY-MM-DD; el valor predeterminado es la ventana más reciente de 28 días
endDatestringnoFecha de fin en formato YYYY-MM-DD; el valor predeterminado es hoy
minImpressionsnumbernoImpresiones mínimas por fila de consulta-página; el valor predeterminado es 10

list_sitemaps

Lista la ruta del sitemap, los tiempos de envío/descarga, los indicadores de pendiente/índice, los recuentos de advertencias/errores y los recuentos de contenido.

{
  "siteUrl": "https://www.example.com/"
}
ParámetroTipoObligatorioPredeterminadoDescripción
siteUrlstringsíPropiedad de Search Console

submit_sitemap

Envía un sitemap y actualiza su estado actual. Esta es una operación de escritura. Si el envío tiene éxito pero la actualización de estado falla, el resultado aún confirma que Google aceptó la escritura e informa la advertencia de actualización.

{
  "siteUrl": "sc-domain:example.com",
  "feedpath": "https://www.example.com/sitemap.xml"
}
ParámetroTipoObligatorioPredeterminadoDescripción
siteUrlstringsíPropiedad de Search Console
feedpathstringsíURL absoluta del sitemap a enviar
dryRunbooleannofalseSi es true, informa lo que se enviaría sin escribir en Search Console

delete_sitemap

Elimina un sitemap enviado de una propiedad de Search Console. Esta es una operación de escritura. Establece dryRun en true para previsualizar la eliminación sin cambiar Search Console.

{
  "siteUrl": "sc-domain:example.com",
  "feedpath": "https://www.example.com/sitemap.xml",
  "dryRun": true
}
ParámetroTipoObligatorioPredeterminadoDescripción
siteUrlstringsíPropiedad de Search Console
feedpathstringsíURL absoluta del sitemap a eliminar
dryRunbooleannofalseSi es true, informa lo que se eliminaría sin escribir en Search Console

inspect_url

Devuelve la cobertura del índice, el veredicto, el estado de robots, el estado de indexación, el tiempo de rastreo, el estado de obtención, los canónicos de Google y del usuario, la usabilidad móvil y el estado de resultados enriquecidos.

{
  "siteUrl": "sc-domain:example.com",
  "inspectionUrl": "https://www.example.com/products/widget"
}
ParámetroTipoObligatorioPredeterminadoDescripción
siteUrlstringsíPropiedad de Search Console que contiene la URL inspeccionada
inspectionUrlstringsíURL completamente calificada a inspeccionar

index_coverage

Obtiene un sitemap y verifica un conjunto limitado de sus URL de página directas con la API de inspección de URL de Google. Devuelve recuentos de indexadas, no indexadas y fallidas, las URL no indexadas y sus estados de cobertura, resultados completos por URL y si el resultado fue truncado. Los índices de sitemap no se siguen hacia sitemaps secundarios.

{
  "siteUrl": "sc-domain:example.com",
  "sitemapUrl": "https://www.example.com/sitemap.xml",
  "maxUrls": 20,
  "concurrency": 3
}
ParámetroTipoObligatorioPredeterminadoDescripción
siteUrlstringsíPropiedad de Search Console que contiene las URL del sitemap
sitemapUrlstringsíURL del sitemap completamente calificada a inspeccionar
maxUrlsnumberno20Número máximo de URL a inspeccionar
concurrencynumberno3Solicitudes de inspección de URL concurrentes

maxUrls tiene un valor predeterminado de 20 y un máximo absoluto de 50. concurrency tiene un valor predeterminado de 3 y un máximo absoluto de 5. Estos límites protegen la cuota de la API de inspección de URL, que es de aproximadamente 2,000 consultas por día y 600 por minuto para cada propiedad.

request_recrawl

Verifica URL con la API de inspección de URL y, cuando algunas no están indexadas, vuelve a enviar el sitemap que las cubre. Ese reenvío es la única señal de recrawleo masivo compatible con Google: no existe una API de solicitud de indexación, y el botón Solicitar indexación de la interfaz de Search Console no tiene equivalente programático. Las URL provienen de urls o se leen de sitemapUrl; el sitemap a reenviar es feedpath, con valor predeterminado sitemapUrl. Esta es una operación de escritura. Establece dryRun en true para inspeccionar e informar sin reenviar.

{
  "siteUrl": "sc-domain:example.com",
  "sitemapUrl": "https://www.example.com/sitemap.xml",
  "maxUrls": 20,
  "dryRun": true
}
ParámetroTipoObligatorioPredeterminadoDescripción
siteUrlstringsíPropiedad de Search Console que contiene las URL
urlslista de stringnoURL explícitas a verificar; omítelas para leerlas de sitemapUrl
sitemapUrlstringnoSitemap del cual leer las URL; también es el sitemap predeterminado a reenviar
feedpathstringnoSitemap a reenviar cuando se encuentren URL no indexadas; el valor predeterminado es sitemapUrl
maxUrlsnumberno20Número máximo de URL del sitemap a inspeccionar
concurrencynumberno3Solicitudes de inspección de URL concurrentes
dryRunbooleannofalseSi es true, inspecciona e informa sin reenviar el sitemap

Comparte los límites de index_coverage (maxUrls hasta 50, concurrency hasta 5) porque ambos usan la misma cuota de Inspección de URL. El reenvío solo provoca un rastreo nuevo de páginas cuyo lastmod del sitemap esté fresco, así que mantén lastmod preciso para las URL cambiadas.

indexnow_submit

Envía hasta 10,000 URL cambiadas en una sola llamada a un endpoint de IndexNow. Los motores participantes (Bing, Yandex, Naver, Seznam, Yep) comparten los envíos entre sí. Google no usa IndexNow; usa request_recrawl para Google. Esta es una operación de escritura y admite dryRun. No necesita credenciales de Google.

{
  "urls": ["https://www.example.com/new-page", "https://www.example.com/updated-page"],
  "key": "your-indexnow-key"
}
ParámetroTipoObligatorioPredeterminadoDescripción
urlslista de cadenassíURL de páginas cambiadas; un envío cubre un solo host
keycadenanoClave de IndexNow; por defecto SEO_MCP_INDEXNOW_KEY. La misma clave debe estar alojada en el sitio como archivo de texto en https:///.txt (o en keyLocation) que contenga solo la clave
keyLocationcadenanoURL del archivo de clave alojado cuando no está en https:///.txt
endpointuno de api.indexnow.org, www.bing.com, yandex.com, searchadvisor.naver.com, search.seznam.cz, indexnow.yep.comno"api.indexnow.org"Endpoint de IndexNow a notificar; los motores participantes comparten envíos
dryRunbooleanonofalseSi es verdadero, informa lo que se enviaría sin notificar al endpoint

Todas las URL en un envío deben compartir un solo host. La clave es cualquier valor de 8 a 128 caracteres de letras, dígitos o guiones, pasada como key o SEO_MCP_INDEXNOW_KEY, y debe estar alojada como archivo de texto que contenga exactamente la clave en https://<host>/<key>.txt (o en keyLocation en el mismo host). Como las URL de archivos de clave convencionalmente contienen la clave, ni la clave ni keyLocation se muestran nunca en la salida de la herramienta. endpoint por defecto es api.indexnow.org; un envío a cualquier endpoint participante llega a todos ellos.

pagespeed

Devuelve datos de campo de CrUX cuando están disponibles, incluidos LCP, CLS, INP o FID, FCP y TTFB. También devuelve puntuaciones de categorías de Lighthouse y hasta diez oportunidades de mayor ahorro.

{
  "url": "https://www.example.com/",
  "strategy": "mobile",
  "category": ["performance", "seo", "accessibility", "best-practices"]
}
ParámetroTipoObligatorioPredeterminadoDescripción
urlcadenasíURL de página pública a analizar
strategyuno de mobile, desktopno"mobile"Estrategia de dispositivo de Lighthouse
categorylista de uno de performance, seo, accessibility, best-practicesno["performance","seo","accessibility","best-practices"]Categorías de Lighthouse a ejecutar
apiKeycadenanoClave opcional de API de PageSpeed Insights; por defecto SEO_MCP_PAGESPEED_KEY

strategy por defecto es mobile. Las cuatro categorías se solicitan por defecto. apiKey es opcional y anula SEO_MCP_PAGESPEED_KEY para esa llamada.

seo_audit

Obtiene hasta 10 MB de HTML con redirecciones habilitadas, un tiempo de espera de 15 segundos y un agente de usuario identificativo. Extrae longitudes de título y descripción, canonical, robots, H1 y esquema de encabezados, etiquetas de Open Graph y Twitter, tipos de JSON-LD, cobertura de texto alternativo de imágenes, enlaces internos/externos, recuento de palabras, idioma y viewport. Señala títulos faltantes o duplicados, descripción faltante, H1 faltantes o múltiples, canonical faltante y JSON-LD faltante o inválido.

{
  "url": "https://www.example.com/landing-page"
}
ParámetroTipoObligatorioPredeterminadoDescripción
urlcadenasíURL de página pública a auditar

audit_site

Obtiene un sitemap y audita hasta 50 de sus URL de página con concurrencia limitada. Los índices de sitemap se admiten con un límite máximo de cinco obtenciones de sitemaps hijos. El resultado incluye hallazgos compactos por página, errores aislados de obtención de páginas, un recuento de cada problema compartido y recuentos explícitos de truncamiento y omisión. No requiere credenciales de Google.

{
  "sitemapUrl": "https://www.example.com/sitemap.xml",
  "maxPages": 20,
  "concurrency": 5
}
ParámetroTipoObligatorioPredeterminadoDescripción
sitemapUrlcadenasíURL de sitemap público a auditar
maxPagesnúmerono20Máximo de páginas a auditar
concurrencynúmerono5Máximo de obtenciones de páginas en vuelo

maxPages por defecto es 20 y concurrency por defecto es 5. Sus valores máximos son 50 y 10, respectivamente.

server_version

Qué compilación del servidor está respondiendo, desde dónde se ejecuta y si salió de una caché de npx. Sin credenciales.

Esta herramienta no toma parámetros.

Cuatro valores parecen este y no lo son: lo que npm llama latest, a lo que resuelve el rango de versiones, lo que declara el manifiesto del plugin y lo que realmente se ejecuta. Los primeros tres son todos legibles y ninguno responde la pregunta. Verificar la herramienta de línea de comandos tampoco es un sustituto, ya que es un proceso separado resuelto por separado y puede ser una compilación diferente en la misma máquina.

La ruta de instalación es la pista. npx reutiliza una compilación en caché sin volver a resolver el rango y sin errores, por lo que un servidor puede ir por detrás de la versión publicada mientras que cada otra señal lee actual; una ruta bajo _npx es lo que lo muestra.

Llámalo después de actualizar, antes de informar cualquier cosa. npm view <pkg> version lee una caché de registro local y puede devolver la versión anterior durante minutos después de una publicación exitosa, mientras que dist-tags y la matriz de versiones ya llevan la nueva. Dos sesiones aquí concluyeron independientemente que una publicación había fallado cuando no lo había hecho, en versiones separadas. Una lectura de registro no puede distinguir una publicación lenta de una fallida; preguntar al proceso en ejecución qué es puede hacerlo.

wporg_plugin

Busca un plugin de WordPress.org por slug y devuelve instalaciones activas, descargas, calificaciones, hilos de soporte y fechas de versión. Usa la API pública de wp.org y no necesita credenciales ni clave de API. Un plugin publicado en los últimos días se informa con possiblyLagging: true cuando un campo parece vacío, porque la API de wp.org subinforma plugins recientes; el campo puede estar ya visible en la página.

{ "slug": "akismet" }
ParámetroTipoObligatorioPredeterminadoDescripción
slugcadenasíSlug de plugin de WordPress.org, p. ej. akismet
downloadDaysnúmerono30Días de historial de descargas diarias a obtener; 0 lo omite
includeVersionDistributionbooleanonotrueTambién obtener la cuota de instalaciones activas en cada versión del plugin

play_store_stats

Lee los informes masivos de Google Play para una aplicación y devuelve Instalaciones de Dispositivos Activos, además de visitantes de la ficha de la tienda y adquisiciones agrupadas por fuente de tráfico y término de búsqueda. hasPlaySearchRows indica explícitamente si aparece algún tráfico de búsqueda de Play, ya que su ausencia es un hallazgo más que un error. Los informes tienen un retraso de días, por lo que lastDatePresent es la última fecha realmente en los archivos en lugar de hoy.

{ "packageName": "com.example.app", "month": "202608" }
ParámetroTipoObligatorioPredeterminadoDescripción
packageNamecadenasíNombre de paquete de Android, p. ej. app.getpsst
monthcadenanoMes del informe como YYYYMM; por defecto el mes UTC actual. Se ignora cuando se dan startDate y endDate
installsDimensionuno de overview, country, language, device, os_version, carrier, app_versionno"overview"Qué informe de instalaciones leer. overview no está documentado por Google pero está presente en buckets reales; los otros son los desgloses documentados
includelista de uno de ratings, crashes, reviewsno[]Familias de informes adicionales a leer. Los archivos faltantes son normales: Google emite un informe solo cuando hay algo que informar
storePerformanceDimensionuno de traffic_source, countryno"traffic_source"Qué desglose de rendimiento de tienda leer
storePerformanceTotalsbooleanonofalseLeer la variante total_ en su lugar. Es un informe diferente, no un resumen del mismo: lleva solo adquisiciones, sin visitantes y sin tasa de conversión, y para algunas aplicaciones cubre muchos menos fechas y atribuye cada adquisición a una fuente de marcador de posición
ratingsDimensionuno de country, language, device, os_version, carrier, app_versionno"country"Dimensión para el informe de calificaciones
crashesDimensionuno de device, os_version, app_versionno"app_version"Dimensión para el informe de fallos
startDatecadenanoInicio de ventana en YYYY-MM-DD. Con endDate, lee cada mes que toca la ventana y filtra filas a ella
endDatecadenanoFin de ventana en YYYY-MM-DD

Establece SEO_MCP_PLAY_BUCKET al bucket de informes (gs://pubsite_prod_... y el nombre simple ambos funcionan) y SEO_MCP_PLAY_CREDENTIALS a una clave de cuenta de servicio con acceso de lectura a ese bucket, recurriendo a GOOGLE_APPLICATION_CREDENTIALS. El acceso de lectura al bucket es una concesión diferente de la invitación de Play Console que play_vitals necesita. month por defecto es el mes UTC actual.

app_store_listing

Lee una ficha de App Store a través de App Store Connect y mide los campos de cada locale contra los límites de Apple: nombre 30, subtítulo 30, palabras clave 100, texto promocional 170. Apple indexa solo el nombre, el subtítulo y el campo de palabras clave, por lo que la descripción se informa pero nunca se puntúa, y un campo que supera su límite por un carácter se elimina silenciosamente en lugar de rechazarse, por lo que cada campo se informa contra su límite. El texto promocional se destaca por separado porque es el único de estos que se puede cambiar en una versión en vivo sin revisión.

Una aplicación puede tener un registro en vivo y uno editable al mismo tiempo, por lo que state selecciona cuál se lee y el resultado indica el registro y la versión que usó. Cuando el registro que pediste no existe, se informa el otro y una nota lo dice en lugar de pasarlo como lo que pediste.

El estado informado proviene de appVersionState, recurriendo al obsoleto appStoreState. Los dos escriben lo mismo de manera diferente: una ficha en vivo lee READY_FOR_DISTRIBUTION donde el atributo obsoleto decía READY_FOR_SALE. La salida capturada antes y después de ese cambio diferirá solo en la cadena, sin que haya pasado nada con la ficha.

{ "bundleId": "com.example.app", "state": "live", "platform": "IOS", "storefronts": ["us", "gb"] }
ParámetroTipoObligatorioPredeterminadoDescripción
appIdcadenanoID de aplicación numérico de App Store Connect; proporciona este o bundleId
bundleIdcadenanoID de paquete, resuelto a un ID de aplicación cuando no se da appId; proporciona este o appId
platformuno de IOS, MAC_OS, TV_OS, VISION_OSno"IOS"Plataforma de App Store cuya versión se lee
stateuno de live, editableno"live"Leer la ficha en vivo o la editable que se prepara para el lanzamiento
storefrontslista de cadenasno["us"]Códigos de país de la tienda para la consulta pública de calificaciones

Proporciona appId o bundleId. Establece SEO_MCP_ASC_KEY_PATH a la clave privada de .p8 y SEO_MCP_ASC_KEY_ID a su ID de clave, además de SEO_MCP_ASC_ISSUER_ID para una clave de equipo (las claves individuales no tienen ID de emisor). La clave y el token que firma nunca aparecen en la salida.

Una clave de equipo llega a todas las aplicaciones del equipo, por lo que una clave puede servirlas a todas. Lo que la limita es el rol que se le dio, y Apple no permite que el rol de una clave se cambie después: la única edición ofrecida es Revocar. Una clave de App Manager lee fichas pero no informes de Ventas y Tendencias ni de analítica, por lo que esos necesitan una clave separada creada con Admin, Finance o Sales and Reports en lugar de una actualización de la que tienes. ratings es una lista, una entrada por tienda solicitada, no un objeto claveado por tienda:

{ "ratings": [{ "storefront": "us", "source": "itunes-lookup", "averageUserRating": 4.5, "userRatingCount": 12 }] }

La calificación de estrellas no proviene de App Store Connect. Su API no tiene ningún recurso de calificación agregada, solo calificaciones por edad, por lo que la calificación se lee de la búsqueda pública de tiendas de App Store, mientras que todos los demás campos de esta herramienta provienen de App Store Connect. Dos fuentes que informan un número que se ve igual de cualquier manera, por eso cada entrada lleva source. Una calificación de una página de tienda y una calificación de una API privada no son intercambiables y no deben compararse como si fueran la misma medición.

list_snapshots

Lista los documentos de instantánea que ya están en el directorio de instantáneas, del más reciente al más antiguo, con cuándo se tomó cada uno, la ventana que cubre y cuántas propiedades, aplicaciones, paquetes y complementos contiene.

{ "limit": 50 }
ParámetroTipoObligatorioPredeterminadoDescripción
limitnúmerono50Máximo de instantáneas a devolver, del más reciente al más antiguo

Un par de instantáneas no vale nada si nada puede decir qué archivos existen, y cada llamador de lo contrario mantenía su propio índice de un directorio que el servidor posee. Un archivo en el directorio que no es un documento de instantánea se lista con su error en lugar de ocultarse, para que un nombre que esperas encontrar nunca se lea silenciosamente como ausente. Un directorio faltante es una lista vacía, no un fallo: aún no se ha capturado nada. total y truncated se colocan junto a la lista porque la línea de comandos imprime solo la mitad estructurada, donde un corte de página en limit de otro modo se leería como el historial completo.

snapshot

Captura cuatro superficies en un solo documento con marca de tiempo: totales de Search Console y filas principales por propiedad, listados de App Store, instalaciones y tráfico de Google Play, y estadísticas de WordPress.org. Los datos de campo de Core Web Vitals, las estadísticas vitales de Android, las ventas de App Store y las reseñas de App Store no están incluidos; crux_field_data, play_vitals, app_store_sales y app_store_reviews leen esos. Esta es la herramienta para registrar un punto en una serie, porque ninguna de las consolas mantiene un historial que puedas comparar más tarde.

Los totales de Search Console provienen de la dimensión fecha, nunca sumando la dimensión de consulta. Google retiene consultas de bajo volumen, por lo que una suma a nivel de consulta subestima, y esa brecha se lee más tarde como una disminución que nunca ocurrió.

Una superficie que no se puede leer se registra en su lugar con su error y se nombra en surfacesWithErrors, nunca se omite, porque una superficie que desaparece silenciosamente se lee más tarde como una caída a cero. Una superficie lenta agota el tiempo de espera sin derribar el documento.

{
  "properties": ["sc-domain:example.com"],
  "apps": ["1234567890"],
  "packages": ["com.example.app"],
  "slugs": ["akismet"],
  "windowDays": 28,
  "outPath": "2026-09-03.json"
}
ParámetroTipoObligatorioPredeterminadoDescripción
propertieslista de cadenasno[]Propiedades de Search Console a capturar
appslista de cadenasno[]Aplicaciones de App Store, cada una un id de aplicación numérico o un id de paquete
packageslista de cadenasno[]Nombres de paquetes de Google Play
slugslista de cadenasno[]Slugs de complementos de WordPress.org
windowDaysnúmerono28Ventana de Search Console en días, que termina hoy
platformuno de IOS, MAC_OS, TV_OS, VISION_OSno"IOS"Plataforma de App Store para las superficies de aplicaciones
storefrontslista de cadenasno["us"]Códigos de país de tienda para calificaciones de App Store
outPathcadenanoNombre de archivo o ruta dentro del directorio de instantáneas (SEO_MCP_SNAPSHOT_DIR, predeterminado ~/.config/seo-mcp/snapshots); debe terminar en .json, o pasa auto para nombrar el archivo según el momento en que se tomó. Un archivo existente no se sobrescribe a menos que overwrite sea verdadero
overwritebooleanonofalseReemplaza un archivo existente en outPath; sin esto, un archivo existente se deja intacto y se informa

Pasa outPath para escribir el documento donde compare_snapshots pueda leerlo más tarde, o outPath: "auto" para que se nombre según el momento en que se tomó (2026-09-04T00-15Z.json), que es lo que hace que una ejecución desatendida produzca una serie en lugar de un archivo sobrescrito para siempre. Es un nombre de archivo dentro del directorio de instantáneas, SEO_MCP_SNAPSHOT_DIR o ~/.config/seo-mcp/snapshots por predeterminado; una ruta que se resuelve fuera de ese directorio o que no termina en .json se rechaza, y un archivo existente se deja en su lugar y se informa a menos que pases overwrite: true. Un modelo elige esta cadena, por lo que el directorio es el límite que evita que una llamada de herramienta trunque cualquier otra cosa en la máquina. La posición y el CTR son null en lugar de 0 cuando una ventana no tiene impresiones, para que una ventana vacía nunca se compare con datos reales como un colapso.

compare_snapshots

Lee dos documentos de instantánea e informa qué cambió entre ellos: clics, impresiones y posición por propiedad, cambios a nivel de página y de consulta por encima de un umbral de impresiones, deltas de instalaciones y calificaciones, cambios de versión y recuento de locales de App Store, longitudes de nombre, subtítulo, palabras clave, texto promocional y descripción por locale, además de qué campos cruzaron un límite de caracteres, fuentes de tráfico de Google Play por visitantes y adquisiciones, y el histograma de cinco estrellas de WordPress.org.

{ "from": "2026-08-06.json", "to": "2026-09-03.json", "minImpressions": 100 }
ParámetroTipoObligatorioPredeterminadoDescripción
fromcadenasíNombre de archivo o ruta de instantánea dentro del directorio de instantáneas; latest nombra la instantánea más reciente en disco y previous la anterior
tocadenasíNombre de archivo o ruta de instantánea dentro del directorio de instantáneas; latest nombra la instantánea más reciente en disco y previous la anterior
minImpressionsnúmerono100Ignora movimientos de posición de página por debajo de este número de impresiones en ambos lados

from y to se resuelven dentro del mismo directorio de instantáneas que snapshot's outPath, por lo que esta herramienta lee instantáneas y nada más. Cualquiera de los dos también acepta latest o previous en lugar de un nombre de archivo, que es la comparación que casi todos los llamadores realmente quieren y la única que pueden solicitar sin listar el directorio primero. Ambos omiten un archivo que no se analizará, y pedir previous con una sola instantánea en disco lo dice en lugar de comparar un documento consigo mismo.

Hace aritmética, nunca juicio. No te dirá si un cambio fue bueno o qué lo causó, porque un diff no puede respaldar esa afirmación. Una superficie que falló o falta en cualquiera de los lados se marca como no comparable y se nombra, para que un fallo de recopilación nunca se lea como un cambio, y un archivo que no es un documento de instantánea se rechaza en lugar de analizarse a medias.

Las instantáneas tomadas antes de que se capturara un campo aún se comparan. Un campo que un lado no lleva regresa como un delta null en lugar de como un cambio, y un par de aplicaciones sin longitudes por locale en ninguno de los lados informa localesComparable: false en lugar de un listado vaciado a cero caracteres.

app_store_reviews

Lee reseñas de clientes de App Store y tus respuestas, filtradas por calificación de estrellas o tienda, siguiendo el cursor de paginación propio de Apple.

{ "bundleId": "com.example.app", "rating": [1, 2], "territory": "USA", "limit": 100 }
ParámetroTipoObligatorioPredeterminadoDescripción
appIdcadenanoId de aplicación numérico de App Store Connect; proporciona esto o bundleId
bundleIdcadenanoId de paquete; proporciona esto o appId
ratinglista de númerosnoSolo estas calificaciones de estrellas
territorycadenanoSolo reseñas de esta tienda
sortuno de -createdDate, createdDate, rating, -ratingno"-createdDate"Orden de clasificación; más recientes primero por predeterminado
limitnúmerono100Máximo de reseñas a devolver entre páginas
maxPagesnúmerono5Máximo de páginas a seguir

Informa meanOfFetched y histogramOfFetched, nunca "la calificación". Esos describen solo las reseñas que devolvió esta llamada, y una página filtrada o truncada haría que un promedio fuera un número diferente con el mismo nombre. App Store Connect no expone ningún recurso de calificación agregada, lo cual es verificable en la propia especificación OpenAPI de Apple: cada ruta que coincide con "rating" es una calificación de edad.

app_store_discovery

Lee las superficies de App Store más allá del texto del listado: palabras clave de búsqueda (la lista real de palabras clave indexadas de Apple, mantenida por locale), etiquetas de aplicación, experimentos de optimización de página de producto, páginas de producto personalizadas, eventos dentro de la aplicación, disponibilidad territorial y resúmenes de reseñas.

{ "bundleId": "com.example.app", "locales": ["en-US", "ar-SA"], "platform": "IOS" }
ParámetroTipoObligatorioPredeterminadoDescripción
appIdcadenanoId de aplicación numérico de App Store Connect; proporciona esto o bundleId
bundleIdcadenanoId de paquete; proporciona esto o appId
includelista de uno de searchKeywords, appTags, experiments, customProductPages, appEvents, availability, reviewSummarizationsno[]Qué superficies de descubrimiento leer; vacío lee todas
limitnúmerono50Filas por recurso
localeslista de cadenasno["en-US"]Locales para recursos por locale como searchKeywords
platformuno de IOS, MAC_OS, TV_OS, VISION_OSno"IOS"Plataforma para recursos que la requieren
includeRowsbooleanonofalseIncluye cada fila cruda además de los recuentos; desactivado por predeterminado para que una llamada de resumen se mantenga pequeña

Cada recurso lleva sus propios parámetros obligatorios: searchKeywords necesita tanto un filtro de plataforma como de locale, appAvailabilityV2 es una relación de uno a uno que rechaza limit por completo. Un recurso que esta clave o aplicación no puede servir se informa como available: false, nunca como una lista vacía, porque "sin experimentos" y "no se pueden leer experimentos" son respuestas diferentes.

crux_field_data

Core Web Vitals de usuarios reales para un origen o una URL única del Chrome UX Report: el registro de campo actual de 28 días, con p75 y contenedores de histograma completos.

{ "origin": "https://example.com", "formFactor": "PHONE" }
ParámetroTipoObligatorioPredeterminadoDescripción
origincadenanoOrigen como https://example.com; agrega cada página debajo de él. Proporciona origin o url, no ambos
urlcadenanoUna URL de página única. Proporciona origin o url, no ambos
formFactoruno de PHONE, TABLET, DESKTOPnoClase de dispositivo; omite para todos los factores de forma combinados
metricslista de cadenasnoNombres de métricas a solicitar; omite para todas las disponibles

Esto son datos de campo, no una prueba de laboratorio; guarda pagespeed para auditorías de Lighthouse. Google está descontinuando los propios datos del mundo real de PageSpeed, así que aquí es donde se mueven las mediciones de campo. Un origen con muy pocas muestras anonimizadas devuelve hasData: false con una nota en lugar de un error o ceros, ya que un LCP a cero se leería como una regresión catastrófica.

crux_history

Las mismas métricas de campo como una serie semanal, aproximadamente seis meses de historial.

{ "origin": "https://example.com", "formFactor": "PHONE", "collectionPeriodCount": 25 }
ParámetroTipoRequeridoPredeterminadoDescripción
originstringnoOrigen como https://example.com; agrega cada página bajo él. Proporcione origin o url, no ambos
urlstringnoUna URL de página individual. Proporcione origin o url, no ambos
formFactoruno de PHONE, TABLET, DESKTOPnoClase de dispositivo; omita para combinar todos los factores de forma
metricslista de stringnoNombres de métricas a solicitar; omita para todos los disponibles
collectionPeriodCountnumbernoPeríodos semanales a devolver, de 1 a 40. El historial documentado es de unos seis meses; la API decide lo que realmente tiene

Cada período es una ventana móvil de 28 días con pasos semanales, por lo que los puntos consecutivos se superponen por tres semanas y un movimiento de una semana a otra no es un cambio independiente. Los períodos con muy pocas muestras mantienen su lugar en la serie como null en lugar de eliminarse, por lo que los valores permanecen alineados con collectionPeriods.

app_store_sales

Lee App Store Sales and Trends: unidades descargadas por día, por territorio, por aplicación, resumidas por SKU.

{ "reportDate": "2026-08-30", "frequency": "DAILY", "reportType": "SALES", "reportSubType": "SUMMARY" }
ParámetroTipoRequeridoPredeterminadoDescripción
reportDatestringnoFecha del informe. DAILY y WEEKLY toman YYYY-MM-DD (WEEKLY significa la fecha de finalización de la semana), MONTHLY toma YYYY-MM, YEARLY toma YYYY. El valor predeterminado es el período completo más reciente para la frecuencia
frequencyuno de DAILY, WEEKLY, MONTHLY, YEARLYno"DAILY"Período del informe
reportTypeuno de SALES, PRE_ORDER, SUBSCRIPTION, SUBSCRIPTION_EVENT, SUBSCRIBER, INSTALLS, FIRST_ANNUALno"SALES"Tipo de informe de Sales and Trends
reportSubTypeuno de SUMMARY, DETAILED, SUMMARY_INSTALL_TYPE, SUMMARY_TERRITORY, SUMMARY_CHANNELno"SUMMARY"Subtipo de informe
versionstringnoVersión del informe, como 1_0 o 1_3, cuando el valor predeterminado no se acepta
includeRowsbooleannofalseIncluir cada fila de informe sin procesar, así como el resumen por SKU

Establezca SEO_MCP_ASC_VENDOR_NUMBER; App Store Connect muestra el número de proveedor en Payments and Financial Reports, junto al nombre de la entidad legal. Sales and Trends necesita una clave de equipo con el rol Admin, Finance o Sales and Reports. Los informes diarios llegan al día siguiente, por lo que la fecha de informe predeterminada es dos días atrás en lugar de hoy. reportDate toma la forma que su frecuencia necesita: YYYY-MM-DD para DAILY y para WEEKLY, donde significa el domingo de finalización de la semana, YYYY-MM para MONTHLY, y YYYY para YEARLY; déjelo fuera y cada frecuencia usa su período completo más reciente.

Un período sin ventas devuelve hasData: false con una nota, no un error, porque un día tranquilo no debería parecer una integración rota. Las unidades provienen de la canalización de Sales and Trends, que es separada de App Analytics y puede diferir de ella.

play_vitals

Lee Android vitals de la API Play Developer Reporting: tasa de fallos, tasa de ANR, conteos de errores y métricas de inicio, diarias o por hora, con desgloses opcionales como versionCode o countryCode.

{ "packageName": "com.example.app", "metricSets": ["crashRate", "anrRate"], "days": 28 }
ParámetroTipoRequeridoPredeterminadoDescripción
packageNamestringsíNombre del paquete de Android
metricSetslista de uno de crashRate, anrRate, errorCount, slowStartRate, excessiveWakeupRateno["crashRate","anrRate"]Qué conjuntos de métricas de Android vitals consultar
aggregationPerioduno de DAILY, HOURLYno"DAILY"DAILY se informa en America/Los_Angeles, HOURLY en UTC
daysnumberno28Cuántos días atrás consultar
dimensionslista de stringno[]Dimensiones de desglose como versionCode o countryCode
pageSizenumberno1000Filas por conjunto de métricas
includeRowsbooleannofalseIncluir cada fila sin procesar, así como los conteos; desactivado por defecto para que una llamada de resumen siga siendo pequeña

Establezca SEO_MCP_PLAY_CREDENTIALS a la clave de cuenta de servicio, con respaldo a GOOGLE_APPLICATION_CREDENTIALS. La cuenta también debe ser invitada en Play Console bajo Users and permissions con el permiso para ver información de la aplicación y calidad de la aplicación. El token se acuña para el alcance playdeveloperreporting, que es una concesión separada de la lectura de Cloud Storage que play_store_stats necesita. Una cuenta puede tener ambos, pero una clave que solo tiene la concesión del bucket recibe un 403 aquí.

La ventana se limita a la frescura que la API informa por sí misma, ya que rechaza una fecha de finalización más allá de eso y preguntar hasta hoy siempre falla. El resultado dice cuán actuales son realmente los datos, por lo que cero filas hasta una fecha conocida se distingue de cero filas porque el día aún no ha llegado. Esta API no lleva datos de adquisición o conversión; play_store_stats tiene eso.

Google Ads

Lee la cuenta a través de la API en lugar de la consola. Una tabla de consola pagina, por lo que un conteo tomado de la primera pantalla puede ser incorrecto sin parecerlo: un conteo de palabras clave se leyó como dos cuando la respuesta era cinco, porque la tabla muestra diez filas y había catorce. Estas herramientas devuelven cada fila.

Establezca GOOGLE_ADS_DEVELOPER_TOKEN, GOOGLE_ADS_CLIENT_ID, GOOGLE_ADS_CLIENT_SECRET, GOOGLE_ADS_REFRESH_TOKEN y GOOGLE_ADS_CUSTOMER_ID (guiones opcionales). Dos conveniencias: GOOGLE_ADS_CLIENT_SECRET_PATH lee el id de cliente y el secreto del JSON de cliente OAuth que Google Cloud le da, y GOOGLE_ADS_ENV_FILE apunta a un archivo existente con forma de .env que contiene cualquiera de estos, por lo que un token de actualización que ya vive en algún lugar se lee en su lugar en lugar de copiarse. El entorno del proceso gana sobre el archivo. GOOGLE_ADS_API_VERSION anula la versión de la API.

ads_campaigns

Nombre de campaña, estado, presupuesto diario, impresiones, clics, costo y conversiones en una ventana.

{ "days": 30 }
ParámetroTipoRequeridoPredeterminadoDescripción
daysnumberno30Cuántos días atrás informar, terminando hoy

ads_keywords

Cada palabra clave con su estado, oferta de CPC efectiva, estado de aprobación, estado de publicación y métricas.

ELIGIBLE no significa publicación. Significa aprobada y capaz de publicarse, y una palabra clave en pausa lo informa. Por eso status se devuelve junto a él: sin él, la fila de una palabra clave en pausa es idéntica a una activa, y alguien que acaba de pausar tres palabras clave lee eso como que la pausa no se aplicó. Las palabras clave en pausa se nombran en una nota en lugar de eliminarse, porque eliminar filas silenciosamente es el mismo fallo un nivel más abajo: preguntas si una palabra clave está en la cuenta y no obtienes nada. Pase status para filtrar deliberadamente.

{ "days": 30 }
ParámetroTipoRequeridoPredeterminadoDescripción
daysnumberno30Cuántos días atrás informar, terminando hoy
statusuno de ENABLED, PAUSED, REMOVEDnoLimitar a un estado de palabra clave. Omitido, cada palabra clave se devuelve con su estado nombrado, porque eliminar filas silenciosamente es cómo un conteo tomado de esta herramienta sale mal como lo hace un conteo de consola

ads_ads

Cada anuncio con su fortaleza de anuncio, estado de aprobación de política, estado de publicación y métricas.

{ "days": 30 }
ParámetroTipoRequeridoPredeterminadoDescripción
daysnumberno30Cuántos días atrás informar, terminando hoy

ads_ad_copy

Lee lo que un anuncio realmente dice. ads_ads da el id, la fortaleza, la aprobación y el estado; esto da el texto, que es lo que toda pregunta creativa necesita y la razón por la que esa pregunta de otro modo termina en el navegador.

{ "adGroup": "brand-exact" }
ParámetroTipoRequeridoPredeterminadoDescripción
adGroupstringnoLimitar a un grupo de anuncios por nombre. Omitido, cada anuncio en la cuenta se lee, que es lo que responde si un titular se repite entre grupos de anuncios
adIdstringnoLimitar a un anuncio por su id numérico, para leer de vuelta el texto que se suponía que debía enviarse
includeRemovedbooleannofalseIncluir anuncios eliminados. Desactivado por defecto: el texto de un anuncio eliminado es historia y desplaza a los anuncios que se están publicando

Cada titular y descripción vuelve con su fijación y la etiqueta de rendimiento propia de Google, más la ruta de visualización, las URL finales y los temas de política detrás de un estado limitado o desaprobado. La palabra de aprobación dice que algo está mal; el tema dice qué. APPROVED_LIMITED junto a TRADEMARKS_IN_AD_TEXT es una solución; APPROVED_LIMITED por sí solo es un viaje a la consola.

También responde las dos preguntas que una vista por anuncio no puede:

  • Por qué la fortaleza es Poor. El conteo contra lo que Google quiere, 3 of 15 headlines, 2 of 4 descriptions, y cuántos activos están fijados. La fijación suele ser deliberada, suele ser invisible en la palabra de fortaleza, y es una razón común por la que la fortaleza se lee más baja de lo que el texto merece. El texto repetido dentro de un anuncio también se nombra, ya que un activo repetido ocupa un espacio sin agregar una variación.
  • Si un titular está duplicado entre anuncios. El texto de titular que aparece en más de un anuncio se lista con los anuncios y grupos de anuncios que lo llevan. Dos anuncios en un grupo de anuncios que comparten sus titulares no son dos variantes probándose entre sí, y nada en la consola lo dice de un vistazo.

Los anuncios eliminados se excluyen a menos que includeRemoved esté establecido, y solo un anuncio de búsqueda responsivo lleva texto en estos campos: cualquier otro tipo de anuncio se lista con su tipo y sin texto, en lugar de como un anuncio sin nada que decir. Los activos adjuntos al anuncio, campaña o cuenta, como enlaces de sitio, destacados y precios, no se leen aquí, por lo que un anuncio que parece delgado en esta salida puede seguir publicándose con activos junto a él.

ads_assets

Lo que está adjunto bajo el anuncio: enlaces de sitio, destacados, fragmentos estructurados, promociones, precios, activos de llamada e imagen, en los tres niveles, con lo que cada uno realmente dice en lugar de solo su tipo e id.

{ "campaign": "search-uk-us-2026-09" }
ParámetroTipoRequeridoPredeterminadoDescripción
campaignstringnoLimitar activos de campaña y grupo de anuncios a una campaña por nombre. Los activos a nivel de cuenta aún se listan, porque se aplican a cada campaña incluida esta
typeuno de SITELINK, CALLOUT, STRUCTURED_SNIPPET, PROMOTION, PRICE, CALL, IMAGEnoLimitar a un tipo de activo. Omitido, cada tipo se lista, incluidos los tipos para los que esta herramienta no tiene lectura estructurada
includeRemovedbooleannofalseIncluir enlaces cuyo estado es eliminado. Desactivado por defecto: un activo eliminado es historia y desplaza a los que pueden publicarse

Una promoción se lee de vuelta como up to 20% off on Pro plan with code LAUNCH20, 2026-01-01 to 2026-01-31, no como PROMOTION #4417. Eso importa porque Google declara el porcentaje de una promoción en millonésimas, donde 1,000,000 es 100%, por lo que el campo sin procesar es un número que nadie reconocería como descuento. Los precios vuelven con sus ofertas y moneda, los enlaces de sitio con sus descripciones.

Tres cosas que vale la pena saber antes de leer un resultado:

  • Un recurso a nivel de cuenta aplica a cada campaña, por lo que aparece listado incluso cuando nombras una sola campaña. Esta es la otra mitad de ads_ad_copy: un anuncio que parece vacío allí puede estar sirviéndose con cuatro enlaces de sitio y una promoción a su lado, ninguno de los cuales está adjunto a su campaña.
  • Adjunto no se muestra. Google decide en cada subasta si mostrar un recurso y cuáles. Esto indica lo que está disponible para servirse, no lo que se sirvió.
  • Un nombre de campaña que no coincide con nada se rechaza, no se responde. Un error tipográfico solía devolver cero filas sin error, junto a una nota que explicaba que los recursos a nivel de cuenta también se listan, por lo que el lector concluía que la cuenta no tenía ninguno. El nombre se resuelve antes de leer cualquier cosa, y uno desconocido lo dice. Quien escribe mal una campaña es exactamente quien luego dice "esa campaña no tiene enlaces de sitio" y actúa en consecuencia.
  • Un nivel que no se puede leer se reporta como un error en su lugar. Los tres niveles son tres consultas separadas, y si una falla, las otras dos aún regresan con levelErrors nombrando la que no lo hizo. Una lista vacía que silenciosamente significara "la consulta falló" se leería como "nada adjunto", que es la respuesta incorrecta a la única pregunta que se le hace a esta herramienta.

Un tipo para el que esta herramienta no tiene una lectura estructurada se nombra con su tipo de recurso y su tipo de campo, y se deja así, en lugar de dar un resumen inventado: un recurso TEXT archivado como BUSINESS_NAME se describe principalmente por la segunda mitad. El tipo de campo se muestra solo cuando difiere del tipo de recurso, ya que normalmente son la misma palabra y repetirla es ruido. Las métricas de recursos no se reportan aquí.

ads_query

Una consulta GAQL SELECT arbitraria para una pregunta que las lecturas estructuradas no cubren. GAQL no tiene otra declaración que SELECT, por lo que esto no puede cambiar nada, y una consulta que no comience con SELECT se rechaza.

{ "query": "SELECT campaign.name, metrics.cost_micros FROM campaign WHERE segments.date DURING LAST_7_DAYS" }
ParámetroTipoObligatorioPredeterminadoDescripción
querystringsíUna declaración SELECT de GAQL. GAQL no tiene otra declaración, por lo que esto no puede cambiar nada

ads_search_terms

Las consultas que realmente activaron un anuncio, con la palabra clave que coincidió con cada una. Este es el equivalente de pago de la dimensión de consultas de Search Console, y conlleva la misma advertencia: Google retiene términos que muy pocas personas buscaron, por lo que un término que no aparece listado es desconocido, no ausente.

{ "days": 90, "minImpressions": 1 }
ParámetroTipoObligatorioPredeterminadoDescripción
daysnumberno30Cuántos días hacia atrás reportar, terminando hoy
minCostnumberno0Eliminar términos de búsqueda que cuesten menos que esto durante el período
minImpressionsnumberno0Eliminar términos de búsqueda por debajo de este número de impresiones
zeroConversionsOnlybooleannofalseConservar solo términos que no convirtieron nada, que es la lista que alimenta las palabras clave negativas

ads_changes

Qué cambió en la cuenta, cuándo, qué campos, quién, y si vino de una herramienta o de alguien en el navegador: client es GOOGLE_ADS_API para lo primero y GOOGLE_ADS_WEB_CLIENT para lo segundo. Este es el rastro de auditoría para cualquier cosa que ads_update escriba, y para ediciones de consola hechas a mano. Google conserva 30 días, por lo que un período más largo se rechaza en lugar de truncarse silenciosamente.

{ "days": 14, "limit": 100 }
ParámetroTipoObligatorioPredeterminadoDescripción
daysnumberno14Cuántos días de historial de cambios leer, terminando ahora. Google conserva 30 días y rechaza más
limitnumberno100Los cambios más recientes a devolver

ads_negatives

Las palabras clave negativas ya existentes, a nivel de campaña, grupo de anuncios o conjunto compartido. Un negativo bloquea tráfico sin dejar registro de que lo hizo, por lo que esta es la lista a consultar cuando una palabra clave deja de servirse y nada parece estar mal, y antes de agregar un término que quizás ya esté allí.

{ "level": "all" }
ParámetroTipoObligatorioPredeterminadoDescripción
leveluno de campaign, adGroup, sharedSet, allno"all"Qué negativos leer. Un término bloqueado a nivel de campaña está bloqueado en toda ella; un conjunto compartido aplica a cada campaña a la que esté adjunto

ads_negatives_update

Agrega o elimina palabras clave negativas en un lote, enumeradas una por una. No hay forma de patrón ni de coincidir con todo a propósito: "bloquear todo término que coincida con X" está a un error tipográfico de un error del tamaño de una cuenta, y una lista explícita no puede cometer ese error.

{ "action": "add", "level": "campaign", "target": "search-uk-us", "keywords": ["free", "crack"], "matchType": "EXACT", "dryRun": false }
ParámetroTipoObligatorioPredeterminadoDescripción
actionuno de add, removesíAgregar palabras clave negativas o eliminar las existentes. La eliminación importa tanto como la adición: un negativo incorrecto se manifiesta como nada en absoluto
leveluno de campaign, adGroupno"campaign"Dónde viven los negativos. Un negativo a nivel de campaña bloquea el término en toda esa campaña
targetstringsíEl nombre de la campaña o grupo de anuncios. Debe coincidir exactamente con uno o no se cambia nada
keywordslista de stringsíLos términos negativos, enumerados uno por uno. No hay forma de patrón ni de coincidir con todo: un selector está a un error tipográfico de bloquear una campaña entera
matchTypeuno de BROAD, PHRASE, EXACTno"EXACT"Cómo bloquea cada término. BROAD bloquea cualquier consulta que contenga todas sus palabras, que es la configuración que puede matar silenciosamente una campaña
dryRunbooleannotrueReportar qué cambiaría, y qué negativos propuestos bloquearían una palabra clave activa, sin cambiar nada
confirmbooleannofalseEjecutar el lote aunque una protección se active. La ejecución de prueba lista qué se activó, por lo que esto confirma algo ya leído

Los negativos parecen seguros porque solo reducen el gasto, y ese instinto es lo que los hace peligrosos. Una oferta incorrecta se manifiesta como gasto. Un negativo incorrecto se manifiesta como nada: el tráfico deja de llegar, el término sale del informe de términos de búsqueda, y ninguna fila en ningún lugar dice por qué. Agregar backup como negativo amplio a una campaña de plugins de respaldo termina su tráfico, y Google no reporta error porque es un negativo perfectamente válido.

Entonces, antes de agregar cualquier cosa, cada negativo propuesto se verifica contra las palabras clave activas de la propia campaña, y el lote se rechaza a menos que confirm esté configurado. El rechazo nombra lo que habría costado: "backup" as a BROAD negative would block this campaign's own keyword "wordpress backup", which served 41 impressions. La verificación refleja de cerca la coincidencia de Google, pero Google es la autoridad, y es deliberadamente generosa, porque una advertencia falsa cuesta una frase y una omitida cuesta la campaña.

Las eliminaciones no se verifican por colisiones. Eliminar un negativo solo puede dejar pasar tráfico, lo que se manifiesta como gasto en lugar de silencio.

ads_update

Cambia una oferta de palabra clave, presupuesto diario de campaña, estado de campaña, estado de anuncio o estado de palabra clave. Esta es la única herramienta aquí que gasta dinero, por lo que está diseñada para ser difícil de disparar por accidente.

Pausar una palabra clave es su propio tipo, porque bajar su oferta no es lo mismo. Una palabra clave con oferta reducida sigue habilitada, sigue siendo elegible y sigue compitiendo por el mismo presupuesto diario. Si la razón para actuar era que el presupuesto es la restricción, bajar la oferta no libera nada de él.

{ "kind": "budget", "target": "search-uk-us-2026-09", "value": "5.00", "dryRun": false, "confirm": true }
ParámetroTipoObligatorioPredeterminadoDescripción
kinduno de bid, budget, campaignStatus, adStatus, keywordStatussíQué cambiar: la oferta de CPC máximo de una palabra clave, el presupuesto diario de una campaña, el estado de una campaña, el estado de un anuncio o el estado de una palabra clave. Usa keywordStatus para detener el servicio de una palabra clave; bajar su oferta no es lo mismo, porque la palabra clave sigue siendo elegible y sigue compitiendo por el mismo presupuesto
targetstringsíEl texto de la palabra clave, el nombre de la campaña o el id numérico del anuncio. Debe coincidir exactamente con una cosa o la llamada se rechaza
valuestringsíEl nuevo monto en dólares para una oferta o presupuesto, o pause o enable para un estado
dryRunbooleannotrueReportar qué cambiaría y qué protecciones activa, sin cambiar nada. Activado por defecto: esta herramienta gasta dinero, por lo que realizar un cambio debe solicitarse
confirmbooleannofalseRealizar un cambio que active una protección. Se ignora en una ejecución de prueba. La ejecución de prueba lista las razones de las protecciones, por lo que esto confirma algo ya leído en lugar de algo no visto

Cuatro rieles, cada uno de un fallo real en lugar de uno hipotético:

  • Una ejecución de prueba por defecto. dryRun es verdadero por defecto, por lo que omitirlo reporta el cambio y se detiene. Un parámetro obligatorio lo impone mejor que una bandera de línea de comandos, porque una bandera puede olvidarse y un valor predeterminado no.
  • Exactamente una coincidencia o rechazo. Un objetivo que no coincide con nada es un error tipográfico; un objetivo que coincide con dos es una solicitud para cambiar algo que no nombraste. Ambos se detienen antes de cualquier escritura.
  • Protecciones con razones, en palabras. Más de tres veces el monto actual, más de $25 en una sola oferta o presupuesto diario, o pausar algo que actualmente se está sirviendo. Un cambio de presupuesto también indica el equivalente mensual, porque $30 al día parece poco y son unos $912 al mes. La ejecución de prueba lista las razones, y confirm luego confirma algo que has leído en lugar de algo no visto.
  • El valor se lee de nuevo después de la escritura. Un HTTP 200 significa que la solicitud fue aceptada, no que almacenó lo que querías decir. El resultado lleva readBack y matches, y una discrepancia se devuelve como error.

Un cambio que sería una no-operación lo dice en lugar de enviar una mutación sin sentido.

Desde la línea de comandos, esta herramienta necesita --allow-spend además de --allow-write. Una bandera que autorice tanto "reenviar un sitemap" como "triplicar un presupuesto diario" no es una barrera.

ads_keyword_create

Agrega una palabra clave a un grupo de anuncios. La única herramienta aquí que crea en lugar de cambiar, y está protegida de manera diferente por esa razón.

{ "keyword": "wordpress backup plugin", "adGroup": "brand-exact", "bid": 1.2, "dryRun": false }
ParámetroTipoObligatorioPredeterminadoDescripción
keywordstringsíEl texto de la palabra clave a agregar. Se crea tal como está escrito; esta herramienta no adivina variantes
adGroupstringsíEl grupo de anuncios al que agregarla. Debe coincidir exactamente con uno o no se agrega nada
bidnumbersíLa oferta de CPC máximo en dólares. No hay oferta actual con la que comparar en una creación, por lo que la única verificación de tamaño es el techo
matchTypeuno de EXACT, PHRASE, BROADno"EXACT"Cómo coincide la palabra clave. EXACT por defecto porque es la que compra lo que dice; PHRASE y BROAD compran más que el texto escrito aquí y cada una activa una protección
dryRunbooleannotrueReportar qué se agregaría y qué protecciones activa, sin agregar nada
confirmbooleannofalseAgregarla aunque una protección se active. La ejecución de prueba lista cada razón, por lo que esto confirma algo ya leído

Cada otra escritura en este paquete lee un valor actual, lo compara con el solicitado y se niega cuando ya coinciden. Una creación no tiene valor actual. No hay nada que comparar ni contra qué negarse, por lo que la comparación debe reemplazarse en lugar de omitirse, y lo que la reemplaza es una verificación de duplicados:

  • Se rechaza una palabra clave que ya existe en el grupo de anuncios objetivo, incluidas las eliminadas. Un criterio eliminado aún conserva el texto, y Google rechaza el duplicado con un error que menciona un recurso que la interfaz no muestra, lo cual resulta confuso de encontrar sin previo aviso.
  • Una copia en otro lugar de la cuenta activa una protección en lugar de rechazarse. Ejecutar el mismo texto en dos grupos de anuncios puede ser intencional, por lo que rechazarlo haría imposible una estructura legítima; no decir nada permitiría que dos copias compitieran silenciosamente por un mismo presupuesto.
  • EXACT de forma predeterminada. PHRASE y BROAD compran cada una más que el texto escrito aquí, por lo que cada una activa una protección. Amplia es el tipo de concordancia que gasta en búsquedas que nadie tenía intención de comprar.
  • La palabra clave se lee de vuelta después de la escritura, y su tipo de concordancia y estado se comparan con lo enviado. Un 200 en una creación significa aceptada, no presente y correcta.

Una asimetría que vale la pena señalar claramente: una palabra clave creada comienza a servir de inmediato, y a diferencia de un cambio de oferta, no hay estado anterior al que volver. Deshacerla significa pausar o eliminar lo que se creó.

ads_update_batch

Cambia varias ofertas de palabras clave, o varios presupuestos diarios de campañas, en una sola llamada. Un tipo por llamada: un total entre ofertas y presupuestos sumaría un límite por clic a una cantidad por día, y ninguna frase honesta describe esa suma.

{ "kind": "bid", "changes": [{ "target": "wordpress backup", "value": 0.85 }, { "target": "backup plugin", "value": 0.6 }], "dryRun": false, "confirm": true }
ParámetroTipoObligatorioPredeterminadoDescripción
kinduno de oferta, presupuestosíUn tipo por llamada. Una protección de suma solo es honesta dentro de un tipo: las ofertas y los presupuestos suman dólares, los estados no, y mezclarlos hace que el total sea ilegible
changeslista JSONsíUna lista nombrada de pares, cada uno con su propio valor. No hay forma de selector: la enumeración no puede cometer el error que un patrón sí puede
dryRunbooleanonotrueResolver y valorar cada entrada e informar el total, sin cambiar nada
confirmbooleanonofalseEjecutar el lote aunque se haya activado una protección. La ejecución de prueba enumera cada motivo, por lo que esto confirma algo ya leído

Es una lista nombrada de pares, no una regla aplicada a muchas cosas. No hay "subir todo un 20%" ni selector, porque el error que esta herramienta existe para prevenir es exactamente el que un selector facilita: un patrón que coincide con más de lo que el llamador imaginó, aplicado antes de que nadie pueda ver la lista que produjo. Cada entrada nombra un objetivo y el valor al que debe terminar, y la ejecución de prueba imprime esa lista de vuelta.

Cuatro cosas que hace que ads_update llamado en un bucle no hace:

  • Todo se resuelve antes de que se escriba nada. Si la entrada cuatro no coincide con nada, las entradas uno a tres no están ya activas. Un bucle de llamadas individuales falla a mitad de camino y deja la cuenta en un estado que nadie eligió, sin una sola fila en ningún lugar que lo indique.
  • La suma está protegida, no solo cada entrada. Cinco aumentos que están cada uno dentro de los límites por elemento siguen siendo un cambio de gasto grande en conjunto, y hacerlos uno a la vez es como eso pasa desapercibido.
  • Se nombra la entrada que se desvía del resto. Diecinueve ofertas que se mueven unos centavos y una que se mueve $40 pueden estar bajo todos los límites y seguir siendo el error. Una entrada cuyo movimiento es mucho mayor que la mediana del lote se marca por nombre, porque un error tipográfico se esconde dentro de un total aceptable, y eso es exactamente cómo un lote difiere de las mismas escrituras enviadas una a la vez.
  • Dos entradas no pueden nombrar lo mismo. El mismo objetivo dos veces se rechaza, y también dos campañas con nombres diferentes que comparten un presupuesto, donde el total lo contaría dos veces y la segunda escritura ganaría silenciosamente.

Los límites por entrada permanecen planos sin importar cuán larga sea la lista, porque la pregunta por entrada es si esa entrada es un error tipográfico, y un error tipográfico no se vuelve más aceptable en un lote más grande. El límite en el total del lote crece con el lote, lentamente: veinte entradas no son veinte veces el riesgo de una, es una decisión tomada una vez. Una protección que se activa en cada lote realista no es una protección, es una casilla de verificación, y una vez que confirm es rutinario, se pasa sin leer.

El total siempre se indica en palabras, ya sea que algo se active o no, porque la frase es lo que se lee y la protección es solo lo que te detiene cuando no lo hace. Para presupuestos, esa es la cifra mensual en ambos sentidos: These daily budgets come to $13.00 a day, about $395 a month, up from $10.00 a day, about $304 a month.

Cada valor se lee de vuelta después de la escritura, entrada por entrada. El resultado nombra qué entradas no almacenaron lo enviado primero, luego cuáles están activas con lo que la cuenta tiene ahora, porque en un aterrizaje parcial la pregunta nunca es cuántas sino cuáles. Una solicitud aceptada es una aceptación, no N valores almacenados, y un lote es exactamente donde se esconde un aterrizaje parcial. El lote se envía como una sola solicitud sin fallo parcial, por lo que una escritura rechazada no deja nada atrás, y el error lo dice en lugar de dejarte adivinar.

Como ads_update, necesita --allow-spend además de --allow-write desde la línea de comandos.

Ejecutar una herramienta desde la línea de comandos

Cada herramienta anterior también se puede ejecutar sin un cliente MCP, que es lo que se usa cuando un resultado debe aterrizar en un archivo que una ejecución posterior pueda comparar:

seo-mcp query search_analytics --site-url sc-domain:example.com --start-date 2026-08-05 --out /tmp/sa.json
seo-mcp query --help                  # list the tools
seo-mcp query wporg_plugin --help     # list one tool's parameters

Cada ejecución nombra su propia versión en stderr (seo-console-mcp 0.15.1 running wporg_plugin), para que stdout permanezca analizable y un archivo --out permanezca como JSON puro. Un resultado no dice de otro modo qué binario lo produjo, y eso no es académico: npx reutilizará una compilación anterior en caché sin ningún error, y una instalación fallida deja la versión anterior en su lugar y funcionando. Lo que se está ejecutando, a lo que resuelve el rango de versiones y lo que npm llama último son tres valores que generalmente coinciden e independientemente no tienen que hacerlo.

Las banderas son los nombres de parámetros de la herramienta en kebab-case (--site-url para siteUrl); la ortografía camelCase también funciona. Los valores de lista están separados por comas. El resultado se escribe en --out, o en stdout cuando se omite, y un fallo sale con código distinto de cero con el mensaje en stderr. Ejecuta la misma implementación que expone la superficie MCP, por lo que los dos no pueden divergir.

Las herramientas que cambian datos (submit_sitemap, delete_sitemap, request_recrawl, indexnow_submit) están marcadas como (write) en el listado y se niegan a ejecutarse desde la línea de comandos a menos que se pase --allow-write.

Un historial está a una línea de cron de distancia, y el servidor deliberadamente no posee un programador: tu máquina ya tiene uno que sobrevive a un reinicio.

# every Monday at 06:00, one snapshot named after the moment it was taken
0 6 * * 1 seo-mcp query snapshot --properties sc-domain:example.com --out-path auto

Desarrollo

npm run dev
npm run build
npm test
npm run lint
npm run format
npm run format:check
npx tsc --noEmit

Las pruebas usan clientes falsos de Google inyectados y nunca llaman a servicios reales de Google. No confirmes claves de cuentas de servicio. Además de *.key.json, este repositorio ignora credentials*.json, .env*, archivos PEM y archivos P12.