
De mock-only-works para real-world-works: 48 horas de depuração do reCAPTCHA
Primeiro, uma estrutura honesta:
mk-qa-masteré um servidor MCP de código aberto para engenheiros de QA. O solucionador de reCAPTCHA nele é um fallback de Nível 3 para testar seus próprios aplicativos quando o Nível 1 (as chaves de teste oficiais do Google) e o Nível 2 (flags de recurso / lista de permissões de IP) não estão disponíveis. Ele não é uma ferramenta para "burlar captcha". Ele se recusa a rodar em páginas de login do Google / Apple / Microsoft / Discord, independentemente da flag de consentimento. Com isso esclarecido…
Este é um diário sobre as 48 horas que levei para passar de "enviei um solucionador de reCAPTCHA, todos os testes unitários passaram" para "ele realmente funciona contra a demonstração real do Google." Quatro versões (v0.7.0 → v0.7.4), três intermediárias quebradas e um monte de lições que quero registrar antes de esquecer.
A configuração
A ideia por trás do solucionador é simples. Duas ferramentas MCP atômicas:
-
inspect_visual_challenge— encontra o iframe do captcha na página atual, tira uma captura de tela, retorna as coordenadas da grade de azulejos + uma captura de tela. -
solve_visual_challenge— aceita a seleção de azulejos do cliente de IA (quais azulejos contêm ônibus, quais contêm faixas de pedestres, etc.), clica neles, pressiona Verificar, retorna o token.
O cliente de IA (Claude Code, Cursor, etc.) vê a captura de tela, decide quais azulejos correspondem ao prompt e chama a solução. O servidor é os olhos e as mãos; a IA é o cérebro. Modelos multimodais como Claude 4.7 são surpreendentemente bons nisso — eles foram treinados na web aberta, que tem muitas fotos de ônibus.
Até agora, tudo bem na teoria.
Dia 1 — v0.7.0 é lançado
A primeira versão foi lançada na segunda-feira. Ela detectou o reCAPTCHA iframe[src*="bframe"], tirou uma captura de tela, calculou as coordenadas dos azulejos dividindo a caixa delimitadora do iframe em uma grade de 3×3 ou 4×4 e clicou no centro de cada azulejo selecionado.
Os testes unitários passaram. O fixture de mock empacotado (uma página HTML autônoma que imita a estrutura do reCAPTCHA) funcionou de ponta a ponta. Eu escrevi um PRD, lancei uma versão, postei um Dev.to walkthrough. Foi ótimo.
A estrutura do fixture de mock era:
<table class="rc-imageselect-table">
<tr><td>...</td><td>...</td><td>...</td></tr>
...
</table>
Seletores na impressão digital:
"tile_table_selector": ".rc-imageselect-table",
"tile_cell_selector": "td",
O que poderia dar errado?
Dia 2 — v0.7.1 adiciona hCaptcha
No dia seguinte, estendi a tabela de impressão digital para suportar hCaptcha. Mesma arquitetura — seletores diferentes. Nenhuma nova ferramenta MCP. Os testes permaneceram verdes. Eu me senti bem sobre o design: quando um fornecedor muda, você adiciona uma linha à tabela de impressão digital, e pronto.
Eu também não fiz um teste real para hCaptcha. (Voltaremos a isso.)
Dia 3 — v0.7.2: o primeiro "conserto"
Eu escrevi um pequeno script de dogfood — abri o Chromium, naveguei até https://www.google.com/recaptcha/api2/demo, cliquei no link para acionar um desafio de imagem, chamei inspect_visual_challenge, salvei a captura de tela, pedi à IA os índices dos azulejos, chamei solve_visual_challenge, vi se um token voltava.
A primeira execução retornou com status falhou. Perguntei ao usuário (neste caso: eu) o que ele viu no navegador. A resposta foi inquietante: "Eu disse para clicar 2, 5 e 8 — apenas 5 e 8 realmente foram destacados."
Eu investiguei a matemática das coordenadas. A abordagem de divisão do iframe dividiu o iframe completo em células de linhas × colunas. Mas o iframe contém um banner de cabeçalho (o texto do prompt) acima da grade e um rodapé (o botão Verificar) abaixo. Então:
- Para uma grade de 3×3 em um iframe de 400×580 com cabeçalho ~130px e rodapé ~130px:
- A grade real tem 320px de altura, ~106px por linha.
- A divisão ingênua do iframe dá 193px por linha.
- O centro computado da linha 0 cai no banner do cabeçalho.
- O centro computado da linha 2 cai no rodapé.
- Apenas a linha 1 acontece de estar aproximadamente correta.
Eu escrevi v0.7.2 para corrigir isso. Em vez de dividir o iframe, eu leria o bounding_box() real de cada célula do DOM via Playwright:
for index in range(tile_count):
bb = cells.nth(index).bounding_box()
if not is_real_dict(bb):
# voltar para a divisão do iframe para fixtures de mock
break
candidate.append({"viewport_x": bb["x"], ...})
O teste unitário (contra o fixture de mock) imediatamente confirmou a correção. Eu aumentei para v0.7.2, abri um PR, mesclei, lancei, publiquei no PyPI. Pronto.
Dia 4 de manhã — espere, ainda está quebrado
No dia seguinte, executei o dogfood novamente. Saída do console para inspect:
"tiles": [
{"index": 0, "viewport_x": 85, "viewport_y": 92, "w": 133, "h": 193},
{"index": 1, "viewport_x": 218,O desenvolvimento de soluções para reCAPTCHA pode impactar empresas brasileiras que dependem de testes de software. A capacidade de simular interações com captchas pode acelerar o desenvolvimento e a validação de aplicações web. Isso é especialmente relevante em um cenário onde a automação e a eficiência são cruciais.
Noticias relacionadas

Um Curso Intensivo em MCP: Um Guia para Iniciantes Usando TypeScript
Este artigo apresenta o Model Context Protocol (MCP) como um padrão aberto para conectar aplicações de IA, explicando suas três pilares e como construir um servidor MCP em TypeScript.

Construímos operações de dados colunares para agentes de IA — aqui está o porquê e como
O Frame é uma suíte de operações de dados colunares que permite que agentes de IA manipulem dados empresariais sem a necessidade de infraestrutura adicional ou chamadas externas, eliminando riscos de alucinação.

Eu deduplicei todos os registros MCP em um único índice. Veja como 22.561 servidores realmente se parecem
Registros MCP mostram contagens duplicadas. Após a deduplicação, 22.561 servidores distintos foram identificados, revelando um ecossistema maior do que muitos plugins maduros.
Gostou do conteudo?
Receba toda semana as principais novidades sobre WebMCP.