@taiga-ai/mcp-server 0.26.0-dev.495 → 0.26.0-dev.499

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 +206 -13
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -562,6 +562,31 @@ function lenientList(item) {
562
562
  return list;
563
563
  }
564
564
  //#endregion
565
+ //#region ../../packages/types/dist/wire-fields.js
566
+ /**
567
+ * Zod fields shared by more than one wire schema, so a change to the rule
568
+ * lands once rather than at every schema that carries the field.
569
+ */
570
+ /**
571
+ * Reusable optional-UUID field for nested schemas (e.g. discriminated-union
572
+ * members that all accept an optional resource id). Centralising the rule
573
+ * means a future change (UUID v7-only, error-message tweak) lands in one
574
+ * place instead of N call sites.
575
+ */
576
+ const optionalUuid = z.string().uuid().optional();
577
+ /**
578
+ * A source line anchor on a finding, or nothing.
579
+ *
580
+ * 0 means "no line anchor" -- a dependency in go.mod, a checkov rule on a
581
+ * whole file -- and the scanners emit 0 for it. Rejecting it with `.min(1)`
582
+ * bounced the WHOLE hardening report of staging run 01a07123 (52 findings,
583
+ * `/findings/46/lineNumber too_small`), so the UI never saw the phase
584
+ * (PRO-1481). Normalized to absent, never stored as 0. One schema for every
585
+ * wire shape that carries a line (findings, narrative, residual findings) so
586
+ * the next tightening cannot land on one of them and not the others.
587
+ */
588
+ const optionalLineAnchor = z.number().int().min(0).optional().transform((v) => v ? v : void 0);
589
+ //#endregion
565
590
  //#region ../../packages/types/dist/tenants.js
566
591
  const TENANT_DOCUMENT_TYPES = [
567
592
  "policy_security",
@@ -1432,6 +1457,149 @@ function hardeningCoverage(result) {
1432
1457
  gaps
1433
1458
  };
1434
1459
  }
