@gethmy/mcp 2.9.8 → 2.10.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/README.md CHANGED
@@ -5,7 +5,7 @@ Enables AI coding agents (Claude Code, OpenAI Codex, Cursor) to interact with yo
5
5
 
6
6
  ## Features
7
7
 
8
- - **70 MCP Tools** for full board control, knowledge graph, and workflow plans
8
+ - **71 MCP Tools** for full board control, knowledge graph, and workflow plans
9
9
  - **7 Global Skills** — `/hmy`, `/hmy-plan`, `/hmy-cleanup`, `/hmy-standup`, `/hmy-memory-prune`, `/hmy-upgrade`, `/hmy-review`, served from the DB-backed [skill hub](../../docs/skills.md) with auto-update and admin-managed versioning
10
10
  - **Knowledge Graph Memory** — Phase 1 surface: hybrid retrieval (vector + lexical + RRF), session-scoped working memory, activity feed. See [docs/memory.md](../../docs/memory.md)
11
11
  - **GSD Workflow Plans** - plan/execute/verify/done lifecycle with auto card creation
@@ -255,6 +255,7 @@ Every skill ships with an `hmy-update-check` preamble that fires on invocation.
255
255
  - `harmony_list_projects` - List projects
256
256
  - `harmony_get_board` - Get board state (supports pagination via `limit`/`offset`, `summary` mode, `columnId` filter, `includeArchived`)
257
257
  - `harmony_get_workspace_members` - Get team members
258
+ - `harmony_list_agents` - List a workspace's virtual agents (daemons); use the id with `harmony_assign_card` (`agentId`)
258
259
  - `harmony_set_workspace_context` - Set active workspace
259
260
  - `harmony_set_project_context` - Set active project
260
261
  - `harmony_get_context` - Get current context
