@gethmy/mcp 2.9.7 → 2.9.9

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,8 +5,8 @@ Enables AI coding agents (Claude Code, OpenAI Codex, Cursor) to interact with yo
5
5
 
6
6
  ## Features
7
7
 
8
- - **69 MCP Tools** for full board control, knowledge graph, and workflow plans
9
- - **5 Global Skills** — `/hmy`, `/hmy-plan`, `/hmy-cleanup`, `/hmy-standup`, `/hmy-memory-prune`, served from the DB-backed [skill hub](../../docs/skills.md) with auto-update and admin-managed versioning
8
+ - **70 MCP Tools** for full board control, knowledge graph, and workflow plans
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
12
12
  - **Card Linking** - create relationships between cards (blocks, relates_to, duplicates, is_part_of)
@@ -89,7 +89,7 @@ If you prefer to configure manually (e.g., in Claude.ai's UI):
89
89
  1. Get an API key from [Harmony](https://gethmy.com/user/keys)
90
90
  2. In Claude.ai, add a remote MCP server with URL `https://mcp.gethmy.com/mcp`
91
91
  3. Set the Authorization header to `Bearer hmy_your_key_here`
92
- 4. All 56 Harmony tools become available in your conversation
92
+ 4. All 70 Harmony tools become available in your conversation
93
93
 
94
94
  **Session management** is automatic - sessions have a 1-hour TTL and are created/renewed transparently.
95
95
 
@@ -150,7 +150,7 @@ npx @gethmy/mcp serve # Start MCP server
150
150
 
151
151
  ## Skills
152
152
 
153
- Five global skills ship with the MCP server and are installed automatically by `npx @gethmy/mcp setup`. They live in the `skill_resource` Postgres table, are fetched via `GET /v1/skills/<name>`, and render-time composed with a shared auto-update preamble.
153
+ Seven global skills ship with the MCP server and are installed automatically by `npx @gethmy/mcp setup`. They live in the `skill_resource` Postgres table, are fetched via `GET /v1/skills/<name>`, and render-time composed with a shared auto-update preamble.
154
154
 
155
155
  For the full skill hub architecture (storage, versioning, auto-update, admin management), see [docs/skills.md](../../docs/skills.md).
156
156
 
package/dist/cli.js CHANGED
@@ -1385,7 +1385,6 @@ import {
1385
1385
  ReadResourceRequestSchema
1386
1386
  } from "@modelcontextprotocol/sdk/types.js";
1387
1387
  import { z } from "zod";
1388
-
1389
1388
  // ../harmony-shared/dist/cardLinks.js
1390
1389
  var LINK_TYPE_INVERSES = {
1391
1390
  relates_to: "relates_to",
@@ -1859,6 +1858,9 @@ class HarmonyApiClient {
1859
1858
  async getCardExternalLinks(cardId) {
1860
1859
  return this.request("GET", `/cards/${cardId}/external-links`);
1861
1860
  }
1861
+ async classifyCard(cardId) {
1862
+ return this.request("POST", `/cards/${cardId}/classify`);
1863
+ }
1862
1864
  async createColumn(projectId, name) {
1863
1865
  return this.request("POST", "/columns", { projectId, name });
1864
1866
  }
@@ -1920,9 +1922,15 @@ class HarmonyApiClient {
1920
1922
  async flushActivityLog(cardId, data) {
1921
1923
  return this.request("POST", `/cards/${cardId}/agent-activity-log`, data);
1922
1924
  }
1925
+ async appendAgentRunEvents(cardId, data) {
1926
+ return this.request("POST", `/cards/${cardId}/agent-run-events`, data);
1927
+ }
1923
1928
  async getActivityLog(cardId, sessionId) {
1924
1929
  return this.request("GET", `/cards/${cardId}/agent-activity-log?sessionId=${sessionId}`);
1925
1930
  }
1931
+ async getPendingUserMessages(cardId, sessionId, sinceSeq) {
1932
+ return this.request("GET", `/cards/${cardId}/agent-messages?sessionId=${sessionId}&sinceSeq=${sinceSeq}`);
1933
+ }
1926
1934
  async getAgentSession(cardId, options) {
1927
1935
  const params = new URLSearchParams;
1928
1936
  if (options?.includeEnded)
@@ -3882,6 +3890,16 @@ var TOOLS = {
3882
3890
  required: ["cardId"]
3883
3891
  }
3884
3892
  },
3893
+ harmony_classify_card: {
3894
+ description: "Classify a card with the LLM classifier: sets `intent` (plan/think/implement/review), `complexity_score` (0-10), and `model_tier` (simple/advanced/research), stamps `classified_at`, and applies the canonical type label (feature/bug/idea). Use this right after creating a card (e.g. in the `hmy-new` flow) so it's classified in-flow instead of waiting for it to surface on the web board. Idempotent — safe to re-run. Never touches the user-owned `model_override`.",
3895
+ inputSchema: {
3896
+ type: "object",
3897
+ properties: {
3898
+ cardId: { type: "string", description: "Card UUID" }
3899
+ },
3900
+ required: ["cardId"]
3901
+ }
3902
+ },
3885
3903
  harmony_get_card_external_links: {
3886
3904
  description: "Get external URL references attached to a card (links to docs, gists, dashboards, etc.).",
3887
3905
  inputSchema: {
@@ -5349,6 +5367,11 @@ async function handleToolCall(name, args, deps) {
5349
5367
  const result = await client3.getCardExternalLinks(cardId);
5350
5368
  return result;
5351
5369
  }
5370
+ case "harmony_classify_card": {
5371
+ const cardId = z.string().uuid().parse(args.cardId);
5372
+ const result = await client3.classifyCard(cardId);
5373
+ return result;
5374
+ }
5352
5375
  case "harmony_create_subtask": {
5353
5376
  const cardId = z.string().uuid().parse(args.cardId);
5354
5377
  const title = z.string().min(1).max(500).parse(args.title);
@@ -7821,12 +7844,13 @@ only for unattended CI where you accept that risk.`));
7821
7844
  options: [
7822
7845
  {
7823
7846
  value: "browser",
7824
- label: "Sign in with your browser",
7825
- hint: "recommended — secure, no key handling"
7847
+ label: "Sign in or create an account in your browser",
7848
+ hint: "recommended — new or existing users, no key handling"
7826
7849
  },
7827
7850
  {
7828
7851
  value: "create",
7829
- label: "Create a free account"
7852
+ label: "Create an account here in the terminal",
7853
+ hint: "no browser"
7830
7854
  },
7831
7855
  {
7832
7856
  value: "apikey",
@@ -8378,6 +8402,11 @@ Specify the workspace with --workspace <id>, or select one below.`);
8378
8402
  console.log(` ${colors.brand("Cursor:")} MCP tools available automatically`);
8379
8403
  }
8380
8404
  console.log("");
8405
+ console.log(` ${colors.bold("Next steps:")}`);
8406
+ console.log(` 1. Open Claude Code and say: ${colors.highlight('"Show me my board"')}`);
8407
+ console.log(` 2. Create a card: ${colors.highlight('"Create a card called Auth token refresh"')}`);
8408
+ console.log(` 3. Start the daemon: ${colors.highlight("npx @gethmy/agent")}`);
8409
+ console.log("");
8381
8410
  console.log(` ${colors.dim("Add to new project: npx @gethmy/mcp setup")}`);
8382
8411
  console.log(` ${colors.dim("Need help? Visit https://app.gethmy.com/docs/mcp")}`);
8383
8412
  }
package/dist/index.js CHANGED
@@ -1380,7 +1380,6 @@ import {
1380
1380
  ReadResourceRequestSchema
1381
1381
  } from "@modelcontextprotocol/sdk/types.js";
1382
1382
  import { z } from "zod";
1383
-
1384
1383
  // ../harmony-shared/dist/cardLinks.js
1385
1384
  var LINK_TYPE_INVERSES = {
1386
1385
  relates_to: "relates_to",
@@ -1854,6 +1853,9 @@ class HarmonyApiClient {
1854
1853
  async getCardExternalLinks(cardId) {
1855
1854
  return this.request("GET", `/cards/${cardId}/external-links`);
1856
1855
  }
1856
+ async classifyCard(cardId) {
1857
+ return this.request("POST", `/cards/${cardId}/classify`);
1858
+ }
1857
1859
  async createColumn(projectId, name) {
1858
1860
  return this.request("POST", "/columns", { projectId, name });
1859
1861
  }
@@ -1915,9 +1917,15 @@ class HarmonyApiClient {
1915
1917
  async flushActivityLog(cardId, data) {
1916
1918
  return this.request("POST", `/cards/${cardId}/agent-activity-log`, data);
1917
1919
  }
1920
+ async appendAgentRunEvents(cardId, data) {
1921
+ return this.request("POST", `/cards/${cardId}/agent-run-events`, data);
1922
+ }
1918
1923
  async getActivityLog(cardId, sessionId) {
1919
1924
  return this.request("GET", `/cards/${cardId}/agent-activity-log?sessionId=${sessionId}`);
1920
1925
  }
1926
+ async getPendingUserMessages(cardId, sessionId, sinceSeq) {
1927
+ return this.request("GET", `/cards/${cardId}/agent-messages?sessionId=${sessionId}&sinceSeq=${sinceSeq}`);
1928
+ }
1921
1929
  async getAgentSession(cardId, options) {
1922
1930
  const params = new URLSearchParams;
1923
1931
  if (options?.includeEnded)
@@ -3877,6 +3885,16 @@ var TOOLS = {
3877
3885
  required: ["cardId"]
3878
3886
  }
3879
3887
  },
3888
+ harmony_classify_card: {
3889
+ description: "Classify a card with the LLM classifier: sets `intent` (plan/think/implement/review), `complexity_score` (0-10), and `model_tier` (simple/advanced/research), stamps `classified_at`, and applies the canonical type label (feature/bug/idea). Use this right after creating a card (e.g. in the `hmy-new` flow) so it's classified in-flow instead of waiting for it to surface on the web board. Idempotent — safe to re-run. Never touches the user-owned `model_override`.",
3890
+ inputSchema: {
3891
+ type: "object",
3892
+ properties: {
3893
+ cardId: { type: "string", description: "Card UUID" }
3894
+ },
3895
+ required: ["cardId"]
3896
+ }
3897
+ },
3880
3898
  harmony_get_card_external_links: {
3881
3899
  description: "Get external URL references attached to a card (links to docs, gists, dashboards, etc.).",
3882
3900
  inputSchema: {
@@ -5344,6 +5362,11 @@ async function handleToolCall(name, args, deps) {
5344
5362
  const result = await client3.getCardExternalLinks(cardId);
5345
5363
  return result;
5346
5364
  }
5365
+ case "harmony_classify_card": {
5366
+ const cardId = z.string().uuid().parse(args.cardId);
5367
+ const result = await client3.classifyCard(cardId);
5368
+ return result;
5369
+ }
5347
5370
  case "harmony_create_subtask": {
5348
5371
  const cardId = z.string().uuid().parse(args.cardId);
5349
5372
  const title = z.string().min(1).max(500).parse(args.title);
@@ -833,7 +833,6 @@ var init_oauth_refresh = __esm(() => {
833
833
  init_config();
834
834
  init_oauth_login();
835
835
  });
836
-
837
836
  // ../harmony-shared/dist/cardLinks.js
838
837
  var LINK_TYPE_INVERSES = {
839
838
  relates_to: "relates_to",
@@ -1307,6 +1306,9 @@ class HarmonyApiClient {
1307
1306
  async getCardExternalLinks(cardId) {
1308
1307
  return this.request("GET", `/cards/${cardId}/external-links`);
1309
1308
  }
1309
+ async classifyCard(cardId) {
1310
+ return this.request("POST", `/cards/${cardId}/classify`);
1311
+ }
1310
1312
  async createColumn(projectId, name) {
1311
1313
  return this.request("POST", "/columns", { projectId, name });
1312
1314
  }
@@ -1368,9 +1370,15 @@ class HarmonyApiClient {
1368
1370
  async flushActivityLog(cardId, data) {
1369
1371
  return this.request("POST", `/cards/${cardId}/agent-activity-log`, data);
1370
1372
  }
1373
+ async appendAgentRunEvents(cardId, data) {
1374
+ return this.request("POST", `/cards/${cardId}/agent-run-events`, data);
1375
+ }
1371
1376
  async getActivityLog(cardId, sessionId) {
1372
1377
  return this.request("GET", `/cards/${cardId}/agent-activity-log?sessionId=${sessionId}`);
1373
1378
  }
1379
+ async getPendingUserMessages(cardId, sessionId, sinceSeq) {
1380
+ return this.request("GET", `/cards/${cardId}/agent-messages?sessionId=${sessionId}&sinceSeq=${sinceSeq}`);
1381
+ }
1374
1382
  async getAgentSession(cardId, options) {
1375
1383
  const params = new URLSearchParams;
1376
1384
  if (options?.includeEnded)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethmy/mcp",
3
- "version": "2.9.7",
3
+ "version": "2.9.9",
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
@@ -1,4 +1,5 @@
1
1
  import {
2
+ type AgentRunEventDraft,
2
3
  type Comment,
3
4
  getDisplayLinkType,
4
5
  serializeCommentThread,
@@ -151,6 +152,18 @@ export interface CardExternalLinkRow {
151
152
  created_at: string;
152
153
  }
153
154
 
155
+ /** Result of the classify-card classifier (card #415). Any field may be null
156
+ * if the LLM didn't return a usable value. `model_override` is never touched. */
157
+ export interface CardClassificationResult {
158
+ type: "feature" | "bug" | "idea" | null;
159
+ intent: "plan" | "think" | "implement" | "review" | null;
160
+ complexity_score: number | null;
161
+ model_tier: "simple" | "advanced" | "research" | null;
162
+ classified_at: string;
163
+ applied_type_label_id: string | null;
164
+ reasoning: string | null;
165
+ }
166
+
154
167
  export class HarmonyApiClient {
155
168
  private apiKey: string;
156
169
  private apiUrl: string;
@@ -668,6 +681,12 @@ export class HarmonyApiClient {
668
681
  return this.request("GET", `/cards/${cardId}/external-links`);
669
682
  }
670
683
 
684
+ async classifyCard(
685
+ cardId: string,
686
+ ): Promise<{ classification: CardClassificationResult }> {
687
+ return this.request("POST", `/cards/${cardId}/classify`);
688
+ }
689
+
671
690
  // ============ COLUMN OPERATIONS ============
672
691
 
673
692
  async createColumn(
@@ -880,6 +899,20 @@ export class HarmonyApiClient {
880
899
  return this.request("POST", `/cards/${cardId}/agent-activity-log`, data);
881
900
  }
882
901
 
902
+ /**
903
+ * Append events to a run's agent_run_events stream (card #417). Send drafts in
904
+ * chronological order — the server's seq trigger assigns the monotonic per-run order.
905
+ */
906
+ async appendAgentRunEvents(
907
+ cardId: string,
908
+ data: {
909
+ sessionId: string;
910
+ events: (AgentRunEventDraft & { createdAt?: string })[];
911
+ },
912
+ ): Promise<{ inserted: number }> {
913
+ return this.request("POST", `/cards/${cardId}/agent-run-events`, data);
914
+ }
915
+
883
916
  async getActivityLog(
884
917
  cardId: string,
885
918
  sessionId: string,
@@ -900,6 +933,31 @@ export class HarmonyApiClient {
900
933
  );
901
934
  }
902
935
 
936
+ /**
937
+ * Drain queued chat-steering messages for a run (card #417). Returns `user_message`
938
+ * events with `seq` greater than `sinceSeq`, oldest first, so the daemon can continue
939
+ * the run via `claude --resume` at a turn boundary.
940
+ */
941
+ async getPendingUserMessages(
942
+ cardId: string,
943
+ sessionId: string,
944
+ sinceSeq: number,
945
+ ): Promise<{
946
+ messages: {
947
+ id: string;
948
+ seq: number;
949
+ text: string;
950
+ authorName?: string;
951
+ authorUserId?: string;
952
+ createdAt: string;
953
+ }[];
954
+ }> {
955
+ return this.request(
956
+ "GET",
957
+ `/cards/${cardId}/agent-messages?sessionId=${sessionId}&sinceSeq=${sinceSeq}`,
958
+ );
959
+ }
960
+
903
961
  async getAgentSession(
904
962
  cardId: string,
905
963
  options?: { includeEnded?: boolean },
package/src/server.ts CHANGED
@@ -686,6 +686,17 @@ export const TOOLS = {
686
686
  required: ["cardId"],
687
687
  },
688
688
  },
689
+ harmony_classify_card: {
690
+ description:
691
+ "Classify a card with the LLM classifier: sets `intent` (plan/think/implement/review), `complexity_score` (0-10), and `model_tier` (simple/advanced/research), stamps `classified_at`, and applies the canonical type label (feature/bug/idea). Use this right after creating a card (e.g. in the `hmy-new` flow) so it's classified in-flow instead of waiting for it to surface on the web board. Idempotent — safe to re-run. Never touches the user-owned `model_override`.",
692
+ inputSchema: {
693
+ type: "object",
694
+ properties: {
695
+ cardId: { type: "string", description: "Card UUID" },
696
+ },
697
+ required: ["cardId"],
698
+ },
699
+ },
689
700
  harmony_get_card_external_links: {
690
701
  description:
691
702
  "Get external URL references attached to a card (links to docs, gists, dashboards, etc.).",
@@ -2484,6 +2495,12 @@ async function handleToolCall(
2484
2495
  return result;
2485
2496
  }
2486
2497
 
2498
+ case "harmony_classify_card": {
2499
+ const cardId = z.string().uuid().parse(args.cardId);
2500
+ const result = await client.classifyCard(cardId);
2501
+ return result;
2502
+ }
2503
+
2487
2504
  // Subtask operations
2488
2505
  case "harmony_create_subtask": {
2489
2506
  const cardId = z.string().uuid().parse(args.cardId);
package/src/tui/setup.ts CHANGED
@@ -783,12 +783,13 @@ export async function runSetup(options: SetupOptions = {}): Promise<void> {
783
783
  options: [
784
784
  {
785
785
  value: "browser",
786
- label: "Sign in with your browser",
787
- hint: "recommended — secure, no key handling",
786
+ label: "Sign in or create an account in your browser",
787
+ hint: "recommended — new or existing users, no key handling",
788
788
  },
789
789
  {
790
790
  value: "create",
791
- label: "Create a free account",
791
+ label: "Create an account here in the terminal",
792
+ hint: "no browser",
792
793
  },
793
794
  {
794
795
  value: "apikey",
@@ -1599,6 +1600,18 @@ export async function runSetup(options: SetupOptions = {}): Promise<void> {
1599
1600
  );
1600
1601
  }
1601
1602
 
1603
+ console.log("");
1604
+ console.log(` ${colors.bold("Next steps:")}`);
1605
+ console.log(
1606
+ ` 1. Open Claude Code and say: ${colors.highlight('"Show me my board"')}`,
1607
+ );
1608
+ console.log(
1609
+ ` 2. Create a card: ${colors.highlight('"Create a card called Auth token refresh"')}`,
1610
+ );
1611
+ console.log(
1612
+ ` 3. Start the daemon: ${colors.highlight("npx @gethmy/agent")}`,
1613
+ );
1614
+
1602
1615
  console.log("");
1603
1616
  console.log(` ${colors.dim("Add to new project: npx @gethmy/mcp setup")}`);
1604
1617
  console.log(