@gethmy/mcp 2.9.7 → 2.9.8
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 +4 -4
- package/dist/cli.js +30 -4
- package/dist/index.js +21 -1
- package/dist/lib/api-client.js +6 -1
- package/package.json +1 -1
- package/src/api-client.ts +33 -0
- package/src/server.ts +17 -0
- package/src/tui/setup.ts +16 -3
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
|
-
- **
|
|
9
|
-
- **
|
|
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
|
|
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
|
-
|
|
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,6 +1922,9 @@ 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
|
}
|
|
@@ -3882,6 +3887,16 @@ var TOOLS = {
|
|
|
3882
3887
|
required: ["cardId"]
|
|
3883
3888
|
}
|
|
3884
3889
|
},
|
|
3890
|
+
harmony_classify_card: {
|
|
3891
|
+
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`.",
|
|
3892
|
+
inputSchema: {
|
|
3893
|
+
type: "object",
|
|
3894
|
+
properties: {
|
|
3895
|
+
cardId: { type: "string", description: "Card UUID" }
|
|
3896
|
+
},
|
|
3897
|
+
required: ["cardId"]
|
|
3898
|
+
}
|
|
3899
|
+
},
|
|
3885
3900
|
harmony_get_card_external_links: {
|
|
3886
3901
|
description: "Get external URL references attached to a card (links to docs, gists, dashboards, etc.).",
|
|
3887
3902
|
inputSchema: {
|
|
@@ -5349,6 +5364,11 @@ async function handleToolCall(name, args, deps) {
|
|
|
5349
5364
|
const result = await client3.getCardExternalLinks(cardId);
|
|
5350
5365
|
return result;
|
|
5351
5366
|
}
|
|
5367
|
+
case "harmony_classify_card": {
|
|
5368
|
+
const cardId = z.string().uuid().parse(args.cardId);
|
|
5369
|
+
const result = await client3.classifyCard(cardId);
|
|
5370
|
+
return result;
|
|
5371
|
+
}
|
|
5352
5372
|
case "harmony_create_subtask": {
|
|
5353
5373
|
const cardId = z.string().uuid().parse(args.cardId);
|
|
5354
5374
|
const title = z.string().min(1).max(500).parse(args.title);
|
|
@@ -7821,12 +7841,13 @@ only for unattended CI where you accept that risk.`));
|
|
|
7821
7841
|
options: [
|
|
7822
7842
|
{
|
|
7823
7843
|
value: "browser",
|
|
7824
|
-
label: "Sign in
|
|
7825
|
-
hint: "recommended —
|
|
7844
|
+
label: "Sign in or create an account in your browser",
|
|
7845
|
+
hint: "recommended — new or existing users, no key handling"
|
|
7826
7846
|
},
|
|
7827
7847
|
{
|
|
7828
7848
|
value: "create",
|
|
7829
|
-
label: "Create
|
|
7849
|
+
label: "Create an account here in the terminal",
|
|
7850
|
+
hint: "no browser"
|
|
7830
7851
|
},
|
|
7831
7852
|
{
|
|
7832
7853
|
value: "apikey",
|
|
@@ -8378,6 +8399,11 @@ Specify the workspace with --workspace <id>, or select one below.`);
|
|
|
8378
8399
|
console.log(` ${colors.brand("Cursor:")} MCP tools available automatically`);
|
|
8379
8400
|
}
|
|
8380
8401
|
console.log("");
|
|
8402
|
+
console.log(` ${colors.bold("Next steps:")}`);
|
|
8403
|
+
console.log(` 1. Open Claude Code and say: ${colors.highlight('"Show me my board"')}`);
|
|
8404
|
+
console.log(` 2. Create a card: ${colors.highlight('"Create a card called Auth token refresh"')}`);
|
|
8405
|
+
console.log(` 3. Start the daemon: ${colors.highlight("npx @gethmy/agent")}`);
|
|
8406
|
+
console.log("");
|
|
8381
8407
|
console.log(` ${colors.dim("Add to new project: npx @gethmy/mcp setup")}`);
|
|
8382
8408
|
console.log(` ${colors.dim("Need help? Visit https://app.gethmy.com/docs/mcp")}`);
|
|
8383
8409
|
}
|
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,6 +1917,9 @@ 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
|
}
|
|
@@ -3877,6 +3882,16 @@ var TOOLS = {
|
|
|
3877
3882
|
required: ["cardId"]
|
|
3878
3883
|
}
|
|
3879
3884
|
},
|
|
3885
|
+
harmony_classify_card: {
|
|
3886
|
+
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`.",
|
|
3887
|
+
inputSchema: {
|
|
3888
|
+
type: "object",
|
|
3889
|
+
properties: {
|
|
3890
|
+
cardId: { type: "string", description: "Card UUID" }
|
|
3891
|
+
},
|
|
3892
|
+
required: ["cardId"]
|
|
3893
|
+
}
|
|
3894
|
+
},
|
|
3880
3895
|
harmony_get_card_external_links: {
|
|
3881
3896
|
description: "Get external URL references attached to a card (links to docs, gists, dashboards, etc.).",
|
|
3882
3897
|
inputSchema: {
|
|
@@ -5344,6 +5359,11 @@ async function handleToolCall(name, args, deps) {
|
|
|
5344
5359
|
const result = await client3.getCardExternalLinks(cardId);
|
|
5345
5360
|
return result;
|
|
5346
5361
|
}
|
|
5362
|
+
case "harmony_classify_card": {
|
|
5363
|
+
const cardId = z.string().uuid().parse(args.cardId);
|
|
5364
|
+
const result = await client3.classifyCard(cardId);
|
|
5365
|
+
return result;
|
|
5366
|
+
}
|
|
5347
5367
|
case "harmony_create_subtask": {
|
|
5348
5368
|
const cardId = z.string().uuid().parse(args.cardId);
|
|
5349
5369
|
const title = z.string().min(1).max(500).parse(args.title);
|
package/dist/lib/api-client.js
CHANGED
|
@@ -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,6 +1370,9 @@ 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
|
}
|
package/package.json
CHANGED
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,
|
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
|
|
787
|
-
hint: "recommended —
|
|
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
|
|
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(
|