seo-console-mcp

Um servidor MCP e CLI sob licença MIT com 43 ferramentas para Search Console, App Store, Google Play e WordPress.org. Uma linha ausente é reportada como desconhecida, nunca como zero.

Documentação

seo-mcp

seo-mcp é um servidor Model Context Protocol via stdio para Google Search Console, PageSpeed Insights e auditorias de SEO on-page. Ele oferece aos clientes MCP quarenta e três ferramentas que cobrem propriedades verificadas do Search Console e outros locais onde os produtos são descobertos — App Store, Google Play, WordPress.org, Google Ads e Core Web Vitals de usuários reais — mantendo a auditoria de HTML, PageSpeed, IndexNow, ideias de palavras-chave e ferramentas do WordPress.org utilizáveis sem credenciais de conta de serviço do Google. Todas as ferramentas também podem ser executadas pela linha de comando, permitindo que um resultado seja gravado em um arquivo em vez de no contexto de um modelo, e o snapshot registra Search Console, App Store, Google Play e WordPress.org em um único momento para que uma execução posterior possa comparar as diferenças.

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

Requisitos

  • Node.js 20.18.1 ou mais recente
  • gcloud apenas se você usar o assistente de configuração

O que mais você precisa depende de quais ferramentas você usa. O assistente de configuração cobre Search Console e PageSpeed; as ferramentas da App Store, Google Play e Chrome UX Report precisam de uma credencial que você mesmo cria.

FerramentasNecessidadeOrigem
seo_audit, audit_site, keyword_ideas (sem siteUrl), wporg_pluginnadaendpoints públicos
pagespeedSEO_MCP_PAGESPEED_KEY opcionalassistente de configuração --pagespeed-key, ou uma chave de API do Google Cloud
crux_field_data, crux_historySEO_MCP_CRUX_KEY (ou a chave do PageSpeed se ela puder chamar a API CrUX)chave de API do Google Cloud
indexnow_submitSEO_MCP_INDEXNOW_KEYqualquer chave que você hospedar em /<key>.txt
Ferramentas do Search Console, propriedades snapshotchave da conta de serviçoassistente de configuração; depois, adicione a conta à propriedade
snapshot, list_snapshots, compare_snapshotsSEO_MCP_SNAPSHOT_DIR opcionalonde os arquivos de snapshot ficam, por padrão em ~/.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_IDchave de equipe do App Store Connect, qualquer função que possa ler o app
app_store_saleso acima mais SEO_MCP_ASC_VENDOR_NUMBERchave de equipe criada com Admin, Finance ou Sales and Reports
play_store_statsSEO_MCP_PLAY_BUCKET, SEO_MCP_PLAY_CREDENTIALSconta de serviço com acesso de leitura ao bucket de relatórios
play_vitalsSEO_MCP_PLAY_CREDENTIALSconta de serviço convidada no Play Console com acesso à qualidade do app
as ferramentas ads_GOOGLE_ADS_DEVELOPER_TOKEN, GOOGLE_ADS_CLIENT_ID, GOOGLE_ADS_CLIENT_SECRET, GOOGLE_ADS_REFRESH_TOKEN, GOOGLE_ADS_CUSTOMER_IDum token de desenvolvedor do Google Ads e um cliente OAuth com token de atualização

Instalação e build

npm install
npm run build

Execute o servidor local com:

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

O pacote é publicado no npm como seo-console-mcp; ele instala um comando chamado seo-mcp. Um cliente MCP pode iniciá-lo por meio de:

npx -y seo-console-mcp

O servidor em execução usa stdout exclusivamente para o protocolo MCP. Os diagnósticos são gravados em stderr.

Assistente de configuração

A partir de um checkout local:

npm run setup

Ou com npx:

npx -y seo-console-mcp setup

Para uma escolha de projeto sem interação ou um local personalizado da chave:

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

O assistente também oferece uma chave de API opcional do PageSpeed Insights para cota maior. Ela é opcional: use --pagespeed-key para criar uma sem prompt, ou --no-pagespeed-key para pular o prompt explicitamente. Execuções não interativas a ignoram, a menos que --pagespeed-key seja fornecido.

O assistente é seguro para reexecutar. Ele:

  1. Verifica se há gcloud. Se estiver ausente, imprime instruções manuais e sai com sucesso sem alterar nada.
  2. Usa a conta autenticada ativa ou executa gcloud auth login.
  3. Usa o projeto atual, um --project fornecido ou pergunta por um ID de projeto. Ele cria o projeto se não existir e o seleciona.
  4. Ativa searchconsole.googleapis.com, pagespeedonline.googleapis.com e siteverification.googleapis.com.
  5. Reutiliza ou cria a conta de serviço seo-mcp.
  6. Reutiliza uma chave existente ou cria seo-mcp.key.json.
  7. Opcionalmente, cria uma chave de API com escopo de projeto restrita ao PageSpeed Insights.
  8. Imprime a etapa necessária de permissão do Search Console e configurações de cliente prontas para copiar.

O assistente nunca imprime o conteúdo da chave da conta de serviço. Quando a criação da chave do PageSpeed é solicitada e bem-sucedida, ele imprime essa chave uma vez na configuração final do cliente. O nome de arquivo *.key.json gerado é ignorado pelo Git.

Concedendo acesso ao Search Console para a conta de serviço

A API do Search Console não tem endpoint para adicionar um usuário a uma propriedade, então a conta de serviço precisa se tornar uma proprietária verificada do próprio domínio. Há duas maneiras de fazer isso.

Automatizado (DNS Cloudflare)

Se o DNS do domínio estiver na Cloudflare, o verify faz tudo: pede um token de verificação ao Google, grava o registro TXT pela API da Cloudflare, aguarda a verificação e registra a propriedade.

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

O token também pode ser passado com --cf-token, e o caminho da chave com --credentials (caso contrário, GOOGLE_APPLICATION_CREDENTIALS / SEO_MCP_CREDENTIALS é usado). O comando é idempotente: o registro TXT é mantido (o Google o re-verifica), então reexecutar um domínio é seguro. Deixe o registro no DNS ou a propriedade será perdida.

verify lê o token de CLOUDFLARE_API_TOKEN ou CF_API_TOKEN (ou --cf-token) e nunca o armazena ou registra em log, então qualquer cofre de segredos que possa exportar uma variável de ambiente funciona. O token precisa de Zone -> DNS -> Edit e Zone -> Zone -> Read (o modelo "Edit zone DNS"), com escopo para as zonas que você verifica. Para mantê-lo fora do histórico do 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, ou 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

Adicione a conta de serviço como proprietária na interface do Search Console:

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

Use o e-mail exato da conta de serviço impresso pelo assistente. O acesso de proprietário é necessário porque submit_sitemap é uma operação de escrita.

Fallback manual do Google Cloud

Se gcloud não estiver disponível, crie as credenciais manualmente ou execute estes comandos após instalá-lo:

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

Se o projeto já existir, pule gcloud projects create. Depois, conceda à conta de serviço acesso ao Search Console (veja acima) e configure o caminho absoluto da chave no cliente MCP.

Autenticação

As ferramentas do Search Console usam google.auth.GoogleAuth com ambos os escopos:

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

A ordem de busca de credenciais é:

  1. --credentials /absolute/path/key.json
  2. SEO_MCP_CREDENTIALS
  3. GOOGLE_APPLICATION_CREDENTIALS
  4. ~/.config/seo-mcp/seo-mcp.key.json (ou $XDG_CONFIG_HOME/seo-mcp/...) se existir. Este é o local padrão onde o assistente de configuração grava, então uma instalação padrão não precisa de configuração.

Por exemplo:

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

pagespeed é público e não usa a conta de serviço. Defina SEO_MCP_PAGESPEED_KEY ou passe apiKey para essa ferramenta para uma cota maior do PageSpeed Insights. seo_audit, audit_site e indexnow_submit também não precisam de credenciais do Google; indexnow_submit usa uma chave IndexNow via key ou SEO_MCP_INDEXNOW_KEY. keyword_ideas só precisa delas quando siteUrl é passado para a referência cruzada do Search Console. App Store Sales and Trends lê SEO_MCP_ASC_VENDOR_NUMBER. As ferramentas do Chrome UX Report leem SEO_MCP_CRUX_KEY, com fallback para SEO_MCP_PAGESPEED_KEY quando a mesma chave pode chamar chromeuxreport.googleapis.com. snapshot, list_snapshots e compare_snapshots mantêm seus documentos em SEO_MCP_SNAPSHOT_DIR, por padrão em ~/.config/seo-mcp/snapshots, e não podem ler ou gravar fora dele. A tabela em Requisitos mapeia cada ferramenta ao que ela precisa.

Modelo de segurança

  • Verificar um domínio torna a conta de serviço uma Proprietária verificada. Proprietários podem alterar configurações do Search Console e enviar solicitações de remoção (desindexação), então trate a chave como uma credencial sensível, mesmo que a maioria das ferramentas aqui apenas leia.
  • Mantenha a chave local. Ela fica no caminho GOOGLE_APPLICATION_CREDENTIALS (chmod 600 recomendado). Nunca a inclua em um pacote publicado, imagem de contêiner ou cofre de segredos de CI. Se vazar, qualquer pessoa com ela terá controle de proprietário sobre todas as propriedades verificadas.
  • Deixe o registro TXT google-site-verification no DNS. O Google o re-verifica; excluí-lo revoga a propriedade.
  • Nenhum segredo é registrado em log. O assistente e o verify imprimem apenas caminhos de credenciais, nunca conteúdos de chaves ou tokens.
  • Revogar é fácil. Renuncie à propriedade pela interface do Search Console (ou siteVerification.webResource.delete) e rotacione a chave com gcloud iam service-accounts keys delete.
  • seo_audit busca apenas hosts públicos. A URL alvo e cada salto de redirecionamento são resolvidos e recusados se caírem em um endereço loopback, privado, link-local ou outro não público, para que um modelo não seja direcionado a buscar serviços internos ou metadados de nuvem. O endereço é validado novamente no momento da conexão (o socket é fixado ao endereço validado), então um host com rebinding de DNS não pode apresentar um endereço público na validação e um privado na conexão. Defina SEO_MCP_ALLOW_PRIVATE_HOSTS=1 para auditar hosts internos ou de staging que você confia. Isso não substitui o isolamento em nível de rede; execute o servidor atrás de controles de saída se você auditar URLs não confiáveis em um host com serviços internos acessíveis.

