@taiga-ai/mcp-server 0.26.0-dev.516 → 0.26.0-dev.518
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 +59 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4221,6 +4221,20 @@ const RUN_OUTCOME_FIELDS = [
|
|
|
4221
4221
|
* stalled in the hardening pass after the steps and the retry continues it.
|
|
4222
4222
|
* Null for a run still going or that finished.
|
|
4223
4223
|
*/
|
|
4224
|
+
/**
|
|
4225
|
+
* The line of a failed step's error that says why (PRO-2941): a review found the reason buried in a
|
|
4226
|
+
* 2,000-character dump of git and lint output, behind runtime warnings. The first line that reads as an error,
|
|
4227
|
+
* else the first line; capped. Null when no step failed with an error.
|
|
4228
|
+
*/
|
|
4229
|
+
function failureCause(steps) {
|
|
4230
|
+
const failed = steps.find((step) => step["status"] === "failed" && typeof step["error"] === "string");
|
|
4231
|
+
if (!failed) return null;
|
|
4232
|
+
const lines = String(failed["error"]).split("\n").map((line) => line.trim()).filter((line) => line && !/^\(node:\d+\)|^\(Use `node|^[⋯✔↓]/u.test(line));
|
|
4233
|
+
const telling = lines.find((line) => /\berror\b|cannot|not found|denied|refused|no such/iu.test(line)) ?? lines.find((line) => /fail|✖/iu.test(line)) ?? lines[0];
|
|
4234
|
+
if (!telling) return null;
|
|
4235
|
+
const said = telling.length > 200 ? `${telling.slice(0, 197)}...` : telling;
|
|
4236
|
+
return `step ${String(failed["stepOrder"])}: ${said}`;
|
|
4237
|
+
}
|
|
4224
4238
|
function stoppedIn(run, steps) {
|
|
4225
4239
|
if (run.prMerged === true || run.status !== "failed" && run.status !== "stopped") return null;
|
|
4226
4240
|
if (!steps.some((step) => step.status !== "planned")) return "before the first step started, while setting up the run (cloning, preparing the workspace)";
|
|
@@ -4625,7 +4639,7 @@ function registerBuildTools(server, client) {
|
|
|
4625
4639
|
}));
|
|
4626
4640
|
server.registerTool("taiga_get_run", {
|
|
4627
4641
|
title: "Get run",
|
|
4628
|
-
description: "Read one coding-agent run: status and outcome (one sentence), pull request, hardening, and each step with its status, commit, files, verification and headline. scanCoverage names security scanners that did not run, and why. verification is Taiga's check after a step (tests, lint, build); verificationNote says when one failed throughout. A failed or stopped run has stoppedIn and
|
|
4642
|
+
description: "Read one coding-agent run: status and outcome (one sentence), pull request, hardening, and each step with its status, commit, files, verification and headline. scanCoverage names security scanners that did not run, and why. verification is Taiga's check after a step (tests, lint, build); verificationNote says when one failed throughout. A failed or stopped run has stoppedIn, failedReasonMeans and cause (the line of the failed step's error that says why). full: true adds the complete instructions and reports. To resume: taiga_resume_initiative; to stop: move the initiative out of Build. A maintenance run has no initiative and is retried from its finding.",
|
|
4629
4643
|
inputSchema: z.object({
|
|
4630
4644
|
runId,
|
|
4631
4645
|
full: z.boolean().optional().describe("Carry each step's complete instructions and report (default false: a summary)")
|
|
@@ -4636,7 +4650,11 @@ function registerBuildTools(server, client) {
|
|
|
4636
4650
|
const runRow = run.data;
|
|
4637
4651
|
const rows = steps.data.map((step) => neverStarted(step));
|
|
4638
4652
|
const where = stoppedIn(runRow, rows);
|
|
4639
|
-
const
|
|
4653
|
+
const cause = failureCause(rows);
|
|
4654
|
+
const stop = {
|
|
4655
|
+
...where ? { stoppedIn: where } : {},
|
|
4656
|
+
...cause ? { cause } : {}
|
|
4657
|
+
};
|
|
4640
4658
|
const states = await initiativeStates(client, [runRow]);
|
|
4641
4659
|
const readable = withInitiativeState(readableRun(runRow), states);
|
|
4642
4660
|
const note = verificationNote(rows);
|
|
@@ -4818,7 +4836,7 @@ async function queueView(client, product, options = { name: "all" }) {
|
|
|
4818
4836
|
return {
|
|
4819
4837
|
linePaused: paused === null ? null : typeof paused === "string",
|
|
4820
4838
|
entries: rows.map(({ lifecycle: _lifecycle, runId: _runId, ...row }) => row),
|
|
4821
|
-
waitingOn: line ?? waitingOn(rows, idle ? station : null),
|
|
4839
|
+
waitingOn: line ?? waitingOn(rows, idle ? station : null) ?? (rows.length === 0 && "offTheLine" in offLine ? `Nothing is queued. ${offLine.offTheLine}` : null),
|
|
4822
4840
|
...offLine,
|
|
4823
4841
|
...note ? { note } : {}
|
|
4824
4842
|
};
|
|
@@ -5484,12 +5502,31 @@ function groupKey(row) {
|
|
|
5484
5502
|
const rule = typeof location?.ruleId === "string" ? location.ruleId : null;
|
|
5485
5503
|
return rule ? `${String(row["kind"])}|${String(row["severity"])}|${status}|${link}|${rule}` : null;
|
|
5486
5504
|
}
|
|
5487
|
-
/**
|
|
5505
|
+
/**
|
|
5506
|
+
* Where one finding is, in a few characters: its file and line (and the resource a scanner names), or its package.
|
|
5507
|
+
* A review read seven "CKV_GHA_7" where the location keeps no path: the file is in sourcePaths.
|
|
5508
|
+
*/
|
|
5488
5509
|
function whereOf(row) {
|
|
5489
5510
|
const location = row["location"];
|
|
5490
|
-
|
|
5511
|
+
const paths = Array.isArray(row["sourcePaths"]) ? row["sourcePaths"] : [];
|
|
5512
|
+
const file = typeof location?.path === "string" ? location.path : typeof paths[0] === "string" ? paths[0] : null;
|
|
5513
|
+
if (file) {
|
|
5514
|
+
const at = location?.line ? `${file}:${String(location.line)}` : file;
|
|
5515
|
+
return typeof location?.resource === "string" ? `${at} ${location.resource}` : at;
|
|
5516
|
+
}
|
|
5491
5517
|
return [row["packageName"], row["currentVersion"]].filter(Boolean).join(" ") || String(row["id"]);
|
|
5492
5518
|
}
|
|
5519
|
+
/** The words every member's title opens with, at a word boundary; empty when they share fewer than 20 characters. */
|
|
5520
|
+
function sharedTitle(titles) {
|
|
5521
|
+
let prefix = titles[0] ?? "";
|
|
5522
|
+
for (const title of titles.slice(1)) {
|
|
5523
|
+
let at = 0;
|
|
5524
|
+
while (at < prefix.length && prefix[at] === title[at]) at += 1;
|
|
5525
|
+
prefix = prefix.slice(0, at);
|
|
5526
|
+
}
|
|
5527
|
+
const cut = (prefix.length === (titles[0] ?? "").length ? prefix : prefix.slice(0, Math.max(0, prefix.lastIndexOf(" ")))).trim().replace(/[\s,(:;-]+$/, "");
|
|
5528
|
+
return cut.length >= 20 ? cut : "";
|
|
5529
|
+
}
|
|
5493
5530
|
/**
|
|
5494
5531
|
* Findings of one rule or license verdict on a page, folded into one row (PRO-2941): a review read seven
|
|
5495
5532
|
* CKV_GHA_7 rows and fifteen sharp license rows, identical but for where. The first stands for them, with
|
|
@@ -5517,12 +5554,14 @@ function grouped(rows, fold) {
|
|
|
5517
5554
|
if (members.length === 1) return members[0];
|
|
5518
5555
|
const where = members.map(whereOf);
|
|
5519
5556
|
const first = members[0];
|
|
5520
|
-
const shared = Object.fromEntries(Object.entries(first).filter(([key, value]) => key !== "id" && members.every((member) => JSON.stringify(member[key]) === JSON.stringify(value))));
|
|
5557
|
+
const shared = Object.fromEntries(Object.entries(first).filter(([key, value]) => key !== "id" && key !== "title" && members.every((member) => JSON.stringify(member[key]) === JSON.stringify(value))));
|
|
5521
5558
|
const location = first["location"];
|
|
5522
|
-
const
|
|
5559
|
+
const words = sharedTitle(members.map((member) => String(member["title"] ?? "")));
|
|
5560
|
+
const rule = typeof location?.ruleId === "string" ? location.ruleId : null;
|
|
5561
|
+
const said = first["kind"] === "license_violation" ? licenseVerdict(first) : words ? rule && !words.includes(rule) ? `${rule}: ${words}` : words : rule ?? String(first["title"]);
|
|
5523
5562
|
return {
|
|
5524
5563
|
...shared,
|
|
5525
|
-
|
|
5564
|
+
title: said,
|
|
5526
5565
|
count: members.length,
|
|
5527
5566
|
findingIds: members.map((member) => member["id"]),
|
|
5528
5567
|
where: where.slice(0, GROUP_WHERE),
|
|
@@ -5661,8 +5700,11 @@ function standingSummary(standing) {
|
|
|
5661
5700
|
bySeverity[severity] = (bySeverity[severity] ?? 0) + 1;
|
|
5662
5701
|
}
|
|
5663
5702
|
const stated = (state) => controls.filter((c) => c["state"] === state).length;
|
|
5703
|
+
const withAny = (key) => controls.filter((control) => Number(control[key] ?? 0) > 0).length;
|
|
5664
5704
|
return {
|
|
5665
|
-
|
|
5705
|
+
controlsWithViolations: listed.length,
|
|
5706
|
+
controlsBreached: withAny("findingCount"),
|
|
5707
|
+
controlsDiverged: withAny("divergenceCount"),
|
|
5666
5708
|
controlsInPolicies: controls.length,
|
|
5667
5709
|
controlsMet: stated("met"),
|
|
5668
5710
|
controlsNotApplicable: stated("not_applicable"),
|
|
@@ -6148,6 +6190,8 @@ function isDue(row, now) {
|
|
|
6148
6190
|
}
|
|
6149
6191
|
/** Pages read for the summary; past this it says it counted only those. */
|
|
6150
6192
|
const SUMMARY_PAGE = 100;
|
|
6193
|
+
/** Rows an org-wide first page carries when the caller names no limit: the summary answers the rest. */
|
|
6194
|
+
const ORG_WIDE_ROWS = 10;
|
|
6151
6195
|
/** Pages the summary reads before it says it stopped: 1,000 items. */
|
|
6152
6196
|
const SUMMARY_PAGES = 10;
|
|
6153
6197
|
/** Every item the summary counts, up to SUMMARY_PAGES pages; nextCursor set when it stopped short. Null on failure. */
|
|
@@ -6269,8 +6313,10 @@ function registerInboxTools(server, client) {
|
|
|
6269
6313
|
...resolved ? { resolved: true } : {}
|
|
6270
6314
|
};
|
|
6271
6315
|
const first = params.cursor === void 0;
|
|
6316
|
+
const limit = params.limit ?? (params.productId ? void 0 : ORG_WIDE_ROWS);
|
|
6272
6317
|
const result = await client.listInbox({
|
|
6273
6318
|
...query,
|
|
6319
|
+
...limit !== void 0 ? { limit } : {},
|
|
6274
6320
|
...first ? { withTotal: true } : {}
|
|
6275
6321
|
});
|
|
6276
6322
|
const overview = !first || resolved ? null : !result.nextCursor ? result : await everyItem(client, query);
|
|
@@ -6309,7 +6355,7 @@ function registerInboxTools(server, client) {
|
|
|
6309
6355
|
...checked.unchecked > 0 ? (() => {
|
|
6310
6356
|
const onPage = due.filter((row) => checked.uncheckedIds.has(String(row["id"]))).length;
|
|
6311
6357
|
const beyond = checked.unchecked - onPage;
|
|
6312
|
-
return { staleNote: `${[...onPage > 0 ? [`${onPage} review${onPage === 1 ? "" : "s"} on this page could not be checked for whether the subject moved on: those rows say staleChecked: false, and their readWith read tells`] : [], ...beyond > 0 ? [`${beyond} review${beyond === 1 ? "" : "s"}
|
|
6358
|
+
return { staleNote: `${[...onPage > 0 ? [`${onPage} review${onPage === 1 ? "" : "s"} on this page could not be checked for whether the subject moved on: those rows say staleChecked: false, and their readWith read tells`] : [], ...beyond > 0 ? [`${beyond} review${beyond === 1 ? "" : "s"} past this page could not be checked, so the summary counts them as open`] : []].join("; ")}.` };
|
|
6313
6359
|
})() : {}
|
|
6314
6360
|
});
|
|
6315
6361
|
}));
|
|
@@ -6700,13 +6746,12 @@ async function standingPart(client, productId) {
|
|
|
6700
6746
|
const standing = (await client.getPolicyStanding(productId)).data;
|
|
6701
6747
|
const summary = standingSummary(standing);
|
|
6702
6748
|
const state = standing.assessment?.state;
|
|
6703
|
-
const controls = Array.isArray(standing.controls) ? standing.controls : [];
|
|
6704
|
-
const withAny = (key) => controls.filter((control) => Number(control[key] ?? 0) > 0).length;
|
|
6705
6749
|
return {
|
|
6706
6750
|
openViolations: summary.violations,
|
|
6707
6751
|
bySeverity: summary.bySeverity,
|
|
6708
|
-
|
|
6709
|
-
|
|
6752
|
+
controlsWithViolations: summary.controlsWithViolations,
|
|
6753
|
+
controlsBreached: summary.controlsBreached,
|
|
6754
|
+
controlsDiverged: summary.controlsDiverged,
|
|
6710
6755
|
controlsMet: summary.controlsMet,
|
|
6711
6756
|
controlsNotApplicable: summary.controlsNotApplicable,
|
|
6712
6757
|
...state === "never" || state === "failed" ? { note: `The assessment ${state === "never" ? "has never run" : "failed"}: no violations here is not an all-clear.` } : {}
|
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.518",
|
|
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",
|