
Como Encontrei um Bug Silencioso em uma API de um Servidor MCP com 3.548 Estrelas
Como Encontrei um Bug Silencioso na API em um Servidor MCP com 3.548 Estrelas
A API aceitou sua solicitação. Retornou HTTP 200. E então, silenciosamente, não fez nada.
Sem erro. Sem rastreamento de pilha. Apenas um resultado vazio onde sua consulta Cypher deveria ter sido executada.
Esta é a história de como encontrei esse bug no CodeGraphContext — um servidor MCP que indexa bases de código locais em um banco de dados gráfico para fornecer contexto real de código a assistentes de IA. 3.548 estrelas. Ativamente mantido. Usado por desenvolvedores que constroem sobre Claude e outras cadeias de ferramentas LLM.
E havia uma falha silenciosa em seu ponto de extremidade de consulta HTTP pública.
O que é o CodeGraphContext?
Antes do bug — um rápido contexto.
CodeGraphContext é um servidor MCP (Modelo de Protocolo de Contexto). Ele permite que você aponte para uma base de código, indexe-a em um banco de dados gráfico (FalkorDB ou Kuzu) e, em seguida, consulte esse gráfico usando Cypher — a linguagem de consulta para bancos de dados gráficos. A ideia é que assistentes de IA possam usá-lo para entender a estrutura do código, dependências e relacionamentos em uma profundidade que leituras de arquivos planos não oferecem.
Ele expõe tanto uma interface MCP quanto uma API HTTP padrão. A API HTTP é o que é relevante aqui.
Como Eu Encontrei
Eu não estava caçando bugs. Eu estava lendo o código-fonte.
Quando contribuo para um novo repositório, começo rastreando uma única solicitação de ponta a ponta através da base de código. Escolho um ponto de extremidade, sigo-o desde a definição da rota até o manipulador, passando por quaisquer chamadas subsequentes, e volto. É a maneira mais rápida de realmente entender o que uma base de código faz.
Comecei com /api/v1/query — o ponto de extremidade que permite enviar uma consulta Cypher via HTTP e obter resultados de volta.
A rota está em src/codegraphcontext/api/router.py. Aqui está o que encontrei na linha 89:
server.handle_tool_call("execute_cypher_query", {
"query": request.query,
"params": request.params
})
A rota estava chamando handle_tool_call com o nome da ferramenta execute_cypher_query e passando a consulta como "query".
Então fui para o manipulador. src/codegraphcontext/tools/handlers/query_handlers.py, linha 16:
cypher_query = args.get("cypher_query")
if not cypher_query:
return {"error": "A consulta Cypher não pode estar vazia."}
O manipulador lê cypher_query. Não query. cypher_query.
A rota passa query. O manipulador lê cypher_query. Eles nunca se encontram.
Assim, args.get("cypher_query") retorna None. O manipulador atinge a verificação de vazio. Retorna {"error": "A consulta Cypher não pode estar vazia."}. HTTP 200.
Uma solicitação válida, carregando uma consulta Cypher real, falhou silenciosamente — não porque a consulta estava errada, mas porque o nome da chave não correspondia através de uma fronteira de chamada de função interna.
Por que Isso é Fácil de Perder
O modo de falha é o problema.
Se isso lançasse uma exceção, você veria um 500. Se retornasse um 4xx, você saberia que a API rejeitou sua entrada. Mas retornou 200 com uma carga de erro que parece um erro de validação. A leitura natural é: "Enviei uma consulta ruim." O problema real é: a camada da API e o manipulador da ferramenta estavam falando línguas diferentes sobre o que chamar o mesmo campo.
Aqui está a reprodução do problema que eu relatei:
curl -X POST http://localhost:8000/api/v1/query \
-H "Content-Type: application/json" \
-d '{"query":"MATCH (n) RETURN count(n) AS count","params":{}}'
Cypher válido. JSON válido. Solicitação HTTP válida. Mas o manipulador da ferramenta não vê nenhuma chave cypher_query — porque a rota a chamou de query — e retorna vazio.
O corpo da solicitação parece correto na fronteira da API. A falha acontece após a tradução interna. Isso torna o caminho de depuração confuso. Você inspecionaria a solicitação, veria uma carga útil válida e não teria uma razão óbvia para olhar o nome da chave sendo passada entre a rota e o manipulador.
A Solução
Uma mudança de linha em router.py:
# Antes
server.handle_tool_call("execute_cypher_query", {
"query": request.query,
"params": request.params
})
# Depois
server.handle_tool_call("execute_cypher_query", {
"cypher_query": request.query,
"params": request.params
})
Altere a chave de "query" para "cypher_query" para que corresponda ao que o manipulador espera.
Eu mantive a correção estreita — apenas alinhando o nome da chave, sem refatoração, sem mudanças de esquema. Depois adicionei um teste de regressão em tests/unit/api/test_query_router.py que verifica se um POST para /api/v1/query chega a execute_cypher_query com a chave correta. Assim, isso não pode quebrar silenciosamente novamente.
O que Eu Adicionei: O Teste de Regressão
Esta é a parte que eu empurraria qualquer um que estivesse corrigindo um bug como este a fazer
A descoberta de bugs em APIs é crucial para garantir a funcionalidade de sistemas que suportam assistentes de IA. Empresas brasileiras que utilizam servidores MCP devem estar atentas a esse tipo de falha para melhorar a confiabilidade de suas aplicações. A correção e testes adicionais ajudam a prevenir problemas futuros.
