@bicon/mcp-composer 0.3.2 → 0.3.5

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.mjs +60 -20
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -12,7 +12,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
12
12
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13
13
 
14
14
  // src/version.ts
15
- var COMPOSER_VERSION = true ? "0.3.2" : readPackageVersion();
15
+ var COMPOSER_VERSION = true ? "0.3.5" : readPackageVersion();
16
16
 
17
17
  // ../../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
18
18
  var external_exports = {};
@@ -4193,6 +4193,7 @@ var ListSnapshotsResponseItem = objectType({
4193
4193
  "initiatives": arrayType(objectType({
4194
4194
  "id": stringType(),
4195
4195
  "name": stringType(),
4196
+ "externalId": stringType().optional().describe("The organisation's own reference for the initiative (project number, Jira key, IC id, \u2026)"),
4196
4197
  "businessUnit": stringType(),
4197
4198
  "owner": stringType(),
4198
4199
  "description": stringType(),
@@ -4445,6 +4446,7 @@ var CreateSnapshotBody = objectType({
4445
4446
  "initiatives": arrayType(objectType({
4446
4447
  "id": stringType(),
4447
4448
  "name": stringType(),
4449
+ "externalId": stringType().optional().describe("The organisation's own reference for the initiative (project number, Jira key, IC id, \u2026)"),
4448
4450
  "businessUnit": stringType(),
4449
4451
  "owner": stringType(),
4450
4452
  "description": stringType(),
@@ -4702,6 +4704,7 @@ var RenameSnapshotResponse = objectType({
4702
4704
  "initiatives": arrayType(objectType({
4703
4705
  "id": stringType(),
4704
4706
  "name": stringType(),
4707
+ "externalId": stringType().optional().describe("The organisation's own reference for the initiative (project number, Jira key, IC id, \u2026)"),
4705
4708
  "businessUnit": stringType(),
4706
4709
  "owner": stringType(),
4707
4710
  "description": stringType(),
@@ -4950,6 +4953,7 @@ var DeleteSnapshotParams = objectType({
4950
4953
  var ListInitiativesResponseItem = objectType({
4951
4954
  "id": stringType(),
4952
4955
  "name": stringType(),
4956
+ "externalId": stringType().optional().describe("The organisation's own reference for the initiative (project number, Jira key, IC id, \u2026)"),
4953
4957
  "businessUnit": stringType(),
4954
4958
  "owner": stringType(),
4955
4959
  "description": stringType(),
@@ -5194,6 +5198,7 @@ var ListInitiativesResponse = arrayType(ListInitiativesResponseItem);
5194
5198
  var CreateInitiativeBody = objectType({
5195
5199
  "id": stringType(),
5196
5200
  "name": stringType(),
5201
+ "externalId": stringType().optional().describe("The organisation's own reference for the initiative (project number, Jira key, IC id, \u2026)"),
5197
5202
  "businessUnit": stringType(),
5198
5203
  "owner": stringType(),
5199
5204
  "description": stringType(),
@@ -5446,6 +5451,7 @@ var GetInitiativeParams = objectType({
5446
5451
  var GetInitiativeResponse = objectType({
5447
5452
  "id": stringType(),
5448
5453
  "name": stringType(),
5454
+ "externalId": stringType().optional().describe("The organisation's own reference for the initiative (project number, Jira key, IC id, \u2026)"),
5449
5455
  "businessUnit": stringType(),
5450
5456
  "owner": stringType(),
5451
5457
  "description": stringType(),
@@ -5692,6 +5698,7 @@ var UpdateInitiativeParams = objectType({
5692
5698
  var UpdateInitiativeBody = objectType({
5693
5699
  "id": stringType(),
5694
5700
  "name": stringType(),
5701
+ "externalId": stringType().optional().describe("The organisation's own reference for the initiative (project number, Jira key, IC id, \u2026)"),
5695
5702
  "businessUnit": stringType(),
5696
5703
  "owner": stringType(),
5697
5704
  "description": stringType(),
@@ -5941,6 +5948,7 @@ var UpdateInitiativeBody = objectType({
5941
5948
  var UpdateInitiativeResponse = objectType({
5942
5949
  "id": stringType(),
5943
5950
  "name": stringType(),
5951
+ "externalId": stringType().optional().describe("The organisation's own reference for the initiative (project number, Jira key, IC id, \u2026)"),
5944
5952
  "businessUnit": stringType(),
5945
5953
  "owner": stringType(),
5946
5954
  "description": stringType(),
@@ -8602,6 +8610,13 @@ var REQUIRED_FIELDS = [
8602
8610
  ];
8603
8611
  var OPTIONAL_SECTIONS = [
8604
8612
  { key: "tags", hint: { en: "Free-text tags", de: "Freie Schlagworte" } },
8613
+ {
8614
+ key: "externalId",
8615
+ hint: {
8616
+ en: "The organisation's own initiative reference (project number, Jira key, IC id) \u2014 the app's 'Initiative ID' field",
8617
+ de: "Eigene Referenz der Organisation (Projektnummer, Jira-Key, IC-Nummer) \u2014 das Feld \u201EInitiativen-ID\u201C der App"
8618
+ }
8619
+ },
8605
8620
  {
8606
8621
  key: "docsUrl / pmUrl",
8607
8622
  hint: {
@@ -8726,19 +8741,7 @@ function buildSchemaDescriptor(sectionShapes) {
8726
8741
  };
8727
8742
  }
8728
8743
 
8729
- // src/schema.ts
8730
- function contractObjectShape() {
8731
- const def = CreateInitiativeBody._def;
8732
- if (def.typeName === "ZodIntersection" && def.left && def.right) return { ...def.left.shape, ...def.right.shape };
8733
- return CreateInitiativeBody.shape;
8734
- }
8735
- var contractShape = contractObjectShape();
8736
- function contractSection(key) {
8737
- const s = contractShape[key];
8738
- if (!s) throw new Error(`contract has no section ${key}`);
8739
- return s instanceof external_exports.ZodOptional ? s.unwrap() : s;
8740
- }
8741
- var keepDescription = (from, to) => from.description ? to.describe(from.description) : to;
8744
+ // ../../initiative-model/src/dates.ts
8742
8745
  var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
8743
8746
  var DMY = /^(\d{1,2})[./](\d{1,2})[./](\d{4})$/;
8744
8747
  function normaliseDateInput(v) {
@@ -8748,12 +8751,30 @@ function normaliseDateInput(v) {
8748
8751
  const [, d, mo, y] = m;
8749
8752
  return `${y}-${mo.padStart(2, "0")}-${d.padStart(2, "0")}`;
8750
8753
  }
8754
+ function isCalendarParts(y, m, d) {
8755
+ const dt = /* @__PURE__ */ new Date(0);
8756
+ dt.setUTCFullYear(y, m - 1, d);
8757
+ return dt.getUTCFullYear() === y && dt.getUTCMonth() === m - 1 && dt.getUTCDate() === d;
8758
+ }
8751
8759
  function isCalendarDate(s) {
8752
8760
  if (!ISO_DATE.test(s)) return false;
8753
8761
  const [y, m, d] = s.split("-").map(Number);
8754
- const dt = new Date(Date.UTC(y, m - 1, d));
8755
- return dt.getUTCFullYear() === y && dt.getUTCMonth() === m - 1 && dt.getUTCDate() === d;
8762
+ return isCalendarParts(y, m, d);
8763
+ }
8764
+
8765
+ // src/schema.ts
8766
+ function contractObjectShape() {
8767
+ const def = CreateInitiativeBody._def;
8768
+ if (def.typeName === "ZodIntersection" && def.left && def.right) return { ...def.left.shape, ...def.right.shape };
8769
+ return CreateInitiativeBody.shape;
8756
8770
  }
8771
+ var contractShape = contractObjectShape();
8772
+ function contractSection(key) {
8773
+ const s = contractShape[key];
8774
+ if (!s) throw new Error(`contract has no section ${key}`);
8775
+ return s instanceof external_exports.ZodOptional ? s.unwrap() : s;
8776
+ }
8777
+ var keepDescription = (from, to) => from.description ? to.describe(from.description) : to;
8757
8778
  var dateField = (what) => external_exports.preprocess(normaliseDateInput, external_exports.string().refine(isCalendarDate, { message: `${what} must be a real calendar date \u2014 dd/mm/yyyy or yyyy-mm-dd` })).describe("dd/mm/yyyy (or yyyy-mm-dd)");
8758
8779
  var DATE_LIKE_KEY = /^date$|Date$|At$/;
8759
8780
  var MINTED_KEYS = /* @__PURE__ */ new Set(["id", "createdAt", "updatedAt"]);
@@ -8825,6 +8846,7 @@ var compactSpecSchema = external_exports.object({
8825
8846
  "ONLY to UPDATE an existing initiative: its id (from read_portfolio_context \u2192 lists.initiatives, or the app). The exported file then updates that initiative in place. Omit for a new initiative \u2014 never invent an id."
8826
8847
  ),
8827
8848
  name: requiredText(),
8849
+ externalId: external_exports.preprocess((v) => typeof v === "string" && v.trim() === "" ? void 0 : v, external_exports.string().trim().min(1).optional()).describe("The organisation's own reference for the initiative (project number, Jira key, IC id, \u2026) \u2014 ask for it when the organisation uses such references"),
8828
8850
  businessUnit: requiredText().describe("With a portfolio context: one of the organisation's business units (read_portfolio_context \u2192 lists.businessUnits)"),
8829
8851
  owner: requiredText(),
8830
8852
  description: requiredText(),
@@ -8944,7 +8966,7 @@ var portfolioContextSchema = external_exports.object({
8944
8966
  categories: external_exports.array(external_exports.string()),
8945
8967
  suggestedTags: external_exports.array(external_exports.string()),
8946
8968
  skills: external_exports.array(external_exports.object({ id: external_exports.string().min(1), name: external_exports.string() }).passthrough()),
8947
- initiatives: external_exports.array(external_exports.object({ id: external_exports.string().min(1), name: external_exports.string(), status: external_exports.enum(STATUS_VALUES) }).passthrough()),
8969
+ initiatives: external_exports.array(external_exports.object({ id: external_exports.string().min(1), externalId: external_exports.string().trim().optional(), name: external_exports.string(), status: external_exports.enum(STATUS_VALUES) }).passthrough()),
8948
8970
  sliders: external_exports.array(sliderSchema)
8949
8971
  }).passthrough();
8950
8972
  var CONTEXT_SUPPORTED_VERSION = PORTFOLIO_CONTEXT_VERSION;
@@ -9003,6 +9025,15 @@ function checkAgainstContext(spec, ctx) {
9003
9025
  const warnings = [];
9004
9026
  const v = versionNotice(ctx);
9005
9027
  if (v) warnings.push(v);
9028
+ const specExt = spec.externalId?.trim().toLowerCase();
9029
+ if (specExt) {
9030
+ const holder = ctx.initiatives.find((x) => (x.externalId ?? "").trim().toLowerCase() === specExt && x.id !== spec.id);
9031
+ if (holder) {
9032
+ warnings.push(
9033
+ spec.id ? `externalId "${spec.externalId.trim()}" already belongs to "${holder.name}" (${holder.id}) \u2014 two initiatives would share the same organisation reference.` : `externalId "${spec.externalId.trim()}" already belongs to "${holder.name}" (${holder.id}) \u2014 pass that id as \`id\` to UPDATE it instead of creating a duplicate.`
9034
+ );
9035
+ }
9036
+ }
9006
9037
  const bus = ctx.businessUnits;
9007
9038
  let businessUnit = spec.businessUnit;
9008
9039
  const buMatch = canonical(spec.businessUnit, bus);
@@ -9079,7 +9110,7 @@ function buildInterviewPlan(ctx, contextId) {
9079
9110
  ...allOff.length ? [`Dimension(s) with EVERY sub-dimension switched off (${allOff.join(", ")}) still need an entry in evaluation: { subs: {}, confidence } \u2014 the score is 0 there; ask only for the confidence.`] : [],
9080
9111
  ctx.businessUnits.length && ctx.categories.length ? "businessUnit MUST be one of lists.businessUnits and aiCategory one of lists.categories (matched case-insensitively, normalised to the organisation's spelling) \u2014 the Composer rejects anything else; new ones are created in the app's Settings, not in the interview." : "This organisation has no business units and/or categories configured yet \u2014 they must be added in the app's Settings and the context downloaded again before an initiative can be assembled.",
9081
9112
  "dependsOn entries must be ids from lists.initiatives; requiredSkills[].skillId must be ids from lists.skills. Never invent an id; leave the list empty when unsure.",
9082
- "To UPDATE an existing initiative instead of creating a new one, pass its id (from lists.initiatives) as `id` in the spec \u2014 the exported file then updates it in the app (Import/Export \u2192 Single initiative \u2192 'Update'). Omit id for a new initiative.",
9113
+ "To UPDATE an existing initiative instead of creating a new one, pass its id (from lists.initiatives) as `id` in the spec \u2014 the exported file then updates it in the app (Import/Export \u2192 Single initiative \u2192 'Update'). Omit id for a new initiative. Entries in lists.initiatives may carry externalId \u2014 the organisation's own reference (e.g. IC-26-118) \u2014 use it to recognise which initiative the user means. An update REPLACES the whole record: any field, section or row you omit is CLEARED \u2014 decision log, status reports and links included \u2014 so re-send everything that must survive. Exception: the organisation's externalId is carried over automatically when omitted.",
9083
9114
  ...ctx.sliderGranularity ? [`Ratings are in steps of ${ctx.sliderGranularity} (the organisation's slider step \u2014 Settings \u203A Slider granularity): ask for and send only multiples of ${ctx.sliderGranularity} between 0 and 100 (${gridExample(ctx.sliderGranularity)}); any other value is rejected.`] : [],
9084
9115
  "Dates: talk to the user in dd/mm/yyyy; you may send dates as dd/mm/yyyy, dd.mm.yyyy or yyyy-mm-dd \u2014 the Composer stores yyyy-mm-dd.",
9085
9116
  ctx.aiActEnabled ? "The organisation's EU-AI-Act module is ON: collect the AI-Act block and governance flags." : "The organisation's EU-AI-Act module is OFF: skip the AI-Act block and governance flags (the app would remove them on import).",
@@ -9103,7 +9134,7 @@ function buildInterviewPlan(ctx, contextId) {
9103
9134
  categories: ctx.categories,
9104
9135
  suggestedTags: ctx.suggestedTags,
9105
9136
  skills: ctx.skills.map((s) => ({ id: s.id, name: s.name })),
9106
- initiatives: ctx.initiatives.map((i) => ({ id: i.id, name: i.name, status: i.status }))
9137
+ initiatives: ctx.initiatives.map((i) => ({ id: i.id, ...i.externalId ? { externalId: i.externalId } : {}, name: i.name, status: i.status }))
9107
9138
  },
9108
9139
  interview: {
9109
9140
  dimensions,
@@ -9224,11 +9255,20 @@ function assembleInitiative(input, now = /* @__PURE__ */ new Date()) {
9224
9255
  return [dim, { score: dimensionScore(subs, disabled), confidence: d.confidence, subs }];
9225
9256
  })
9226
9257
  );
9258
+ let externalId = spec.externalId;
9259
+ if (externalId === void 0 && spec.id && context) {
9260
+ const kept = context.initiatives.find((i) => i.id === spec.id)?.externalId?.trim();
9261
+ if (kept) {
9262
+ externalId = kept;
9263
+ warnings.push(`externalId "${kept}" carried over from the organisation's records \u2014 send externalId explicitly to change or clear it.`);
9264
+ }
9265
+ }
9227
9266
  const stamp = now.toISOString();
9228
9267
  const aiAct = spec.aiAct;
9229
9268
  const candidate = {
9230
9269
  ...spec,
9231
9270
  id: spec.id ?? randomUUID(),
9271
+ ...externalId !== void 0 ? { externalId } : {},
9232
9272
  businessUnit,
9233
9273
  aiCategory,
9234
9274
  tags: spec.tags ?? [],
@@ -9274,7 +9314,7 @@ var IMPORT_INSTRUCTIONS = [
9274
9314
  "2. In the BICon app (signed in to the target organisation), open Import/Export.",
9275
9315
  "3. If the initiative carries EU-AI-Act data, make sure the organisation's EU-AI-Act module is switched on (Settings \u203A AI Act) \u2014 the app strips AI-Act fields while it is off.",
9276
9316
  "4. In the 'Single initiative' card, drop the file (or Browse).",
9277
- "5. Review the preview \u2014 it should show 0 warnings \u2014 then choose 'Add as new' (or 'Update' when the file carries an existing initiative's id or the same name \u2014 the app then keeps that initiative's id and history) and Confirm.",
9317
+ "5. Review the preview \u2014 it should show 0 warnings \u2014 then choose 'Add as new' (or 'Update' when the file carries an existing initiative's id, Initiative ID or name \u2014 the app keeps that initiative's id and creation date, and REPLACES everything else with this file, decision log and status reports included) and Confirm.",
9278
9318
  "6. The initiative appears in the Inventory with the derived dimension scores."
9279
9319
  ].join("\n");
9280
9320
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bicon/mcp-composer",
3
- "version": "0.3.2",
3
+ "version": "0.3.5",
4
4
  "description": "MCP server that helps an AI client compose a valid BICon portfolio initiative and hand it back as an import file (draft-first, no auth, no API calls).",
5
5
  "license": "UNLICENSED",
6
6
  "author": "BICon Establishment",