@codazen/harmonica-mcp 3.4.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +179 -352
  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
  });
@@ -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) {
@@ -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);
@@ -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(", ")}`;
@@ -25241,7 +25139,7 @@ var STAGE_ENUM = external_exports.enum([
25241
25139
  "beta",
25242
25140
  "prod"
25243
25141
  ]);
25244
- function registerFeatureFlagTools(server, _ctx, client) {
25142
+ function registerFeatureFlagTools(server, ctx, client) {
25245
25143
  server.tool(
25246
25144
  "list_flags",
25247
25145
  "List feature flags and their enabled stages. Use check_flag to test a single flag against a specific stage, or get_flag for full detail on one flag.",
@@ -25307,6 +25205,89 @@ function registerFeatureFlagTools(server, _ctx, client) {
25307
25205
  }
25308
25206
  }
25309
25207
  );
25208
+ server.tool(
25209
+ "create_flag",
25210
+ "Create or update a feature flag with the specified enabled stages. Idempotent \u2014 calling with an existing flag name replaces its enabledIn list.",
25211
+ {
25212
+ name: external_exports.string().min(1).describe('Flag name (e.g. "rbac:enforcement")'),
25213
+ enabledIn: external_exports.array(STAGE_ENUM).describe("Stages to enable the flag in"),
25214
+ description: external_exports.string().optional().describe("Human-readable description of what this flag gates"),
25215
+ note: external_exports.string().optional().describe("Reason for this change (recorded in change history)")
25216
+ },
25217
+ async ({ name, enabledIn, description, note }) => {
25218
+ if (!ctx.user.email) {
25219
+ return { content: [{ type: "text", text: "Write operations require an authenticated user with a valid email." }], isError: true };
25220
+ }
25221
+ try {
25222
+ const flag = await client.createFlag(name, enabledIn, { description, note, updatedBy: ctx.user.email });
25223
+ return { content: [{ type: "text", text: formatFlag(flag) }] };
25224
+ } catch (err) {
25225
+ const message = err instanceof Error ? err.message : String(err);
25226
+ return { content: [{ type: "text", text: `Failed to create flag: ${message}` }], isError: true };
25227
+ }
25228
+ }
25229
+ );
25230
+ server.tool(
25231
+ "set_flag",
25232
+ "Update which stages a feature flag is enabled in. Replaces the current enabledIn list entirely.",
25233
+ {
25234
+ name: external_exports.string().min(1).describe("Flag name"),
25235
+ enabledIn: external_exports.array(STAGE_ENUM).describe("New set of stages to enable the flag in. Pass [] to disable everywhere without archiving."),
25236
+ note: external_exports.string().optional().describe("Reason for this change (recorded in change history)")
25237
+ },
25238
+ async ({ name, enabledIn, note }) => {
25239
+ if (!ctx.user.email) {
25240
+ return { content: [{ type: "text", text: "Write operations require an authenticated user with a valid email." }], isError: true };
25241
+ }
25242
+ try {
25243
+ const flag = await client.setFlag(name, enabledIn, { note, updatedBy: ctx.user.email });
25244
+ return { content: [{ type: "text", text: formatFlag(flag) }] };
25245
+ } catch (err) {
25246
+ const message = err instanceof Error ? err.message : String(err);
25247
+ return { content: [{ type: "text", text: `Failed to set flag: ${message}` }], isError: true };
25248
+ }
25249
+ }
25250
+ );
25251
+ server.tool(
25252
+ "archive_flag",
25253
+ "Archive a feature flag. Clears its enabledIn list and marks it archived so it cannot be re-enabled via set_flag. Use unarchive_flag to restore it.",
25254
+ {
25255
+ name: external_exports.string().min(1).describe("Flag name"),
25256
+ note: external_exports.string().optional().describe("Reason for archiving (recorded in change history)")
25257
+ },
25258
+ async ({ name, note }) => {
25259
+ if (!ctx.user.email) {
25260
+ return { content: [{ type: "text", text: "Write operations require an authenticated user with a valid email." }], isError: true };
25261
+ }
25262
+ try {
25263
+ const flag = await client.archiveFlag(name, { note, updatedBy: ctx.user.email });
25264
+ return { content: [{ type: "text", text: formatFlag(flag) }] };
25265
+ } catch (err) {
25266
+ const message = err instanceof Error ? err.message : String(err);
25267
+ return { content: [{ type: "text", text: `Failed to archive flag: ${message}` }], isError: true };
25268
+ }
25269
+ }
25270
+ );
25271
+ server.tool(
25272
+ "unarchive_flag",
25273
+ "Restore an archived feature flag to operable state. The flag is re-enabled with an empty enabledIn list \u2014 use set_flag afterward to enable it in specific stages.",
25274
+ {
25275
+ name: external_exports.string().min(1).describe("Flag name"),
25276
+ note: external_exports.string().optional().describe("Reason for restoring (recorded in change history)")
25277
+ },
25278
+ async ({ name, note }) => {
25279
+ if (!ctx.user.email) {
25280
+ return { content: [{ type: "text", text: "Write operations require an authenticated user with a valid email." }], isError: true };
25281
+ }
25282
+ try {
25283
+ const flag = await client.unarchiveFlag(name, { note, updatedBy: ctx.user.email });
25284
+ return { content: [{ type: "text", text: formatFlag(flag) }] };
25285
+ } catch (err) {
25286
+ const message = err instanceof Error ? err.message : String(err);
25287
+ return { content: [{ type: "text", text: `Failed to unarchive flag: ${message}` }], isError: true };
25288
+ }
25289
+ }
25290
+ );
25310
25291
  }
25311
25292
  function formatFlag(flag, history) {
25312
25293
  const archived = flag.archivedAt ? " **(archived)**" : "";
@@ -25316,7 +25297,8 @@ function formatFlag(flag, history) {
25316
25297
  `**Scope:** ${flag.scope}`,
25317
25298
  `**Enabled in:** ${enabled}`,
25318
25299
  flag.description ? `**Description:** ${flag.description}` : null,
25319
- `**Last updated:** ${flag.updatedAt.slice(0, 10)} by ${flag.updatedBy}`
25300
+ `**Last updated:** ${flag.updatedAt.slice(0, 10)} by ${flag.updatedBy}`,
25301
+ flag.archivedAt ? `**Archived:** ${flag.archivedAt.slice(0, 10)} by ${flag.archivedBy}` : null
25320
25302
  ].filter(Boolean).join("\n");
25321
25303
  if (!history?.length) return parts;
25322
25304
  const historyLines = history.map((h) => {
@@ -28429,7 +28411,7 @@ ${lines.join("\n")}` }] };
28429
28411
  }
