@gethmy/mcp 2.10.0 → 2.11.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.
package/dist/cli.js CHANGED
@@ -1913,6 +1913,12 @@ class HarmonyApiClient {
1913
1913
  async startAgentSession(cardId, data) {
1914
1914
  return this.request("POST", `/cards/${cardId}/agent-context`, data);
1915
1915
  }
1916
+ async getPendingMessages(cardId, sessionId, sinceSeq) {
1917
+ const params = new URLSearchParams;
1918
+ params.set("sessionId", sessionId);
1919
+ params.set("sinceSeq", String(sinceSeq));
1920
+ return this.request("GET", `/cards/${cardId}/agent-messages?${params.toString()}`);
1921
+ }
1916
1922
  async updateAgentProgress(cardId, data) {
1917
1923
  return this.request("POST", `/cards/${cardId}/agent-context`, data);
1918
1924
  }
@@ -4172,6 +4178,10 @@ var TOOLS = {
4172
4178
  type: "array",
4173
4179
  items: { type: "string" },
4174
4180
  description: 'Label names to add to card (e.g., ["agent"]). Case-insensitive match.'
4181
+ },
4182
+ steerable: {
4183
+ type: "boolean",
4184
+ description: "Set true only if this session will poll harmony_get_pending_messages at its checkpoints. Enables the live steering composer for the run; leave unset/false if you won't consume steering messages."
4175
4185
  }
4176
4186
  },
4177
4187
  required: ["cardId", "agentIdentifier", "agentName"]
@@ -4262,6 +4272,27 @@ var TOOLS = {
4262
4272
  required: ["cardId"]
4263
4273
  }
4264
4274
  },
