@gethmy/mcp 2.9.9 → 2.11.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,15 +1919,9 @@ 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}`);
1930
- }
1931
1925
  async getPendingUserMessages(cardId, sessionId, sinceSeq) {
1932
1926
  return this.request("GET", `/cards/${cardId}/agent-messages?sessionId=${sessionId}&sinceSeq=${sinceSeq}`);
1933
1927
  }
@@ -3646,7 +3640,7 @@ var TOOLS = {
3646
3640
  }
3647
3641
  },
3648
3642
  harmony_assign_card: {
3649
- 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.",
3650
3644
  inputSchema: {
3651
3645
  type: "object",
3652
3646
  properties: {
@@ -3654,7 +3648,12 @@ var TOOLS = {
3654
3648
  assigneeId: {
3655
3649
  type: "string",
3656
3650
  nullable: true,
3657
- 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."
3658
3657
  }
3659
3658
  },
3660
3659
  required: ["cardId"]
@@ -4083,6 +4082,18 @@ var TOOLS = {
4083
4082
  }
4084
4083
  }
4085
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
+ },
4086
4097
  harmony_set_workspace_context: {
4087
4098
  description: "Set the active workspace context for subsequent operations",
4088
4099
  inputSchema: {
@@ -5203,8 +5214,16 @@ async function handleToolCall(name, args, deps) {
5203
5214
  }
5204
5215
  case "harmony_assign_card": {
5205
5216
  const cardId = z.string().uuid().parse(args.cardId);
5206
- const assigneeId = args.assigneeId ? z.string().uuid().parse(args.assigneeId) : null;
5207
- 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);
5208
5227
  return { success: true, ...result };
5209
5228
  }
5210
5229
  case "harmony_search_cards": {
@@ -5479,6 +5498,11 @@ async function handleToolCall(name, args, deps) {
5479
5498
  const result = await client3.getWorkspaceMembers(workspaceId);
5480
5499
  return { success: true, ...result };
5481
5500
  }
5501
+ case "harmony_list_agents": {
5502
+ const workspaceId = getWorkspaceId();
5503
+ const result = await client3.listWorkspaceAgents(workspaceId);
5504
+ return { success: true, ...result };
5505
+ }
5482
5506
  case "harmony_set_workspace_context": {
5483
5507
  const workspaceId = z.string().uuid().parse(args.workspaceId);
5484
5508
  deps.setActiveWorkspace(workspaceId);
@@ -7386,6 +7410,7 @@ var SAFE_HARMONY_TOOLS = [
7386
7410
  "harmony_list_plans",
7387
7411
  "harmony_get_agent_session",
7388
7412
  "harmony_get_workspace_members",
7413
+ "harmony_list_agents",
7389
7414
  "harmony_resolve_links",
7390
7415
  "harmony_recall",
7391
7416
  "harmony_memory_search",
package/dist/index.js CHANGED
@@ -1914,15 +1914,9 @@ 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}`);
1925
- }
1926
1920
  async getPendingUserMessages(cardId, sessionId, sinceSeq) {
1927
1921
  return this.request("GET", `/cards/${cardId}/agent-messages?sessionId=${sessionId}&sinceSeq=${sinceSeq}`);
1928
1922
  }
@@ -3641,7 +3635,7 @@ var TOOLS = {
3641
3635
  }
3642
3636
  },
