{"_id":"@aiquaa/mcp-qa-core","name":"@aiquaa/mcp-qa-core","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@aiquaa/mcp-qa-core","version":"0.1.0","description":"Reusable hexagonal-architecture core for AIQUAA QA MCP servers: requirements, traceability, coverage, change planning, patches, security and GitHub/AIQUAA/CodeGraph/Engram adapters.","keywords":["mcp","model-context-protocol","qa","testing","traceability","coverage","aiquaa","hexagonal-architecture"],"repository":{"type":"git","url":"git+https://github.com/stevenayal/aiquaa-mcp-qa-core.git"},"homepage":"https://github.com/stevenayal/aiquaa-mcp-qa-core#readme","bugs":{"url":"https://github.com/stevenayal/aiquaa-mcp-qa-core/issues"},"license":"MIT","author":{"name":"AIQUAA"},"type":"module","sideEffects":false,"engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./requirements":{"types":"./dist/requirements/index.d.ts","import":"./dist/requirements/index.js"},"./coverage":{"types":"./dist/coverage/index.d.ts","import":"./dist/coverage/index.js"},"./traceability":{"types":"./dist/traceability/index.d.ts","import":"./dist/traceability/index.js"},"./changes":{"types":"./dist/changes/index.d.ts","import":"./dist/changes/index.js"},"./patches":{"types":"./dist/patches/index.d.ts","import":"./dist/patches/index.js"},"./repository":{"types":"./dist/repository/index.d.ts","import":"./dist/repository/index.js"},"./github":{"types":"./dist/github/index.d.ts","import":"./dist/github/index.js"},"./aiquaa":{"types":"./dist/aiquaa/index.d.ts","import":"./dist/aiquaa/index.js"},"./codegraph":{"types":"./dist/codegraph/index.d.ts","import":"./dist/codegraph/index.js"},"./memory":{"types":"./dist/memory/index.d.ts","import":"./dist/memory/index.js"},"./security":{"types":"./dist/security/index.d.ts","import":"./dist/security/index.js"},"./mcp":{"types":"./dist/mcp/index.d.ts","import":"./dist/mcp/index.js"},"./testing":{"types":"./dist/testing/index.d.ts","import":"./dist/testing/index.js"},"./package.json":"./package.json"},"scripts":{"build":"tsup","dev":"tsup --watch","typecheck":"tsc -p tsconfig.typecheck.json","lint":"eslint src --ext .ts","lint:fix":"eslint src --ext .ts --fix","format":"prettier --write \"src/**/*.ts\"","format:check":"prettier --check \"src/**/*.ts\"","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","check":"npm run typecheck && npm run lint && npm run test:coverage && npm run build","prepack":"npm run build"},"dependencies":{"zod":"^3.25.76"},"peerDependencies":{"@octokit/rest":">=20.0.0"},"peerDependenciesMeta":{"@octokit/rest":{"optional":true}},"devDependencies":{"@octokit/rest":"^21.0.2","@types/node":"^22.19.19","@typescript-eslint/eslint-plugin":"^8.18.0","@typescript-eslint/parser":"^8.18.0","@vitest/coverage-v8":"^2.1.8","eslint":"^8.57.1","prettier":"^3.4.2","tsup":"^8.3.5","typescript":"^5.9.3","vitest":"^2.1.8"},"publishConfig":{"access":"public","provenance":true},"gitHead":"e33090484be335bf796b68c837c8418c81c42ce3","_id":"@aiquaa/mcp-qa-core@0.1.0","_nodeVersion":"24.18.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-UnjpaYC/x7Glta2x5/Yf2cd5QcEuSJ5soH0HGbY3R2mkAqKHjN8uRfEW17S8N5L1ZF4PQlBI0mEDGjzHkD27aw==","shasum":"505728a866579676c880c7fd2de1e8a740c06cfa","tarball":"https://registry.npmjs.org/@aiquaa/mcp-qa-core/-/mcp-qa-core-0.1.0.tgz","fileCount":98,"unpackedSize":442727,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCID5/n+CLhsQAICxIUP4XGps2XPdLHwCoZ0cOk/mlOViDAiEAlxCPgzTvKK9qnH0OOyliUZwA/O8p5ltTk2QoJumo+bg="}]},"_npmUser":{"name":"aiquaa","email":"admin@aiquaa.com"},"directories":{},"maintainers":[{"name":"aiquaa","email":"admin@aiquaa.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mcp-qa-core_0.1.0_1784690901932_0.4056325310753326"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-22T03:28:21.744Z","0.1.0":"2026-07-22T03:28:22.104Z","modified":"2026-07-22T03:28:22.269Z"},"maintainers":[{"name":"aiquaa","email":"admin@aiquaa.com"}],"description":"Reusable hexagonal-architecture core for AIQUAA QA MCP servers: requirements, traceability, coverage, change planning, patches, security and GitHub/AIQUAA/CodeGraph/Engram adapters.","homepage":"https://github.com/stevenayal/aiquaa-mcp-qa-core#readme","keywords":["mcp","model-context-protocol","qa","testing","traceability","coverage","aiquaa","hexagonal-architecture"],"repository":{"type":"git","url":"git+https://github.com/stevenayal/aiquaa-mcp-qa-core.git"},"author":{"name":"AIQUAA"},"bugs":{"url":"https://github.com/stevenayal/aiquaa-mcp-qa-core/issues"},"license":"MIT","readme":"# @aiquaa/mcp-qa-core\n\nReusable hexagonal-architecture core shared by AIQUAA's QA MCP servers ([`aiquaa-playwright-mcp-server`](https://github.com/stevenayal/aiquaa-playwright-mcp-server), `aiquaa-api-quality-mcp-server`, `aiquaa-performance-mcp-server`, and future Hurl/Pact/k6/Selenium/Appium/REST Assured MCPs).\n\nIt centralizes the parts of an AIQUAA QA MCP server that have nothing to do with a specific testing tool: requirement normalization, traceability, coverage aggregation, change planning, patch generation, security policies, and adapters for GitHub, AIQUAA, CodeGraph, and Engram. It never imports Playwright, Postman, Newman, JMeter, Hurl, k6, Selenium, or Appium.\n\n## Purpose and boundaries\n\n**The core does**: normalize requirements and acceptance criteria; model business rules; build and query a requirement→evidence traceability graph; aggregate coverage from tool-specific evaluators; plan changes (create/extend/modify/keep/deprecate/delete/block) under safety rules; generate/validate/apply unified diffs; scan for secrets and enforce path/write/execution/host policies; talk to GitHub, AIQUAA, CodeGraph, and Engram through ports; and shape MCP tool responses consistently.\n\n**The core does not**: generate Playwright specs or Gherkin, build Postman collections, run Newman or JMeter, open a browser, interpret locators, compute JTL percentiles, or generate JMX XML. Those stay in the consumer MCP, wired in through the [`QaToolPlugin`](#plugin-contract) contract.\n\nSee [ARCHITECTURE.md](ARCHITECTURE.md) for the layer breakdown and [MIGRATION.md](MIGRATION.md) for the inventory of what moved out of `aiquaa-playwright-mcp-server` and why.\n\n## Install\n\n```bash\nnpm install @aiquaa/mcp-qa-core zod\n```\n\nNode.js 20+ required. The package is ESM-only, tree-shakeable, side-effect-free on import, and ships subpath exports so you never need to reach into `dist/`:\n\n```typescript\nimport { createQaCore } from \"@aiquaa/mcp-qa-core\";\nimport { CoverageEngine } from \"@aiquaa/mcp-qa-core/coverage\";\nimport { TraceabilityEngine } from \"@aiquaa/mcp-qa-core/traceability\";\nimport { ChangePlanner } from \"@aiquaa/mcp-qa-core/changes\";\nimport { generateUnifiedDiff, PatchValidator } from \"@aiquaa/mcp-qa-core/patches\";\nimport { LocalGitRepositoryAdapter } from \"@aiquaa/mcp-qa-core/repository\";\nimport { OctokitPullRequestAdapter, GitHubRepositoryAdapter } from \"@aiquaa/mcp-qa-core/github\";\nimport { HttpAiquaaAdapter } from \"@aiquaa/mcp-qa-core/aiquaa\";\nimport { CodeGraphCliAdapter } from \"@aiquaa/mcp-qa-core/codegraph\";\nimport { EngramCliAdapter } from \"@aiquaa/mcp-qa-core/memory\";\nimport { SecretScanner, PathPolicy } from \"@aiquaa/mcp-qa-core/security\";\nimport { createToolSuccess, toMcpStructuredContent } from \"@aiquaa/mcp-qa-core/mcp\";\nimport { createQaCoreTestHarness } from \"@aiquaa/mcp-qa-core/testing\";\n```\n\n## Quick start\n\n```typescript\nimport { createQaCore, createDefaultConfig } from \"@aiquaa/mcp-qa-core\";\n\nconst qaCore = createQaCore({ config: createDefaultConfig() });\n\nconst [requirement] = qaCore.requirements.normalize([\n  { externalId: \"REQ-42\", title: \"Login\", description: \"User can log in with email and password.\" },\n]);\n\nconst context = qaCore.createContext({ operationId: \"generate-123\", dryRun: true });\n```\n\n## Plugin contract\n\nEvery consumer MCP implements `QaToolPlugin<TInput, TAnalysis, TArtifact>` — the seam that keeps tool-specific logic out of the core:\n\n```typescript\nimport type { QaToolPlugin, CoverageReport, ChangePlan } from \"@aiquaa/mcp-qa-core\";\n\nexport const examplePlugin: QaToolPlugin<ExampleInput, ExampleAnalysis, ExampleArtifact> = {\n  name: \"example\",\n  version: \"1.0.0\",\n\n  canHandle(input) {\n    return input.type === \"example\";\n  },\n\n  async analyze(input, context) {\n    return { source: input, findings: [] };\n  },\n\n  async evaluateCoverage(analysis, context): Promise<CoverageReport> {\n    return { items: [], summary: { total: 0, covered: 0, partiallyCovered: 0, uncovered: 0, blocked: 0, outdated: 0, percentage: 0 } };\n  },\n\n  async planChanges(analysis, coverage, context): Promise<ChangePlan> {\n    return { strategy: \"keep\", changes: [], assumptions: [], warnings: [], blockedReasons: [] };\n  },\n\n  async generateArtifacts() {\n    return [];\n  },\n};\n\nconst result = await qaCore.runPlugin(examplePlugin, input);\n// result.analysis, result.coverage, result.changePlan, result.artifacts\n```\n\nSee [MIGRATION.md](MIGRATION.md#2-worked-example-a-qatoolplugin-for-playwright) for a full Playwright-shaped example.\n\n## Coverage\n\nThe core never decides what \"covered\" means for a specific tool — it aggregates evaluators you supply:\n\n```typescript\nimport { CoverageEngine } from \"@aiquaa/mcp-qa-core/coverage\";\n\nconst engine = new CoverageEngine([myToolSpecificEvaluator]);\nconst report = await engine.evaluate(myContext);\n// report.summary.percentage, report.items[]\n```\n\n## Traceability\n\n```typescript\nimport { TraceabilityEngine } from \"@aiquaa/mcp-qa-core/traceability\";\n\nconst engine = new TraceabilityEngine();\nconst graph = engine.build({ requirements, scenarios, artifacts, executionResults, evidence });\n\nengine.findUncoveredCriteria(graph, allCriteria);\nengine.findOrphanArtifacts(graph, allArtifacts);\nengine.findBrokenLinks(graph, { requirement: new Set(requirementIds) });\n```\n\n## Change planning and patches\n\n```typescript\nimport { ChangePlanner } from \"@aiquaa/mcp-qa-core/changes\";\nimport { PatchGenerator, PatchValidator, PatchApplier } from \"@aiquaa/mcp-qa-core/patches\";\n\nconst plan = new ChangePlanner().plan({\n  candidates: [{ targetPath: \"tests/login.spec.ts\", decision: \"create\", reason: \"covers REQ-42\", requirementIds: [\"req-42\"], businessRuleIds: [], risk: \"low\" }],\n  existingArtifactPaths: [],\n  requestedScope: [\"tests\"],\n});\n// plan.strategy, plan.changes[].decision (\"create\" | \"block\" | …), plan.blockedReasons\n```\n\n`ChangePlanner` refuses duplicate artifacts, blind overwrites (`modify`/`delete` without `hasReadExistingContent: true`), out-of-scope paths, unjustified deletes, and changes with no requirement/business-rule evidence — it returns `\"block\"` instead.\n\n## Repository and pull requests\n\n```typescript\nimport { GitHubRepositoryAdapter, OctokitPullRequestAdapter, runPullRequestFlow } from \"@aiquaa/mcp-qa-core/github\";\n\nconst pullRequests = new OctokitPullRequestAdapter({ octokit });\nconst result = await runPullRequestFlow(pullRequests, {\n  repository: { owner: \"aiquaa\", repo: \"demo\" },\n  baseBranch: \"main\",\n  branchName: \"feat/login-coverage\",\n  commitMessage: \"test: add login coverage\",\n  title: \"Add login coverage\",\n  body: \"Generated by the QA MCP.\",\n  files: changePlanFiles,\n  // dryRun and draft both default to true — nothing is written until you opt out.\n});\n```\n\n## Security\n\n```typescript\nimport { SecretScanner, PathPolicy, RepositoryWritePolicy } from \"@aiquaa/mcp-qa-core/security\";\n\nconst scanner = new SecretScanner();\nscanner.scanText(fileContent); // → SecretFinding[], never the full secret value\n\nconst pathPolicy = new PathPolicy({ allowedRoots: [projectRoot] });\npathPolicy.assertSafe(candidatePath); // throws UnsafePathError on traversal, .git, node_modules, or escape\n```\n\n`RepositoryWritePolicy`, `ExecutionPolicy`, and `HostPolicy` apply the same \"explicit and bounded\" default to repository writes, external process execution, and outbound HTTP calls respectively.\n\n## Testing utilities\n\n```typescript\nimport { createQaCoreTestHarness } from \"@aiquaa/mcp-qa-core/testing\";\n\nconst harness = createQaCoreTestHarness({ files: { \"src/controller.ts\": \"...\" } });\nconst result = await harness.runPlugin(myPlugin, input);\n\nexpect(result.changePlan.strategy).toBe(\"extend\");\nexpect(harness.pullRequests.commits).toHaveLength(0); // dryRun by default\n```\n\nThe harness wires `InMemoryFileSystemAdapter`, `InMemoryRepositoryAdapter`, `InMemoryPullRequestAdapter`, `InMemoryAiquaaAdapter`, `InMemoryProjectMemoryAdapter`, and `TestLoggerAdapter` into a real `QaCore` — no network, no disk, no external process.\n\n## MCP responses\n\n```typescript\nimport { createToolSuccess, toMcpStructuredContent } from \"@aiquaa/mcp-qa-core/mcp\";\n\nconst response = createToolSuccess({ operationId: context.operationId, summary: \"Generated 3 artifacts\", data: artifacts });\nreturn toMcpStructuredContent(response, \"files\"); // { content: [...], isError: false, structuredContent: artifacts }\n```\n\n## Configuration\n\n```typescript\nimport { loadConfigFromEnvironment, validateConfig } from \"@aiquaa/mcp-qa-core\";\n\nconst config = loadConfigFromEnvironment(process.env);\nconst validated = validateConfig(config); // Result<QaCoreConfig, ConfigurationError>\n```\n\nRecognized environment variables: `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`. Domain and application code never reads `process.env` directly — only `loadConfigFromEnvironment` does, and only when you call it.\n\n## Development\n\n```bash\nnpm install\nnpm run check   # typecheck + lint + test:coverage + build\n```\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for the full workflow and [SECURITY.md](SECURITY.md) for the vulnerability-reporting process.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","readmeFilename":"README.md","_rev":"1-4ea68607461905a2a20de207047491d6"}