Pular para o conteúdo
mecita
Para desenvolvedores e agências

O diagnóstico do Mecita, visto por quem mexe no código

Recebeu um resultado do Mecita ou um Plano de Correção de um cliente? Esta página mostra exatamente como o diagnóstico lê um site, como reproduzir cada checagem no seu terminal, o formato da API JSON e trechos prontos para as correções mais comuns. Nada aqui depende de confiar na gente: tudo pode ser conferido com curl.

Como o diagnóstico lê um site

Um GET simples, sem navegador e sem JavaScript: o que o servidor entrega é o que conta.

O diagnóstico gratuito faz uma requisição HTTP por arquivo, segue redirecionamentos e desiste depois de 12 segundos. A descompressão (gzip, deflate, brotli) fica com o cliente HTTP, como em qualquer robô. Todas as requisições saem com este user agent:

User-Agent
Mozilla/5.0 (compatible; MecitaBot/0.1; +https://mecita.com.br/bot)

Em cada diagnóstico, lemos quatro coisas, e só elas:

  1. A página inicial, em HTTPS. Dela saem o texto, os dados estruturados, o título, a descrição, os subtítulos e o tempo de resposta.
  2. O /robots.txt, lido em paralelo com a página.
  3. O /llms.txt e, se ele faltar, o /llms-full.txt.
  4. O sitemap: primeiro os declarados no robots.txt, depois o /sitemap.xml.

Sem DOM, sem JavaScript

O HTML é inspecionado com expressões regulares sobre os bytes recebidos, de propósito. Um parser de DOM ou um navegador headless corrige marcação quebrada e executa scripts, e com isso esconde justamente o defeito que tira um site das respostas: texto que só existe depois do JavaScript. A maioria dos robôs de IA não executa JavaScript; se o conteúdo depende dele, o robô lê uma casca vazia. O glossário explica a diferença entre renderização no navegador e no servidor.

Quando o site recusa o acesso

Se a página inicial responde 401, 403, 406, 418 ou 429 para um cliente que não é navegador, isso já é o resultado: a nota vai a zero, com uma única entrada blocked. A mesma regra de firewall que barra o MecitaBot costuma barrar o GPTBot e o PerplexityBot. Se o site não responde, o resultado traz o campo error e nenhuma nota inventada. Mais sobre o nosso robô em MecitaBot.

robots.txt pela RFC 9309

Verificamos 15 user agents. Os essenciais são GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, PerplexityBot, Googlebot.

O parser segue a norma do robots.txt, a RFC 9309, e a decisão é tomada para o caminho /, a página inicial. As regras que mais confundem, na prática:

  • Linhas User-agent consecutivas formam um único grupo.
  • Um robô obedece só ao grupo que tem o nome dele. O grupo * vale apenas para quem não tem grupo próprio.
  • Dentro do grupo, vence o padrão mais longo que casa com o caminho; no empate, Allow ganha. * e $ são suportados.
  • Disallow: vazio não restringe nada.
  • O nome do robô é comparado sem diferenciar maiúsculas de minúsculas.
robots.txt · dois exemplos
# Exemplo: o GPTBot segue só o grupo dele e ignora o grupo *
User-agent: *
Disallow: /

User-agent: GPTBot
Allow: /

# Resultado: GPTBot liberado; ClaudeBot, PerplexityBot e todos os outros bloqueados.

# Exemplo: no mesmo grupo, vence o padrão mais longo; no empate, Allow
User-agent: *
Disallow: /
Allow: /$

# Resultado para a página inicial (/): liberada, porque "/$" é mais longo que "/".

O arquivo tem três destinos possíveis. Com 404 ou 410, nada está bloqueado e a checagem leva os 20 pontos. Legível (status 200, não HTML, com ao menos uma linha User-agent), as regras são aplicadas a cada robô. Qualquer outra coisa (erro 5xx, tempo esgotado, uma página HTML no lugar do texto) fica em alerta, com metade dos pontos: sem ler o arquivo, não afirmamos que está liberado.

Cada robô bloqueado tira pontos na proporção do peso dele. Bloquear um essencial também limita a nota inteira a 39. O que cada robô alimenta está nas páginas de GPTBot, OAI-SearchBot, ClaudeBot, PerplexityBot, Googlebot e nas outras de Robôs de IA. Os critérios completos estão na checagem de robots.txt.

essencial: se estiver bloqueado, a nota não passa de 39

  • GPTBot(essencial)ChatGPT — treino
  • OAI-SearchBot(essencial)ChatGPT Search
  • ChatGPT-User(essencial)ChatGPT navegando ao vivo
  • ClaudeBot(essencial)Claude — treino
  • Claude-UserClaude navegando ao vivo
  • Claude-SearchBotClaude Search
  • PerplexityBot(essencial)Perplexity — índice
  • Perplexity-UserPerplexity navegando ao vivo
  • Googlebot(essencial)Google e AI Overviews
  • Google-ExtendedGemini — treino
  • BingbotBing e Copilot
  • CCBotCommon Crawl — alimenta vários
  • Applebot-ExtendedApple Intelligence
  • meta-externalagentMeta AI
  • AmazonbotAlexa e serviços da Amazon

Como reproduzir cada checagem na sua máquina

Os mesmos limites que o scanner usa, e o comando para ver com os próprios olhos.
O que cada checagem lê e como reproduzir na sua máquina
ChecagemO que o diagnóstico lêComo reproduzir
Acesso dos robôs de IA20 pontos/robots.txt, decidido para o caminho / de cada robô da lista.curl -s -D - /robots.txt: status 200, text/plain e ao menos uma linha User-agent. Depois, aplique as regras do grupo de cada robô.
Conteúdo sem JavaScript20 pontosO texto visível do HTML servido, sem script, style, noscript, template, svg e iframe.view-source: ou o script de contagem abaixo. Nota cheia a partir de 1.500 caracteres; zero abaixo de 300 ou com ponto de montagem vazio.
Trechos citáveis28 pontosAs seções do texto, divididas pelos subtítulos: tamanho, primeira frase, perguntas, números, listas e tabelas.Leia cada seção isolada, como a IA lê um trecho. A primeira frase responde o subtítulo? Há número verificável? As cinco partes estão em trechos citáveis.
Dados estruturados12 pontosBlocos application/ld+json e microdata: JSON válido, tipo de identidade, FAQPage e BreadcrumbList.Extraia os blocos com o comando abaixo e valide no Schema Markup Validator (validator.schema.org) ou no Teste de pesquisa aprimorada do Google.
Identidade da página7 pontosUm <h1>, <title> de 15 a 70 caracteres, meta description com 50 ou mais e lang no <html>.grep -c '<h1' no HTML salvo e uma olhada no <head> pelo view-source:.
Sinais de autoridade6 pontosAutor, data de publicação ou atualização e links para fora, ignorando nav, footer e aside.Procure <time datetime>, datePublished, uma assinatura e ao menos dois links externos no corpo da página.
llms.txt3 pontos/llms.txt e, se faltar, /llms-full.txt: status 200, mais de 50 bytes e não HTML.curl -s -D - /llms.txt
Sitemap2 pontosOs sitemaps declarados no robots.txt e depois /sitemap.xml, com <urlset> ou <sitemapindex>.curl -s /sitemap.xml | head e confira a linha Sitemap: no robots.txt.
Tempo de resposta2 pontosO tempo da busca da página inicial, redirecionamentos incluídos.%{time_total} no primeiro comando abaixo. Nota cheia abaixo de 1,2 s; metade até 3 s.
bash · resposta e tempo
# Status, tipo de conteúdo e tempo total, com o mesmo user agent do diagnóstico
curl -sL -o /dev/null \
  -w '%{http_code} %{content_type} %{time_total}s\n' \
  -A 'Mozilla/5.0 (compatible; MecitaBot/0.1; +https://mecita.com.br/bot)' \
  https://suaempresa.com.br/
bash · robots.txt
# Cabeçalhos e corpo do robots.txt: o status e o Content-Type importam tanto quanto as regras
curl -s -D - https://suaempresa.com.br/robots.txt
bash · texto sem JavaScript
# Baixa o HTML como um robô recebe e conta o texto legível, sem executar nada
curl -sL -A 'Mozilla/5.0 (compatible; MecitaBot/0.1; +https://mecita.com.br/bot)' https://suaempresa.com.br/ -o home.html
grep -c '<script' home.html

node -e "
const html = require('fs').readFileSync('home.html', 'utf8')
const text = html
    .replace(/<(script|style|noscript|template|svg|iframe)\b[\s\S]*?<\/(script|style|noscript|template|svg|iframe)\s*>/gi, ' ')
    .replace(/<!--[\s\S]*?-->/g, ' ')
    .replace(/<[^>]+>/g, ' ')
    .replace(/\s+/g, ' ')
    .trim()
console.log(text.length, 'caracteres legíveis')
"
bash · JSON-LD
# Extrai cada bloco JSON-LD do HTML servido e passa pelo jq: JSON inválido dá erro aqui
curl -sL https://suaempresa.com.br/ \
  | perl -0ne 'print "$1\n" while /<script[^>]*application\/ld\+json[^>]*>(.*?)<\/script>/gs' \
  | jq .

view-source é o melhor amigo

Abra view-source:https://seusite.com.br/ e procure (Ctrl+F) uma frase do texto principal. Se ela não aparece ali, não aparece para o robô. No DevTools do Chrome, a paleta de comandos tem “Disable JavaScript”: recarregue a página e veja o que sobra.

JSON-LD

Um bloco com JSON inválido é descartado inteiro. Valide cada bloco no Schema Markup Validator (validator.schema.org) e, para os tipos que o Google usa em resultados, no Teste de pesquisa aprimorada. Mais em JSON-LD e schema.org.

Fingir ser o GPTBot prova pouco

Trocar o user agent mostra se o servidor trata o nome de forma diferente. Mas firewalls sérios conferem se a requisição vem dos endereços publicados pela OpenAI: o seu GPTBot de mentira pode ser barrado enquanto o verdadeiro passa, ou o contrário. O registro de eventos do firewall é a fonte confiável.

Testar o robots.txt

Para um segundo parecer, o Google publica o parser de robots.txt dele como código aberto (github.com/google/robotstxt), com um utilitário de linha de comando que recebe o arquivo, o user agent e a URL.

bash · como o GPTBot se apresenta
# Os cabeçalhos que o servidor devolve para quem diz ser o GPTBot
curl -sI \
  -A 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; GPTBot/1.1; +https://openai.com/gptbot' \
  https://suaempresa.com.br/

A API JSON pública: GET /api/scan

O mesmo resultado da página /scan/<domínio>, em JSON, para scripts e integrações.
bash
curl -s 'https://mecita.com.br/api/scan?domain=suaempresa.com.br' \
  | jq '{score, criticalBlock, blockedAgents, findings: [.findings[] | {key, earned, max, severity}]}'

O parâmetro domain aceita o domínio com ou sem protocolo. Endereços internos e IPs privados são recusados. As respostas de erro têm sempre a forma { "error": "..." }:

  • 400: parâmetro ausente ou domínio inválido.
  • 404: domínio excluído a pedido do dono.
  • 502: falha inesperada ao analisar.

Um site fora do ar ou que recusa robôs não é erro da API: vem com status 200 e o campo error ou a entrada blocked. Abaixo, uma resposta encurtada, com valores ilustrativos (a real traz as nove checagens):

200 · application/json
{
  "url": "https://www.suaempresa.com.br/",
  "domain": "suaempresa.com.br",
  "score": 64,
  "title": "Sua Empresa | Contabilidade para pequenas empresas em Curitiba",
  "textLength": 4812,
  "findings": [
    {
      "key": "robots",
      "label": "Acesso dos robôs de IA",
      "earned": 19,
      "max": 20,
      "severity": "warn",
      "detail": "Bloqueados: CCBot (Common Crawl — alimenta vários).",
      "fix": "Remova as linhas Disallow desses agentes no robots.txt. Bloquear OAI-SearchBot ou PerplexityBot tira você das respostas com busca; bloquear GPTBot tira você do que o modelo aprende."
    },
    {
      "key": "render",
      "label": "Conteúdo sem JavaScript",
      "earned": 20,
      "max": 20,
      "severity": "ok",
      "detail": "4.812 caracteres de texto já vêm no HTML."
    },
    {
      "key": "citability",
      "label": "Trechos citáveis",
      "earned": 12,
      "max": 28,
      "severity": "warn",
      "detail": "3 de 5 seções com texto em tamanho citável, 2 abrindo com resposta, 0 de 6 subtítulos em forma de pergunta.",
      "fix": "Nenhum subtítulo está em forma de pergunta — é assim que a pessoa digita no assistente.",
      "parts": {
        "quotableBlocks": 5.4,
        "directAnswers": 2.4,
        "questionHeadings": 0,
        "dataDensity": 2.1,
        "structure": 2
      }
    }
  ],
  "blockedAgents": [
    "CCBot"
  ],
  "criticalBlock": false,
  "scannedAt": "2026-09-29T12:00:00.000Z"
}
Campos do resultado
CampoTipoO que significa
urlstringA URL final lida, depois dos redirecionamentos.
domainstringO domínio normalizado, sem protocolo.
scorenumberDe 0 a 100. Limitado a 39 enquanto um robô essencial estiver bloqueado.
titlestring | nullO conteúdo do <title>, ou null.
textLengthnumberCaracteres de texto legível no HTML servido, sem executar JavaScript.
findingsFinding[]Uma entrada por checagem, na ordem de peso.
blockedAgentsstring[]Os user agents da lista que o robots.txt bloqueia na página inicial.
criticalBlockbooleantrue quando algum robô essencial está bloqueado, ou quando o site recusou o acesso.
scannedAtstringData e hora da leitura, em ISO 8601 (UTC).
errorstring?Presente só quando o site não pôde ser lido. Nesse caso, score é 0 e findings vem vazio.
Campos de cada item de findings
CampoTipoO que significa
keystringIdentificador estável da checagem: robots, render, citability, schema, basics, authority, llmsTxt, sitemap, responseTime, blocked.
labelstringO nome da checagem em português.
earnednumberPontos obtidos.
maxnumberPontos possíveis. Os nove somam 100.
severity'ok' | 'warn' | 'fail'A gravidade do resultado.
detailstringO que foi medido, em português.
fixstring?Como corrigir. Ausente quando não há nada a fazer.
partsRecord<string, number>?Subnotas, só em citability: quotableBlocks, directAnswers, questionHeadings, dataDensity, structure.

Como as instruções do Plano de Correção são organizadas

O plano é escrito para duas pessoas ao mesmo tempo: quem paga e quem implementa.

O Plano de Correção parte do mesmo diagnóstico determinístico desta página e das evidências que ele coletou. O texto é escrito por um modelo de IA; a nota, os pontos de cada checagem e a nota alcançável são calculados pelo código e o modelo não tem como alterá-los. Cada ação cita a evidência que a justifica, e uma referência que não existe no diagnóstico é rejeitada antes de o plano ser entregue.

Além das ações, o plano traz um resumo, as correções rápidas e as limitações do que foi possível verificar. Durante 30 dias, dá para rodar o diagnóstico de novo e gerar um plano atualizado até 3 vezes, o que serve para conferir o seu trabalho.

Checagem
Qual das nove checagens a ação corrige. Toda ação nasce de um resultado do diagnóstico; não existe item solto.
Título
O que fazer, numa linha, no imperativo.
Prioridade
Alta, média ou baixa. A ordem do plano começa pelo que libera o acesso dos robôs e só depois chega ao acabamento.
Quem controla
Se a correção depende só do site, em parte dele (um plugin, uma plataforma fechada) ou de algo fora dele, como a CDN de um fornecedor.
Para o dono do site
Por que isso importa, em português sem jargão, para quem aprova o trabalho.
Passo a passo
A lista de tarefas, um passo concreto por item e na ordem em que devem ser feitos.
Para quem mantém o site
O detalhe técnico e o trecho pronto para colar: JSON-LD preenchido, regra de robots.txt, texto reescrito.
Como validar a correção
Como conferir que funcionou: o que abrir, o que procurar e o que o novo diagnóstico deve mostrar.
Evidências
O que o diagnóstico viu e justifica a ação: uma linha do robots.txt, um bloco JSON-LD, um cabeçalho HTTP. Só vale o que foi coletado de fato ou uma URL que o diagnóstico buscou.

Exemplo ilustrativo · Dados estruturados

Prioridade altaDepende só do site

Declarar a empresa como AccountingService na página inicial

Para você

Hoje a página inicial não diz, num formato que a IA entende, que a Sua Empresa é um escritório de contabilidade em Curitiba. Sem isso, o assistente precisa adivinhar pelo texto e pode confundir vocês com outro negócio.

Para quem mantém o site

1. No template da página inicial, inclua um bloco <script type="application/ld+json"> no HTML servido pelo servidor, não injetado por JavaScript. 2. Use @type AccountingService com name, url, telephone, address (PostalAddress) e sameAs. 3. Valide em validator.schema.org. 4. Rode o diagnóstico de novo: a checagem de dados estruturados não deve mais acusar a falta de tipo de identidade.

Evidências

  • ev:14
  • https://suaempresa.com.br/

Correções prontas por stack

Os ajustes que mais aparecem nos diagnósticos, em código. Troque os dados de exemplo pelos do cliente.

Next.js (App Router)

Server Components já saem renderizados no HTML, e Client Components também passam pelo servidor. O texto some quando é buscado num useEffect ou carregado com ssr: false. Declare os robôs de IA explicitamente no app/robots.ts, em vez de depender do grupo *, e escreva o JSON-LD como um <script> na própria página, escapando o caractere <.

app/robots.ts
// app/robots.ts
import type { MetadataRoute } from 'next'

export default function robots(): MetadataRoute.Robots {
    return {
        rules: [
            // Explícito: nenhum robô de IA depende do grupo * para entrar
            {
                userAgent: ['GPTBot', 'OAI-SearchBot', 'ChatGPT-User', 'ClaudeBot', 'PerplexityBot'],
                allow: '/',
            },
            { userAgent: '*', allow: '/', disallow: ['/admin/', '/api/'] },
        ],
        sitemap: 'https://suaempresa.com.br/sitemap.xml',
    }
}
app/page.tsx
// app/page.tsx (Server Component: o texto e o JSON-LD saem no HTML)
import type { Metadata } from 'next'

export const metadata: Metadata = {
    title: 'Sua Empresa | Contabilidade para pequenas empresas em Curitiba',
    description:
        'Escritório de contabilidade em Curitiba para MEI, Simples Nacional e Lucro Presumido. Abertura de empresa, folha e impostos com atendimento por WhatsApp.',
}

const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'AccountingService',
    name: 'Sua Empresa Contabilidade',
    url: 'https://suaempresa.com.br/',
    telephone: '+55-41-0000-0000',
    address: {
        '@type': 'PostalAddress',
        streetAddress: 'Rua Exemplo, 123',
        addressLocality: 'Curitiba',
        addressRegion: 'PR',
        postalCode: '80000-000',
        addressCountry: 'BR',
    },
    sameAs: ['https://www.instagram.com/suaempresa'],
}