Plugin do Claude Code

Este repositório também é um plugin do Claude Code que agrupa o servidor MCP e adiciona três comandos de barra sobre ele. No Claude Code:

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

Ele registra o servidor MCP (via npx -y seo-console-mcp) e adiciona:

  • /seo-console:triage <siteUrl>: triagem completa da propriedade com um plano de ação priorizado
  • /seo-console:content <siteUrl>: conteúdo para criar ou melhorar, com base em dados do Search Console
  • /seo-console:launch <siteUrl>: verificação de prontidão de SEO pré-lançamento / lançamento

O servidor encontra sua chave de conta de serviço automaticamente no local padrão (~/.config/seo-mcp/seo-mcp.key.json, onde o assistente de configuração a grava), então nenhuma configuração é necessária para uma instalação padrão. Para uma chave em outro lugar, defina GOOGLE_APPLICATION_CREDENTIALS (e SEO_MCP_PAGESPEED_KEY para cota maior do PageSpeed) no ambiente em que o Claude Code é executado. As ferramentas seo_audit e pagespeed funcionam sem nenhuma credencial.

Para testar a partir de um checkout local sem marketplace: claude --plugin-dir ..

Claude Code (apenas servidor MCP)

Registre o build local para o usuário atual:

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

O separador -- é obrigatório. Ele separa as opções do Claude Code do comando do servidor MCP.

Ou com npx (sem build local):

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

O escopo de usuário torna o servidor disponível em seus projetos. Use --scope project quando o registro deve ser compartilhado por meio do .mcp.json do projeto atual.

.mcp.json do projeto:

