@lengelhard/imap-email-mcp 1.4.0 → 1.5.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/index.js +35 -26
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -203,7 +203,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
203
203
|
},
|
|
204
204
|
{
|
|
205
205
|
name: 'get_email',
|
|
206
|
-
description: 'Get full email content by UID',
|
|
206
|
+
description: 'Get full email content by UID. IMPORTANT: UIDs are folder-scoped. Always pass the same folder the UID came from (list_emails or search_emails). Omitting folder opens INBOX and may silently return a different message.',
|
|
207
207
|
inputSchema: {
|
|
208
208
|
type: 'object',
|
|
209
209
|
properties: {
|
|
@@ -239,6 +239,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
239
239
|
type: 'string',
|
|
240
240
|
description: 'Search by sender'
|
|
241
241
|
},
|
|
242
|
+
to: {
|
|
243
|
+
type: 'string',
|
|
244
|
+
description: 'Search by recipient (use with folder: "Sent" to find outgoing mail)'
|
|
245
|
+
},
|
|
242
246
|
body: {
|
|
243
247
|
type: 'string',
|
|
244
248
|
description: 'Search in body text'
|
|
@@ -389,7 +393,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
389
393
|
},
|
|
390
394
|
{
|
|
391
395
|
name: 'download_attachment',
|
|
392
|
-
description: 'Download an email attachment.
|
|
396
|
+
description: 'Download an email attachment. Mode is automatic: attachments up to 700KB are returned inline as base64; larger ones are decoded and saved to disk (ATTACHMENT_DIR, default ~/Downloads/email-attachments) with the file path returned. Every response includes a "mode" field ("base64" or "file"). Set return_base64 explicitly to force a mode; forcing base64 on an oversized attachment falls back to disk with a note instead of erroring.',
|
|
393
397
|
inputSchema: {
|
|
394
398
|
type: 'object',
|
|
395
399
|
properties: {
|
|
@@ -416,8 +420,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
416
420
|
},
|
|
417
421
|
return_base64: {
|
|
418
422
|
type: 'boolean',
|
|
419
|
-
description: '
|
|
420
|
-
default: false
|
|
423
|
+
description: 'Optional. Omit for automatic mode: attachments up to 700KB return inline base64, larger ones are saved to disk. Set true/false to force a mode; true on an oversized attachment falls back to disk with a note.'
|
|
421
424
|
}
|
|
422
425
|
},
|
|
423
426
|
required: ['uid']
|
|
@@ -680,6 +683,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
680
683
|
type: 'text',
|
|
681
684
|
text: JSON.stringify({
|
|
682
685
|
uid: msg.attributes.uid,
|
|
686
|
+
folder,
|
|
683
687
|
from: parsed.from?.text,
|
|
684
688
|
to: parsed.to?.text,
|
|
685
689
|
cc: parsed.cc?.text,
|
|
@@ -691,9 +695,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
691
695
|
// Start with what mailparser found (covers Content-Disposition: attachment
|
|
692
696
|
// and inline+filename cases for content it fully parsed).
|
|
693
697
|
const fromParser = new Map(
|
|
694
|
-
(parsed.attachments || []).map(a => [
|
|
695
|
-
|
|
696
|
-
{ filename: a.filename, contentType: a.contentType, size: a.size }
|
|
698
|
+
(parsed.attachments || []).map((a, i) => [
|
|
699
|
+
a.filename ? a.filename.toLowerCase() : `__unnamed_${i}`,
|
|
700
|
+
{ filename: a.filename || null, contentType: a.contentType, size: a.size }
|
|
697
701
|
])
|
|
698
702
|
);
|
|
699
703
|
|
|
@@ -734,6 +738,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
734
738
|
let searchCriteria = [];
|
|
735
739
|
if (args.subject) searchCriteria.push(['SUBJECT', args.subject]);
|
|
736
740
|
if (args.from) searchCriteria.push(['FROM', args.from]);
|
|
741
|
+
if (args.to) searchCriteria.push(['TO', args.to]);
|
|
737
742
|
if (args.body) searchCriteria.push(['BODY', args.body]);
|
|
738
743
|
|
|
739
744
|
if (searchCriteria.length === 0) {
|
|
@@ -1075,23 +1080,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1075
1080
|
const filename = sanitizeFilename(attachmentInfo?.filename || args.filename);
|
|
1076
1081
|
const contentType = attachmentInfo?.contentType || 'application/octet-stream';
|
|
1077
1082
|
|
|
1078
|
-
//
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
}],
|
|
1092
|
-
isError: true
|
|
1093
|
-
};
|
|
1094
|
-
}
|
|
1083
|
+
// Resolve mode:
|
|
1084
|
+
// - return_base64 omitted -> auto: inline when it fits, disk otherwise
|
|
1085
|
+
// - return_base64: true -> inline, but fall back to disk (with a note)
|
|
1086
|
+
// if oversized instead of erroring
|
|
1087
|
+
// - return_base64: false -> disk
|
|
1088
|
+
const fitsInline = buffer.length <= MAX_INLINE_BYTES;
|
|
1089
|
+
const wantInline = args.return_base64 === true
|
|
1090
|
+
? true
|
|
1091
|
+
: args.return_base64 === false
|
|
1092
|
+
? false
|
|
1093
|
+
: fitsInline;
|
|
1094
|
+
|
|
1095
|
+
if (wantInline && fitsInline) {
|
|
1095
1096
|
return {
|
|
1096
1097
|
content: [{
|
|
1097
1098
|
type: 'text',
|
|
@@ -1101,6 +1102,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1101
1102
|
filename,
|
|
1102
1103
|
contentType,
|
|
1103
1104
|
size_bytes: buffer.length,
|
|
1105
|
+
mode: 'base64',
|
|
1104
1106
|
encoding: 'base64',
|
|
1105
1107
|
data: buffer.toString('base64')
|
|
1106
1108
|
}, null, 2)
|
|
@@ -1108,7 +1110,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1108
1110
|
};
|
|
1109
1111
|
}
|
|
1110
1112
|
|
|
1111
|
-
//
|
|
1113
|
+
// Note attached to the result when inline was requested but not possible
|
|
1114
|
+
const fallbackNote = (wantInline && !fitsInline)
|
|
1115
|
+
? `Attachment is ${buffer.length} bytes, over the ${MAX_INLINE_BYTES}-byte inline limit; saved to disk instead.`
|
|
1116
|
+
: undefined;
|
|
1117
|
+
|
|
1118
|
+
// Disk mode: save and return the path
|
|
1112
1119
|
let target = args.save_path || ATTACHMENT_DIR;
|
|
1113
1120
|
let filePath;
|
|
1114
1121
|
if (args.save_path && path.extname(args.save_path)) {
|
|
@@ -1136,7 +1143,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1136
1143
|
filename,
|
|
1137
1144
|
contentType,
|
|
1138
1145
|
size_bytes: buffer.length,
|
|
1139
|
-
|
|
1146
|
+
mode: 'file',
|
|
1147
|
+
saved_to: filePath,
|
|
1148
|
+
...(fallbackNote ? { note: fallbackNote } : {})
|
|
1140
1149
|
}, null, 2)
|
|
1141
1150
|
}]
|
|
1142
1151
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lengelhard/imap-email-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "MCP server for Claude Code that provides email capabilities through IMAP/SMTP. Read, search, compose, and manage emails from any IMAP provider.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|