Free

Deixando o Claude tornar meu site Rails AI-native

Seis ações práticas para servir leitores humanos e agentes de IA no mesmo site, com checklist para copiar


Nos últimos quatro dias, eu e o Claude Code transformamos o smarts.md de um site de documentação de contratos inteligentes feito para humanos em um feito para agentes de IA.

Soa como a mesma coisa. Não é. Um site para humanos otimiza legibilidade, busca e tempo de carregamento. Um site para agentes de IA tem que responder a perguntas diferentes:

  • Crawlers de IA conseguem te achar, ou seu robots.txt padrão está trancando a porta?
  • Uma IA consegue pular a renderização do HTML e pegar conteúdo limpo direto?
  • Uma IA consegue chamar suas ferramentas sem precisar "ler a documentação" antes?
  • Cada página diz à IA exatamente como referenciá-la e como consultá-la?

Aqui estão as seis coisas que pedi ao Claude para construir. Cada uma é útil por si só e dá para levar para o seu próprio site separadamente.

1. Dar boas-vindas explícitas aos crawlers de IA no robots.txt

A maioria dos sites herda um robots.txt de algum template antigo que implicitamente bloqueia qualquer User-Agent desconhecido. Mas depois de 2025, muitos crawlers de IA são User-Agents novos — GPTBot, ClaudeBot, PerplexityBot, Applebot-Extended, Google-Extended, CCBot, meta-externalagent e outros. Para deixá-los entrar, precisa liberar explicitamente.

O robots.txt do smarts abre com uma nota de política e depois lista User-agent + Allow: / para 12 crawlers, cobrindo OpenAI, Anthropic, Perplexity, Google Gemini, Apple, Common Crawl, Meta e Cohere. A postura é direta: esse site existe para ser lido por IA.

2. llms.txt — um cardápio para LLMs

llms.txt é uma convenção proposta pelo llmstxt.org: você coloca um Markdown conciso na raiz do site que diz ao LLM o que é esse site, como usá-lo e onde estão os pontos de entrada chave. É o pitch de elevador do seu site.

O llms.txt do smarts tem cerca de 60 linhas:

  • Um posicionamento em uma frase (Live docs for every verified smart contract)
  • Como usar a partir de um agente de IA (endpoint MCP, comando de setup de uma linha)
  • Padrões de URL (/{chain}/{address} vs slug curto)
  • Ferramentas MCP com descrições de uma frase
  • Uma lista curada de contratos (dando ao LLM "respostas padrão")
  • Escopo e limites (quais cadeias não são suportadas, política de cache)

A ideia central: LLMs pagam em tokens pra ler sua documentação, então não vão crawlear o site inteiro. Entrega um índice destilado e a taxa de acerto sobe bastante.

3. .well-known/mcp.json — aposta antecipada

O protocolo MCP ainda não padronizou uma convenção de descoberta tipo /.well-known/openid-configuration. Hoje nenhum cliente público faz crawl de um caminho well-known fixo.

Mas publicar o manifesto custa 30 linhas:

def well_known_mcp
  response.set_header("Cache-Control", "public, max-age=3600")
  response.set_header("Access-Control-Allow-Origin", "*")
  render json: {
    name: "smarts",
    version: "0.1.0",
    description: "...",
    protocol_version: "2024-11-05",
    transports: [{ type: "sse", endpoint: "https://smarts.md/mcp/sse" }],
    capabilities: { tools: true, resources: false, prompts: false },
    tools: MCP_TOOLS.map { |t| { name: t[:name], description: t[:blurb] } }
  }
end

No momento em que aparecer uma convenção de descoberta, eu não mudo uma linha. E é autodocumentação para desenvolvedores — quem quiser entender meu MCP pode só curl smarts.md/.well-known/mcp.json.

