@taiga-ai/mcp-server 0.25.0-dev.465 → 0.25.0-dev.466

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
@@ -670,6 +670,29 @@ const POLICY_TYPE_BY_CONTROL_PREFIX = {
670
670
  SCS: "policy_supply_chain",
671
671
  CLD: "policy_cloud_governance"
672
672
  };
673
+ /** The policy document a control id belongs to, or undefined for an unknown prefix. */
674
+ function policyTypeForControl(controlId) {
675
+ return POLICY_TYPE_BY_CONTROL_PREFIX[controlId.split("-")[0] ?? ""];
676
+ }
677
+ /**
678
+ * The policy a control id points a reader to (PRO-2809): its rightmost base
679
+ * prefix. An overlay family names its host policy last: TEL-TIC-1.1 is a
680
+ * telecom control in policy_tic, HC-FI-DATA-1.1 a Finnish healthcare control in
681
+ * policy_data, ARC-AWS-5 an AWS preset in policy_architecture. A regulatory
682
+ * family with no base prefix (GDPR, NIS2, AIACT) spans several policies and
683
+ * names none.
684
+ *
685
+ * For saying where to read a control, not for deciding who holds it:
686
+ * {@link policyTypeForControl} keeps an overlay control with the document it is
687
+ * written in, whatever its id says.
688
+ */
689
+ function policyNamedByControl(controlId) {
690
+ const prefixes = controlId.split("-").filter((part) => /^[A-Z]/.test(part));
691
+ for (let at = prefixes.length - 1; at >= 0; at -= 1) {
692
+ const owner = POLICY_TYPE_BY_CONTROL_PREFIX[prefixes[at]];
693
+ if (owner) return owner;
694
+ }
695
+ }
673
696
  /**
674
697
  * The shape of a control id in a policy's control table: `SEC-3.4`, and the
675
698
  * overlay families too (`TEL-TIC-1.1`, `HC-FI-DATA-1.1`, `GDPR-1.1`). One
@@ -2663,17 +2686,6 @@ const sectionsInput = z.array(z.string().min(1).max(200)).max(50).optional().des
2663
2686
  /** The sections input of a company document, where a policy control id also names the sections that state it. */
2664
2687
  const policySectionsInput = sectionsInput.describe("Only these sections, by id, or a policy control id (SEC-5.1) for the sections that state it; [] for the outline (under outline: every section id and its size, no text); omit for the whole document");
