@codazen/harmonica-mcp 3.5.0 → 3.6.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 +111 -376
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -21587,6 +21587,18 @@ async function fetchProjectInOrg(client, projectId, orgId) {
21587
21587
  }
21588
21588
  return project;
21589
21589
  }
21590
+ async function fetchSystemInOrg(client, systemId, orgId) {
21591
+ const system = await client.getSystem(systemId);
21592
+ if (!system) {
21593
+ throw new Error(`System not found: "${systemId}"`);
21594
+ }
21595
+ if (system.orgId !== orgId) {
21596
+ throw new Error(
21597
+ `System "${systemId}" does not belong to the configured organization. Access denied.`
21598
+ );
21599
+ }
21600
+ return system;
21601
+ }
21590
21602
  async function fetchBeatInOrg(client, beatId, orgId) {
21591
21603
  const beat = await client.getBeat(beatId);
21592
21604
  if (!beat) {
@@ -21968,7 +21980,7 @@ function registerActivityTools(server, ctx, client) {
21968
21980
  },
21969
21981
  async ({ projectId, limit, importance }) => {
21970
21982
  await assertProjectInOrg(client, projectId, ctx.orgId);
21971
- const { entries, hasMore } = await client.listProjectActivities(projectId, {
21983
+ const { entries, hasMore } = await client.listSystemActivities(projectId, {
21972
21984
  limit: limit ?? 30,
21973
21985
  minImportance: importance
21974
21986
  });
@@ -22362,7 +22374,7 @@ function registerBeatConvergenceTools(server, ctx, client) {
22362
22374
  { beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
22363
22375
  async ({ beatId }) => {
22364
22376
  const beat = await fetchBeatInOrg(client, beatId, ctx.orgId);
22365
- const report = await client.getBeatConvergence(beat.projectId, beatId);
22377
+ const report = await client.getBeatConvergence(beat.systemId ?? beat.projectId, beatId);
22366
22378
  if (!report) {
22367
22379
  return { content: [{ type: "text", text: `No convergence data for beat "${beatId}".` }], isError: true };
22368
22380
  }
@@ -22689,7 +22701,7 @@ function formatDeliveryPolicy(policy, unresolvedReason) {
22689
22701
  }
22690
22702
  function formatProjectContext(project, notes, orgCoda) {
22691
22703
  const sections = [];
22692
- sections.push(`# Project: ${project.title}`);
22704
+ sections.push(`# System: ${project.title}`);
22693
22705
  sections.push(`**ID:** ${project.projectId}`);
22694
22706
  sections.push(`**Status:** ${project.status.replace("_", " ")}`);
22695
22707
  if (project.repoOwner && project.repoName) {
@@ -22977,7 +22989,7 @@ function formatSessionDetail(session) {
22977
22989
  "",
22978
22990
  `**Type:** ${type}`,
22979
22991
  `**Status:** ${status}`,
22980
- `**Project:** ${session.projectId}`,
22992
+ `**Project:** ${session.systemId ?? session.projectId}`,
22981
22993
  `**Actor:** ${session.actor.agentPersona}${session.actor.agentRole ? ` (${session.actor.agentRole})` : ""}`
22982
22994
  ];
22983
22995
  if (session.actor.initiator) {
@@ -23131,7 +23143,7 @@ function registerBeatTools(server, ctx, client) {
23131
23143
  },
23132
23144
  async ({ beatId, projectId }) => {
23133
23145
  const beat = await fetchBeatInOrg(client, beatId, ctx.orgId);
23134
- const resolvedProjectId = projectId ?? beat.projectId;
23146
+ const resolvedProjectId = projectId ?? beat.systemId ?? beat.projectId;
23135
23147
  const timeline = await client.getUnifiedBeatTimeline(resolvedProjectId, beatId);
23136
23148
  if (timeline.length === 0) {
23137
23149
  return { content: [{ type: "text", text: `No history for beat "${beatId}".` }] };
@@ -23245,7 +23257,7 @@ ${formatBeatDetail(beat)}${similarityWarning}` }] };
23245
23257
  return { content: [{ type: "text", text: `Beat not found: "${beatId}"` }], isError: true };
23246
23258
  }
23247
23259
  await logBeatActivity(client, {
23248
- projectId: beat.projectId,
23260
+ projectId: beat.systemId ?? beat.projectId,
23249
23261
  action: "beat_updated",
23250
23262
  beatIds: [beatId],
23251
23263
  beatTitles: [updated.title],
@@ -23275,7 +23287,7 @@ ${formatBeatDetail(beat)}${similarityWarning}` }] };
23275
23287
  async ({ projectId, beatId, status, includeArchived }) => {
23276
23288
  try {
23277
23289
  await assertProjectInOrg(client, projectId, ctx.orgId);
23278
- const revisions = beatId ? await client.listBeatRevisions(projectId, beatId, { status, includeArchived }) : await client.listProjectRevisions(projectId, { status, includeArchived });
23290
+ const revisions = beatId ? await client.listBeatRevisions(projectId, beatId, { status, includeArchived }) : await client.listSystemRevisions(projectId, { status, includeArchived });
23279
23291
  return { content: [{ type: "text", text: formatRevisionSummaryTable(revisions) }] };
23280
23292
  } catch (err) {
23281
23293
  const message = err instanceof Error ? err.message : String(err);
@@ -23464,7 +23476,7 @@ function registerBeatVersionTools(server, ctx, client) {
23464
23476
  return { content: [{ type: "text", text: `Beat not found: "${beatId}"` }], isError: true };
23465
23477
  }
23466
23478
  await assertBeatInOrg(client, beatId, ctx.orgId);
23467
- const beatVersion = await client.createBeatVersion(beat.projectId, beatId, {
23479
+ const beatVersion = await client.createBeatVersion(beat.systemId ?? beat.projectId, beatId, {
23468
23480
  title,
23469
23481
  description,
23470
23482
  scope,
@@ -23936,7 +23948,7 @@ function registerCheckTools(server, ctx, client) {
23936
23948
  },
23937
23949
  async ({ projectId }) => {
23938
23950
  try {
23939
- const checks = await client.listLatestProjectChecks(projectId);
23951
+ const checks = await client.listLatestSystemChecks(projectId);
23940
23952
  if (checks.length === 0) {
23941
23953
  return { content: [{ type: "text", text: `No checks found for project ${projectId}.` }] };
23942
23954
  }
@@ -24316,18 +24328,6 @@ ${g.acceptanceCriteria}` : null,
24316
24328
  };
24317
24329
  }
24318
24330
  );
24319
- server.tool(
24320
- "list_deliverable_group_deliverables",
24321
- 'List all Deliverables assigned to a DeliverableGroup. Use to answer "what deliverables are in this phase/milestone?" or "what has the team committed to in this engagement group?"',
24322
- {
24323
- deliverableGroupId: external_exports.string().describe("The DeliverableGroup ID")
24324
- },
24325
- async ({ deliverableGroupId }) => {
24326
- const items = await client.listDeliverableGroupDeliverables(deliverableGroupId);
24327
- const text = items.length === 0 ? "No deliverables in this group." : items.map((d) => `[${d.deliverableId}] teamspace: ${d.teamspaceId}`).join("\n");
24328
- return { content: [{ type: "text", text }] };
24329
- }
24330
- );
24331
24331
  server.tool(
24332
24332
  "list_deliverable_group_drops",
24333
24333
  'List all Drops linked to a DeliverableGroup. Use to answer "what releases are part of this phase?" or "which software drops are tied to this engagement milestone?"',
@@ -24385,108 +24385,6 @@ ${g.acceptanceCriteria}` : null,
24385
24385
  );
24386
24386
  }
24387
24387
 
24388
- // ../../libs/harmonica-services/src/mcp/tools/deliverable-tools.ts
24389
- var import_node_crypto = require("node:crypto");
24390
- function registerDeliverableTools(server, ctx, client) {
24391
- server.tool(
24392
- "list_deliverables",
24393
- "List all Deliverables (tangible client-facing artifacts or milestones owed by a date \u2014 e.g. board deck, roadmap doc, audit report) for a Teamspace.",
24394
- {
24395
- teamspaceId: external_exports.string().describe("The teamspace ID")
24396
- },
24397
- async ({ teamspaceId }) => {
24398
- const deliverables = await client.listTeamspaceDeliverables(teamspaceId);
24399
- const text = deliverables.length === 0 ? "No deliverables found." : deliverables.map(
24400
- (d) => `[${d.deliverableId}] ${d.title} \u2014 ${d.status}${d.dueDate ? ` (due ${d.dueDate})` : ""}${d.deliverableGroupId ? ` (group: ${d.deliverableGroupId})` : ""}`
24401
- ).join("\n");
24402
- return { content: [{ type: "text", text }] };
24403
- }
24404
- );
24405
- server.tool(
24406
- "get_deliverable",
24407
- "Get a Deliverable (tangible client-facing artifact or milestone) by ID.",
24408
- {
24409
- deliverableId: external_exports.string().describe("The deliverable ID")
24410
- },
24411
- async ({ deliverableId }) => {
24412
- const d = await client.getDeliverable(deliverableId);
24413
- if (!d) return { content: [{ type: "text", text: `Deliverable not found: ${deliverableId}` }] };
24414
- const text = [
24415
- `ID: ${d.deliverableId}`,
24416
- `Title: ${d.title}`,
24417
- `Teamspace: ${d.teamspaceId}`,
24418
- `Status: ${d.status}`,
24419
- d.description ? `Description: ${d.description}` : null,
24420
- d.dueDate ? `Due: ${d.dueDate}` : null,
24421
- d.deliverableGroupId ? `Group: ${d.deliverableGroupId}` : null,
24422
- `Version: ${d.version}`,
24423
- `Created: ${d.createdAt}`
24424
- ].filter(Boolean).join("\n");
24425
- return { content: [{ type: "text", text }] };
24426
- }
24427
- );
24428
- server.tool(
24429
- "create_deliverable",
24430
- 'Create a Deliverable in a Teamspace. A Deliverable is a tangible commitment the agency owes the client by a date \u2014 board presentation, roadmap document, audit report, design mockups, training session, etc. Use this when the user describes something we will hand over. Do NOT use this for software capabilities the product provides to its end users \u2014 those are Beats (use create_beat). Do NOT use this for context, assumptions, constraints, or decisions \u2014 those are Notes (use create_note). Recognition cue: phrases like "we owe them\u2026", "due by\u2026", "the deck", "the report" \u2192 Deliverable. "Users will be able to\u2026", "the screen that\u2026" \u2192 Beat.',
24431
- {
24432
- teamspaceId: external_exports.string().describe("The teamspace ID"),
24433
- title: external_exports.string().min(1).max(300).describe("Deliverable title"),
24434
- description: external_exports.string().max(2e3).optional().describe("Optional description"),
24435
- dueDate: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe("Due date (YYYY-MM-DD)"),
24436
- status: external_exports.enum(["draft", "active", "delivered", "cancelled"]).optional().describe("Initial status (default: draft)"),
24437
- deliverableGroupId: external_exports.string().optional().describe("Assign to a DeliverableGroup by ID at creation time")
24438
- },
24439
- async ({ teamspaceId, title, description, dueDate, status, deliverableGroupId }) => {
24440
- const deliverableId = `dlv-${(0, import_node_crypto.randomUUID)()}`;
24441
- const d = await client.createDeliverable({
24442
- deliverableId,
24443
- teamspaceId,
24444
- orgId: ctx.orgId,
24445
- title,
24446
- description,
24447
- dueDate,
24448
- status,
24449
- createdBy: ctx.user.userId,
24450
- ...deliverableGroupId !== void 0 && { deliverableGroupId }
24451
- });
24452
- return {
24453
- content: [{
24454
- type: "text",
24455
- text: `Created deliverable: [${d.deliverableId}] ${d.title} \u2014 ${d.status}`
24456
- }]
24457
- };
24458
- }
24459
- );
24460
- server.tool(
24461
- "update_deliverable",
24462
- "Update an existing Deliverable. Transition status as the artifact progresses: draft \u2192 active \u2192 delivered (or cancelled).",
24463
- {
24464
- deliverableId: external_exports.string().describe("The deliverable ID"),
24465
- title: external_exports.string().min(1).max(300).optional().describe("New title"),
24466
- description: external_exports.string().max(2e3).optional().describe("New description"),
24467
- dueDate: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional().describe("New due date (YYYY-MM-DD), or null to clear"),
24468
- status: external_exports.enum(["draft", "active", "delivered", "cancelled"]).optional().describe("New status"),
24469
- deliverableGroupId: external_exports.string().nullable().optional().describe("Assign to a DeliverableGroup by ID, or null to ungroup")
24470
- },
24471
- async ({ deliverableId, title, description, dueDate, status, deliverableGroupId }) => {
24472
- const updates = {};
24473
- if (title !== void 0) updates["title"] = title;
24474
- if (description !== void 0) updates["description"] = description;
24475
- if (dueDate !== void 0) updates["dueDate"] = dueDate;
24476
- if (status !== void 0) updates["status"] = status;
24477
- if (deliverableGroupId !== void 0) updates["deliverableGroupId"] = deliverableGroupId;
24478
- const d = await client.updateDeliverable(deliverableId, updates);
24479
- if (!d) return { content: [{ type: "text", text: `Deliverable not found: ${deliverableId}` }] };
24480
- return {
24481
- content: [{
24482
- type: "text",
24483
- text: `Updated deliverable: [${d.deliverableId}] ${d.title} \u2014 ${d.status}`
24484
- }]
24485
- };
24486
- }
24487
- );
24488
- }
24489
-
24490
24388
  // ../../libs/harmonica-services/src/mcp/tools/downbeat-harmony-report-tools.ts
24491
24389
  function formatQualifyingBeat(b) {
24492
24390
  return `- \`${b.beatId}\` ${b.title}: ${b.signals.join(", ")}`;
@@ -24549,7 +24447,7 @@ function registerDownbeatTools(server, ctx, client) {
24549
24447
  { beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
24550
24448
  async ({ beatId }) => {
24551
24449
  const beat = await fetchBeatInOrg(client, beatId, ctx.orgId);
24552
- const downbeat = await client.setDownbeat(beat.projectId, beatId);
24450
+ const downbeat = await client.setDownbeat(beat.systemId ?? beat.projectId, beatId);
24553
24451
  if (!downbeat) {
24554
24452
  return { content: [{ type: "text", text: `Could not set a Downbeat for beat "${beatId}".` }], isError: true };
24555
24453
  }
@@ -24562,7 +24460,7 @@ function registerDownbeatTools(server, ctx, client) {
24562
24460
  { beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
24563
24461
  async ({ beatId }) => {
24564
24462
  const beat = await fetchBeatInOrg(client, beatId, ctx.orgId);
24565
- const result = await client.listIncrementsSinceDownbeat(beat.projectId, beatId);
24463
+ const result = await client.listIncrementsSinceDownbeat(beat.systemId ?? beat.projectId, beatId);
24566
24464
  if (!result) {
24567
24465
  return { content: [{ type: "text", text: `No such beat "${beatId}".` }], isError: true };
24568
24466
  }
@@ -24575,7 +24473,7 @@ function registerDownbeatTools(server, ctx, client) {
24575
24473
  { beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
24576
24474
  async ({ beatId }) => {
24577
24475
  const beat = await fetchBeatInOrg(client, beatId, ctx.orgId);
24578
- const report = await client.getCodaDrift(beat.projectId, beatId);
24476
+ const report = await client.getCodaDrift(beat.systemId ?? beat.projectId, beatId);
24579
24477
  if (!report) {
24580
24478
  return { content: [{ type: "text", text: `No such beat "${beatId}".` }], isError: true };
24581
24479
  }
@@ -24588,7 +24486,7 @@ function registerDownbeatTools(server, ctx, client) {
24588
24486
  { beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
24589
24487
  async ({ beatId }) => {
24590
24488
  const beat = await fetchBeatInOrg(client, beatId, ctx.orgId);
24591
- const report = await client.getRelationalDrift(beat.projectId, beatId);
24489
+ const report = await client.getRelationalDrift(beat.systemId ?? beat.projectId, beatId);
24592
24490
  if (!report) {
24593
24491
  return { content: [{ type: "text", text: `No such beat "${beatId}".` }], isError: true };
24594
24492
  }
@@ -28513,7 +28411,7 @@ ${lines.join("\n")}` }] };
28513
28411
  }
28514
28412
 
28515
28413
  // ../../libs/harmonica-services/src/mcp/tools/onboarding-tools.ts
28516
- var import_node_crypto2 = require("node:crypto");
28414
+ var import_node_crypto = require("node:crypto");
28517
28415
  var OnboardingNoteInputSchema = external_exports.object({
28518
28416
  noteType: external_exports.enum(["context", "assumption", "constraint", "guidance", "decision"]),
28519
28417
  content: external_exports.string().min(1),
@@ -28525,11 +28423,6 @@ var OnboardingProjectInputSchema = external_exports.object({
28525
28423
  description: external_exports.string().max(2e3).optional(),
28526
28424
  notes: external_exports.array(OnboardingNoteInputSchema).optional()
28527
28425
  });
28528
- var OnboardingDeliverableInputSchema = external_exports.object({
28529
- title: external_exports.string().min(1).max(300),
28530
- description: external_exports.string().max(2e3).optional(),
28531
- dueDate: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
28532
- });
28533
28426
  function registerOnboardingTools(server, ctx, client) {
28534
28427
  server.tool(
28535
28428
  "initiate_teamspace_onboarding",
@@ -28627,7 +28520,7 @@ function registerOnboardingTools(server, ctx, client) {
28627
28520
  server.tool(
28628
28521
  "create_teamspace_onboarding_batch",
28629
28522
  [
28630
- "Create a Teamspace, Projects, Notes, and Deliverables",
28523
+ "Create a Teamspace, Projects, and Notes",
28631
28524
  "from a PM-approved onboarding payload in a single batch operation.",
28632
28525
  "Call this after the PM approves the pre-flight review card from initiate_teamspace_onboarding.",
28633
28526
  "Returns a creation receipt with IDs and counts for all created entities.",
@@ -28636,20 +28529,18 @@ function registerOnboardingTools(server, ctx, client) {
28636
28529
  {
28637
28530
  teamspaceName: external_exports.string().min(1).max(200).describe("Teamspace display name"),
28638
28531
  projects: external_exports.array(OnboardingProjectInputSchema).min(1).describe("Phase 1 projects to create, each linked to the Teamspace"),
28639
- teamspaceNotes: external_exports.array(OnboardingNoteInputSchema).describe("Constraints, assumptions, and context notes at the Teamspace/project level"),
28640
- deliverables: external_exports.array(OnboardingDeliverableInputSchema).describe("Deliverables detected from commitment language in documents")
28532
+ teamspaceNotes: external_exports.array(OnboardingNoteInputSchema).describe("Constraints, assumptions, and context notes at the Teamspace/project level")
28641
28533
  },
28642
28534
  async ({
28643
28535
  teamspaceName,
28644
28536
  projects,
28645
- teamspaceNotes,
28646
- deliverables
28537
+ teamspaceNotes
28647
28538
  }) => {
28648
28539
  const lines = [];
28649
28540
  let teamspace;
28650
28541
  try {
28651
28542
  teamspace = await client.createTeamspace({
28652
- teamspaceId: (0, import_node_crypto2.randomUUID)(),
28543
+ teamspaceId: (0, import_node_crypto.randomUUID)(),
28653
28544
  slug: teamspaceName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, ""),
28654
28545
  orgId: ctx.orgId,
28655
28546
  name: teamspaceName,
@@ -28666,8 +28557,8 @@ Error: ${msg}`);
28666
28557
  const createdProjects = [];
28667
28558
  for (const p of projects) {
28668
28559
  try {
28669
- const project = await client.createProject({
28670
- projectId: (0, import_node_crypto2.randomUUID)(),
28560
+ const project = await client.createSystem({
28561
+ projectId: (0, import_node_crypto.randomUUID)(),
28671
28562
  orgId: ctx.orgId,
28672
28563
  teamspaceId: teamspace.teamspaceId,
28673
28564
  ownerUserId: ctx.user?.userId ?? "system",
@@ -28711,7 +28602,7 @@ Projects (${createdProjects.length}):`);
28711
28602
  for (const note of projects[i]?.notes ?? []) {
28712
28603
  try {
28713
28604
  const n = await client.createNote({
28714
- noteId: `N-TEMP-${(0, import_node_crypto2.randomUUID)()}`,
28605
+ noteId: `N-TEMP-${(0, import_node_crypto.randomUUID)()}`,
28715
28606
  // overridden server-side
28716
28607
  projectId: project.projectId,
28717
28608
  noteType: note.noteType,
@@ -28728,24 +28619,6 @@ Projects (${createdProjects.length}):`);
28728
28619
  }
28729
28620
  lines.push(`
28730
28621
  Notes filed: ${createdNotes.length}`);
28731
- const createdDeliverables = [];
28732
- for (const d of deliverables) {
28733
- try {
28734
- const del = await client.createDeliverable({
28735
- deliverableId: (0, import_node_crypto2.randomUUID)(),
28736
- // overridden server-side
28737
- teamspaceId: teamspace.teamspaceId,
28738
- orgId: ctx.orgId,
28739
- title: d.title,
28740
- description: d.description,
28741
- dueDate: d.dueDate,
28742
- createdBy: ctx.user?.userId ?? "mcp"
28743
- });
28744
- createdDeliverables.push(del.deliverableId);
28745
- } catch {
28746
- }
28747
- }
28748
- lines.push(`Deliverables: ${createdDeliverables.length}`);
28749
28622
  lines.unshift(`\u2705 Onboarding batch created successfully.`);
28750
28623
  return { content: [{ type: "text", text: lines.join("\n") }] };
28751
28624
  }
@@ -29455,11 +29328,11 @@ function registerRevisionLifecycleTools(server, ctx, client) {
29455
29328
  if (!parentBv) {
29456
29329
  return { content: [{ type: "text", text: `Beat Version not found: "${beatVersionId}"` }], isError: true };
29457
29330
  }
29458
- if (parentBv.beatId !== beatId || parentBv.projectId !== beat.projectId) {
29331
+ if (parentBv.beatId !== beatId || (parentBv.systemId ?? parentBv.projectId) !== (beat.systemId ?? beat.projectId)) {
29459
29332
  return { content: [{ type: "text", text: `Beat Version "${beatVersionId}" belongs to a different Beat.` }], isError: true };
29460
29333
  }
29461
29334
  }
29462
- const { revision, warnings } = await client.createRevision(beat.projectId, beatId, {
29335
+ const { revision, warnings } = await client.createRevision(beat.systemId ?? beat.projectId, beatId, {
29463
29336
  title,
29464
29337
  description,
29465
29338
  changeSummary,
@@ -29547,7 +29420,7 @@ function registerRevisionLifecycleTools(server, ctx, client) {
29547
29420
  if (!bv) {
29548
29421
  return { content: [{ type: "text", text: `Beat Version not found: "${beatVersionId}"` }], isError: true };
29549
29422
  }
29550
- if (bv.beatId !== revision.beatId || bv.projectId !== revision.projectId) {
29423
+ if (bv.beatId !== revision.beatId || (bv.systemId ?? bv.projectId) !== (revision.systemId ?? revision.projectId)) {
29551
29424
  return { content: [{ type: "text", text: `Beat Version "${beatVersionId}" belongs to a different Beat.` }], isError: true };
29552
29425
  }
29553
29426
  }
@@ -30051,7 +29924,7 @@ function registerSessionTools(server, ctx, client) {
30051
29924
  includeTerminal: external_exports.boolean().optional().describe("Include terminal (closed) sessions. Default false.")
30052
29925
  },
30053
29926
  async ({ systemId, status, includeTerminal }) => {
30054
- const sessions = await client.listProjectSessions(systemId, {
29927
+ const sessions = await client.listSystemSessions(systemId, {
30055
29928
  status,
30056
29929
  includeTerminal
30057
29930
  });
@@ -30718,7 +30591,7 @@ function registerProjectTools(server, ctx, client) {
30718
30591
  fetchProjectInOrg(client, systemId, ctx.orgId),
30719
30592
  client.getOrg(ctx.orgId)
30720
30593
  ]);
30721
- const notes = await client.listProjectNotes(systemId, {
30594
+ const notes = await client.listSystemNotes(systemId, {
30722
30595
  limit: noteLimit ?? DEFAULT_CONTEXT_NOTE_LIMIT
30723
30596
  });
30724
30597
  const text = formatProjectContext(project, notes, org?.coda);
@@ -30886,7 +30759,7 @@ function registerProjectTools(server, ctx, client) {
30886
30759
  }
30887
30760
 
30888
30761
  // ../../libs/harmonica-services/src/mcp/tools/teamspace-tools.ts
30889
- var import_node_crypto3 = require("node:crypto");
30762
+ var import_node_crypto2 = require("node:crypto");
30890
30763
  var EnsembleMemberSchema = external_exports.discriminatedUnion("type", [
30891
30764
  external_exports.object({ type: external_exports.literal("human"), email: external_exports.string().email().max(254), name: external_exports.string().min(1).max(200) }),
30892
30765
  external_exports.object({ type: external_exports.literal("agent"), agentId: external_exports.string().min(1).max(100), name: external_exports.string().min(1).max(200) })
@@ -30925,7 +30798,7 @@ function registerTeamspaceTools(server, ctx, client) {
30925
30798
  if (!ts || ts.orgId !== ctx.orgId) {
30926
30799
  return { content: [{ type: "text", text: `Teamspace not found: ${teamspaceId}` }], isError: true };
30927
30800
  }
30928
- const allProjects = await client.listOrgProjects(ctx.orgId);
30801
+ const allProjects = await client.listOrgSystems(ctx.orgId);
30929
30802
  const tsProjects = allProjects.filter((p) => p.teamspaceId === teamspaceId);
30930
30803
  const lines = [
30931
30804
  `ID: ${ts.teamspaceId}`,
@@ -30962,7 +30835,7 @@ function registerTeamspaceTools(server, ctx, client) {
30962
30835
  name: external_exports.string().min(1).max(200).describe("Teamspace name")
30963
30836
  },
30964
30837
  async ({ name }) => {
30965
- const teamspaceId = `ts-${(0, import_node_crypto3.randomUUID)()}`;
30838
+ const teamspaceId = `ts-${(0, import_node_crypto2.randomUUID)()}`;
30966
30839
  const slug = generateTeamspaceSlug(name);
30967
30840
  const ts = await client.createTeamspace({
30968
30841
  teamspaceId,
@@ -31100,7 +30973,7 @@ function registerValueVelocityTools(server, ctx, client) {
31100
30973
  try {
31101
30974
  await assertProjectInOrg(client, systemId, ctx.orgId);
31102
30975
  const [rankedResult, staleWResult] = await Promise.allSettled([
31103
- withLeverage ? client.rankProjectBeatVersionsWithLeverage(systemId, lens) : client.rankProjectBeatVersions(systemId, lens),
30976
+ withLeverage ? client.rankSystemBeatVersionsWithLeverage(systemId, lens) : client.rankSystemBeatVersions(systemId, lens),
31104
30977
  client.getStaleWBeatIds(systemId)
31105
30978
  ]);
31106
30979
  if (rankedResult.status === "rejected") {
@@ -31594,7 +31467,7 @@ ${JSON.stringify(task.result, null, 2)}
31594
31467
  },
31595
31468
  async ({ projectId, status, limit }) => {
31596
31469
  await assertProjectInOrg(client, projectId, ctx.orgId);
31597
- const tasks = await client.listProjectTasks(projectId, {
31470
+ const tasks = await client.listSystemTasks(projectId, {
31598
31471
  status,
31599
31472
  limit
31600
31473
  });
@@ -31652,7 +31525,7 @@ ${JSON.stringify(task.result, null, 2)}
31652
31525
  },
31653
31526
  async ({ projectId, status, limit }) => {
31654
31527
  await assertProjectInOrg(client, projectId, ctx.orgId);
31655
- const tasks = await client.listProjectTasks(projectId, {
31528
+ const tasks = await client.listSystemTasks(projectId, {
31656
31529
  status,
31657
31530
  limit
31658
31531
  });
@@ -31954,7 +31827,6 @@ var TOOL_PROFILES = Object.freeze({
31954
31827
  allow(registerBeatVersionTools, ["list_beat_versions", "get_beat_version"]),
31955
31828
  allow(registerNoteTools, ["list_notes", "get_note", "create_note", "list_documents"]),
31956
31829
  allow(registerDropTools, ["list_drops", "get_drop"]),
31957
- allow(registerDeliverableTools, ["list_deliverables", "get_deliverable"]),
31958
31830
  allow(registerEmbeddingTools, ["search", "find_similar_notes"])
31959
31831
  ])
31960
31832
  });
@@ -32007,7 +31879,6 @@ function registerAllTools(server, ctx, client, profile) {
32007
31879
  registerBaselineTools(server, ctx, client);
32008
31880
  registerTeamspaceTools(server, ctx, client);
32009
31881
  registerDeliverableGroupTools(server, ctx, client);
32010
- registerDeliverableTools(server, ctx, client);
32011
31882
  registerDropTools(server, ctx, client);
32012
31883
  registerSessionTools(server, ctx, client);
32013
31884
  registerOnboardingTools(server, ctx, client);
@@ -32026,12 +31897,12 @@ function registerAllTools(server, ctx, client, profile) {
32026
31897
  // ../../libs/harmonica-services/src/mcp/resources/beat-resources.ts
32027
31898
  function registerBeatResources(server, ctx, client) {
32028
31899
  server.resource(
32029
- "project-beats",
32030
- new ResourceTemplate("harmonica://project/{projectId}/beats", { list: void 0 }),
32031
- async (uri, { projectId }) => {
32032
- const pid = String(projectId);
32033
- await assertProjectInOrg(client, pid, ctx.orgId);
32034
- const beats = await client.listSystemBeats(pid);
31900
+ "system-beats",
31901
+ new ResourceTemplate("harmonica://system/{systemId}/beats", { list: void 0 }),
31902
+ async (uri, { systemId }) => {
31903
+ const sid = String(systemId);
31904
+ await fetchSystemInOrg(client, sid, ctx.orgId);
31905
+ const beats = await client.listSystemBeats(sid);
32035
31906
  const text = formatBeatSummaryTable(beats);
32036
31907
  return { contents: [{ uri: uri.href, text }] };
32037
31908
  }
@@ -32066,14 +31937,14 @@ function registerGuidelinesResources(server, client) {
32066
31937
  }
32067
31938
 
32068
31939
  // ../../libs/harmonica-services/src/mcp/resources/system-resources.ts
32069
- function registerProjectResources(server, ctx, client) {
31940
+ function registerSystemResources(server, ctx, client) {
32070
31941
  server.resource(
32071
- "project-context",
32072
- new ResourceTemplate("harmonica://project/{projectId}/context", { list: void 0 }),
32073
- async (uri, { projectId }) => {
32074
- const pid = String(projectId);
32075
- const project = await fetchProjectInOrg(client, pid, ctx.orgId);
32076
- const notes = await client.listSystemNotes(pid, { limit: DEFAULT_CONTEXT_NOTE_LIMIT });
31942
+ "system-context",
31943
+ new ResourceTemplate("harmonica://system/{systemId}/context", { list: void 0 }),
31944
+ async (uri, { systemId }) => {
31945
+ const sid = String(systemId);
31946
+ const project = await fetchSystemInOrg(client, sid, ctx.orgId);
31947
+ const notes = await client.listSystemNotes(sid, { limit: DEFAULT_CONTEXT_NOTE_LIMIT });
32077
31948
  const text = formatProjectContext(project, notes);
32078
31949
  return { contents: [{ uri: uri.href, text }] };
32079
31950
  }
@@ -32082,7 +31953,7 @@ function registerProjectResources(server, ctx, client) {
32082
31953
 
32083
31954
  // ../../libs/harmonica-services/src/mcp/resources/index.ts
32084
31955
  function registerAllResources(server, ctx, client) {
32085
- registerProjectResources(server, ctx, client);
31956
+ registerSystemResources(server, ctx, client);
32086
31957
  registerBeatResources(server, ctx, client);
32087
31958
  registerGuidelinesResources(server, client);
32088
31959
  }
@@ -32353,46 +32224,11 @@ function createHttpClient(config2) {
32353
32224
  if (!raw) return void 0;
32354
32225
  return { ...raw, projectId: raw.id ?? raw.projectId, title: raw.name ?? raw.title, orgId: raw.organizationId ?? raw.orgId };
32355
32226
  },
32356
- // @deprecated aliases — remove in cleanup increment (BV2)
32357
- listOrgProjects: async (orgId) => {
32358
- const raw = await request("GET", `/api/organizations/${encodeURIComponent(orgId)}/projects/summaries`);
32359
- return (raw ?? []).map((p) => ({ ...p, projectId: p.id, title: p.name, orgId: p.organizationId }));
32360
- },
32361
- getProject: async (projectId) => {
32362
- const raw = await request("GET", `/api/systems/${encodeURIComponent(projectId)}`);
32363
- if (!raw) return void 0;
32364
- return {
32365
- ...raw,
32366
- projectId: raw.id ?? raw.projectId,
32367
- title: raw.name ?? raw.title,
32368
- orgId: raw.organizationId ?? raw.orgId
32369
- };
32370
- },
32371
- createProject: async (input) => {
32372
- const raw = await request("POST", "/api/systems", {
32373
- organizationId: input.orgId,
32374
- teamspaceId: input.teamspaceId,
32375
- name: input.title,
32376
- description: input.description,
32377
- strategy: input.strategy,
32378
- // Without this the owning Account is dropped on the wire and the System
32379
- // is created unlinked — invisible in the Account's Systems list.
32380
- ...input.accountId !== void 0 && { accountId: input.accountId }
32381
- });
32382
- return { ...raw, orgId: raw?.organizationId ?? raw?.orgId };
32383
- },
32384
- updateProject: (projectId, updates) => request("PATCH", `/api/systems/${encodeURIComponent(projectId)}`, updates),
32385
- archiveProject: (projectId) => request("PATCH", `/api/systems/${encodeURIComponent(projectId)}/archive`),
32386
32227
  getSystemFact: async (projectId) => {
32387
32228
  const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/facts`);
32388
32229
  return result?.data;
32389
32230
  },
32390
- getProjectFact: async (projectId) => {
32391
- const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/facts`);
32392
- return result?.data;
32393
- },
32394
32231
  saveSystemFact: (input) => request("PUT", `/api/systems/${encodeURIComponent(String(input.projectId))}/facts`, input),
32395
- saveProjectFact: (input) => request("PUT", `/api/systems/${encodeURIComponent(String(input.projectId))}/facts`, input),
32396
32232
  transitionSystemLifecycleState: async (projectId, targetState, options) => {
32397
32233
  return request("POST", `/api/systems/${encodeURIComponent(projectId)}/lifecycle/transitions`, {
32398
32234
  targetState,
@@ -32400,13 +32236,6 @@ function createHttpClient(config2) {
32400
32236
  decisionNoteId: options.decisionNoteId
32401
32237
  });
32402
32238
  },
32403
- transitionProjectLifecycleState: async (projectId, targetState, options) => {
32404
- return request("POST", `/api/systems/${encodeURIComponent(projectId)}/lifecycle/transitions`, {
32405
- targetState,
32406
- reason: options.reason,
32407
- decisionNoteId: options.decisionNoteId
32408
- });
32409
- },
32410
32239
  // Beats
32411
32240
  getNextBeatId: async (projectId) => {
32412
32241
  const result = await request("POST", `/api/systems/${encodeURIComponent(projectId)}/beats/next-id`);
@@ -32424,7 +32253,7 @@ function createHttpClient(config2) {
32424
32253
  getBeat: async (beatId) => {
32425
32254
  const raw = await request("GET", `/api/beats/${encodeURIComponent(beatId)}`);
32426
32255
  if (!raw) return void 0;
32427
- return { ...raw, beatId: raw.beatId ?? raw.id, status: raw.status ?? "open", beatStatus: coerceBeatStatusLocal(raw.beatStatus) };
32256
+ return { ...raw, beatId: raw.beatId ?? raw.id, projectId: raw.projectId ?? raw.systemId, status: raw.status ?? "open", beatStatus: coerceBeatStatusLocal(raw.beatStatus) };
32428
32257
  },
32429
32258
  getBeatConvergence: async (projectId, beatId) => {
32430
32259
  const raw = await request(
@@ -32471,7 +32300,9 @@ function createHttpClient(config2) {
32471
32300
  }
32472
32301
  const beat = await request("GET", `/api/beats/${encodeURIComponent(beatId)}`);
32473
32302
  if (!beat) return void 0;
32474
- const result = await request("PUT", `/api/systems/${encodeURIComponent(beat.projectId)}/beats/${encodeURIComponent(beatId)}`, updates);
32303
+ const systemId = beat.systemId ?? beat.projectId;
32304
+ if (!systemId) return void 0;
32305
+ const result = await request("PUT", `/api/systems/${encodeURIComponent(systemId)}/beats/${encodeURIComponent(beatId)}`, updates);
32475
32306
  const raw = result?.beat ?? result;
32476
32307
  return { ...raw, beatStatus: coerceBeatStatusLocal(raw.beatStatus) };
32477
32308
  },
@@ -32584,22 +32415,6 @@ function createHttpClient(config2) {
32584
32415
  const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/notes${qs}`);
32585
32416
  return result?.notes ?? [];
32586
32417
  },
32587
- listProjectNotes: async (projectId, filters) => {
32588
- const params = new URLSearchParams();
32589
- if (filters?.noteType) {
32590
- const types = Array.isArray(filters.noteType) ? filters.noteType : [filters.noteType];
32591
- types.forEach((t) => params.append("type", t));
32592
- }
32593
- if (filters?.status) params.set("status", filters.status);
32594
- if (filters?.revisionId) params.set("revisionId", filters.revisionId);
32595
- if (filters?.beatVersionId) params.set("beatVersionId", filters.beatVersionId);
32596
- if (filters?.excludeChildren) params.set("excludeChildren", "true");
32597
- if (filters?.significance) params.set("significance", filters.significance);
32598
- if (filters?.limit !== void 0) params.set("limit", String(filters.limit));
32599
- const qs = params.toString() ? `?${params.toString()}` : "";
32600
- const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/notes${qs}`);
32601
- return result?.notes ?? [];
32602
- },
32603
32418
  listAllSystemNotes: async (projectId, filters, limit, cursor) => {
32604
32419
  const params = new URLSearchParams();
32605
32420
  if (filters?.noteType) {
@@ -32618,27 +32433,11 @@ function createHttpClient(config2) {
32618
32433
  const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/notes/all${qs}`);
32619
32434
  return { notes: result?.notes ?? [], cursor: result?.cursor, hasMore: result?.hasMore ?? false };
32620
32435
  },
32621
- listAllProjectNotes: async (projectId, filters, limit, cursor) => {
32622
- const params = new URLSearchParams();
32623
- if (filters?.noteType) {
32624
- const types = Array.isArray(filters.noteType) ? filters.noteType : [filters.noteType];
32625
- types.forEach((t) => params.append("type", t));
32626
- }
32627
- if (filters?.excludeNoteType) params.set("excludeType", filters.excludeNoteType);
32628
- if (filters?.status) params.set("status", filters.status);
32629
- if (filters?.revisionId) params.set("revisionId", filters.revisionId);
32630
- if (filters?.beatVersionId) params.set("beatVersionId", filters.beatVersionId);
32631
- if (filters?.excludeChildren) params.set("excludeChildren", "true");
32632
- if (filters?.significance) params.set("significance", filters.significance);
32633
- if (limit !== void 0) params.set("limit", String(limit));
32634
- if (cursor !== void 0) params.set("cursor", cursor);
32635
- const qs = params.toString() ? `?${params.toString()}` : "";
32636
- const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/notes/all${qs}`);
32637
- return { notes: result?.notes ?? [], cursor: result?.cursor, hasMore: result?.hasMore ?? false };
32638
- },
32639
32436
  listBeatNotes: async (beatId, filters) => {
32640
32437
  const beat = await request("GET", `/api/beats/${encodeURIComponent(beatId)}`);
32641
32438
  if (!beat) return [];
32439
+ const systemId = beat.systemId ?? beat.projectId;
32440
+ if (!systemId) return [];
32642
32441
  const params = new URLSearchParams();
32643
32442
  if (filters?.noteType) {
32644
32443
  const types = Array.isArray(filters.noteType) ? filters.noteType : [filters.noteType];
@@ -32650,7 +32449,7 @@ function createHttpClient(config2) {
32650
32449
  if (filters?.excludeChildren) params.set("excludeChildren", "true");
32651
32450
  if (filters?.significance) params.set("significance", filters.significance);
32652
32451
  const qs = params.toString() ? `?${params.toString()}` : "";
32653
- const result = await request("GET", `/api/systems/${encodeURIComponent(beat.projectId)}/beats/${encodeURIComponent(beatId)}/notes${qs}`);
32452
+ const result = await request("GET", `/api/systems/${encodeURIComponent(systemId)}/beats/${encodeURIComponent(beatId)}/notes${qs}`);
32654
32453
  return result?.notes ?? [];
32655
32454
  },
32656
32455
  getNote: async (noteId) => {
@@ -32728,7 +32527,6 @@ function createHttpClient(config2) {
32728
32527
  },
32729
32528
  // Gaps are deprecated — replaced by assumption Notes. Return empty results.
32730
32529
  getSystemGaps: async () => [],
32731
- getProjectGaps: async () => [],
32732
32530
  updateInformationGap: async () => false,
32733
32531
  // Revisions
32734
32532
  createRevision: async (projectId, beatId, input) => {
@@ -32753,14 +32551,6 @@ function createHttpClient(config2) {
32753
32551
  const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/revisions${qs}`);
32754
32552
  return result ?? [];
32755
32553
  },
32756
- listProjectRevisions: async (projectId, options) => {
32757
- const params = new URLSearchParams();
32758
- if (options?.status) params.set("status", options.status);
32759
- if (options?.includeArchived) params.set("includeArchived", "true");
32760
- const qs = params.toString() ? `?${params.toString()}` : "";
32761
- const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/revisions${qs}`);
32762
- return result ?? [];
32763
- },
32764
32554
  listBeatRevisions: async (projectId, beatId, options) => {
32765
32555
  const params = new URLSearchParams();
32766
32556
  params.set("beatId", beatId);
@@ -32982,17 +32772,9 @@ function createHttpClient(config2) {
32982
32772
  // partitions, but the REST surface is unified per the project-level
32983
32773
  // "avoid nested/duplicate routes" guidance.
32984
32774
  getSession: (sessionId) => request("GET", `/api/sessions/${encodeURIComponent(sessionId)}`),
32985
- listSystemSessions: async (projectId, options) => {
32775
+ listSystemSessions: async (systemId, options) => {
32986
32776
  const params = new URLSearchParams();
32987
- params.set("projectId", projectId);
32988
- if (options?.status) params.set("status", options.status);
32989
- if (options?.includeTerminal) params.set("includeTerminal", "true");
32990
- const result = await request("GET", `/api/sessions?${params.toString()}`);
32991
- return result ?? [];
32992
- },
32993
- listProjectSessions: async (projectId, options) => {
32994
- const params = new URLSearchParams();
32995
- params.set("projectId", projectId);
32777
+ params.set("systemId", systemId);
32996
32778
  if (options?.status) params.set("status", options.status);
32997
32779
  if (options?.includeTerminal) params.set("includeTerminal", "true");
32998
32780
  const result = await request("GET", `/api/sessions?${params.toString()}`);
@@ -33185,14 +32967,6 @@ function createHttpClient(config2) {
33185
32967
  const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/tasks${qs}`);
33186
32968
  return result?.tasks ?? [];
33187
32969
  },
33188
- listProjectTasks: async (projectId, options) => {
33189
- const params = new URLSearchParams();
33190
- if (options?.status) params.set("status", options.status);
33191
- if (options?.limit) params.set("limit", String(options.limit));
33192
- const qs = params.toString() ? `?${params.toString()}` : "";
33193
- const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/tasks${qs}`);
33194
- return result?.tasks ?? [];
33195
- },
33196
32970
  listTaskEvents: async (taskId, options) => {
33197
32971
  const params = new URLSearchParams();
33198
32972
  if (options?.afterSeq !== void 0) params.set("after", String(options.afterSeq));
@@ -33225,15 +32999,6 @@ function createHttpClient(config2) {
33225
32999
  const result = await request("GET", path);
33226
33000
  return { entries: result.activities ?? [], hasMore: false };
33227
33001
  },
33228
- listProjectActivities: async (projectId, options) => {
33229
- const params = new URLSearchParams();
33230
- if (options?.limit) params.set("limit", String(options.limit));
33231
- if (options?.minImportance) params.set("importance", options.minImportance);
33232
- const qs = params.toString();
33233
- const path = `/api/systems/${encodeURIComponent(projectId)}/activities${qs ? `?${qs}` : ""}`;
33234
- const result = await request("GET", path);
33235
- return { entries: result.activities ?? [], hasMore: false };
33236
- },
33237
33002
  createActivity: async (input) => {
33238
33003
  const result = await request("POST", `/api/systems/${encodeURIComponent(input.projectId)}/activities`, {
33239
33004
  action: input.action,
@@ -33308,20 +33073,6 @@ function createHttpClient(config2) {
33308
33073
  );
33309
33074
  return result.ranked ?? [];
33310
33075
  },
33311
- rankProjectBeatVersions: async (projectId, lens) => {
33312
- const result = await request(
33313
- "GET",
33314
- `/api/systems/${encodeURIComponent(projectId)}/beat-versions/ranked?lens=${encodeURIComponent(lens)}`
33315
- );
33316
- return result.ranked ?? [];
33317
- },
33318
- rankProjectBeatVersionsWithLeverage: async (projectId, lens) => {
33319
- const result = await request(
33320
- "GET",
33321
- `/api/systems/${encodeURIComponent(projectId)}/beat-versions/ranked?lens=${encodeURIComponent(lens)}&withLeverage=true`
33322
- );
33323
- return result.ranked ?? [];
33324
- },
33325
33076
  // All four use requestOrThrow, not request. These endpoints answer 404 for a
33326
33077
  // missing edge/Beat with a real domain message, and `request()` turns a 404
33327
33078
  // into `undefined` — so the old `result.edge` access threw
@@ -33574,7 +33325,7 @@ function createHttpClient(config2) {
33574
33325
  },
33575
33326
  searchByText: async (scope, query, options) => {
33576
33327
  const params = new URLSearchParams({ q: query });
33577
- if ("projectId" in scope) params.set("projectId", scope.projectId);
33328
+ if ("projectId" in scope) params.set("systemId", scope.projectId);
33578
33329
  else if ("teamspaceId" in scope) params.set("teamspaceId", scope.teamspaceId);
33579
33330
  else params.set("orgId", scope.orgId);
33580
33331
  if (options?.limit) params.set("limit", String(options.limit));
@@ -33620,18 +33371,6 @@ function createHttpClient(config2) {
33620
33371
  );
33621
33372
  return { checks: res?.checks ?? [], nextCursor: res?.nextCursor };
33622
33373
  },
33623
- listProjectChecks: async (projectId, checkType, options) => {
33624
- const params = new URLSearchParams();
33625
- if (checkType) params.set("type", checkType);
33626
- if (options?.limit !== void 0) params.set("limit", String(options.limit));
33627
- if (options?.cursor) params.set("cursor", options.cursor);
33628
- const qs = params.toString() ? `?${params.toString()}` : "";
33629
- const res = await request(
33630
- "GET",
33631
- `/api/systems/${encodeURIComponent(projectId)}/checks${qs}`
33632
- );
33633
- return { checks: res?.checks ?? [], nextCursor: res?.nextCursor };
33634
- },
33635
33374
  listLatestSystemChecks: async (projectId) => {
33636
33375
  const res = await request(
33637
33376
  "GET",
@@ -33639,13 +33378,6 @@ function createHttpClient(config2) {
33639
33378
  );
33640
33379
  return res?.checks ?? [];
33641
33380
  },
33642
- listLatestProjectChecks: async (projectId) => {
33643
- const res = await request(
33644
- "GET",
33645
- `/api/checks/latest?projectId=${encodeURIComponent(projectId)}`
33646
- );
33647
- return res?.checks ?? [];
33648
- },
33649
33381
  listBeatChecks: async (beatId, projectId, checkType) => {
33650
33382
  const qs = checkType ? `?type=${encodeURIComponent(checkType)}` : "";
33651
33383
  const res = await request(
@@ -33705,20 +33437,6 @@ function createHttpClient(config2) {
33705
33437
  updateTeamspace: async (teamspaceId, updates) => {
33706
33438
  return request("PATCH", `/api/teamspaces/${encodeURIComponent(teamspaceId)}`, updates);
33707
33439
  },
33708
- // Deliverables
33709
- listTeamspaceDeliverables: async (teamspaceId) => {
33710
- const res = await request("GET", `/api/teamspaces/${encodeURIComponent(teamspaceId)}/deliverables`);
33711
- return res?.deliverables ?? [];
33712
- },
33713
- getDeliverable: async (deliverableId) => {
33714
- return request("GET", `/api/deliverables/${encodeURIComponent(deliverableId)}`);
33715
- },
33716
- createDeliverable: async (input) => {
33717
- return request("POST", `/api/teamspaces/${encodeURIComponent(input.teamspaceId)}/deliverables`, input);
33718
- },
33719
- updateDeliverable: async (deliverableId, updates) => {
33720
- return request("PATCH", `/api/deliverables/${encodeURIComponent(deliverableId)}`, updates);
33721
- },
33722
33440
  // DeliverableGroups
33723
33441
  listTeamspaceDeliverableGroups: async (teamspaceId) => {
33724
33442
  const res = await request("GET", `/api/teamspaces/${encodeURIComponent(teamspaceId)}/deliverable-groups`);
@@ -33730,11 +33448,6 @@ function createHttpClient(config2) {
33730
33448
  if (res === void 0) throw new Error(`Project ${projectId} not found`);
33731
33449
  return res.deliverableGroups;
33732
33450
  },
33733
- listProjectDeliverableGroups: async (projectId) => {
33734
- const res = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/deliverable-groups`);
33735
- if (res === void 0) throw new Error(`Project ${projectId} not found`);
33736
- return res.deliverableGroups;
33737
- },
33738
33451
  listMovementDeliverableGroups: async (movementId) => {
33739
33452
  const res = await request("GET", `/api/movements/${encodeURIComponent(movementId)}/deliverable-groups`);
33740
33453
  if (res === void 0) throw new Error(`Movement ${movementId} not found`);
@@ -33771,10 +33484,6 @@ function createHttpClient(config2) {
33771
33484
  ...opts ?? {}
33772
33485
  });
33773
33486
  },
33774
- listDeliverableGroupDeliverables: async (deliverableGroupId) => {
33775
- const res = await request("GET", `/api/deliverable-groups/${encodeURIComponent(deliverableGroupId)}/deliverables`);
33776
- return res?.deliverables ?? [];
33777
- },
33778
33487
  listDeliverableGroupDrops: async (deliverableGroupId) => {
33779
33488
  const res = await request("GET", `/api/deliverable-groups/${encodeURIComponent(deliverableGroupId)}/drops`);
33780
33489
  return res?.drops ?? [];
@@ -34354,28 +34063,54 @@ function createHttpClient(config2) {
34354
34063
  );
34355
34064
  return result?.usedBy;
34356
34065
  },
34357
- // Feature flags (B-135 v9/v10) — not available in HTTP transport; the published
34358
- // npm package has no DynamoDB credentials.
34359
- listFlags: async () => {
34360
- throw new Error("Feature flag queries are not available in HTTP transport mode");
34066
+ // Feature flags (B-135 v11) — routed through /api/mcp/feature-flags/* so the
34067
+ // published npm package (no DynamoDB credentials) can manage flags via HTTP.
34068
+ listFlags: async (options) => {
34069
+ const params = new URLSearchParams();
34070
+ if (options?.includeArchived) params.set("includeArchived", "true");
34071
+ if (options?.search) params.set("search", options.search);
34072
+ if (options?.cursor) params.set("cursor", options.cursor);
34073
+ if (options?.limit !== void 0) params.set("limit", String(options.limit));
34074
+ const qs = params.toString();
34075
+ return requestOrThrow(
34076
+ "GET",
34077
+ `/api/mcp/feature-flags${qs ? `?${qs}` : ""}`
34078
+ );
34361
34079
  },
34362
- getFlag: async () => {
34363
- throw new Error("Feature flag queries are not available in HTTP transport mode");
34080
+ getFlag: async (name, options) => {
34081
+ const qs = options?.includeHistory ? "?includeHistory=true" : "";
34082
+ return request("GET", `/api/mcp/feature-flags/${encodeURIComponent(name)}${qs}`);
34364
34083
  },
34365
- checkFlag: async () => {
34366
- throw new Error("Feature flag queries are not available in HTTP transport mode");
34084
+ checkFlag: async (name, stage) => {
34085
+ const flag = await request(
34086
+ "GET",
34087
+ `/api/mcp/feature-flags/${encodeURIComponent(name)}`
34088
+ );
34089
+ return flag ? flag.enabledIn.includes(stage) : false;
34367
34090
  },
34368
- createFlag: async () => {
34369
- throw new Error("Feature flag writes are not available in HTTP transport mode");
34091
+ createFlag: async (name, enabledIn, options) => {
34092
+ return requestOrThrow("POST", "/api/mcp/feature-flags", {
34093
+ name,
34094
+ enabledIn,
34095
+ description: options?.description,
34096
+ note: options?.note
34097
+ });
34370
34098
  },
34371
- setFlag: async () => {
34372
- throw new Error("Feature flag writes are not available in HTTP transport mode");
34099
+ setFlag: async (name, enabledIn, options) => {
34100
+ return requestOrThrow("PUT", `/api/mcp/feature-flags/${encodeURIComponent(name)}`, {
34101
+ enabledIn,
34102
+ note: options?.note
34103
+ });
34373
34104
  },
34374
- archiveFlag: async () => {
34375
- throw new Error("Feature flag writes are not available in HTTP transport mode");
34105
+ archiveFlag: async (name, options) => {
34106
+ return requestOrThrow("POST", `/api/mcp/feature-flags/${encodeURIComponent(name)}/archive`, {
34107
+ note: options?.note
34108
+ });
34376
34109
  },
34377
- unarchiveFlag: async () => {
34378
- throw new Error("Feature flag writes are not available in HTTP transport mode");
34110
+ unarchiveFlag: async (name, options) => {
34111
+ return requestOrThrow("POST", `/api/mcp/feature-flags/${encodeURIComponent(name)}/unarchive`, {
34112
+ note: options?.note
34113
+ });
34379
34114
  }
34380
34115
  };
34381
34116
  return client;
@@ -34426,7 +34161,7 @@ function loadConfig() {
34426
34161
  };
34427
34162
  }
34428
34163
  async function main() {
34429
- console.error(`[harmonica-mcp] v${"3.5.0"} starting\u2026`);
34164
+ console.error(`[harmonica-mcp] v${"3.6.1"} starting\u2026`);
34430
34165
  const config2 = loadConfig();
34431
34166
  const client = createHttpClient({
34432
34167
  apiBaseUrl: config2.apiBaseUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codazen/harmonica-mcp",
3
- "version": "3.5.0",
3
+ "version": "3.6.1",
4
4
  "description": "MCP server for Harmonica — connect any MCP-compatible AI assistant to Harmonica",
5
5
  "license": "MIT",
6
6
  "bin": {