hypermail-mcp 0.7.1 → 0.7.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 +5 -1
- package/dist/cli.js +62 -12
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
A **Model Context Protocol** server that lets an agent operate any of the user's
|
|
4
4
|
inboxes through a single, unified tool surface.
|
|
5
5
|
|
|
6
|
-
> **v0.7.
|
|
6
|
+
> **v0.7.2** — `add_attachment_to_draft` now accepts `filePath` (absolute path to
|
|
7
|
+
a local file) as an alternative to `contentBytes`. The file is read from disk and
|
|
8
|
+
base64-encoded automatically, the MIME type is inferred from the extension, and
|
|
9
|
+
`name` defaults to the file's basename.
|
|
10
|
+
>> **v0.7.1** — Every config field is now settable via a dedicated
|
|
7
11
|
> `HYPERMAIL_*` env var. Legacy env vars (`MS_CLIENT_ID`, `MS_TENANT_ID`,
|
|
8
12
|
> `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`) still work as fallbacks. See
|
|
9
13
|
> [Environment Variables](#environment-variables) for the full reference.
|
package/dist/cli.js
CHANGED
|
@@ -3301,6 +3301,8 @@ function registerOrganizeTools(server, ctx) {
|
|
|
3301
3301
|
|
|
3302
3302
|
// src/tools/compose.ts
|
|
3303
3303
|
import { z as z6 } from "zod";
|
|
3304
|
+
import { readFileSync } from "fs";
|
|
3305
|
+
import { basename, extname } from "path";
|
|
3304
3306
|
function registerComposeTools(server, ctx) {
|
|
3305
3307
|
const { store, registry, tools } = ctx;
|
|
3306
3308
|
const sendEmailSchema = z6.object({
|
|
@@ -3506,6 +3508,31 @@ function registerComposeTools(server, ctx) {
|
|
|
3506
3508
|
}
|
|
3507
3509
|
);
|
|
3508
3510
|
}
|
|
3511
|
+
const MIME_TYPES = {
|
|
3512
|
+
".pdf": "application/pdf",
|
|
3513
|
+
".png": "image/png",
|
|
3514
|
+
".jpg": "image/jpeg",
|
|
3515
|
+
".jpeg": "image/jpeg",
|
|
3516
|
+
".gif": "image/gif",
|
|
3517
|
+
".svg": "image/svg+xml",
|
|
3518
|
+
".webp": "image/webp",
|
|
3519
|
+
".txt": "text/plain",
|
|
3520
|
+
".html": "text/html",
|
|
3521
|
+
".css": "text/css",
|
|
3522
|
+
".csv": "text/csv",
|
|
3523
|
+
".json": "application/json",
|
|
3524
|
+
".xml": "application/xml",
|
|
3525
|
+
".zip": "application/zip",
|
|
3526
|
+
".gz": "application/gzip",
|
|
3527
|
+
".tar": "application/x-tar",
|
|
3528
|
+
".doc": "application/msword",
|
|
3529
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
3530
|
+
".xls": "application/vnd.ms-excel",
|
|
3531
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
3532
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
3533
|
+
".mp3": "audio/mpeg",
|
|
3534
|
+
".mp4": "video/mp4"
|
|
3535
|
+
};
|
|
3509
3536
|
const addAttachmentOutputSchema = {
|
|
3510
3537
|
attached: z6.literal(true),
|
|
3511
3538
|
id: z6.string(),
|
|
@@ -3519,25 +3546,48 @@ function registerComposeTools(server, ctx) {
|
|
|
3519
3546
|
server.registerTool(
|
|
3520
3547
|
"add_attachment_to_draft",
|
|
3521
3548
|
{
|
|
3522
|
-
description: "Add a file attachment to an existing draft email by ID. `contentBytes`
|
|
3523
|
-
inputSchema: {
|
|
3549
|
+
description: "Add a file attachment to an existing draft email by ID. Provide exactly one of `contentBytes` (base64-encoded content) or `filePath` (absolute path to a local file). When using `filePath`, the file is read from disk and base64-encoded automatically, and `name` defaults to the file's basename if not provided. `contentType` is the MIME type (e.g. 'application/pdf'); when using `filePath` it is inferred from the extension if omitted. Disabled in --read-only mode.",
|
|
3550
|
+
inputSchema: z6.object({
|
|
3524
3551
|
account: z6.string().email(),
|
|
3525
3552
|
id: z6.string().min(1).describe("Draft message ID"),
|
|
3526
|
-
name: z6.string().min(1).describe(
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3553
|
+
name: z6.string().min(1).optional().describe(
|
|
3554
|
+
"Attachment filename (e.g. 'report.pdf'). Defaults to the file's basename when `filePath` is used."
|
|
3555
|
+
),
|
|
3556
|
+
contentBytes: z6.string().min(1).optional().describe("Base64-encoded file content. Mutually exclusive with `filePath`."),
|
|
3557
|
+
filePath: z6.string().min(1).optional().describe(
|
|
3558
|
+
"Absolute path to a local file. The file is read and base64-encoded automatically. Mutually exclusive with `contentBytes`."
|
|
3559
|
+
),
|
|
3560
|
+
contentType: z6.string().optional().describe("MIME type (e.g. 'application/pdf'). Inferred from `filePath` extension if omitted.")
|
|
3561
|
+
}).refine(
|
|
3562
|
+
(v) => Boolean(v.contentBytes) !== Boolean(v.filePath),
|
|
3563
|
+
{ message: "Provide exactly one of `contentBytes` or `filePath`." }
|
|
3564
|
+
),
|
|
3530
3565
|
outputSchema: addAttachmentOutputSchema
|
|
3531
3566
|
},
|
|
3532
3567
|
async (args) => {
|
|
3533
3568
|
try {
|
|
3534
3569
|
const { provider, account } = registry.resolveByEmail(args.account);
|
|
3570
|
+
let contentBytes;
|
|
3571
|
+
let name;
|
|
3572
|
+
let contentType = args.contentType;
|
|
3573
|
+
if (args.filePath) {
|
|
3574
|
+
const fileData = readFileSync(args.filePath);
|
|
3575
|
+
contentBytes = fileData.toString("base64");
|
|
3576
|
+
name = args.name ?? basename(args.filePath);
|
|
3577
|
+
if (!contentType) {
|
|
3578
|
+
const ext = extname(args.filePath).toLowerCase();
|
|
3579
|
+
contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
3580
|
+
}
|
|
3581
|
+
} else {
|
|
3582
|
+
contentBytes = args.contentBytes;
|
|
3583
|
+
name = args.name;
|
|
3584
|
+
}
|
|
3535
3585
|
const res = await provider.addAttachmentToDraft(
|
|
3536
3586
|
account,
|
|
3537
3587
|
args.id,
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3588
|
+
name,
|
|
3589
|
+
contentBytes,
|
|
3590
|
+
contentType
|
|
3541
3591
|
);
|
|
3542
3592
|
const data = {
|
|
3543
3593
|
attached: true,
|
|
@@ -3566,7 +3616,7 @@ function registerTools(server, opts) {
|
|
|
3566
3616
|
// package.json
|
|
3567
3617
|
var package_default = {
|
|
3568
3618
|
name: "hypermail-mcp",
|
|
3569
|
-
version: "0.7.
|
|
3619
|
+
version: "0.7.2",
|
|
3570
3620
|
description: "Unified email MCP server \u2014 operate any inbox (Outlook now, IMAP/Gmail later) by passing an email address.",
|
|
3571
3621
|
type: "module",
|
|
3572
3622
|
bin: {
|
|
@@ -3747,7 +3797,7 @@ var WatcherManager = class {
|
|
|
3747
3797
|
};
|
|
3748
3798
|
|
|
3749
3799
|
// src/config.ts
|
|
3750
|
-
import { readFileSync } from "fs";
|
|
3800
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
3751
3801
|
import { z as z7 } from "zod";
|
|
3752
3802
|
var httpConfigSchema = z7.object({
|
|
3753
3803
|
enabled: z7.boolean().default(false),
|
|
@@ -3886,7 +3936,7 @@ function loadConfig(configPath, cliOverrides = {}) {
|
|
|
3886
3936
|
let raw = {};
|
|
3887
3937
|
if (configPath) {
|
|
3888
3938
|
try {
|
|
3889
|
-
raw = JSON.parse(
|
|
3939
|
+
raw = JSON.parse(readFileSync2(configPath, "utf-8"));
|
|
3890
3940
|
} catch (err) {
|
|
3891
3941
|
const detail = err instanceof SyntaxError ? "Invalid JSON" : err instanceof Error ? err.message : String(err);
|
|
3892
3942
|
throw new Error(`Failed to read config file "${configPath}": ${detail}`);
|