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.
209 lines
10 KiB
Markdown
209 lines
10 KiB
Markdown
---
|
|
name: seo-audit
|
|
description: Auditoria SEO completa com dados reais via OpenSEO (crawl, Search Console, SERP, backlinks, keywords). Analisa SEO técnico, conteúdo, autoridade e desempenho, e prioriza por retorno. Usar quando "auditoria SEO", "audit", "analisar site", "Core Web Vitals", "Search Console", "striking distance", "porque não tenho tráfego".
|
|
---
|
|
|
|
# SEO Audit — Auditoria Completa (OpenSEO)
|
|
|
|
Auditoria SEO com dados reais. **Motor único: OpenSEO** (`seo.descomplicar.pt`), que substituiu o stack antigo (SEO Tools API + Ahrefs + GSC MCP + Lighthouse MCP) — ver `references/ferramentas-api.md` para a tabela de migração.
|
|
|
|
---
|
|
|
|
## Regra zero — a ordem é económica, não estética
|
|
|
|
O OpenSEO tem ferramentas **grátis** e ferramentas **que gastam créditos**. Executar pela ordem errada gasta dinheiro a produzir conclusões que as ferramentas grátis já davam.
|
|
|
|
| Fase | Ferramentas | Custo | Porquê nesta ordem |
|
|
|---|---|---|---|
|
|
| 1 | `get_search_console_performance`, `inspect_urls` | **grátis** | Dados de primeira mão. Diz o que já posiciona e quais páginas importam |
|
|
| 2 | `run_site_audit`, `get_audit_issues`, `get_audit_pages` | **grátis** | Estado técnico. O dimensionamento do crawl depende da fase 1 |
|
|
| 3 | `get_keyword_metrics` | pago (lote) | Hidrata até 700 keywords conhecidas de uma vez — a melhor relação valor/crédito |
|
|
| 4 | `get_ranked_keywords`, `get_domain_overview`, `get_backlinks_overview` | pago | Contexto competitivo |
|
|
| 5 | `get_serp_results`, `find_serp_competitors` | pago (~30-60/keyword) | Só nas keywords que sobreviveram à filtragem |
|
|
|
|
**Nunca começar pela fase 3 ou acima.** As fases 1-2 resolvem a maioria das auditorias sem gastar um crédito.
|
|
|
|
---
|
|
|
|
## Pré-voo obrigatório (3 verificações, 30 segundos)
|
|
|
|
```
|
|
1. openseo → whoami → confirma ligação, org, modo
|
|
2. openseo → list_projects → obtém projectId E o mercado do projecto
|
|
3. confirmar mercado → PT = locationCode 2620, languageCode "pt"
|
|
```
|
|
|
|
**GATE — mercado.** Se o projecto estiver em `2840/en` (EUA/inglês, que é o default) e o cliente for português, **todas** as chamadas pagas devolvem dados do mercado errado. Ou se corrige o mercado do projecto, ou se passa `locationCode`/`languageCode` explicitamente em **cada** chamada. Um esquecimento = créditos gastos em lixo.
|
|
|
|
> `locationCode: 2620` = Portugal — confirmado empiricamente (SERP devolve domínios `.pt`). Não inventar códigos: se houver dúvida, correr uma query de teste e validar pelos resultados.
|
|
|
|
---
|
|
|
|
## Fase 1 — Search Console primeiro (grátis, é o mapa)
|
|
|
|
```
|
|
openseo → get_search_console_performance
|
|
dimensions: ["query"] dateRange: "last_3_months" rowLimit: 1000
|
|
openseo → get_search_console_performance
|
|
dimensions: ["page"] dateRange: "last_3_months" rowLimit: 1000
|
|
```
|
|
|
|
Paginar com `startRow` enquanto o cabeçalho disser `more available`.
|
|
|
|
### As três armadilhas do GSC
|
|
|
|
**1. Vista por query ≠ vista por página.** O GSC anonimiza queries raras, por isso os totais por query são sempre **muito menores** que os totais por página. A vista por **página** é a real; usar essa para números globais.
|
|
|
|
**2. O GSC não filtra por posição.** `striking distance` tem de ser filtrado do lado do cliente, depois de puxar as linhas.
|
|
|
|
**3. Há lixo nas queries.** Colagens de relatórios do Google Ads, operadores `site:`, strings com mojibake. Filtrar antes de contar, ou as métricas mentem.
|
|
|
|
```python
|
|
def is_noise(k):
|
|
return ('ativado' in k and 'correspond' in k) or k.startswith('-site:') \
|
|
or k.startswith('=') or len(k) > 90 or '0,00' in k
|
|
```
|
|
|
|
### O que extrair
|
|
|
|
| Métrica | Cálculo | Leitura |
|
|
|---|---|---|
|
|
| CTR global | cliques ÷ impressões (vista por página) | < 1% = problema de captação, não de ranking |
|
|
| Distribuição por posição | buckets 1-3 / 4-10 / 11-20 / 21-50 / 51+ | Impressões concentradas em 21+ = visibilidade inútil |
|
|
| Branded vs não-branded | queries com o nome da marca vs resto | Se só a marca converte, o SEO não está a trabalhar |
|
|
| **Striking distance** | pos 4-20 **e** impressões ≥ 25 | **É aqui que está o retorno** |
|
|
| Desperdício | pos > 40 **e** impressões ≥ 300 | Páginas com procura real enterradas |
|
|
|
|
### O sinal mais accionável de todos
|
|
|
|
**Posição 4-10 com CTR perto de 0%.** Não é problema de ranking — o site está na primeira página. É o **título e a meta description** a não convencerem. Cruzar esta lista com os issues `missing-meta-description` e `title-too-long` da fase 2: a intersecção é a lista de trabalho, por ordem.
|
|
|
|
---
|
|
|
|
## Fase 2 — Auditoria técnica (grátis)
|
|
|
|
```
|
|
openseo → run_site_audit
|
|
url: "<url>" maxPages: <N> runLighthouse: true
|
|
openseo → get_audit_status (poll até "completed")
|
|
openseo → get_audit_issues (severity: critical → warning → info)
|
|
```
|
|
|
|
### GATE — `maxPages` (o erro que cega a auditoria)
|
|
|
|
**`maxPages` tem default 50.** Um site de 600 páginas auditado com o default devolve um relatório limpo e falso: descreve 8% do site e omite exactamente o hub de conteúdo que gera o tráfego.
|
|
|
|
**Regra:** `maxPages` ≥ número de páginas com impressões no GSC (fase 1), com folga de ~25%. Por isso é que a fase 1 vem primeiro — é ela que dimensiona o crawl.
|
|
|
|
> Caso real (descomplicar.pt, 07-2026): auditoria com default 50 → 25 URLs únicos, 92 issues, **zero críticos**, nenhuma das 119 páginas `/guia-*`. Relançada com `maxPages: 600` → **1337 issues, 9 críticos**. O relatório "limpo" descrevia 4% do site.
|
|
|
|
### Prioridade dos issues
|
|
|
|
| Severidade | Tipos | Acção |
|
|
|---|---|---|
|
|
| **critical** | `broken-internal-link`, `blocked-page`, `server-error`, `broken-page` | Corrigir já — sangram autoridade e crawl budget |
|
|
| **warning** | `missing-h1`, `missing-meta-description`, `duplicate-content`, `multiple-h1`, `duplicate-title` | Priorizar pelas páginas com impressões (fase 1) |
|
|
| **info** | `title-too-long`, `noindex-page`, `slow-response`, `heading-order-skip` | Lote; só vale a pena onde há procura |
|
|
|
|
**Nunca tratar a lista de issues por ordem de contagem.** 300 metas em falta em páginas sem impressões valem menos que 6 em páginas na posição 8. A fase 1 é que ordena a fase 2.
|
|
|
|
### Limitação conhecida — issues sem URL
|
|
|
|
`get_audit_issues` e `get_audit_pages` devolvem **apenas contagens agregadas** através da bridge MCP. As linhas por URL vivem em `structuredContent.issues`, que a bridge não entrega; `get_audit_pages` trunca a listagem em ~25 linhas independentemente do `limit`.
|
|
|
|
**Contorno:** o campo `details.mcpMeta.url` da resposta traz o link do relatório na UI. Entregar esse link ao humano para os URLs concretos:
|
|
```
|
|
https://seo.descomplicar.pt/p/<projectId>/audit?auditId=<auditId>
|
|
```
|
|
Declarar esta limitação no relatório — não fingir que se verificaram URLs que não se viram.
|
|
|
|
### Core Web Vitals por página
|
|
|
|
`run_site_audit` com `runLighthouse: true` corre Lighthouse numa amostra (até 20 páginas). Para uma página específica, usar `chrome-devtools`:
|
|
|
|
```
|
|
chrome-devtools → lighthouse_audit (SEO, acessibilidade, best practices)
|
|
chrome-devtools → performance_start_trace (LCP, INP, CLS reais)
|
|
```
|
|
|
|
**Thresholds 2026:** LCP < 2,5s · **INP** < 200ms · CLS < 0,1
|
|
(INP substituiu o FID — se algum documento ainda disser FID, está desactualizado.)
|
|
|
|
---
|
|
|
|
## Fase 3 — Hidratar keywords (pago, lote)
|
|
|
|
```
|
|
openseo → get_keyword_metrics
|
|
keywords: [<as keywords em striking distance da fase 1>]
|
|
locationCode: 2620 languageCode: "pt"
|
|
```
|
|
|
|
Até **700 keywords numa só chamada** — volume, dificuldade (KD), intenção, CPC, tendência. É a forma barata de priorizar. Só depois disto se decide onde vale a pena competir.
|
|
|
|
Guardar o que sobreviver: `openseo → save_keywords` (grátis, idempotente).
|
|
|
|
---
|
|
|
|
## Fase 4 — Contexto competitivo (pago)
|
|
|
|
```
|
|
openseo → get_domain_overview (tráfego orgânico estimado, nº keywords, backlinks)
|
|
openseo → get_ranked_keywords (onde o domínio posiciona, por mercado)
|
|
openseo → get_backlinks_overview (~50 créditos por domínio)
|
|
openseo → get_backlinks_profile (linhas detalhadas: anchors, dofollow, spam)
|
|
```
|
|
|
|
---
|
|
|
|
## Fase 5 — SERP (pago, por último)
|
|
|
|
```
|
|
openseo → get_serp_results (1-10 keywords por chamada, ~30-60 créditos cada)
|
|
openseo → find_serp_competitors (quem compete num conjunto de keywords)
|
|
```
|
|
|
|
Ler a SERP como **estrutura**, não como lista: se o top-10 estiver cheio de directórios e artigos "melhores X", a intenção é comparativa e uma homepage não entra — é preciso uma página de comparação. Se estiver cheio de homepages de concorrentes, é disputa de marca.
|
|
|
|
Local/Maps: `get_local_serp_results`, `search_local_businesses`, `get_google_business_questions`.
|
|
|
|
---
|
|
|
|
## Custos — o que sabemos e o que não sabemos
|
|
|
|
`whoami` **não expõe saldo de créditos** em modo self-hosted (verificado antes e depois de uma chamada paga: output idêntico). Os `~30-60 créditos/keyword` são a **estimativa do schema**, não uma medição.
|
|
|
|
**Consequência prática:** não há medidor no OpenSEO. O contador real vive na conta DataForSEO. Para lotes grandes, confirmar saldo aí primeiro. Nunca reportar custo consumido como facto — reportar como estimativa.
|
|
|
|
---
|
|
|
|
## Anti-patterns
|
|
|
|
- **Correr `run_site_audit` com o `maxPages` por omissão.** Produz relatórios limpos e falsos.
|
|
- **Começar por ferramentas pagas.** O GSC é grátis e responde a metade das perguntas.
|
|
- Usar totais por query como totais do site (o GSC anonimiza — usar a vista por página).
|
|
- Priorizar issues por contagem em vez de por impressões da página afectada.
|
|
- Correr chamadas pagas sem confirmar o mercado do projecto.
|
|
- Apresentar contagens de issues como se fossem URLs verificados.
|
|
- Auditar sem dados reais ou simular métricas.
|
|
- Recomendações sem prioridade (crítico/importante/melhoria) e sem esforço estimado.
|
|
|
|
---
|
|
|
|
## References
|
|
|
|
| Ficheiro | Conteúdo |
|
|
|---|---|
|
|
| `references/ferramentas-api.md` | Inventário OpenSEO completo + tabela de migração do stack antigo |
|
|
| `references/template-relatorio-auditoria.md` | Template do relatório com todas as secções e tabelas |
|
|
|
|
---
|
|
|
|
**Versão:** 3.0.0 | **Autor:** Descomplicar® | **Motor:** OpenSEO
|
|
|
|
## Healing Log
|
|
<!-- Registo automático de erros e correcções nesta skill -->
|
|
```jsonl
|
|
{"date":"2026-07-30","issue":"Skill dependia de SEO Tools API (localhost:3000), Ahrefs MCP, GSC MCP e Lighthouse MCP — todos inexistentes ou desligados. Nenhum dos 6 passos era executável.","fix":"Reescrita completa sobre OpenSEO, com ordem por custo, gates de mercado e maxPages.","source":"auto"}
|
|
```
|