export default function Page() {
    return (
        <main>
            <script
                type="application/ld+json"
                dangerouslySetInnerHTML={{
                    __html: JSON.stringify(jsonLd).replace(/</g, '\\u003c'),
                }}
            />
            <h1>Contabilidade para pequenas empresas em Curitiba</h1>
            {/* O conteúdo principal aqui, vindo do servidor, não de um useEffect */}
        </main>
    )
}

WordPress

Antes de tudo, confira em Configurações › Leitura se a opção de evitar a indexação está desmarcada: marcada, ela muda o robots.txt virtual do WordPress. Um arquivo robots.txt físico na raiz substitui o virtual. Plugins de SEO costumam gerar o próprio JSON-LD; olhe o HTML antes de acrescentar outro bloco, para não declarar a empresa duas vezes com dados diferentes. O sitemap nativo fica em /wp-sitemap.xml: como o diagnóstico procura primeiro os sitemaps declarados no robots.txt, garanta a linha Sitemap:. Mais em WordPress e robôs de IA.

functions.php
<?php
// functions.php do tema filho, ou um plugin próprio
add_action('wp_head', function () {
    if (!is_front_page()) {
        return;
    }
    $data = [
        '@context' => 'https://schema.org',
        '@type' => 'LocalBusiness',
        'name' => get_bloginfo('name'),
        'url' => home_url('/'),
        'telephone' => '+55-11-0000-0000',
        'address' => [
            '@type' => 'PostalAddress',
            'streetAddress' => 'Rua Exemplo, 123',
            'addressLocality' => 'São Paulo',
            'addressRegion' => 'SP',
            'addressCountry' => 'BR',
        ],
    ];
    echo '<script type="application/ld+json">'
        . wp_json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
        . "</script>\n";
});
robots.txt
# robots.txt físico na raiz do WordPress (substitui o virtual)
User-agent: *
Allow: /
Disallow: /wp-admin/
Allow: /wp-admin/admin-ajax.php

