@gethmy/mcp 2.5.0 → 2.5.2
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 +3 -20
- package/dist/cli.js +1396 -32545
- package/dist/index.js +970 -28746
- package/dist/lib/api-client.js +11 -0
- package/package.json +3 -2
- package/src/api-client.ts +51 -0
- package/src/server.ts +66 -2
- package/src/skills.ts +67 -485
- package/src/tui/setup.ts +57 -40
- package/dist/http.js +0 -1959
- package/dist/remote.js +0 -32328
- package/dist/server.js +0 -31967
- package/src/__tests__/auto-session.test.ts +0 -912
- package/src/__tests__/graph-expansion.test.ts +0 -285
- package/src/__tests__/integration-memory-crud.test.ts +0 -948
- package/src/__tests__/integration-memory-system.test.ts +0 -321
- package/src/__tests__/mcp-integration.test.ts +0 -141
- package/src/__tests__/memory-floor.test.ts +0 -126
- package/src/__tests__/memory-park.test.ts +0 -213
- package/src/__tests__/memory-session.test.ts +0 -77
- package/src/__tests__/prompt-builder.test.ts +0 -739
- package/src/__tests__/remote-routing.test.ts +0 -285
- package/src/__tests__/skills.test.ts +0 -111
- package/src/__tests__/tool-dispatch.test.ts +0 -260
package/dist/lib/api-client.js
CHANGED
|
@@ -918,6 +918,17 @@ class HarmonyApiClient {
|
|
|
918
918
|
}
|
|
919
919
|
throw lastError || new Error("Request failed after retries");
|
|
920
920
|
}
|
|
921
|
+
async fetchSkillsVersion() {
|
|
922
|
+
return this.request("GET", "/skills/version");
|
|
923
|
+
}
|
|
924
|
+
async fetchSkill(name) {
|
|
925
|
+
return this.request("GET", `/skills/${encodeURIComponent(name)}`);
|
|
926
|
+
}
|
|
927
|
+
async recordSkillInvocation(name) {
|
|
928
|
+
try {
|
|
929
|
+
await this.request("POST", "/skills/telemetry", { name });
|
|
930
|
+
} catch {}
|
|
931
|
+
}
|
|
921
932
|
async listWorkspaces() {
|
|
922
933
|
return this.request("GET", "/workspaces");
|
|
923
934
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gethmy/mcp",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.2",
|
|
4
4
|
"description": "MCP server for Harmony Kanban board - enables AI coding agents to manage your boards",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"files": [
|
|
26
26
|
"dist",
|
|
27
27
|
"src",
|
|
28
|
+
"!src/__tests__",
|
|
28
29
|
"README.md"
|
|
29
30
|
],
|
|
30
31
|
"repository": {
|
|
@@ -54,7 +55,7 @@
|
|
|
54
55
|
"bun": ">=1.0.0"
|
|
55
56
|
},
|
|
56
57
|
"scripts": {
|
|
57
|
-
"build": "bun build src/index.ts src/cli.ts --outdir dist --target node && bun build src/api-client.ts src/config.ts --outdir dist/lib --root src --target node",
|
|
58
|
+
"build": "rm -rf dist && bun build src/index.ts src/cli.ts --outdir dist --target node --external @clack/prompts --external @modelcontextprotocol/sdk --external commander --external hono --external picocolors --external zod && bun build src/api-client.ts src/config.ts --outdir dist/lib --root src --target node --external @clack/prompts --external @modelcontextprotocol/sdk --external commander --external hono --external picocolors --external zod",
|
|
58
59
|
"build:bun": "bun build src/index.ts src/http.ts src/remote.ts src/cli.ts --outdir dist --target bun",
|
|
59
60
|
"serve:remote": "bun src/remote.ts",
|
|
60
61
|
"dev": "bun --watch src/index.ts",
|
package/src/api-client.ts
CHANGED
|
@@ -323,6 +323,57 @@ export class HarmonyApiClient {
|
|
|
323
323
|
throw lastError || new Error("Request failed after retries");
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
+
// ============ SKILLS OPERATIONS ============
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* GET /v1/skills/version — unauthenticated discovery endpoint.
|
|
330
|
+
* Returns aggregate version, the list of skill names, sha256 integrity
|
|
331
|
+
* hashes, and the ed25519 public key for signature verification.
|
|
332
|
+
*/
|
|
333
|
+
async fetchSkillsVersion(): Promise<{
|
|
334
|
+
version: string;
|
|
335
|
+
skills: string[];
|
|
336
|
+
integrity: Record<string, string>;
|
|
337
|
+
publicKey: string;
|
|
338
|
+
}> {
|
|
339
|
+
return this.request("GET", "/skills/version");
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* GET /v1/skills/{name} — authenticated content fetch.
|
|
344
|
+
* Returns rendered SKILL.md (frontmatter + body) ready to install.
|
|
345
|
+
*/
|
|
346
|
+
async fetchSkill(name: string): Promise<{
|
|
347
|
+
name: string;
|
|
348
|
+
version: string;
|
|
349
|
+
content: string;
|
|
350
|
+
sha256: string;
|
|
351
|
+
signature: string;
|
|
352
|
+
}> {
|
|
353
|
+
return this.request("GET", `/skills/${encodeURIComponent(name)}`);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* POST /v1/skills/telemetry — fire-and-forget activation signal.
|
|
358
|
+
* Called from the ReadResourceRequest handler when an agent reads
|
|
359
|
+
* a harmony://skills/<name> resource. Increments invocation_count +
|
|
360
|
+
* sets last_used = now() on the matching skill_resource row.
|
|
361
|
+
*
|
|
362
|
+
* Best-effort: returns void; never throws. If the endpoint doesn't
|
|
363
|
+
* exist yet (pre-Phase-2 backend), or the network is flaky, the
|
|
364
|
+
* activation is dropped silently. The skill content read itself
|
|
365
|
+
* is NOT gated on this call succeeding.
|
|
366
|
+
*/
|
|
367
|
+
async recordSkillInvocation(name: string): Promise<void> {
|
|
368
|
+
try {
|
|
369
|
+
await this.request("POST", "/skills/telemetry", { name });
|
|
370
|
+
} catch {
|
|
371
|
+
// Fire-and-forget: any failure (404 if endpoint not deployed yet,
|
|
372
|
+
// 401 if API key rotated, network timeout) is swallowed so the
|
|
373
|
+
// caller can proceed with serving content.
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
326
377
|
// ============ WORKSPACE OPERATIONS ============
|
|
327
378
|
|
|
328
379
|
async listWorkspaces(): Promise<{ workspaces: unknown[] }> {
|
package/src/server.ts
CHANGED
|
@@ -1530,6 +1530,38 @@ export const RESOURCES = [
|
|
|
1530
1530
|
},
|
|
1531
1531
|
];
|
|
1532
1532
|
|
|
1533
|
+
/**
|
|
1534
|
+
* Build the dynamic resource list. Skill resources (harmony://skills/<name>)
|
|
1535
|
+
* are derived at request time from /v1/skills/version so the list always
|
|
1536
|
+
* matches what the agent will get on read. Phase 1 of card #162 — the
|
|
1537
|
+
* ReadResourceRequest on harmony://skills/<name> is the activation signal
|
|
1538
|
+
* for skill telemetry.
|
|
1539
|
+
*
|
|
1540
|
+
* Offline / unauthenticated → returns just the static RESOURCES. Agents
|
|
1541
|
+
* that can't reach the API still see the context resource and any error
|
|
1542
|
+
* surfaces on read rather than list (matches the existing offline-safe
|
|
1543
|
+
* posture of refreshSkills()).
|
|
1544
|
+
*/
|
|
1545
|
+
async function listResourcesDynamic(deps: ToolDeps): Promise<typeof RESOURCES> {
|
|
1546
|
+
if (!deps.isConfigured()) return RESOURCES;
|
|
1547
|
+
try {
|
|
1548
|
+
const versionInfo = await deps.getClient().fetchSkillsVersion();
|
|
1549
|
+
// hmy-update-check is a shell script served on the same URL but is
|
|
1550
|
+
// not an installable SKILL.md; don't surface it as an MCP resource.
|
|
1551
|
+
const skillResources = versionInfo.skills
|
|
1552
|
+
.filter((name) => name !== "hmy-update-check")
|
|
1553
|
+
.map((name) => ({
|
|
1554
|
+
uri: `harmony://skills/${name}`,
|
|
1555
|
+
name: `Skill: ${name}`,
|
|
1556
|
+
description: `Harmony skill (SKILL.md) — version ${versionInfo.version}`,
|
|
1557
|
+
mimeType: "text/markdown",
|
|
1558
|
+
}));
|
|
1559
|
+
return [...RESOURCES, ...skillResources];
|
|
1560
|
+
} catch {
|
|
1561
|
+
return RESOURCES;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1533
1565
|
/**
|
|
1534
1566
|
* Reusable end-session pipeline.
|
|
1535
1567
|
* Called by both explicit harmony_end_agent_session and auto-session timeout/card-switch.
|
|
@@ -1654,9 +1686,10 @@ export function registerHandlers(server: Server, deps: ToolDeps): void {
|
|
|
1654
1686
|
}
|
|
1655
1687
|
});
|
|
1656
1688
|
|
|
1657
|
-
// List resources
|
|
1689
|
+
// List resources — dynamic so harmony://skills/<name> entries reflect
|
|
1690
|
+
// the live /v1/skills/version payload at request time.
|
|
1658
1691
|
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
1659
|
-
resources:
|
|
1692
|
+
resources: await listResourcesDynamic(deps),
|
|
1660
1693
|
}));
|
|
1661
1694
|
|
|
1662
1695
|
// Read resource
|
|
@@ -1683,6 +1716,37 @@ export function registerHandlers(server: Server, deps: ToolDeps): void {
|
|
|
1683
1716
|
};
|
|
1684
1717
|
}
|
|
1685
1718
|
|
|
1719
|
+
// harmony://skills/<name> — Phase 1 of card #162. This read IS the
|
|
1720
|
+
// activation event: agent matched the skill's description, harness
|
|
1721
|
+
// calls ReadResource, we fire fire-and-forget telemetry before
|
|
1722
|
+
// returning the body. recordSkillInvocation() swallows all errors
|
|
1723
|
+
// so a telemetry outage never blocks skill delivery.
|
|
1724
|
+
const SKILL_URI_RE = /^harmony:\/\/skills\/([a-z0-9][a-z0-9-]*[a-z0-9])$/;
|
|
1725
|
+
const skillMatch = uri.match(SKILL_URI_RE);
|
|
1726
|
+
if (skillMatch) {
|
|
1727
|
+
const name = skillMatch[1];
|
|
1728
|
+
if (!deps.isConfigured()) {
|
|
1729
|
+
throw new Error(
|
|
1730
|
+
`Cannot read skill "${name}": Harmony MCP server is not configured. Run \`hmy-mcp setup\`.`,
|
|
1731
|
+
);
|
|
1732
|
+
}
|
|
1733
|
+
const client = deps.getClient();
|
|
1734
|
+
// Fire-and-forget BEFORE awaiting the content fetch so a slow
|
|
1735
|
+
// telemetry endpoint never delays skill delivery. The promise
|
|
1736
|
+
// is intentionally dropped (caught inside recordSkillInvocation).
|
|
1737
|
+
void client.recordSkillInvocation(name);
|
|
1738
|
+
const fetched = await client.fetchSkill(name);
|
|
1739
|
+
return {
|
|
1740
|
+
contents: [
|
|
1741
|
+
{
|
|
1742
|
+
uri,
|
|
1743
|
+
mimeType: "text/markdown",
|
|
1744
|
+
text: fetched.content,
|
|
1745
|
+
},
|
|
1746
|
+
],
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1686
1750
|
throw new Error(`Unknown resource: ${uri}`);
|
|
1687
1751
|
});
|
|
1688
1752
|
}
|