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

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
@@ -4892,7 +4892,7 @@ function registerMaintenanceTools(server, client) {
4892
4892
  }));
4893
4893
  server.registerTool("taiga_get_policy_standing", {
4894
4894
  title: "Get a product's policy standing",
4895
- 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. An unlisted control is not \"passing\" (summary.controlsWithNothingRecorded counts them). full: true adds evidence and resolved ones. Fix with taiga_fix_policy_violation.",
4895
+ 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.",
4896
4896
  inputSchema: z.object({
4897
4897
  productId,
4898
4898
  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)"),
@@ -5227,7 +5227,8 @@ const SETUP_TASK_FIELDS = [
5227
5227
  "providerActionUrl",
5228
5228
  "inboxItemId",
5229
5229
  "inboxItemStatus",
5230
- "createdAt"
5230
+ "createdAt",
5231
+ "stale"
5231
5232
  ];
5232
5233
  /** The API's count across every page, on the first page; nothing from an API that does not send one. */
5233
5234
  function totalOf(result) {
@@ -5267,7 +5268,10 @@ function sameKeyOnce(rows) {
5267
5268
  const { alsoFiledAs: _also, ...was } = kept;
5268
5269
  for (const field of Object.keys(kept)) delete kept[field];
5269
5270
  Object.assign(kept, row, { alsoFiledAs: [...also, record(was)] });
5270
- } else kept["alsoFiledAs"] = [...also, record(row)];
5271
+ } else {
5272
+ kept["alsoFiledAs"] = [...also, record(row)];
5273
+ if (isOpen(row) && row["stale"] === void 0) delete kept["stale"];
5274
+ }
5271
5275
  }
5272
5276
  return out;
5273
5277
  }
@@ -5342,7 +5346,7 @@ function registerInboxTools(server, client) {
5342
5346
  }));
