@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.
- package/dist/index.js +43 -8
- 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:
|
|
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
|
-
|
|
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).
|
|
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 =
|
|
6164
|
+
let characters = serializeResult(result).length;
|
|
6131
6165
|
for (let measured = -1; measured !== characters;) {
|
|
6132
6166
|
measured = characters;
|
|
6133
|
-
characters =
|
|
6167
|
+
characters = serializeResult({
|
|
6134
6168
|
...result,
|
|
6135
6169
|
characters: measured
|
|
6136
|
-
})
|
|
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.
|
|
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",
|