28430
28412
 
28431
28413
  // ../../libs/harmonica-services/src/mcp/tools/onboarding-tools.ts
28432
- var import_node_crypto2 = require("node:crypto");
28414
+ var import_node_crypto = require("node:crypto");
28433
28415
  var OnboardingNoteInputSchema = external_exports.object({
28434
28416
  noteType: external_exports.enum(["context", "assumption", "constraint", "guidance", "decision"]),
28435
28417
  content: external_exports.string().min(1),
@@ -28441,11 +28423,6 @@ var OnboardingProjectInputSchema = external_exports.object({
28441
28423
  description: external_exports.string().max(2e3).optional(),
28442
28424
  notes: external_exports.array(OnboardingNoteInputSchema).optional()
28443
28425
  });
28444
- var OnboardingDeliverableInputSchema = external_exports.object({
28445
- title: external_exports.string().min(1).max(300),
28446
- description: external_exports.string().max(2e3).optional(),
28447
- dueDate: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
28448
- });
28449
28426
  function registerOnboardingTools(server, ctx, client) {
28450
28427
  server.tool(
28451
28428
  "initiate_teamspace_onboarding",
@@ -28543,7 +28520,7 @@ function registerOnboardingTools(server, ctx, client) {
28543
28520
  server.tool(
28544
28521
  "create_teamspace_onboarding_batch",
28545
28522
  [
28546
- "Create a Teamspace, Projects, Notes, and Deliverables",
28523
+ "Create a Teamspace, Projects, and Notes",
28547
28524
  "from a PM-approved onboarding payload in a single batch operation.",
28548
28525
  "Call this after the PM approves the pre-flight review card from initiate_teamspace_onboarding.",
28549
28526
  "Returns a creation receipt with IDs and counts for all created entities.",
@@ -28552,20 +28529,18 @@ function registerOnboardingTools(server, ctx, client) {
28552
28529
  {
28553
28530
  teamspaceName: external_exports.string().min(1).max(200).describe("Teamspace display name"),
28554
28531
  projects: external_exports.array(OnboardingProjectInputSchema).min(1).describe("Phase 1 projects to create, each linked to the Teamspace"),
28555
- teamspaceNotes: external_exports.array(OnboardingNoteInputSchema).describe("Constraints, assumptions, and context notes at the Teamspace/project level"),
28556
- 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")
28557
28533
  },
28558
28534
  async ({
28559
28535
  teamspaceName,
28560
28536
  projects,
28561
- teamspaceNotes,
28562
- deliverables
28537
+ teamspaceNotes
28563
28538
  }) => {
28564
28539
  const lines = [];
28565
28540
  let teamspace;
28566
28541
  try {
28567
28542
  teamspace = await client.createTeamspace({
28568
- teamspaceId: (0, import_node_crypto2.randomUUID)(),
28543
+ teamspaceId: (0, import_node_crypto.randomUUID)(),
28569
28544
  slug: teamspaceName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, ""),
28570
28545
  orgId: ctx.orgId,
28571
28546
  name: teamspaceName,
@@ -28582,8 +28557,8 @@ Error: ${msg}`);
28582
28557
  const createdProjects = [];
28583
28558
  for (const p of projects) {
28584
28559
  try {
28585
- const project = await client.createProject({
28586
- projectId: (0, import_node_crypto2.randomUUID)(),
28560
+ const project = await client.createSystem({
28561
+ projectId: (0, import_node_crypto.randomUUID)(),
28587
28562
  orgId: ctx.orgId,
28588
28563
  teamspaceId: teamspace.teamspaceId,
28589
28564
  ownerUserId: ctx.user?.userId ?? "system",
@@ -28627,7 +28602,7 @@ Projects (${createdProjects.length}):`);
28627
28602
  for (const note of projects[i]?.notes ?? []) {
28628
28603
  try {
28629
28604
  const n = await client.createNote({
28630
- noteId: `N-TEMP-${(0, import_node_crypto2.randomUUID)()}`,
28605
+ noteId: `N-TEMP-${(0, import_node_crypto.randomUUID)()}`,
28631
28606
  // overridden server-side
28632
28607
  projectId: project.projectId,
28633
28608
  noteType: note.noteType,
@@ -28644,24 +28619,6 @@ Projects (${createdProjects.length}):`);
28644
28619
  }
28645
28620
  lines.push(`
28646
28621
  Notes filed: ${createdNotes.length}`);
28647
- const createdDeliverables = [];
28648
- for (const d of deliverables) {
28649
- try {
28650
- const del = await client.createDeliverable({
28651
- deliverableId: (0, import_node_crypto2.randomUUID)(),
28652
- // overridden server-side
28653
- teamspaceId: teamspace.teamspaceId,
28654
- orgId: ctx.orgId,
28655
- title: d.title,
28656
- description: d.description,
28657
- dueDate: d.dueDate,
28658
- createdBy: ctx.user?.userId ?? "mcp"
28659
- });
28660
- createdDeliverables.push(del.deliverableId);
28661
- } catch {
28662
- }
28663
- }
28664
- lines.push(`Deliverables: ${createdDeliverables.length}`);
28665
28622
  lines.unshift(`\u2705 Onboarding batch created successfully.`);
28666
28623
  return { content: [{ type: "text", text: lines.join("\n") }] };
28667
28624
  }
@@ -29967,7 +29924,7 @@ function registerSessionTools(server, ctx, client) {
29967
29924
  includeTerminal: external_exports.boolean().optional().describe("Include terminal (closed) sessions. Default false.")
29968
29925
  },
29969
29926
  async ({ systemId, status, includeTerminal }) => {
29970
- const sessions = await client.listProjectSessions(systemId, {
29927
+ const sessions = await client.listSystemSessions(systemId, {
29971
29928
  status,
29972
29929
  includeTerminal
29973
29930
  });
@@ -30634,7 +30591,7 @@ function registerProjectTools(server, ctx, client) {
30634
30591
  fetchProjectInOrg(client, systemId, ctx.orgId),
30635
30592
  client.getOrg(ctx.orgId)
30636
30593
  ]);
30637
- const notes = await client.listProjectNotes(systemId, {
30594
+ const notes = await client.listSystemNotes(systemId, {
30638
30595
  limit: noteLimit ?? DEFAULT_CONTEXT_NOTE_LIMIT
30639
30596
  });
30640
30597
  const text = formatProjectContext(project, notes, org?.coda);
@@ -30802,7 +30759,7 @@ function registerProjectTools(server, ctx, client) {
30802
30759
  }
30803
30760
 
30804
30761
  // ../../libs/harmonica-services/src/mcp/tools/teamspace-tools.ts
30805
- var import_node_crypto3 = require("node:crypto");
30762
+ var import_node_crypto2 = require("node:crypto");
30806
30763
  var EnsembleMemberSchema = external_exports.discriminatedUnion("type", [
30807
30764
  external_exports.object({ type: external_exports.literal("human"), email: external_exports.string().email().max(254), name: external_exports.string().min(1).max(200) }),
30808
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) })
@@ -30841,7 +30798,7 @@ function registerTeamspaceTools(server, ctx, client) {
30841
30798
  if (!ts || ts.orgId !== ctx.orgId) {
30842
30799
  return { content: [{ type: "text", text: `Teamspace not found: ${teamspaceId}` }], isError: true };
30843
30800
  }
30844
- const allProjects = await client.listOrgProjects(ctx.orgId);
30801
+ const allProjects = await client.listOrgSystems(ctx.orgId);
30845
30802
  const tsProjects = allProjects.filter((p) => p.teamspaceId === teamspaceId);
30846
30803
  const lines = [
30847
30804
  `ID: ${ts.teamspaceId}`,
@@ -30878,7 +30835,7 @@ function registerTeamspaceTools(server, ctx, client) {
30878
30835
  name: external_exports.string().min(1).max(200).describe("Teamspace name")
30879
30836
  },
30880
30837
  async ({ name }) => {
30881
- const teamspaceId = `ts-${(0, import_node_crypto3.randomUUID)()}`;
30838
+ const teamspaceId = `ts-${(0, import_node_crypto2.randomUUID)()}`;
30882
30839
  const slug = generateTeamspaceSlug(name);
30883
30840
  const ts = await client.createTeamspace({
30884
30841
  teamspaceId,
@@ -31016,7 +30973,7 @@ function registerValueVelocityTools(server, ctx, client) {
31016
30973
  try {
31017
30974
  await assertProjectInOrg(client, systemId, ctx.orgId);
31018
30975
  const [rankedResult, staleWResult] = await Promise.allSettled([
31019
- withLeverage ? client.rankProjectBeatVersionsWithLeverage(systemId, lens) : client.rankProjectBeatVersions(systemId, lens),
30976
+ withLeverage ? client.rankSystemBeatVersionsWithLeverage(systemId, lens) : client.rankSystemBeatVersions(systemId, lens),
31020
30977
  client.getStaleWBeatIds(systemId)
31021
30978
  ]);
31022
30979
  if (rankedResult.status === "rejected") {
@@ -31510,7 +31467,7 @@ ${JSON.stringify(task.result, null, 2)}
31510
31467
  },
31511
31468
  async ({ projectId, status, limit }) => {
31512
31469
  await assertProjectInOrg(client, projectId, ctx.orgId);
31513
- const tasks = await client.listProjectTasks(projectId, {
31470
+ const tasks = await client.listSystemTasks(projectId, {
31514
31471
  status,
31515
31472
  limit
31516
31473
  });
@@ -31568,7 +31525,7 @@ ${JSON.stringify(task.result, null, 2)}
31568
31525
  },
31569
31526
  async ({ projectId, status, limit }) => {
31570
31527
  await assertProjectInOrg(client, projectId, ctx.orgId);
31571
- const tasks = await client.listProjectTasks(projectId, {
31528
+ const tasks = await client.listSystemTasks(projectId, {
31572
31529
  status,
31573
31530
  limit
31574
31531
  });
@@ -31870,7 +31827,6 @@ var TOOL_PROFILES = Object.freeze({
31870
31827
  allow(registerBeatVersionTools, ["list_beat_versions", "get_beat_version"]),
31871
31828
  allow(registerNoteTools, ["list_notes", "get_note", "create_note", "list_documents"]),
31872
31829
  allow(registerDropTools, ["list_drops", "get_drop"]),
31873
- allow(registerDeliverableTools, ["list_deliverables", "get_deliverable"]),
31874
31830
  allow(registerEmbeddingTools, ["search", "find_similar_notes"])
31875
31831
  ])
31876
31832
  });
@@ -31923,7 +31879,6 @@ function registerAllTools(server, ctx, client, profile) {
31923
31879
  registerBaselineTools(server, ctx, client);
31924
31880
  registerTeamspaceTools(server, ctx, client);
31925
31881
  registerDeliverableGroupTools(server, ctx, client);
31926
- registerDeliverableTools(server, ctx, client);
31927
31882
  registerDropTools(server, ctx, client);
31928
31883
  registerSessionTools(server, ctx, client);
31929
31884
  registerOnboardingTools(server, ctx, client);
@@ -31942,12 +31897,12 @@ function registerAllTools(server, ctx, client, profile) {
31942
31897
  // ../../libs/harmonica-services/src/mcp/resources/beat-resources.ts
31943
31898
  function registerBeatResources(server, ctx, client) {
31944
31899
  server.resource(
31945
- "project-beats",
31946
- new ResourceTemplate("harmonica://project/{projectId}/beats", { list: void 0 }),
31947
- async (uri, { projectId }) => {
31948
- const pid = String(projectId);
31949
- await assertProjectInOrg(client, pid, ctx.orgId);
31950
- 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);
31951
31906
  const text = formatBeatSummaryTable(beats);
31952
31907
  return { contents: [{ uri: uri.href, text }] };
31953
31908
  }
@@ -31982,14 +31937,14 @@ function registerGuidelinesResources(server, client) {
31982
31937
  }
31983
31938
 
31984
31939
  // ../../libs/harmonica-services/src/mcp/resources/system-resources.ts
31985
- function registerProjectResources(server, ctx, client) {
31940
+ function registerSystemResources(server, ctx, client) {
31986
31941
  server.resource(
31987
- "project-context",
31988
- new ResourceTemplate("harmonica://project/{projectId}/context", { list: void 0 }),
31989
- async (uri, { projectId }) => {
31990
- const pid = String(projectId);
31991
- const project = await fetchProjectInOrg(client, pid, ctx.orgId);
31992
- 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 });
31993
31948
  const text = formatProjectContext(project, notes);
31994
31949
  return { contents: [{ uri: uri.href, text }] };
31995
31950
  }
@@ -31998,7 +31953,7 @@ function registerProjectResources(server, ctx, client) {
31998
31953
 
31999
31954
  // ../../libs/harmonica-services/src/mcp/resources/index.ts
32000
31955
  function registerAllResources(server, ctx, client) {
32001
- registerProjectResources(server, ctx, client);
31956
+ registerSystemResources(server, ctx, client);
32002
31957
  registerBeatResources(server, ctx, client);
32003
31958
  registerGuidelinesResources(server, client);
32004
31959
  }
@@ -32269,46 +32224,11 @@ function createHttpClient(config2) {
32269
32224
  if (!raw) return void 0;
32270
32225
  return { ...raw, projectId: raw.id ?? raw.projectId, title: raw.name ?? raw.title, orgId: raw.organizationId ?? raw.orgId };
32271
32226
  },
32272
- // @deprecated aliases — remove in cleanup increment (BV2)
32273
- listOrgProjects: async (orgId) => {
32274
- const raw = await request("GET", `/api/organizations/${encodeURIComponent(orgId)}/projects/summaries`);
32275
- return (raw ?? []).map((p) => ({ ...p, projectId: p.id, title: p.name, orgId: p.organizationId }));
32276
- },
32277
- getProject: async (projectId) => {
32278
- const raw = await request("GET", `/api/systems/${encodeURIComponent(projectId)}`);
32279
- if (!raw) return void 0;
32280
- return {
32281
- ...raw,
32282
- projectId: raw.id ?? raw.projectId,
32283
- title: raw.name ?? raw.title,
32284
- orgId: raw.organizationId ?? raw.orgId
32285
- };
32286
- },
32287
- createProject: async (input) => {
32288
- const raw = await request("POST", "/api/systems", {
32289
- organizationId: input.orgId,
32290
- teamspaceId: input.teamspaceId,
32291
- name: input.title,
32292
- description: input.description,
32293
- strategy: input.strategy,
32294
- // Without this the owning Account is dropped on the wire and the System
32295
- // is created unlinked — invisible in the Account's Systems list.
32296
- ...input.accountId !== void 0 && { accountId: input.accountId }
32297
- });
32298
- return { ...raw, orgId: raw?.organizationId ?? raw?.orgId };
32299
- },
32300
- updateProject: (projectId, updates) => request("PATCH", `/api/systems/${encodeURIComponent(projectId)}`, updates),
32301
- archiveProject: (projectId) => request("PATCH", `/api/systems/${encodeURIComponent(projectId)}/archive`),
32302
32227
  getSystemFact: async (projectId) => {
32303
32228
  const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/facts`);
32304
32229
  return result?.data;
32305
32230
  },
32306
- getProjectFact: async (projectId) => {
32307
- const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/facts`);
32308
- return result?.data;
32309
- },
32310
32231
  saveSystemFact: (input) => request("PUT", `/api/systems/${encodeURIComponent(String(input.projectId))}/facts`, input),
32311
- saveProjectFact: (input) => request("PUT", `/api/systems/${encodeURIComponent(String(input.projectId))}/facts`, input),
32312
32232
  transitionSystemLifecycleState: async (projectId, targetState, options) => {
32313
32233
  return request("POST", `/api/systems/${encodeURIComponent(projectId)}/lifecycle/transitions`, {
32314
32234
  targetState,
@@ -32316,13 +32236,6 @@ function createHttpClient(config2) {
32316
32236
  decisionNoteId: options.decisionNoteId
32317
32237
  });
32318
32238
  },
32319
- transitionProjectLifecycleState: async (projectId, targetState, options) => {
32320
- return request("POST", `/api/systems/${encodeURIComponent(projectId)}/lifecycle/transitions`, {
32321
- targetState,
32322
- reason: options.reason,
32323
- decisionNoteId: options.decisionNoteId
32324
- });
32325
- },
32326
32239
  // Beats
32327
32240
  getNextBeatId: async (projectId) => {
32328
32241
  const result = await request("POST", `/api/systems/${encodeURIComponent(projectId)}/beats/next-id`);
@@ -32500,22 +32413,6 @@ function createHttpClient(config2) {
32500
32413
  const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/notes${qs}`);
32501
32414
  return result?.notes ?? [];
32502
32415
  },
32503
- listProjectNotes: async (projectId, filters) => {
32504
- const params = new URLSearchParams();
32505
- if (filters?.noteType) {
32506
- const types = Array.isArray(filters.noteType) ? filters.noteType : [filters.noteType];
32507
- types.forEach((t) => params.append("type", t));
32508
- }
32509
- if (filters?.status) params.set("status", filters.status);
32510
- if (filters?.revisionId) params.set("revisionId", filters.revisionId);
32511
- if (filters?.beatVersionId) params.set("beatVersionId", filters.beatVersionId);
32512
- if (filters?.excludeChildren) params.set("excludeChildren", "true");
32513
- if (filters?.significance) params.set("significance", filters.significance);
32514
- if (filters?.limit !== void 0) params.set("limit", String(filters.limit));
32515
- const qs = params.toString() ? `?${params.toString()}` : "";
32516
- const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/notes${qs}`);
32517
- return result?.notes ?? [];
32518
- },
32519
32416
  listAllSystemNotes: async (projectId, filters, limit, cursor) => {
32520
32417
  const params = new URLSearchParams();
32521
32418
  if (filters?.noteType) {
@@ -32534,24 +32431,6 @@ function createHttpClient(config2) {
32534
32431
  const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/notes/all${qs}`);
32535
32432
  return { notes: result?.notes ?? [], cursor: result?.cursor, hasMore: result?.hasMore ?? false };
32536
32433
  },
32537
- listAllProjectNotes: async (projectId, filters, limit, cursor) => {
32538
- const params = new URLSearchParams();
32539
- if (filters?.noteType) {
32540
- const types = Array.isArray(filters.noteType) ? filters.noteType : [filters.noteType];
32541
- types.forEach((t) => params.append("type", t));
32542
- }
32543
- if (filters?.excludeNoteType) params.set("excludeType", filters.excludeNoteType);
32544
- if (filters?.status) params.set("status", filters.status);
32545
- if (filters?.revisionId) params.set("revisionId", filters.revisionId);
32546
- if (filters?.beatVersionId) params.set("beatVersionId", filters.beatVersionId);
32547
- if (filters?.excludeChildren) params.set("excludeChildren", "true");
32548
- if (filters?.significance) params.set("significance", filters.significance);
32549
- if (limit !== void 0) params.set("limit", String(limit));
32550
- if (cursor !== void 0) params.set("cursor", cursor);
32551
- const qs = params.toString() ? `?${params.toString()}` : "";
32552
- const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/notes/all${qs}`);
32553
- return { notes: result?.notes ?? [], cursor: result?.cursor, hasMore: result?.hasMore ?? false };
32554
- },
32555
32434
  listBeatNotes: async (beatId, filters) => {
32556
32435
  const beat = await request("GET", `/api/beats/${encodeURIComponent(beatId)}`);
32557
32436
  if (!beat) return [];
@@ -32644,7 +32523,6 @@ function createHttpClient(config2) {
32644
32523
  },
32645
32524
  // Gaps are deprecated — replaced by assumption Notes. Return empty results.
32646
32525
  getSystemGaps: async () => [],
32647
- getProjectGaps: async () => [],
32648
32526
  updateInformationGap: async () => false,
32649
32527
  // Revisions
32650
32528
  createRevision: async (projectId, beatId, input) => {
@@ -32669,14 +32547,6 @@ function createHttpClient(config2) {
32669
32547
  const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/revisions${qs}`);
32670
32548
  return result ?? [];
32671
32549
  },
32672
- listProjectRevisions: async (projectId, options) => {
32673
- const params = new URLSearchParams();
32674
- if (options?.status) params.set("status", options.status);
32675
- if (options?.includeArchived) params.set("includeArchived", "true");
32676
- const qs = params.toString() ? `?${params.toString()}` : "";
32677
- const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/revisions${qs}`);
32678
- return result ?? [];
32679
- },
32680
32550
  listBeatRevisions: async (projectId, beatId, options) => {
32681
32551
  const params = new URLSearchParams();
32682
32552
  params.set("beatId", beatId);
@@ -32906,14 +32776,6 @@ function createHttpClient(config2) {
32906
32776
  const result = await request("GET", `/api/sessions?${params.toString()}`);
32907
32777
  return result ?? [];
32908
32778
  },
32909
- listProjectSessions: async (projectId, options) => {
32910
- const params = new URLSearchParams();
32911
- params.set("projectId", projectId);
32912
- if (options?.status) params.set("status", options.status);
32913
- if (options?.includeTerminal) params.set("includeTerminal", "true");
32914
- const result = await request("GET", `/api/sessions?${params.toString()}`);
32915
- return result ?? [];
32916
- },
32917
32779
  listBeatSessions: async (beatId, options) => {
32918
32780
  const params = new URLSearchParams();
32919
32781
  params.set("beatId", beatId);
@@ -33101,14 +32963,6 @@ function createHttpClient(config2) {
33101
32963
  const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/tasks${qs}`);
33102
32964
  return result?.tasks ?? [];
33103
32965
  },
33104
- listProjectTasks: async (projectId, options) => {
33105
- const params = new URLSearchParams();
33106
- if (options?.status) params.set("status", options.status);
33107
- if (options?.limit) params.set("limit", String(options.limit));
33108
- const qs = params.toString() ? `?${params.toString()}` : "";
33109
- const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/tasks${qs}`);
33110
- return result?.tasks ?? [];
33111
- },
33112
32966
  listTaskEvents: async (taskId, options) => {
33113
32967
  const params = new URLSearchParams();
33114
32968
  if (options?.afterSeq !== void 0) params.set("after", String(options.afterSeq));
@@ -33141,15 +32995,6 @@ function createHttpClient(config2) {
33141
32995
  const result = await request("GET", path);
33142
32996
  return { entries: result.activities ?? [], hasMore: false };
33143
32997
  },
33144
- listProjectActivities: async (projectId, options) => {
33145
- const params = new URLSearchParams();
33146
- if (options?.limit) params.set("limit", String(options.limit));
33147
- if (options?.minImportance) params.set("importance", options.minImportance);
33148
- const qs = params.toString();
33149
- const path = `/api/systems/${encodeURIComponent(projectId)}/activities${qs ? `?${qs}` : ""}`;
33150
- const result = await request("GET", path);
33151
- return { entries: result.activities ?? [], hasMore: false };
33152
- },
33153
32998
  createActivity: async (input) => {
33154
32999
  const result = await request("POST", `/api/systems/${encodeURIComponent(input.projectId)}/activities`, {
33155
33000
  action: input.action,
@@ -33224,20 +33069,6 @@ function createHttpClient(config2) {
33224
33069
  );
33225
33070
  return result.ranked ?? [];
33226
33071
  },
33227
- rankProjectBeatVersions: async (projectId, lens) => {
33228
- const result = await request(
33229
- "GET",
33230
- `/api/systems/${encodeURIComponent(projectId)}/beat-versions/ranked?lens=${encodeURIComponent(lens)}`
33231
- );
33232
- return result.ranked ?? [];
33233
- },
33234
- rankProjectBeatVersionsWithLeverage: async (projectId, lens) => {
33235
- const result = await request(
33236
- "GET",
33237
- `/api/systems/${encodeURIComponent(projectId)}/beat-versions/ranked?lens=${encodeURIComponent(lens)}&withLeverage=true`
33238
- );
33239
- return result.ranked ?? [];
33240
- },
33241
33072
  // All four use requestOrThrow, not request. These endpoints answer 404 for a
33242
33073
  // missing edge/Beat with a real domain message, and `request()` turns a 404
33243
33074
  // into `undefined` — so the old `result.edge` access threw
@@ -33536,18 +33367,6 @@ function createHttpClient(config2) {
33536
33367
  );
33537
33368
  return { checks: res?.checks ?? [], nextCursor: res?.nextCursor };
33538
33369
  },
33539
- listProjectChecks: async (projectId, checkType, options) => {
33540
- const params = new URLSearchParams();
33541
- if (checkType) params.set("type", checkType);
33542
- if (options?.limit !== void 0) params.set("limit", String(options.limit));
33543
- if (options?.cursor) params.set("cursor", options.cursor);
33544
- const qs = params.toString() ? `?${params.toString()}` : "";
33545
- const res = await request(
33546
- "GET",
33547
- `/api/systems/${encodeURIComponent(projectId)}/checks${qs}`
33548
- );
33549
- return { checks: res?.checks ?? [], nextCursor: res?.nextCursor };
33550
- },
33551
33370
  listLatestSystemChecks: async (projectId) => {
33552
33371
  const res = await request(
33553
33372
  "GET",
@@ -33555,13 +33374,6 @@ function createHttpClient(config2) {
33555
33374
  );
33556
33375
  return res?.checks ?? [];
33557
33376
  },
33558
- listLatestProjectChecks: async (projectId) => {
33559
- const res = await request(
33560
- "GET",
33561
- `/api/checks/latest?projectId=${encodeURIComponent(projectId)}`
33562
- );
33563
- return res?.checks ?? [];
33564
- },
33565
33377
  listBeatChecks: async (beatId, projectId, checkType) => {
33566
33378
  const qs = checkType ? `?type=${encodeURIComponent(checkType)}` : "";
33567
33379
  const res = await request(
@@ -33621,20 +33433,6 @@ function createHttpClient(config2) {
33621
33433
  updateTeamspace: async (teamspaceId, updates) => {
33622
33434
  return request("PATCH", `/api/teamspaces/${encodeURIComponent(teamspaceId)}`, updates);
33623
33435
  },
33624
- // Deliverables
33625
- listTeamspaceDeliverables: async (teamspaceId) => {
33626
- const res = await request("GET", `/api/teamspaces/${encodeURIComponent(teamspaceId)}/deliverables`);
33627
- return res?.deliverables ?? [];
33628
- },
33629
- getDeliverable: async (deliverableId) => {
33630
- return request("GET", `/api/deliverables/${encodeURIComponent(deliverableId)}`);
33631
- },
33632
- createDeliverable: async (input) => {
33633
- return request("POST", `/api/teamspaces/${encodeURIComponent(input.teamspaceId)}/deliverables`, input);
33634
- },
33635
- updateDeliverable: async (deliverableId, updates) => {
33636
- return request("PATCH", `/api/deliverables/${encodeURIComponent(deliverableId)}`, updates);
33637
- },
33638
33436
  // DeliverableGroups
33639
33437
  listTeamspaceDeliverableGroups: async (teamspaceId) => {
33640
33438
  const res = await request("GET", `/api/teamspaces/${encodeURIComponent(teamspaceId)}/deliverable-groups`);
@@ -33646,11 +33444,6 @@ function createHttpClient(config2) {
33646
33444
  if (res === void 0) throw new Error(`Project ${projectId} not found`);
33647
33445
  return res.deliverableGroups;
33648
33446
  },
33649
- listProjectDeliverableGroups: async (projectId) => {
33650
- const res = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/deliverable-groups`);
33651
- if (res === void 0) throw new Error(`Project ${projectId} not found`);
33652
- return res.deliverableGroups;
33653
- },
33654
33447
  listMovementDeliverableGroups: async (movementId) => {
33655
33448
  const res = await request("GET", `/api/movements/${encodeURIComponent(movementId)}/deliverable-groups`);
33656
33449
  if (res === void 0) throw new Error(`Movement ${movementId} not found`);
@@ -33687,10 +33480,6 @@ function createHttpClient(config2) {
33687
33480
  ...opts ?? {}
33688
33481
  });
33689
33482
  },
33690
- listDeliverableGroupDeliverables: async (deliverableGroupId) => {
33691
- const res = await request("GET", `/api/deliverable-groups/${encodeURIComponent(deliverableGroupId)}/deliverables`);
33692
- return res?.deliverables ?? [];
33693
- },
33694
33483
  listDeliverableGroupDrops: async (deliverableGroupId) => {
33695
33484
  const res = await request("GET", `/api/deliverable-groups/${encodeURIComponent(deliverableGroupId)}/drops`);
33696
33485
  return res?.drops ?? [];
@@ -34270,16 +34059,54 @@ function createHttpClient(config2) {
34270
34059
  );
34271
34060
  return result?.usedBy;
34272
34061
  },
34273
- // Feature flags (B-135 v9) — not available in HTTP transport; the published
34274
- // npm package has no DynamoDB credentials.
34275
- listFlags: async () => {
34276
- throw new Error("Feature flag queries are not available in HTTP transport mode");
34062
+ // Feature flags (B-135 v11) — routed through /api/mcp/feature-flags/* so the
34063
+ // published npm package (no DynamoDB credentials) can manage flags via HTTP.
34064
+ listFlags: async (options) => {
34065
+ const params = new URLSearchParams();
34066
+ if (options?.includeArchived) params.set("includeArchived", "true");
34067
+ if (options?.search) params.set("search", options.search);
34068
+ if (options?.cursor) params.set("cursor", options.cursor);
34069
+ if (options?.limit !== void 0) params.set("limit", String(options.limit));
34070
+ const qs = params.toString();
34071
+ return requestOrThrow(
34072
+ "GET",
34073
+ `/api/mcp/feature-flags${qs ? `?${qs}` : ""}`
34074
+ );
34075
+ },
34076
+ getFlag: async (name, options) => {
34077
+ const qs = options?.includeHistory ? "?includeHistory=true" : "";
34078
+ return request("GET", `/api/mcp/feature-flags/${encodeURIComponent(name)}${qs}`);
34079
+ },
34080
+ checkFlag: async (name, stage) => {
34081
+ const flag = await request(
34082
+ "GET",
34083
+ `/api/mcp/feature-flags/${encodeURIComponent(name)}`
34084
+ );
34085
+ return flag ? flag.enabledIn.includes(stage) : false;
34086
+ },
34087
+ createFlag: async (name, enabledIn, options) => {
34088
+ return requestOrThrow("POST", "/api/mcp/feature-flags", {
34089
+ name,
34090
+ enabledIn,
34091
+ description: options?.description,
34092
+ note: options?.note
34093
+ });
34277
34094
  },
34278
- getFlag: async () => {
34279
- throw new Error("Feature flag queries are not available in HTTP transport mode");
34095
+ setFlag: async (name, enabledIn, options) => {
34096
+ return requestOrThrow("PUT", `/api/mcp/feature-flags/${encodeURIComponent(name)}`, {
34097
+ enabledIn,
34098
+ note: options?.note
34099
+ });
34100
+ },
34101
+ archiveFlag: async (name, options) => {
34102
+ return requestOrThrow("POST", `/api/mcp/feature-flags/${encodeURIComponent(name)}/archive`, {
34103
+ note: options?.note
34104
+ });
34280
34105
  },
34281
- checkFlag: async () => {
34282
- throw new Error("Feature flag queries are not available in HTTP transport mode");
34106
+ unarchiveFlag: async (name, options) => {
34107
+ return requestOrThrow("POST", `/api/mcp/feature-flags/${encodeURIComponent(name)}/unarchive`, {
34108
+ note: options?.note
34109
+ });
34283
34110
  }
34284
34111
  };
34285
34112
  return client;
@@ -34330,7 +34157,7 @@ function loadConfig() {
34330
34157
  };
34331
34158
  }
34332
34159
  async function main() {
34333
- console.error(`[harmonica-mcp] v${"3.4.0"} starting\u2026`);
34160
+ console.error(`[harmonica-mcp] v${"3.6.0"} starting\u2026`);
34334
34161
  const config2 = loadConfig();
34335
34162
  const client = createHttpClient({
34336
34163
  apiBaseUrl: config2.apiBaseUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codazen/harmonica-mcp",
3
- "version": "3.4.0",
3
+ "version": "3.6.0",
4
4
  "description": "MCP server for Harmonica — connect any MCP-compatible AI assistant to Harmonica",
5
5
  "license": "MIT",
6
6
  "bin": {