AIQUAA QA Core
Núcleo de QA reutilizável para servidores MCP, fornecendo análise de requisitos, rastreabilidade, cobertura, planejamento de mudanças, correções seguras e integrações com GitHub.
Documentação
@aiquaa/mcp-qa-core
Núcleo reutilizável de arquitetura hexagonal compartilhado pelos servidores MCP de QA da AIQUAA (aiquaa-playwright-mcp-server, aiquaa-api-quality-mcp-server, aiquaa-performance-mcp-server e futuros MCPs de Hurl/Pact/k6/Selenium/Appium/REST Assured).
Ele centraliza as partes de um servidor MCP de QA da AIQUAA que não têm relação com uma ferramenta de teste específica: normalização de requisitos, rastreabilidade, agregação de cobertura, planejamento de mudanças, geração de patches, políticas de segurança e adaptadores para GitHub, AIQUAA, CodeGraph e Engram. Ele nunca importa Playwright, Postman, Newman, JMeter, Hurl, k6, Selenium ou Appium.
Propósito e limites
O núcleo faz: normalizar requisitos e critérios de aceitação; modelar regras de negócio; construir e consultar um grafo de rastreabilidade requisito→evidência; agregar cobertura de avaliadores específicos de ferramentas; planejar mudanças (criar/estender/modificar/manter/descontinuar/excluir/bloquear) sob regras de segurança; gerar/validar/aplicar diffs unificados; verificar segredos e aplicar políticas de caminho/gravação/execução/host; comunicar-se com GitHub, AIQUAA, CodeGraph e Engram por meio de portas; e moldar respostas de ferramentas MCP de forma consistente.
O núcleo não faz: gerar especificações Playwright ou Gherkin, construir coleções Postman, executar Newman ou JMeter, abrir um navegador, interpretar localizadores, calcular percentis JTL ou gerar XML JMX. Isso permanece no MCP consumidor, conectado por meio do contrato QaToolPlugin.
Consulte ARCHITECTURE.md para a divisão em camadas e MIGRATION.md para o inventário do que saiu de aiquaa-playwright-mcp-server e por quê.
Instalação
npm install @aiquaa/mcp-qa-core zod
Node.js 20+ é necessário. O pacote é somente ESM, tree-shakeable, sem efeitos colaterais na importação e inclui exports de subcaminho para que você nunca precise acessar dist/:
import { createQaCore } from "@aiquaa/mcp-qa-core";
import { CoverageEngine } from "@aiquaa/mcp-qa-core/coverage";
import { TraceabilityEngine } from "@aiquaa/mcp-qa-core/traceability";
import { ChangePlanner } from "@aiquaa/mcp-qa-core/changes";
import { generateUnifiedDiff, PatchValidator } from "@aiquaa/mcp-qa-core/patches";
import { LocalGitRepositoryAdapter } from "@aiquaa/mcp-qa-core/repository";
import { OctokitPullRequestAdapter, GitHubRepositoryAdapter } from "@aiquaa/mcp-qa-core/github";
import { HttpAiquaaAdapter } from "@aiquaa/mcp-qa-core/aiquaa";
import { CodeGraphCliAdapter } from "@aiquaa/mcp-qa-core/codegraph";
import { EngramCliAdapter } from "@aiquaa/mcp-qa-core/memory";
import { SecretScanner, PathPolicy } from "@aiquaa/mcp-qa-core/security";
import { createToolSuccess, toMcpStructuredContent } from "@aiquaa/mcp-qa-core/mcp";
import { createQaCoreTestHarness } from "@aiquaa/mcp-qa-core/testing";
Início rápido
import { createQaCore, createDefaultConfig } from "@aiquaa/mcp-qa-core";
const qaCore = createQaCore({ config: createDefaultConfig() });
const [requirement] = qaCore.requirements.normalize([
{ externalId: "REQ-42", title: "Login", description: "User can log in with email and password." },
]);
const context = qaCore.createContext({ operationId: "generate-123", dryRun: true });
Contrato de plugin
Todo MCP consumidor implementa QaToolPlugin<TInput, TAnalysis, TArtifact> — a costura que mantém a lógica específica de ferramenta fora do núcleo:
import type { QaToolPlugin, CoverageReport, ChangePlan } from "@aiquaa/mcp-qa-core";
export const examplePlugin: QaToolPlugin<ExampleInput, ExampleAnalysis, ExampleArtifact> = {
name: "example",
version: "1.0.0",
canHandle(input) {
return input.type === "example";
},
async analyze(input, context) {
return { source: input, findings: [] };
},
async evaluateCoverage(analysis, context): Promise<CoverageReport> {
return { items: [], summary: { total: 0, covered: 0, partiallyCovered: 0, uncovered: 0, blocked: 0, outdated: 0, percentage: 0 } };
},
async planChanges(analysis, coverage, context): Promise<ChangePlan> {
return { strategy: "keep", changes: [], assumptions: [], warnings: [], blockedReasons: [] };
},
async generateArtifacts() {
return [];
},
};
const result = await qaCore.runPlugin(examplePlugin, input);
// result.analysis, result.coverage, result.changePlan, result.artifacts
Consulte MIGRATION.md para um exemplo completo no formato Playwright.
Cobertura
O núcleo nunca decide o que "coberto" significa para uma ferramenta específica — ele agrega os avaliadores que você fornece:
import { CoverageEngine } from "@aiquaa/mcp-qa-core/coverage";
const engine = new CoverageEngine([myToolSpecificEvaluator]);
const report = await engine.evaluate(myContext);
// report.summary.percentage, report.items[]
Rastreabilidade
import { TraceabilityEngine } from "@aiquaa/mcp-qa-core/traceability";
const engine = new TraceabilityEngine();
const graph = engine.build({ requirements, scenarios, artifacts, executionResults, evidence });
engine.findUncoveredCriteria(graph, allCriteria);
engine.findOrphanArtifacts(graph, allArtifacts);
engine.findBrokenLinks(graph, { requirement: new Set(requirementIds) });
Planejamento de mudanças e patches
import { ChangePlanner } from "@aiquaa/mcp-qa-core/changes";
import { PatchGenerator, PatchValidator, PatchApplier } from "@aiquaa/mcp-qa-core/patches";
const plan = new ChangePlanner().plan({
candidates: [{ targetPath: "tests/login.spec.ts", decision: "create", reason: "covers REQ-42", requirementIds: ["req-42"], businessRuleIds: [], risk: "low" }],
existingArtifactPaths: [],
requestedScope: ["tests"],
});
// plan.strategy, plan.changes[].decision ("create" | "block" | …), plan.blockedReasons
ChangePlanner recusa artefatos duplicados, sobrescritas cegas (modify/delete sem hasReadExistingContent: true), caminhos fora do escopo, exclusões injustificadas e mudanças sem evidência de requisito/regra de negócio — ele retorna "block" em vez disso.
Repositório e pull requests
import { GitHubRepositoryAdapter, OctokitPullRequestAdapter, runPullRequestFlow } from "@aiquaa/mcp-qa-core/github";
const pullRequests = new OctokitPullRequestAdapter({ octokit });
const result = await runPullRequestFlow(pullRequests, {
repository: { owner: "aiquaa", repo: "demo" },
baseBranch: "main",
branchName: "feat/login-coverage",
commitMessage: "test: add login coverage",
title: "Add login coverage",
body: "Generated by the QA MCP.",
files: changePlanFiles,
// dryRun and draft both default to true — nothing is written until you opt out.
});
Segurança
import { SecretScanner, PathPolicy, RepositoryWritePolicy } from "@aiquaa/mcp-qa-core/security";
const scanner = new SecretScanner();
scanner.scanText(fileContent); // → SecretFinding[], never the full secret value
const pathPolicy = new PathPolicy({ allowedRoots: [projectRoot] });
pathPolicy.assertSafe(candidatePath); // throws UnsafePathError on traversal, .git, node_modules, or escape
RepositoryWritePolicy, ExecutionPolicy e HostPolicy aplicam o mesmo padrão "explícito e limitado" a gravações no repositório, execução de processos externos e chamadas HTTP de saída, respectivamente.
Utilitários de teste
import { createQaCoreTestHarness } from "@aiquaa/mcp-qa-core/testing";
const harness = createQaCoreTestHarness({ files: { "src/controller.ts": "..." } });
const result = await harness.runPlugin(myPlugin, input);
expect(result.changePlan.strategy).toBe("extend");
expect(harness.pullRequests.commits).toHaveLength(0); // dryRun by default
O harness conecta InMemoryFileSystemAdapter, InMemoryRepositoryAdapter, InMemoryPullRequestAdapter, InMemoryAiquaaAdapter, InMemoryProjectMemoryAdapter e TestLoggerAdapter a um QaCore real — sem rede, sem disco, sem processo externo.
Respostas MCP
import { createToolSuccess, toMcpStructuredContent } from "@aiquaa/mcp-qa-core/mcp";
const response = createToolSuccess({ operationId: context.operationId, summary: "Generated 3 artifacts", data: artifacts });
return toMcpStructuredContent(response, "files"); // { content: [...], isError: false, structuredContent: artifacts }
Configuração
import { loadConfigFromEnvironment, validateConfig } from "@aiquaa/mcp-qa-core";
const config = loadConfigFromEnvironment(process.env);
const validated = validateConfig(config); // Result<QaCoreConfig, ConfigurationError>
Variáveis de ambiente reconhecidas: GITHUB_TOKEN, GITHUB_API_URL, AIQUAA_API_BASE_URL, AIQUAA_ACCESS_TOKEN, CODEGRAPH_BIN, CODEGRAPH_ALLOWED_ROOTS, ENGRAM_BIN, ENGRAM_PROJECT_PREFIX, QA_CORE_ALLOWED_ROOTS, QA_CORE_MAX_FILE_SIZE, QA_CORE_LOG_LEVEL, QA_CORE_DRY_RUN. O código de domínio e de aplicação nunca lê process.env diretamente — apenas loadConfigFromEnvironment o faz, e somente quando você o chama.
Desenvolvimento
npm install
npm run check # typecheck + lint + test:coverage + build
Consulte CONTRIBUTING.md para o fluxo de trabalho completo e SECURITY.md para o processo de relato de vulnerabilidades.
Licença
MIT — consulte LICENSE.