@marlinjai/email-mcp 1.2.8 → 1.3.0
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 -2
- package/dist/index.js +179 -11
- package/dist/index.js.map +2 -2
- package/dist/setup/wizard.js +119 -11
- package/dist/setup/wizard.js.map +2 -2
- package/package.json +1 -1
package/dist/setup/wizard.js
CHANGED
|
@@ -363,6 +363,7 @@ var FolderType = {
|
|
|
363
363
|
};
|
|
364
364
|
|
|
365
365
|
// src/providers/gmail/adapter.ts
|
|
366
|
+
import { Readable } from "node:stream";
|
|
366
367
|
import { google } from "googleapis";
|
|
367
368
|
import { OAuth2Client as OAuth2Client2 } from "google-auth-library";
|
|
368
369
|
|
|
@@ -567,20 +568,32 @@ var GmailAdapter = class {
|
|
|
567
568
|
async search(query) {
|
|
568
569
|
const gmail = this.ensureConnected();
|
|
569
570
|
const q = buildGmailQuery(query);
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
571
|
+
const limit = query.limit || 20;
|
|
572
|
+
const offset = query.offset || 0;
|
|
573
|
+
const needed = offset + limit;
|
|
574
|
+
const ids = [];
|
|
575
|
+
let pageToken;
|
|
576
|
+
do {
|
|
577
|
+
const params = {
|
|
578
|
+
userId: "me",
|
|
579
|
+
maxResults: Math.min(500, needed - ids.length)
|
|
580
|
+
};
|
|
581
|
+
if (q) params.q = q;
|
|
582
|
+
if (query.folder) params.labelIds = [query.folder];
|
|
583
|
+
if (pageToken) params.pageToken = pageToken;
|
|
584
|
+
const res = await gmail.users.messages.list(params);
|
|
585
|
+
for (const msg of res.data.messages || []) {
|
|
586
|
+
if (msg.id) ids.push(msg.id);
|
|
587
|
+
}
|
|
588
|
+
pageToken = res.data.nextPageToken || void 0;
|
|
589
|
+
} while (pageToken && ids.length < needed);
|
|
590
|
+
const windowIds = ids.slice(offset, offset + limit);
|
|
591
|
+
if (windowIds.length === 0) return [];
|
|
579
592
|
const emails = [];
|
|
580
|
-
for (const
|
|
593
|
+
for (const id of windowIds) {
|
|
581
594
|
const full = await gmail.users.messages.get({
|
|
582
595
|
userId: "me",
|
|
583
|
-
id
|
|
596
|
+
id,
|
|
584
597
|
format: "full"
|
|
585
598
|
});
|
|
586
599
|
emails.push(mapGmailMessage(full.data, this.accountId));
|
|
@@ -828,6 +841,40 @@ var GmailAdapter = class {
|
|
|
828
841
|
}
|
|
829
842
|
return result;
|
|
830
843
|
}
|
|
844
|
+
async getRawMessage(emailId, _sourceFolder) {
|
|
845
|
+
const gmail = this.ensureConnected();
|
|
846
|
+
const res = await gmail.users.messages.get({
|
|
847
|
+
userId: "me",
|
|
848
|
+
id: emailId,
|
|
849
|
+
format: "raw"
|
|
850
|
+
});
|
|
851
|
+
return Buffer.from(res.data.raw || "", "base64url");
|
|
852
|
+
}
|
|
853
|
+
async appendRawMessage(raw, targetFolder, flags) {
|
|
854
|
+
const gmail = this.ensureConnected();
|
|
855
|
+
const labelIds = [];
|
|
856
|
+
labelIds.push(targetFolder ? await this.resolveLabelId(targetFolder) : "INBOX");
|
|
857
|
+
if (flags?.read === false) labelIds.push("UNREAD");
|
|
858
|
+
if (flags?.starred === true) labelIds.push("STARRED");
|
|
859
|
+
const res = await gmail.users.messages.insert({
|
|
860
|
+
userId: "me",
|
|
861
|
+
internalDateSource: "dateHeader",
|
|
862
|
+
requestBody: { labelIds },
|
|
863
|
+
media: { mimeType: "message/rfc822", body: Readable.from(raw) }
|
|
864
|
+
});
|
|
865
|
+
return { id: res.data.id || "" };
|
|
866
|
+
}
|
|
867
|
+
/** Resolve a Gmail label name or id to a label id (accepts either). */
|
|
868
|
+
async resolveLabelId(nameOrId) {
|
|
869
|
+
const gmail = this.ensureConnected();
|
|
870
|
+
const res = await gmail.users.labels.list({ userId: "me" });
|
|
871
|
+
const labels = res.data.labels || [];
|
|
872
|
+
const byId = labels.find((l) => l.id === nameOrId);
|
|
873
|
+
if (byId?.id) return byId.id;
|
|
874
|
+
const byName = labels.find((l) => (l.name || "").toLowerCase() === nameOrId.toLowerCase());
|
|
875
|
+
if (byName?.id) return byName.id;
|
|
876
|
+
return nameOrId;
|
|
877
|
+
}
|
|
831
878
|
async addLabels(emailId, labels) {
|
|
832
879
|
const gmail = this.ensureConnected();
|
|
833
880
|
await gmail.users.messages.modify({
|
|
@@ -1209,6 +1256,41 @@ var OutlookAdapter = class {
|
|
|
1209
1256
|
destinationId
|
|
1210
1257
|
});
|
|
1211
1258
|
}
|
|
1259
|
+
async getRawMessage(emailId, _sourceFolder) {
|
|
1260
|
+
this.ensureClient();
|
|
1261
|
+
const res = await fetch(
|
|
1262
|
+
`https://graph.microsoft.com/v1.0/me/messages/${encodeURIComponent(emailId)}/$value`,
|
|
1263
|
+
{ headers: { Authorization: `Bearer ${this.accessToken}` } }
|
|
1264
|
+
);
|
|
1265
|
+
if (!res.ok) {
|
|
1266
|
+
throw new Error(`Graph $value fetch failed for ${emailId}: HTTP ${res.status}`);
|
|
1267
|
+
}
|
|
1268
|
+
return Buffer.from(await res.arrayBuffer());
|
|
1269
|
+
}
|
|
1270
|
+
async appendRawMessage(raw, targetFolder, flags) {
|
|
1271
|
+
this.ensureClient();
|
|
1272
|
+
const endpoint = targetFolder ? `https://graph.microsoft.com/v1.0/me/mailFolders/${encodeURIComponent(await this.resolveFolder(targetFolder))}/messages` : "https://graph.microsoft.com/v1.0/me/messages";
|
|
1273
|
+
const res = await fetch(endpoint, {
|
|
1274
|
+
method: "POST",
|
|
1275
|
+
headers: {
|
|
1276
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
1277
|
+
"Content-Type": "text/plain"
|
|
1278
|
+
},
|
|
1279
|
+
body: raw.toString("base64")
|
|
1280
|
+
});
|
|
1281
|
+
if (!res.ok) {
|
|
1282
|
+
const detail = await res.text().catch(() => "");
|
|
1283
|
+
throw new Error(`Graph MIME import failed: HTTP ${res.status} ${detail.slice(0, 200)}`);
|
|
1284
|
+
}
|
|
1285
|
+
const data = await res.json().catch(() => ({}));
|
|
1286
|
+
if (flags && data.id && (flags.read !== void 0 || flags.starred !== void 0)) {
|
|
1287
|
+
try {
|
|
1288
|
+
await this.markEmail(data.id, flags);
|
|
1289
|
+
} catch {
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
return { id: data.id || "" };
|
|
1293
|
+
}
|
|
1212
1294
|
async deleteEmail(emailId, permanent, _sourceFolder) {
|
|
1213
1295
|
const client = this.ensureClient();
|
|
1214
1296
|
if (permanent) {
|
|
@@ -1931,6 +2013,32 @@ var ImapAdapter = class {
|
|
|
1931
2013
|
lock.release();
|
|
1932
2014
|
}
|
|
1933
2015
|
}
|
|
2016
|
+
async getRawMessage(emailId, sourceFolder) {
|
|
2017
|
+
if (!this.client) throw new Error("Not connected");
|
|
2018
|
+
const folder = sourceFolder ? await this.resolveFolder(sourceFolder) : "INBOX";
|
|
2019
|
+
let lock;
|
|
2020
|
+
try {
|
|
2021
|
+
lock = await this.client.getMailboxLock(folder);
|
|
2022
|
+
} catch (error) {
|
|
2023
|
+
throw formatImapError(error, `Failed to open folder "${folder}"`);
|
|
2024
|
+
}
|
|
2025
|
+
try {
|
|
2026
|
+
const msg = await this.client.fetchOne(String(emailId), { source: true, uid: true }, { uid: true });
|
|
2027
|
+
if (!msg || !msg.source) throw new Error(`Email ${emailId} not found in "${folder}"`);
|
|
2028
|
+
return msg.source;
|
|
2029
|
+
} finally {
|
|
2030
|
+
lock.release();
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
async appendRawMessage(raw, targetFolder, flags) {
|
|
2034
|
+
if (!this.client) throw new Error("Not connected");
|
|
2035
|
+
const folder = targetFolder ? await this.resolveFolder(targetFolder) : "INBOX";
|
|
2036
|
+
const imapFlags = [];
|
|
2037
|
+
if (flags?.read) imapFlags.push("\\Seen");
|
|
2038
|
+
if (flags?.starred) imapFlags.push("\\Flagged");
|
|
2039
|
+
const result = await this.client.append(folder, raw, imapFlags);
|
|
2040
|
+
return { id: String(result?.uid ?? "") };
|
|
2041
|
+
}
|
|
1934
2042
|
async batchDelete(emailIds, permanent, sourceFolder) {
|
|
1935
2043
|
if (!this.client) throw new Error("Not connected");
|
|
1936
2044
|
const result = { succeeded: [], failed: [] };
|