@taiga-ai/mcp-server 0.26.0-dev.486 → 0.26.0-dev.487

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +74 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1385,6 +1385,49 @@ z.object({
1385
1385
  ...e.failingChecks === void 0 ? {} : { failingChecks: e.failingChecks },
1386
1386
  ...e.blockers === void 0 ? {} : { blockers: e.blockers }
1387
1387
  }));
1388
+ /**
1389
+ * What a run's hardening came to, coverage included (PRO-2829). `passed`
1390
+ * counted only blocking findings, so a run whose scanners never ran said
1391
+ * "passed" all the same.
1392
+ *
1393
+ * - `failed`: blocking findings remain (the gaps are still listed).
1394
+ * - `not_scanned`: no scanner that applies to the repository ran to the end.
1395
+ * - `passed_with_gaps`: nothing blocks, but some scanner did not run.
1396
+ * - `passed`: nothing blocks and every scanner that applies ran.
1397
+ */
1398
+ const HARDENING_VERDICTS = [
1399
+ "passed",
1400
+ "passed_with_gaps",
1401
+ "not_scanned",
1402
+ "failed"
1403
+ ];
1404
+ /**
1405
+ * The verdict and the gaps of a hardening result (PRO-2829), for every reader
1406
+ * (the run read, the web's run page, the MCP), from what the agent reported.
1407
+ *
1408
+ * A gap is a scanner that applies and did not run: `not_run` (the scan never
1409
+ * reached it), `skipped` (its tool was missing), or `failed` with a reason,
1410
+ * which is a scanner that could not finish. A `failed` scanner without one
1411
+ * found something (tflint fails on a MAJOR that blocks nothing, and rows from
1412
+ * before `scannerReasons` used `failed` for "had findings"), so it ran and is
1413
+ * no gap. A scanner listed in `notApplicable` is no gap; on a result from
1414
+ * before that list, a skipped tflint is read the same way, as the only reason
1415
+ * the agent ever skipped it was a repository with no Terraform.
1416
+ */
1417
+ function hardeningCoverage(result) {
1418
+ const notApplicable = new Set(result.notApplicable ?? []);
1419
+ const applies = Object.entries(result.scanners ?? {}).filter(([scanner, status]) => !notApplicable.has(scanner) && !(result.notApplicable === void 0 && scanner === "tflint" && status === "skipped"));
1420
+ const reasons = result.scannerReasons ?? {};
1421
+ const gaps = applies.filter(([scanner, status]) => status === "not_run" || status === "skipped" || status === "failed" && scanner in reasons).map(([scanner, status]) => ({
1422
+ scanner,
1423
+ status,
1424
+ reason: reasons[scanner] ?? null
1425
+ }));
1426
+ return {
1427
+ verdict: !result.passed ? "failed" : gaps.length === applies.length ? "not_scanned" : gaps.length > 0 ? "passed_with_gaps" : "passed",
1428
+ gaps
1429
+ };
1430
+ }
1388
1431
  [...BUILD_RUN_ACTIVE_STATUSES.filter((s) => s !== "reviewing")];
1389
1432
  //#endregion
1390
1433
  //#region ../../packages/types/dist/agents.js
@@ -3942,6 +3985,34 @@ function hardeningSaid(blocking, named) {
3942
3985
  const recorded = named.length > shown.length ? `, ${named.length} recorded in taiga_get_run` : "";
3943
3986
  return `${count}: ${shown.join("; ")}${rest > 0 ? `; and ${rest} more${recorded}` : ""}.`;
3944
3987
  }