2665
2688
  /**
2666
- * The policy that states a control: the rightmost policy prefix in its id, so an overlay family's
2667
- * TEL-TIC-1.1 is policy_tic and HC-FI-DATA-1.1 policy_data, as the overlays file them.
2668
- */
2669
- function policyOfControl(id) {
2670
- const prefixes = id.split("-").filter((part) => /^[A-Z]/.test(part));
2671
- for (const prefix of prefixes.reverse()) {
2672
- const policy = POLICY_TYPE_BY_CONTROL_PREFIX[prefix];
2673
- if (policy) return policy;
2674
- }
2675
- }
2676
- /**
2677
2689
  * The sections that state each control id asked for (PRO-2800): policy
2678
2690
  * standing cites SEC-5.1, and the section holding it has an id of its own
2679
2691
  * (secretsManagement) an agent could only guess. A control id is found in
@@ -2699,7 +2711,7 @@ function withControls(outline, sections, policyType) {
2699
2711
  return outline.map((entry) => {
2700
2712
  const text = sectionContent(sections, entry.id) ?? "";
2701
2713
  const ids = [...new Set([...text.matchAll(pattern)].filter((match) => !/^_?Reserved_?/i.test(match[2] ?? "")).map((match) => match[1]).filter((id) => {
2702
- const owner = POLICY_TYPE_BY_CONTROL_PREFIX[id.split("-")[0] ?? ""];
2714
+ const owner = policyTypeForControl(id);
2703
2715
  return owner === void 0 || owner === policyType;
2704
2716
  }))];
2705
2717
  return ids.length > 0 ? {
@@ -2727,7 +2739,7 @@ function sectionReadResult(doc, asked, label, options = {}) {
2727
2739
  const known = ids.filter((id) => all.includes(id));
2728
2740
  const found = Object.keys(controls).length > 0 ? { controls } : {};
2729
2741
  const asControl = options.controlIds ? void 0 : unknownSections.find((id) => isControlId(id));
2730
- const policy = asControl ? policyOfControl(asControl) : void 0;
2742
+ const policy = asControl ? policyNamedByControl(asControl) : void 0;
2731
2743
  const controlNote = asControl ? { note: policy ? `${asControl} is a control of ${policy}: read it with taiga_get_document (type: ${policy}, sections: ["${asControl}"]).` : `${asControl} looks like a policy control id, which only a policy resolves: taiga_get_document without type lists them.` } : {};
2732
2744
  if (known.length === 0) {
2733
2745
  const { data: _data, ...meta } = cut.doc;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taiga-ai/mcp-server",
3
- "version": "0.25.0-dev.465",
3
+ "version": "0.25.0-dev.466",
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",
@@ -13,7 +13,7 @@
13
13
 
14
14
  import type { CallToolResult } from '@modelcontextprotocol/server';
15
15
  import { z } from 'zod';
16
- import { CONTROL_ID_SOURCE, isControlId, POLICY_TYPE_BY_CONTROL_PREFIX } from '@taiga/types';
16
+ import { CONTROL_ID_SOURCE, isControlId, policyNamedByControl, policyTypeForControl } from '@taiga/types';
17
17
  import { toolError, toolResult } from './errors.js';
18
18
 
19
19
  /** A document's section map, keyed by section id: each value a `StoredSection` or (older rows) a bare string. */
@@ -177,19 +177,6 @@ export const policySectionsInput = sectionsInput.describe(
177
177
  'Only these sections, by id, or a policy control id (SEC-5.1) for the sections that state it; [] for the outline (under outline: every section id and its size, no text); omit for the whole document'
178
178
  );
179
179
 
180
- /**
181
- * The policy that states a control: the rightmost policy prefix in its id, so an overlay family's
182
- * TEL-TIC-1.1 is policy_tic and HC-FI-DATA-1.1 policy_data, as the overlays file them.
183
- */
184
- function policyOfControl(id: string): string | undefined {
185
- const prefixes = id.split('-').filter((part) => /^[A-Z]/.test(part));
186
- for (const prefix of prefixes.reverse()) {
187
- const policy = POLICY_TYPE_BY_CONTROL_PREFIX[prefix];
188
- if (policy) return policy;
189
- }
190
- return undefined;
191
- }
192
-
193
180
  /**
194
181
  * The sections that state each control id asked for (PRO-2800): policy
195
182
  * standing cites SEC-5.1, and the section holding it has an id of its own
@@ -231,7 +218,7 @@ function withControls(outline: { id: string; characters: number }[], sections: S
231
218
  // A base control belongs to the policy its prefix names: an SEC row in the data policy is a stale mention,
232
219
  // as heldControlMap reads it. An overlay prefix stays where it is written.
233
220
  .filter((id) => {
234
- const owner = POLICY_TYPE_BY_CONTROL_PREFIX[id.split('-')[0] ?? ''];
221
+ const owner = policyTypeForControl(id);
235
222
  return owner === undefined || owner === policyType;
236
223
  })
237
224
  ),
@@ -267,7 +254,7 @@ export function sectionReadResult(
267
254
  const found = Object.keys(controls).length > 0 ? { controls } : {};
268
255
  // A control id asked of a document that does not state controls: name the policy that does.
269
256
  const asControl = options.controlIds ? undefined : unknownSections.find((id) => isControlId(id));
270
- const policy = asControl ? policyOfControl(asControl) : undefined;
257
+ const policy = asControl ? policyNamedByControl(asControl) : undefined;
271
258
  const controlNote = asControl
272
259
  ? {
273
260
  note: policy