3643
3637
  harmony_assign_card: {
3644
- 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.",
3645
3639
  inputSchema: {
3646
3640
  type: "object",
3647
3641
  properties: {
@@ -3649,7 +3643,12 @@ var TOOLS = {
3649
3643
  assigneeId: {
3650
3644
  type: "string",
3651
3645
  nullable: true,
3652
- 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."
3653
3652
  }
3654
3653
  },
3655
3654
  required: ["cardId"]
@@ -4078,6 +4077,18 @@ var TOOLS = {
4078
4077
  }
4079
4078
  }
4080
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
+ },
4081
4092
  harmony_set_workspace_context: {
4082
4093
  description: "Set the active workspace context for subsequent operations",
4083
4094
  inputSchema: {
@@ -5198,8 +5209,16 @@ async function handleToolCall(name, args, deps) {
5198
5209
  }
5199
5210
  case "harmony_assign_card": {
5200
5211
  const cardId = z.string().uuid().parse(args.cardId);
5201
- const assigneeId = args.assigneeId ? z.string().uuid().parse(args.assigneeId) : null;
5202
- 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);
5203
5222
  return { success: true, ...result };
5204
5223
  }
5205
5224
  case "harmony_search_cards": {
@@ -5474,6 +5493,11 @@ async function handleToolCall(name, args, deps) {
5474
5493
  const result = await client3.getWorkspaceMembers(workspaceId);
5475
5494
  return { success: true, ...result };
5476
5495
  }
5496
+ case "harmony_list_agents": {
5497
+ const workspaceId = getWorkspaceId();
5498
+ const result = await client3.listWorkspaceAgents(workspaceId);
5499
+ return { success: true, ...result };
5500
+ }
5477
5501
  case "harmony_set_workspace_context": {
5478
5502
  const workspaceId = z.string().uuid().parse(args.workspaceId);
5479
5503
  deps.setActiveWorkspace(workspaceId);
@@ -1367,15 +1367,9 @@ 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}`);
1378
- }
1379
1373
  async getPendingUserMessages(cardId, sessionId, sinceSeq) {
1380
1374
  return this.request("GET", `/cards/${cardId}/agent-messages?sessionId=${sessionId}&sinceSeq=${sinceSeq}`);
1381
1375
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethmy/mcp",
3
- "version": "2.9.9",
3
+ "version": "2.11.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,26 +896,6 @@ export class HarmonyApiClient {
913
896
  return this.request("POST", `/cards/${cardId}/agent-run-events`, data);
914
897
  }
915
898
 
916
- async getActivityLog(
917
- cardId: string,
918
- sessionId: string,
919
- ): Promise<{
920
- entries: {
921
- id: string;
922
- phase: string | null;
923
- eventType: string;
924
- toolName: string | null;
925
- description: string;
926
- metadata: Record<string, unknown>;
927
- createdAt: string;
928
- }[];
929
- }> {
930
- return this.request(
931
- "GET",
932
- `/cards/${cardId}/agent-activity-log?sessionId=${sessionId}`,
933
- );
934
- }
935
-
936
899
  /**
937
900
  * Drain queued chat-steering messages for a run (card #417). Returns `user_message`
938
901
  * events with `seq` greater than `sinceSeq`, oldest first, so the daemon can continue
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,30 @@ 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
+ // Send only the side being set. The MCP `agentId` arg maps to the
2291
+ // backend's `assignedAgentId` field (DB column `assigned_agent_id`);
2292
+ // `assigneeId` maps straight through. When one side is set to a non-null
2293
+ // value the backend clears the other, so we never send both — that keeps
2294
+ // a card from ending up assigned to a human AND an agent at once.
2295
+ const updates: {
2296
+ assigneeId?: string | null;
2297
+ assignedAgentId?: string | null;
2298
+ } = {};
2299
+ if (args.agentId !== undefined) {
2300
+ updates.assignedAgentId = args.agentId
2301
+ ? z.string().uuid().parse(args.agentId)
2302
+ : null;
2303
+ } else {
2304
+ updates.assigneeId = args.assigneeId
2305
+ ? z.string().uuid().parse(args.assigneeId)
2306
+ : null;
2307
+ }
2308
+ const result = await client.updateCard(cardId, updates);
2268
2309
  return { success: true, ...result };
2269
2310
  }
2270
2311
 
@@ -2652,6 +2693,12 @@ async function handleToolCall(
2652
2693
  return { success: true, ...result };
2653
2694
  }
2654
2695
 
2696
+ case "harmony_list_agents": {
2697
+ const workspaceId = getWorkspaceId();
2698
+ const result = await client.listWorkspaceAgents(workspaceId);
2699
+ return { success: true, ...result };
2700
+ }
2701
+
2655
2702
  case "harmony_set_workspace_context": {
2656
2703
  const workspaceId = z.string().uuid().parse(args.workspaceId);
2657
2704
  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",