Desenvolvimento do Servidor MCP Recce
Arquitetura
RecceMCPServer registra handlers list_tools/call_tool via Server do SDK MCP. call_tool despacha para métodos _tool_*, classifica erros, gera logs/emite métricas, e relança exceções.
O ponto de entrada run_mcp_server() remove single_env antes de passar os kwargs para load_context().
Padrões Chave
Classificação de erros — Listas de indicadores compartilhadas definidas em recce/tasks/rowcount.py. Ordem de prioridade (PERMISSION_DENIED > TABLE_NOT_FOUND > SYNTAX_ERROR) é aplicada por _classify_db_error() em mcp_server.py e _query_row_count() em rowcount.py. Classificado → logger.warning() + sentry_metrics.count() (quando sentry_sdk está disponível). Não classificado → logger.error() + traceback.
Quirk do SDK MCP — O handler deve levantar exceção para que o SDK defina isError=True.
Contratos de resposta — Veja CLAUDE.md. Apenas _meta aditivo. summary.py: proteger com is None, não com dict.get(key, 0). Exibição N/A inclui motivo: "N/A (table_not_found)".
Ambiente único — _maybe_add_single_env_warning() adiciona _warning aos resultados de diff. Descrições recebem nota condicional.
Testes (Três Camadas)
| Camada | Arquivo | Fonte de Dados | Executado Em | Objetivo |
|---|---|---|---|---|
| Unitário | tests/test_mcp_server.py | Mock RecceContext | CI (pytest) | Corretude lógica — handlers de ferramentas, classificação de erros, formato de resposta |
| Integração | tests/test_mcp_e2e.py | DbtTestHelper + DuckDB (dados fixos) | CI (pytest) | Protocolo MCP funciona ponta a ponta via fluxos de memória anyio |
| Smoke (E2E) | skill /recce-mcp-e2e | Projeto dbt real do usuário + banco de dados real | Manual | Todas as 8 ferramentas retornam resultados válidos contra dados reais |
Cada nova funcionalidade MCP ou mudança de comportamento deve ser coberta nas três camadas.
Análise de Lacunas de Cobertura de Testes
Após concluir uma rodada de mudanças MCP (veja o Portão de Verificação E2E abaixo para definição), procure proativamente por cobertura ausente nas três camadas antes de perguntar sobre a verificação E2E.
Como verificar:
- Identifique o que mudou — novo handler de ferramenta? novo caminho de erro? novo campo de resposta?
- Para cada mudança, verifique se a cobertura existe em cada camada:
- Unitário: O arquivo
tests/test_mcp_server.pytem um caso de teste para o novo comportamento? (caminho feliz + caminho de erro) - Integração: O arquivo
tests/test_mcp_e2e.pyexercita a nova ferramenta/recurso via protocolo MCP? - Smoke: O template
/recce-mcp-e2ecobrirá a nova ferramenta? (Se uma nova ferramenta foi adicionada, o template pode precisar ser atualizado)
- Unitário: O arquivo
Se lacunas forem encontradas, reporte-as ao usuário antes do prompt do portão E2E:
Lacunas de cobertura de teste encontradas:
- Unitário: falta teste para o caminho de erro de
_tool_fooquando tabela não encontrada- Integração:
test_mcp_e2e.pynão exercita a ferramentafoo- Smoke: template
/recce-mcp-e2enão inclui a ferramentafooDeseja preencher essas lacunas antes de executar o E2E?
NÃO escaneie após: mudanças apenas de teste, edições de comentários/documentação, reordenação de imports.
Portão de Verificação E2E
Após cada rodada significativa de mudanças MCP, você DEVE perguntar ao usuário:
Mudanças MCP concluídas para esta rodada. Executar
/recce-mcp-e2epara verificar?
Se o usuário disser sim, invoque /recce-mcp-e2e. Se um caminho de projeto dbt foi usado anteriormente nesta sessão, reutilize-o automaticamente; caso contrário, pergunte.
O que conta como "uma rodada":
- Um handler de ferramenta adicionado ou modificado + seus testes unitários passam
- Lógica de classificação de erro alterada + testes passam
- Formato de ambiente único ou resposta alterado + testes passam
NÃO pergunte após: mudanças apenas de teste, edições de comentários/documentação, reordenação de imports.
Isso é separado de tests/test_mcp_e2e.py — esse arquivo testa com DbtTestHelper + DuckDB no CI. /recce-mcp-e2e verifica todas as 8 ferramentas contra um projeto dbt real com um banco de dados real.
Armadilhas
- Import
sentry_sdk:# pragma: no coverno except (CI sempre o tem) - Python 3.9:
Union[X, Y]nãoX | Y - Pre-commit: black/isort pode reformatar — re-estage e commite
run.pyschema_diff_should_be_approved()try/except é intencional (garante criação de verificação)
Mapa de Arquivos
recce/mcp_server.py (servidor + handlers), recce/tasks/rowcount.py (indicadores de erro, RowCountStatus), recce/run.py (preset CLI), recce/summary.py (lógica de exibição), recce/event/__init__.py (Sentry)