@codazen/harmonica-mcp 4.4.0 → 4.4.2

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 +91 -57
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -21913,7 +21913,7 @@ function registerActivityTools(server, ctx, client) {
21913
21913
  minImportance: importance
21914
21914
  });
21915
21915
  if (entries.length === 0) {
21916
- return { content: [{ type: "text", text: "No activities found for this project." }] };
21916
+ return { content: [{ type: "text", text: "No activities found for this system." }] };
21917
21917
  }
21918
21918
  const lines = [
21919
21919
  `# Project Activities (${entries.length}${hasMore ? "+" : ""})`,
@@ -22416,6 +22416,54 @@ var CHECK_MAX_SCORES = {
22416
22416
  root_coda_quality: 5
22417
22417
  };
22418
22418
 
22419
+ // ../../libs/harmonica-services/src/mcp/errors.ts
22420
+ var BeatQualityGateError = class extends Error {
22421
+ code = "beat_quality_gate_failed";
22422
+ failure;
22423
+ constructor(failure) {
22424
+ super(failure.message);
22425
+ this.failure = failure;
22426
+ }
22427
+ };
22428
+ var PlanVersionsIdempotencyError = class extends Error {
22429
+ code = "plan_versions_already_exist";
22430
+ existingBeatVersionIds;
22431
+ constructor(existingBeatVersionIds) {
22432
+ super(
22433
+ `Beat already has ${existingBeatVersionIds.length} planned Beat Version(s). Pass force=true to plan again.`
22434
+ );
22435
+ this.existingBeatVersionIds = existingBeatVersionIds;
22436
+ }
22437
+ };
22438
+ function isRenderableGateFailure(value) {
22439
+ if (typeof value !== "object" || value === null) return false;
22440
+ const f = value;
22441
+ return typeof f.reason === "string" && typeof f.message === "string";
22442
+ }
22443
+ var CheckResultPendingError = class extends Error {
22444
+ code = "check_result_pending";
22445
+ taskId;
22446
+ constructor(taskId) {
22447
+ super(`Task ${taskId} completed but its scorecard is not yet readable on the blocking path.`);
22448
+ this.name = "CheckResultPendingError";
22449
+ this.taskId = taskId;
22450
+ }
22451
+ };
22452
+ function renderCheckResultPending(err, alsoTry = ["list_checks"]) {
22453
+ const paths = [`get_job_status (jobId="${err.taskId}")`, ...alsoTry].join(" or ");
22454
+ return {
22455
+ content: [{
22456
+ type: "text",
22457
+ text: [
22458
+ `**Status:** completed \u2014 scorecard not yet readable on the blocking path`,
22459
+ `**Job ID:** ${err.taskId}`,
22460
+ "",
22461
+ `The check succeeded on the async path. Retrieve the scorecard with ${paths}.`
22462
+ ].join("\n")
22463
+ }]
22464
+ };
22465
+ }
22466
+
22419
22467
  // ../../libs/harmonica-services/src/mcp/tools/beat-quality-tools.ts
22420
22468
  var POLL_INTERVAL_MS = 4e3;
22421
22469
  var POLL_TIMEOUT_MS = 18e4;
@@ -22438,7 +22486,7 @@ async function pollForCheck(client, taskId) {
22438
22486
  consecutiveErrors = 0;
22439
22487
  if (!task) throw new Error(`Task ${taskId} not found`);
22440
22488
  if (task.status === "completed") {
22441
- if (!task.result) throw new Error(`async:completed:no-result Task ${taskId} succeeded on the async path but the scorecard is not yet readable on the blocking path \u2014 use list_checks or get_job_status to retrieve it.`);
22489
+ if (!task.result) throw new CheckResultPendingError(taskId);
22442
22490
  return task.result;
22443
22491
  }
22444
22492
  if (task.status === "failed") throw new Error(`Quality check failed: ${task.error ?? "unknown error"}`);
@@ -22486,6 +22534,7 @@ Use get_job_status with jobId="${taskId}" to poll for results, or list_checks to
22486
22534
  const check2 = await pollForCheck(client, taskId);
22487
22535
  return { content: [{ type: "text", text: formatScorecard(check2) }] };
22488
22536
  } catch (err) {
22537
+ if (err instanceof CheckResultPendingError) return renderCheckResultPending(err);
22489
22538
  const message = err instanceof Error ? err.message : String(err);
22490
22539
  return { content: [{ type: "text", text: `Quality check failed: ${message}` }], isError: true };
22491
22540
  }
@@ -23094,7 +23143,7 @@ function registerBeatTools(server, ctx, client) {
23094
23143
  beatId: external_exports.string().optional().describe("Override beat ID (auto-generated if omitted)"),
23095
23144
  humanAssignee: humanAssigneeSchema.optional().describe('Human assignee (e.g. { email: "lwu@codazen.com", name: "Liam Wu" })'),
23096
23145
  agentAssignee: agentAssigneeSchema.optional().describe('Agent assignee (e.g. { agentId: "maya", name: "Maya" })'),
23097
- branch: external_exports.string().optional().describe('Git branch for code assessment (falls back to Project.repoDefaultBranch \u2192 "main")')
23146
+ branch: external_exports.string().optional().describe('Git branch for code assessment (falls back to System.repoDefaultBranch \u2192 "main")')
23098
23147
  },
23099
23148
  async ({ systemId, title, description, tags, priority, estimatedEffort, beatId, humanAssignee, agentAssignee, branch }) => {
23100
23149
  try {
@@ -23302,31 +23351,6 @@ var BeatVersionRelocateError = class extends Error {
23302
23351
  var REVISION_STATUSES = ["active", "archived"];
23303
23352
  var BEAT_VERSION_STATUSES = ["active", "archived"];
23304
23353
 
23305
- // ../../libs/harmonica-services/src/mcp/errors.ts
23306
- var BeatQualityGateError = class extends Error {
23307
- code = "beat_quality_gate_failed";
23308
- failure;
23309
- constructor(failure) {
23310
- super(failure.message);
23311
- this.failure = failure;
23312
- }
23313
- };
23314
- var PlanVersionsIdempotencyError = class extends Error {
23315
- code = "plan_versions_already_exist";
23316
- existingBeatVersionIds;
23317
- constructor(existingBeatVersionIds) {
23318
- super(
23319
- `Beat already has ${existingBeatVersionIds.length} planned Beat Version(s). Pass force=true to plan again.`
23320
- );
23321
- this.existingBeatVersionIds = existingBeatVersionIds;
23322
- }
23323
- };
23324
- function isRenderableGateFailure(value) {
23325
- if (typeof value !== "object" || value === null) return false;
23326
- const f = value;
23327
- return typeof f.reason === "string" && typeof f.message === "string";
23328
- }
23329
-
23330
23354
  // ../../libs/harmonica-services/src/mcp/tools/beat-version-tools.ts
23331
23355
  function registerBeatVersionTools(server, ctx, client) {
23332
23356
  server.tool(
@@ -24150,7 +24174,7 @@ ${g.acceptanceCriteria}` : null,
24150
24174
  deliverables: external_exports.string().max(2e4).optional().describe("The Track's deliverables as Markdown bullet points (parallel to acceptanceCriteria)"),
24151
24175
  acceptanceCriteria: external_exports.string().max(2e4).optional().describe("The Track's acceptance criteria (Markdown), parallel to its deliverables"),
24152
24176
  vuRate: external_exports.number().nonnegative().describe("Value-Unit rate (billing rate for this engagement)"),
24153
- systemId: external_exports.string().optional().describe("Optional project to scope this Track to"),
24177
+ systemId: external_exports.string().optional().describe("Optional System to scope this Track to"),
24154
24178
  movementId: external_exports.string().optional().describe("Optional Movement (client SOW) to scope this Track to"),
24155
24179
  parentGroupId: external_exports.string().optional().describe("Optional parent Track ID for nested grouping (Phase 0 scaffolding)")
24156
24180
  };
@@ -24464,7 +24488,7 @@ async function pollForCheck2(client, taskId) {
24464
24488
  consecutiveErrors = 0;
24465
24489
  if (!task) throw new Error(`Task ${taskId} not found`);
24466
24490
  if (task.status === "completed") {
24467
- if (!task.result) throw new Error(`Task ${taskId} completed with no result`);
24491
+ if (!task.result) throw new CheckResultPendingError(taskId);
24468
24492
  return task.result;
24469
24493
  }
24470
24494
  if (task.status === "failed") throw new Error(`Drop quality check failed: ${task.error ?? "unknown error"}`);
@@ -24520,7 +24544,7 @@ function registerDropQualityTools(server, ctx, client) {
24520
24544
  `Drop quality check enqueued for ${dropId}.`,
24521
24545
  `Task ID: ${taskId}`,
24522
24546
  "",
24523
- `Use get_job_status with jobId="${taskId}" to poll, or list_checks with checkType="drop_quality" to view results.`
24547
+ `Use get_job_status with jobId="${taskId}" to view results \u2014 list_checks has no dropId filter.`
24524
24548
  ].join("\n")
24525
24549
  }]
24526
24550
  };
@@ -24528,6 +24552,7 @@ function registerDropQualityTools(server, ctx, client) {
24528
24552
  const check2 = await pollForCheck2(client, taskId);
24529
24553
  return { content: [{ type: "text", text: formatReport(check2) }] };
24530
24554
  } catch (err) {
24555
+ if (err instanceof CheckResultPendingError) return renderCheckResultPending(err, []);
24531
24556
  const message = err instanceof Error ? err.message : String(err);
24532
24557
  return { content: [{ type: "text", text: `Drop quality check failed: ${message}` }], isError: true };
24533
24558
  }
@@ -24914,8 +24939,8 @@ function registerEmbeddingTools(server, ctx, client) {
24914
24939
  "search",
24915
24940
  "Free-text semantic search across system, teamspace, or org scope. Provide exactly one of systemId / teamspaceId / orgId. Teamspace and org searches fan out across child projects and merge by score. Cross-project results carry the source project name.",
24916
24941
  {
24917
- systemId: external_exports.string().optional().describe("Search within a single project"),
24918
- teamspaceId: external_exports.string().optional().describe("Search across all projects in a teamspace"),
24942
+ systemId: external_exports.string().optional().describe("Search within a single System"),
24943
+ teamspaceId: external_exports.string().optional().describe("Search across all Systems in a teamspace"),
24919
24944
  orgId: external_exports.string().optional().describe("Search across every system in this organization \u2014 see list_organizations"),
24920
24945
  query: external_exports.string().describe("Natural language search query"),
24921
24946
  limit: external_exports.coerce.number().int().min(1).max(50).optional().default(10).describe("Max results to return. Default 10"),
@@ -27464,7 +27489,7 @@ function registerNoteTools(server, ctx, client) {
27464
27489
  {
27465
27490
  systemId: external_exports.string().describe("The system ID"),
27466
27491
  beatId: external_exports.string().optional().describe("Scope to a specific beat (omit for all system notes)"),
27467
- noteType: external_exports.union([external_exports.enum(NOTE_TYPE_VALUES), external_exports.array(external_exports.enum(NOTE_TYPE_VALUES)).min(1)]).optional().describe('Filter by note type \u2014 single value or array for multi-select (e.g. ["constraint", "assumption"])'),
27492
+ noteType: external_exports.union([external_exports.enum(NOTE_TYPE_VALUES), external_exports.array(external_exports.enum(NOTE_TYPE_VALUES)).min(1)]).optional().describe('Filter by note type \u2014 single value or array for multi-select (e.g. ["constraint", "guidance"])'),
27468
27493
  status: external_exports.enum(NOTE_STATUS_VALUES).optional().describe("Filter by status"),
27469
27494
  revisionId: external_exports.string().optional().describe("Filter to notes scoped to a specific revision"),
27470
27495
  beatVersionId: external_exports.string().optional().describe("Filter to notes scoped to a specific Beat Version (planning increment)"),
@@ -27632,12 +27657,12 @@ function registerNoteTools(server, ctx, client) {
27632
27657
  },
27633
27658
  async ({ systemId }) => {
27634
27659
  await assertSystemExists(client, systemId);
27635
- const DOCUMENT_FETCH_LIMIT = 5e3;
27636
- const { notes: docs, hasMore: truncated } = await client.listAllSystemNotes(systemId, { noteType: "document" }, DOCUMENT_FETCH_LIMIT);
27660
+ const { notes: docs, hasMore } = await client.listAllSystemNotes(systemId, { noteType: "document" });
27637
27661
  const visibleDocs = docs.filter((d) => d.status !== DISMISSED_NOTE_STATUS);
27638
- const truncationNotice = truncated && visibleDocs.length > 0 ? "\n\n> \u26A0\uFE0F This system has more than 5000 documents. Some documents may not be shown.\n" : "";
27639
- const text = formatDocumentList(visibleDocs) + truncationNotice;
27640
- return { content: [{ type: "text", text }] };
27662
+ if (hasMore) {
27663
+ return { content: [{ type: "text", text: `Unexpected: listAllSystemNotes returned hasMore=true on the no-limit path for system ${systemId} \u2014 the document list may be incomplete. This is a server-side regression.` }], isError: true };
27664
+ }
27665
+ return { content: [{ type: "text", text: formatDocumentList(visibleDocs) }] };
27641
27666
  }
27642
27667
  );
27643
27668
  server.tool(
@@ -27780,7 +27805,7 @@ ${text}` }] };
27780
27805
  systemId: external_exports.string().optional().describe("The system ID (for access control). Provide systemId or orgId, not both."),
27781
27806
  orgId: external_exports.string().optional().describe("The org ID (for org-level notes). Provide orgId or systemId, not both."),
27782
27807
  noteId: external_exports.string().describe("The note ID to update"),
27783
- noteType: external_exports.enum(NOTE_TYPE_VALUES).optional().describe("Reclassify the note type (e.g. assumption \u2192 guidance)"),
27808
+ noteType: external_exports.enum(NOTE_TYPE_VALUES).optional().describe("Reclassify the note type (e.g. context \u2192 guidance)"),
27784
27809
  status: external_exports.enum(NOTE_STATUS_VALUES).optional().describe("New status"),
27785
27810
  content: external_exports.string().optional().describe("Updated content"),
27786
27811
  rationale: external_exports.string().optional().describe("Why this note exists or why this decision was made"),
@@ -28226,7 +28251,7 @@ ${formatTree(tree.root)}` }] };
28226
28251
  heading: external_exports.string().min(1).max(200).describe('Section label, e.g. "Decision Log"'),
28227
28252
  parentNoteId: external_exports.string().optional().describe("Parent page's Note ID; defaults to the notebook root"),
28228
28253
  noteType: external_exports.enum(["context", "constraint", "guidance", "decision", "document"]).optional().describe("Note type to collect (default: decision)"),
28229
- systemId: external_exports.string().optional().describe("Project scope; if omitted, the Notebook's first bound project scope is used"),
28254
+ systemId: external_exports.string().optional().describe("System scope; if omitted, the Notebook's first bound system scope is used"),
28230
28255
  limit: external_exports.number().int().min(1).max(500).optional().describe("Max members to show (default: 25)"),
28231
28256
  order: external_exports.enum(["newest", "oldest"]).optional().describe("Sort order (default: newest)"),
28232
28257
  significance: external_exports.enum(["strategic", "structural", "implementation"]).optional().describe("Only for decision notes \u2014 filter to a significance tier (e.g. strategic). Omit to include all.")
@@ -28244,15 +28269,15 @@ ${formatTree(tree.root)}` }] };
28244
28269
  isError: true
