@taiga-ai/mcp-server 0.25.0-dev.469 → 0.25.0-dev.470

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.
package/dist/index.js CHANGED
@@ -3741,6 +3741,22 @@ function runOutcome(run) {
3741
3741
  };
3742
3742
  }
3743
3743
  /**
3744
+ * What hardening left blocking, in a sentence: the findings the run names (PRO-2817), or, for a run reported before
3745
+ * it recorded them, how many.
3746
+ */
3747
+ function hardeningSaid(blocking, named) {
3748
+ const count = `Hardening ended with ${blocking} blocking finding${blocking === 1 ? "" : "s"} it could not fix`;
3749
+ if (named.length === 0) return `${count}; the run records how many, not which.`;
3750
+ const shown = named.slice(0, 3).map((finding) => {
3751
+ const message = String(finding["message"]);
3752
+ const where = finding["line"] ? `${String(finding["path"])}:${String(finding["line"])}` : String(finding["path"]);
3753
+ return `${message} (${String(finding["tool"])}, ${where})`;
3754
+ });
3755
+ const rest = blocking - shown.length;
3756
+ const recorded = named.length > shown.length ? `, ${named.length} recorded in taiga_get_run` : "";
3757
+ return `${count}: ${shown.join("; ")}${rest > 0 ? `; and ${rest} more${recorded}` : ""}.`;
3758
+ }
3759
+ /**
3744
3760
  * What each failedReason means for the next move (PRO-2800): reviewers met dispatch_rejected and agent_reported as
3745
3761
  * bare enums. As @taiga/types documents them.
3746
3762
  */
