@gethmy/mcp 2.15.0 → 2.16.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.
package/dist/cli.js CHANGED
@@ -1826,6 +1826,10 @@ class HarmonyApiClient {
1826
1826
  async updateCard(cardId, updates) {
1827
1827
  return this.request("PATCH", `/cards/${cardId}`, updates);
1828
1828
  }
1829
+ async claimCard(cardId, agentId) {
1830
+ const res = await this.request("PATCH", `/cards/${cardId}`, { assignedAgentId: agentId, ifAssignedAgentNull: true });
1831
+ return { claimed: res.claimed !== false };
1832
+ }
1829
1833
  async moveCard(cardId, columnId, position) {
1830
1834
  return this.request("POST", `/cards/${cardId}/move`, {
1831
1835
  columnId,
@@ -6335,11 +6339,11 @@ async function handleToolCall(name, args, deps) {
6335
6339
  }
6336
6340
  case "harmony_generate_prompt": {
6337
6341
  let cardId;
6342
+ const projectId = args.projectId || deps.getActiveProjectId() || undefined;
6338
6343
  if (args.cardId) {
6339
6344
  cardId = z.string().uuid().parse(args.cardId);
6340
6345
  } else if (args.shortId !== undefined) {
6341
6346
  const shortId = z.number().int().positive().parse(args.shortId);
6342
- const projectId = args.projectId || deps.getActiveProjectId();
6343
6347
  if (!projectId) {
6344
6348
  throw new Error("Project ID required when using shortId. Use harmony_set_project_context or provide projectId.");
6345
6349
  }
@@ -6348,23 +6352,40 @@ async function handleToolCall(name, args, deps) {
6348
6352
  } else {
6349
6353
  throw new Error("Either cardId or shortId must be provided");
6350
6354
  }
6351
- let cardTitle = "";
6352
- let cardDescription = "";
6355
+ const variant = args.variant || undefined;
6356
+ const customConstraints = typeof args.customConstraints === "string" ? args.customConstraints : undefined;
6357
+ const contextOptions = {
6358
+ includeSubtasks: args.includeSubtasks !== false,
6359
+ includeLinks: args.includeLinks !== false,
6360
+ includeDescription: args.includeDescription !== false
6361
+ };
6353
6362
  try {
6354
- const { card } = await client3.getCard(cardId);
6355
- const typedCard = card;
6356
- cardTitle = typedCard.title || "";
6357
- cardDescription = typedCard.description || "";
6358
- } catch {}
6359
- const taskBlock = [cardTitle, cardDescription].filter(Boolean).join(`
6363
+ const result = await client3.generateCardPrompt({
6364
+ cardId,
6365
+ workspaceId: deps.getActiveWorkspaceId() || "",
6366
+ projectId,
6367
+ variant,
6368
+ customConstraints,
6369
+ contextOptions
6370
+ });
6371
+ return { success: true, ...result };
6372
+ } catch (err) {
6373
+ const msg = err instanceof Error ? err.message : String(err);
6374
+ console.debug(`[harmony_generate_prompt] falling back: ${msg}`);
6375
+ let cardTitle = "";
6376
+ let cardDescription = "";
6377
+ try {
6378
+ const { card } = await client3.getCard(cardId);
6379
+ const typedCard = card;
6380
+ cardTitle = typedCard.title || "";
6381
+ cardDescription = typedCard.description || "";
6382
+ } catch {}
6383
+ const taskBlock = [cardTitle, cardDescription].filter(Boolean).join(`
6360
6384
 
6361
6385
  `);
6362
- const prompt = `Here is the task: ${taskBlock || "(no description available)"}`;
6363
- return {
6364
- success: true,
6365
- prompt,
6366
- cardId
6367
- };
6386
+ const prompt = `Here is the task: ${taskBlock || "(no description available)"}`;
6387
+ return { success: true, prompt, cardId };
6388
+ }
6368
6389
  }
6369
6390
  case "harmony_remember": {
6370
6391
  const title = z.string().min(1).max(300).parse(args.title);
package/dist/index.js CHANGED
@@ -1821,6 +1821,10 @@ class HarmonyApiClient {
1821
1821
  async updateCard(cardId, updates) {
1822
1822
  return this.request("PATCH", `/cards/${cardId}`, updates);
1823
1823
  }
1824
+ async claimCard(cardId, agentId) {
1825
+ const res = await this.request("PATCH", `/cards/${cardId}`, { assignedAgentId: agentId, ifAssignedAgentNull: true });
1826
+ return { claimed: res.claimed !== false };
1827
+ }
1824
1828
  async moveCard(cardId, columnId, position) {
1825
1829
  return this.request("POST", `/cards/${cardId}/move`, {
1826
1830
  columnId,
@@ -6330,11 +6334,11 @@ async function handleToolCall(name, args, deps) {
6330
6334
  }
6331
6335
  case "harmony_generate_prompt": {
6332
6336
  let cardId;
6337
+ const projectId = args.projectId || deps.getActiveProjectId() || undefined;
6333
6338
  if (args.cardId) {
6334
6339
  cardId = z.string().uuid().parse(args.cardId);
6335
6340
  } else if (args.shortId !== undefined) {
6336
6341
  const shortId = z.number().int().positive().parse(args.shortId);
6337
- const projectId = args.projectId || deps.getActiveProjectId();
6338
6342
  if (!projectId) {
6339
6343
  throw new Error("Project ID required when using shortId. Use harmony_set_project_context or provide projectId.");
6340
6344
  }
@@ -6343,23 +6347,40 @@ async function handleToolCall(name, args, deps) {
6343
6347
  } else {
6344
6348
  throw new Error("Either cardId or shortId must be provided");
6345
6349
  }
6346
- let cardTitle = "";
6347
- let cardDescription = "";
6350
+ const variant = args.variant || undefined;
6351
+ const customConstraints = typeof args.customConstraints === "string" ? args.customConstraints : undefined;
6352
+ const contextOptions = {
6353
+ includeSubtasks: args.includeSubtasks !== false,
6354
+ includeLinks: args.includeLinks !== false,
6355
+ includeDescription: args.includeDescription !== false
6356
+ };
6348
6357
  try {
6349
- const { card } = await client3.getCard(cardId);
6350
- const typedCard = card;
6351
- cardTitle = typedCard.title || "";
6352
- cardDescription = typedCard.description || "";
6353
- } catch {}
6354
- const taskBlock = [cardTitle, cardDescription].filter(Boolean).join(`
6358
+ const result = await client3.generateCardPrompt({
6359
+ cardId,
6360
+ workspaceId: deps.getActiveWorkspaceId() || "",
6361
+ projectId,
6362
+ variant,
6363
+ customConstraints,
6364
+ contextOptions
6365
+ });
6366
+ return { success: true, ...result };
6367
+ } catch (err) {
6368
+ const msg = err instanceof Error ? err.message : String(err);
6369
+ console.debug(`[harmony_generate_prompt] falling back: ${msg}`);
6370
+ let cardTitle = "";
6371
+ let cardDescription = "";
6372
+ try {
6373
+ const { card } = await client3.getCard(cardId);
6374
+ const typedCard = card;
6375
+ cardTitle = typedCard.title || "";
6376
+ cardDescription = typedCard.description || "";
6377
+ } catch {}
6378
+ const taskBlock = [cardTitle, cardDescription].filter(Boolean).join(`
6355
6379
 
6356
6380
  `);
6357
- const prompt = `Here is the task: ${taskBlock || "(no description available)"}`;
6358
- return {
6359
- success: true,
6360
- prompt,
6361
- cardId
6362
- };
6381
+ const prompt = `Here is the task: ${taskBlock || "(no description available)"}`;
6382
+ return { success: true, prompt, cardId };
6383
+ }
6363
6384
  }
6364
6385
  case "harmony_remember": {
6365
6386
  const title = z.string().min(1).max(300).parse(args.title);
@@ -1273,6 +1273,10 @@ class HarmonyApiClient {
1273
1273
  async updateCard(cardId, updates) {
1274
1274
  return this.request("PATCH", `/cards/${cardId}`, updates);
1275
1275
  }
1276
+ async claimCard(cardId, agentId) {
1277
+ const res = await this.request("PATCH", `/cards/${cardId}`, { assignedAgentId: agentId, ifAssignedAgentNull: true });
1278
+ return { claimed: res.claimed !== false };
1279
+ }
1276
1280
  async moveCard(cardId, columnId, position) {
1277
1281
  return this.request("POST", `/cards/${cardId}/move`, {
1278
1282
  columnId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethmy/mcp",
3
- "version": "2.15.0",
3
+ "version": "2.16.0",
4
4
  "description": "MCP server for Harmony Kanban board - enables AI coding agents to manage your boards",
5
5
  "publishConfig": {
6
6
  "access": "public"
package/src/api-client.ts CHANGED
@@ -625,6 +625,25 @@ export class HarmonyApiClient {
625
625
  return this.request("PATCH", `/cards/${cardId}`, updates);
626
626
  }
627
627
 
628
+ /**
629
+ * Compare-and-set claim of a card for a virtual agent: sets assigned_agent_id
630
+ * only if it is currently NULL (server-side guard). Returns `{ claimed: false }`
631
+ * when another daemon already owns it. Used by the agent-agnostic review pickup.
632
+ */
633
+ async claimCard(
634
+ cardId: string,
635
+ agentId: string,
636
+ ): Promise<{ claimed: boolean }> {
637
+ const res = await this.request<{ card?: unknown; claimed?: boolean }>(
638
+ "PATCH",
639
+ `/cards/${cardId}`,
640
+ { assignedAgentId: agentId, ifAssignedAgentNull: true },
641
+ );
642
+ // Success body is `{ card }` (no `claimed` key) → won. Lost-race body is
643
+ // `{ claimed: false }`.
644
+ return { claimed: res.claimed !== false };
645
+ }
646
+
628
647
  async moveCard(
629
648
  cardId: string,
630
649
  columnId: string,
package/src/server.ts CHANGED
@@ -3824,17 +3824,16 @@ async function handleToolCall(
3824
3824
  }
3825
3825
 
3826
3826
  // Prompt generation
3827
- // TODO Phase 1: rebuild full context assembly per docs/superpowers/plans/2026-05-07-memory-architecture-v2.md §10
3828
3827
  case "harmony_generate_prompt": {
3829
3828
  // Resolve card ID — either directly or via short ID
3830
3829
  let cardId: string;
3830
+ const projectId =
3831
+ (args.projectId as string) || deps.getActiveProjectId() || undefined;
3831
3832
 
3832
3833
  if (args.cardId) {
3833
3834
  cardId = z.string().uuid().parse(args.cardId);
3834
3835
  } else if (args.shortId !== undefined) {
3835
3836
  const shortId = z.number().int().positive().parse(args.shortId);
3836
- const projectId =
3837
- (args.projectId as string) || deps.getActiveProjectId();
3838
3837
  if (!projectId) {
3839
3838
  throw new Error(
3840
3839
  "Project ID required when using shortId. Use harmony_set_project_context or provide projectId.",
@@ -3846,29 +3845,58 @@ async function handleToolCall(
3846
3845
  throw new Error("Either cardId or shortId must be provided");
3847
3846
  }
3848
3847
 
3849
- // Phase 0: minimal placeholder prompt with no context assembly.
3850
- // Fetch just the card title/description so the agent has the basics.
3851
- let cardTitle = "";
3852
- let cardDescription = "";
3848
+ const variant =
3849
+ (args.variant as "analysis" | "draft" | "execute") || undefined;
3850
+ const customConstraints =
3851
+ typeof args.customConstraints === "string"
3852
+ ? args.customConstraints
3853
+ : undefined;
3854
+
3855
+ // Context toggles default ON — a card's reference fields (linked cards,
3856
+ // external URLs, attachments) are hydrated inside generateCardPrompt so
3857
+ // the prompt carries the links the agent needs to fetch related content.
3858
+ const contextOptions = {
3859
+ includeSubtasks: args.includeSubtasks !== false,
3860
+ includeLinks: args.includeLinks !== false,
3861
+ includeDescription: args.includeDescription !== false,
3862
+ };
3863
+
3853
3864
  try {
3854
- const { card } = await client.getCard(cardId);
3855
- const typedCard = card as { title?: string; description?: string };
3856
- cardTitle = typedCard.title || "";
3857
- cardDescription = typedCard.description || "";
3858
- } catch {
3859
- // Card fetch failed; return an empty task description.
3860
- }
3865
+ const result = await client.generateCardPrompt({
3866
+ cardId,
3867
+ workspaceId: deps.getActiveWorkspaceId() || "",
3868
+ projectId,
3869
+ variant,
3870
+ customConstraints,
3871
+ contextOptions,
3872
+ });
3873
+ return { success: true, ...result };
3874
+ } catch (err) {
3875
+ // Graceful fallback: if full context assembly fails (e.g. an offline
3876
+ // sub-fetch), still return a usable title/description prompt so the
3877
+ // Harmony command never dead-ends. The reference-link hydration is
3878
+ // best-effort inside generateCardPrompt and won't reach here.
3879
+ const msg = err instanceof Error ? err.message : String(err);
3880
+ console.debug(`[harmony_generate_prompt] falling back: ${msg}`);
3881
+
3882
+ let cardTitle = "";
3883
+ let cardDescription = "";
3884
+ try {
3885
+ const { card } = await client.getCard(cardId);
3886
+ const typedCard = card as { title?: string; description?: string };
3887
+ cardTitle = typedCard.title || "";
3888
+ cardDescription = typedCard.description || "";
3889
+ } catch {
3890
+ // Card fetch failed; return an empty task description.
3891
+ }
3861
3892
 
3862
- const taskBlock = [cardTitle, cardDescription]
3863
- .filter(Boolean)
3864
- .join("\n\n");
3865
- const prompt = `Here is the task: ${taskBlock || "(no description available)"}`;
3893
+ const taskBlock = [cardTitle, cardDescription]
3894
+ .filter(Boolean)
3895
+ .join("\n\n");
3896
+ const prompt = `Here is the task: ${taskBlock || "(no description available)"}`;
3866
3897
 
3867
- return {
3868
- success: true,
3869
- prompt,
3870
- cardId,
3871
- };
3898
+ return { success: true, prompt, cardId };
3899
+ }
3872
3900
  }
3873
3901
 
3874
3902
  // Memory / Knowledge Graph operations