@gethmy/mcp 2.9.2 → 2.9.4
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/dist/cli.js +65 -0
- package/dist/index.js +65 -0
- package/dist/lib/api-client.js +3 -0
- package/package.json +1 -1
- package/src/api-client.ts +9 -0
- package/src/server.ts +75 -0
package/dist/cli.js
CHANGED
|
@@ -695,6 +695,10 @@ function getMemoryDir() {
|
|
|
695
695
|
return config.memoryDir;
|
|
696
696
|
return join(homedir(), ".harmony", "memory");
|
|
697
697
|
}
|
|
698
|
+
|
|
699
|
+
// src/server.ts
|
|
700
|
+
import { readFile } from "node:fs/promises";
|
|
701
|
+
import { basename } from "node:path";
|
|
698
702
|
// ../memory/dist/sync.js
|
|
699
703
|
import { createHash } from "node:crypto";
|
|
700
704
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -1523,6 +1527,9 @@ class HarmonyApiClient {
|
|
|
1523
1527
|
async getCardAttachments(cardId) {
|
|
1524
1528
|
return this.request("GET", `/cards/${cardId}/attachments`);
|
|
1525
1529
|
}
|
|
1530
|
+
async uploadCardAttachment(cardId, data) {
|
|
1531
|
+
return this.request("POST", `/cards/${cardId}/attachments`, data);
|
|
1532
|
+
}
|
|
1526
1533
|
async getCardExternalLinks(cardId) {
|
|
1527
1534
|
return this.request("GET", `/cards/${cardId}/external-links`);
|
|
1528
1535
|
}
|
|
@@ -3485,6 +3492,32 @@ var TOOLS = {
|
|
|
3485
3492
|
required: ["cardId"]
|
|
3486
3493
|
}
|
|
3487
3494
|
},
|
|
3495
|
+
harmony_upload_card_attachment: {
|
|
3496
|
+
description: "Upload a file attachment to a card (e.g. a pasted screenshot or a document). Provide the file either as `filePath` (a local path the MCP server can read — works in local/stdio mode) or as `base64Data` (raw base64 bytes — works everywhere, including remote mode). Max 5MB. Allowed: PNG, JPEG, GIF, WebP, HEIC/HEIF, PDF, DOC/DOCX, XLS/XLSX, TXT. Returns the stored attachment with a short-lived signed URL.",
|
|
3497
|
+
inputSchema: {
|
|
3498
|
+
type: "object",
|
|
3499
|
+
properties: {
|
|
3500
|
+
cardId: { type: "string", description: "Card UUID" },
|
|
3501
|
+
filePath: {
|
|
3502
|
+
type: "string",
|
|
3503
|
+
description: "Absolute path to a local file the MCP server process can read. Mutually exclusive with base64Data."
|
|
3504
|
+
},
|
|
3505
|
+
base64Data: {
|
|
3506
|
+
type: "string",
|
|
3507
|
+
description: "Base64-encoded file bytes (a `data:` URL prefix is accepted and stripped). Requires fileName. Mutually exclusive with filePath."
|
|
3508
|
+
},
|
|
3509
|
+
fileName: {
|
|
3510
|
+
type: "string",
|
|
3511
|
+
description: "File name including extension (e.g. 'screenshot.png'). Required with base64Data; defaults to the basename of filePath otherwise."
|
|
3512
|
+
},
|
|
3513
|
+
contentType: {
|
|
3514
|
+
type: "string",
|
|
3515
|
+
description: "Optional MIME type (e.g. 'image/png'). Inferred from the file extension when omitted."
|
|
3516
|
+
}
|
|
3517
|
+
},
|
|
3518
|
+
required: ["cardId"]
|
|
3519
|
+
}
|
|
3520
|
+
},
|
|
3488
3521
|
harmony_get_card_external_links: {
|
|
3489
3522
|
description: "Get external URL references attached to a card (links to docs, gists, dashboards, etc.).",
|
|
3490
3523
|
inputSchema: {
|
|
@@ -4903,6 +4936,38 @@ async function handleToolCall(name, args, deps) {
|
|
|
4903
4936
|
const result = await client3.getCardAttachments(cardId);
|
|
4904
4937
|
return result;
|
|
4905
4938
|
}
|
|
4939
|
+
case "harmony_upload_card_attachment": {
|
|
4940
|
+
const cardId = z.string().uuid().parse(args.cardId);
|
|
4941
|
+
const filePath = args.filePath != null ? z.string().parse(args.filePath) : undefined;
|
|
4942
|
+
const base64Data = args.base64Data != null ? z.string().parse(args.base64Data) : undefined;
|
|
4943
|
+
let fileName = args.fileName != null ? z.string().parse(args.fileName) : undefined;
|
|
4944
|
+
const contentType = args.contentType != null ? z.string().parse(args.contentType) : undefined;
|
|
4945
|
+
if (filePath && base64Data) {
|
|
4946
|
+
throw new Error("Provide either filePath or base64Data, not both.");
|
|
4947
|
+
}
|
|
4948
|
+
let data;
|
|
4949
|
+
if (filePath) {
|
|
4950
|
+
const bytes = await readFile(filePath);
|
|
4951
|
+
if (bytes.byteLength === 0) {
|
|
4952
|
+
throw new Error(`File is empty: ${filePath}`);
|
|
4953
|
+
}
|
|
4954
|
+
data = bytes.toString("base64");
|
|
4955
|
+
fileName = fileName || basename(filePath);
|
|
4956
|
+
} else if (base64Data) {
|
|
4957
|
+
if (!fileName) {
|
|
4958
|
+
throw new Error("fileName is required when using base64Data.");
|
|
4959
|
+
}
|
|
4960
|
+
data = base64Data;
|
|
4961
|
+
} else {
|
|
4962
|
+
throw new Error("Provide either filePath or base64Data.");
|
|
4963
|
+
}
|
|
4964
|
+
const result = await client3.uploadCardAttachment(cardId, {
|
|
4965
|
+
fileName,
|
|
4966
|
+
data,
|
|
4967
|
+
fileType: contentType
|
|
4968
|
+
});
|
|
4969
|
+
return result;
|
|
4970
|
+
}
|
|
4906
4971
|
case "harmony_get_card_external_links": {
|
|
4907
4972
|
const cardId = z.string().uuid().parse(args.cardId);
|
|
4908
4973
|
const result = await client3.getCardExternalLinks(cardId);
|
package/dist/index.js
CHANGED
|
@@ -506,6 +506,10 @@ var init_prompt_builder = __esm(() => {
|
|
|
506
506
|
execute: `EXECUTE MODE: Implement this task completely. Write production-ready code following best practices. Include necessary tests and documentation.`
|
|
507
507
|
};
|
|
508
508
|
});
|
|
509
|
+
|
|
510
|
+
// src/server.ts
|
|
511
|
+
import { readFile } from "node:fs/promises";
|
|
512
|
+
import { basename } from "node:path";
|
|
509
513
|
// ../memory/dist/sync.js
|
|
510
514
|
import { createHash } from "node:crypto";
|
|
511
515
|
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
@@ -1519,6 +1523,9 @@ class HarmonyApiClient {
|
|
|
1519
1523
|
async getCardAttachments(cardId) {
|
|
1520
1524
|
return this.request("GET", `/cards/${cardId}/attachments`);
|
|
1521
1525
|
}
|
|
1526
|
+
async uploadCardAttachment(cardId, data) {
|
|
1527
|
+
return this.request("POST", `/cards/${cardId}/attachments`, data);
|
|
1528
|
+
}
|
|
1522
1529
|
async getCardExternalLinks(cardId) {
|
|
1523
1530
|
return this.request("GET", `/cards/${cardId}/external-links`);
|
|
1524
1531
|
}
|
|
@@ -3481,6 +3488,32 @@ var TOOLS = {
|
|
|
3481
3488
|
required: ["cardId"]
|
|
3482
3489
|
}
|
|
3483
3490
|
},
|
|
3491
|
+
harmony_upload_card_attachment: {
|
|
3492
|
+
description: "Upload a file attachment to a card (e.g. a pasted screenshot or a document). Provide the file either as `filePath` (a local path the MCP server can read — works in local/stdio mode) or as `base64Data` (raw base64 bytes — works everywhere, including remote mode). Max 5MB. Allowed: PNG, JPEG, GIF, WebP, HEIC/HEIF, PDF, DOC/DOCX, XLS/XLSX, TXT. Returns the stored attachment with a short-lived signed URL.",
|
|
3493
|
+
inputSchema: {
|
|
3494
|
+
type: "object",
|
|
3495
|
+
properties: {
|
|
3496
|
+
cardId: { type: "string", description: "Card UUID" },
|
|
3497
|
+
filePath: {
|
|
3498
|
+
type: "string",
|
|
3499
|
+
description: "Absolute path to a local file the MCP server process can read. Mutually exclusive with base64Data."
|
|
3500
|
+
},
|
|
3501
|
+
base64Data: {
|
|
3502
|
+
type: "string",
|
|
3503
|
+
description: "Base64-encoded file bytes (a `data:` URL prefix is accepted and stripped). Requires fileName. Mutually exclusive with filePath."
|
|
3504
|
+
},
|
|
3505
|
+
fileName: {
|
|
3506
|
+
type: "string",
|
|
3507
|
+
description: "File name including extension (e.g. 'screenshot.png'). Required with base64Data; defaults to the basename of filePath otherwise."
|
|
3508
|
+
},
|
|
3509
|
+
contentType: {
|
|
3510
|
+
type: "string",
|
|
3511
|
+
description: "Optional MIME type (e.g. 'image/png'). Inferred from the file extension when omitted."
|
|
3512
|
+
}
|
|
3513
|
+
},
|
|
3514
|
+
required: ["cardId"]
|
|
3515
|
+
}
|
|
3516
|
+
},
|
|
3484
3517
|
harmony_get_card_external_links: {
|
|
3485
3518
|
description: "Get external URL references attached to a card (links to docs, gists, dashboards, etc.).",
|
|
3486
3519
|
inputSchema: {
|
|
@@ -4899,6 +4932,38 @@ async function handleToolCall(name, args, deps) {
|
|
|
4899
4932
|
const result = await client3.getCardAttachments(cardId);
|
|
4900
4933
|
return result;
|
|
4901
4934
|
}
|
|
4935
|
+
case "harmony_upload_card_attachment": {
|
|
4936
|
+
const cardId = z.string().uuid().parse(args.cardId);
|
|
4937
|
+
const filePath = args.filePath != null ? z.string().parse(args.filePath) : undefined;
|
|
4938
|
+
const base64Data = args.base64Data != null ? z.string().parse(args.base64Data) : undefined;
|
|
4939
|
+
let fileName = args.fileName != null ? z.string().parse(args.fileName) : undefined;
|
|
4940
|
+
const contentType = args.contentType != null ? z.string().parse(args.contentType) : undefined;
|
|
4941
|
+
if (filePath && base64Data) {
|
|
4942
|
+
throw new Error("Provide either filePath or base64Data, not both.");
|
|
4943
|
+
}
|
|
4944
|
+
let data;
|
|
4945
|
+
if (filePath) {
|
|
4946
|
+
const bytes = await readFile(filePath);
|
|
4947
|
+
if (bytes.byteLength === 0) {
|
|
4948
|
+
throw new Error(`File is empty: ${filePath}`);
|
|
4949
|
+
}
|
|
4950
|
+
data = bytes.toString("base64");
|
|
4951
|
+
fileName = fileName || basename(filePath);
|
|
4952
|
+
} else if (base64Data) {
|
|
4953
|
+
if (!fileName) {
|
|
4954
|
+
throw new Error("fileName is required when using base64Data.");
|
|
4955
|
+
}
|
|
4956
|
+
data = base64Data;
|
|
4957
|
+
} else {
|
|
4958
|
+
throw new Error("Provide either filePath or base64Data.");
|
|
4959
|
+
}
|
|
4960
|
+
const result = await client3.uploadCardAttachment(cardId, {
|
|
4961
|
+
fileName,
|
|
4962
|
+
data,
|
|
4963
|
+
fileType: contentType
|
|
4964
|
+
});
|
|
4965
|
+
return result;
|
|
4966
|
+
}
|
|
4902
4967
|
case "harmony_get_card_external_links": {
|
|
4903
4968
|
const cardId = z.string().uuid().parse(args.cardId);
|
|
4904
4969
|
const result = await client3.getCardExternalLinks(cardId);
|
package/dist/lib/api-client.js
CHANGED
|
@@ -1126,6 +1126,9 @@ class HarmonyApiClient {
|
|
|
1126
1126
|
async getCardAttachments(cardId) {
|
|
1127
1127
|
return this.request("GET", `/cards/${cardId}/attachments`);
|
|
1128
1128
|
}
|
|
1129
|
+
async uploadCardAttachment(cardId, data) {
|
|
1130
|
+
return this.request("POST", `/cards/${cardId}/attachments`, data);
|
|
1131
|
+
}
|
|
1129
1132
|
async getCardExternalLinks(cardId) {
|
|
1130
1133
|
return this.request("GET", `/cards/${cardId}/external-links`);
|
|
1131
1134
|
}
|
package/package.json
CHANGED
package/src/api-client.ts
CHANGED
|
@@ -583,6 +583,13 @@ export class HarmonyApiClient {
|
|
|
583
583
|
return this.request("GET", `/cards/${cardId}/attachments`);
|
|
584
584
|
}
|
|
585
585
|
|
|
586
|
+
async uploadCardAttachment(
|
|
587
|
+
cardId: string,
|
|
588
|
+
data: { fileName: string; data: string; fileType?: string },
|
|
589
|
+
): Promise<{ attachment: CardAttachment }> {
|
|
590
|
+
return this.request("POST", `/cards/${cardId}/attachments`, data);
|
|
591
|
+
}
|
|
592
|
+
|
|
586
593
|
async getCardExternalLinks(
|
|
587
594
|
cardId: string,
|
|
588
595
|
): Promise<{ external_links: CardExternalLinkRow[] }> {
|
|
@@ -728,6 +735,7 @@ export class HarmonyApiClient {
|
|
|
728
735
|
cacheCreationInputTokens?: number;
|
|
729
736
|
cacheReadInputTokens?: number;
|
|
730
737
|
modelName?: string;
|
|
738
|
+
numTurns?: number;
|
|
731
739
|
recentActions?: { action: string; ts: string }[];
|
|
732
740
|
failureReason?:
|
|
733
741
|
| "verification"
|
|
@@ -761,6 +769,7 @@ export class HarmonyApiClient {
|
|
|
761
769
|
cacheCreationInputTokens?: number;
|
|
762
770
|
cacheReadInputTokens?: number;
|
|
763
771
|
modelName?: string;
|
|
772
|
+
numTurns?: number;
|
|
764
773
|
failureReason?:
|
|
765
774
|
| "verification"
|
|
766
775
|
| "review"
|
package/src/server.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { basename } from "node:path";
|
|
1
3
|
import { syncFull, syncPull, syncPush } from "@harmony/memory";
|
|
2
4
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -616,6 +618,37 @@ export const TOOLS = {
|
|
|
616
618
|
required: ["cardId"],
|
|
617
619
|
},
|
|
618
620
|
},
|
|
621
|
+
harmony_upload_card_attachment: {
|
|
622
|
+
description:
|
|
623
|
+
"Upload a file attachment to a card (e.g. a pasted screenshot or a document). Provide the file either as `filePath` (a local path the MCP server can read — works in local/stdio mode) or as `base64Data` (raw base64 bytes — works everywhere, including remote mode). Max 5MB. Allowed: PNG, JPEG, GIF, WebP, HEIC/HEIF, PDF, DOC/DOCX, XLS/XLSX, TXT. Returns the stored attachment with a short-lived signed URL.",
|
|
624
|
+
inputSchema: {
|
|
625
|
+
type: "object",
|
|
626
|
+
properties: {
|
|
627
|
+
cardId: { type: "string", description: "Card UUID" },
|
|
628
|
+
filePath: {
|
|
629
|
+
type: "string",
|
|
630
|
+
description:
|
|
631
|
+
"Absolute path to a local file the MCP server process can read. Mutually exclusive with base64Data.",
|
|
632
|
+
},
|
|
633
|
+
base64Data: {
|
|
634
|
+
type: "string",
|
|
635
|
+
description:
|
|
636
|
+
"Base64-encoded file bytes (a `data:` URL prefix is accepted and stripped). Requires fileName. Mutually exclusive with filePath.",
|
|
637
|
+
},
|
|
638
|
+
fileName: {
|
|
639
|
+
type: "string",
|
|
640
|
+
description:
|
|
641
|
+
"File name including extension (e.g. 'screenshot.png'). Required with base64Data; defaults to the basename of filePath otherwise.",
|
|
642
|
+
},
|
|
643
|
+
contentType: {
|
|
644
|
+
type: "string",
|
|
645
|
+
description:
|
|
646
|
+
"Optional MIME type (e.g. 'image/png'). Inferred from the file extension when omitted.",
|
|
647
|
+
},
|
|
648
|
+
},
|
|
649
|
+
required: ["cardId"],
|
|
650
|
+
},
|
|
651
|
+
},
|
|
619
652
|
harmony_get_card_external_links: {
|
|
620
653
|
description:
|
|
621
654
|
"Get external URL references attached to a card (links to docs, gists, dashboards, etc.).",
|
|
@@ -2344,6 +2377,48 @@ async function handleToolCall(
|
|
|
2344
2377
|
return result;
|
|
2345
2378
|
}
|
|
2346
2379
|
|
|
2380
|
+
case "harmony_upload_card_attachment": {
|
|
2381
|
+
const cardId = z.string().uuid().parse(args.cardId);
|
|
2382
|
+
const filePath =
|
|
2383
|
+
args.filePath != null ? z.string().parse(args.filePath) : undefined;
|
|
2384
|
+
const base64Data =
|
|
2385
|
+
args.base64Data != null ? z.string().parse(args.base64Data) : undefined;
|
|
2386
|
+
let fileName =
|
|
2387
|
+
args.fileName != null ? z.string().parse(args.fileName) : undefined;
|
|
2388
|
+
const contentType =
|
|
2389
|
+
args.contentType != null
|
|
2390
|
+
? z.string().parse(args.contentType)
|
|
2391
|
+
: undefined;
|
|
2392
|
+
|
|
2393
|
+
if (filePath && base64Data) {
|
|
2394
|
+
throw new Error("Provide either filePath or base64Data, not both.");
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
let data: string;
|
|
2398
|
+
if (filePath) {
|
|
2399
|
+
const bytes = await readFile(filePath);
|
|
2400
|
+
if (bytes.byteLength === 0) {
|
|
2401
|
+
throw new Error(`File is empty: ${filePath}`);
|
|
2402
|
+
}
|
|
2403
|
+
data = bytes.toString("base64");
|
|
2404
|
+
fileName = fileName || basename(filePath);
|
|
2405
|
+
} else if (base64Data) {
|
|
2406
|
+
if (!fileName) {
|
|
2407
|
+
throw new Error("fileName is required when using base64Data.");
|
|
2408
|
+
}
|
|
2409
|
+
data = base64Data;
|
|
2410
|
+
} else {
|
|
2411
|
+
throw new Error("Provide either filePath or base64Data.");
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
const result = await client.uploadCardAttachment(cardId, {
|
|
2415
|
+
fileName: fileName as string,
|
|
2416
|
+
data,
|
|
2417
|
+
fileType: contentType,
|
|
2418
|
+
});
|
|
2419
|
+
return result;
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2347
2422
|
case "harmony_get_card_external_links": {
|
|
2348
2423
|
const cardId = z.string().uuid().parse(args.cardId);
|
|
2349
2424
|
const result = await client.getCardExternalLinks(cardId);
|