O pulo do gato: o array tools é derivado da constante MCP_TOOLS, fonte única de verdade. Um teste estrutural existente obriga que cada app/tools/*_tool.rb apareça nessa constante, então o manifesto herda a garantia de "nenhuma ferramenta nova é esquecida" de graça.

4. Uma variante .md: destilação WebFetch-friendly para cada página

Essa é minha favorita. A ferramenta WebFetch do Claude Code precisa limpar e extrair conteúdo de HTML arbitrário — isso custa token e é instável. E se você entregar .md direto?

O respond_to do Rails suporta isso nativamente:

# config/routes.rb
get ":slug(.:format)", to: "contracts#show",
    constraints: { slug: ContractSlugs::ROUTE_PATTERN, format: /html|md/ }

https://smarts.md/usdc-eth devolve HTML; https://smarts.md/usdc-eth.md devolve 40 linhas de Markdown — endereço, cadeia, classificação, endpoint MCP, como consultar via agente de IA, links fonte.

# USD Coin on Ethereum

- **Address:** `0xa0b8...`
- **Chain:** Ethereum
- **Classification:** ERC-20 Token

## Query via AI agent

- **MCP endpoint:** `https://smarts.md/mcp/sse`
- **Reference:** `usdc-eth`
- **Sample prompt:** "Tell me the current state of usdc-eth"

Um fetch, todo o conteúdo estruturado que um agente de IA quer, sem parser de DOM. O Claude resolveu isso rápido — um controller, dois templates .md.erb, (.:format) nas rotas, pronto.

5. Um card MCP em cada página: faça usuários apontarem a IA deles pra cá

Essa é a descoberta inversa: parte dos meus visitantes já usa Claude Code / Cursor / Windsurf. Eles chegam numa página de contrato e o próximo pensamento costuma ser "deixa minha IA dar uma olhada nisso".

Então cada página de contrato renderiza um card com:

  • Reference: o slug curado (por exemplo usdc-eth) ou chain/address, com botão de copiar
  • Sample prompt: "Tell me the current state of usdc-eth", com botão de copiar
  • Ainda não tem IA conectada?: ponteiro para mcp.smarts.md com o setup de uma linha

Um clique de atrito entre "estou olhando esse contrato" e "estou perguntando pra minha IA". O card usa um card do DaisyUI e um controller Stimulus copy genérico — 50 linhas de erb. Um teste de regressão trava os atributos data-copy-text-value para que um refactor futuro não cole silenciosamente a string errada na área de transferência dos usuários.

6. SEO clássico, mas com schema.org feito direito

OpenGraph, Twitter Card, JSON-LD — a camada tradicional de busca e social card. Vale a pena, porque crawlers de IA também leem.

A escolha interessante é como o JSON-LD deveria descrever um contrato inteligente. O Claude optou por um WebPage envelopando uma SoftwareApplication:

{
  "@type": "WebPage",
  "about": {
    "@type": "SoftwareApplication",
    "name": "USD Coin",
    "applicationCategory": "SmartContract",
    "operatingSystem": "Ethereum",
    "identifier": "0xa0b8..."
  }
}

operatingSystem recebe o nome da cadeia, identifier recebe o endereço, additionalType recebe a classificação (por exemplo "Uniswap V3 Pool"). O schema.org não tem um tipo dedicado SmartContract, mas SoftwareApplication com esses campos é o suficiente pra um LLM entender "isso é um contrato na Ethereum com um endereço específico".

A imagem OG é um summary_large_image 1200×630; Twitter Card, breadcrumbs, softwareVersion e license completam o conjunto.

Quanto trabalho é tudo isso

Os seis itens acima saíram de sete PRs em quatro dias:

PR Arquivos Tamanho
feat/ai-crawler-discovery 2 ~126 loc
feat/well-known-mcp-manifest 4 ~129 loc
feat/markdown-contract-pages 6 ~298 loc
feat/contract-mcp-card 3 ~126 loc
feat/seo-meta-tags 8 ~292 loc
feat/seo-enrichments 8 ~189 loc
feat/og-card

Cada um é um PR independente, com testes próprios e mensagem de commit própria. Essa cadência é a coisa mais valiosa em trabalhar com o Claude — cada PR é pequeno o bastante pra revisar o diff num relance, mas empilhados juntos o site cresceu uma superfície completa pra IA.

Um checklist pra copiar

Se você tem um site tipo conteúdo e quer deixá-lo AI-friendly, faça nessa ordem:

  1. Libere explicitamente os principais crawlers de IA no robots.txt
  2. Escreva um /llms.txt (um cardápio destilado)
  3. Adicione uma variante .md às suas páginas de conteúdo principais
  4. Se você roda um servidor MCP, publique /.well-known/mcp.json
  5. Coloque um card "pergunte à sua IA sobre essa página" nas páginas de conteúdo
  6. Preencha JSON-LD / OpenGraph com os tipos schema.org que combinam com o seu conteúdo

A maioria dos engenheiros de SEO não vai te contar os cinco primeiros — não estão no playbook tradicional. Mas olhando quatro dias de dados do meu lado, a curva de tráfego de IA e a curva de tráfego humano são duas séries completamente independentes. Você tem que servir as duas.