ctxai

Um servidor MCP ciente de versão que previne alucinações de codificação de IA ao validar sugestões contra seus pacotes instalados reais.

Documentação

ctxai

ctxai é um servidor Model Context Protocol (MCP) que torna assistentes de codificação com IA cientes da versão do seu ambiente. Ele lê os pacotes realmente instalados, injeta esse contexto no LLM e valida cada sugestão de código contra o seu ambiente real — detectando imports alucinados, chamadas de métodos inexistentes e pacotes fantasmas perigosos antes que cheguem ao seu editor.


O problema que ele resolve

Assistentes de codificação com IA alucinam de três maneiras específicas que são difíceis de detectar:

  1. Alucinação de pacotes — sugerir import helmet from 'helmet' quando helmet não está no seu package.json
  2. Alucinação de métodos — chamar prisma.user.findFirstOrThrow() quando você está no Prisma v3, onde esse método ainda não existe
  3. Pacotes fantasmas — inventar nomes de pacotes como express-mongoose ou react-query-utils que não existem no npm, que agentes mal-intencionados podem registrar como typosquats

Todos os três parecem código válido. Todos os três falham em tempo de execução — ou pior, instalam malware. O ctxai os detecta no momento da sugestão.


Como funciona

O ctxai expõe quatro ferramentas MCP que um cliente LLM (Claude Desktop, Cursor, Kiro, etc.) chama automaticamente:

get_project_context   →  scan project       →  return version fingerprint
validate_suggestion   →  check code         →  return hallucination warnings
check_package_safety  →  check new packages →  return safety issues
get_package_docs      →  fetch registry     →  return real API info

Ferramenta 1 — get_project_context

Escaneia a raiz do seu projeto e retorna uma impressão digital estruturada de cada pacote instalado e sua versão exata:

node: express@4.18.2
node: @prisma/client@3.15.2
python: fastapi@0.100.0
python: requests@2.31.0

Esta impressão digital é injetada no contexto do LLM antes de cada resposta, restringindo-o a sugerir apenas APIs que existem nas suas versões instaladas. Os resultados são armazenados em cache por 5 minutos, para que chamadas repetidas dentro de uma sessão sejam instantâneas.

Ferramenta 2 — validate_suggestion

Recebe o código gerado por IA e a impressão digital da Ferramenta 1, e executa três camadas de validação:

CamadaO que verificaTipo de aviso
1Todo pacote importado está nas suas dependências?MISSING_PACKAGE
2Toda chamada de método existe na sua versão instalada?HALLUCINATED_METHOD
3Qual é a alternativa real mais próxima?Sugestão no aviso

Retorna saída legível com o identificador exato do problema, gravidade e um comando de instalação corrigido ou sugestão de método.

Exemplo de saída:

⚠️  Found 1 issue in the suggested code:

🔴 [Missing package] 'helmet' is not listed in your project dependencies.
   → Run 'npm install helmet' to add it, or check if the package name has changed.

The code above cannot run as-is. Fix the missing packages before using it.

Ferramenta 3 — check_package_safety

Verifica cada pacote novo que a IA sugere instalar contra três camadas de segurança:

CamadaO que verificaTipo de problema
1Este pacote existe no npm / PyPI?PHANTOM_PACKAGE
2É suspeitamente semelhante a um pacote popular?LIKELY_TYPOSQUAT / LIKELY_CONFLATION
3É muito novo, não tem repositório ou tem pouquíssimas versões?LOW_TRUST_PACKAGE

Pacotes já presentes na sua impressão digital são ignorados — você já tomou essa decisão de confiança.

Exemplo de saída:

🚨 Found 1 critical issue across 1 new package.

📦 expres
   🚨 [Likely typosquat] 'expres' exists on the registry but is suspiciously
      similar to 'express' (edit distance: 1). This is a known typosquatting pattern.
      → Verify you meant 'express'. If you intentionally want 'expres', inspect
        its source code and maintainers before installing.

🛑 Do NOT install the flagged packages without manual verification.

Ferramenta 4 — get_package_docs

Busca metadados ao vivo do npm ou PyPI para uma versão específica de pacote. Usada pelo LLM para se autocorrigir após uma alucinação ser detectada — encontra o nome correto do método para a versão que você realmente tem instalada.


Arquitetura

ctxai/
├── src/
│   ├── index.ts                      # MCP server — registers all 4 tools
│   ├── formatters.ts                 # Converts typed results → readable MCP strings
│   ├── tools/
│   │   ├── getProjectContext.ts      # Tool 1: scan project + build fingerprint
│   │   ├── validateSuggestion.ts     # Tool 2: 3-layer hallucination validator
│   │   └── getPackageDocs.ts         # Tool 4: live registry metadata
│   ├── utils/
│   │   ├── checkPackageSafety.ts     # Tool 3: phantom/typosquat/trust checker
│   │   ├── registryClient.ts         # Typed npm + PyPI registry clients
│   │   ├── typosquatDetector.ts      # Levenshtein-based typosquat detection
│   │   ├── fuzzy.ts                  # Closest-match suggestions
│   │   ├── npmRegistry.ts            # npm metadata client (used by getPackageDocs)
│   │   └── pypiRegistry.ts           # PyPI metadata client (used by getPackageDocs)
│   ├── parser/
│   │   ├── responseParser.ts         # Extracts imports + method calls from code
│   │   └── fingerprintBuilder.ts     # Formats detected packages into fingerprint
│   ├── detectors/
│   │   ├── index.ts                  # Orchestrates Node + Python detection
│   │   ├── node.ts                   # Reads package.json + TypeScript API surface
│   │   └── python.ts                 # Reads requirements.txt + Python API surface
│   └── cache/
│       └── sessionCache.ts           # In-memory TTL cache (5 min)
└── benchmark/
    ├── run.ts                        # Benchmark runner with hallucination metrics
    └── prompts/                      # 28 test cases (JSON)