package/dist/cli.js CHANGED
@@ -1919,14 +1919,11 @@ class HarmonyApiClient {
1919
1919
  async endAgentSession(cardId, data) {
1920
1920
  return this.request("DELETE", `/cards/${cardId}/agent-context`, data);
1921
1921
  }
1922
- async flushActivityLog(cardId, data) {
1923
- return this.request("POST", `/cards/${cardId}/agent-activity-log`, data);
1924
- }
1925
1922
  async appendAgentRunEvents(cardId, data) {
1926
1923
  return this.request("POST", `/cards/${cardId}/agent-run-events`, data);
1927
1924
  }
1928
- async getActivityLog(cardId, sessionId) {
1929
- return this.request("GET", `/cards/${cardId}/agent-activity-log?sessionId=${sessionId}`);
1925
+ async getPendingUserMessages(cardId, sessionId, sinceSeq) {
1926
+ return this.request("GET", `/cards/${cardId}/agent-messages?sessionId=${sessionId}&sinceSeq=${sinceSeq}`);
1930
1927
  }
1931
1928
  async getAgentSession(cardId, options) {
1932
1929
  const params = new URLSearchParams;
@@ -3643,7 +3640,7 @@ var TOOLS = {
3643
3640
  }
3644
3641
  },
3645
3642
  harmony_assign_card: {
3646
- description: "Assign a card to a team member",
3643
+ description: "Assign a card to a team member (human) or a virtual agent (e.g. the autonomous agent daemon). Pass at most one of assigneeId / agentId — a card is assigned to a human OR an agent, never both. Use harmony_list_agents to find a virtual agent's id.",
3647
3644
  inputSchema: {
3648
3645
  type: "object",
3649
3646
  properties: {
@@ -3651,7 +3648,12 @@ var TOOLS = {
3651
3648
  assigneeId: {
3652
3649
  type: "string",
3653
3650
  nullable: true,
3654
- description: "User ID (null to unassign)"
3651
+ description: "Human user ID (null to unassign). Mutually exclusive with agentId."
3652
+ },
3653
+ agentId: {
3654
+ type: "string",
3655
+ nullable: true,
3656
+ description: "Virtual agent ID from harmony_list_agents (null to unassign). Setting it hands the card to the agent daemon. Mutually exclusive with assigneeId."
3655
3657
  }
3656
3658
  },
3657
3659
  required: ["cardId"]
@@ -4080,6 +4082,18 @@ var TOOLS = {
4080
4082
  }
4081
4083
  }
4082
4084
  },
4085
+ harmony_list_agents: {
4086
+ description: "List a workspace's virtual agents (autonomous agent daemons registered to the board). Returns each agent's id, identifier, name and last-seen time. Use the returned id with harmony_assign_card (agentId) to hand a card to the daemon.",
4087
+ inputSchema: {
4088
+ type: "object",
4089
+ properties: {
4090
+ workspaceId: {
4091
+ type: "string",
4092
+ description: "Workspace ID (optional if context set)"
4093
+ }
4094
+ }
4095
+ }
4096
+ },
4083
4097
  harmony_set_workspace_context: {
4084
4098
  description: "Set the active workspace context for subsequent operations",
4085
4099
  inputSchema: {
@@ -5200,8 +5214,16 @@ async function handleToolCall(name, args, deps) {
5200
5214
  }
5201
5215
  case "harmony_assign_card": {
5202
5216
  const cardId = z.string().uuid().parse(args.cardId);
5203
- const assigneeId = args.assigneeId ? z.string().uuid().parse(args.assigneeId) : null;
5204
- const result = await client3.updateCard(cardId, { assigneeId });
5217
+ if (args.assigneeId != null && args.agentId != null) {
5218
+ throw new Error("Pass at most one of assigneeId / agentId — a card is assigned to a human OR an agent, never both.");
5219
+ }
5220
+ const updates = {};
5221
+ if (args.agentId !== undefined) {
5222
+ updates.assignedAgentId = args.agentId ? z.string().uuid().parse(args.agentId) : null;
5223
+ } else {
5224
+ updates.assigneeId = args.assigneeId ? z.string().uuid().parse(args.assigneeId) : null;
5225
+ }
5226
+ const result = await client3.updateCard(cardId, updates);
5205
5227
  return { success: true, ...result };
5206
5228
  }
5207
5229
  case "harmony_search_cards": {
@@ -5476,6 +5498,11 @@ async function handleToolCall(name, args, deps) {
5476
5498
  const result = await client3.getWorkspaceMembers(workspaceId);
5477
5499
  return { success: true, ...result };
5478
5500
  }
5501
+ case "harmony_list_agents": {
5502
+ const workspaceId = getWorkspaceId();
5503
+ const result = await client3.listWorkspaceAgents(workspaceId);
5504
+ return { success: true, ...result };
5505
+ }
5479
5506
  case "harmony_set_workspace_context": {
5480
5507
  const workspaceId = z.string().uuid().parse(args.workspaceId);
5481
5508
  deps.setActiveWorkspace(workspaceId);
@@ -7383,6 +7410,7 @@ var SAFE_HARMONY_TOOLS = [
7383
7410
  "harmony_list_plans",
7384
7411
  "harmony_get_agent_session",
7385
7412
  "harmony_get_workspace_members",
7413
+ "harmony_list_agents",
7386
7414
  "harmony_resolve_links",
7387
7415
  "harmony_recall",
7388
7416
  "harmony_memory_search",
package/dist/index.js CHANGED
@@ -1914,14 +1914,11 @@ class HarmonyApiClient {
1914
1914
  async endAgentSession(cardId, data) {
1915
1915
  return this.request("DELETE", `/cards/${cardId}/agent-context`, data);
1916
1916
  }
1917
- async flushActivityLog(cardId, data) {
1918
- return this.request("POST", `/cards/${cardId}/agent-activity-log`, data);
1919
- }
1920
1917
  async appendAgentRunEvents(cardId, data) {
1921
1918
  return this.request("POST", `/cards/${cardId}/agent-run-events`, data);
1922
1919
  }
1923
- async getActivityLog(cardId, sessionId) {
1924
- return this.request("GET", `/cards/${cardId}/agent-activity-log?sessionId=${sessionId}`);
1920
+ async getPendingUserMessages(cardId, sessionId, sinceSeq) {
1921
+ return this.request("GET", `/cards/${cardId}/agent-messages?sessionId=${sessionId}&sinceSeq=${sinceSeq}`);
1925
1922
  }
1926
1923
  async getAgentSession(cardId, options) {
1927
1924
  const params = new URLSearchParams;
@@ -3638,7 +3635,7 @@ var TOOLS = {
3638
3635
  }
3639
3636
  },
3640
3637
  harmony_assign_card: {
3641
- description: "Assign a card to a team member",
3638
+ description: "Assign a card to a team member (human) or a virtual agent (e.g. the autonomous agent daemon). Pass at most one of assigneeId / agentId — a card is assigned to a human OR an agent, never both. Use harmony_list_agents to find a virtual agent's id.",
3642
3639
  inputSchema: {
3643
3640
  type: "object",
3644
3641
  properties: {
@@ -3646,7 +3643,12 @@ var TOOLS = {
3646
3643
  assigneeId: {
3647
3644
  type: "string",
3648
3645
  nullable: true,
3649
- description: "User ID (null to unassign)"
3646
+ description: "Human user ID (null to unassign). Mutually exclusive with agentId."
3647
+ },
3648
+ agentId: {
3649
+ type: "string",
3650
+ nullable: true,
3651
+ description: "Virtual agent ID from harmony_list_agents (null to unassign). Setting it hands the card to the agent daemon. Mutually exclusive with assigneeId."
3650
3652
  }
3651
3653
  },
3652
3654
  required: ["cardId"]
@@ -4075,6 +4077,18 @@ var TOOLS = {
4075
4077
  }
4076
4078
  }
4077
4079
  },
4080
+ harmony_list_agents: {
4081
+ description: "List a workspace's virtual agents (autonomous agent daemons registered to the board). Returns each agent's id, identifier, name and last-seen time. Use the returned id with harmony_assign_card (agentId) to hand a card to the daemon.",
4082
+ inputSchema: {
4083
+ type: "object",
4084
+ properties: {
4085
+ workspaceId: {
4086
+ type: "string",
4087
+ description: "Workspace ID (optional if context set)"
4088
+ }
4089
+ }
4090
+ }
4091
+ },
4078
4092
  harmony_set_workspace_context: {
4079
4093
  description: "Set the active workspace context for subsequent operations",
4080
4094
  inputSchema: {
@@ -5195,8 +5209,16 @@ async function handleToolCall(name, args, deps) {
5195
5209
  }
5196
5210
  case "harmony_assign_card": {
5197
5211
  const cardId = z.string().uuid().parse(args.cardId);
5198
- const assigneeId = args.assigneeId ? z.string().uuid().parse(args.assigneeId) : null;
5199
- const result = await client3.updateCard(cardId, { assigneeId });
5212
+ if (args.assigneeId != null && args.agentId != null) {
5213
+ throw new Error("Pass at most one of assigneeId / agentId — a card is assigned to a human OR an agent, never both.");
5214
+ }
5215
+ const updates = {};
5216
+ if (args.agentId !== undefined) {
5217
+ updates.assignedAgentId = args.agentId ? z.string().uuid().parse(args.agentId) : null;
5218
+ } else {
5219
+ updates.assigneeId = args.assigneeId ? z.string().uuid().parse(args.assigneeId) : null;
5220
+ }
5221
+ const result = await client3.updateCard(cardId, updates);
5200
5222
  return { success: true, ...result };
5201
5223
  }
5202
5224
  case "harmony_search_cards": {
@@ -5471,6 +5493,11 @@ async function handleToolCall(name, args, deps) {
5471
5493
  const result = await client3.getWorkspaceMembers(workspaceId);
5472
5494
  return { success: true, ...result };
5473
5495
  }
5496
+ case "harmony_list_agents": {
5497
+ const workspaceId = getWorkspaceId();
5498
+ const result = await client3.listWorkspaceAgents(workspaceId);
5499
+ return { success: true, ...result };
5500
+ }
5474
5501
  case "harmony_set_workspace_context": {
5475
5502
  const workspaceId = z.string().uuid().parse(args.workspaceId);
5476
5503
  deps.setActiveWorkspace(workspaceId);
@@ -1367,14 +1367,11 @@ class HarmonyApiClient {
1367
1367
  async endAgentSession(cardId, data) {
1368
1368
  return this.request("DELETE", `/cards/${cardId}/agent-context`, data);
1369
1369
  }
1370
- async flushActivityLog(cardId, data) {
1371
- return this.request("POST", `/cards/${cardId}/agent-activity-log`, data);
1372
- }
1373
1370
  async appendAgentRunEvents(cardId, data) {
1374
1371
  return this.request("POST", `/cards/${cardId}/agent-run-events`, data);
1375
1372
  }
1376
- async getActivityLog(cardId, sessionId) {
1377
- return this.request("GET", `/cards/${cardId}/agent-activity-log?sessionId=${sessionId}`);
1373
+ async getPendingUserMessages(cardId, sessionId, sinceSeq) {
1374
+ return this.request("GET", `/cards/${cardId}/agent-messages?sessionId=${sessionId}&sinceSeq=${sinceSeq}`);
1378
1375
  }
1379
1376
  async getAgentSession(cardId, options) {
1380
1377
  const params = new URLSearchParams;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethmy/mcp",
3
- "version": "2.9.8",
3
+ "version": "2.10.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
@@ -882,23 +882,6 @@ export class HarmonyApiClient {
882
882
  return this.request("DELETE", `/cards/${cardId}/agent-context`, data);
883
883
  }
884
884
 
885
- async flushActivityLog(
886
- cardId: string,
887
- data: {
888
- sessionId: string;
889
- entries: {
890
- phase?: string;
891
- eventType: string;
892
- toolName?: string;
893
- description: string;
894
- metadata?: Record<string, unknown>;
895
- createdAt?: string;
896
- }[];
897
- },
898
- ): Promise<{ inserted: number }> {
899
- return this.request("POST", `/cards/${cardId}/agent-activity-log`, data);
900
- }
901
-
902
885
  /**
903
886
  * Append events to a run's agent_run_events stream (card #417). Send drafts in
904
887
  * chronological order — the server's seq trigger assigns the monotonic per-run order.
@@ -913,23 +896,28 @@ export class HarmonyApiClient {
913
896
  return this.request("POST", `/cards/${cardId}/agent-run-events`, data);
914
897
  }
915
898
 
916
- async getActivityLog(
899
+ /**
900
+ * Drain queued chat-steering messages for a run (card #417). Returns `user_message`
901
+ * events with `seq` greater than `sinceSeq`, oldest first, so the daemon can continue
902
+ * the run via `claude --resume` at a turn boundary.
903
+ */
904
+ async getPendingUserMessages(
917
905
  cardId: string,
918
906
  sessionId: string,
907
+ sinceSeq: number,
919
908
  ): Promise<{
920
- entries: {
909
+ messages: {
921
910
  id: string;
922
- phase: string | null;
923
- eventType: string;
924
- toolName: string | null;
925
- description: string;
926
- metadata: Record<string, unknown>;
911
+ seq: number;
912
+ text: string;
913
+ authorName?: string;
914
+ authorUserId?: string;
927
915
  createdAt: string;
928
916
  }[];
929
917
  }> {
930
918
  return this.request(
931
919
  "GET",
932
- `/cards/${cardId}/agent-activity-log?sessionId=${sessionId}`,
920
+ `/cards/${cardId}/agent-messages?sessionId=${sessionId}&sinceSeq=${sinceSeq}`,
933
921
  );
934
922
  }
935
923
 
package/src/server.ts CHANGED
@@ -417,7 +417,8 @@ export const TOOLS = {
417
417
  },
418
418
  },
419
419
  harmony_assign_card: {
420
- description: "Assign a card to a team member",
420
+ description:
421
+ "Assign a card to a team member (human) or a virtual agent (e.g. the autonomous agent daemon). Pass at most one of assigneeId / agentId — a card is assigned to a human OR an agent, never both. Use harmony_list_agents to find a virtual agent's id.",
421
422
  inputSchema: {
422
423
  type: "object",
423
424
  properties: {
@@ -425,7 +426,14 @@ export const TOOLS = {
425
426
  assigneeId: {
426
427
  type: "string",
427
428
  nullable: true,
428
- description: "User ID (null to unassign)",
429
+ description:
430
+ "Human user ID (null to unassign). Mutually exclusive with agentId.",
431
+ },
432
+ agentId: {
433
+ type: "string",
434
+ nullable: true,
435
+ description:
436
+ "Virtual agent ID from harmony_list_agents (null to unassign). Setting it hands the card to the agent daemon. Mutually exclusive with assigneeId.",
429
437
  },
430
438
  },
431
439
  required: ["cardId"],
@@ -894,6 +902,19 @@ export const TOOLS = {
894
902
  },
895
903
  },
896
904
  },
905
+ harmony_list_agents: {
906
+ description:
907
+ "List a workspace's virtual agents (autonomous agent daemons registered to the board). Returns each agent's id, identifier, name and last-seen time. Use the returned id with harmony_assign_card (agentId) to hand a card to the daemon.",
908
+ inputSchema: {
909
+ type: "object",
910
+ properties: {
911
+ workspaceId: {
912
+ type: "string",
913
+ description: "Workspace ID (optional if context set)",
914
+ },
915
+ },
916
+ },
917
+ },
897
918
  harmony_set_workspace_context: {
898
919
  description: "Set the active workspace context for subsequent operations",
899
920
  inputSchema: {
@@ -2261,10 +2282,26 @@ async function handleToolCall(
2261
2282
 
2262
2283
  case "harmony_assign_card": {
2263
2284
  const cardId = z.string().uuid().parse(args.cardId);
2264
- const assigneeId = args.assigneeId
2265
- ? z.string().uuid().parse(args.assigneeId)
2266
- : null;
2267
- const result = await client.updateCard(cardId, { assigneeId });
2285
+ if (args.assigneeId != null && args.agentId != null) {
2286
+ throw new Error(
2287
+ "Pass at most one of assigneeId / agentId — a card is assigned to a human OR an agent, never both.",
2288
+ );
2289
+ }
2290
+ // The backend nulls the opposite field, so only send the side being set.
2291
+ const updates: {
2292
+ assigneeId?: string | null;
2293
+ assignedAgentId?: string | null;
2294
+ } = {};
2295
+ if (args.agentId !== undefined) {
2296
+ updates.assignedAgentId = args.agentId
2297
+ ? z.string().uuid().parse(args.agentId)
2298
+ : null;
2299
+ } else {
2300
+ updates.assigneeId = args.assigneeId
2301
+ ? z.string().uuid().parse(args.assigneeId)
2302
+ : null;
2303
+ }
2304
+ const result = await client.updateCard(cardId, updates);
2268
2305
  return { success: true, ...result };
2269
2306
  }
2270
2307
 
@@ -2652,6 +2689,12 @@ async function handleToolCall(
2652
2689
  return { success: true, ...result };
2653
2690
  }
2654
2691
 
2692
+ case "harmony_list_agents": {
2693
+ const workspaceId = getWorkspaceId();
2694
+ const result = await client.listWorkspaceAgents(workspaceId);
2695
+ return { success: true, ...result };
2696
+ }
2697
+
2655
2698
  case "harmony_set_workspace_context": {
2656
2699
  const workspaceId = z.string().uuid().parse(args.workspaceId);
2657
2700
  deps.setActiveWorkspace(workspaceId);
package/src/tui/setup.ts CHANGED
@@ -74,6 +74,7 @@ const SAFE_HARMONY_TOOLS = [
74
74
  "harmony_list_plans",
75
75
  "harmony_get_agent_session",
76
76
  "harmony_get_workspace_members",
77
+ "harmony_list_agents",
77
78
  "harmony_resolve_links",
78
79
  "harmony_recall",
79
80
  "harmony_memory_search",