5343
5347
  server.registerTool("taiga_list_setup_tasks", {
5344
5348
  title: "List setup tasks",
5345
- description: "List what a person must set up for a product (secrets, variables, accounts), with exactly what and where: key (the name to create; Taiga never stores values), the provider page, and phase: prereq (the deploy fails without it), wiring (generated values back to providers) or followup (can wait until after launch). Runs do not wait for them. One product or all, oldest first; includeDone: true adds done and dismissed. A variable filed twice lists the others under alsoFiledAs; totalCount counts records, and a page that folded some says recordsOnPage. Unlike taiga_list_action_required, which lists the same work as items to resolve.",
5349
+ description: "List what a person must set up for a product (secrets, variables, accounts), with exactly what and where: key (the name to create; Taiga never stores values), the provider page, and phase: prereq (the deploy fails without it), wiring (generated values back to providers) or followup (can wait until after launch). Runs do not wait for them. One product or all, oldest first; includeDone: true adds done and dismissed. A variable filed twice lists the others under alsoFiledAs; totalCount counts records, and a page that folded some says recordsOnPage. stale on an open task says no document derived from the code since it was raised mentions its key: it may not apply any more, so check before asking anyone to set it. Unlike taiga_list_action_required, which lists the same work as items to resolve.",
5346
5350
  inputSchema: z.object({
5347
5351
  productId: productId.optional(),
5348
5352
  includeDone: z.boolean().optional().describe("Also the tasks already done or dismissed (default false)"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taiga-ai/mcp-server",
3
- "version": "0.25.0-dev.467",
3
+ "version": "0.25.0-dev.469",
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",
@@ -6856,6 +6856,52 @@ describe('what the second agent review found (PRO-2778, PRO-2780)', () => {
6856
6856
  expect(sentUrl(fetchSpy, '/v1/inbox').searchParams.has('withTotal')).toBe(false);
6857
6857
  });
6858
6858
 
6859
+ it("passes on a setup task's stale note (PRO-2819)", async () => {
6860
+ const stale = "None of the current code's documents, derived since this task was raised, mention JWT_SECRET.";
6861
+ const { client } = await bootMcpPair({
6862
+ capabilities: ALL_CAPABILITIES,
6863
+ routes: {
6864
+ 'GET /v1/setup-tasks': {
6865
+ data: [
6866
+ {
6867
+ id: ENV_ID,
6868
+ productId: ENV_ID,
6869
+ key: 'JWT_SECRET',
6870
+ inboxItemStatus: 'open',
6871
+ title: 'Set JWT_SECRET',
6872
+ stale,
6873
+ },
6874
+ ],
6875
+ nextCursor: null,
6876
+ },
6877
+ },
6878
+ });
6879
+
6880
+ const tasks = parseToolResult(await client.callTool({ name: 'taiga_list_setup_tasks', arguments: {} }));
6881
+ expect(tasks.data[0].stale).toBe(stale);
6882
+ });
6883
+
6884
+ it('drops the stale note from a folded variable when one open record of it is live (PRO-2819)', async () => {
6885
+ const stale = "None of the current code's documents, derived since this task was raised, mention JWT_SECRET.";
6886
+ const record = { productId: ENV_ID, key: 'JWT_SECRET', inboxItemStatus: 'open' };
6887
+ const { client } = await bootMcpPair({
6888
+ capabilities: ALL_CAPABILITIES,
6889
+ routes: {
6890
+ 'GET /v1/setup-tasks': {
6891
+ data: [
6892
+ { ...record, id: 'old', category: 'secret', title: 'Set JWT_SECRET', stale },
6893
+ { ...record, id: 'new', category: 'env_var', title: 'Set JWT_SECRET' },
6894
+ ],
6895
+ nextCursor: null,
6896
+ },
6897
+ },
6898
+ });
6899
+
6900
+ const tasks = parseToolResult(await client.callTool({ name: 'taiga_list_setup_tasks', arguments: {} }));
6901
+ expect(tasks.data).toHaveLength(1);
6902
+ expect(tasks.data[0].stale).toBeUndefined();
6903
+ });
6904
+
6859
6905
  it('carries who cancelled a failed plan and when, and leaves the item as it was when the history is refused', async () => {
6860
6906
  const failed = { id: ITEM_ID, title: 'Login', planningStatus: 'failed', planFailureCause: 'cancelled' };
6861
6907
  const history = {
@@ -306,6 +306,7 @@ const SETUP_TASK_FIELDS = [
306
306
  'inboxItemId',
307
307
  'inboxItemStatus',
308
308
  'createdAt',
309
+ 'stale',
309
310
  ] as const;
310
311
 
311
312
  /** The API's count across every page, on the first page; nothing from an API that does not send one. */
@@ -350,6 +351,9 @@ export function sameKeyOnce(rows: Row[]): Row[] {
350
351
  Object.assign(kept, row, { alsoFiledAs: [...also, record(was)] });
351
352
  } else {
352
353
  kept['alsoFiledAs'] = [...also, record(row)];
354
+ // Stale only while every open record is (PRO-2819): one raised after the code's documents were derived
355
+ // is a live ask, whichever record leads.
356
+ if (isOpen(row) && row['stale'] === undefined) delete kept['stale'];
353
357
  }
354
358
  }
355
359
  return out;
@@ -471,7 +475,9 @@ export function registerInboxTools(server: McpServer, client: TaigaClient) {
471
475
  'deploy fails without it), wiring (generated values back to providers) or followup (can wait until after ' +
472
476
  'launch). Runs do not wait for them. One product or all, oldest first; includeDone: true adds done and ' +
473
477
  'dismissed. A variable filed twice lists the others under alsoFiledAs; totalCount counts records, and a ' +
474
- 'page that folded some says recordsOnPage. Unlike ' +
478
+ 'page that folded some says recordsOnPage. stale on an open task says no document derived from the code ' +
479
+ 'since it was raised mentions its key: it may not apply any more, so check before asking anyone to set ' +
480
+ 'it. Unlike ' +
475
481
  'taiga_list_action_required, which lists the same work as items to resolve.',
476
482
  inputSchema: z.object({
477
483
  productId: productId.optional(),
@@ -311,6 +311,9 @@ export function registerMaintenanceTools(server: McpServer, client: TaigaClient)
311
311
  'sentence and the contradicting passage) and the work on each. state: breached (open scanner findings ' +
312
312
  'map to it, by findingsByTier; routine-only is not urgent) or diverged (the documents contradict it). ' +
313
313
  "live: false means the violation's initiative ended without fixing it: it needs a person or a new fix. " +
314
+ "raisedFor: false means the link is the backlog planner's: it said an initiative a person asked it for " +
315
+ 'would close the violation, which may be a dedicated fix or broader work; true means a fix was raised ' +
316
+ 'for it (taiga_fix_policy_violation or the sweep). ' +
314
317
  'An unlisted control is not "passing" (summary.controlsWithNothingRecorded counts them). full: true adds ' +
315
318
  'evidence and resolved ones. Fix with taiga_fix_policy_violation.',
316
319
  inputSchema: z.object({