@@ -3765,10 +3781,11 @@ function readableRun(run, opts = {}) {
3765
3781
  const means = FAILED_REASON_MEANS[String(run["failedReason"])];
3766
3782
  const hardening = run["hardeningResult"];
3767
3783
  const blocking = typeof hardening?.blockingFindings === "number" ? hardening.blockingFindings : 0;
3784
+ const named = Array.isArray(hardening?.blocking) ? hardening.blocking : [];
3768
3785
  return {
3769
3786
  ...rest,
3770
3787
  ...means ? { failedReasonMeans: means } : {},
3771
- ...blocking > 0 ? { hardeningNote: `Hardening ended with ${blocking} blocking finding${blocking === 1 ? "" : "s"} it could not fix; the run records how many, not which.` } : {},
3788
+ ...blocking > 0 ? { hardeningNote: hardeningSaid(blocking, named) } : {},
3772
3789
  ...opts.row || error === void 0 ? {} : { error },
3773
3790
  ...opts.row || reviewEscalation === void 0 ? {} : { reviewEscalation },
3774
3791
  ...typeof rest["prNumber"] === "number" || rest["prUrl"] ? { prDraft } : {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taiga-ai/mcp-server",
3
- "version": "0.25.0-dev.469",
3
+ "version": "0.25.0-dev.470",
4
4
  "description": "MCP server for Taiga -- gives AI assistants access to documents and products",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -8950,6 +8950,78 @@ describe('what the seventh agent review found (PRO-2800)', () => {
8950
8950
  expect(bundle.runs[0]).not.toHaveProperty('hardeningResult');
8951
8951
  });
8952
8952
 
8953
+ it('names the blocking hardening findings a run recorded', async () => {
8954
+ const { client } = await bootMcpPair({
8955
+ capabilities: ['products:read'],
8956
+ routes: {
8957
+ [`/v1/builds/${BUILD_ID}/steps`]: { data: [] },
8958
+ [`/v1/builds/${BUILD_ID}`]: {
8959
+ data: {
8960
+ id: BUILD_ID,
8961
+ productId: PRODUCT_ID,
8962
+ status: 'needs_review',
8963
+ prNumber: 4,
8964
+ hardeningResult: {
8965
+ passed: false,
8966
+ scanners: {},
8967
+ blockingFindings: 1,
8968
+ blocking: [
8969
+ {
8970
+ tool: 'betterleaks',
8971
+ severity: 'critical',
8972
+ path: 'src/config.rs',
8973
+ line: 12,
8974
+ message: 'generic-api-key: Hardcoded API key',
8975
+ },
8976
+ ],
8977
+ },
8978
+ },
8979
+ },
8980
+ },
8981
+ });
8982
+
8983
+ const run = parseToolResult(await client.callTool({ name: 'taiga_get_run', arguments: { runId: BUILD_ID } }));
8984
+
8985
+ expect(run.hardeningNote).toBe(
8986
+ 'Hardening ended with 1 blocking finding it could not fix: generic-api-key: Hardcoded API key (betterleaks, src/config.rs:12).'
8987
+ );
8988
+ });
8989
+
8990
+ it('always says where a blocking finding is, even when its title names the path', async () => {
8991
+ const { client } = await bootMcpPair({
8992
+ capabilities: ['products:read'],
8993
+ routes: {
8994
+ [`/v1/builds/${BUILD_ID}/steps`]: { data: [] },
8995
+ [`/v1/builds/${BUILD_ID}`]: {
8996
+ data: {
8997
+ id: BUILD_ID,
8998
+ productId: PRODUCT_ID,
8999
+ status: 'needs_review',
9000
+ hardeningResult: {
9001
+ passed: false,
9002
+ blockingFindings: 1,
9003
+ blocking: [
9004
+ {
9005
+ tool: 'betterleaks',
9006
+ severity: 'critical',
9007
+ path: 'src/config.rs',
9008
+ line: 12,
9009
+ message: 'aws-key: AWS key in src/config.rs',
9010
+ },
9011
+ ],
9012
+ },
9013
+ },
9014
+ },
9015
+ },
9016
+ });
9017
+
9018
+ const run = parseToolResult(await client.callTool({ name: 'taiga_get_run', arguments: { runId: BUILD_ID } }));
9019
+
9020
+ expect(run.hardeningNote).toBe(
9021
+ 'Hardening ended with 1 blocking finding it could not fix: aws-key: AWS key in src/config.rs (betterleaks, src/config.rs:12).'
9022
+ );
9023
+ });
9024
+
8953
9025
  it('says what the API says when it names the product holding a repository', async () => {
8954
9026
  const { client, fetchSpy } = await bootMcpPair({
8955
9027
  capabilities: ALL_CAPABILITIES,
@@ -222,6 +222,24 @@ export function runOutcome(run: Record<string, unknown>): { disposition: string;
222
222
  return { disposition, outcome: sentence[disposition] ?? status };
223
223
  }
224
224
 
225
+ /**
226
+ * What hardening left blocking, in a sentence: the findings the run names (PRO-2817), or, for a run reported before
227
+ * it recorded them, how many.
228
+ */
229
+ function hardeningSaid(blocking: number, named: Record<string, unknown>[]): string {
230
+ const count = `Hardening ended with ${blocking} blocking finding${blocking === 1 ? '' : 's'} it could not fix`;
231
+ if (named.length === 0) return `${count}; the run records how many, not which.`;
232
+ const shown = named.slice(0, 3).map((finding) => {
233
+ const message = String(finding['message']);
234
+ const where = finding['line'] ? `${String(finding['path'])}:${String(finding['line'])}` : String(finding['path']);
235
+ // Always where it is, though a scanner's title may repeat the path: a substring test would drop a real one.
236
+ return `${message} (${String(finding['tool'])}, ${where})`;
237
+ });
238
+ const rest = blocking - shown.length;
239
+ const recorded = named.length > shown.length ? `, ${named.length} recorded in taiga_get_run` : '';
240
+ return `${count}: ${shown.join('; ')}${rest > 0 ? `; and ${rest} more${recorded}` : ''}.`;
241
+ }
242
+
225
243
  /**
226
244
  * What each failedReason means for the next move (PRO-2800): reviewers met dispatch_rejected and agent_reported as
227
245
  * bare enums. As @taiga/types documents them.
@@ -247,14 +265,15 @@ export function readableRun(run: Record<string, unknown>, opts: { row?: boolean
247
265
  const { prDraft, error, reviewEscalation, runtime: _runtime, ...rest } = run;
248
266
  const said = runOutcome(run);
249
267
  const means = FAILED_REASON_MEANS[String(run['failedReason'])];
250
- const hardening = run['hardeningResult'] as { blockingFindings?: unknown } | null | undefined;
268
+ const hardening = run['hardeningResult'] as { blockingFindings?: unknown; blocking?: unknown } | null | undefined;
251
269
  const blocking = typeof hardening?.blockingFindings === 'number' ? hardening.blockingFindings : 0;
270
+ const named = Array.isArray(hardening?.blocking) ? (hardening.blocking as Record<string, unknown>[]) : [];
252
271
  return {
253
272
  ...rest,
254
273
  ...(means ? { failedReasonMeans: means } : {}),
255
274
  ...(blocking > 0
256
275
  ? {
257
- hardeningNote: `Hardening ended with ${blocking} blocking finding${blocking === 1 ? '' : 's'} it could not fix; the run records how many, not which.`,
276
+ hardeningNote: hardeningSaid(blocking, named),
258
277
  }
259
278
  : {}),
260
279
  // A row's outcome says the error and the handback; the full read keeps them whole.