Files
claude-plugins/marketing/skills/seo-report/references/implementacao-tecnica.md
T
ealmeida 2d05873642 marketing: migra superfície SEO para OpenSEO (motor único)
As skills de SEO assentavam em quatro dependências, nenhuma operacional:
SEO Tools API (localhost:3000, pasta inexistente em disco), Ahrefs MCP
(nunca esteve em config), gsc e lighthouse (em disabledServers). Os seis
passos da /seo-audit e o workflow inteiro da /seo-report apontavam para
tooling morto — não eram executáveis.

- seo-audit v3.0: ordem por custo (grátis → lote pago → unitário pago),
  gate de mercado (PT = 2620/pt) e gate de maxPages
- seo-report v3.0: cobertura de crawl obrigatória no entregável
- ferramentas-api.md: inventário das 24 ferramentas OpenSEO + tabela de
  migração endpoint a endpoint
- implementacao-tecnica.md: striking distance, desperdício e filtro de
  ruído do GSC em código
- seo-specialist v3.0: primary_mcps [openseo], bloco de MCPs desligados,
  FID → INP nos Core Web Vitals

maxPages tem default 50: descomplicar.pt auditado com o default deu 25
URLs e 92 issues sem críticos; com maxPages 600 deu 1337 issues e 9
críticos. Documentado como gate — a fase Search Console dimensiona o crawl.

Limitações declaradas nas skills: get_audit_issues devolve contagens sem
URLs pela bridge MCP (structuredContent.issues não é entregue) e o whoami
não expõe saldo de créditos em self-hosted.
2026-07-30 22:39:40 +01:00

5.5 KiB

Implementação Técnica — SEO Report (OpenSEO)

Workflow

graph TD
    A[Input: URL] --> B[whoami + list_projects]
    B --> C{Mercado correcto?}
    C -->|Não| C2[Passar locationCode 2620 + languageCode pt]
    C -->|Sim| D[GSC: dimensions query]
    C2 --> D

    D --> E[GSC: dimensions page]
    E --> F[Dimensionar crawl:<br/>maxPages = páginas GSC + 25%]

    F --> G[run_site_audit]
    G --> H[poll get_audit_status]
    H -->|running| H
    H -->|completed| I[get_audit_issues por severidade]

    I --> J[Cruzar: páginas com procura<br/>E com defeito]
    J --> K{Fases pagas contratadas?}

    K -->|Sim| L[get_keyword_metrics<br/>get_backlinks_overview<br/>get_serp_results]
    K -->|Não| M[Saltar fases pagas]

    L --> N[Processar]
    M --> N
    N --> O[Markdown] --> P[Google Doc] --> Q[Partilhar]

O ponto crítico é o nó F. Dimensionar o crawl a partir do GSC é o que distingue um relatório verdadeiro de um relatório limpo e falso.


Sequência de chamadas

// 1. Contexto e mercado
const me   = await openseo.whoami();
const projs = await openseo.list_projects();
const projectId = projs[0].id;
const MARKET = { locationCode: 2620, languageCode: 'pt' };  // Portugal

// 2. Search Console — paginar até esgotar (grátis)
async function gscAll(dimensions) {
  const rows = [];
  let startRow = 0;
  while (true) {
    const r = await openseo.get_search_console_performance({
      projectId, dimensions, dateRange: 'last_3_months',
      rowLimit: 1000, startRow
    });
    const parsed = parseRows(r.text);
    rows.push(...parsed);
    if (!r.text.split('\n')[0].includes('more available') || !parsed.length) break;
    startRow += 1000;
  }
  return rows;
}

const byQuery = await gscAll(['query']);
const byPage  = await gscAll(['page']);

// 3. Dimensionar o crawl a partir da realidade, não de um default
const maxPages = Math.ceil(byPage.length * 1.25);

// 4. Crawl (grátis) — assíncrono, exige poll
const audit = await openseo.run_site_audit({
  projectId, url, maxPages, runLighthouse: true
});
let status;
do {
  await sleep(30_000);
  status = await openseo.get_audit_status({ projectId, auditId: audit.id });
} while (!status.text.includes('completed'));

// 5. Issues por severidade (grátis)
const critical = await openseo.get_audit_issues({ projectId, auditId: audit.id, severity: 'critical' });
const warning  = await openseo.get_audit_issues({ projectId, auditId: audit.id, severity: 'warning'  });

// 6. Fases pagas — so se contratadas
if (options.paid) {
  const kw = await openseo.get_keyword_metrics({
    projectId, keywords: strikingDistance(byQuery).map(r => r.key), ...MARKET
  });
  const bl = await openseo.get_backlinks_overview({ projectId, target: domain });
}

Processamento — as funções que importam

// GSC anonimiza queries: totais por query < totais por página.
// Para números globais usar SEMPRE a vista por página.
const globals = {
  clicks:      sum(byPage, 'clicks'),
  impressions: sum(byPage, 'impressions'),
  ctr:         sum(byPage, 'clicks') / sum(byPage, 'impressions')
};

// O GSC não filtra por posição — filtrar do lado do cliente.
const strikingDistance = rows => rows
  .filter(r => r.position >= 4 && r.position <= 20 && r.impressions >= 25)
  .sort((a, b) => b.impressions - a.impressions);

// Páginas com procura real enterradas fora das primeiras 4 páginas.
const waste = rows => rows
  .filter(r => r.position > 40 && r.impressions >= 300)
  .sort((a, b) => b.impressions - a.impressions);

// O sinal mais accionável: primeira página, CTR nulo.
// Não é ranking — é título e meta description.
const titleProblem = rows => rows
  .filter(r => r.position <= 10 && r.impressions >= 200 && r.ctr < 0.005);

// Lixo nas queries do GSC (colagens do Google Ads, operadores site:).
const isNoise = k =>
  (k.includes('ativado') && k.includes('correspond')) ||
  k.startsWith('-site:') || k.startsWith('=') ||
  k.length > 90 || k.includes('0,00');

Entrega

const doc = await googleWorkspace.docs_create_document({
  title: `Relatório SEO — ${domain} — ${today}`,
  user_google_email: email
});
await googleWorkspace.docs_append_text({ document_id: doc.id, text: markdown });

Notas técnicas

Requisitos

  • OpenSEO acessível (whoami responde)
  • Propriedade ligada ao Search Console
  • google-workspace MCP para o documento

Tempos reais (medidos, descomplicar.pt, 07-2026)

Fase Duração
GSC (2 dimensões, ~1700+474 linhas) ~10s
Crawl 600 páginas + Lighthouse 20 ~4 min
get_audit_issues instantâneo

Limitações

  • Issues sem URL: get_audit_issues / get_audit_pages devolvem só contagens pela bridge MCP; structuredContent.issues não é entregue e get_audit_pages trunca em ~25 linhas. Usar details.mcpMeta.url para o link da UI.
  • Sem medidor de créditos: whoami não muda depois de chamadas pagas. Custos são estimativas.
  • Lag do GSC: últimos ~3 dias incompletos; datas em Pacific Time; máx. 16 meses.

Erros comuns

Erro Sintoma Solução
maxPages por omissão (50) Poucos issues, relatório "limpo" Redimensionar pelo GSC, relançar
Site fora do GSC Fase GSC vazia Gerar sem GSC, aviso destacado
Mercado errado Keywords em inglês/EUA locationCode: 2620, languageCode: "pt"
Cancelar o crawl cedo demais Sem auditId utilizável Fazer poll; 600 páginas ≈ 4 min

Actualizado: 30-07-2026 | Substitui a implementação baseada em Lighthouse MCP + SEO Tools API + Ahrefs