@bicon/mcp-composer 0.4.0 → 0.5.0

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 (3) hide show
  1. package/README.md +2 -2
  2. package/dist/index.mjs +157 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -24,7 +24,7 @@ via API keys is a separate, security-reviewed phase.
24
24
  | Name | What it does |
25
25
  |---|---|
26
26
  | resource `initiative://schema` / tool `get_initiative_schema` | The field catalog: required fields, enum values, the four dimensions with their named sub-dimensions (EN/DE labels + help text, plus the wording the app uses in digital / innovation portfolios), the exact shape of every optional section, the EU-AI-Act vocabularies, and the handoff steps. **Read this first.** |
27
- | `assemble_initiative` | Compact spec (metadata + named sub-ratings 0–100 + confidence per dimension + optional sections, optionally the organisation's `context`) → fully-formed initiative validated against the API contract (`CreateInitiativeBody` from `@workspace/api-zod`). Mints the initiative id and every nested row id (decision-log entries, status reports, incidents — a client never invents ids), **derives** the four dimension scores (the app's own scoring function), accepts dates as dd/mm/yyyy, dd.mm.yyyy or yyyy-mm-dd, returns `{ok, initiative, derivedScores, warnings, errors, contextId}`. Pass `id` (an existing initiative's) only to **update** that initiative. |
27
+ | `assemble_initiative` | Compact spec (metadata + named sub-ratings 0–100 + confidence per dimension + optional sections, optionally the organisation's `context`) → fully-formed initiative validated against the API contract (`CreateInitiativeBody` from `@workspace/api-zod`). Mints the initiative id and every nested row id (decision-log entries, status reports, milestones, incidents — a client never invents ids), **derives** the four dimension scores (the app's own scoring function), accepts dates as dd/mm/yyyy, dd.mm.yyyy or yyyy-mm-dd, returns `{ok, initiative, derivedScores, warnings, errors, contextId}`. Pass `id` (an existing initiative's) only to **update** that initiative. |
28
28
  | `export_initiative_file` | Takes the **same compact spec**, assembles it and wraps the result in the app's single-initiative JSON envelope; returns `{ok, filename, json, importInstructions, derivedScores, warnings}`. There is one pipeline — a hand-edited initiative object is not an input, so client-supplied scores or unknown keys can never bypass assembly. |
29
29
  | `read_portfolio_context` | Takes the organisation's **portfolio-context file** (see below) and returns the interview plan for that organisation — its own labels, help texts and rating notes per sub-dimension, which sub-dimensions are switched off, its business units, categories, tags, skills (id + name), non-archived initiatives (id + name) and the capability map's capabilities (id, name, domain, importance, current → target maturity; context version 2), currency, discount rate, whether the EU-AI-Act module is on — plus a short **contextId** that stands in for the file in the two other tools. |
30
30
  | `composer_version` | Returns `{composerVersion, capabilities}`. Every *successful* tool result also carries `composerVersion`. |
@@ -93,7 +93,7 @@ vocabularies (`aiActVocabularies` in the descriptor; the app drops unknown
93
93
  obligation keys and never shows unknown artifact keys), the score derivation —
94
94
  is checked in the assembler, which reports **all** problems of a call at once
95
95
  with the allowed values in the message (id shapes included). Row ids of
96
- decision-log entries, status reports and incidents are minted by the
96
+ decision-log entries, status reports, milestones and incidents are minted by the
97
97
  Composer — a client-supplied UUID is kept, anything else replaced — because
98
98
  the app keys those rows per initiative and a re-used id from another
99
99
  initiative is exactly what collided on import once.
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.4.0" : readPackageVersion();
15
+ var COMPOSER_VERSION = true ? "0.5.0" : readPackageVersion();
16
16
 
17
17
  // ../../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
18
18
  var external_exports = {};
@@ -4170,7 +4170,7 @@ var ListOrganizationMembersResponseItem = objectType({
4170
4170
  "email": stringType(),
4171
4171
  "displayName": stringType().nullable(),
4172
4172
  "role": enumType(["admin", "member", "viewer"]),
4173
- "permissions": arrayType(stringType()).describe("App-managed permission overrides on top of the fixed role (free-form strings, not a closed enum). Known keys: manage_skills, manage_ai_act, manage_metadata, manage_stage_gate, manage_economics, manage_decision_log, manage_dependencies, manage_required_skills, manage_scoring, manage_status_report, manage_prioritisation, manage_capabilities, manage_capability_impact.")
4173
+ "permissions": arrayType(stringType()).describe("App-managed permission overrides on top of the fixed role (free-form strings, not a closed enum). Known keys: manage_skills, manage_ai_act, manage_metadata, manage_stage_gate, manage_economics, manage_decision_log, manage_dependencies, manage_required_skills, manage_scoring, manage_status_report, manage_prioritisation, manage_capabilities, manage_capability_impact, manage_milestones.")
4174
4174
  });
4175
4175
  var ListOrganizationMembersResponse = arrayType(ListOrganizationMembersResponseItem);
4176
4176
  var SyncOrganizationMembersResponse = objectType({
@@ -4180,18 +4180,18 @@ var SyncOrganizationMembersResponse = objectType({
4180
4180
  var GetMyOrganizationAccessResponse = objectType({
4181
4181
  "userId": stringType().describe(`The caller's own local user id (matches AuthorRef.userId on initiatives), so the client can tell its own changes apart from a teammate's \u2014 e.g. to suppress the "updated by X" collaboration banner when an edit echoing back is the caller's own save.`),
4182
4182
  "role": enumType(["admin", "member", "viewer"]),
4183
- "permissions": arrayType(stringType()).describe("App-managed permission overrides on top of the fixed role (free-form strings, not a closed enum). Known keys: manage_skills, manage_ai_act, manage_metadata, manage_stage_gate, manage_economics, manage_decision_log, manage_dependencies, manage_required_skills, manage_scoring, manage_status_report, manage_prioritisation, manage_capabilities, manage_capability_impact."),
4183
+ "permissions": arrayType(stringType()).describe("App-managed permission overrides on top of the fixed role (free-form strings, not a closed enum). Known keys: manage_skills, manage_ai_act, manage_metadata, manage_stage_gate, manage_economics, manage_decision_log, manage_dependencies, manage_required_skills, manage_scoring, manage_status_report, manage_prioritisation, manage_capabilities, manage_capability_impact, manage_milestones."),
4184
4184
  "initiativeLimit": numberType().describe("The most initiatives this organisation may hold, archived ones included. Creating one more answers 507 with code initiative_limit_reached, so the client blocks New, Duplicate and Import up front.")
4185
4185
  });
4186
4186
  var UpdateOrganizationMemberPermissionsParams = objectType({
4187
4187
  "userId": coerce.string().uuid()
4188
4188
  });
4189
4189
  var UpdateOrganizationMemberPermissionsBody = objectType({
4190
- "permissions": arrayType(stringType()).describe("App-managed permission overrides on top of the fixed role (free-form strings, not a closed enum). Known keys: manage_skills, manage_ai_act, manage_metadata, manage_stage_gate, manage_economics, manage_decision_log, manage_dependencies, manage_required_skills, manage_scoring, manage_status_report, manage_prioritisation, manage_capabilities, manage_capability_impact.")
4190
+ "permissions": arrayType(stringType()).describe("App-managed permission overrides on top of the fixed role (free-form strings, not a closed enum). Known keys: manage_skills, manage_ai_act, manage_metadata, manage_stage_gate, manage_economics, manage_decision_log, manage_dependencies, manage_required_skills, manage_scoring, manage_status_report, manage_prioritisation, manage_capabilities, manage_capability_impact, manage_milestones.")
4191
4191
  });
4192
4192
  var UpdateOrganizationMemberPermissionsResponse = objectType({
4193
4193
  "userId": stringType().uuid(),
4194
- "permissions": arrayType(stringType()).describe("App-managed permission overrides on top of the fixed role (free-form strings, not a closed enum). Known keys: manage_skills, manage_ai_act, manage_metadata, manage_stage_gate, manage_economics, manage_decision_log, manage_dependencies, manage_required_skills, manage_scoring, manage_status_report, manage_prioritisation, manage_capabilities, manage_capability_impact.")
4194
+ "permissions": arrayType(stringType()).describe("App-managed permission overrides on top of the fixed role (free-form strings, not a closed enum). Known keys: manage_skills, manage_ai_act, manage_metadata, manage_stage_gate, manage_economics, manage_decision_log, manage_dependencies, manage_required_skills, manage_scoring, manage_status_report, manage_prioritisation, manage_capabilities, manage_capability_impact, manage_milestones.")
4195
4195
  });
4196
4196
  var ListPermissionProfilesResponseItem = objectType({
4197
4197
  "id": stringType().uuid(),
@@ -4280,6 +4280,12 @@ var listSnapshotsResponseInitiativesItemStatusReportsItemBudgetCommentMax = 1e4;
4280
4280
  var listSnapshotsResponseInitiativesItemStatusReportsItemSkillsCommentMax = 1e4;
4281
4281
  var listSnapshotsResponseInitiativesItemStatusReportsItemSummaryMax = 1e4;
4282
4282
  var listSnapshotsResponseInitiativesItemStatusReportsMax = 1e3;
4283
+ var listSnapshotsResponseInitiativesItemMilestonesItemTitleMax = 300;
4284
+ var listSnapshotsResponseInitiativesItemMilestonesItemBaselineDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
4285
+ var listSnapshotsResponseInitiativesItemMilestonesItemForecastDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
4286
+ var listSnapshotsResponseInitiativesItemMilestonesItemDoneDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
4287
+ var listSnapshotsResponseInitiativesItemMilestonesItemNoteMax = 1e4;
4288
+ var listSnapshotsResponseInitiativesItemMilestonesMax = 30;
4283
4289
  var listSnapshotsResponseInitiativesItemDependsOnMax = 1e3;
4284
4290
  var listSnapshotsResponseInitiativesItemRequiredSkillsItemRunFteMin = 0;
4285
4291
  var listSnapshotsResponseInitiativesItemRequiredSkillsMax = 1e3;
@@ -4481,6 +4487,14 @@ var ListSnapshotsResponseItem = objectType({
4481
4487
  }),
4482
4488
  "summary": stringType().max(listSnapshotsResponseInitiativesItemStatusReportsItemSummaryMax).optional()
4483
4489
  })).max(listSnapshotsResponseInitiativesItemStatusReportsMax).optional(),
4490
+ "milestones": arrayType(objectType({
4491
+ "id": stringType(),
4492
+ "title": stringType().min(1).max(listSnapshotsResponseInitiativesItemMilestonesItemTitleMax),
4493
+ "baselineDate": stringType().regex(listSnapshotsResponseInitiativesItemMilestonesItemBaselineDateRegExp),
4494
+ "forecastDate": stringType().regex(listSnapshotsResponseInitiativesItemMilestonesItemForecastDateRegExp).optional(),
4495
+ "doneDate": stringType().regex(listSnapshotsResponseInitiativesItemMilestonesItemDoneDateRegExp).optional(),
4496
+ "note": stringType().max(listSnapshotsResponseInitiativesItemMilestonesItemNoteMax).optional()
4497
+ }).describe("A dated intermediate result (docs/milestones-concept-2026-10-17.md). baselineDate is the agreed plan, forecastDate today's expectation (absent = the baseline), doneDate marks it reached; all yyyy-mm-dd.")).max(listSnapshotsResponseInitiativesItemMilestonesMax).optional(),
4484
4498
  "dependsOn": arrayType(stringType()).max(listSnapshotsResponseInitiativesItemDependsOnMax),
4485
4499
  "requiredSkills": arrayType(objectType({
4486
4500
  "skillId": stringType(),
@@ -4618,6 +4632,12 @@ var createSnapshotBodyInitiativesItemStatusReportsItemBudgetCommentMax = 1e4;
4618
4632
  var createSnapshotBodyInitiativesItemStatusReportsItemSkillsCommentMax = 1e4;
4619
4633
  var createSnapshotBodyInitiativesItemStatusReportsItemSummaryMax = 1e4;
4620
4634
  var createSnapshotBodyInitiativesItemStatusReportsMax = 1e3;
4635
+ var createSnapshotBodyInitiativesItemMilestonesItemTitleMax = 300;
4636
+ var createSnapshotBodyInitiativesItemMilestonesItemBaselineDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
4637
+ var createSnapshotBodyInitiativesItemMilestonesItemForecastDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
4638
+ var createSnapshotBodyInitiativesItemMilestonesItemDoneDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
4639
+ var createSnapshotBodyInitiativesItemMilestonesItemNoteMax = 1e4;
4640
+ var createSnapshotBodyInitiativesItemMilestonesMax = 30;
4621
4641
  var createSnapshotBodyInitiativesItemDependsOnMax = 1e3;
4622
4642
  var createSnapshotBodyInitiativesItemRequiredSkillsItemRunFteMin = 0;
4623
4643
  var createSnapshotBodyInitiativesItemRequiredSkillsMax = 1e3;
@@ -4819,6 +4839,14 @@ var CreateSnapshotBody = objectType({
4819
4839
  }),
4820
4840
  "summary": stringType().max(createSnapshotBodyInitiativesItemStatusReportsItemSummaryMax).optional()
4821
4841
  })).max(createSnapshotBodyInitiativesItemStatusReportsMax).optional(),
4842
+ "milestones": arrayType(objectType({
4843
+ "id": stringType(),
4844
+ "title": stringType().min(1).max(createSnapshotBodyInitiativesItemMilestonesItemTitleMax),
4845
+ "baselineDate": stringType().regex(createSnapshotBodyInitiativesItemMilestonesItemBaselineDateRegExp),
4846
+ "forecastDate": stringType().regex(createSnapshotBodyInitiativesItemMilestonesItemForecastDateRegExp).optional(),
4847
+ "doneDate": stringType().regex(createSnapshotBodyInitiativesItemMilestonesItemDoneDateRegExp).optional(),
4848
+ "note": stringType().max(createSnapshotBodyInitiativesItemMilestonesItemNoteMax).optional()
4849
+ }).describe("A dated intermediate result (docs/milestones-concept-2026-10-17.md). baselineDate is the agreed plan, forecastDate today's expectation (absent = the baseline), doneDate marks it reached; all yyyy-mm-dd.")).max(createSnapshotBodyInitiativesItemMilestonesMax).optional(),
4822
4850
  "dependsOn": arrayType(stringType()).max(createSnapshotBodyInitiativesItemDependsOnMax),
4823
4851
  "requiredSkills": arrayType(objectType({
4824
4852
  "skillId": stringType(),
@@ -4962,6 +4990,12 @@ var renameSnapshotResponseInitiativesItemStatusReportsItemBudgetCommentMax = 1e4
4962
4990
  var renameSnapshotResponseInitiativesItemStatusReportsItemSkillsCommentMax = 1e4;
4963
4991
  var renameSnapshotResponseInitiativesItemStatusReportsItemSummaryMax = 1e4;
4964
4992
  var renameSnapshotResponseInitiativesItemStatusReportsMax = 1e3;
4993
+ var renameSnapshotResponseInitiativesItemMilestonesItemTitleMax = 300;
4994
+ var renameSnapshotResponseInitiativesItemMilestonesItemBaselineDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
4995
+ var renameSnapshotResponseInitiativesItemMilestonesItemForecastDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
4996
+ var renameSnapshotResponseInitiativesItemMilestonesItemDoneDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
4997
+ var renameSnapshotResponseInitiativesItemMilestonesItemNoteMax = 1e4;
4998
+ var renameSnapshotResponseInitiativesItemMilestonesMax = 30;
4965
4999
  var renameSnapshotResponseInitiativesItemDependsOnMax = 1e3;
4966
5000
  var renameSnapshotResponseInitiativesItemRequiredSkillsItemRunFteMin = 0;
4967
5001
  var renameSnapshotResponseInitiativesItemRequiredSkillsMax = 1e3;
@@ -5163,6 +5197,14 @@ var RenameSnapshotResponse = objectType({
5163
5197
  }),
5164
5198
  "summary": stringType().max(renameSnapshotResponseInitiativesItemStatusReportsItemSummaryMax).optional()
5165
5199
  })).max(renameSnapshotResponseInitiativesItemStatusReportsMax).optional(),
5200
+ "milestones": arrayType(objectType({
5201
+ "id": stringType(),
5202
+ "title": stringType().min(1).max(renameSnapshotResponseInitiativesItemMilestonesItemTitleMax),
5203
+ "baselineDate": stringType().regex(renameSnapshotResponseInitiativesItemMilestonesItemBaselineDateRegExp),
5204
+ "forecastDate": stringType().regex(renameSnapshotResponseInitiativesItemMilestonesItemForecastDateRegExp).optional(),
5205
+ "doneDate": stringType().regex(renameSnapshotResponseInitiativesItemMilestonesItemDoneDateRegExp).optional(),
5206
+ "note": stringType().max(renameSnapshotResponseInitiativesItemMilestonesItemNoteMax).optional()
5207
+ }).describe("A dated intermediate result (docs/milestones-concept-2026-10-17.md). baselineDate is the agreed plan, forecastDate today's expectation (absent = the baseline), doneDate marks it reached; all yyyy-mm-dd.")).max(renameSnapshotResponseInitiativesItemMilestonesMax).optional(),
5166
5208
  "dependsOn": arrayType(stringType()).max(renameSnapshotResponseInitiativesItemDependsOnMax),
5167
5209
  "requiredSkills": arrayType(objectType({
5168
5210
  "skillId": stringType(),
@@ -5301,6 +5343,12 @@ var listInitiativesResponseStatusReportsItemBudgetCommentMax = 1e4;
5301
5343
  var listInitiativesResponseStatusReportsItemSkillsCommentMax = 1e4;
5302
5344
  var listInitiativesResponseStatusReportsItemSummaryMax = 1e4;
5303
5345
  var listInitiativesResponseStatusReportsMax = 1e3;
5346
+ var listInitiativesResponseMilestonesItemTitleMax = 300;
5347
+ var listInitiativesResponseMilestonesItemBaselineDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
5348
+ var listInitiativesResponseMilestonesItemForecastDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
5349
+ var listInitiativesResponseMilestonesItemDoneDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
5350
+ var listInitiativesResponseMilestonesItemNoteMax = 1e4;
5351
+ var listInitiativesResponseMilestonesMax = 30;
5304
5352
  var listInitiativesResponseDependsOnMax = 1e3;
5305
5353
  var listInitiativesResponseRequiredSkillsItemRunFteMin = 0;
5306
5354
  var listInitiativesResponseRequiredSkillsMax = 1e3;
@@ -5496,6 +5544,14 @@ var ListInitiativesResponseItem = objectType({
5496
5544
  }),
5497
5545
  "summary": stringType().max(listInitiativesResponseStatusReportsItemSummaryMax).optional()
5498
5546
  })).max(listInitiativesResponseStatusReportsMax).optional(),
5547
+ "milestones": arrayType(objectType({
5548
+ "id": stringType(),
5549
+ "title": stringType().min(1).max(listInitiativesResponseMilestonesItemTitleMax),
5550
+ "baselineDate": stringType().regex(listInitiativesResponseMilestonesItemBaselineDateRegExp),
5551
+ "forecastDate": stringType().regex(listInitiativesResponseMilestonesItemForecastDateRegExp).optional(),
5552
+ "doneDate": stringType().regex(listInitiativesResponseMilestonesItemDoneDateRegExp).optional(),
5553
+ "note": stringType().max(listInitiativesResponseMilestonesItemNoteMax).optional()
5554
+ }).describe("A dated intermediate result (docs/milestones-concept-2026-10-17.md). baselineDate is the agreed plan, forecastDate today's expectation (absent = the baseline), doneDate marks it reached; all yyyy-mm-dd.")).max(listInitiativesResponseMilestonesMax).optional(),
5499
5555
  "dependsOn": arrayType(stringType()).max(listInitiativesResponseDependsOnMax),
5500
5556
  "requiredSkills": arrayType(objectType({
5501
5557
  "skillId": stringType(),
@@ -5630,6 +5686,12 @@ var createInitiativeBodyOneStatusReportsItemBudgetCommentMax = 1e4;
5630
5686
  var createInitiativeBodyOneStatusReportsItemSkillsCommentMax = 1e4;
5631
5687
  var createInitiativeBodyOneStatusReportsItemSummaryMax = 1e4;
5632
5688
  var createInitiativeBodyOneStatusReportsMax = 1e3;
5689
+ var createInitiativeBodyOneMilestonesItemTitleMax = 300;
5690
+ var createInitiativeBodyOneMilestonesItemBaselineDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
5691
+ var createInitiativeBodyOneMilestonesItemForecastDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
5692
+ var createInitiativeBodyOneMilestonesItemDoneDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
5693
+ var createInitiativeBodyOneMilestonesItemNoteMax = 1e4;
5694
+ var createInitiativeBodyOneMilestonesMax = 30;
5633
5695
  var createInitiativeBodyOneDependsOnMax = 1e3;
5634
5696
  var createInitiativeBodyOneRequiredSkillsItemRunFteMin = 0;
5635
5697
  var createInitiativeBodyOneRequiredSkillsMax = 1e3;
@@ -5825,6 +5887,14 @@ var CreateInitiativeBody = objectType({
5825
5887
  }),
5826
5888
  "summary": stringType().max(createInitiativeBodyOneStatusReportsItemSummaryMax).optional()
5827
5889
  })).max(createInitiativeBodyOneStatusReportsMax).optional(),
5890
+ "milestones": arrayType(objectType({
5891
+ "id": stringType(),
5892
+ "title": stringType().min(1).max(createInitiativeBodyOneMilestonesItemTitleMax),
5893
+ "baselineDate": stringType().regex(createInitiativeBodyOneMilestonesItemBaselineDateRegExp),
5894
+ "forecastDate": stringType().regex(createInitiativeBodyOneMilestonesItemForecastDateRegExp).optional(),
5895
+ "doneDate": stringType().regex(createInitiativeBodyOneMilestonesItemDoneDateRegExp).optional(),
5896
+ "note": stringType().max(createInitiativeBodyOneMilestonesItemNoteMax).optional()
5897
+ }).describe("A dated intermediate result (docs/milestones-concept-2026-10-17.md). baselineDate is the agreed plan, forecastDate today's expectation (absent = the baseline), doneDate marks it reached; all yyyy-mm-dd.")).max(createInitiativeBodyOneMilestonesMax).optional(),
5828
5898
  "dependsOn": arrayType(stringType()).max(createInitiativeBodyOneDependsOnMax),
5829
5899
  "requiredSkills": arrayType(objectType({
5830
5900
  "skillId": stringType(),
@@ -5969,6 +6039,12 @@ var getInitiativeResponseStatusReportsItemBudgetCommentMax = 1e4;
5969
6039
  var getInitiativeResponseStatusReportsItemSkillsCommentMax = 1e4;
5970
6040
  var getInitiativeResponseStatusReportsItemSummaryMax = 1e4;
5971
6041
  var getInitiativeResponseStatusReportsMax = 1e3;
6042
+ var getInitiativeResponseMilestonesItemTitleMax = 300;
6043
+ var getInitiativeResponseMilestonesItemBaselineDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
6044
+ var getInitiativeResponseMilestonesItemForecastDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
6045
+ var getInitiativeResponseMilestonesItemDoneDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
6046
+ var getInitiativeResponseMilestonesItemNoteMax = 1e4;
6047
+ var getInitiativeResponseMilestonesMax = 30;
5972
6048
  var getInitiativeResponseDependsOnMax = 1e3;
5973
6049
  var getInitiativeResponseRequiredSkillsItemRunFteMin = 0;
5974
6050
  var getInitiativeResponseRequiredSkillsMax = 1e3;
@@ -6164,6 +6240,14 @@ var GetInitiativeResponse = objectType({
6164
6240
  }),
6165
6241
  "summary": stringType().max(getInitiativeResponseStatusReportsItemSummaryMax).optional()
6166
6242
  })).max(getInitiativeResponseStatusReportsMax).optional(),
6243
+ "milestones": arrayType(objectType({
6244
+ "id": stringType(),
6245
+ "title": stringType().min(1).max(getInitiativeResponseMilestonesItemTitleMax),
6246
+ "baselineDate": stringType().regex(getInitiativeResponseMilestonesItemBaselineDateRegExp),
6247
+ "forecastDate": stringType().regex(getInitiativeResponseMilestonesItemForecastDateRegExp).optional(),
6248
+ "doneDate": stringType().regex(getInitiativeResponseMilestonesItemDoneDateRegExp).optional(),
6249
+ "note": stringType().max(getInitiativeResponseMilestonesItemNoteMax).optional()
6250
+ }).describe("A dated intermediate result (docs/milestones-concept-2026-10-17.md). baselineDate is the agreed plan, forecastDate today's expectation (absent = the baseline), doneDate marks it reached; all yyyy-mm-dd.")).max(getInitiativeResponseMilestonesMax).optional(),
6167
6251
  "dependsOn": arrayType(stringType()).max(getInitiativeResponseDependsOnMax),
6168
6252
  "requiredSkills": arrayType(objectType({
6169
6253
  "skillId": stringType(),
@@ -6300,6 +6384,12 @@ var updateInitiativeBodyOneStatusReportsItemBudgetCommentMax = 1e4;
6300
6384
  var updateInitiativeBodyOneStatusReportsItemSkillsCommentMax = 1e4;
6301
6385
  var updateInitiativeBodyOneStatusReportsItemSummaryMax = 1e4;
6302
6386
  var updateInitiativeBodyOneStatusReportsMax = 1e3;
6387
+ var updateInitiativeBodyOneMilestonesItemTitleMax = 300;
6388
+ var updateInitiativeBodyOneMilestonesItemBaselineDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
6389
+ var updateInitiativeBodyOneMilestonesItemForecastDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
6390
+ var updateInitiativeBodyOneMilestonesItemDoneDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
6391
+ var updateInitiativeBodyOneMilestonesItemNoteMax = 1e4;
6392
+ var updateInitiativeBodyOneMilestonesMax = 30;
6303
6393
  var updateInitiativeBodyOneDependsOnMax = 1e3;
6304
6394
  var updateInitiativeBodyOneRequiredSkillsItemRunFteMin = 0;
6305
6395
  var updateInitiativeBodyOneRequiredSkillsMax = 1e3;
@@ -6495,6 +6585,14 @@ var UpdateInitiativeBody = objectType({
6495
6585
  }),
6496
6586
  "summary": stringType().max(updateInitiativeBodyOneStatusReportsItemSummaryMax).optional()
6497
6587
  })).max(updateInitiativeBodyOneStatusReportsMax).optional(),
6588
+ "milestones": arrayType(objectType({
6589
+ "id": stringType(),
6590
+ "title": stringType().min(1).max(updateInitiativeBodyOneMilestonesItemTitleMax),
6591
+ "baselineDate": stringType().regex(updateInitiativeBodyOneMilestonesItemBaselineDateRegExp),
6592
+ "forecastDate": stringType().regex(updateInitiativeBodyOneMilestonesItemForecastDateRegExp).optional(),
6593
+ "doneDate": stringType().regex(updateInitiativeBodyOneMilestonesItemDoneDateRegExp).optional(),
6594
+ "note": stringType().max(updateInitiativeBodyOneMilestonesItemNoteMax).optional()
6595
+ }).describe("A dated intermediate result (docs/milestones-concept-2026-10-17.md). baselineDate is the agreed plan, forecastDate today's expectation (absent = the baseline), doneDate marks it reached; all yyyy-mm-dd.")).max(updateInitiativeBodyOneMilestonesMax).optional(),
6498
6596
  "dependsOn": arrayType(stringType()).max(updateInitiativeBodyOneDependsOnMax),
6499
6597
  "requiredSkills": arrayType(objectType({
6500
6598
  "skillId": stringType(),
@@ -6636,6 +6734,12 @@ var updateInitiativeResponseStatusReportsItemBudgetCommentMax = 1e4;
6636
6734
  var updateInitiativeResponseStatusReportsItemSkillsCommentMax = 1e4;
6637
6735
  var updateInitiativeResponseStatusReportsItemSummaryMax = 1e4;
6638
6736
  var updateInitiativeResponseStatusReportsMax = 1e3;
6737
+ var updateInitiativeResponseMilestonesItemTitleMax = 300;
6738
+ var updateInitiativeResponseMilestonesItemBaselineDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
6739
+ var updateInitiativeResponseMilestonesItemForecastDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
6740
+ var updateInitiativeResponseMilestonesItemDoneDateRegExp = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
6741
+ var updateInitiativeResponseMilestonesItemNoteMax = 1e4;
6742
+ var updateInitiativeResponseMilestonesMax = 30;
6639
6743
  var updateInitiativeResponseDependsOnMax = 1e3;
6640
6744
  var updateInitiativeResponseRequiredSkillsItemRunFteMin = 0;
6641
6745
  var updateInitiativeResponseRequiredSkillsMax = 1e3;
@@ -6831,6 +6935,14 @@ var UpdateInitiativeResponse = objectType({
6831
6935
  }),
6832
6936
  "summary": stringType().max(updateInitiativeResponseStatusReportsItemSummaryMax).optional()
6833
6937
  })).max(updateInitiativeResponseStatusReportsMax).optional(),
6938
+ "milestones": arrayType(objectType({
6939
+ "id": stringType(),
6940
+ "title": stringType().min(1).max(updateInitiativeResponseMilestonesItemTitleMax),
6941
+ "baselineDate": stringType().regex(updateInitiativeResponseMilestonesItemBaselineDateRegExp),
6942
+ "forecastDate": stringType().regex(updateInitiativeResponseMilestonesItemForecastDateRegExp).optional(),
6943
+ "doneDate": stringType().regex(updateInitiativeResponseMilestonesItemDoneDateRegExp).optional(),
6944
+ "note": stringType().max(updateInitiativeResponseMilestonesItemNoteMax).optional()
6945
+ }).describe("A dated intermediate result (docs/milestones-concept-2026-10-17.md). baselineDate is the agreed plan, forecastDate today's expectation (absent = the baseline), doneDate marks it reached; all yyyy-mm-dd.")).max(updateInitiativeResponseMilestonesMax).optional(),
6834
6946
  "dependsOn": arrayType(stringType()).max(updateInitiativeResponseDependsOnMax),
6835
6947
  "requiredSkills": arrayType(objectType({
6836
6948
  "skillId": stringType(),
@@ -9434,6 +9546,13 @@ var OPTIONAL_SECTIONS = [
9434
9546
  { key: "economics", hint: { en: "Benefit/cost P10/P50/P90 ranges, ramp, confidence, discount rate", de: "Nutzen/Kosten P10/P50/P90, Ramp, Konfidenz, Diskontsatz" } },
9435
9547
  { key: "decisionLog", hint: { en: "Dated decisions (go/pause/stop/rescope/rebudget/other)", de: "Datierte Entscheidungen" } },
9436
9548
  { key: "statusReports", hint: { en: "Dated RAG reports (scope/timeline/budget/skills)", de: "Datierte RAG-Berichte" } },
9549
+ {
9550
+ key: "milestones",
9551
+ hint: {
9552
+ en: "[{title, baselineDate, forecastDate?, doneDate?, note?}] \u2014 dated intermediate results (pilot live, vendor selected, go-live; at most 30, title up to 300 characters). baselineDate is the agreed plan; forecastDate today's expectation when it differs; doneDate once reached. Ask for the 3\u20135 that the steering committee watches. On an update, send every milestone the initiative keeps: the file replaces the list.",
9553
+ de: "[{title, baselineDate, forecastDate?, doneDate?, note?}] \u2014 datierte Zwischenergebnisse (Pilot live, Anbieter gew\xE4hlt, Go-live; h\xF6chstens 30, Titel bis 300 Zeichen). baselineDate ist der vereinbarte Plan, forecastDate die heutige Erwartung, wenn sie abweicht, doneDate sobald erreicht. Nach den 3\u20135 fragen, die der Lenkungsausschuss verfolgt. Bei einem Update alle Meilensteine senden, die bleiben sollen: die Datei ersetzt die Liste."
9554
+ }
9555
+ },
9437
9556
  {
9438
9557
  key: "capabilityImpacts",
9439
9558
  hint: {
@@ -9493,7 +9612,7 @@ function describeZodShape(schema, depth = 0) {
9493
9612
  }
9494
9613
  function buildSchemaDescriptor(sectionShapes) {
9495
9614
  return {
9496
- about: "BICon initiative composer. Collect the required fields, then rate every sub-dimension 0\u2013100 (higher = more of the named quality) and a confidence per dimension. Dimension scores are DERIVED (mean of subs) \u2014 never ask the user for them. Values are used verbatim; enum values must match exactly; unknown keys are rejected. Dates: talk dd/mm/yyyy with the user (dd/mm/yyyy, dd.mm.yyyy and yyyy-mm-dd are all accepted; stored as yyyy-mm-dd). Row ids of decision-log entries, status reports and incidents are minted by the Composer \u2014 never invent ids. To UPDATE an existing initiative pass its id as `id`; omit it for a new one.",
9615
+ about: "BICon initiative composer. Collect the required fields, then rate every sub-dimension 0\u2013100 (higher = more of the named quality) and a confidence per dimension. Dimension scores are DERIVED (mean of subs) \u2014 never ask the user for them. Values are used verbatim; enum values must match exactly; unknown keys are rejected. Dates: talk dd/mm/yyyy with the user (dd/mm/yyyy, dd.mm.yyyy and yyyy-mm-dd are all accepted; stored as yyyy-mm-dd). Row ids of decision-log entries, status reports, milestones and incidents are minted by the Composer \u2014 never invent ids. To UPDATE an existing initiative pass its id as `id`; omit it for a new one.",
9497
9616
  requiredFields: REQUIRED_FIELDS,
9498
9617
  enums: {
9499
9618
  status: STATUS_VALUES,
@@ -9525,7 +9644,7 @@ function buildSchemaDescriptor(sectionShapes) {
9525
9644
  },
9526
9645
  optionalSections: OPTIONAL_SECTIONS,
9527
9646
  // Exact shapes of the object/array sections (aiAct, economics, actuals,
9528
- // decisionLog, statusReports, requiredSkills, capabilityImpacts). Keys ending in "?" are
9647
+ // decisionLog, statusReports, requiredSkills, capabilityImpacts, milestones). Keys ending in "?" are
9529
9648
  // optional; "enum[a|b]" lists the allowed values; "<key>" marks a map.
9530
9649
  // Pass these sections as JSON objects/arrays. Any key not listed is
9531
9650
  // rejected with its path.
@@ -9621,7 +9740,7 @@ var parseIfString = (v) => typeof v === "string" ? tryParseJson(v) : v;
9621
9740
  function wireSection(inner, description) {
9622
9741
  return external_exports.preprocess(parseIfString, deepStrict(inner)).describe(description);
9623
9742
  }
9624
- var SECTION_KEYS = ["aiAct", "economics", "actuals", "decisionLog", "statusReports", "requiredSkills", "capabilityImpacts"];
9743
+ var SECTION_KEYS = ["aiAct", "economics", "actuals", "decisionLog", "statusReports", "requiredSkills", "capabilityImpacts", "milestones"];
9625
9744
  var SECTION_SCHEMAS = Object.fromEntries(
9626
9745
  SECTION_KEYS.map((k) => [k, contractSection(k)])
9627
9746
  );
@@ -9632,6 +9751,7 @@ var SECTION_DESCRIPTIONS = {
9632
9751
  decisionLog: "Decision log entries \u2014 see sectionShapes.decisionLog. Entry ids are minted by the Composer \u2014 leave them out. Dates dd/mm/yyyy or yyyy-mm-dd.",
9633
9752
  statusReports: "Status reports \u2014 see sectionShapes.statusReports. Report ids are minted by the Composer \u2014 leave them out. Dates dd/mm/yyyy or yyyy-mm-dd.",
9634
9753
  capabilityImpacts: "Capability links [{capabilityId, kind: improve|new|replace|retire, maturityTo?}]. maturityTo 1\u20135 = level reached (omit = the capability's target; never for retire). capabilityId: a capability of the map, from read_portfolio_context \u2192 lists.capabilities; without the context leave the list empty.",
9754
+ milestones: "Milestones (max 30, title \u2264300 chars): baselineDate = the plan, forecastDate = today's expectation (omit = baseline), doneDate = reached. Dates dd/mm/yyyy or yyyy-mm-dd; ids minted. An update replaces the list: send all.",
9635
9755
  requiredSkills: "Required skills [{skillId, fte, runFte?}] \u2014 fte is the build-phase demand, the optional runFte the run-team demand after the planned end (absent = build fte continues, 0 = no run demand). skillId must be the id of a skill that exists in the organisation. With the portfolio context the Composer validates it (read_portfolio_context \u2192 lists.skills gives id + name); without it, leave the list empty and link skills in the app."
9636
9756
  };
9637
9757
  var contextWire = () => external_exports.preprocess(parseIfString, external_exports.union([external_exports.string().regex(/^ctx_[0-9a-f]{16}$/, "a contextId looks like ctx_<16 hex>"), external_exports.object({}).passthrough()]));
@@ -9676,6 +9796,7 @@ var compactSpecSchema = external_exports.object({
9676
9796
  statusReports: wireSection(SECTION_SCHEMAS.statusReports, SECTION_DESCRIPTIONS.statusReports).optional(),
9677
9797
  requiredSkills: wireSection(SECTION_SCHEMAS.requiredSkills, SECTION_DESCRIPTIONS.requiredSkills).optional(),
9678
9798
  capabilityImpacts: wireSection(SECTION_SCHEMAS.capabilityImpacts, SECTION_DESCRIPTIONS.capabilityImpacts).optional(),
9799
+ milestones: wireSection(SECTION_SCHEMAS.milestones, SECTION_DESCRIPTIONS.milestones).optional(),
9679
9800
  aiRiskTier: external_exports.enum(AI_RISK_TIERS).optional(),
9680
9801
  hitlPosture: external_exports.enum(HITL_POSTURES).optional(),
9681
9802
  dpiaStatus: external_exports.enum(DPIA_STATUSES).optional(),
@@ -10047,8 +10168,11 @@ function checkAiActVocabularies(block, errors) {
10047
10168
  }
10048
10169
  function mintNestedIds(rows, stamp, withStamps = false) {
10049
10170
  if (!rows) return void 0;
10171
+ const seen = /* @__PURE__ */ new Set();
10050
10172
  return rows.map((r) => {
10051
- const next = { ...r, id: isUuid(r.id) ? r.id : randomUUID() };
10173
+ const id = isUuid(r.id) && !seen.has(r.id) ? r.id : randomUUID();
10174
+ seen.add(id);
10175
+ const next = { ...r, id };
10052
10176
  if (withStamps) {
10053
10177
  if (!next.createdAt) next.createdAt = stamp;
10054
10178
  if (!next.updatedAt) next.updatedAt = stamp;
@@ -10095,6 +10219,28 @@ function assembleInitiative(input, now = /* @__PURE__ */ new Date()) {
10095
10219
  if (link.kind === "retire" && link.maturityTo !== void 0) errors.push(`capabilityImpacts.${i}.maturityTo: a retire link has no target level \u2014 omit maturityTo.`);
10096
10220
  else if (link.maturityTo !== void 0 && !Number.isInteger(link.maturityTo)) errors.push(`capabilityImpacts.${i}.maturityTo: must be a whole number 1\u20135 (got ${link.maturityTo}).`);
10097
10221
  }
10222
+ const milestones = spec.milestones ?? [];
10223
+ const pad = (n) => String(n).padStart(2, "0");
10224
+ const today = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
10225
+ for (const [i, m] of milestones.entries()) {
10226
+ if (!m.title.trim()) errors.push(`milestones.${i}.title: must not be empty (got only spaces).`);
10227
+ for (const field of ["baselineDate", "forecastDate", "doneDate"]) {
10228
+ const v = m[field];
10229
+ if (v !== void 0 && !isCalendarDate(v)) errors.push(`milestones.${i}.${field}: "${v}" is not a real calendar date \u2014 dd/mm/yyyy or yyyy-mm-dd`);
10230
+ }
10231
+ const due = m.doneDate ?? m.forecastDate ?? m.baselineDate;
10232
+ if (isCalendarDate(due) && (spec.timelineStart && due < spec.timelineStart || spec.timelineEnd && due > spec.timelineEnd)) {
10233
+ warnings.push(`milestones.${i}: "${m.title.trim()}" falls on ${due}, outside the timeline ${spec.timelineStart} \u2013 ${spec.timelineEnd}.`);
10234
+ }
10235
+ if (m.doneDate && isCalendarDate(m.doneDate) && m.doneDate > today) warnings.push(`milestones.${i}.doneDate: ${m.doneDate} lies in the future \u2014 a done date marks a milestone already reached.`);
10236
+ }
10237
+ const norm2 = (v) => (v ?? "").trim().toLowerCase();
10238
+ const others = context?.initiatives ?? [];
10239
+ const ext = norm2(spec.externalId);
10240
+ const updates = spec.id !== void 0 || !!ext && others.filter((x) => norm2(x.externalId) === ext).length === 1 || others.some((x) => norm2(x.name) === norm2(spec.name));
10241
+ if (updates && spec.milestones === void 0) {
10242
+ warnings.push("milestones: none sent, and the app will offer this file as an update \u2014 it replaces the initiative's milestones, so any the app holds are removed. Send the full list to keep them.");
10243
+ }
10098
10244
  let businessUnit = spec.businessUnit;
10099
10245
  let aiCategory = spec.aiCategory;
10100
10246
  if (context) {
@@ -10145,6 +10291,7 @@ function assembleInitiative(input, now = /* @__PURE__ */ new Date()) {
10145
10291
  evaluation,
10146
10292
  decisionLog: mintNestedIds(spec.decisionLog, stamp),
10147
10293
  statusReports: mintNestedIds(spec.statusReports, stamp),
10294
+ milestones: mintNestedIds(spec.milestones, stamp)?.map((m) => ({ ...m, title: m.title.trim() })),
10148
10295
  aiAct: aiAct ? { ...aiAct, ...aiAct.incidents ? { incidents: mintNestedIds(aiAct.incidents, stamp, true) } : {} } : void 0,
10149
10296
  createdAt: stamp,
10150
10297
  updatedAt: stamp
@@ -10214,7 +10361,7 @@ function createServer() {
10214
10361
  async () => text({
10215
10362
  capabilities: [
10216
10363
  "strict typed schemas: unknown keys anywhere in the spec are rejected with their exact path (never silently dropped)",
10217
- "sections (aiAct/economics/actuals/decisionLog/statusReports/requiredSkills/capabilityImpacts) accept a JSON object/array or the same JSON as a string; row ids are minted by the Composer",
10364
+ "sections (aiAct/economics/actuals/decisionLog/statusReports/requiredSkills/capabilityImpacts/milestones) accept a JSON object/array or the same JSON as a string; row ids are minted by the Composer",
10218
10365
  "dates accepted as dd/mm/yyyy, dd.mm.yyyy or yyyy-mm-dd (stored as yyyy-mm-dd)",
10219
10366
  "get_initiative_schema: sectionShapes, aiActVocabularies, portfolio-mode wording overlays",
10220
10367
  "AI-Act vocabulary and id-format enforcement with the allowed values in the error",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bicon/mcp-composer",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
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",