{
  "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

Adicione a mesma entrada de servidor em mcpServers no arquivo de configuração do Claude Desktop e reinicie o 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 executar sem build local, use "command": "npx" e "args": ["-y", "seo-console-mcp"].

Recursos

seo://properties retorna as propriedades do Google Search Console disponíveis para a conta de serviço como JSON. Ele chama o Search Console a cada leitura, então o resultado está sempre atualizado.

Prompts

Os clientes MCP exibem esses prompts como pontos de partida que um usuário pode escolher para fluxos de trabalho comuns de SEO:

  • seo_triage confirma uma propriedade, analisa desempenho recente e oportunidades, audita o site e produz um plano de ação de impacto versus esforço.
  • content_opportunities agrupa recomendações baseadas em evidências em conteúdo para criar e conteúdo existente para melhorar.
  • launch_seo_check produz uma lista de verificação de aprovação/reprovação para prontidão técnica e de indexação antes do lançamento.

Ferramentas

Toda ferramenta valida sua entrada com Zod. Falhas de ferramenta retornam um resultado de erro MCP em vez de encerrar o servidor. O status, a mensagem e o motivo da API do Google são incluídos quando disponíveis. Um 403 do Search Console também explica como conceder acesso à propriedade para a conta de serviço.

list_properties

Lista todas as propriedades do Google Search Console que a conta de serviço pode acessar, retornando o siteUrl e o permissionLevel exatos de cada propriedade. Não recebe nenhuma entrada. Credenciais de conta de serviço são necessárias, ao contrário de pagespeed, seo_audit, audit_site e indexnow_submit.

Esta ferramenta não recebe parâmetros.

search_analytics

Consulta o searchanalytics.query e retorna uma tabela classificada compacta, além de linhas estruturadas.

{
  "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âmetroTipoObrigatórioPadrãoDescrição
siteUrlstringsimPropriedade do Search Console, como https://example.com/ ou sc-domain:example.com
startDatestringnãoData inicial em AAAA-MM-DD; o padrão é 28 dias atrás
endDatestringnãoData final em AAAA-MM-DD; o padrão é hoje
dimensionslista de um de query, page, country, device, date, searchAppearancenão["query"]Dimensões usadas para agrupar resultados
rowLimitnúmeronão25Número máximo de linhas a retornar
startRownúmeronão0Linha inicial baseada em zero, para paginar um resultado grande
maxTableRowsnúmeronão25Limita as linhas exibidas na tabela de texto; as linhas estruturadas ficam sempre completas. 0 = apenas resumo.
dimensionFilterGroupslista JSONnãoFiltros de dimensão do Search Console
typeum de web, image, video, news, discover, googleNewsnãoTipo de resultado. discover é o feed do Discover e googleNews é o app Google News e news.google.com, não a aba Notícias na Pesquisa. Ambos suportam menos dimensões que web: nenhum reporta dimensão de query
dataStateum de full, allnãofull = dados finalizados (padrão, atraso de ~2-3 dias); all = incluir dados parciais recentes
aggregationTypeum de auto, byProperty, byPagenãoComo o Search Console agrega as linhas

maxTableRows limita apenas a tabela de texto; as linhas estruturadas permanecem completas, então 0 retorna os totais sem tabela, em vez de um resultado vazio. discover e googleNews suportam menos dimensões que web: nenhum reporta uma dimensão query.

keyword_ideas

Expande uma semente pelo Google Autocomplete e retorna ideias de palavras-chave normalizadas e deduplicadas, agrupadas por família de descoberta. Usa o endpoint público de autocomplete, não precisa de chave de API extra e funciona sem credenciais do Google, a menos que siteUrl seja fornecido. Com uma propriedade do Search Console, marca ideias já ranqueadas com posição média, cliques e impressões na janela de retrospectiva selecionada.

{
  "seed": "technical seo",
  "siteUrl": "sc-domain:example.com",
  "language": "en",
  "country": "us",
  "expansions": ["alphabet", "questions", "prepositions", "comparisons"],
  "days": 90,
  "limit": 100
}
ParâmetroTipoObrigatórioPadrãoDescrição
seedstringsimPalavra-chave semente a expandir
siteUrlstringnãoPropriedade opcional do Search Console usada para identificar consultas já ranqueadas
languagestringnão"en"Idioma da interface do Autocomplete, passado como hl
countrystringnãoPaís do Autocomplete, passado como gl
expansionslista de um de alphabet, questions, prepositions, comparisonsnão["alphabet","questions","prepositions","comparisons"]Famílias de expansão de sugestões a executar além da semente pura
daysnúmeronão90Janela de retrospectiva do Search Console em dias
limitnúmeronão100Número máximo de ideias de palavras-chave a retornar

Todas as quatro famílias de expansão são executadas por padrão. days tem padrão de 90 e limite máximo de 480; limit tem padrão de 100 e limite máximo de 500. Falhas individuais de autocomplete são contadas sem descartar sugestões bem-sucedidas.

search_opportunities

Encontra consultas de alta impressão a uma distância impressionante de rankings mais fortes. Agrupa por consulta e página, usa por padrão as posições 5 a 20 e retorna oportunidades classificadas por posição ponderada por impressões.

{
  "siteUrl": "sc-domain:example.com",
  "startDate": "2026-06-01",
  "endDate": "2026-06-28",
  "minPosition": 5,
  "maxPosition": 20,
  "minImpressions": 100,
  "limit": 25
}
ParâmetroTipoObrigatórioPadrãoDescrição
siteUrlstringsimPropriedade do Search Console a analisar
startDatestringnãoData inicial em AAAA-MM-DD; o padrão é a janela mais recente de 28 dias
endDatestringnãoData final em AAAA-MM-DD; o padrão é hoje
minPositionnúmeronãoMenor posição média a incluir; o padrão é 5
maxPositionnúmeronãoMaior posição média a incluir; o padrão é 20
minImpressionsnúmeronãoImpressões mínimas exigidas; o padrão é 10
limitnúmeronãoNúmero máximo de oportunidades a retornar; o padrão é 50

compare_search_periods

Compara uma janela selecionada com a janela imediatamente anterior de mesmo comprimento. Retorna os maiores ganhadores e perdedores de cliques, agrupados por consulta ou página.

{
  "siteUrl": "sc-domain:example.com",
  "startDate": "2026-06-01",
  "endDate": "2026-06-28",
  "by": "query",
  "limit": 25
}
ParâmetroTipoObrigatórioPadrãoDescrição
siteUrlstringsimPropriedade do Search Console a analisar
startDatestringnãoData inicial em AAAA-MM-DD; o padrão é a janela mais recente de 28 dias
endDatestringnãoData final em AAAA-MM-DD; o padrão é hoje
byum de query, pagenão"query"Dimensão usada para comparar o desempenho
limitnúmeronãoNúmero máximo de ganhadores e perdedores a retornar; o padrão é 50 de cada

ctr_gaps

Encontra consultas ou páginas de alta impressão cujo CTR fica abaixo da média das linhas na mesma posição arredondada. A estimativa de cliques perdidos ajuda a priorizar reescritas de título e descrição.

{
  "siteUrl": "sc-domain:example.com",
  "by": "page",
  "minImpressions": 250,
  "limit": 25
}
ParâmetroTipoObrigatórioPadrãoDescrição
siteUrlstringsimPropriedade do Search Console a analisar
startDatestringnãoData inicial em AAAA-MM-DD; o padrão é a janela mais recente de 28 dias
endDatestringnãoData final em AAAA-MM-DD; o padrão é hoje
byum de query, pagenão"query"Dimensão usada para identificar lacunas de CTR
minImpressionsnúmeronãoImpressões mínimas exigidas; o padrão é 100
limitnúmeronãoNúmero máximo de lacunas a retornar; o padrão é 50

query_cannibalization

Encontra consultas para as quais várias páginas recebem impressões no Search Console. Os resultados agrupam as páginas concorrentes e classificam os grupos por total de impressões.

{
  "siteUrl": "sc-domain:example.com",
  "startDate": "2026-06-01",
  "endDate": "2026-06-28",
  "minImpressions": 25
}
ParâmetroTipoObrigatórioPadrãoDescrição
siteUrlstringsimPropriedade do Search Console a analisar
startDatestringnãoData inicial em AAAA-MM-DD; o padrão é a janela mais recente de 28 dias
endDatestringnãoData final em AAAA-MM-DD; o padrão é hoje
minImpressionsnúmeronãoImpressões mínimas por linha de consulta-página; o padrão é 10

list_sitemaps

Lista o caminho do sitemap, horários de envio/download, flags de pendência/índice, contagens de avisos/erros e contagens de conteúdo.

{
  "siteUrl": "https://www.example.com/"
}
ParâmetroTipoObrigatórioPadrãoDescrição
siteUrlstringsimPropriedade do Search Console

submit_sitemap

Envia um sitemap e atualiza o estado atual dele. Esta é uma operação de escrita. Se o envio for bem-sucedido, mas a atualização de estado falhar, o resultado ainda confirma que o Google aceitou a escrita e relata o aviso de atualização.

{
  "siteUrl": "sc-domain:example.com",
  "feedpath": "https://www.example.com/sitemap.xml"
}
ParâmetroTipoObrigatórioPadrãoDescrição
siteUrlstringsimPropriedade do Search Console
feedpathstringsimURL absoluta do sitemap a enviar
dryRunbooleanonãofalseSe verdadeiro, relata o que seria enviado sem gravar no Search Console

delete_sitemap

Remove um sitemap enviado de uma propriedade do Search Console. Esta é uma operação de escrita. Defina dryRun como true para visualizar a remoção sem alterar o Search Console.

{
  "siteUrl": "sc-domain:example.com",
  "feedpath": "https://www.example.com/sitemap.xml",
  "dryRun": true
}
ParâmetroTipoObrigatórioPadrãoDescrição
siteUrlstringsimPropriedade do Search Console
feedpathstringsimURL absoluta do sitemap a remover
dryRunbooleanonãofalseSe verdadeiro, relata o que seria removido sem gravar no Search Console

inspect_url

Retorna cobertura de índice, veredito, estado de robots, estado de indexação, tempo de rastreamento, estado de busca, canônicos do Google e do usuário, usabilidade mobile e status de resultados avançados.

{
  "siteUrl": "sc-domain:example.com",
  "inspectionUrl": "https://www.example.com/products/widget"
}
ParâmetroTipoObrigatórioPadrãoDescrição
siteUrlstringsimPropriedade do Search Console que contém a URL inspecionada
inspectionUrlstringsimURL totalmente qualificada a inspecionar

index_coverage

Busca um sitemap e verifica um conjunto limitado das URLs de página diretas dele com a API de Inspeção de URL do Google. Retorna contagens de indexadas, não indexadas e com falha, as URLs não indexadas e estados de cobertura, resultados completos por URL e se o resultado foi truncado. Índices de sitemap não são seguidos para sitemaps filhos.

{
  "siteUrl": "sc-domain:example.com",
  "sitemapUrl": "https://www.example.com/sitemap.xml",
  "maxUrls": 20,
  "concurrency": 3
}
ParâmetroTipoObrigatórioPadrãoDescrição
siteUrlstringsimPropriedade do Search Console que contém as URLs do sitemap
sitemapUrlstringsimURL totalmente qualificada do sitemap a inspecionar
maxUrlsnúmeronão20Número máximo de URLs a inspecionar
concurrencynúmeronão3Solicitações simultâneas de Inspeção de URL

maxUrls tem padrão de 20 e máximo absoluto de 50. concurrency tem padrão de 3 e máximo absoluto de 5. Esses limites protegem a cota da API de Inspeção de URL, que é de aproximadamente 2.000 consultas por dia e 600 por minuto para cada propriedade.

request_recrawl

Verifica URLs com a API de Inspeção de URL e, quando algumas não estão indexadas, reenvia o sitemap que as cobre. Esse reenvio é o único sinal de recrawl em massa suportado pelo Google: não existe API de solicitação de indexação, e o botão Solicitar Indexação da interface do Search Console não tem equivalente programático. As URLs vêm de urls ou são lidas de sitemapUrl; o sitemap a reenviar é feedpath, com padrão de sitemapUrl. Esta é uma operação de escrita. Defina dryRun como true para inspecionar e relatar sem reenviar.

{
  "siteUrl": "sc-domain:example.com",
  "sitemapUrl": "https://www.example.com/sitemap.xml",
  "maxUrls": 20,
  "dryRun": true
}
ParâmetroTipoObrigatórioPadrãoDescrição
siteUrlstringsimPropriedade do Search Console que contém as URLs
urlslista de stringnãoURLs explícitas a verificar; omita para lê-las de sitemapUrl
sitemapUrlstringnãoSitemap do qual ler as URLs; também é o sitemap padrão a reenviar
feedpathstringnãoSitemap a reenviar quando URLs não indexadas forem encontradas; o padrão é sitemapUrl
maxUrlsnúmeronão20Número máximo de URLs do sitemap a inspecionar
concurrencynúmeronão3Solicitações simultâneas de Inspeção de URL
dryRunbooleanonãofalseSe verdadeiro, inspeciona e relata sem reenviar o sitemap

Ele compartilha os limites de index_coverage (maxUrls até 50, concurrency até 5) porque ambos usam a mesma cota de Inspeção de URL. O reenvio apenas solicita um novo rastreamento de páginas cujo lastmod do sitemap está atualizado, então mantenha o lastmod preciso para URLs alteradas.

indexnow_submit

Envia até 10.000 URLs alteradas em uma única chamada para um endpoint IndexNow. Os mecanismos participantes (Bing, Yandex, Naver, Seznam, Yep) compartilham envios entre si. O Google não usa IndexNow; use request_recrawl para o Google. Esta é uma operação de gravação e suporta dryRun. Não requer credenciais do Google.

{
  "urls": ["https://www.example.com/new-page", "https://www.example.com/updated-page"],
  "key": "your-indexnow-key"
}
ParâmetroTipoObrigatórioPadrãoDescrição
urlslista de stringssimURLs de páginas alteradas; um envio cobre um único host
keystringnãoChave IndexNow; o padrão é SEO_MCP_INDEXNOW_KEY. A mesma chave deve estar hospedada no site como um arquivo de texto em https:///.txt (ou em keyLocation) contendo apenas a chave
keyLocationstringnãoURL do arquivo de chave hospedado quando não estiver em https:///.txt
endpointum de api.indexnow.org, www.bing.com, yandex.com, searchadvisor.naver.com, search.seznam.cz, indexnow.yep.comnão"api.indexnow.org"Endpoint IndexNow a notificar; mecanismos participantes compartilham envios
dryRunbooleanonãofalseSe verdadeiro, relata o que seria enviado sem notificar o endpoint

Todas as URLs em um único envio devem compartilhar um host. A chave é qualquer valor de 8 a 128 caracteres composto por letras, dígitos ou hífens, passada como key ou SEO_MCP_INDEXNOW_KEY, e deve ser hospedada como um arquivo de texto contendo exatamente a chave em https://<host>/<key>.txt (ou em keyLocation no mesmo host). Como as URLs de arquivos de chave convencionalmente contêm a chave, nem a chave nem keyLocation são exibidos na saída da ferramenta. endpoint tem como padrão api.indexnow.org; um envio para qualquer endpoint participante alcança todos eles.

pagespeed

Retorna dados de campo CrUX quando disponíveis, incluindo LCP, CLS, INP ou FID, FCP e TTFB. Também retorna pontuações de categorias do Lighthouse e até dez oportunidades de maior economia.

{
  "url": "https://www.example.com/",
  "strategy": "mobile",
  "category": ["performance", "seo", "accessibility", "best-practices"]
}
ParâmetroTipoObrigatórioPadrãoDescrição
urlstringsimURL pública da página a analisar
strategyum de mobile, desktopnão"mobile"Estratégia de dispositivo do Lighthouse
categorylista de um de performance, seo, accessibility, best-practicesnão["performance","seo","accessibility","best-practices"]Categorias do Lighthouse a executar
apiKeystringnãoChave opcional da API PageSpeed Insights; o padrão é SEO_MCP_PAGESPEED_KEY

strategy tem como padrão mobile. Todas as quatro categorias são solicitadas por padrão. apiKey é opcional e substitui SEO_MCP_PAGESPEED_KEY para essa chamada.

seo_audit

Busca até 10 MB de HTML com redirecionamentos habilitados, um tempo limite de 15 segundos e um user agent identificador. Extrai comprimentos de título e descrição, canonical, robots, H1s e estrutura de cabeçalhos, tags Open Graph e Twitter, tipos JSON-LD, cobertura de alt de imagens, links internos/externos, contagem de palavras, idioma e viewport. Sinaliza títulos ausentes ou duplicados, descrição ausente, H1s ausentes ou múltiplos, canonical ausente e JSON-LD ausente ou inválido.

{
  "url": "https://www.example.com/landing-page"
}
ParâmetroTipoObrigatórioPadrãoDescrição
urlstringsimURL pública da página a auditar

audit_site

Busca um sitemap e audita até 50 de suas URLs de página com concorrência limitada. Índices de sitemap são suportados com um limite máximo de cinco buscas de sitemaps filhos. O resultado inclui descobertas compactas por página, erros isolados de busca de página, uma contagem de cada problema compartilhado e contagens explícitas de truncamento e pulos. Não requer credenciais do Google.

{
  "sitemapUrl": "https://www.example.com/sitemap.xml",
  "maxPages": 20,
  "concurrency": 5
}
ParâmetroTipoObrigatórioPadrãoDescrição
sitemapUrlstringsimURL pública do sitemap a auditar
maxPagesnúmeronão20Máximo de páginas a auditar
concurrencynúmeronão5Máximo de buscas de página em andamento

maxPages tem como padrão 20 e concurrency tem como padrão 5. Seus valores máximos são 50 e 10, respectivamente.

server_version

Qual versão do servidor está respondendo, de onde está sendo executado e se veio de um cache npx. Sem credenciais.

Esta ferramenta não aceita parâmetros.

Quatro valores parecem com este e não são: o que o npm chama de latest, o que o intervalo de versão resolve, o que o manifesto do plugin declara e o que está realmente em execução. Os três primeiros são todos legíveis e nenhum deles responde à pergunta. Verificar a ferramenta de linha de comando também não é um substituto, pois é um processo separado resolvido separadamente e pode ser uma versão diferente na mesma máquina.

O caminho de instalação é o indicador. npx reutiliza uma versão em cache sem re-resolver o intervalo e sem gerar erro, então um servidor pode ficar atrás da versão publicada enquanto todos os outros sinais leem o atual; um caminho sob _npx é o que mostra isso.

Chame após atualizar, antes de relatar qualquer coisa. npm view <pkg> version lê um cache de registro local e pode retornar a versão anterior por minutos após uma publicação bem-sucedida, enquanto dist-tags e o array de versões já carregam a nova. Duas sessões aqui concluíram independentemente que uma publicação havia falhado quando não havia, em versões separadas. Uma leitura de registro não pode distinguir uma publicação lenta de uma falha; perguntar ao processo em execução o que ele é pode.

wporg_plugin

Consulta um plugin do WordPress.org por slug e retorna instalações ativas, downloads, avaliações, tópicos de suporte e datas de versão. Usa a API pública do wp.org e não requer credenciais ou chave de API. Um plugin publicado nos últimos dias é relatado com possiblyLagging: true quando um campo parece vazio, porque a API do wp.org subnotifica plugins novos; o campo pode já estar ativo na página.

{ "slug": "akismet" }
ParâmetroTipoObrigatórioPadrãoDescrição
slugstringsimSlug do plugin WordPress.org, ex.: akismet
downloadDaysnúmeronão30Dias de histórico diário de downloads a buscar; 0 pula
includeVersionDistributionbooleanonãotrueTambém busca a parcela de instalações ativas em cada versão do plugin

play_store_stats

Lê os relatórios em massa do Google Play para um aplicativo e retorna Instalações Ativas de Dispositivos, além de visitantes da listagem da loja e aquisições agrupadas por fonte de tráfego e termo de pesquisa. hasPlaySearchRows declara explicitamente se qualquer tráfego de pesquisa do Play aparece, já que sua ausência é uma descoberta, não um erro. Os relatórios atrasam por dias, então lastDatePresent é a última data realmente nos arquivos, não hoje.

{ "packageName": "com.example.app", "month": "202608" }
ParâmetroTipoObrigatórioPadrãoDescrição
packageNamestringsimNome do pacote Android, ex.: app.getpsst
monthstringnãoMês do relatório como YYYYMM; o padrão é o mês UTC atual. Ignorado quando startDate e endDate são fornecidos
installsDimensionum de overview, country, language, device, os_version, carrier, app_versionnão"overview"Qual relatório de instalações ler. overview é indocumentado pelo Google, mas presente em buckets reais; os outros são os detalhamentos documentados
includelista de um de ratings, crashes, reviewsnão[]Famílias extras de relatórios a ler. Arquivos ausentes são normais: o Google emite um relatório apenas quando há algo a relatar
storePerformanceDimensionum de traffic_source, countrynão"traffic_source"Qual detalhamento de desempenho da loja ler
storePerformanceTotalsbooleanonãofalseLer a variante total_ em vez disso. É um relatório diferente, não um resumo do mesmo: contém apenas aquisições, sem visitantes e sem taxa de conversão, e para alguns aplicativos cobre muito menos datas e atribui cada aquisição a uma fonte de espaço reservado
ratingsDimensionum de country, language, device, os_version, carrier, app_versionnão"country"Dimensão para o relatório de avaliações
crashesDimensionum de device, os_version, app_versionnão"app_version"Dimensão para o relatório de falhas
startDatestringnãoInício da janela em YYYY-MM-DD. Com endDate, lê todos os meses que a janela toca e filtra linhas para ela
endDatestringnãoFim da janela em YYYY-MM-DD

Defina SEO_MCP_PLAY_BUCKET para o bucket de relatórios (gs://pubsite_prod_... e o nome simples ambos funcionam) e SEO_MCP_PLAY_CREDENTIALS para uma chave de conta de serviço com acesso de leitura a esse bucket, com fallback para GOOGLE_APPLICATION_CREDENTIALS. Acesso de leitura ao bucket é uma permissão diferente do convite do Play Console que play_vitals precisa. month tem como padrão o mês UTC atual.

app_store_listing

Lê uma listagem da App Store por meio do App Store Connect e mede os campos de cada localidade em relação aos limites da Apple: nome 30, subtítulo 30, palavras-chave 100, texto promocional 170. A Apple indexa apenas o nome, o subtítulo e o campo de palavras-chave, então a descrição é relatada, mas nunca pontuada, e um campo um caractere acima do limite é descartado silenciosamente em vez de rejeitado, por isso cada campo é relatado em relação ao seu limite. O texto promocional é destacado separadamente porque é o único desses que pode ser alterado em uma versão ativa sem revisão.

Um aplicativo pode ter um registro ativo e um editável ao mesmo tempo, então state seleciona qual é lido e o resultado declara o registro e a versão usados. Quando o registro solicitado não existe, o outro é relatado e uma nota diz isso em vez de apresentá-lo como o que você pediu.

O estado relatado vem de appVersionState, com fallback para o obsoleto appStoreState. Os dois escrevem a mesma coisa de forma diferente: uma listagem ativa lê READY_FOR_DISTRIBUTION onde o atributo obsoleto dizia READY_FOR_SALE. A saída capturada antes e depois dessa mudança diferirá apenas na string, sem que nada tenha acontecido com a listagem.

{ "bundleId": "com.example.app", "state": "live", "platform": "IOS", "storefronts": ["us", "gb"] }
ParâmetroTipoObrigatórioPadrãoDescrição
appIdstringnãoID numérico do aplicativo no App Store Connect; forneça este ou bundleId
bundleIdstringnãoID do pacote, resolvido para um ID de aplicativo quando appId não é fornecido; forneça este ou appId
platformum de IOS, MAC_OS, TV_OS, VISION_OSnão"IOS"Plataforma da App Store cuja versão é lida
stateum de live, editablenão"live"Ler a listagem ativa ou a editável sendo preparada para lançamento
storefrontslista de stringsnão["us"]Códigos de país da loja para a consulta pública de avaliações

Forneça appId ou bundleId. Defina SEO_MCP_ASC_KEY_PATH para a chave privada .p8 e SEO_MCP_ASC_KEY_ID para seu ID de chave, além de SEO_MCP_ASC_ISSUER_ID para uma chave de equipe (chaves individuais não têm ID de emissor). A chave e o token que ela assina nunca aparecem na saída.

Uma chave de equipe alcança todos os aplicativos da equipe, então uma chave pode servir a todos. O que a limita é a função que recebeu, e a Apple não permite que a função de uma chave seja alterada depois: a única edição oferecida é Revogar. Uma chave de App Manager lê listagens, mas não relatórios de Vendas e Tendências ou análises, então esses precisam de uma chave separada criada com Admin, Finanças ou Vendas e Relatórios, em vez de um upgrade da que você tem. ratings é uma lista, uma entrada por loja solicitada, não um objeto chaveado por loja:

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

A classificação por estrelas não vem do App Store Connect. Sua API não possui nenhum recurso de classificação agregada, apenas classificações etárias. Portanto, a classificação é lida da consulta pública da loja App Store, enquanto todos os outros campos desta ferramenta vêm do App Store Connect. Duas fontes relatando um número que parece o mesmo de qualquer forma, é por isso que cada entrada carrega source. Uma classificação de uma página da loja e uma classificação de uma API privada não são intercambiáveis e não devem ser comparadas como se fossem a mesma medição.

list_snapshots

Lista os documentos de snapshot já presentes no diretório de snapshots, do mais recente para o mais antigo, com quando cada um foi capturado, a janela que cobre e quantas propriedades, apps, pacotes e plugins contém.

{ "limit": 50 }
ParâmetroTipoObrigatórioPadrãoDescrição
limitnúmeronão50Máximo de snapshots a retornar, do mais recente para o mais antigo

Um par de snapshots não vale nada se nada puder dizer quais arquivos existem, e todo chamador, de outra forma, ficava mantendo seu próprio índice de um diretório que o servidor possui. Um arquivo no diretório que não é um documento de snapshot é listado com seu erro em vez de oculto, para que um nome que você espera encontrar nunca leia silenciosamente como ausente. Um diretório ausente é uma lista vazia, não uma falha: nada foi capturado ainda. total e truncated ficam ao lado da lista porque a linha de comando imprime apenas a metade estruturada, onde um corte de página em limit seria lido como o histórico completo.

snapshot

Captura quatro superfícies em um único documento com timestamp: totais do Search Console e principais linhas por propriedade, listagens da App Store, instalações e tráfego do Google Play, e estatísticas do WordPress.org. Dados de campo do Core Web Vitals, Android vitals, vendas da App Store e avaliações da App Store não estão incluídos; crux_field_data, play_vitals, app_store_sales e app_store_reviews leem esses. Esta é a ferramenta para registrar um ponto em uma série, porque nenhum dos consoles mantém um histórico que você possa comparar depois.

Os totais do Search Console vêm da dimensão date, nunca somando a dimensão de consulta. O Google retém consultas de baixo volume, então uma soma no nível de consulta subestima, e essa lacuna é lida depois como um declínio que nunca aconteceu.

Uma superfície que não pode ser lida é registrada no lugar com seu erro e nomeada em surfacesWithErrors, nunca omitida, porque uma superfície que desaparece silenciosamente é lida depois como uma queda para zero. Uma superfície lenta expira sem derrubar o documento.

{
  "properties": ["sc-domain:example.com"],
  "apps": ["1234567890"],
  "packages": ["com.example.app"],
  "slugs": ["akismet"],
  "windowDays": 28,
  "outPath": "2026-09-03.json"
}
ParâmetroTipoObrigatórioPadrãoDescrição
propertieslista de stringnão[]Propriedades do Search Console a capturar
appslista de stringnão[]Apps da App Store, cada um com um ID numérico de app ou um bundle id
packageslista de stringnão[]Nomes de pacotes do Google Play
slugslista de stringnão[]Slugs de plugins do WordPress.org
windowDaysnúmeronão28Janela do Search Console em dias, terminando hoje
platformum de IOS, MAC_OS, TV_OS, VISION_OSnão"IOS"Plataforma da App Store para as superfícies de app
storefrontslista de stringnão["us"]Códigos de país da loja para classificações da App Store
outPathstringnãoNome do arquivo ou caminho dentro do diretório de snapshots (SEO_MCP_SNAPSHOT_DIR, padrão ~/.config/seo-mcp/snapshots); deve terminar em .json, ou passe auto para nomear o arquivo após o momento em que foi capturado. Um arquivo existente não é sobrescrito a menos que overwrite seja true
overwritebooleanonãofalseSubstitui um arquivo existente em outPath; sem isso, um arquivo existente é deixado intacto e relatado

Passe outPath para escrever o documento onde compare_snapshots possa lê-lo depois, ou outPath: "auto" para que seja nomeado após o momento em que foi capturado (2026-09-04T00-15Z.json), que é o que faz uma execução não supervisionada produzir uma série em vez de um arquivo sobrescrito para sempre. É um nome de arquivo dentro do diretório de snapshots, SEO_MCP_SNAPSHOT_DIR ou ~/.config/seo-mcp/snapshots por padrão; um caminho que resolve fora desse diretório ou que não termina em .json é recusado, e um arquivo existente é deixado no lugar e relatado a menos que você passe overwrite: true. Um modelo escolhe essa string, então o diretório é o limite que impede uma chamada de ferramenta de truncar qualquer outra coisa na máquina. Posição e CTR são null em vez de 0 quando uma janela não tem impressões, para que uma janela vazia nunca compare contra dados reais como um colapso.

compare_snapshots

Lê dois documentos de snapshot e relata o que mudou entre eles: cliques, impressões e posição por propriedade, movimentadores no nível de página e de consulta acima de um piso de impressões, deltas de instalações e classificações, mudanças de versão e contagem de localidades da App Store, comprimentos por localidade de nome, subtítulo, palavras-chave, texto promocional e descrição, além de quais campos cruzaram um limite de caracteres, fontes de tráfego do Google Play por visitantes e aquisições, e o histograma de cinco estrelas do WordPress.org.

{ "from": "2026-08-06.json", "to": "2026-09-03.json", "minImpressions": 100 }
ParâmetroTipoObrigatórioPadrãoDescrição
fromstringsimNome do arquivo de snapshot ou caminho dentro do diretório de snapshots; latest nomeia o snapshot mais recente no disco e previous o anterior
tostringsimNome do arquivo de snapshot ou caminho dentro do diretório de snapshots; latest nomeia o snapshot mais recente no disco e previous o anterior
minImpressionsnúmeronão100Ignora movimentos de posição de página abaixo deste número de impressões em ambos os lados

from e to resolvem dentro do mesmo diretório de snapshots que o outPath de snapshot, então esta ferramenta lê snapshots e nada mais. Qualquer um também aceita latest ou previous em vez de um nome de arquivo, que é a comparação que quase todo chamador realmente quer e a única que pode pedir sem listar o diretório primeiro. Ambos pulam um arquivo que não será analisado, e pedir previous com um único snapshot no disco diz isso em vez de comparar um documento contra si mesmo.

Faz aritmética, nunca julgamento. Não dirá se uma mudança foi boa ou o que a causou, porque um diff não pode sustentar essa afirmação. Uma superfície que falhou ou está ausente em qualquer lado é marcada como não comparável e nomeada, para que uma falha de coleta nunca seja lida como uma mudança, e um arquivo que não é um documento de snapshot é recusado em vez de parcialmente analisado.

Snapshots capturados antes de um campo ser registrado ainda comparam. Um campo que um lado não carrega retorna como um delta null em vez de uma mudança, e um par de apps sem comprimentos por localidade em nenhum lado relata localesComparable: false em vez de uma listagem esvaziada para zero caracteres.

app_store_reviews

Lê avaliações de clientes da App Store e suas respostas, filtradas por classificação por estrelas ou loja, seguindo o cursor de paginação da própria Apple.

{ "bundleId": "com.example.app", "rating": [1, 2], "territory": "USA", "limit": 100 }
ParâmetroTipoObrigatórioPadrãoDescrição
appIdstringnãoID numérico do app no App Store Connect; forneça este ou bundleId
bundleIdstringnãoBundle id; forneça este ou appId
ratinglista de númeronãoApenas estas classificações por estrelas
territorystringnãoApenas avaliações desta loja
sortum de -createdDate, createdDate, rating, -ratingnão"-createdDate"Ordem de classificação; mais recentes primeiro por padrão
limitnúmeronão100Máximo de avaliações a retornar entre páginas
maxPagesnúmeronão5Máximo de páginas a seguir

Relata meanOfFetched e histogramOfFetched, nunca "a classificação". Esses descrevem apenas as avaliações que esta chamada retornou, e uma página filtrada ou truncada faria uma média ser um número diferente usando o mesmo nome. O App Store Connect não expõe nenhum recurso de classificação agregada, o que é verificável na própria especificação OpenAPI da Apple: todo caminho que corresponde a "rating" é uma classificação etária.

app_store_discovery

Lê as superfícies da App Store além do texto da listagem: palavras-chave de busca (a lista real de palavras-chave indexadas da Apple, mantida por localidade), tags do app, experimentos de otimização de página de produto, páginas de produto personalizadas, eventos no app, disponibilidade por território e resumos de avaliações.

{ "bundleId": "com.example.app", "locales": ["en-US", "ar-SA"], "platform": "IOS" }
ParâmetroTipoObrigatórioPadrãoDescrição
appIdstringnãoID numérico do app no App Store Connect; forneça este ou bundleId
bundleIdstringnãoBundle id; forneça este ou appId
includelista de um de searchKeywords, appTags, experiments, customProductPages, appEvents, availability, reviewSummarizationsnão[]Quais superfícies de descoberta ler; vazio lê todas
limitnúmeronão50Linhas por recurso
localeslista de stringnão["en-US"]Localidades para recursos por localidade, como searchKeywords
platformum de IOS, MAC_OS, TV_OS, VISION_OSnão"IOS"Plataforma para recursos que exigem uma
includeRowsbooleanonãofalseInclui cada linha bruta além das contagens; desativado por padrão para que uma chamada de resumo permaneça pequena

Cada recurso carrega seus próprios parâmetros obrigatórios: searchKeywords precisa tanto de um filtro de plataforma quanto de localidade, appAvailabilityV2 é um relacionamento um-para-um que rejeita limit diretamente. Um recurso que esta chave ou app não pode atender é relatado como available: false, nunca como uma lista vazia, porque "sem experimentos" e "não é possível ler experimentos" são respostas diferentes.

crux_field_data

Core Web Vitals de usuários reais para uma origem ou uma única URL do Chrome UX Report: o registro de campo atual de 28 dias, com p75s e bins completos do histograma.

{ "origin": "https://example.com", "formFactor": "PHONE" }
ParâmetroTipoObrigatórioPadrãoDescrição
originstringnãoOrigem como https://example.com; agrega todas as páginas sob ela. Forneça origin ou url, não ambos
urlstringnãoUma única URL de página. Forneça origin ou url, não ambos
formFactorum de PHONE, TABLET, DESKTOPnãoClasse de dispositivo; omita para todos os fatores de forma combinados
metricslista de stringnãoNomes de métricas a solicitar; omita para todas as disponíveis

Estes são dados de campo, não um teste de laboratório; mantenha pagespeed para auditorias do Lighthouse. O Google está descontinuando os dados de mundo real do PageSpeed, então é para onde as medições de campo estão migrando. Uma origem com poucas amostras anonimizadas retorna hasData: false com uma nota em vez de um erro ou zeros, já que um LCP zerado seria lido como uma regressão catastrófica.

crux_history

As mesmas métricas de campo como uma série semanal, aproximadamente seis meses de histórico.

{ "origin": "https://example.com", "formFactor": "PHONE", "collectionPeriodCount": 25 }
ParâmetroTipoObrigatórioPadrãoDescrição
originstringnãoOrigem, como https://example.com;, que agrega todas as páginas sob ela. Informe origin ou url, não ambos
urlstringnãoURL de uma única página. Informe origin ou url, não ambos
formFactorum de PHONE, TABLET, DESKTOPnãoClasse de dispositivo; omita para combinar todos os formatos
metricslista de stringnãoNomes das métricas a solicitar; omita para todas as disponíveis
collectionPeriodCountnumbernãoPeríodos semanais a retornar, de 1 a 40. O histórico documentado é de cerca de seis meses; a API decide o que realmente possui

Cada período é uma janela móvel de 28 dias com avanço semanal, então pontos consecutivos se sobrepõem em três semanas e uma mudança isolada de uma semana para outra não é uma alteração independente. Períodos com poucas amostras mantêm seu lugar na série como null, em vez de serem descartados, para que os valores permaneçam alinhados com collectionPeriods.

app_store_sales

Lê Vendas e Tendências da App Store: unidades baixadas por dia, por território, por app, resumidas por SKU.

{ "reportDate": "2026-08-30", "frequency": "DAILY", "reportType": "SALES", "reportSubType": "SUMMARY" }
ParâmetroTipoObrigatórioPadrãoDescrição
reportDatestringnãoData do relatório. DAILY e WEEKLY usam AAAA-MM-DD (WEEKLY significa a data de término da semana), MONTHLY usa AAAA-MM, YEARLY usa AAAA. O padrão é o período completo mais recente para a frequência
frequencyum de DAILY, WEEKLY, MONTHLY, YEARLYnão"DAILY"Período do relatório
reportTypeum de SALES, PRE_ORDER, SUBSCRIPTION, SUBSCRIPTION_EVENT, SUBSCRIBER, INSTALLS, FIRST_ANNUALnão"SALES"Tipo de relatório de Vendas e Tendências
reportSubTypeum de SUMMARY, DETAILED, SUMMARY_INSTALL_TYPE, SUMMARY_TERRITORY, SUMMARY_CHANNELnão"SUMMARY"Subtipo do relatório
versionstringnãoVersão do relatório, como 1_0 ou 1_3, quando o padrão não for aceito
includeRowsbooleannãofalseIncluir todas as linhas brutas do relatório, além do resumo por SKU

Defina SEO_MCP_ASC_VENDOR_NUMBER; o App Store Connect mostra o número do fornecedor em Pagamentos e Relatórios Financeiros, ao lado do nome da entidade legal. Vendas e Tendências exige uma chave de equipe com a função Admin, Finance ou Sales and Reports. Relatórios diários chegam no dia seguinte, então a data padrão do relatório é dois dias atrás, não hoje. reportDate assume a forma que sua frequência exige: YYYY-MM-DD para DAILY e para WEEKLY, onde significa o domingo de término da semana, YYYY-MM para MONTHLY e YYYY para YEARLY; omita e cada frequência usa como padrão seu período completo mais recente.

Um período sem vendas retorna hasData: false com uma nota, não um erro, porque um dia tranquilo não deve parecer uma integração quebrada. As unidades vêm do pipeline de Vendas e Tendências, que é separado do App Analytics e pode divergir dele.

play_vitals

Lê Android vitals da API Play Developer Reporting: taxa de crash, taxa de ANR, contagens de erros e métricas de inicialização, diárias ou por hora, com detalhamentos opcionais como versionCode ou countryCode.

{ "packageName": "com.example.app", "metricSets": ["crashRate", "anrRate"], "days": 28 }
ParâmetroTipoObrigatórioPadrãoDescrição
packageNamestringsimNome do pacote Android
metricSetslista de um de crashRate, anrRate, errorCount, slowStartRate, excessiveWakeupRatenão["crashRate","anrRate"]Quais conjuntos de métricas de Android vitals consultar
aggregationPeriodum de DAILY, HOURLYnão"DAILY"DAILY é relatado em America/Los_Angeles, HOURLY em UTC
daysnumbernão28Quantos dias para trás consultar
dimensionslista de stringnão[]Dimensões de detalhamento, como versionCode ou countryCode
pageSizenumbernão1000Linhas por conjunto de métricas
includeRowsbooleannãofalseIncluir todas as linhas brutas, além das contagens; desativado por padrão para que uma chamada de resumo permaneça pequena

Defina SEO_MCP_PLAY_CREDENTIALS para a chave da conta de serviço, com fallback para GOOGLE_APPLICATION_CREDENTIALS. A conta também precisa ser convidada no Play Console em Usuários e permissões, com permissão para visualizar informações do app e qualidade do app. O token é emitido para o escopo playdeveloperreporting, que é uma concessão separada da leitura do Cloud Storage que play_store_stats precisa. Uma única conta pode ter ambos, mas uma chave que só tem a concessão do bucket recebe um 403 aqui.

A janela é limitada à atualização que a API relata para si mesma, pois ela recusa uma data final além disso e perguntar até hoje sempre falha. O resultado diz o quão atual os dados realmente estão, então zero linhas até uma data conhecida é distinguível de zero linhas porque o dia ainda não chegou. Esta API não carrega dados de aquisição ou conversão; play_store_stats tem isso.

Google Ads

Lê a conta pela API, em vez do console. Uma tabela do console pagina, então uma contagem tirada da primeira tela pode estar errada sem parecer errada: uma contagem de palavras-chave foi lida como duas quando a resposta era cinco, porque a tabela mostra dez linhas e havia catorze. Estas ferramentas retornam todas as linhas.

Defina GOOGLE_ADS_DEVELOPER_TOKEN, GOOGLE_ADS_CLIENT_ID, GOOGLE_ADS_CLIENT_SECRET, GOOGLE_ADS_REFRESH_TOKEN e GOOGLE_ADS_CUSTOMER_ID (hífens opcionais). Duas conveniências: GOOGLE_ADS_CLIENT_SECRET_PATH lê o client id e o secret do JSON do cliente OAuth que o Google Cloud fornece, e GOOGLE_ADS_ENV_FILE aponta para um arquivo existente no formato .env contendo qualquer um desses, então um refresh token que já existe em algum lugar é lido no lugar, em vez de copiado. O ambiente do processo vence sobre o arquivo. GOOGLE_ADS_API_VERSION substitui a versão da API.

ads_campaigns

Nome da campanha, status, orçamento diário, impressões, cliques, custo e conversões em uma janela.

{ "days": 30 }
ParâmetroTipoObrigatórioPadrãoDescrição
daysnumbernão30Quantos dias para trás relatar, terminando hoje

ads_keywords

Cada palavra-chave com seu estado, lance de CPC efetivo, status de aprovação, status de veiculação e métricas.

ELIGIBLE não significa veiculação. Significa aprovada e capaz de veicular, e uma palavra-chave pausada relata isso. É por isso que status é retornado junto: sem ele, a linha de uma palavra-chave pausada é idêntica à de uma ativa, e alguém que acabou de pausar três palavras-chave lê isso como se a pausa não tivesse ocorrido. Palavras-chave pausadas são nomeadas em uma nota, em vez de descartadas, porque remover linhas silenciosamente é a mesma falha um nível abaixo: você pergunta se uma palavra-chave está na conta e não recebe nada. Passe status para filtrar deliberadamente.

{ "days": 30 }
ParâmetroTipoObrigatórioPadrãoDescrição
daysnumbernão30Quantos dias para trás relatar, terminando hoje
statusum de ENABLED, PAUSED, REMOVEDnãoLimitar a um estado de palavra-chave. Omitido, toda palavra-chave é retornada com seu estado nomeado, porque descartar linhas silenciosamente é como uma contagem tirada desta ferramenta dá errado, da mesma forma que uma contagem do console

ads_ads

Cada anúncio com sua força, status de aprovação de política, status de veiculação e métricas.

{ "days": 30 }
ParâmetroTipoObrigatórioPadrãoDescrição
daysnumbernão30Quantos dias para trás relatar, terminando hoje

ads_ad_copy

Lê o que um anúncio realmente diz. ads_ads fornece o id, a força, a aprovação e o status; isto fornece o texto, que é o que toda pergunta criativa precisa e a razão pela qual essa pergunta, caso contrário, termina no navegador.

{ "adGroup": "brand-exact" }
ParâmetroTipoObrigatórioPadrãoDescrição
adGroupstringnãoLimitar a um grupo de anúncios por nome. Omitido, todo anúncio na conta é lido, o que responde se um título é repetido entre grupos de anúncios
adIdstringnãoLimitar a um anúncio pelo seu id numérico, para reler o texto que deveria ter sido publicado
includeRemovedbooleannãofalseIncluir anúncios removidos. Desativado por padrão: o texto de um anúncio removido é histórico e ocupa espaço dos anúncios que estão veiculando

Cada título e descrição retorna com seu pinning e o rótulo de desempenho do próprio Google, além do caminho de exibição, as URLs finais e os tópicos de política por trás de um status limitado ou reprovado. A palavra de aprovação diz que algo está errado; o tópico diz o quê. APPROVED_LIMITED ao lado de TRADEMARKS_IN_AD_TEXT é uma correção; APPROVED_LIMITED sozinho é uma ida ao console.

Também responde às duas perguntas que uma visão por anúncio não consegue:

  • Por que a força é Ruim. A contagem contra o que o Google quer, 3 of 15 headlines, 2 of 4 descriptions, e quantos ativos estão fixados. O pinning geralmente é deliberado, geralmente invisível na palavra de força, e uma razão comum para a força ler mais baixa do que o texto merece. Texto repetido dentro de um único anúncio também é nomeado, já que um ativo repetido ocupa um espaço sem adicionar uma variação.
  • Um título está duplicado entre anúncios. Texto de título que aparece em mais de um anúncio é listado com os anúncios e grupos de anúncios que o carregam. Dois anúncios em um grupo de anúncios que compartilham seus títulos não são duas variantes sendo testadas uma contra a outra, e nada no console diz isso de relance.

Anúncios removidos são excluídos, a menos que includeRemoved seja definido, e apenas um anúncio de pesquisa responsivo carrega texto nesses campos: qualquer outro tipo de anúncio é listado com seu tipo e sem texto, em vez de um anúncio sem nada a dizer. Ativos anexados ao anúncio, campanha ou conta, como sitelinks, promoções e preços, não são lidos aqui, então um anúncio que parece enxuto nesta saída ainda pode estar veiculando com ativos ao lado.

ads_assets

O que está anexado sob o anúncio: sitelinks, callouts, snippets estruturados, promoções, preços, ativos de chamada e imagem, nos três níveis, com o que cada um realmente diz, em vez de apenas seu tipo e id.

{ "campaign": "search-uk-us-2026-09" }
ParâmetroTipoObrigatórioPadrãoDescrição
campaignstringnãoLimitar ativos de campanha e grupo de anúncios a uma campanha por nome. Ativos de nível de conta ainda são listados, porque se aplicam a toda campanha, incluindo esta
typeum de SITELINK, CALLOUT, STRUCTURED_SNIPPET, PROMOTION, PRICE, CALL, IMAGEnãoLimitar a um tipo de ativo. Omitido, todo tipo é listado, incluindo tipos para os quais esta ferramenta não tem leitura formatada
includeRemovedbooleannãofalseIncluir links cujo status é removido. Desativado por padrão: um ativo removido é histórico e ocupa espaço dos que podem veicular

Uma promoção é lida de volta como up to 20% off on Pro plan with code LAUNCH20, 2026-01-01 to 2026-01-31, não como PROMOTION #4417. Isso importa porque o Google declara a porcentagem de uma promoção em milionésimos, onde 1.000.000 é 100%, então o campo bruto é um número que ninguém reconheceria como desconto. Preços retornam com suas ofertas e moeda, sitelinks com suas descrições.

Três coisas que vale a pena saber antes de ler um resultado:

  • Um ativo de nível de conta se aplica a todas as campanhas, portanto ele é listado mesmo quando você nomeia uma campanha específica. Esta é a outra metade de ads_ad_copy: um anúncio que parece vazio ali pode estar veiculando com quatro sitelinks e uma promoção ao lado, nenhum deles vinculado à campanha.
  • Vinculado não é exibido. O Google decide por leilão se mostra um ativo e quais deles. Isso indica o que está disponível para veicular, não o que veiculou.
  • Um nome de campanha que não corresponde a nada é recusado, não respondido. Um erro de digitação costumava retornar zero linhas sem erro, ao lado de uma nota explicando que ativos de nível de conta também são listados, então o leitor concluía que a conta não tinha nenhum. O nome é resolvido antes de qualquer leitura, e um nome desconhecido informa isso. Quem digita errado o nome de uma campanha é exatamente quem depois diz "essa campanha não tem sitelinks" e age com base nisso.
  • Um nível que não pode ser lido é reportado como erro no lugar. Os três níveis são três consultas separadas, e se uma falhar, as outras duas ainda retornam com levelErrors nomeando a que não funcionou. Uma lista vazia que silenciosamente significasse "a consulta quebrou" seria lida como "nada vinculado", que é a resposta errada para a única pergunta que esta ferramenta recebe.

Um tipo para o qual esta ferramenta não tem leitura estruturada é nomeado com seu tipo de ativo e seu tipo de campo, e fica nisso, em vez de receber um resumo inventado: um ativo TEXT arquivado como BUSINESS_NAME é descrito principalmente pela segunda metade. O tipo de campo é mostrado apenas quando difere do tipo de ativo, já que os dois costumam ser a mesma palavra e repeti-la é ruído. Métricas de ativos não são reportadas aqui.

ads_query

Uma consulta GAQL SELECT arbitrária para uma pergunta que as leituras estruturadas não cobrem. GAQL não tem outra instrução além de SELECT, então isso não pode alterar nada, e uma consulta que não comece com SELECT é recusada.

{ "query": "SELECT campaign.name, metrics.cost_micros FROM campaign WHERE segments.date DURING LAST_7_DAYS" }
ParâmetroTipoObrigatórioPadrãoDescrição
querystringsimUma instrução SELECT GAQL. GAQL não tem outra instrução, então isso não pode alterar nada

ads_search_terms

As consultas que realmente dispararam um anúncio, com a palavra-chave que cada uma correspondeu. Este é o equivalente pago da dimensão de consultas do Search Console, e carrega a mesma ressalva: o Google retém termos que poucas pessoas pesquisaram, então um termo que não está listado é desconhecido, não ausente.

{ "days": 90, "minImpressions": 1 }
ParâmetroTipoObrigatórioPadrãoDescrição
daysnumbernão30Quantos dias para trás reportar, terminando hoje
minCostnumbernão0Descarta termos de pesquisa que custaram menos que isso no período
minImpressionsnumbernão0Descarta termos de pesquisa abaixo deste número de impressões
zeroConversionsOnlybooleannãofalseMantém apenas termos que não converteram nada, que é a lista que alimenta palavras-chave negativas

ads_changes

O que mudou na conta, quando, quais campos, por quem, e se veio de uma ferramenta ou de alguém no navegador: client é GOOGLE_ADS_API para o primeiro caso e GOOGLE_ADS_WEB_CLIENT para o segundo. Este é o trilho de auditoria para qualquer coisa que ads_update escreve, e para edições feitas manualmente no console. O Google mantém 30 dias, então um período maior é recusado em vez de truncado silenciosamente.

{ "days": 14, "limit": 100 }
ParâmetroTipoObrigatórioPadrãoDescrição
daysnumbernão14Quantos dias de histórico de mudanças ler, terminando agora. O Google mantém 30 dias e recusa mais
limitnumbernão100Mudanças mais recentes a retornar

ads_negatives

As palavras-chave negativas já em vigor, no nível de campanha, grupo de anúncios ou conjunto compartilhado. Uma negativa bloqueia tráfego sem deixar registro de que o fez, então esta é a lista para verificar quando uma palavra-chave para de veicular e nada parece errado, e antes de adicionar um termo que talvez já exista.

{ "level": "all" }
ParâmetroTipoObrigatórioPadrãoDescrição
levelum de campaign, adGroup, sharedSet, allnão"all"Quais negativas ler. Um termo bloqueado no nível de campanha está bloqueado em toda ela; um conjunto compartilhado se aplica a toda campanha à qual está vinculado

ads_negatives_update

Adiciona ou remove palavras-chave negativas em lote, enumeradas uma a uma. Não há forma de padrão ou correspondência total de propósito: "bloquear todo termo que corresponda a X" está a um erro de digitação de um erro do tamanho da conta, e uma lista explícita não pode cometer esse erro.

{ "action": "add", "level": "campaign", "target": "search-uk-us", "keywords": ["free", "crack"], "matchType": "EXACT", "dryRun": false }
ParâmetroTipoObrigatórioPadrãoDescrição
actionum de add, removesimAdicionar palavras-chave negativas ou remover as existentes. A remoção importa tanto quanto a adição: uma negativa errada aparece como nada
levelum de campaign, adGroupnão"campaign"Onde as negativas vivem. Uma negativa de nível de campanha bloqueia o termo em toda a campanha
targetstringsimO nome da campanha ou grupo de anúncios. Deve corresponder exatamente a um ou nada é alterado
keywordslista de stringsimOs termos negativos, enumerados um a um. Não há forma de padrão ou correspondência total: um seletor está a um erro de digitação de bloquear uma campanha inteira
matchTypeum de BROAD, PHRASE, EXACTnão"EXACT"Como cada termo bloqueia. BROAD bloqueia qualquer consulta que contenha todas as suas palavras, que é a configuração que pode matar uma campanha silenciosamente
dryRunbooleannãotrueReporta o que mudaria, e quais negativas propostas bloqueariam uma palavra-chave ativa, sem alterar nada
confirmbooleannãofalseExecuta o lote mesmo que uma proteção tenha disparado. A execução de teste lista o que disparou, então isso confirma algo já lido

Negativas parecem seguras porque só reduzem gastos, e esse instinto é o que as torna perigosas. Um lance errado aparece como gasto. Uma negativa errada aparece como nada: o tráfego para de chegar, o termo sai do relatório de termos de pesquisa, e nenhuma linha em lugar algum diz o porquê. Adicionar backup como negativa ampla a uma campanha de plugin de backup encerra seu tráfego, e o Google não reporta erro porque é uma negativa perfeitamente válida.

Então, antes de adicionar qualquer coisa, cada negativa proposta é verificada contra as palavras-chave ativas da própria campanha, e o lote é recusado a menos que confirm esteja definido. A recusa nomeia o que teria custado: "backup" as a BROAD negative would block this campaign's own keyword "wordpress backup", which served 41 impressions. A verificação espelha de perto a correspondência do Google, mas o Google é a autoridade, e ela é deliberadamente generosa, porque um aviso falso custa uma frase e um perdido custa a campanha.

Remoções não passam por verificação de colisão. Remover uma negativa só pode deixar tráfego passar, o que aparece como gasto em vez de silêncio.

ads_update

Altera um lance de palavra-chave, orçamento diário de campanha, status de campanha, status de anúncio ou status de palavra-chave. Esta é a única ferramenta aqui que gasta dinheiro, então é construída para ser difícil de disparar por acidente.

Pausar uma palavra-chave é um tipo próprio, porque reduzir seu lance não é a mesma coisa. Uma palavra-chave com lance reduzido ainda está ativada, ainda elegível, e ainda competindo pelo mesmo orçamento diário. Se o motivo para agir era que o orçamento é a restrição, reduzir o lance não libera nada dele.

{ "kind": "budget", "target": "search-uk-us-2026-09", "value": "5.00", "dryRun": false, "confirm": true }
ParâmetroTipoObrigatórioPadrãoDescrição
kindum de bid, budget, campaignStatus, adStatus, keywordStatussimO que alterar: o lance de CPC máximo de uma palavra-chave, o orçamento diário de uma campanha, o status de uma campanha, o status de um anúncio ou o status de uma palavra-chave. Use keywordStatus para parar uma palavra-chave de veicular; reduzir seu lance não é a mesma coisa, porque a palavra-chave continua elegível e competindo pelo mesmo orçamento
targetstringsimO texto da palavra-chave, o nome da campanha ou o ID numérico do anúncio. Deve corresponder exatamente a uma coisa ou a chamada é recusada
valuestringsimO novo valor em dólares para um lance ou orçamento, ou pause ou enable para um status
dryRunbooleannãotrueReporta o que mudaria e quais proteções dispara, sem alterar nada. Ativado por padrão: esta ferramenta gasta dinheiro, então executar uma alteração precisa ser solicitado
confirmbooleannãofalseExecuta uma alteração que dispara uma proteção. Ignorado em execução de teste. A execução de teste lista os motivos das proteções, então isso confirma algo já lido em vez de algo não visto

Quatro trilhos, cada um de uma falha real em vez de hipotética:

  • Execução de teste por padrão. dryRun é verdadeiro por padrão, então omiti-lo reporta a alteração e para. Um parâmetro obrigatório impõe isso melhor que uma flag de linha de comando, porque uma flag pode ser esquecida e um padrão não.
  • Exatamente uma correspondência ou recusa. Um alvo que não corresponde a nada é um erro de digitação; um alvo que corresponde a dois é um pedido para alterar algo que você não nomeou. Ambos param antes de qualquer escrita.
  • Proteções com motivos, em palavras. Mais de três vezes o valor atual, mais de $25 em um único lance ou orçamento diário, ou pausar algo que está atualmente veiculando. Uma alteração de orçamento também declara o equivalente mensal, porque $30 por dia parece pouco e é cerca de $912 por mês. A execução de teste lista os motivos, e confirm então confirma algo que você leu em vez de algo não visto.
  • O valor é relido após a escrita. Um HTTP 200 significa que a solicitação foi aceita, não que armazenou o que você quis dizer. O resultado carrega readBack e matches, e uma discrepância é retornada como erro.

Uma alteração que seria um no-op informa isso em vez de enviar uma mutação sem sentido.

Da linha de comando, esta ferramenta precisa de --allow-spend além de --allow-write. Uma flag autorizando tanto "reenviar um sitemap" quanto "triplicar um orçamento diário" não é um portão.

ads_keyword_create

Adiciona uma palavra-chave a um grupo de anúncios. A única ferramenta aqui que cria em vez de alterar, e é protegida de forma diferente por esse motivo.

{ "keyword": "wordpress backup plugin", "adGroup": "brand-exact", "bid": 1.2, "dryRun": false }
ParâmetroTipoObrigatórioPadrãoDescrição
keywordstringsimO texto da palavra-chave a adicionar. Ela é criada como escrita; esta ferramenta não adivinha variantes
adGroupstringsimO grupo de anúncios ao qual adicionar. Deve corresponder exatamente a um ou nada é adicionado
bidnumbersimO lance de CPC máximo em dólares. Não há lance atual para comparar em uma criação, então a única verificação de tamanho é o teto
matchTypeum de EXACT, PHRASE, BROADnão"EXACT"Como a palavra-chave corresponde. EXACT por padrão porque é a que compra o que diz; PHRASE e BROAD compram mais que o texto escrito aqui e cada uma dispara uma proteção
dryRunbooleannãotrueReporta o que seria adicionado e quais proteções dispara, sem adicionar nada
confirmbooleannãofalseAdiciona mesmo que uma proteção tenha disparado. A execução de teste lista cada motivo, então isso confirma algo já lido

Toda outra escrita neste pacote lê um valor atual, compara com o solicitado e recusa quando já correspondem. Uma criação não tem valor atual. Não há nada para comparar e nada contra o que recusar, então a comparação precisa ser substituída em vez de pulada, e o que a substitui é uma verificação de duplicidade:

  • Uma palavra-chave que já existe no grupo de anúncios de destino é recusada, inclusive uma que foi removida. Um critério removido ainda mantém o texto, e o Google rejeita a duplicata com um erro que nomeia um recurso que a interface não mostra, o que é algo confuso de encontrar sem aviso.
  • Uma cópia em outro lugar da conta dispara uma proteção em vez de recusar. Executar o mesmo texto em dois grupos de anúncios pode ser intencional, então recusar tornaria uma estrutura legítima impossível; não dizer nada deixaria duas cópias competirem por um orçamento silenciosamente.
  • EXACT por padrão. PHRASE e BROAD cada uma compra mais do que o texto escrito aqui, então cada uma dispara uma proteção. Ampla é o tipo de correspondência que gasta em buscas que ninguém pretendia comprar.
  • A palavra-chave é lida de volta após a gravação, e seu tipo de correspondência e status são comparados com o que foi enviado. Um 200 em uma criação significa aceito, não presente-e-correto.

Uma assimetria que vale a pena afirmar claramente: uma palavra-chave criada começa a ser veiculada imediatamente, e ao contrário de uma alteração de lance, não há estado anterior para retornar. Desfazê-la significa pausar ou remover o que foi feito.

ads_update_batch

Altera vários lances de palavras-chave, ou vários orçamentos diários de campanhas, em uma única chamada. Um tipo por chamada: um total entre lances e orçamentos adicionaria um teto por clique a um valor por dia, e nenhuma frase honesta descreve essa soma.

{ "kind": "bid", "changes": [{ "target": "wordpress backup", "value": 0.85 }, { "target": "backup plugin", "value": 0.6 }], "dryRun": false, "confirm": true }
ParâmetroTipoObrigatórioPadrãoDescrição
kindum de lance, orçamentosimUm tipo por chamada. Uma proteção somada só é honesta dentro de um tipo: lances e orçamentos somam em dólares, status não somam, e misturá-los torna o total ilegível
changeslista JSONsimUma lista nomeada de pares, cada um com seu próprio valor. Não há forma de seletor: a enumeração não pode cometer o erro que um padrão pode
dryRunbooleanonãotrueResolver e precificar cada entrada e relatar o total, sem alterar nada
confirmbooleanonãofalseExecutar o lote mesmo que uma proteção tenha disparado. A execução de teste lista todos os motivos, então isso confirma algo já lido

É uma lista nomeada de pares, não uma regra aplicada a muitas coisas. Não há "aumentar tudo em 20%" e não há seletor, porque o erro que esta ferramenta existe para prevenir é exatamente aquele que um seletor torna fácil: um padrão que corresponde a mais do que o chamador imaginou, aplicado antes que alguém possa ver a lista que produziu. Cada entrada nomeia um alvo e o valor em que deve terminar, e a execução de teste imprime essa lista de volta.

Quatro coisas que ela faz que ads_update chamada em um loop não faz:

  • Tudo é resolvido antes que qualquer coisa seja gravada. Se a entrada quatro não corresponde a nada, as entradas um a três não estão já ativas. Um loop de chamadas únicas falha no meio e deixa a conta em um estado que ninguém escolheu, sem nenhuma linha única em lugar algum dizendo isso.
  • A soma é protegida, não apenas cada entrada. Cinco aumentos que estão cada um dentro dos tetos por item ainda são uma grande mudança de gasto juntos, e fazê-los um de cada vez é como isso passa despercebido.
  • A entrada que está fora de linha com as demais é nomeada. Dezenove lances movendo alguns centavos e um movendo $40 podem ficar sob todos os tetos e ainda ser o erro. Uma entrada cujo movimento é muito maior que a mediana do lote é sinalizada pelo nome, porque um erro de digitação se esconde dentro de um total aceitável, e é exatamente assim que um lote difere das mesmas gravações enviadas uma de cada vez.
  • Duas entradas não podem nomear a mesma coisa. O mesmo alvo duas vezes é recusado, e também são recusadas duas campanhas com nomes diferentes que compartilham um orçamento, onde o total contaria duas vezes e a segunda gravação venceria silenciosamente.

Os tetos por entrada permanecem fixos por mais longa que seja a lista, porque a questão por entrada é se aquela entrada é um erro de digitação, e um erro de digitação não se torna mais aceitável em um lote maior. O teto no total do lote cresce com o lote, lentamente: vinte entradas não são vinte vezes o risco de uma, é uma decisão tomada uma vez. Uma proteção que dispara em todo lote realista não é uma proteção, é uma caixa de seleção, e uma vez que confirm é rotineiro, ela passa sem ser lida.

O total é sempre declarado em palavras, quer algo tenha disparado ou não, já que a frase é o que é lido e a proteção é apenas o que impede você quando não dispara. Para orçamentos, esse é o valor mensal nos dois 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 é lido de volta após a gravação, entrada por entrada. O resultado nomeia quais entradas não armazenaram o que foi enviado primeiro, depois quais estão ativas com o que a conta agora contém, porque em um pouso parcial a questão nunca é quantos, mas quais. Uma solicitação aceita é uma aceitação, não N valores armazenados, e um lote é exatamente onde um pouso parcial se esconde. O lote é enviado como uma única solicitação sem falha parcial, então uma gravação rejeitada não deixa nada para trás, e o erro diz isso em vez de deixar você adivinhar.

Como ads_update, ele precisa de --allow-spend além de --allow-write da linha de comando.

Executando uma ferramenta a partir da linha de comando

Toda ferramenta acima também é executável sem um cliente MCP, que é o que usar quando um resultado precisa cair em um arquivo que uma execução posterior possa 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 execução nomeia sua própria versão no stderr (seo-console-mcp 0.15.1 running wporg_plugin), então o stdout permanece analisável e um arquivo --out permanece JSON puro. Um resultado não diz de outra forma qual binário o produziu, e isso não é acadêmico: npx reutilizará um build mais antigo em cache sem nenhum erro, e uma instalação falha deixa a versão anterior no lugar e funcionando. O que está rodando, o que o intervalo de versões resolve e o que o npm chama de mais recente são três valores que geralmente concordam e independentemente não precisam.

As flags são os nomes dos parâmetros da ferramenta em kebab-case (--site-url para siteUrl); a grafia camelCase também funciona. Valores de lista são separados por vírgula. O resultado é gravado em --out, ou no stdout quando omitido, e uma falha sai com código não zero com a mensagem no stderr. Ele executa a mesma implementação que a superfície MCP expõe, então os dois não podem divergir.

Ferramentas que alteram dados (submit_sitemap, delete_sitemap, request_recrawl, indexnow_submit) são marcadas como (write) na listagem e recusam-se a executar a partir da linha de comando a menos que --allow-write seja passado.

Um histórico está a uma linha de cron de distância, e o servidor deliberadamente não possui um agendador: sua máquina já tem um que sobrevive a uma reinicialização.

# 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

Desenvolvimento

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

Os testes usam clientes falsos do Google injetados e nunca chamam serviços ao vivo do Google. Não faça commit de chaves de conta de serviço. Além de *.key.json, este repositório ignora credentials*.json, .env*, arquivos PEM e arquivos P12.