@taiga-ai/mcp-server 0.26.0-dev.488 → 0.26.0-dev.490

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 +43 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2643,11 +2643,27 @@ function inProductWords(value) {
2643
2643
  function inProductWording(said) {
2644
2644
  return said.replace(/^Backlog item not found/i, "Initiative not found").replace(/^Build run step not found/i, "Run step not found").replace(/^Build run not found/i, "Run not found").replace(/^Custom document not found/i, "Knowledge file not found").replace(/^(Branch "[^"]*" does not exist in \S+?)\.?$/i, "$1. taiga_list_repository_branches lists the branches it has.").replace(/^This product already has a repository\. Change it instead of linking another\./i, "This product already has a repository. Pass replacesRepositoryId to swap it in one step, or taiga_unlink_repository first (taiga_list_product_repositories gives its ID).");
2645
2645
  }
2646
+ /**
2647
+ * A float without its binary noise: six significant digits. Delivery metrics
2648
+ * came as `deploysPerWeek: 6.766666666666667`, sixteen digits nobody reads
2649
+ * (PRO-2847). Significant digits rather than fixed decimals: an EPSS score of
2650
+ * 0.00043, a 0.4% failure rate or 99.995% uptime keep what they say, where
2651
+ * two decimals read them as 0, 0 and 100.
2652
+ */
2653
+ const roundFloats = (_key, value) => typeof value === "number" && Number.isFinite(value) && !Number.isInteger(value) ? Number(value.toPrecision(6)) : value;
2654
+ /**
2655
+ * A result as the model reads it: compact JSON, in the product's words, its
2656
+ * floats rounded. Indentation was a fifth of a list's characters (PRO-2847),
2657
+ * paid on every call; the model reads JSON without it.
2658
+ */
2659
+ function serializeResult(value) {
2660
+ return JSON.stringify(inProductWords(value), roundFloats);
2661
+ }
2646
2662
  /** A successful tool result: one JSON text block the model reads. */
2647
2663
  function toolResult(value) {
2648
2664
  return { content: [{
2649
2665
  type: "text",
2650
- text: JSON.stringify(inProductWords(value), null, 2)
2666
+ text: serializeResult(value)
2651
2667
  }] };
2652
2668
  }
2653
2669
  /**
@@ -5161,10 +5177,13 @@ function standingSummary(standing) {
5161
5177
  const severity = String(violation["severity"] ?? "unknown");
5162
5178
  bySeverity[severity] = (bySeverity[severity] ?? 0) + 1;
5163
5179
  }
5180
+ const stated = (state) => controls.filter((c) => c["state"] === state).length;
5164
5181
  return {
5165
5182
  controls: listed.length,
5166
5183
  controlsInPolicies: controls.length,
5167
- controlsWithNothingRecorded: controls.length - listed.length,
5184
+ controlsMet: stated("met"),
5185
+ controlsNotApplicable: stated("not_applicable"),
5186
+ controlsNotAssessed: stated("no_evidence"),
5168
5187
  violations: violations.length,
5169
5188
  bySeverity,
5170
5189
  resolvedViolations: resolved
@@ -5232,11 +5251,12 @@ function registerMaintenanceTools(server, client) {
5232
5251
  }));
5233
5252
  server.registerTool("taiga_get_policy_standing", {
5234
5253
  title: "Get a product's policy standing",
5235
- description: "How a product stands against the organization's published policies: summary counts, when the documents were assessed, and each violated control with its violations (critical and high only, citing the policy sentence and the contradicting passage) and the work on each. state: breached (open scanner findings map to it, by findingsByTier; routine-only is not urgent) or diverged (the documents contradict it). live: false means the violation's initiative ended without fixing it: it needs a person or a new fix. raisedFor: false means the link is the backlog planner's: it said an initiative a person asked it for would close the violation, which may be a dedicated fix or broader work; true means a fix was raised for it (taiga_fix_policy_violation or the sweep). An unlisted control is not \"passing\" (summary.controlsWithNothingRecorded counts them). full: true adds evidence and resolved ones. Fix with taiga_fix_policy_violation.",
5254
+ description: "How a product stands against the organization's published policies: summary counts, when the documents were assessed, and each violated control with its violations (critical and high only, citing the policy sentence and the contradicting passage) and the work on each. state: breached (open scanner findings map to it, by findingsByTier; routine-only is not urgent) or diverged (the documents contradict it). live: false means the violation's initiative ended without fixing it: it needs a person or a new fix. raisedFor: false means the link is the backlog planner's: it said an initiative a person asked it for would close the violation, which may be a dedicated fix or broader work; true means a fix was raised for it (taiga_fix_policy_violation or the sweep). summary counts the other controls: met or not applicable as the documents show it, or not assessed (no evidence either way, which is no pass). controlId reads one control: its state, the passage behind a verdict, its document violations and how many scanner findings breach it. full: true adds evidence and resolved ones. Fix with taiga_fix_policy_violation.",
5236
5255
  inputSchema: z.object({
5237
5256
  productId,
5238
5257
  full: z.boolean().optional().describe("Carry each violation's evidence and policy excerpt, cut to about 1,500 characters each, and up to 20 resolved violations (default false)"),
5239
- violationId: idInput("violation", "this tool lists them").optional().describe("Only this violation, in full and uncut: its evidence, policy excerpt and work")
5258
+ violationId: idInput("violation", "this tool lists them").optional().describe("Only this violation, in full and uncut: its evidence, policy excerpt and work"),
5259
+ controlId: z.string().trim().min(1).max(40).optional().describe("Only this control, such as SEC-5.1: whether it is met, not applicable, violated or not assessed, with the passage")
5240
5260
  }),
5241
5261
  annotations: READ_ONLY
5242
5262
  }, (params) => runTool(async () => {
@@ -5248,6 +5268,20 @@ function registerMaintenanceTools(server, client) {
5248
5268
  const note = standing.assessment?.state === "never" ? "Never assessed: the documents have not been checked against the policies yet, so nothing here is an all-clear. An assessment runs when the reality documents are derived or refreshed (taiga_refresh_reality_documents, paid work: ask first)." : null;
5249
5269
  const resolvedAny = Array.isArray(standing.resolvedDivergences) && standing.resolvedDivergences.length > 0;
5250
5270
  const wording = violations.length > 0 || resolvedAny ? { wordingNote: "A violation's evidence is from its assessedAt; its title and summary may be from when it was first recorded. Where they disagree, the evidence is current." } : {};
5271
+ if (params.controlId && params.violationId) return toolError("VALIDATION_ERROR", "Name either a control (controlId) or one violation (violationId), not both.");
5272
+ if (params.controlId) {
5273
+ const wanted = params.controlId.toUpperCase();
5274
+ const control = controls.find((c) => String(c.controlId).toUpperCase() === wanted);
5275
+ if (!control) return toolError("NOT_FOUND", "No control with that id in this product's policies; taiga_get_document (type, sections: []) lists a policy's controls.");
5276
+ const own = violations.filter((violation) => String(violation["controlId"]).toUpperCase() === wanted);
5277
+ return toolResult({
5278
+ ...control,
5279
+ ...control.state === "no_evidence" ? { meaning: "Not assessed: no finding or violation is recorded against it, and the documents were not found to show it met. That is no evidence either way, not a pass." } : {},
5280
+ violations: pickFields(own, VIOLATION_FIELDS),
5281
+ ...Number(control.findingCount ?? 0) > 0 ? { findingsNote: `${String(control.findingCount)} open scanner finding${control.findingCount === 1 ? "" : "s"} breach it; taiga_list_findings (productId) lists them with the controls each breaches.` } : {},
5282
+ ...own.length > 0 ? wording : {}
5283
+ });
5284
+ }
5251
5285
  if (params.violationId) {
5252
5286
  const one = [...violations, ...Array.isArray(standing.resolvedDivergences) ? standing.resolvedDivergences : []].find((violation) => violation["id"] === params.violationId);
5253
5287
  if (!one) return toolError("NOT_FOUND", "No violation with that ID on this product; this tool without violationId lists them.");
@@ -6127,13 +6161,13 @@ async function productContext(client, product, options) {
6127
6161
  unavailable,
6128
6162
  readNext: "Read a policy, which is the organization's and takes no productId, with taiga_get_document (type, then sections: [] for its outline or a control id such as SEC-5.1); the product's own specification, architecture and other documents with taiga_get_product_document (productId); an initiative with taiga_get_initiative (taiga_list_initiatives pages a truncated backlog) and a run with taiga_get_run."
6129
6163
  };
6130
- let characters = JSON.stringify(inProductWords(result), null, 2).length;
6164
+ let characters = serializeResult(result).length;
6131
6165
  for (let measured = -1; measured !== characters;) {
6132
6166
  measured = characters;
6133
- characters = JSON.stringify(inProductWords({
6167
+ characters = serializeResult({
6134
6168
  ...result,
6135
6169
  characters: measured
6136
- }), null, 2).length;
6170
+ }).length;
6137
6171
  }
6138
6172
  return toolResult({
6139
6173
  characters,
@@ -6515,12 +6549,13 @@ function registerObserveTools(server, client) {
6515
6549
  ]);
6516
6550
  const now = current.data;
6517
6551
  const note = environments.rows.length === 0 ? "This product has no environments yet: an environment is defined with taiga_create_environment, and its deployments are mirrored from the provider once it is." : history.data.length === 0 && !params.cursor ? "No deployments recorded yet: none was mirrored from the provider (syncStatus says when it last synced), or nothing has been deployed." : null;
6552
+ const live = new Set([...now.current ?? [], ...now.deploying ?? []].map((row) => row.id));
6518
6553
  return toolResult({
6519
6554
  environments: pickFields(environments.rows, ENVIRONMENT_FIELDS),
6520
6555
  ...environments.more ? { moreEnvironments: true } : {},
6521
6556
  running: deploymentLines(now.current ?? []),
6522
6557
  deploying: deploymentLines(now.deploying ?? []),
6523
- history: page(deploymentLines(history.data), history.nextCursor),
6558
+ history: page(deploymentLines(history.data.filter((row) => !live.has(row.id))), history.nextCursor),
6524
6559
  ..."value" in sync ? { syncStatus: sync.value.data } : {
6525
6560
  syncStatus: null,
6526
6561
  syncUnavailable: sync.unavailable
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taiga-ai/mcp-server",
3
- "version": "0.26.0-dev.488",
3
+ "version": "0.26.0-dev.490",
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",