@codazen/harmonica-mcp 3.9.0 → 3.9.1

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 +122 -140
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -21542,18 +21542,18 @@ async function validateAuth(client, orgIdentifier, isOrgSlug, userId) {
21542
21542
  }
21543
21543
 
21544
21544
  // ../../libs/harmonica-services/src/mcp/org-guard.ts
21545
- async function assertProjectInOrg(client, systemId, orgId) {
21546
- const project = await client.getSystem(systemId);
21547
- if (!project) {
21545
+ async function assertSystemInOrg(client, systemId, orgId) {
21546
+ const system = await client.getSystem(systemId);
21547
+ if (!system) {
21548
21548
  throw new Error(`System not found: "${systemId}"`);
21549
21549
  }
21550
- if (project.orgId !== orgId) {
21550
+ if (system.orgId !== orgId) {
21551
21551
  throw new Error(
21552
21552
  `System "${systemId}" does not belong to the configured organization. Access denied.`
21553
21553
  );
21554
21554
  }
21555
21555
  }
21556
- async function assertRevisionInProject(client, revisionId, systemId) {
21556
+ async function assertRevisionInSystem(client, revisionId, systemId) {
21557
21557
  const revision = await client.getRevision(revisionId);
21558
21558
  if (!revision) {
21559
21559
  throw new Error(`Revision not found: "${revisionId}"`);
@@ -21575,18 +21575,6 @@ async function assertBeatInOrg(client, beatId, orgId) {
21575
21575
  );
21576
21576
  }
21577
21577
  }
21578
- async function fetchProjectInOrg(client, systemId, orgId) {
21579
- const project = await client.getSystem(systemId);
21580
- if (!project) {
21581
- throw new Error(`System not found: "${systemId}"`);
21582
- }
21583
- if (project.orgId !== orgId) {
21584
- throw new Error(
21585
- `System "${systemId}" does not belong to the configured organization. Access denied.`
21586
- );
21587
- }
21588
- return project;
21589
- }
21590
21578
  async function fetchSystemInOrg(client, systemId, orgId) {
21591
21579
  const system = await client.getSystem(systemId);
21592
21580
  if (!system) {
@@ -21979,7 +21967,7 @@ function registerActivityTools(server, ctx, client) {
21979
21967
  importance: external_exports.enum(IMPORTANCE_VALUES).optional().describe("Filter by minimum importance level")
21980
21968
  },
21981
21969
  async ({ systemId, limit, importance }) => {
21982
- await assertProjectInOrg(client, systemId, ctx.orgId);
21970
+ await assertSystemInOrg(client, systemId, ctx.orgId);
21983
21971
  const { entries, hasMore } = await client.listSystemActivities(systemId, {
21984
21972
  limit: limit ?? 30,
21985
21973
  minImportance: importance
@@ -22011,7 +21999,7 @@ function registerAnalysisTools(server, ctx, client) {
22011
21999
  minClusterSize: external_exports.coerce.number().int().min(2).optional().default(3).describe("Minimum cluster size. Default 3")
22012
22000
  },
22013
22001
  async ({ systemId, entityType, threshold, minClusterSize }) => {
22014
- await assertProjectInOrg(client, systemId, ctx.orgId);
22002
+ await assertSystemInOrg(client, systemId, ctx.orgId);
22015
22003
  const result = await client.clusterEntities(systemId, entityType, { threshold, minClusterSize });
22016
22004
  if (result.clusters.length === 0) {
22017
22005
  return {
@@ -22043,7 +22031,7 @@ Analyzed: ${result.totalAnalyzed} | Clusters: ${result.clusters.length} | Noise:
22043
22031
  minClusterSize: external_exports.coerce.number().int().min(2).optional().default(2).describe("Minimum cluster size. Default 2")
22044
22032
  },
22045
22033
  async ({ systemId, analysisType, entityType, threshold, minClusterSize }) => {
22046
- await assertProjectInOrg(client, systemId, ctx.orgId);
22034
+ await assertSystemInOrg(client, systemId, ctx.orgId);
22047
22035
  const result = await client.runClusteredAnalysis(systemId, analysisType, {
22048
22036
  entityType,
22049
22037
  threshold,
@@ -22078,7 +22066,7 @@ function registerAuditIntegrityTools(server, ctx, client) {
22078
22066
  },
22079
22067
  async ({ systemId, limit }) => {
22080
22068
  try {
22081
- await assertProjectInOrg(client, systemId, ctx.orgId);
22069
+ await assertSystemInOrg(client, systemId, ctx.orgId);
22082
22070
  const report = await client.verifyAuditIntegrity(systemId, { limit });
22083
22071
  const lines = [
22084
22072
  `## Audit Integrity Report`,
@@ -22207,7 +22195,7 @@ function registerBaselineTools(server, ctx, client) {
22207
22195
  },
22208
22196
  async ({ systemId, beatIds, runChecks, localPath }) => {
22209
22197
  try {
22210
- await assertProjectInOrg(client, systemId, ctx.orgId);
22198
+ await assertSystemInOrg(client, systemId, ctx.orgId);
22211
22199
  const task = await client.baselineSystem(systemId, { beatIds, runChecks, localPath });
22212
22200
  return {
22213
22201
  content: [{
@@ -22296,7 +22284,7 @@ Rationale: ${rationale}`;
22296
22284
  },
22297
22285
  async ({ systemId, items, reason }) => {
22298
22286
  try {
22299
- await assertProjectInOrg(client, systemId, ctx.orgId);
22287
+ await assertSystemInOrg(client, systemId, ctx.orgId);
22300
22288
  const result = await client.bulkReassignRevisions(items, reason, ctx.user.userId);
22301
22289
  const lines = [
22302
22290
  `# Bulk reassignment complete: ${result.reassigned.length} succeeded, ${result.failed.length} failed`,
@@ -22335,7 +22323,7 @@ Rationale: ${rationale}`;
22335
22323
  },
22336
22324
  async ({ systemId, revisionId, toBeatId, reason }) => {
22337
22325
  try {
22338
- await assertProjectInOrg(client, systemId, ctx.orgId);
22326
+ await assertSystemInOrg(client, systemId, ctx.orgId);
22339
22327
  const result = await client.reassignRevision(revisionId, toBeatId, reason, ctx.user.userId);
22340
22328
  const text = [
22341
22329
  `Revision reassigned successfully.`,
@@ -23103,7 +23091,7 @@ function registerBeatTools(server, ctx, client) {
23103
23091
  includeArchived: external_exports.boolean().optional().default(false).describe("Include archived and deprecated beats (excluded by default)")
23104
23092
  },
23105
23093
  async ({ systemId, offset = 0, limit = 100, includeArchived }) => {
23106
- await assertProjectInOrg(client, systemId, ctx.orgId);
23094
+ await assertSystemInOrg(client, systemId, ctx.orgId);
23107
23095
  let beats = await client.listSystemBeats(systemId);
23108
23096
  if (!includeArchived) {
23109
23097
  beats = beats.filter((b) => b.beatStatus !== "archived");
@@ -23169,7 +23157,7 @@ function registerBeatTools(server, ctx, client) {
23169
23157
  },
23170
23158
  async ({ systemId, title, description, tags, priority, estimatedEffort, beatId, humanAssignee, agentAssignee, branch }) => {
23171
23159
  try {
23172
- await fetchProjectInOrg(client, systemId, ctx.orgId);
23160
+ await fetchSystemInOrg(client, systemId, ctx.orgId);
23173
23161
  const resolvedBeatId = beatId ?? await client.getNextBeatId(systemId);
23174
23162
  const beat = await client.createBeat({
23175
23163
  beatId: resolvedBeatId,
@@ -23286,7 +23274,7 @@ ${formatBeatDetail(beat)}${similarityWarning}` }] };
23286
23274
  },
23287
23275
  async ({ systemId, beatId, status, includeArchived }) => {
23288
23276
  try {
23289
- await assertProjectInOrg(client, systemId, ctx.orgId);
23277
+ await assertSystemInOrg(client, systemId, ctx.orgId);
23290
23278
  const revisions = beatId ? await client.listBeatRevisions(systemId, beatId, { status, includeArchived }) : await client.listSystemRevisions(systemId, { status, includeArchived });
23291
23279
  return { content: [{ type: "text", text: formatRevisionSummaryTable(revisions) }] };
23292
23280
  } catch (err) {
@@ -23972,7 +23960,7 @@ function registerCheckTools(server, ctx, client) {
23972
23960
  },
23973
23961
  async ({ checkType, targetId, systemId, branch, localPath }) => {
23974
23962
  try {
23975
- await assertProjectInOrg(client, systemId, ctx.orgId);
23963
+ await assertSystemInOrg(client, systemId, ctx.orgId);
23976
23964
  const targetError = checkType === "build_quality" ? buildQualityTargetError(targetId) : checkType === "beat_version_quality" || checkType === "plan_quality" ? beatVersionQualityTargetError(targetId) : null;
23977
23965
  if (targetError) {
23978
23966
  return { content: [{ type: "text", text: targetError }], isError: true };
@@ -24560,7 +24548,7 @@ function registerDropQualityTools(server, ctx, client) {
24560
24548
  },
24561
24549
  async ({ dropId, systemId, wait }) => {
24562
24550
  try {
24563
- await assertProjectInOrg(client, systemId, ctx.orgId);
24551
+ await assertSystemInOrg(client, systemId, ctx.orgId);
24564
24552
  const drop = await client.getDrop(dropId);
24565
24553
  if (!drop) {
24566
24554
  return { content: [{ type: "text", text: `Drop "${dropId}" not found.` }], isError: true };
@@ -24954,8 +24942,8 @@ function registerEmbeddingTools(server, ctx, client) {
24954
24942
  systemId: external_exports.string().describe("The system ID to embed entities for")
24955
24943
  },
24956
24944
  async ({ systemId }) => {
24957
- await assertProjectInOrg(client, systemId, ctx.orgId);
24958
- const result = await client.embedProjectEntities(systemId);
24945
+ await assertSystemInOrg(client, systemId, ctx.orgId);
24946
+ const result = await client.embedSystemEntities(systemId);
24959
24947
  const text = `## Embedding Results
24960
24948
 
24961
24949
  - Embedded: ${result.embedded}
@@ -24986,7 +24974,7 @@ function registerEmbeddingTools(server, ctx, client) {
24986
24974
  }
24987
24975
  let scope;
24988
24976
  if (systemId) {
24989
- await assertProjectInOrg(client, systemId, ctx.orgId);
24977
+ await assertSystemInOrg(client, systemId, ctx.orgId);
24990
24978
  scope = { systemId };
24991
24979
  } else if (teamspaceId) {
24992
24980
  await assertTeamspaceInOrg(client, teamspaceId, ctx.orgId);
@@ -25012,7 +25000,7 @@ function registerEmbeddingTools(server, ctx, client) {
25012
25000
  beatId: external_exports.string().optional().describe("Restrict results to notes belonging to a specific beat (DynamoDB post-filter)")
25013
25001
  },
25014
25002
  async ({ noteId, systemId, threshold, limit, includeArchived = false, noteStatus, beatId }) => {
25015
- await assertProjectInOrg(client, systemId, ctx.orgId);
25003
+ await assertSystemInOrg(client, systemId, ctx.orgId);
25016
25004
  const ids = Array.isArray(noteId) ? noteId : [noteId];
25017
25005
  if (ids.length === 1) {
25018
25006
  const results = await client.findSimilarNotes(ids[0], systemId, { threshold, limit, includeArchived, noteStatus, beatId });
@@ -25053,7 +25041,7 @@ function registerEmbeddingTools(server, ctx, client) {
25053
25041
  limit: external_exports.coerce.number().int().min(1).max(50).optional().default(10).describe("Max results to return. Default 10")
25054
25042
  },
25055
25043
  async ({ noteId, systemId, threshold, limit }) => {
25056
- await assertProjectInOrg(client, systemId, ctx.orgId);
25044
+ await assertSystemInOrg(client, systemId, ctx.orgId);
25057
25045
  const results = await client.findRelatedBeats(noteId, systemId, { threshold, limit });
25058
25046
  const text = formatSimilarityResults(results, noteId, "Related Beats");
25059
25047
  return { content: [{ type: "text", text }] };
@@ -25069,7 +25057,7 @@ function registerEmbeddingTools(server, ctx, client) {
25069
25057
  includeLinked: external_exports.coerce.boolean().optional().default(false).describe("Include notes that already have dependsOnNotes links. Default false (excludes them). Set true for thematic grouping pass.")
25070
25058
  },
25071
25059
  async ({ systemId, threshold, minClusterSize, includeLinked }) => {
25072
- await assertProjectInOrg(client, systemId, ctx.orgId);
25060
+ await assertSystemInOrg(client, systemId, ctx.orgId);
25073
25061
  const result = await client.clusterNotes(systemId, { threshold, minClusterSize, excludeAlreadyLinked: !includeLinked });
25074
25062
  if (result.clusters.length === 0) {
25075
25063
  return { content: [{ type: "text", text: `No clusters found among ${result.totalAnalyzed} analyzed notes. Try lowering the threshold.` }] };
@@ -25100,7 +25088,7 @@ ${members}`;
25100
25088
  includeLinked: external_exports.coerce.boolean().optional().default(false).describe("Include notes that already have dependsOnNotes links. Default false.")
25101
25089
  },
25102
25090
  async ({ systemId, threshold, minClusterSize, includeLinked }) => {
25103
- await assertProjectInOrg(client, systemId, ctx.orgId);
25091
+ await assertSystemInOrg(client, systemId, ctx.orgId);
25104
25092
  const result = await client.consolidateNotes(systemId, { threshold, minClusterSize, includeLinked });
25105
25093
  if (result.proposals.length === 0) {
25106
25094
  return { content: [{ type: "text", text: `No consolidation candidates found among ${result.totalAnalyzed} analyzed notes.` }] };
@@ -25341,7 +25329,7 @@ function registerLayerTools(server, ctx, client) {
25341
25329
  },
25342
25330
  async ({ systemId }) => {
25343
25331
  try {
25344
- await assertProjectInOrg(client, systemId, ctx.orgId);
25332
+ await assertSystemInOrg(client, systemId, ctx.orgId);
25345
25333
  } catch (err) {
25346
25334
  const message = err instanceof Error ? err.message : String(err);
25347
25335
  return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
@@ -25366,7 +25354,7 @@ function registerLayerTools(server, ctx, client) {
25366
25354
  },
25367
25355
  async ({ systemId, layerId }) => {
25368
25356
  try {
25369
- await assertProjectInOrg(client, systemId, ctx.orgId);
25357
+ await assertSystemInOrg(client, systemId, ctx.orgId);
25370
25358
  } catch (err) {
25371
25359
  const message = err instanceof Error ? err.message : String(err);
25372
25360
  return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
@@ -25401,7 +25389,7 @@ function registerLayerTools(server, ctx, client) {
25401
25389
  },
25402
25390
  async ({ systemId, layerKey, name, description, parentLayerId, repoPatterns }) => {
25403
25391
  try {
25404
- await assertProjectInOrg(client, systemId, ctx.orgId);
25392
+ await assertSystemInOrg(client, systemId, ctx.orgId);
25405
25393
  } catch (err) {
25406
25394
  const message = err instanceof Error ? err.message : String(err);
25407
25395
  return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
@@ -25440,7 +25428,7 @@ ${formatLayerDetail(layer)}` }] };
25440
25428
  },
25441
25429
  async ({ systemId, layerId, name, description, repoPatterns }) => {
25442
25430
  try {
25443
- await assertProjectInOrg(client, systemId, ctx.orgId);
25431
+ await assertSystemInOrg(client, systemId, ctx.orgId);
25444
25432
  } catch (err) {
25445
25433
  const message = err instanceof Error ? err.message : String(err);
25446
25434
  return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
@@ -25484,7 +25472,7 @@ ${formatLayerDetail(layer)}` }] };
25484
25472
  },
25485
25473
  async ({ systemId, layerId, status, reason }) => {
25486
25474
  try {
25487
- await assertProjectInOrg(client, systemId, ctx.orgId);
25475
+ await assertSystemInOrg(client, systemId, ctx.orgId);
25488
25476
  } catch (err) {
25489
25477
  const message = err instanceof Error ? err.message : String(err);
25490
25478
  return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
@@ -25828,7 +25816,7 @@ function registerNextActionsTools(server, ctx, client) {
25828
25816
  },
25829
25817
  async ({ systemId, limit }) => {
25830
25818
  try {
25831
- await assertProjectInOrg(client, systemId, ctx.orgId);
25819
+ await assertSystemInOrg(client, systemId, ctx.orgId);
25832
25820
  const actions = await client.getNextActions(systemId, { limit });
25833
25821
  return { content: [{ type: "text", text: formatNextActions(actions) }] };
25834
25822
  } catch (err) {
@@ -25845,7 +25833,7 @@ function registerNextActionsTools(server, ctx, client) {
25845
25833
  },
25846
25834
  async ({ systemId }) => {
25847
25835
  try {
25848
- await assertProjectInOrg(client, systemId, ctx.orgId);
25836
+ await assertSystemInOrg(client, systemId, ctx.orgId);
25849
25837
  const actions = await client.listTriageActions(systemId);
25850
25838
  return { content: [{ type: "text", text: formatTriageActions(actions) }] };
25851
25839
  } catch (err) {
@@ -25863,7 +25851,7 @@ function registerNextActionsTools(server, ctx, client) {
25863
25851
  },
25864
25852
  async ({ systemId, actionKey }) => {
25865
25853
  try {
25866
- await assertProjectInOrg(client, systemId, ctx.orgId);
25854
+ await assertSystemInOrg(client, systemId, ctx.orgId);
25867
25855
  await client.dismissTriageAction(systemId, actionKey, ctx.user?.userId ?? "mcp");
25868
25856
  return { content: [{ type: "text", text: `Action ${actionKey} dismissed.` }] };
25869
25857
  } catch (err) {
@@ -26341,7 +26329,7 @@ var normalizeSystemId = (raw) => {
26341
26329
  const b = raw;
26342
26330
  const id = b["systemId"] || b["projectId"];
26343
26331
  if (!id) return raw;
26344
- return { ...b, systemId: id, projectId: id };
26332
+ return { ...b, systemId: id };
26345
26333
  };
26346
26334
  var BeatApiSchema = external_exports.preprocess(
26347
26335
  (raw) => {
@@ -26349,12 +26337,11 @@ var BeatApiSchema = external_exports.preprocess(
26349
26337
  const b = raw;
26350
26338
  const id = b["systemId"] || b["projectId"];
26351
26339
  if (!id) return raw;
26352
- return { ...b, systemId: id, projectId: id };
26340
+ return { ...b, systemId: id };
26353
26341
  },
26354
26342
  external_exports.object({
26355
26343
  beatId: external_exports.string(),
26356
26344
  systemId: external_exports.string(),
26357
- projectId: external_exports.string(),
26358
26345
  /** Source project's title; attached by the flat /api/beats endpoint for
26359
26346
  * multi-project (teamspace/org) responses. */
26360
26347
  projectName: external_exports.string().nullish(),
@@ -26386,7 +26373,6 @@ var RevisionApiSchema = external_exports.preprocess(
26386
26373
  revisionId: external_exports.string(),
26387
26374
  beatId: external_exports.string(),
26388
26375
  systemId: external_exports.string(),
26389
- projectId: external_exports.string(),
26390
26376
  status: RevisionStatusSchema,
26391
26377
  title: external_exports.string().default(""),
26392
26378
  versionNumber: external_exports.number(),
@@ -26413,7 +26399,6 @@ var NoteApiSchema = external_exports.preprocess(normalizeSystemId, external_expo
26413
26399
  status: NoteStatusSchema,
26414
26400
  content: external_exports.string(),
26415
26401
  systemId: external_exports.string().nullish(),
26416
- projectId: external_exports.string().nullish(),
26417
26402
  beatId: external_exports.string().nullish(),
26418
26403
  revisionId: external_exports.string().nullish(),
26419
26404
  rationale: external_exports.string().nullish(),
@@ -26429,7 +26414,6 @@ var SystemApiSchema = external_exports.preprocess(
26429
26414
  normalizeSystemId,
26430
26415
  external_exports.object({
26431
26416
  systemId: external_exports.string(),
26432
- projectId: external_exports.string(),
26433
26417
  title: external_exports.string(),
26434
26418
  status: SystemStatusSchema,
26435
26419
  description: external_exports.string().optional(),
@@ -26631,8 +26615,7 @@ var CheckApiSchema = external_exports.preprocess(
26631
26615
  checkType: CheckTypeSchema,
26632
26616
  targetType: CheckTargetTypeSchema,
26633
26617
  targetId: external_exports.string(),
26634
- systemId: external_exports.string().optional(),
26635
- projectId: external_exports.string().optional(),
26618
+ systemId: external_exports.string(),
26636
26619
  overallScore: external_exports.number(),
26637
26620
  dimensions: external_exports.array(CheckDimensionScoreSchema),
26638
26621
  summary: external_exports.string(),
@@ -26651,8 +26634,7 @@ var SystemCheckApiSchema = external_exports.preprocess(
26651
26634
  checkType: CheckTypeSchema,
26652
26635
  targetType: CheckTargetTypeSchema,
26653
26636
  targetId: external_exports.string(),
26654
- systemId: external_exports.string().optional(),
26655
- projectId: external_exports.string().optional(),
26637
+ systemId: external_exports.string(),
26656
26638
  overallScore: external_exports.number(),
26657
26639
  summary: external_exports.string(),
26658
26640
  triggeredBy: external_exports.string().optional(),
@@ -26712,7 +26694,6 @@ var ActivitySchema = external_exports.preprocess(
26712
26694
  external_exports.object({
26713
26695
  activityId: external_exports.string(),
26714
26696
  systemId: external_exports.string(),
26715
- projectId: external_exports.string(),
26716
26697
  action: external_exports.string(),
26717
26698
  importance: external_exports.enum(["minor", "moderate", "major"]),
26718
26699
  reason: external_exports.string(),
@@ -26758,7 +26739,6 @@ var TriageActionSchema = external_exports.preprocess(
26758
26739
  external_exports.object({
26759
26740
  actionKey: external_exports.string(),
26760
26741
  systemId: external_exports.string(),
26761
- projectId: external_exports.string(),
26762
26742
  entityId: external_exports.string().optional(),
26763
26743
  entityType: external_exports.enum(["beat", "revision", "note"]).optional(),
26764
26744
  beatId: external_exports.string().optional(),
@@ -26799,7 +26779,6 @@ var SearchResultSchema = external_exports.preprocess(normalizeSystemId, external
26799
26779
  score: external_exports.number(),
26800
26780
  snippet: external_exports.string(),
26801
26781
  systemId: external_exports.string().optional(),
26802
- projectId: external_exports.string().optional(),
26803
26782
  projectName: external_exports.string().optional(),
26804
26783
  teamspaceId: external_exports.string().optional()
26805
26784
  }).passthrough());
@@ -26838,33 +26817,28 @@ var LandscapeMetaSchema = external_exports.object({
26838
26817
  cacheHit: external_exports.boolean(),
26839
26818
  projectionHash: external_exports.string()
26840
26819
  }).passthrough();
26841
- var LandscapeResponseSchema = external_exports.preprocess(
26842
- (raw) => {
26843
- if (typeof raw !== "object" || raw === null) return raw;
26844
- const r = raw;
26845
- const sb = r["scopeBoundary"];
26846
- if (typeof sb === "object" && sb !== null) {
26847
- const s = sb;
26848
- const id = s["systemId"] || s["projectId"];
26849
- if (id) return { ...r, scopeBoundary: { ...s, systemId: id, projectId: id } };
26850
- }
26851
- return r;
26852
- },
26853
- external_exports.object({
26854
- points: external_exports.array(LandscapePointSchema),
26855
- links: external_exports.array(external_exports.object({ from: external_exports.string(), to: external_exports.string() }).passthrough()),
26856
- clusters: external_exports.array(LandscapeClusterSchema),
26857
- scopeBoundary: external_exports.object({
26858
- systemId: external_exports.string().optional(),
26859
- projectId: external_exports.string().optional(),
26820
+ var LandscapeResponseSchema = external_exports.object({
26821
+ points: external_exports.array(LandscapePointSchema),
26822
+ links: external_exports.array(external_exports.object({ from: external_exports.string(), to: external_exports.string() }).passthrough()),
26823
+ clusters: external_exports.array(LandscapeClusterSchema),
26824
+ scopeBoundary: external_exports.preprocess(
26825
+ (raw) => {
26826
+ if (typeof raw !== "object" || raw === null) return raw;
26827
+ const o = raw;
26828
+ const id = o["systemId"] ?? o["projectId"];
26829
+ if (!id) return raw;
26830
+ return { ...o, systemId: id };
26831
+ },
26832
+ external_exports.object({
26833
+ systemId: external_exports.string(),
26860
26834
  cx: external_exports.number(),
26861
26835
  cy: external_exports.number(),
26862
26836
  radius: external_exports.number(),
26863
26837
  label: external_exports.string()
26864
- }).passthrough().optional(),
26865
- meta: LandscapeMetaSchema
26866
- }).passthrough()
26867
- );
26838
+ }).passthrough()
26839
+ ).optional(),
26840
+ meta: LandscapeMetaSchema
26841
+ }).passthrough();
26868
26842
  var ClusterDescriptionSchema = external_exports.object({
26869
26843
  title: external_exports.string(),
26870
26844
  description: external_exports.string()
@@ -26873,7 +26847,6 @@ var BeatVersionItemSchema = external_exports.preprocess(normalizeSystemId, exter
26873
26847
  beatVersionId: external_exports.string(),
26874
26848
  beatId: external_exports.string(),
26875
26849
  systemId: external_exports.string().optional(),
26876
- projectId: external_exports.string().optional(),
26877
26850
  status: external_exports.string()
26878
26851
  }).passthrough());
26879
26852
  var BeatVersionsResponseSchema = external_exports.object({
@@ -26882,7 +26855,6 @@ var BeatVersionsResponseSchema = external_exports.object({
26882
26855
  var ConversationSummaryBaseSchema = external_exports.object({
26883
26856
  conversationId: external_exports.string(),
26884
26857
  systemId: external_exports.string().optional(),
26885
- projectId: external_exports.string().optional(),
26886
26858
  teamspaceId: external_exports.string().optional(),
26887
26859
  userId: external_exports.string(),
26888
26860
  conversationType: external_exports.string(),
@@ -27001,6 +26973,13 @@ var WorkItemApiSchema = external_exports.object({
27001
26973
  costPerValueUnit: external_exports.number().optional(),
27002
26974
  /** Optional capability-lineage link to a Beat Version (v2, N-4E09-6574). */
27003
26975
  beatVersionId: external_exports.string().optional(),
26976
+ /**
26977
+ * Optional link to the Work Order this item delivers part of (B-331,
26978
+ * N-4E09-7160). Absent means unattached, which is a supported state — a
26979
+ * reader grouping a Track's work by Order must render those as their own
26980
+ * named group, not hide them.
26981
+ */
26982
+ workOrderId: external_exports.string().min(1).optional(),
27004
26983
  createdBy: external_exports.string().optional(),
27005
26984
  version: external_exports.number().optional(),
27006
26985
  createdAt: external_exports.string(),
@@ -27137,12 +27116,11 @@ var LayerApiSchema = external_exports.preprocess(
27137
27116
  const l = raw;
27138
27117
  const id = l["systemId"] || l["projectId"];
27139
27118
  if (!id) return raw;
27140
- return { ...l, systemId: id, projectId: id };
27119
+ return { ...l, systemId: id };
27141
27120
  },
27142
27121
  external_exports.object({
27143
27122
  layerId: external_exports.string(),
27144
27123
  systemId: external_exports.string(),
27145
- projectId: external_exports.string(),
27146
27124
  layerKey: external_exports.string(),
27147
27125
  name: external_exports.string(),
27148
27126
  description: external_exports.string().nullish(),
@@ -27199,7 +27177,6 @@ var DeliverableGroupApiSchema = external_exports.preprocess(normalizeSystemId, e
27199
27177
  groupCode: external_exports.string(),
27200
27178
  teamspaceId: external_exports.string(),
27201
27179
  systemId: external_exports.string().nullish(),
27202
- projectId: external_exports.string().nullish(),
27203
27180
  movementId: external_exports.string().nullish(),
27204
27181
  orgId: external_exports.string(),
27205
27182
  title: external_exports.string(),
@@ -27238,8 +27215,7 @@ var WorkPlanBeatVersionApiSchema = external_exports.preprocess(
27238
27215
  external_exports.object({
27239
27216
  beatVersionId: external_exports.string(),
27240
27217
  beatId: external_exports.string(),
27241
- systemId: external_exports.string().optional(),
27242
- projectId: external_exports.string().optional(),
27218
+ systemId: external_exports.string(),
27243
27219
  versionNumber: external_exports.number(),
27244
27220
  title: external_exports.string(),
27245
27221
  status: BeatVersionStatusSchema,
@@ -27259,7 +27235,7 @@ var RevisionClaimActorSchema = external_exports.union([
27259
27235
  external_exports.object({ kind: external_exports.literal("user"), userId: external_exports.string() }),
27260
27236
  external_exports.preprocess(
27261
27237
  normalizeSystemId,
27262
- external_exports.object({ kind: external_exports.literal("agent"), agentId: external_exports.string(), systemId: external_exports.string(), projectId: external_exports.string() })
27238
+ external_exports.object({ kind: external_exports.literal("agent"), agentId: external_exports.string(), systemId: external_exports.string() })
27263
27239
  )
27264
27240
  ]);
27265
27241
  var RevisionClaimSchema = external_exports.object({
@@ -27279,8 +27255,7 @@ var ProposedRevisionSchema = external_exports.preprocess(
27279
27255
  state: external_exports.string(),
27280
27256
  beatId: external_exports.string(),
27281
27257
  beatTitle: external_exports.string().optional(),
27282
- systemId: external_exports.string().optional(),
27283
- projectId: external_exports.string().optional(),
27258
+ systemId: external_exports.string(),
27284
27259
  projectTitle: external_exports.string().optional()
27285
27260
  }).passthrough()
27286
27261
  );
@@ -27297,8 +27272,7 @@ var EligibleBeatVersionForDropSchema = external_exports.preprocess(
27297
27272
  state: external_exports.string(),
27298
27273
  beatId: external_exports.string(),
27299
27274
  beatTitle: external_exports.string().optional(),
27300
- systemId: external_exports.string().optional(),
27301
- projectId: external_exports.string().optional(),
27275
+ systemId: external_exports.string(),
27302
27276
  projectTitle: external_exports.string().optional(),
27303
27277
  createdAt: external_exports.string(),
27304
27278
  updatedAt: external_exports.string(),
@@ -27572,7 +27546,7 @@ function registerNoteTools(server, ctx, client) {
27572
27546
  return { content: [{ type: "text", text: "orderBy requires beatId \u2014 it is only supported for beat-scoped queries." }], isError: true };
27573
27547
  }
27574
27548
  try {
27575
- await assertProjectInOrg(client, systemId, ctx.orgId);
27549
+ await assertSystemInOrg(client, systemId, ctx.orgId);
27576
27550
  } catch (err) {
27577
27551
  const message = err instanceof Error ? err.message : String(err);
27578
27552
  return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
@@ -27718,7 +27692,7 @@ function registerNoteTools(server, ctx, client) {
27718
27692
  systemId: external_exports.string().describe("The system ID")
27719
27693
  },
27720
27694
  async ({ systemId }) => {
27721
- await assertProjectInOrg(client, systemId, ctx.orgId);
27695
+ await assertSystemInOrg(client, systemId, ctx.orgId);
27722
27696
  const DOCUMENT_FETCH_LIMIT = 5e3;
27723
27697
  const { notes: docs, hasMore: truncated } = await client.listAllSystemNotes(systemId, { noteType: "document" }, DOCUMENT_FETCH_LIMIT);
27724
27698
  const visibleDocs = docs.filter((d) => d.status !== DISMISSED_NOTE_STATUS);
@@ -27753,7 +27727,7 @@ function registerNoteTools(server, ctx, client) {
27753
27727
  }
27754
27728
  return { content: [{ type: "text", text: formatNoteDetail(note2) }] };
27755
27729
  }
27756
- await assertProjectInOrg(client, systemId, ctx.orgId);
27730
+ await assertSystemInOrg(client, systemId, ctx.orgId);
27757
27731
  const note = await client.getNote(noteId);
27758
27732
  if (!note) {
27759
27733
  return { content: [{ type: "text", text: `Note not found: "${noteId}"` }], isError: true };
@@ -27792,7 +27766,7 @@ function registerNoteTools(server, ctx, client) {
27792
27766
  const assumptionMeta = noteType === "assumption" && confidence !== void 0 ? { confidence } : void 0;
27793
27767
  let note;
27794
27768
  if (systemId) {
27795
- await assertProjectInOrg(client, systemId, ctx.orgId);
27769
+ await assertSystemInOrg(client, systemId, ctx.orgId);
27796
27770
  const projectCode = systemId.substring(0, 4).toUpperCase();
27797
27771
  const noteId = await client.getNextNoteId(projectCode, systemId);
27798
27772
  note = await client.createNote({
@@ -27920,7 +27894,7 @@ ${text}` }] };
27920
27894
 
27921
27895
  ${formatNoteDetail(updated2)}` }] };
27922
27896
  }
27923
- await assertProjectInOrg(client, systemId, ctx.orgId);
27897
+ await assertSystemInOrg(client, systemId, ctx.orgId);
27924
27898
  const updated = await client.updateNote(noteId, {
27925
27899
  ...noteType !== void 0 && { noteType },
27926
27900
  status,
@@ -27962,7 +27936,7 @@ ${text}` }] };
27962
27936
  },
27963
27937
  async ({ systemId, noteIds, noteType, status, content, response, humanAssignee, agentAssignee }) => {
27964
27938
  try {
27965
- await assertProjectInOrg(client, systemId, ctx.orgId);
27939
+ await assertSystemInOrg(client, systemId, ctx.orgId);
27966
27940
  const result = await client.bulkUpdateNotes(noteIds, {
27967
27941
  ...noteType !== void 0 && { noteType },
27968
27942
  status,
@@ -28001,7 +27975,7 @@ ${text}` }] };
28001
27975
  },
28002
27976
  async ({ systemId, noteId, targetBeatId, targetRevisionId, reason }) => {
28003
27977
  try {
28004
- await assertProjectInOrg(client, systemId, ctx.orgId);
27978
+ await assertSystemInOrg(client, systemId, ctx.orgId);
28005
27979
  const updated = await client.reassignNote(noteId, targetBeatId, reason, ctx.user.userId, targetRevisionId);
28006
27980
  if (!updated) {
28007
27981
  return { content: [{ type: "text", text: `Note not found: "${noteId}"` }], isError: true };
@@ -28023,7 +27997,7 @@ ${text}` }] };
28023
27997
  },
28024
27998
  async ({ systemId, noteId, reason }) => {
28025
27999
  try {
28026
- await assertProjectInOrg(client, systemId, ctx.orgId);
28000
+ await assertSystemInOrg(client, systemId, ctx.orgId);
28027
28001
  const note = await client.getNote(noteId);
28028
28002
  if (!note || (note.systemId ?? note.projectId) !== systemId) {
28029
28003
  return { content: [{ type: "text", text: `Note not found: "${noteId}"` }], isError: true };
@@ -28050,7 +28024,7 @@ ${text}` }] };
28050
28024
  },
28051
28025
  async ({ systemId, noteId, reason }) => {
28052
28026
  try {
28053
- await assertProjectInOrg(client, systemId, ctx.orgId);
28027
+ await assertSystemInOrg(client, systemId, ctx.orgId);
28054
28028
  const note = await client.getNote(noteId);
28055
28029
  if (!note || (note.systemId ?? note.projectId) !== systemId) {
28056
28030
  return { content: [{ type: "text", text: `Document not found: "${noteId}"` }], isError: true };
@@ -28080,7 +28054,7 @@ ${text}` }] };
28080
28054
  systemId: external_exports.string().describe("The system ID")
28081
28055
  },
28082
28056
  async ({ systemId }) => {
28083
- await assertProjectInOrg(client, systemId, ctx.orgId);
28057
+ await assertSystemInOrg(client, systemId, ctx.orgId);
28084
28058
  const assumptions = await client.getKeyAssumptions(systemId);
28085
28059
  if (assumptions.length === 0) {
28086
28060
  return { content: [{ type: "text", text: "No unvalidated assumptions found." }] };
@@ -28783,7 +28757,7 @@ function registerOnboardingTools(server, ctx, client) {
28783
28757
  analysisJson: external_exports.string().optional().describe("JSON-serialized DiscoveryAnalysis from initiate_teamspace_onboarding. Pass this to produce Beats grounded in the actual discovery data.")
28784
28758
  },
28785
28759
  async ({ systemId, systemTitle, projectDescription, analysisJson }) => {
28786
- await assertProjectInOrg(client, systemId, ctx.orgId);
28760
+ await assertSystemInOrg(client, systemId, ctx.orgId);
28787
28761
  const result = await client.draftOnboardingBeats(systemId, { systemTitle, projectDescription, analysisJson });
28788
28762
  if (!result.suggestions || result.suggestions.length === 0) {
28789
28763
  return {
@@ -29084,7 +29058,7 @@ function registerOrganizationTools(server, ctx, client) {
29084
29058
  if (system.orgId !== ctx.orgId) {
29085
29059
  return { content: [{ type: "text", text: `System "${systemId}" does not belong to the configured organization.` }], isError: true };
29086
29060
  }
29087
- const result = await client.transferProject(ctx.orgId, systemId, targetOrgId);
29061
+ const result = await client.transferSystem(ctx.orgId, systemId, targetOrgId);
29088
29062
  if (result.error) {
29089
29063
  return { content: [{ type: "text", text: result.error }], isError: true };
29090
29064
  }
@@ -29235,7 +29209,7 @@ function registerPlanQualityTools(server, ctx, client) {
29235
29209
  isError: true
29236
29210
  };
29237
29211
  }
29238
- await assertProjectInOrg(client, systemId, ctx.orgId);
29212
+ await assertSystemInOrg(client, systemId, ctx.orgId);
29239
29213
  const opts = branch || localPath ? { branch, localPath } : void 0;
29240
29214
  const task = await client.runCheck(systemId, "beat_version_quality", beatVersionId, opts);
29241
29215
  if (!wait) {
@@ -29382,7 +29356,7 @@ function registerPortfolioCoherenceTools(server, ctx, client) {
29382
29356
  },
29383
29357
  async ({ systemId, wait }) => {
29384
29358
  try {
29385
- await assertProjectInOrg(client, systemId, ctx.orgId);
29359
+ await assertSystemInOrg(client, systemId, ctx.orgId);
29386
29360
  const { taskId } = await client.runCheck(systemId, "portfolio_coherence", systemId);
29387
29361
  if (!wait) {
29388
29362
  return {
@@ -29419,7 +29393,7 @@ function registerPortfolioCoherenceTools(server, ctx, client) {
29419
29393
  const archivedBeatIds = [];
29420
29394
  let totalNotesReassigned = 0;
29421
29395
  try {
29422
- await assertProjectInOrg(client, systemId, ctx.orgId);
29396
+ await assertSystemInOrg(client, systemId, ctx.orgId);
29423
29397
  const uniqueBeatIds = [...new Set(beatIds)];
29424
29398
  const resolvedPrimaryId = primaryBeatId ?? uniqueBeatIds[0];
29425
29399
  if (!uniqueBeatIds.includes(resolvedPrimaryId)) {
@@ -30172,8 +30146,8 @@ function registerRevisionQualityTools(server, ctx, client) {
30172
30146
  isError: true
30173
30147
  };
30174
30148
  }
30175
- await assertProjectInOrg(client, systemId, ctx.orgId);
30176
- await assertRevisionInProject(client, revisionId, systemId);
30149
+ await assertSystemInOrg(client, systemId, ctx.orgId);
30150
+ await assertRevisionInSystem(client, revisionId, systemId);
30177
30151
  const task = await client.runCheck(systemId, "revision_quality", revisionId);
30178
30152
  if (!wait) {
30179
30153
  return {
@@ -30478,7 +30452,7 @@ function registerSnapshotTools(server, ctx, client) {
30478
30452
  "Export a complete system snapshot for environment sync. On a remote server (production/staging) returns an S3 presigned URL; on a local server writes to a temp file and returns the path.",
30479
30453
  { systemId: external_exports.string().describe("The system ID to export") },
30480
30454
  async ({ systemId }) => {
30481
- await assertProjectInOrg(client, systemId, ctx.orgId);
30455
+ await assertSystemInOrg(client, systemId, ctx.orgId);
30482
30456
  const result = await client.exportSystemSnapshot(systemId);
30483
30457
  if (typeof result === "string") {
30484
30458
  if (!result.startsWith("https://")) {
@@ -30726,7 +30700,7 @@ function registerSubscriptionTools(server, ctx, client) {
30726
30700
  },
30727
30701
  async ({ entityType, entityId, systemId }) => {
30728
30702
  try {
30729
- await assertProjectInOrg(client, systemId, ctx.orgId);
30703
+ await assertSystemInOrg(client, systemId, ctx.orgId);
30730
30704
  await client.subscribe(ctx.user.userId, entityType, entityId, systemId, "manual");
30731
30705
  return {
30732
30706
  content: [{
@@ -30790,7 +30764,7 @@ function registerSubscriptionTools(server, ctx, client) {
30790
30764
  }
30791
30765
 
30792
30766
  // ../../libs/harmonica-services/src/mcp/tools/system-lifecycle-tools.ts
30793
- function registerProjectLifecycleTools(server, ctx, client) {
30767
+ function registerSystemLifecycleTools(server, ctx, client) {
30794
30768
  server.tool(
30795
30769
  "transition_system_lifecycle",
30796
30770
  "Advance a System's capability-maturity lifecycle state. Allowed transitions follow the state machine (Concept \u2192 Incubating \u2192 Piloting \u2192 Activated \u2192 Commercializing \u2192 Scaled, plus paused/killed/sunset/archived exits). The activated \u2192 commercializing edge requires a `decisionNoteId` pointing to a system-scoped Decision Note (the governance review). Other gates are advisory.",
@@ -30896,7 +30870,7 @@ function registerSystemTools(server, ctx, client) {
30896
30870
  }) => {
30897
30871
  try {
30898
30872
  const [project, org] = await Promise.all([
30899
- fetchProjectInOrg(client, systemId, ctx.orgId),
30873
+ fetchSystemInOrg(client, systemId, ctx.orgId),
30900
30874
  client.getOrg(ctx.orgId)
30901
30875
  ]);
30902
30876
  const notes = await client.listSystemNotes(systemId, {
@@ -30940,7 +30914,7 @@ function registerSystemTools(server, ctx, client) {
30940
30914
  if (Object.keys(nonEmpty).length === 0) {
30941
30915
  return { content: [{ type: "text", text: "No updates provided." }], isError: true };
30942
30916
  }
30943
- await assertProjectInOrg(client, systemId, ctx.orgId);
30917
+ await assertSystemInOrg(client, systemId, ctx.orgId);
30944
30918
  if (nonEmpty["accountId"] === "") delete nonEmpty["accountId"];
30945
30919
  if (typeof updates.teamspaceId === "string") {
30946
30920
  const teamspace = await client.getTeamspace(updates.teamspaceId);
@@ -31279,7 +31253,7 @@ function registerValueVelocityTools(server, ctx, client) {
31279
31253
  },
31280
31254
  async ({ systemId, lens, withLeverage }) => {
31281
31255
  try {
31282
- await assertProjectInOrg(client, systemId, ctx.orgId);
31256
+ await assertSystemInOrg(client, systemId, ctx.orgId);
31283
31257
  const [rankedResult, staleWResult] = await Promise.allSettled([
31284
31258
  withLeverage ? client.rankSystemBeatVersionsWithLeverage(systemId, lens) : client.rankSystemBeatVersions(systemId, lens),
31285
31259
  client.getStaleWBeatIds(systemId)
@@ -31386,7 +31360,7 @@ function registerValueVelocityTools(server, ctx, client) {
31386
31360
  },
31387
31361
  async ({ systemId, beatId }) => {
31388
31362
  try {
31389
- await assertProjectInOrg(client, systemId, ctx.orgId);
31363
+ await assertSystemInOrg(client, systemId, ctx.orgId);
31390
31364
  const actorId = resolveActorId(ctx);
31391
31365
  if (!actorId) {
31392
31366
  return { content: [{ type: "text", text: "Actor identity unavailable: user has no email or userId." }], isError: true };
@@ -31569,6 +31543,7 @@ function registerWorkItemTools(server, _ctx, client) {
31569
31543
  wi.valueUnits !== void 0 ? `Value Units: ${wi.valueUnits}` : null,
31570
31544
  wi.costPerValueUnit !== void 0 ? `Cost per Value Unit: ${wi.costPerValueUnit}` : null,
31571
31545
  wi.beatVersionId ? `Linked Beat Version: ${wi.beatVersionId}` : null,
31546
+ wi.workOrderId ? `Work Order: ${wi.workOrderId}` : "Work Order: none (unattached)",
31572
31547
  estimateHint ? `Estimate Hint: ${estimateHint.hours}h (source: ${estimateHint.source})` : null,
31573
31548
  wi.createdBy ? `Created By: ${wi.createdBy}` : null,
31574
31549
  `Created: ${wi.createdAt}`,
@@ -31579,7 +31554,7 @@ function registerWorkItemTools(server, _ctx, client) {
31579
31554
  );
31580
31555
  server.tool(
31581
31556
  "create_work_item",
31582
- 'Create a work item under a Track (DeliverableGroup). A work item is an owned, statused unit of operational work \u2014 it is NOT a Beat. Starts in "not_started" status. Optionally link it to a Beat Version for capability lineage (v2, N-4E09-6574) \u2014 the link is one-directional and never required.',
31557
+ 'Create a work item under a Track (DeliverableGroup). A work item is an owned, statused unit of operational work \u2014 it is NOT a Beat. Starts in "not_started" status. Optionally link it to a Beat Version for capability lineage (v2, N-4E09-6574) \u2014 the link is one-directional and never required. Optionally link it to a Work Order it delivers part of; leaving it unattached is a supported state, not a gap to fill (N-4E09-7160).',
31583
31558
  {
31584
31559
  trackId: external_exports.string().describe("The Track (DeliverableGroup) ID this work item belongs to"),
31585
31560
  title: external_exports.string().min(1).max(300).describe("Work item title"),
@@ -31590,9 +31565,10 @@ function registerWorkItemTools(server, _ctx, client) {
31590
31565
  valueUnits: external_exports.number().nonnegative().optional().describe("Optional Value Units (commercial lens) \u2014 independent of effortSize"),
31591
31566
  costPerValueUnit: external_exports.number().nonnegative().optional().describe("Optional price per Value Unit"),
31592
31567
  createdBy: external_exports.string().optional().describe("Optional creator identity for audit"),
31593
- beatVersionId: external_exports.string().min(1).optional().describe("Optional Beat Version to link for capability lineage. Must exist.")
31568
+ beatVersionId: external_exports.string().min(1).optional().describe("Optional Beat Version to link for capability lineage. Must exist."),
31569
+ workOrderId: external_exports.string().min(1).max(128).optional().describe("Optional Work Order this item delivers part of. Must exist. Omit when there is no Order \u2014 unattached work is legitimate, so do not invent one.")
31594
31570
  },
31595
- async ({ trackId, title, ownerName, ownerEmail, committedEstimateHours, effortSize, valueUnits, costPerValueUnit, createdBy, beatVersionId }) => {
31571
+ async ({ trackId, title, ownerName, ownerEmail, committedEstimateHours, effortSize, valueUnits, costPerValueUnit, createdBy, beatVersionId, workOrderId }) => {
31596
31572
  if (ownerName === void 0 !== (ownerEmail === void 0)) {
31597
31573
  return {
31598
31574
  content: [{ type: "text", text: "Setting the owner requires both ownerName and ownerEmail. Omit both to create an unowned work item." }],
@@ -31608,7 +31584,8 @@ function registerWorkItemTools(server, _ctx, client) {
31608
31584
  ...valueUnits !== void 0 && { valueUnits },
31609
31585
  ...costPerValueUnit !== void 0 && { costPerValueUnit },
31610
31586
  ...createdBy !== void 0 && { createdBy },
31611
- ...beatVersionId !== void 0 && { beatVersionId }
31587
+ ...beatVersionId !== void 0 && { beatVersionId },
31588
+ ...workOrderId !== void 0 && { workOrderId }
31612
31589
  });
31613
31590
  return {
31614
31591
  content: [
@@ -31622,7 +31599,7 @@ function registerWorkItemTools(server, _ctx, client) {
31622
31599
  );
31623
31600
  server.tool(
31624
31601
  "update_work_item",
31625
- "Update mutable content fields on a work item (title, owner, committedEstimateHours, actualHours, effortSize, valueUnits, costPerValueUnit, beatVersionId). To move it between Tracks use move_work_item; to change its status use transition_work_item_status.",
31602
+ "Update mutable content fields on a work item (title, owner, committedEstimateHours, actualHours, effortSize, valueUnits, costPerValueUnit, beatVersionId, workOrderId). To move it between Tracks use move_work_item; to change its status use transition_work_item_status.",
31626
31603
  {
31627
31604
  workItemId: external_exports.string().describe("The work item ID"),
31628
31605
  title: external_exports.string().min(1).max(300).optional().describe("New title"),
@@ -31634,9 +31611,10 @@ function registerWorkItemTools(server, _ctx, client) {
31634
31611
  effortSize: external_exports.enum(WORK_ITEM_EFFORT_SIZE_ENUM).nullable().optional().describe("Rough t-shirt effort size (delivery lens). Pass null to clear; omit to leave untouched."),
31635
31612
  valueUnits: external_exports.number().nonnegative().nullable().optional().describe("Value Units (commercial lens). Pass null to clear; omit to leave untouched."),
31636
31613
  costPerValueUnit: external_exports.number().nonnegative().nullable().optional().describe("Price per Value Unit. Pass null to clear; omit to leave untouched."),
31637
- beatVersionId: external_exports.string().min(1).nullable().optional().describe("Link to a Beat Version for capability lineage. Pass null to unlink; omit to leave the current link untouched.")
31614
+ beatVersionId: external_exports.string().min(1).nullable().optional().describe("Link to a Beat Version for capability lineage. Pass null to unlink; omit to leave the current link untouched."),
31615
+ workOrderId: external_exports.string().min(1).max(128).nullable().optional().describe("Link to the Work Order this item delivers part of. Pass null to unlink, leaving the item unattached; omit to leave the current link untouched.")
31638
31616
  },
31639
- async ({ workItemId, title, ownerName, ownerEmail, unassignOwner, committedEstimateHours, actualHours, effortSize, valueUnits, costPerValueUnit, beatVersionId }) => {
31617
+ async ({ workItemId, title, ownerName, ownerEmail, unassignOwner, committedEstimateHours, actualHours, effortSize, valueUnits, costPerValueUnit, beatVersionId, workOrderId }) => {
31640
31618
  const hasOwnerUpdate = ownerName !== void 0 || ownerEmail !== void 0;
31641
31619
  if (hasOwnerUpdate && (ownerName === void 0 || ownerEmail === void 0)) {
31642
31620
  return {
@@ -31650,7 +31628,9 @@ function registerWorkItemTools(server, _ctx, client) {
31650
31628
  isError: true
31651
31629
  };
31652
31630
  }
31653
- const hasUpdates = title !== void 0 || hasOwnerUpdate || unassignOwner === true || committedEstimateHours !== void 0 || actualHours !== void 0 || effortSize !== void 0 || valueUnits !== void 0 || costPerValueUnit !== void 0 || beatVersionId !== void 0;
31631
+ const hasUpdates = title !== void 0 || hasOwnerUpdate || unassignOwner === true || committedEstimateHours !== void 0 || actualHours !== void 0 || effortSize !== void 0 || valueUnits !== void 0 || costPerValueUnit !== void 0 || beatVersionId !== void 0 || // `!== undefined`, never truthiness: an unlink arrives as `null`, and
31632
+ // a truthy check here would report "no updates provided" and drop it.
31633
+ workOrderId !== void 0;
31654
31634
  if (!hasUpdates) {
31655
31635
  return {
31656
31636
  content: [{ type: "text", text: `No updates provided for work item: ${workItemId}` }],
@@ -31666,7 +31646,8 @@ function registerWorkItemTools(server, _ctx, client) {
31666
31646
  ...effortSize !== void 0 && { effortSize },
31667
31647
  ...valueUnits !== void 0 && { valueUnits },
31668
31648
  ...costPerValueUnit !== void 0 && { costPerValueUnit },
31669
- ...beatVersionId !== void 0 && { beatVersionId }
31649
+ ...beatVersionId !== void 0 && { beatVersionId },
31650
+ ...workOrderId !== void 0 && { workOrderId }
31670
31651
  });
31671
31652
  if (!wi) {
31672
31653
  return {
@@ -32105,7 +32086,7 @@ ${JSON.stringify(task.result, null, 2)}
32105
32086
  limit: external_exports.coerce.number().optional().default(20).describe("Max number of jobs to return")
32106
32087
  },
32107
32088
  async ({ systemId, status, limit }) => {
32108
- await assertProjectInOrg(client, systemId, ctx.orgId);
32089
+ await assertSystemInOrg(client, systemId, ctx.orgId);
32109
32090
  const tasks = await client.listSystemTasks(systemId, {
32110
32091
  status,
32111
32092
  limit
@@ -32163,7 +32144,7 @@ ${JSON.stringify(task.result, null, 2)}
32163
32144
  limit: external_exports.coerce.number().optional().default(20).describe("Max number of jobs to return")
32164
32145
  },
32165
32146
  async ({ systemId, status, limit }) => {
32166
- await assertProjectInOrg(client, systemId, ctx.orgId);
32147
+ await assertSystemInOrg(client, systemId, ctx.orgId);
32167
32148
  const tasks = await client.listSystemTasks(systemId, {
32168
32149
  status,
32169
32150
  limit
@@ -32187,7 +32168,7 @@ ${JSON.stringify(task.result, null, 2)}
32187
32168
  },
32188
32169
  async ({ systemId, beatId }) => {
32189
32170
  try {
32190
- await assertProjectInOrg(client, systemId, ctx.orgId);
32171
+ await assertSystemInOrg(client, systemId, ctx.orgId);
32191
32172
  const task = await client.draftCoda(systemId, beatId);
32192
32173
  return {
32193
32174
  content: [{
@@ -32220,7 +32201,7 @@ ${JSON.stringify(task.result, null, 2)}
32220
32201
  },
32221
32202
  async ({ systemId, noteIds }) => {
32222
32203
  try {
32223
- await assertProjectInOrg(client, systemId, ctx.orgId);
32204
+ await assertSystemInOrg(client, systemId, ctx.orgId);
32224
32205
  const task = await client.validateAssumptions(systemId, noteIds);
32225
32206
  return {
32226
32207
  content: [{
@@ -32255,7 +32236,7 @@ ${JSON.stringify(task.result, null, 2)}
32255
32236
  },
32256
32237
  async ({ systemId, message, revisionId }) => {
32257
32238
  try {
32258
- await assertProjectInOrg(client, systemId, ctx.orgId);
32239
+ await assertSystemInOrg(client, systemId, ctx.orgId);
32259
32240
  const task = await client.startSession(
32260
32241
  systemId,
32261
32242
  message,
@@ -32292,7 +32273,7 @@ ${JSON.stringify(task.result, null, 2)}
32292
32273
  },
32293
32274
  async ({ systemId, revisionIds }) => {
32294
32275
  try {
32295
- await assertProjectInOrg(client, systemId, ctx.orgId);
32276
+ await assertSystemInOrg(client, systemId, ctx.orgId);
32296
32277
  const task = await client.planRevisionBatch(systemId, revisionIds);
32297
32278
  return {
32298
32279
  content: [{
@@ -32324,7 +32305,7 @@ ${JSON.stringify(task.result, null, 2)}
32324
32305
  },
32325
32306
  async ({ systemId }) => {
32326
32307
  try {
32327
- await assertProjectInOrg(client, systemId, ctx.orgId);
32308
+ await assertSystemInOrg(client, systemId, ctx.orgId);
32328
32309
  const task = await client.runTriage(systemId);
32329
32310
  return {
32330
32311
  content: [{
@@ -32370,7 +32351,7 @@ ${JSON.stringify(task.result, null, 2)}
32370
32351
  },
32371
32352
  async ({ systemId, content, filename, encoding }) => {
32372
32353
  try {
32373
- await assertProjectInOrg(client, systemId, ctx.orgId);
32354
+ await assertSystemInOrg(client, systemId, ctx.orgId);
32374
32355
  const result = encoding === "base64" ? await client.analyzeBinaryDocument(systemId, content, filename) : await client.analyzeDocument(systemId, content, filename);
32375
32356
  return {
32376
32357
  content: [{
@@ -32447,7 +32428,7 @@ var TOOL_PROFILES = Object.freeze({
32447
32428
  registerTeamspaceTools,
32448
32429
  registerMembershipTools,
32449
32430
  registerSystemTools,
32450
- registerProjectLifecycleTools,
32431
+ registerSystemLifecycleTools,
32451
32432
  allow(registerBeatVersionTools, ["relocate_beat_version"])
32452
32433
  ]),
32453
32434
  "onboarding": Object.freeze([
@@ -32485,7 +32466,7 @@ function registerAllTools(server, ctx, client, profile) {
32485
32466
  return;
32486
32467
  }
32487
32468
  registerSystemTools(server, ctx, client);
32488
- registerProjectLifecycleTools(server, ctx, client);
32469
+ registerSystemLifecycleTools(server, ctx, client);
32489
32470
  registerMembershipTools(server, ctx, client);
32490
32471
  registerBeatTools(server, ctx, client);
32491
32472
  registerBeatConvergenceTools(server, ctx, client);
@@ -32808,7 +32789,7 @@ function createHttpClient(config2) {
32808
32789
  return { error: String(err) };
32809
32790
  }
32810
32791
  },
32811
- transferProject: async (sourceOrgId, projectId, targetOrgId) => {
32792
+ transferSystem: async (sourceOrgId, projectId, targetOrgId) => {
32812
32793
  try {
32813
32794
  const result = await request(
32814
32795
  "POST",
@@ -33936,7 +33917,7 @@ function createHttpClient(config2) {
33936
33917
  return importResult;
33937
33918
  },
33938
33919
  // Embedding similarity
33939
- embedProjectEntities: (projectId) => request("POST", `/api/systems/${encodeURIComponent(projectId)}/embeddings/generate`),
33920
+ embedSystemEntities: (projectId) => request("POST", `/api/systems/${encodeURIComponent(projectId)}/embeddings/generate`),
33940
33921
  findSimilarNotes: async (noteId, projectId, options) => {
33941
33922
  const qs = buildSimilarityQS(options);
33942
33923
  const res = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/notes/${encodeURIComponent(noteId)}/similar${qs}`);
@@ -34180,7 +34161,7 @@ function createHttpClient(config2) {
34180
34161
  },
34181
34162
  // WorkItems (WorkItem exposure layer — owned, statused unit of operational work under a Track)
34182
34163
  createWorkItem: async (input) => {
34183
- const { trackId, title, owner, committedEstimateHours, effortSize, valueUnits, costPerValueUnit, createdBy, beatVersionId } = input;
34164
+ const { trackId, title, owner, committedEstimateHours, effortSize, valueUnits, costPerValueUnit, createdBy, beatVersionId, workOrderId } = input;
34184
34165
  const body = {
34185
34166
  title,
34186
34167
  ...owner !== void 0 && { owner },
@@ -34189,7 +34170,8 @@ function createHttpClient(config2) {
34189
34170
  ...valueUnits !== void 0 && { valueUnits },
34190
34171
  ...costPerValueUnit !== void 0 && { costPerValueUnit },
34191
34172
  ...createdBy !== void 0 && { createdBy },
34192
- ...beatVersionId !== void 0 && { beatVersionId }
34173
+ ...beatVersionId !== void 0 && { beatVersionId },
34174
+ ...workOrderId !== void 0 && { workOrderId }
34193
34175
  };
34194
34176
  const res = await request(
34195
34177
  "POST",
@@ -34930,7 +34912,7 @@ function loadConfig() {
34930
34912
  };
34931
34913
  }
34932
34914
  async function main() {
34933
- console.error(`[harmonica-mcp] v${"3.9.0"} starting\u2026`);
34915
+ console.error(`[harmonica-mcp] v${"3.9.1"} starting\u2026`);
34934
34916
  const config2 = loadConfig();
34935
34917
  const client = createHttpClient({
34936
34918
  apiBaseUrl: config2.apiBaseUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codazen/harmonica-mcp",
3
- "version": "3.9.0",
3
+ "version": "3.9.1",
4
4
  "description": "MCP server for Harmonica — connect any MCP-compatible AI assistant to Harmonica",
5
5
  "license": "MIT",
6
6
  "bin": {