1460
+ z.discriminatedUnion("kind", [
1461
+ z.object({
1462
+ type: z.literal("narrative"),
1463
+ kind: z.literal("decision"),
1464
+ what: z.string().min(1).max(2e3),
1465
+ why: z.string().min(1).max(2e3),
1466
+ stepId: optionalUuid
1467
+ }),
1468
+ z.object({
1469
+ type: z.literal("narrative"),
1470
+ kind: z.literal("tradeoff"),
1471
+ picked: z.string().min(1).max(2e3),
1472
+ over: z.string().min(1).max(2e3),
1473
+ reason: z.string().min(1).max(2e3),
1474
+ stepId: optionalUuid
1475
+ }),
1476
+ z.object({
1477
+ type: z.literal("narrative"),
1478
+ kind: z.literal("assumption"),
1479
+ what: z.string().min(1).max(2e3),
1480
+ current_choice: z.string().min(1).max(2e3),
1481
+ stepId: optionalUuid
1482
+ }),
1483
+ z.object({
1484
+ type: z.literal("narrative"),
1485
+ kind: z.literal("attention"),
1486
+ what: z.string().min(1).max(2e3),
1487
+ currently_handling: z.string().min(1).max(2e3),
1488
+ stepId: optionalUuid
1489
+ }),
1490
+ z.object({
1491
+ type: z.literal("narrative"),
1492
+ kind: z.literal("scope_change"),
1493
+ what: z.string().min(1).max(2e3),
1494
+ reason: z.string().min(1).max(2e3),
1495
+ stepId: optionalUuid
1496
+ }),
1497
+ z.object({
1498
+ type: z.literal("narrative"),
1499
+ kind: z.literal("risk"),
1500
+ what: z.string().min(1).max(2e3),
1501
+ stepId: optionalUuid
1502
+ }),
1503
+ z.object({
1504
+ type: z.literal("narrative"),
1505
+ kind: z.literal("outcome"),
1506
+ capability: z.string().min(1).max(2e3),
1507
+ stepId: optionalUuid
1508
+ }),
1509
+ z.object({
1510
+ type: z.literal("narrative"),
1511
+ kind: z.literal("agent_update"),
1512
+ agent: z.string().min(1).max(120),
1513
+ state: z.string().min(1).max(60),
1514
+ activity: z.string().max(200).optional(),
1515
+ message: z.string().max(2e3).optional(),
1516
+ stepId: optionalUuid
1517
+ }),
1518
+ z.object({
1519
+ type: z.literal("narrative"),
1520
+ kind: z.literal("activity"),
1521
+ verb: z.enum([
1522
+ "exploring",
1523
+ "drafting",
1524
+ "testing",
1525
+ "reviewing",
1526
+ "preparing"
1527
+ ]),
1528
+ target: z.string().min(1).max(2e3),
1529
+ stepId: optionalUuid
1530
+ }),
1531
+ z.object({
1532
+ type: z.literal("narrative"),
1533
+ kind: z.literal("setup_required"),
1534
+ category: z.enum([
1535
+ "env_var",
1536
+ "secret",
1537
+ "third_party_account",
1538
+ "api_key",
1539
+ "oauth_app",
1540
+ "dns_record",
1541
+ "billing_approval",
1542
+ "cloud_permission",
1543
+ "manual_config"
1544
+ ]),
1545
+ title: z.string().min(1).max(300),
1546
+ instructions: z.string().min(1).max(2e3),
1547
+ key: z.string().regex(/^[A-Za-z_][A-Za-z0-9_.]*$/).max(255).optional(),
1548
+ isSecret: z.boolean().default(false),
1549
+ target: z.enum([
1550
+ "github_secret",
1551
+ "github_env",
1552
+ "cloud_env",
1553
+ "secrets_manager",
1554
+ "local_env",
1555
+ "external"
1556
+ ]).optional(),
1557
+ providerDocUrl: z.string().url().max(1e3).refine((u) => /^https?:\/\//i.test(u), { message: "providerDocUrl must use http or https scheme" }).optional(),
1558
+ providerActionUrl: z.string().url().max(1e3).refine((u) => /^https?:\/\//i.test(u), { message: "providerActionUrl must use http or https scheme" }).optional(),
1559
+ phase: z.enum([
1560
+ "prereq",
1561
+ "wiring",
1562
+ "followup"
1563
+ ]).optional(),
1564
+ suggestedValue: z.string().max(2e3).optional(),
1565
+ stepId: optionalUuid
1566
+ }),
1567
+ z.object({
1568
+ type: z.literal("narrative"),
1569
+ kind: z.literal("quality_finding"),
1570
+ source: z.enum([
1571
+ "tests",
1572
+ "self_review",
1573
+ "hardening",
1574
+ "pr_review",
1575
+ "ci_fix",
1576
+ "post_remediation"
1577
+ ]),
1578
+ severity: z.enum([
1579
+ "blocking",
1580
+ "advisory",
1581
+ "info",
1582
+ "pass"
1583
+ ]),
1584
+ title: z.string().min(1).max(2e3),
1585
+ body: z.string().max(2e3).optional(),
1586
+ filePath: z.string().max(500).optional(),
1587
+ lineNumber: optionalLineAnchor,
1588
+ stepId: optionalUuid,
1589
+ stats: z.object({
1590
+ testsTotal: z.number().int().min(0).optional(),
1591
+ testsPassed: z.number().int().min(0).optional(),
1592
+ testsFailed: z.number().int().min(0).optional(),
1593
+ testsAdded: z.number().int().min(0).optional(),
1594
+ coveragePct: z.number().min(0).max(100).optional(),
1595
+ coverageDelta: z.number().optional(),
1596
+ blockingFindings: z.number().int().min(0).optional(),
1597
+ advisoryFindings: z.number().int().min(0).optional(),
1598
+ criticalsAddressed: z.number().int().min(0).optional(),
1599
+ cycles: z.number().int().min(0).optional()
1600
+ }).optional()
1601
+ })
1602
+ ]).options.map((option) => option.shape.kind.value);
1435
1603
  [...BUILD_RUN_ACTIVE_STATUSES.filter((s) => s !== "reviewing")];
1436
1604
  //#endregion
1437
1605
  //#region ../../packages/types/dist/agents.js
@@ -5400,8 +5568,8 @@ function registerMaintenanceTools(server, client) {
5400
5568
  ...control,
5401
5569
  ...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." } : {},
5402
5570
  ...control.assessment == null && (control.state === "breached" || control.state === "diverged") ? { assessmentNote: "No met or not-applicable verdict is shown: an open finding or violation against the control outranks one." } : {},
5571
+ ...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. violations below are the documents' only.` } : {},
5403
5572
  violations: pickFields(own, VIOLATION_FIELDS),
5404
- ...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.` } : {},
5405
5573
  ...own.length > 0 ? wording : {}
5406
5574
  });
5407
5575
  }
@@ -6317,7 +6485,7 @@ function registerContextTools(server, client) {
6317
6485
  if (!client.hasCapability("products:read")) return;
6318
6486
  server.registerTool("taiga_get_product_context", {
6319
6487
  title: "Get product context",
6320
- description: `Read where a product stands in one call; the first read for any product question. Name it by ID, slug or git remote URL. Returns product (in setup, readiness and next move), factory with its instructions, specification and architecture outlines (section ids, sizes), policies with section counts, a count of initiatives per board column (done and canceled too) and up to ${BACKLOG_IN_CONTEXT} open ones, a person's turn first (taiga_list_initiatives lists the rest), repositories, the latest five runs (taiga_list_runs has more), and counts for Action Required, open findings and policy violations. A part that failed is under unavailable, one the key cannot read under notGranted; neither shows as empty. documents: budget returns whole sections up to about ${DOCUMENT_BUDGET / 1e3}k characters each, full returns all.`,
6488
+ description: `Read where a product stands in one call; the first read for any product question. Name it by ID, slug or git remote URL. Returns product (in setup, readiness and next move), factory with its instructions, specification and architecture outlines (section ids, sizes), policies with section counts, a count of initiatives per board column (done and canceled too) and up to ${BACKLOG_IN_CONTEXT} open ones, a person's turn first (taiga_list_initiatives lists them all, newest first), repositories, the latest five runs (taiga_list_runs has more), and counts for Action Required, open findings and policy violations. A part that failed is under unavailable, one the key cannot read under notGranted; neither shows as empty. documents: budget returns whole sections up to about ${DOCUMENT_BUDGET / 1e3}k characters each, full returns all.`,
6321
6489
  inputSchema: z.object({
6322
6490
  productId: productId.optional(),
6323
6491
  slug: slug.optional(),
@@ -21990,6 +22158,8 @@ const MISSING_DOCUMENTS_HOW = "An imported product gets them from its import (ta
21990
22158
  * allowDuplicateName says the person means it.
21991
22159
  */
21992
22160
  const allowDuplicateName = z.boolean().optional().describe("Create it even when one with the same name exists (default false: refused, naming the existing one)");
22161
+ /** The same choice on a rename (PRO-2823). */
22162
+ const allowDuplicateRename = z.boolean().optional().describe("Rename it even when another has the new name (default false: refused, naming the existing one)");
21993
22163
  /**
21994
22164
  * A product name is refused when taken in its own factory only; the same name
21995
22165
  * in another factory is allowed and the slug takes a suffix. A review read the
@@ -22072,13 +22242,25 @@ function registerSetupTools(server, client) {
22072
22242
  }));
22073
22243
  server.registerTool("taiga_rename_factory", {
22074
22244
  title: "Rename a factory",
22075
- description: "Rename a factory. Its slug follows the new name, so links to it change; products, members, knowledge and instructions stay as they are.",
22245
+ description: "Rename a factory. Its slug follows the new name, so links to it change; products, members, knowledge and instructions stay as they are. A name another factory has is refused, as on create.",
22076
22246
  inputSchema: z.object({
22077
22247
  factoryId,
22078
- name: z.string().trim().min(1).max(255).describe("The new name")
22248
+ name: z.string().trim().min(1).max(255).describe("The new name"),
22249
+ allowDuplicateName: allowDuplicateRename
22079
22250
  }),
22080
22251
  annotations: OPERATE_SETTING
22081
- }, (params) => runTool(async () => toolResult(pickFields([(await client.updateFactory(params.factoryId, { name: params.name })).data], FACTORY_FIELDS)[0])));
22252
+ }, (params) => runTool(async () => {
22253
+ try {
22254
+ return toolResult(pickFields([(await client.updateFactory(params.factoryId, {
22255
+ name: params.name,
22256
+ refuseDuplicateName: !params.allowDuplicateName
22257
+ })).data], FACTORY_FIELDS)[0]);
22258
+ } catch (error) {
22259
+ const refused = duplicateName("factory", error, "in this organization");
22260
+ if (refused) return refused;
22261
+ throw error;
22262
+ }
22263
+ }));
22082
22264
  server.registerTool("taiga_preview_product_move", {
22083
22265
  title: "Preview moving a product to another factory",
22084
22266
  description: "Show who gains and who loses access if a product moves to another factory: the source factory's members lose it, the target's gain it; people in both, the product's own members, and owners and admins keep it, named with why. Show it to the person before taiga_move_product.",
@@ -22160,21 +22342,32 @@ function registerSetupTools(server, client) {
22160
22342
  inputSchema: z.object({
22161
22343
  productId,
22162
22344
  name: z.string().trim().min(1).max(255).optional().describe("A new name"),
22345
+ allowDuplicateName: allowDuplicateRename,
22163
22346
  ...buildScope,
22164
22347
  deploymentNotes: deploymentNotes.nullable().optional(),
22165
22348
  emitDesignStandards: z.boolean().nullable().optional().describe("Inherit the organization's design standards; null follows the default")
22166
22349
  }),
22167
22350
  annotations: OPERATE_SETTING
22168
22351
  }, (params) => runTool(async () => {
22169
- const { productId: id, ...changes } = params;
22352
+ const { productId: id, allowDuplicateName: allowed, ...changes } = params;
22170
22353
  if (Object.values(changes).every((value) => value === void 0)) return toolError("VALIDATION_ERROR", "Change at least one setting.");
22171
22354
  const contradiction = notesWithNothingToDescribe(params);
22172
22355
  if (contradiction) return contradiction;
22173
- const updated = (await client.updateProduct(id, changes)).data;
22356
+ let updated;
22357
+ try {
22358
+ updated = (await client.updateProduct(id, {
22359
+ ...changes,
22360
+ ...changes.name !== void 0 ? { refuseDuplicateName: !allowed } : {}
22361
+ })).data;
22362
+ } catch (error) {
22363
+ const refused = duplicateName("product", error, "in this factory");
22364
+ if (refused) return refused;
22365
+ throw error;
22366
+ }
22174
22367
  const warnings = missingNotesWarning(updated);
22175
22368
  return toolResult({
22176
- ...await productRow(client, updated),
22177
- ...warnings.length > 0 ? { warnings } : {}
22369
+ ...warnings.length > 0 ? { warnings } : {},
22370
+ ...await productRow(client, updated)
22178
22371
  });
22179
22372
  }));
22180
22373
  server.registerTool("taiga_refresh_reality_documents", {
@@ -22822,7 +23015,7 @@ const SERVER_INSTRUCTIONS = [
22822
23015
  "Taiga is a policy-aware software delivery platform. Use these tools (taiga_*) for anything about a Taiga",
22823
23016
  "product: its initiatives, runs, policies, findings, deployments or setup.",
22824
23017
  "",
22825
- "**Which read.** Where a product stands (counts, not detail): taiga_get_product_context, by remote URL, slug",
23018
+ "**Which read.** Where a product stands (counts, first rows): taiga_get_product_context, by remote URL, slug",
22826
23019
  "or ID. What waits on people: taiga_list_action_required. A policy: taiga_get_organization_document (sections:",
22827
23020
  "[], or a control id such as SEC-5.1), or the taiga://documents/{type} resources. Findings: taiga_list_findings.",
22828
23021
  "One initiative or run: taiga_get_initiative, taiga_get_run. What this key may call: taiga_whoami.",
@@ -22830,10 +23023,10 @@ const SERVER_INSTRUCTIONS = [
22830
23023
  "**Words.** An initiative is a unit of work, on a board of columns (backlog, todo, queue, plan, build, done,",
22831
23024
  "canceled); plan and build are stations, and the line starts the next queued initiative when both are empty. A",
22832
23025
  "run is one coding-agent attempt; hardening is its security pass; handed back means a person must act. A gate is",
22833
- "a person's review; a sweep is a scan for findings; standing is how a product meets the policies. Discovery sets",
22834
- "up a new product, from a web conversation or an import. A factory owns products.",
23026
+ "a person's review; a sweep is a scan for findings; standing is how a product meets policy. Discovery sets",
23027
+ "up a new product, from a web conversation or an import. Factories own products.",
22835
23028
  "",
22836
- "**IDs** are UUIDs; the product, factory and initiative lists take query (product and factory misses name the",
23029
+ "**IDs** are UUIDs; the product, factory and initiative lists take query (product and factory misses may name the",
22837
23030
  "closest). A NOT_FOUND names the tool that lists the right ones. Lists page with nextCursor. outcome, situation",
22838
23031
  "and nextStep are one-sentence summaries; setup writes return readiness. Product documents have two timelines:",
22839
23032
  "plan (intent) and reality (from the code).",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taiga-ai/mcp-server",
3
- "version": "0.26.0-dev.495",
3
+ "version": "0.26.0-dev.499",
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",