Sitemap: https://suaempresa.com.br/wp-sitemap.xml

Nginx

O erro mais traiçoeiro está nas SPAs: o fallback para index.html faz um robots.txt inexistente responder 200 com HTML. O diagnóstico trata isso como “não deu para verificar”, com metade dos pontos, e o llms.txt e o sitemap somem do mesmo jeito. Sirva esses arquivos com correspondência exata e 404 de verdade, e procure regras que recusem user agents de IA.

nginx.conf
# O fallback de SPA não pode engolir os arquivos lidos pelos robôs:
# sem isto, /robots.txt inexistente vira index.html com status 200.
location = /robots.txt  { try_files $uri =404; }
location = /sitemap.xml { try_files $uri =404; }
location = /llms.txt    { charset utf-8; try_files $uri =404; }

location / {
    try_files $uri $uri/ /index.html;
}

# Procure e remova regras que barram robôs de IA antes do robots.txt, como:
# if ($http_user_agent ~* "(GPTBot|ClaudeBot|PerplexityBot)") { return 403; }

Cloudflare e outros firewalls

O firewall decide antes do robots.txt. Confira o bloqueio de robôs de IA, o robots.txt gerenciado, os modos de proteção contra bots e as regras personalizadas do WAF. Para abrir uma exceção, prefira a verificação de bot do próprio firewall, que confere a origem, a uma regra por nome de user agent, que qualquer um falsifica. O passo a passo está em Cloudflare e firewall bloqueando robôs de IA.

