@codazen/harmonica-mcp 0.25.1 → 0.25.3

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 +28 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -22602,6 +22602,7 @@ var LIFECYCLE_STATES = [
22602
22602
  "archived",
22603
22603
  "deprecated"
22604
22604
  ];
22605
+ var PR_LIFECYCLE_STATES = ["draft", "open"];
22605
22606
 
22606
22607
  // ../../libs/harmonica-services/src/mcp/tools/beat-version-tools.ts
22607
22608
  function registerBeatVersionTools(server, ctx, client) {
@@ -24937,7 +24938,7 @@ function registerRevisionLifecycleTools(server, ctx, client) {
24937
24938
  "Transition a revision to a new lifecycle state (e.g., concept \u2192 plan, build \u2192 deploy \u2192 support).",
24938
24939
  {
24939
24940
  revisionId: external_exports.string().describe("The revision ID (e.g., rev-abc123)"),
24940
- targetState: external_exports.enum(LIFECYCLE_STATES).describe("The target lifecycle state"),
24941
+ targetState: external_exports.enum([...LIFECYCLE_STATES, ...PR_LIFECYCLE_STATES]).describe("The target lifecycle state"),
24941
24942
  reason: external_exports.string().optional().describe("Why this transition is being made"),
24942
24943
  skipQualityCheck: external_exports.boolean().optional().describe("Skip the quality check guard (for reconciling already-shipped code)"),
24943
24944
  reconcile: external_exports.boolean().optional().describe("Walk through all intermediate forward states automatically. Use for already-shipped work that needs state machine catch-up.")
@@ -25154,11 +25155,14 @@ function registerSnapshotTools(server, ctx, client) {
25154
25155
  "",
25155
25156
  ...versionNote,
25156
25157
  ` Beats: ${snapshot.beats.length}`,
25158
+ ` Beat Versions: ${snapshot.beatVersions?.length ?? 0}`,
25157
25159
  ` Proposals: ${snapshot.proposals.length}`,
25158
25160
  ` Notes: ${snapshot.notes.length}`,
25159
25161
  ` Tasks: ${snapshot.tasks.length}`,
25160
25162
  ` Submissions: ${snapshot.submissions.length}`,
25161
25163
  ` PromptLogs: ${snapshot.promptLogs.length}`,
25164
+ ` Drops: ${snapshot.drops?.length ?? 0}`,
25165
+ ` Deliverables: ${snapshot.deliverables?.length ?? 0}`,
25162
25166
  "",
25163
25167
  "Use import_project_snapshot with this file path to import into another environment."
25164
25168
  ];
@@ -25169,9 +25173,12 @@ function registerSnapshotTools(server, ctx, client) {
25169
25173
  "import_project_snapshot",
25170
25174
  "Import a project snapshot into the current environment. Accepts an S3 presigned URL (https:// only), a file path, or inline JSON. Blocked in production by default.",
25171
25175
  {
25172
- snapshot: external_exports.string().describe("S3 presigned URL (https://...), file path, or inline JSON string")
25176
+ snapshot: external_exports.string().describe("S3 presigned URL (https://...), file path, or inline JSON string"),
25177
+ targetTeamspaceId: external_exports.string().min(1).optional().describe(
25178
+ "Remap all teamspace references (Drops, Deliverables, Project record) to this teamspace ID in the target environment. Required when the source and target environments use different teamspace UUIDs. When omitted and the snapshot contains Drops or Deliverables, a warning is included in the import summary."
25179
+ )
25173
25180
  },
25174
- async ({ snapshot: snapshotInput }) => {
25181
+ async ({ snapshot: snapshotInput, targetTeamspaceId }) => {
25175
25182
  const isProduction = process.env.NODE_ENV === "production";
25176
25183
  const importAllowed = process.env.ALLOW_PROJECT_IMPORT === "true";
25177
25184
  if (isProduction && !importAllowed) {
@@ -25184,13 +25191,13 @@ function registerSnapshotTools(server, ctx, client) {
25184
25191
  };
25185
25192
  }
25186
25193
  const trimmed = snapshotInput.trim();
25187
- const importOptions = { targetOrgId: ctx.orgId, targetUserId: ctx.user.userId };
25194
+ const importOptions = { targetOrgId: ctx.orgId, targetUserId: ctx.user.userId, targetTeamspaceId };
25188
25195
  if (trimmed.startsWith("https://")) {
25189
25196
  if (isPrivateHost(trimmed)) {
25190
25197
  throw new Error("Snapshot URL must point to a public host \u2014 private, link-local, and loopback addresses are not permitted.");
25191
25198
  }
25192
25199
  const result2 = await client.importProjectSnapshotFromUrl(trimmed, importOptions);
25193
- return { content: [{ type: "text", text: formatImportSummary(result2) }] };
25200
+ return { content: [{ type: "text", text: formatImportSummary(result2, targetTeamspaceId) }] };
25194
25201
  }
25195
25202
  if (trimmed.startsWith("http://")) {
25196
25203
  throw new Error("Snapshot URL must use https:// \u2014 http:// is not permitted.");
@@ -25205,7 +25212,7 @@ function registerSnapshotTools(server, ctx, client) {
25205
25212
  assertSnapshotShape(parsed, "");
25206
25213
  const normalized = normalizeSnapshot(parsed);
25207
25214
  const result = await client.importProjectSnapshot(normalized, importOptions);
25208
- return { content: [{ type: "text", text: formatImportSummary(result) }] };
25215
+ return { content: [{ type: "text", text: formatImportSummary(result, targetTeamspaceId) }] };
25209
25216
  }
25210
25217
  );
25211
25218
  }
@@ -25248,7 +25255,7 @@ async function resolveSnapshotInput(input) {
25248
25255
  }
25249
25256
  return trimmed;
25250
25257
  }
25251
- function formatImportSummary(result) {
25258
+ function formatImportSummary(result, targetTeamspaceId) {
25252
25259
  const lines = [
25253
25260
  `Imported project: ${result.projectId}`,
25254
25261
  ` Beats: ${result.counts.beats}`,
@@ -25259,12 +25266,24 @@ function formatImportSummary(result) {
25259
25266
  ` Notes: ${result.counts.notes}`,
25260
25267
  ` Tasks: ${result.counts.tasks}`,
25261
25268
  ` Submissions: ${result.counts.submissions}`,
25262
- ` PromptLogs: ${result.counts.promptLogs}`
25269
+ ` PromptLogs: ${result.counts.promptLogs}`,
25270
+ ` Beat Versions: ${result.counts.beatVersions ?? 0}`,
25271
+ ` Drops: ${result.counts.drops ?? 0}`,
25272
+ ` Deliverables: ${result.counts.deliverables ?? 0}`
25263
25273
  ];
25264
25274
  if (result.errors.length > 0) {
25265
25275
  lines.push("", `Errors (${result.errors.length}):`);
25266
25276
  result.errors.forEach((e) => lines.push(` - ${e}`));
25267
25277
  }
25278
+ const hasTeamspaceData = result.projectTeamspaceId !== void 0 || (result.counts.drops ?? 0) + (result.counts.deliverables ?? 0) > 0;
25279
+ if (!targetTeamspaceId && hasTeamspaceData) {
25280
+ lines.push(
25281
+ "",
25282
+ "Warning: snapshot contains teamspace-scoped data but targetTeamspaceId was not provided.",
25283
+ "The project record and any Drops or Deliverables were imported with their original teamspaceId and may not appear under the correct teamspace in this environment.",
25284
+ "Re-run with targetTeamspaceId set to the target teamspace UUID to remap them."
25285
+ );
25286
+ }
25268
25287
  return lines.join("\n");
25269
25288
  }
25270
25289
  function isPrivateHost(url) {
@@ -27038,7 +27057,7 @@ function loadConfig() {
27038
27057
  };
27039
27058
  }
27040
27059
  async function main() {
27041
- console.error(`[harmonica-mcp] v${"0.25.1"} starting\u2026`);
27060
+ console.error(`[harmonica-mcp] v${"0.25.3"} starting\u2026`);
27042
27061
  const config2 = loadConfig();
27043
27062
  const client = createHttpClient({
27044
27063
  apiBaseUrl: config2.apiBaseUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codazen/harmonica-mcp",
3
- "version": "0.25.1",
3
+ "version": "0.25.3",
4
4
  "description": "MCP server for Harmonica — connect Claude to your Harmonica projects",
5
5
  "license": "MIT",
6
6
  "bin": {