28245
28270
  };
28246
28271
  }
28247
- let scopeProjectId = systemId;
28248
- if (!scopeProjectId) {
28272
+ let scopeSystemId = systemId;
28273
+ if (!scopeSystemId) {
28249
28274
  try {
28250
28275
  const bindings = await client.getNotebookScopeBindings(notebookId);
28251
- scopeProjectId = bindings?.find((b) => b.scopeType === "system" || b.scopeType === "project")?.scopeId;
28276
+ scopeSystemId = bindings?.find((b) => b.scopeType === "system" || b.scopeType === "project")?.scopeId;
28252
28277
  } catch {
28253
28278
  }
28254
28279
  }
28255
- if (!scopeProjectId) {
28280
+ if (!scopeSystemId) {
28256
28281
  return { content: [{ type: "text", text: "No system scope: pass systemId or bind the notebook to a system first." }], isError: true };
28257
28282
  }
28258
28283
  let tree;
@@ -28282,7 +28307,7 @@ ${formatTree(tree.root)}` }] };
28282
28307
  parentId: placement.parentNoteId,
28283
28308
  query: {
28284
28309
  source: "notes",
28285
- scope: `project:${scopeProjectId}`,
28310
+ scope: `system:${scopeSystemId}`,
28286
28311
  noteType: noteType ?? "decision",
28287
28312
  limit: limit ?? 25,
28288
28313
  sortBy: order === "oldest" ? "createdAt_asc" : "createdAt_desc",
@@ -28301,7 +28326,7 @@ ${formatTree(tree.root)}` }] };
28301
28326
  return { content: [{ type: "text", text: `Failed to add collection: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
28302
28327
  }
28303
28328
  return {
28304
- content: [{ type: "text", text: `Live collection added: **${heading}** (${noteType ?? "decision"} @ project:${scopeProjectId}) under ${placement.parentNoteId} at depth ${placement.childDepth}, position ${position}.` }]
28329
+ content: [{ type: "text", text: `Live collection added: **${heading}** (${noteType ?? "decision"} @ system:${scopeSystemId}) under ${placement.parentNoteId} at depth ${placement.childDepth}, position ${position}.` }]
28305
28330
  };
28306
28331
  }
28307
28332
  );
@@ -28314,7 +28339,7 @@ ${formatTree(tree.root)}` }] };
28314
28339
  parentNoteId: external_exports.string().optional().describe("The page Note ID the section sits under; defaults to the notebook root"),