3988
+ /** How each hardening verdict reads (PRO-2829): "passed" only when every scanner that applies ran. */
3989
+ const VERDICT_SAID = {
3990
+ passed: "Hardening passed: every scanner that applies ran and nothing blocks",
3991
+ passed_with_gaps: "Hardening passed, but not every scanner ran, so its checks are missing from the result",
3992
+ not_scanned: "No security scanner ran in hardening: \"passed\" only means no blocking finding was recorded",
3993
+ failed: "Hardening failed: blocking findings remain"
3994
+ };
3995
+ /**
3996
+ * What a run's hardening came to, coverage included (PRO-2829), from the
3997
+ * verdict the API reads off it, or read here from an API from before that.
3998
+ * Null without a hardening result.
3999
+ */
4000
+ function scanSaid(hardening) {
4001
+ if (!hardening || typeof hardening !== "object") return null;
4002
+ const h = hardening;
4003
+ const coverage = typeof h.verdict === "string" && HARDENING_VERDICTS.includes(h.verdict) && Array.isArray(h.gaps) ? {
4004
+ verdict: h.verdict,
4005
+ gaps: h.gaps
4006
+ } : typeof h.passed === "boolean" && h.scanners ? hardeningCoverage(h) : null;
4007
+ if (!coverage) return null;
4008
+ const gaps = coverage.gaps.map((gap) => `${gap.scanner}${gap.status === "failed" ? " did not finish" : ""}${gap.reason ? ` (${gap.reason})` : ""}`);
4009
+ const missing = gaps.length > 0 ? `; did not run: ${gaps.join(", ")}` : "";
4010
+ return {
4011
+ verdict: coverage.verdict,
4012
+ gaps: gaps.length,
4013
+ said: `${VERDICT_SAID[coverage.verdict]}${missing}.`
4014
+ };
4015
+ }
3945
4016
  /**
3946
4017
  * What each failedReason means for the next move (PRO-2800): reviewers met dispatch_rejected and agent_reported as
3947
4018
  * bare enums. As @taiga/types documents them.
@@ -3968,9 +4039,11 @@ function readableRun(run, opts = {}) {
3968
4039
  const hardening = run["hardeningResult"];
3969
4040
  const blocking = typeof hardening?.blockingFindings === "number" ? hardening.blockingFindings : 0;
3970
4041
  const named = Array.isArray(hardening?.blocking) ? hardening.blocking : [];
4042
+ const scan = scanSaid(hardening);
3971
4043
  return {
3972
4044
  ...rest,
3973
4045
  ...means ? { failedReasonMeans: means } : {},
4046
+ ...scan && scan.gaps + (scan.verdict === "not_scanned" ? 1 : 0) > 0 ? { scanCoverage: scan.said } : {},
3974
4047
  ...blocking > 0 ? { hardeningNote: hardeningSaid(blocking, named) } : {},
3975
4048
  ...opts.row || error === void 0 ? {} : { error },
3976
4049
  ...opts.row || reviewEscalation === void 0 ? {} : { reviewEscalation },
@@ -4172,7 +4245,7 @@ function registerBuildTools(server, client) {
4172
4245
  }));
4173
4246
  server.registerTool("taiga_get_run", {
4174
4247
  title: "Get run",
4175
- description: "Read one run with its steps: outcome, disposition, pull request, hardening, and per step its title, status, commit, files changed, verification and headline (first paragraph of the agent's report). verification is Taiga's own check after the step (tests, lint, build), so a docs-only step can fail on the repository's failing tests (verificationNote says when one failed throughout). A failed or stopped run has stoppedIn, lastProgressAt and failedReasonMeans (what its failedReason means). full: true adds complete instructions and reports. actions: resume and retry_from_failed_step are taiga_resume_initiative; webOnlyActions (start_over) are in the web; to stop it, move the initiative out of Build. A maintenance run has no initiative and is retried from its finding.",
4248
+ description: "Read one run with its steps: outcome, disposition, pull request, hardening (scanCoverage names the security scanners that did not run, and why; hardeningResult.verdict is passed only when every one ran), and per step its title, status, commit, files changed, verification and headline (first paragraph of the agent's report). verification is Taiga's own check after the step (tests, lint, build), so a docs-only step can fail on the repository's failing tests (verificationNote says when one failed throughout). A failed or stopped run has stoppedIn, lastProgressAt and failedReasonMeans (what its failedReason means). full: true adds complete instructions and reports. actions: resume and retry_from_failed_step are taiga_resume_initiative; webOnlyActions (start_over) are in the web; to stop it, move the initiative out of Build. A maintenance run has no initiative and is retried from its finding.",
4176
4249
  inputSchema: z.object({
4177
4250
  runId,
4178
4251
  full: z.boolean().optional().describe("Carry each step's complete instructions and report (default false: a summary)")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taiga-ai/mcp-server",
3
- "version": "0.26.0-dev.486",
3
+ "version": "0.26.0-dev.487",
4
4
  "description": "Taiga for AI coding assistants: read products, policies, initiatives and runs, and operate the line, over MCP",
5
5
  "mcpName": "ga.tai/mcp-server",
6
6
  "type": "module",