@taiga-ai/mcp-server 0.26.0-dev.481 → 0.26.0-dev.483
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 +118 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1763,6 +1763,85 @@ const MAINTENANCE_IGNORE_REASONS = [
|
|
|
1763
1763
|
"accepted_risk",
|
|
1764
1764
|
"wont_fix"
|
|
1765
1765
|
];
|
|
1766
|
+
/**
|
|
1767
|
+
* What one lockfile's group list says, read against what its osv-scanner
|
|
1768
|
+
* (osv-scalibr) extractor actually writes. Checked per extractor, because the
|
|
1769
|
+
* same list means different things in different files:
|
|
1770
|
+
*
|
|
1771
|
+
* - package-lock.json / npm-shrinkwrap.json: every package entry carries npm's
|
|
1772
|
+
* `dev` / `optional` / `devOptional` flags, transitive ones included, so an
|
|
1773
|
+
* EMPTY list is a runtime dependency, `dev` alone is dev-only, `optional`
|
|
1774
|
+
* alone is shipped, and `dev` + `optional` (devOptional) is left undecided.
|
|
1775
|
+
* - pnpm-lock.yaml: `dev` comes from a per-package `dev: true` that lockfile
|
|
1776
|
+
* v9 no longer writes, so an empty list says nothing; `dev` still means dev.
|
|
1777
|
+
* - poetry.lock: group names with `main` dropped; a non-main, non-optional
|
|
1778
|
+
* group is not in the runtime install. Poetry 1.x lockfiles carry no groups
|
|
1779
|
+
* at all, so an empty list says nothing.
|
|
1780
|
+
* - uv.lock: only direct members of a group are tagged, so an empty list says
|
|
1781
|
+
* nothing; `dev` means dev.
|
|
1782
|
+
* - yarn.lock, go.mod/go.sum, Cargo.lock, Gemfile.lock and the rest carry no
|
|
1783
|
+
* group information: unknown whatever the list says.
|
|
1784
|
+
*
|
|
1785
|
+
* Anything this cannot place is `unknown`, never `runtime`: a wrong "runtime"
|
|
1786
|
+
* on a build tool is the claim this exists to stop, and a wrong "dev only" on a
|
|
1787
|
+
* shipped package is worse.
|
|
1788
|
+
*/
|
|
1789
|
+
function lockfileScope(path, groups) {
|
|
1790
|
+
const name = path.slice(path.lastIndexOf("/") + 1);
|
|
1791
|
+
const set = new Set(groups.map((g) => g.trim().toLowerCase()).filter((g) => g !== ""));
|
|
1792
|
+
switch (name) {
|
|
1793
|
+
case "package-lock.json":
|
|
1794
|
+
case "npm-shrinkwrap.json": {
|
|
1795
|
+
if (set.size === 0) return "runtime";
|
|
1796
|
+
const dev = set.has("dev");
|
|
1797
|
+
const optional = set.has("optional");
|
|
1798
|
+
if (dev && optional) return "unknown";
|
|
1799
|
+
if (dev) return "dev";
|
|
1800
|
+
if (optional && set.size === 1) return "runtime";
|
|
1801
|
+
return "unknown";
|
|
1802
|
+
}
|
|
1803
|
+
case "pnpm-lock.yaml":
|
|
1804
|
+
case "uv.lock": return set.size === 1 && set.has("dev") ? "dev" : "unknown";
|
|
1805
|
+
case "poetry.lock": return set.size > 0 && !set.has("optional") && !set.has("main") ? "dev" : "unknown";
|
|
1806
|
+
default: return "unknown";
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
/**
|
|
1810
|
+
* Whether a dependency finding sits only in development dependencies, in at
|
|
1811
|
+
* least one runtime dependency set, or in lockfiles that do not say. Decided
|
|
1812
|
+
* per lockfile (`lockfileScope`) and combined pessimistically: one runtime
|
|
1813
|
+
* lockfile makes the finding runtime, one lockfile that cannot say makes it
|
|
1814
|
+
* unknown, and only all-dev reads as dev. Groups the sweep did not record
|
|
1815
|
+
* (older images, code kinds) are unknown. Presentation only: the tier and the
|
|
1816
|
+
* auto-remediation gate do not read this, and a malicious package runs its
|
|
1817
|
+
* install script in any group.
|
|
1818
|
+
*/
|
|
1819
|
+
function dependencyScope(finding) {
|
|
1820
|
+
if (finding.kind !== "vulnerable_dependency" && finding.kind !== "outdated_dependency") return "unknown";
|
|
1821
|
+
const entries = Object.entries(finding.dependencyGroups ?? {});
|
|
1822
|
+
if (entries.length === 0) return "unknown";
|
|
1823
|
+
return combineScopes(entries.map(([path, groups]) => lockfileScope(path, Array.isArray(groups) ? groups : [])));
|
|
1824
|
+
}
|
|
1825
|
+
function combineScopes(scopes) {
|
|
1826
|
+
if (scopes.includes("runtime")) return "runtime";
|
|
1827
|
+
if (scopes.includes("unknown") || scopes.length === 0) return "unknown";
|
|
1828
|
+
return "dev";
|
|
1829
|
+
}
|
|
1830
|
+
/**
|
|
1831
|
+
* Whether the repository declares the package itself. Per lockfile the sweep
|
|
1832
|
+
* says direct or transitive; across lockfiles one direct declaration makes the
|
|
1833
|
+
* finding direct (that copy is the repository's to bump), transitive only when
|
|
1834
|
+
* every readable lockfile says so, unknown when the sweep could read none of
|
|
1835
|
+
* them or did not record it (older images, code kinds). Presentation only; the
|
|
1836
|
+
* remediation step already finds every resolved copy and its parent itself.
|
|
1837
|
+
*/
|
|
1838
|
+
function dependencyRelation(finding) {
|
|
1839
|
+
if (finding.kind !== "vulnerable_dependency" && finding.kind !== "outdated_dependency") return "unknown";
|
|
1840
|
+
const values = Object.values(finding.dependencyRelation ?? {});
|
|
1841
|
+
if (values.includes("direct")) return "direct";
|
|
1842
|
+
if (values.length > 0 && values.every((v) => v === "transitive")) return "transitive";
|
|
1843
|
+
return "unknown";
|
|
1844
|
+
}
|
|
1766
1845
|
new Set(CODE_REMEDIATION_KINDS);
|
|
1767
1846
|
/** A non-empty string, or null. */
|
|
1768
1847
|
const locationText = z.string().min(1).nullable().catch(null);
|
|
@@ -4892,6 +4971,40 @@ function withFixability(row) {
|
|
|
4892
4971
|
};
|
|
4893
4972
|
return row;
|
|
4894
4973
|
}
|
|
4974
|
+
/**
|
|
4975
|
+
* Dev-only, runtime or unknown, said on a dependency row (PRO-2861) in place of the raw per-lockfile groups
|
|
4976
|
+
* the API carries: the groups need the lockfile name to read, which `dependencyScope` does once here.
|
|
4977
|
+
*/
|
|
4978
|
+
function withDependencyScope(row, { keepGroups = false } = {}) {
|
|
4979
|
+
const { dependencyGroups: _groups, dependencyRelation: _relation, ...lean } = row;
|
|
4980
|
+
const rest = keepGroups ? {
|
|
4981
|
+
...row,
|
|
4982
|
+
dependencyRelation: void 0
|
|
4983
|
+
} : lean;
|
|
4984
|
+
if (!(row["kind"] === "vulnerable_dependency" || row["kind"] === "outdated_dependency")) return rest;
|
|
4985
|
+
const kind = row["kind"];
|
|
4986
|
+
return {
|
|
4987
|
+
...rest,
|
|
4988
|
+
dependencyScope: dependencyScope({
|
|
4989
|
+
kind,
|
|
4990
|
+
dependencyGroups: row["dependencyGroups"]
|
|
4991
|
+
}),
|
|
4992
|
+
dependencyRelation: dependencyRelation({
|
|
4993
|
+
kind,
|
|
4994
|
+
dependencyRelation: row["dependencyRelation"]
|
|
4995
|
+
}),
|
|
4996
|
+
...keepGroups ? { dependencyRelationByLockfile: row["dependencyRelation"] ?? null } : {}
|
|
4997
|
+
};
|
|
4998
|
+
}
|
|
4999
|
+
/** A finding as a tool answers it: the row fields, the scope, and whether Fix would take it. */
|
|
5000
|
+
function findingRows(findings, extra = []) {
|
|
5001
|
+
return pickFields(findings, [
|
|
5002
|
+
...FINDING_ROW_FIELDS,
|
|
5003
|
+
"dependencyGroups",
|
|
5004
|
+
"dependencyRelation",
|
|
5005
|
+
...extra
|
|
5006
|
+
]).map((row) => withDependencyScope(row)).map(withFixability);
|
|
5007
|
+
}
|
|
4895
5008
|
/** The parts of an advisory record an agent reads; the rest (every affected range, every reference) is full: true. */
|
|
4896
5009
|
const ADVISORY_KEPT = [
|
|
4897
5010
|
"id",
|
|
@@ -5015,7 +5128,7 @@ function registerMaintenanceTools(server, client) {
|
|
|
5015
5128
|
sweep: null,
|
|
5016
5129
|
sweepNote: "The sweep overview could not be read: an empty list here is not an all-clear."
|
|
5017
5130
|
} : sweep ? { sweep } : {},
|
|
5018
|
-
...page(
|
|
5131
|
+
...page(findingRows(result.data), result.nextCursor),
|
|
5019
5132
|
...typeof result.totalCount === "number" ? { totalCount: result.totalCount } : {},
|
|
5020
5133
|
...params.status === void 0 && params.severity === void 0 && params.kind === void 0 && typeof result.totalCount === "number" && sweep && sweep !== "unread" && typeof sweep["openFindings"] === "number" && sweep["openFindings"] !== result.totalCount ? { totalNote: "totalCount includes findings with a fix under way (remediating) and ignored findings whose ignore has expired; sweep.openFindings counts open ones only." } : {}
|
|
5021
5134
|
});
|
|
@@ -5030,10 +5143,10 @@ function registerMaintenanceTools(server, client) {
|
|
|
5030
5143
|
annotations: READ_ONLY
|
|
5031
5144
|
}, (params) => runTool(async () => {
|
|
5032
5145
|
const { runId, ...raw } = (await client.getFinding(params.findingId)).data;
|
|
5033
|
-
const finding = withFixability(params.full ? raw : {
|
|
5146
|
+
const finding = withFixability(params.full ? withDependencyScope(raw, { keepGroups: true }) : withDependencyScope({
|
|
5034
5147
|
...raw,
|
|
5035
5148
|
data: leanAdvisory(raw["data"])
|
|
5036
|
-
});
|
|
5149
|
+
}));
|
|
5037
5150
|
return toolResult(runId === void 0 ? finding : {
|
|
5038
5151
|
sweepId: runId,
|
|
5039
5152
|
...finding
|
|
@@ -5120,7 +5233,7 @@ function registerMaintenanceTools(server, client) {
|
|
|
5120
5233
|
...params.expiresAt !== void 0 ? { ignoreExpiresAt: params.expiresAt } : {}
|
|
5121
5234
|
} : { status: "open" };
|
|
5122
5235
|
const finding = (await client.updateFinding(params.findingId, body)).data;
|
|
5123
|
-
return toolResult(
|
|
5236
|
+
return toolResult(findingRows([finding], ["ignoreNote"])[0]);
|
|
5124
5237
|
}));
|
|
5125
5238
|
server.registerTool("taiga_fix_finding", {
|
|
5126
5239
|
title: "Fix a finding",
|
|
@@ -5136,7 +5249,7 @@ function registerMaintenanceTools(server, client) {
|
|
|
5136
5249
|
const fixed = (await client.remediateFinding(params.productId, params.findingId)).data;
|
|
5137
5250
|
return toolResult({
|
|
5138
5251
|
backlogItemId: fixed.backlogItemId,
|
|
5139
|
-
finding: fixed.finding ?
|
|
5252
|
+
finding: fixed.finding ? findingRows([fixed.finding])[0] : null
|
|
5140
5253
|
});
|
|
5141
5254
|
}
|
|
5142
5255
|
const finding = (await client.getFinding(params.findingId)).data;
|
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.483",
|
|
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",
|