28315
28340
  heading: external_exports.string().min(1).max(200).optional().describe("New section label"),
28316
28341
  noteType: external_exports.enum(["context", "constraint", "guidance", "decision", "document"]).optional().describe("New note type to collect"),
28317
- systemId: external_exports.string().optional().describe("New project scope"),
28342
+ systemId: external_exports.string().optional().describe("New system scope"),
28318
28343
  limit: external_exports.number().int().min(1).max(500).optional().describe("New max members to show"),
28319
28344
  order: external_exports.enum(["newest", "oldest"]).optional().describe("New sort order"),
28320
28345
  significance: external_exports.enum(["strategic", "structural", "implementation"]).optional().describe("New significance-tier filter (decision notes only)")
@@ -28356,9 +28381,14 @@ ${formatTree(tree.root)}` }] };
28356
28381
  return { content: [{ type: "text", text: `Element "${elementId}" is not a live collection \u2014 its configuration cannot be edited here.` }], isError: true };
28357
28382
  }
28358
28383
  const base = existing.query;
28384
+ const normalizedScope = systemId ? `system:${systemId}` : base.scope.replace(/^project:/, "system:");
28385
+ const colonIdx = normalizedScope.indexOf(":");
28386
+ if (colonIdx <= 0 || colonIdx === normalizedScope.length - 1) {
28387
+ return { content: [{ type: "text", text: `Stored scope '${base.scope}' has no system id \u2014 pass an explicit systemId to re-bind this collection.` }], isError: true };
28388
+ }
28359
28389
  const mergedQuery = {
28360
28390
  source: "notes",
28361
- scope: systemId !== void 0 ? `project:${systemId}` : base.scope,
28391
+ scope: normalizedScope,
28362
28392
  noteType: noteType ?? base.noteType,
28363
28393
  limit: limit ?? base.limit,
28364
28394
  sortBy: order !== void 0 ? order === "oldest" ? "createdAt_asc" : "createdAt_desc" : base.sortBy,
@@ -28933,7 +28963,7 @@ async function pollForRootCodaCheck(client, taskId) {
28933
28963
  consecutiveErrors = 0;
28934
28964
  if (!task) throw new Error(`Task ${taskId} not found`);
28935
28965
  if (task.status === "completed") {
28936
- if (!task.result) throw new Error(`Task ${taskId} completed with no result`);
28966
+ if (!task.result) throw new CheckResultPendingError(taskId);
28937
28967
  const result = task.result;
28938
28968
  if (!Array.isArray(result.dimensions) || typeof result.overallScore !== "number") {
28939
28969
  throw new Error(`Task ${taskId} did not return a root Coda scorecard \u2014 was it a run_check task?`);
@@ -29138,7 +29168,7 @@ function registerOrganizationTools(server, ctx, client) {
29138
29168
  `Root Coda quality check enqueued for ${orgId}.`,
29139
29169
  `Task ID: ${taskId}`,
29140
29170
  "",
29141
- `Use get_job_status with jobId="${taskId}" to poll, or list_checks with checkType="root_coda_quality" to view results.`
29171
+ `Use get_job_status with jobId="${taskId}" to view results \u2014 org-scoped checks are not listed by list_checks.`
29142
29172
  ].join("\n")
29143
29173
  }]
29144
29174
  };
@@ -29146,6 +29176,7 @@ function registerOrganizationTools(server, ctx, client) {
29146
29176
  const check2 = await pollForRootCodaCheck(client, taskId);
29147
29177
  return { content: [{ type: "text", text: formatRootCodaReport(check2) }] };
29148
29178
  } catch (err) {
29179
+ if (err instanceof CheckResultPendingError) return renderCheckResultPending(err, []);
29149
29180
  const message = err instanceof Error ? err.message : String(err);
29150
29181
  return { content: [{ type: "text", text: `Root Coda quality check failed: ${message}` }], isError: true };
29151
29182
  }
@@ -29181,7 +29212,7 @@ async function pollForCheck3(client, taskId) {
29181
29212
  }
29182
29213
  consecutiveErrors = 0;
29183
29214
  if (task.status === "completed") {
29184
- if (!task.result) throw new Error(`async:completed:no-result Task ${taskId} succeeded on the async path but the scorecard is not yet readable on the blocking path \u2014 use list_checks or get_job_status to retrieve it.`);
29215
+ if (!task.result) throw new CheckResultPendingError(taskId);
29185
29216
  return task.result;
29186
29217
  }
29187
29218
  if (task.status === "failed") throw new Error(`Beat version quality check failed: ${task.error ?? "unknown error"}`);
@@ -29242,6 +29273,7 @@ Use get_job_status with jobId="${task.taskId}" to poll for results, or list_chec
29242
29273
  const check2 = await pollForCheck3(client, task.taskId);
29243
29274
  return { content: [{ type: "text", text: formatScorecard2(check2) }] };
29244
29275
  } catch (err) {
29276
+ if (err instanceof CheckResultPendingError) return renderCheckResultPending(err);
29245
29277
  const message = err instanceof Error ? err.message : String(err);
29246
29278
  return { content: [{ type: "text", text: `Beat version quality check failed: ${message}` }], isError: true };
29247
29279
  }
@@ -29286,7 +29318,7 @@ async function pollForCheck4(client, taskId) {
29286
29318
  consecutiveErrors = 0;
29287
29319
  if (!task) throw new Error(`Task ${taskId} not found`);
29288
29320
  if (task.status === "completed") {
29289
- if (!task.result) throw new Error(`Task ${taskId} completed with no result`);
29321
+ if (!task.result) throw new CheckResultPendingError(taskId);
29290
29322
  return task.result;
29291
29323
  }
29292
29324
  if (task.status === "failed") throw new Error(`Portfolio coherence check failed: ${task.error ?? "unknown error"}`);
@@ -29390,6 +29422,7 @@ function registerPortfolioCoherenceTools(server, ctx, client) {
29390
29422
  const check2 = await pollForCheck4(client, taskId);
29391
29423
  return { content: [{ type: "text", text: formatReport2(check2) }] };
29392
29424
  } catch (err) {
29425
+ if (err instanceof CheckResultPendingError) return renderCheckResultPending(err);
29393
29426
  const message = err instanceof Error ? err.message : String(err);
29394
29427
  return { content: [{ type: "text", text: `Portfolio coherence check failed: ${message}` }], isError: true };
29395
29428
  }
@@ -30149,7 +30182,7 @@ async function pollForCheck5(client, taskId) {
30149
30182
  }
30150
30183
  consecutiveErrors = 0;
30151
30184
  if (task.status === "completed") {
30152
- if (!task.result) throw new Error(`Task ${taskId} completed with no result`);
30185
+ if (!task.result) throw new CheckResultPendingError(taskId);
30153
30186
  return task.result;
30154
30187
  }
30155
30188
  if (task.status === "failed") throw new Error(`Revision quality check failed: ${task.error ?? "unknown error"}`);
@@ -30211,6 +30244,7 @@ Use get_job_status with jobId="${task.taskId}" to poll for results, or list_chec
30211
30244
  const check2 = await pollForCheck5(client, task.taskId);
30212
30245
  return { content: [{ type: "text", text: formatScorecard3(check2) }] };
30213
30246
  } catch (err) {
30247
+ if (err instanceof CheckResultPendingError) return renderCheckResultPending(err);
30214
30248
  const message = err instanceof Error ? err.message : String(err);
30215
30249
  return { content: [{ type: "text", text: `Revision quality check failed: ${message}` }], isError: true };
30216
30250
  }
@@ -34218,7 +34252,7 @@ function createHttpClient(config2) {
34218
34252
  },
34219
34253
  listSystemDeliverableGroups: async (systemId) => {
34220
34254
  const res = await request("GET", `/api/systems/${encodeURIComponent(systemId)}/deliverable-groups`);
34221
- if (res === void 0) throw new Error(`Project ${systemId} not found`);
34255
+ if (res === void 0) throw new Error(`System ${systemId} not found`);
34222
34256
  return res.deliverableGroups;
34223
34257
  },
34224
34258
  listMovementDeliverableGroups: async (movementId) => {
@@ -34798,7 +34832,7 @@ function createHttpClient(config2) {
34798
34832
  `/api/systems/${encodeURIComponent(systemId)}/beats/draft`,
34799
34833
  input
34800
34834
  );
34801
- if (!result) throw new Error(`Beat draft failed: project ${systemId} not found`);
34835
+ if (!result) throw new Error(`Beat draft failed: system ${systemId} not found`);
34802
34836
  return result;
34803
34837
  },
34804
34838
  triggerSystemEmbedding: async (systemId) => {
@@ -35064,7 +35098,7 @@ function loadConfig() {
35064
35098
  };
35065
35099
  }
35066
35100
  async function main() {
35067
- console.error(`[harmonica-mcp] v${"4.4.0"} starting\u2026`);
35101
+ console.error(`[harmonica-mcp] v${"4.4.2"} starting\u2026`);
35068
35102
  const config2 = loadConfig();
35069
35103
  const client = createHttpClient({
35070
35104
  apiBaseUrl: config2.apiBaseUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codazen/harmonica-mcp",
3
- "version": "4.4.0",
3
+ "version": "4.4.2",
4
4
  "description": "MCP server for Harmonica — connect any MCP-compatible AI assistant to Harmonica",
5
5
  "license": "MIT",
6
6
  "bin": {