4275
+ harmony_get_pending_messages: {
4276
+ description: "Drain queued steering messages a teammate sent to your live agent session (card #473). Call at your progress checkpoints with the session id from harmony_start_agent_session and the highest seq you've already consumed; returns user messages with seq > sinceSeq, oldest first. Fold them into your next step and advance sinceSeq to the largest returned seq so each is handled exactly once.",
4277
+ inputSchema: {
4278
+ type: "object",
4279
+ properties: {
4280
+ cardId: {
4281
+ type: "string",
4282
+ description: "Card ID with the live session"
4283
+ },
4284
+ sessionId: {
4285
+ type: "string",
4286
+ description: "Agent session id (the `session.id` returned by harmony_start_agent_session)"
4287
+ },
4288
+ sinceSeq: {
4289
+ type: "number",
4290
+ description: "Return only messages with seq greater than this (the last seq you consumed). Defaults to 0 to fetch all."
4291
+ }
4292
+ },
4293
+ required: ["cardId", "sessionId"]
4294
+ }
4295
+ },
4265
4296
  harmony_generate_prompt: {
4266
4297
  description: "Generate an AI-ready prompt from a card. Automatically infers role and focus based on labels (bug, feature, design, etc.). Use this to create context-rich prompts for working on cards.",
4267
4298
  inputSchema: {
@@ -5597,7 +5628,8 @@ async function handleToolCall(name, args, deps) {
5597
5628
  agentName,
5598
5629
  status: "working",
5599
5630
  currentTask: args.currentTask,
5600
- estimatedMinutesRemaining: args.estimatedMinutesRemaining
5631
+ estimatedMinutesRemaining: args.estimatedMinutesRemaining,
5632
+ steerable: args.steerable === true || args.steerable === "true" ? true : undefined
5601
5633
  });
5602
5634
  markExplicit(cardId, {
5603
5635
  agentIdentifier,
@@ -5636,6 +5668,7 @@ async function handleToolCall(name, args, deps) {
5636
5668
  } else if (callerRecentActions.length > 0) {
5637
5669
  mergedRecentActions = callerRecentActions;
5638
5670
  }
5671
+ const runActivity = (callerActions || []).map((a) => a.description).filter((d) => typeof d === "string" && d.length > 0);
5639
5672
  const result = await client3.updateAgentProgress(cardId, {
5640
5673
  agentIdentifier,
5641
5674
  agentName,
@@ -5644,7 +5677,8 @@ async function handleToolCall(name, args, deps) {
5644
5677
  currentTask: args.currentTask,
5645
5678
  blockers: args.blockers,
5646
5679
  estimatedMinutesRemaining: args.estimatedMinutesRemaining,
5647
- ...mergedRecentActions && { recentActions: mergedRecentActions }
5680
+ ...mergedRecentActions && { recentActions: mergedRecentActions },
5681
+ ...runActivity.length > 0 && { runActivity }
5648
5682
  });
5649
5683
  return { success: true, midSessionLearnings: 0, ...result };
5650
5684
  }
@@ -5700,6 +5734,13 @@ async function handleToolCall(name, args, deps) {
5700
5734
  });
5701
5735
  return { success: true, ...result };
5702
5736
  }
5737
+ case "harmony_get_pending_messages": {
5738
+ const cardId = z.string().uuid().parse(args.cardId);
5739
+ const sessionId = z.string().min(1).parse(args.sessionId);
5740
+ const sinceSeq = args.sinceSeq !== undefined ? z.number().int().min(0).parse(args.sinceSeq) : 0;
5741
+ const result = await client3.getPendingMessages(cardId, sessionId, sinceSeq);
5742
+ return { success: true, ...result };
5743
+ }
5703
5744
  case "harmony_generate_prompt": {
5704
5745
  let cardId;
5705
5746
  if (args.cardId) {
package/dist/index.js CHANGED
@@ -1908,6 +1908,12 @@ class HarmonyApiClient {
1908
1908
  async startAgentSession(cardId, data) {
1909
1909
  return this.request("POST", `/cards/${cardId}/agent-context`, data);
1910
1910
  }
1911
+ async getPendingMessages(cardId, sessionId, sinceSeq) {
1912
+ const params = new URLSearchParams;
1913
+ params.set("sessionId", sessionId);
1914
+ params.set("sinceSeq", String(sinceSeq));
1915
+ return this.request("GET", `/cards/${cardId}/agent-messages?${params.toString()}`);
1916
+ }
1911
1917
  async updateAgentProgress(cardId, data) {
1912
1918
  return this.request("POST", `/cards/${cardId}/agent-context`, data);
1913
1919
  }
@@ -4167,6 +4173,10 @@ var TOOLS = {
4167
4173
  type: "array",
4168
4174
  items: { type: "string" },
4169
4175
  description: 'Label names to add to card (e.g., ["agent"]). Case-insensitive match.'
4176
+ },
4177
+ steerable: {
4178
+ type: "boolean",
4179
+ description: "Set true only if this session will poll harmony_get_pending_messages at its checkpoints. Enables the live steering composer for the run; leave unset/false if you won't consume steering messages."
4170
4180
  }
4171
4181
  },
4172
4182
  required: ["cardId", "agentIdentifier", "agentName"]
@@ -4257,6 +4267,27 @@ var TOOLS = {
4257
4267
  required: ["cardId"]
4258
4268
  }
4259
4269
  },
4270
+ harmony_get_pending_messages: {
4271
+ description: "Drain queued steering messages a teammate sent to your live agent session (card #473). Call at your progress checkpoints with the session id from harmony_start_agent_session and the highest seq you've already consumed; returns user messages with seq > sinceSeq, oldest first. Fold them into your next step and advance sinceSeq to the largest returned seq so each is handled exactly once.",
4272
+ inputSchema: {
4273
+ type: "object",
4274
+ properties: {
4275
+ cardId: {
4276
+ type: "string",
4277
+ description: "Card ID with the live session"
4278
+ },
4279
+ sessionId: {
4280
+ type: "string",
4281
+ description: "Agent session id (the `session.id` returned by harmony_start_agent_session)"
4282
+ },
4283
+ sinceSeq: {
4284
+ type: "number",
4285
+ description: "Return only messages with seq greater than this (the last seq you consumed). Defaults to 0 to fetch all."
4286
+ }
4287
+ },
4288
+ required: ["cardId", "sessionId"]
4289
+ }
4290
+ },
4260
4291
  harmony_generate_prompt: {
4261
4292
  description: "Generate an AI-ready prompt from a card. Automatically infers role and focus based on labels (bug, feature, design, etc.). Use this to create context-rich prompts for working on cards.",
4262
4293
  inputSchema: {
@@ -5592,7 +5623,8 @@ async function handleToolCall(name, args, deps) {
5592
5623
  agentName,
5593
5624
  status: "working",
5594
5625
  currentTask: args.currentTask,
5595
- estimatedMinutesRemaining: args.estimatedMinutesRemaining
5626
+ estimatedMinutesRemaining: args.estimatedMinutesRemaining,
5627
+ steerable: args.steerable === true || args.steerable === "true" ? true : undefined
5596
5628
  });
5597
5629
  markExplicit(cardId, {
5598
5630
  agentIdentifier,
@@ -5631,6 +5663,7 @@ async function handleToolCall(name, args, deps) {
5631
5663
  } else if (callerRecentActions.length > 0) {
5632
5664
  mergedRecentActions = callerRecentActions;
5633
5665
  }
5666
+ const runActivity = (callerActions || []).map((a) => a.description).filter((d) => typeof d === "string" && d.length > 0);
5634
5667
  const result = await client3.updateAgentProgress(cardId, {
5635
5668
  agentIdentifier,
5636
5669
  agentName,
@@ -5639,7 +5672,8 @@ async function handleToolCall(name, args, deps) {
5639
5672
  currentTask: args.currentTask,
5640
5673
  blockers: args.blockers,
5641
5674
  estimatedMinutesRemaining: args.estimatedMinutesRemaining,
5642
- ...mergedRecentActions && { recentActions: mergedRecentActions }
5675
+ ...mergedRecentActions && { recentActions: mergedRecentActions },
5676
+ ...runActivity.length > 0 && { runActivity }
5643
5677
  });
5644
5678
  return { success: true, midSessionLearnings: 0, ...result };
5645
5679
  }
@@ -5695,6 +5729,13 @@ async function handleToolCall(name, args, deps) {
5695
5729
  });
5696
5730
  return { success: true, ...result };
5697
5731
  }
5732
+ case "harmony_get_pending_messages": {
5733
+ const cardId = z.string().uuid().parse(args.cardId);
5734
+ const sessionId = z.string().min(1).parse(args.sessionId);
5735
+ const sinceSeq = args.sinceSeq !== undefined ? z.number().int().min(0).parse(args.sinceSeq) : 0;
5736
+ const result = await client3.getPendingMessages(cardId, sessionId, sinceSeq);
5737
+ return { success: true, ...result };
5738
+ }
5698
5739
  case "harmony_generate_prompt": {
5699
5740
  let cardId;
5700
5741
  if (args.cardId) {
@@ -1361,6 +1361,12 @@ class HarmonyApiClient {
1361
1361
  async startAgentSession(cardId, data) {
1362
1362
  return this.request("POST", `/cards/${cardId}/agent-context`, data);
1363
1363
  }
1364
+ async getPendingMessages(cardId, sessionId, sinceSeq) {
1365
+ const params = new URLSearchParams;
1366
+ params.set("sessionId", sessionId);
1367
+ params.set("sinceSeq", String(sinceSeq));
1368
+ return this.request("GET", `/cards/${cardId}/agent-messages?${params.toString()}`);
1369
+ }
1364
1370
  async updateAgentProgress(cardId, data) {
1365
1371
  return this.request("POST", `/cards/${cardId}/agent-context`, data);
1366
1372
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethmy/mcp",
3
- "version": "2.10.0",
3
+ "version": "2.11.1",
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
@@ -792,6 +792,8 @@ export class HarmonyApiClient {
792
792
  agentIdentifier: string;
793
793
  agentName: string;
794
794
  agentId?: string | null;
795
+ /** Advertise that this session polls for queued steering messages (card #473). */
796
+ steerable?: boolean;
795
797
  status?: "working" | "blocked" | "paused" | "completed";
796
798
  progressPercent?: number;
797
799
  currentTask?: string;
@@ -802,6 +804,26 @@ export class HarmonyApiClient {
802
804
  return this.request("POST", `/cards/${cardId}/agent-context`, data);
803
805
  }
804
806
 
807
+ /**
808
+ * Drain queued `user_message` steering events for a session (card #473).
809
+ * Wraps the existing `GET /cards/{id}/agent-messages` endpoint that the daemon
810
+ * already polls — exposed so MCP/human sessions (e.g. /hmy) can consume steering
811
+ * at their checkpoints. Returns messages with `seq > sinceSeq`, oldest first.
812
+ */
813
+ async getPendingMessages(
814
+ cardId: string,
815
+ sessionId: string,
816
+ sinceSeq: number,
817
+ ): Promise<{ messages: unknown[] }> {
818
+ const params = new URLSearchParams();
819
+ params.set("sessionId", sessionId);
820
+ params.set("sinceSeq", String(sinceSeq));
821
+ return this.request(
822
+ "GET",
823
+ `/cards/${cardId}/agent-messages?${params.toString()}`,
824
+ );
825
+ }
826
+
805
827
  async updateAgentProgress(
806
828
  cardId: string,
807
829
  data: {
@@ -828,6 +850,8 @@ export class HarmonyApiClient {
828
850
  modelName?: string;
829
851
  numTurns?: number;
830
852
  recentActions?: { action: string; ts: string }[];
853
+ /** Per-call activity descriptions → one assistant_text timeline event each (card #470). */
854
+ runActivity?: string[];
831
855
  failureReason?:
832
856
  | "verification"
833
857
  | "review"
package/src/server.ts CHANGED
@@ -1002,6 +1002,11 @@ export const TOOLS = {
1002
1002
  description:
1003
1003
  'Label names to add to card (e.g., ["agent"]). Case-insensitive match.',
1004
1004
  },
1005
+ steerable: {
1006
+ type: "boolean",
1007
+ description:
1008
+ "Set true only if this session will poll harmony_get_pending_messages at its checkpoints. Enables the live steering composer for the run; leave unset/false if you won't consume steering messages.",
1009
+ },
1005
1010
  },
1006
1011
  required: ["cardId", "agentIdentifier", "agentName"],
1007
1012
  },
@@ -1097,6 +1102,30 @@ export const TOOLS = {
1097
1102
  required: ["cardId"],
1098
1103
  },
1099
1104
  },
1105
+ harmony_get_pending_messages: {
1106
+ description:
1107
+ "Drain queued steering messages a teammate sent to your live agent session (card #473). Call at your progress checkpoints with the session id from harmony_start_agent_session and the highest seq you've already consumed; returns user messages with seq > sinceSeq, oldest first. Fold them into your next step and advance sinceSeq to the largest returned seq so each is handled exactly once.",
1108
+ inputSchema: {
1109
+ type: "object",
1110
+ properties: {
1111
+ cardId: {
1112
+ type: "string",
1113
+ description: "Card ID with the live session",
1114
+ },
1115
+ sessionId: {
1116
+ type: "string",
1117
+ description:
1118
+ "Agent session id (the `session.id` returned by harmony_start_agent_session)",
1119
+ },
1120
+ sinceSeq: {
1121
+ type: "number",
1122
+ description:
1123
+ "Return only messages with seq greater than this (the last seq you consumed). Defaults to 0 to fetch all.",
1124
+ },
1125
+ },
1126
+ required: ["cardId", "sessionId"],
1127
+ },
1128
+ },
1100
1129
 
1101
1130
  // Prompt generation
1102
1131
  harmony_generate_prompt: {
@@ -2287,7 +2316,11 @@ async function handleToolCall(
2287
2316
  "Pass at most one of assigneeId / agentId — a card is assigned to a human OR an agent, never both.",
2288
2317
  );
2289
2318
  }
2290
- // The backend nulls the opposite field, so only send the side being set.
2319
+ // Send only the side being set. The MCP `agentId` arg maps to the
2320
+ // backend's `assignedAgentId` field (DB column `assigned_agent_id`);
2321
+ // `assigneeId` maps straight through. When one side is set to a non-null
2322
+ // value the backend clears the other, so we never send both — that keeps
2323
+ // a card from ending up assigned to a human AND an agent at once.
2291
2324
  const updates: {
2292
2325
  assigneeId?: string | null;
2293
2326
  assignedAgentId?: string | null;
@@ -2832,6 +2865,10 @@ async function handleToolCall(
2832
2865
  estimatedMinutesRemaining: args.estimatedMinutesRemaining as
2833
2866
  | number
2834
2867
  | undefined,
2868
+ steerable:
2869
+ args.steerable === true || args.steerable === "true"
2870
+ ? true
2871
+ : undefined,
2835
2872
  });
2836
2873
 
2837
2874
  // Mark as explicit so auto-session won't interfere
@@ -2892,6 +2929,13 @@ async function handleToolCall(
2892
2929
  mergedRecentActions = callerRecentActions;
2893
2930
  }
2894
2931
 
2932
+ // Per-call deltas for the run timeline. Sourced from THIS call's `actions`
2933
+ // only (not the cumulative `recentActions` cache), so each lands as exactly
2934
+ // one assistant_text event without duplication. (card #470)
2935
+ const runActivity = (callerActions || [])
2936
+ .map((a) => a.description)
2937
+ .filter((d): d is string => typeof d === "string" && d.length > 0);
2938
+
2895
2939
  const result = await client.updateAgentProgress(cardId, {
2896
2940
  agentIdentifier,
2897
2941
  agentName,
@@ -2908,6 +2952,7 @@ async function handleToolCall(
2908
2952
  | number
2909
2953
  | undefined,
2910
2954
  ...(mergedRecentActions && { recentActions: mergedRecentActions }),
2955
+ ...(runActivity.length > 0 && { runActivity }),
2911
2956
  });
2912
2957
 
2913
2958
  // Phase 0 (memory architecture v2): mid-session learning extraction removed.
@@ -3004,6 +3049,21 @@ async function handleToolCall(
3004
3049
  return { success: true, ...result };
3005
3050
  }
3006
3051
 
3052
+ case "harmony_get_pending_messages": {
3053
+ const cardId = z.string().uuid().parse(args.cardId);
3054
+ const sessionId = z.string().min(1).parse(args.sessionId);
3055
+ const sinceSeq =
3056
+ args.sinceSeq !== undefined
3057
+ ? z.number().int().min(0).parse(args.sinceSeq)
3058
+ : 0;
3059
+ const result = await client.getPendingMessages(
3060
+ cardId,
3061
+ sessionId,
3062
+ sinceSeq,
3063
+ );
3064
+ return { success: true, ...result };
3065
+ }
3066
+
3007
3067
  // Prompt generation
3008
3068
  // TODO Phase 1: rebuild full context assembly per docs/superpowers/plans/2026-05-07-memory-architecture-v2.md §10
3009
3069
  case "harmony_generate_prompt": {