Instalação

Pré-requisitos

  • Node.js 18+
  • TypeScript 5+
  • Python 3 (opcional, para validação de projetos Python)

Build

cd ctxai
npm install
npm run build

Executar

npm start
# or in dev mode (no build step)
npm run dev

O servidor se comunica via stdio, que é o transporte MCP padrão.


Configuração do cliente MCP

Kiro

Adicione ao .kiro/settings/mcp.json no seu workspace:

{
  "mcpServers": {
    "ctxai": {
      "command": "node",
      "args": ["/absolute/path/to/ctxai/build/index.js"],
      "disabled": false,
      "autoApprove": [
        "get_project_context",
        "validate_suggestion",
        "check_package_safety",
        "get_package_docs"
      ]
    }
  }
}

Usuários de Windows + fnm/nvm: node pode não ser resolvido quando o Kiro inicia o servidor fora da sua sessão de shell. Use o caminho completo para node.exe:

"command": "C:\\Users\\YOU\\AppData\\Roaming\\fnm\\node-versions\\v20.0.0\\installation\\node.exe"

Encontre seu caminho com: Get-Command node | Select-Object -ExpandProperty Source (PowerShell)

Claude Desktop

Edite ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) ou %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "ctxai": {
      "command": "node",
      "args": ["/absolute/path/to/ctxai/build/index.js"]
    }
  }
}

Cursor / VS Code (Cline)

Edite seu cline_mcp_settings.json:

{
  "mcpServers": {
    "ctxai": {
      "command": "node",
      "args": ["/absolute/path/to/ctxai/build/index.js"],
      "disabled": false,
      "alwaysAllow": []
    }
  }
}

Após adicionar a configuração, recarregue/reconecte os servidores MCP pela paleta de comandos. Você deve ver o ctxai com 4 ferramentas listadas.


Testando a integração

1. Teste de fumaça — o servidor inicia?

npm run build
node build/index.js
# Expected: ctxai MCP server v0.1.0 running on stdio

2. Chamadas manuais de ferramentas via stdio

Teste cada ferramenta enviando JSON diretamente ao servidor:

Ferramenta 1 — escaneie seu projeto:

echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_project_context","arguments":{"path":"/your/project/path"}}}' \
  | node build/index.js

Ferramenta 2 — detecte um pacote ausente:

echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"validate_suggestion","arguments":{"code":"import helmet from \"helmet\";","contextFingerprint":"node: express@4.18.2"}}}' \
  | node build/index.js
# Expected: 🔴 [Missing package] 'helmet' is not listed in your project dependencies.

Ferramenta 3 — detecte um typosquat:

echo '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"check_package_safety","arguments":{"code":"import expres from \"expres\";","contextFingerprint":"node: express@4.18.2"}}}' \
  | node build/index.js
# Expected: 🚨 [Likely typosquat] 'expres' is suspiciously similar to 'express'

Ferramenta 4 — busque documentação ao vivo:

echo '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"get_package_docs","arguments":{"packageName":"express","version":"4.18.2","registry":"npm"}}}' \
  | node build/index.js

3. Execute o benchmark

npm run benchmark

Saída esperada:

════════════════════════════════════════════════════════════
  HALLUCINATION REDUCTION METRICS
════════════════════════════════════════════════════════════

  Benchmark accuracy     100.0%  (28/28 tests match expected)

  Detection rate         100.0%
  False positives            0
  Precision              100.0%
  F1 Score               100.0%

Benchmark

O ctxai acompanha 28 casos de teste cobrindo todos os cenários de validação. Os resultados incluem métricas de redução de alucinação — taxa de detecção, precisão e pontuação F1 — para que você possa medir o impacto de qualquer alteração.

O que o benchmark cobre

CategoriaPromptsO que é testado
Caminho feliz11Código válido contra impressão digital correta — zero falsos positivos
Pacotes Node6Imports npm ausentes, únicos e múltiplos
Pacotes Python5Pacotes pip ausentes, normalização hífen/sublinhado, correção de nome pip
Alucinação de método2Métodos que não existem na versão instalada (via superfície de API simulada)
Multi-pacote2Testes de estresse com 3–5 pacotes alucinados de uma vez
Casos extremos2Impressão digital vazia, blocos de prosa+código

Adicionando um caso de teste

Crie um arquivo JSON em benchmark/prompts/:

{
  "name": "My Test Case",
  "projectFingerprint": "node: express@4.18.2",
  "aiGeneratedCode": "import helmet from 'helmet';\nconst app = require('express')();",
  "expectedViolations": 1
}

Para testes de alucinação de método, injete uma superfície de API simulada para que o teste não exija node_modules real:

{
  "name": "Prisma - Method Hallucination",
  "projectFingerprint": "node: @prisma/client@3.15.2",
  "aiGeneratedCode": "const prisma = new PrismaClient();\nawait prisma.user.findFirstOrThrow({ where: { id: 1 } });",
  "apiSurfaceOverrides": {
    "@prisma/client": ["findFirst", "findMany", "create", "update", "delete"]
  },
  "expectedViolations": 1,
  "_note": "findFirstOrThrow was added in Prisma v4 — should be caught on v3"
}

Campos:

CampoObrigatórioDescrição
nameNome legível do teste
projectFingerprintPacotes instalados simulados (source: name@version por linha)
aiGeneratedCodeO código gerado por IA a validar
expectedViolationsNúmero exato de avisos esperados
apiSurfaceOverridesSuperfície de API simulada para verificações de método (ignora node_modules)
_noteDocumentação interna, ignorada pelo executor

Tipos de avisos e problemas

Avisos validate_suggestion

interface ValidationWarning {
  type: "MISSING_PACKAGE" | "HALLUCINATED_METHOD" | "UNKNOWN_PACKAGE";
  severity: "error" | "warning" | "info";
  message: string;           // Human-readable description
  suggestion: string;        // Correct install command or method name
  offender: string;          // The exact identifier that triggered the warning
  packageName?: string;      // Package context (HALLUCINATED_METHOD only)
  installedVersion?: string; // Installed version (HALLUCINATED_METHOD only)
}

Problemas check_package_safety

interface SafetyIssue {
  type: "PHANTOM_PACKAGE" | "LIKELY_TYPOSQUAT" | "LIKELY_CONFLATION"
      | "LOW_TRUST_PACKAGE" | "SECURITY_HOLD";
  severity: "critical" | "warning" | "info";
  packageName: string;
  ecosystem: "node" | "python";
  message: string;
  suggestion: string;
  meta?: {
    similarTo?: string;      // The popular package it resembles
    editDistance?: number;   // Levenshtein distance to the popular package
    ageInDays?: number;      // How old the package is
    versionCount?: number;   // How many versions it has
    hasRepository?: boolean; // Whether it has a repo link
  }
}

Linguagens e ecossistemas suportados

LinguagemArquivo de pacotesRegistroValidação de método
JavaScript / TypeScriptpackage.jsonnpmVia definições de tipo .d.ts
Pythonrequirements.txt, pyproject.tomlPyPIVia introspecção dir()

Mapeamento import Python → nome pip

O ctxai sabe que nomes de import em Python frequentemente diferem dos nomes de pacotes pip e gera comandos de instalação corretos:

Importpip install
from rest_framework import ...pip install djangorestframework
from PIL import Imagepip install Pillow
import cv2pip install opencv-python
from sklearn import ...pip install scikit-learn
import jwtpip install PyJWT
import yamlpip install PyYAML
from bs4 import ...pip install beautifulsoup4

Mais de 80 mapeamentos estão embutidos. Veja src/tools/validateSuggestion.tsPYTHON_IMPORT_TO_PIP para a lista completa.


Decisões de design

Por que quatro ferramentas em vez de uma? Cada ferramenta tem uma condição de acionamento distinta. get_project_context roda uma vez por sessão. validate_suggestion roda em cada resposta de código. check_package_safety roda apenas quando novos pacotes são sugeridos. get_package_docs roda sob demanda para autocorreção. Dividi-las permite que o LLM chame apenas o que for necessário.

Por que MCP? MCP é o padrão emergente para dar aos LLMs acesso estruturado a ferramentas locais. Qualquer cliente compatível com MCP obtém o ctxai gratuitamente, sem integrações personalizadas.

Por que uma string de impressão digital em vez de JSON? O formato de impressão digital (node: express@4.18.2) é compacto, legível e eficiente em tokens. Ele cabe no contexto do LLM sem desperdiçar tokens com sintaxe JSON.

Por que não usar apenas os dados de treinamento do LLM? Os dados de treinamento são congelados em uma data de corte e não sabem o que está instalado no seu projeto. O ctxai lê seu node_modules e requirements.txt reais em tempo de execução.

Por que preferir falsos negativos a falsos positivos? Se o ctxai não conseguir determinar se um método existe (sem definições de tipo, sem stubs), ele permanece em silêncio em vez de avisar. Uma alucinação perdida é menos disruptiva do que um alarme falso em código válido.


Contribuindo

O benchmark é o melhor lugar para começar. Se você encontrar um caso em que o ctxai produza um falso positivo ou perca uma alucinação:

  1. Adicione um JSON de prompt em benchmark/prompts/ que reproduza o problema
  2. Defina expectedViolations para o comportamento correto esperado
  3. Execute npm run benchmark — se falhar, o bug está confirmado
  4. Corrija o validador e verifique se o benchmark fica verde

Licença

MIT