Cloudflare · regra do WAF
# Regra personalizada do WAF: expressão
(cf.client.bot)

# Ação: Skip (pular) as proteções que estão desafiando ou bloqueando os robôs.
# Os campos e as ações disponíveis variam por plano: confira na documentação da Cloudflare.
bash · desafio do firewall
# Uma resposta de desafio costuma trazer este cabeçalho
curl -sI https://suaempresa.com.br/ | grep -i -E '^(HTTP|cf-mitigated|server)'

Depois de corrigir

Conferir é parte da entrega. O diagnóstico é o mesmo antes e depois, então a diferença é mensurável.
  1. Rode os comandos acima de novo. Eles mostram o que mudou no HTML e nos arquivos em segundos, sem esperar cache nenhum.
  2. Rode o diagnóstico. A página de resultado pode ficar guardada por até um dia; a API lê o site na hora.
  3. Publique um llms.txt se ainda não existe. É barato e ainda raro no Brasil. O guia do llms.txt mostra o formato.
  4. Cuide do texto. Depois da parte técnica, o que mais pesa é o conteúdo que a IA consegue citar: veja trechos citáveis e citabilidade.

Para ir além: as nove checagens com os critérios exatos, o glossário com user agent, crawler, sitemap.xml e llms.txt, e os artigos sobre sites em JavaScript e robots.txt para robôs de IA.

Rode o diagnóstico no site do seu cliente

Gratuito, sem cadastro e sem instalar nada. O resultado tem uma página própria que você pode mandar para quem decide.

✓ Sem cadastro✓ Sem cartão✓ 15 robôs de IA verificados