@lengelhard/imap-email-mcp 1.4.1 → 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.
Files changed (2) hide show
  1. package/index.js +25 -22
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -393,7 +393,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
393
393
  },
394
394
  {
395
395
  name: 'download_attachment',
396
- description: 'Download an email attachment. By default the file is decoded and saved to disk (ATTACHMENT_DIR, default ~/Downloads/email-attachments) and the local file path is returned. Set return_base64=true to get the content inline instead (only allowed for attachments under 700KB).',
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.',
397
397
  inputSchema: {
398
398
  type: 'object',
399
399
  properties: {
@@ -420,8 +420,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
420
420
  },
421
421
  return_base64: {
422
422
  type: 'boolean',
423
- description: 'Return content inline as base64 instead of saving to disk. Fails for attachments over 700KB; prefer the default file mode.',
424
- 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.'
425
424
  }
426
425
  },
427
426
  required: ['uid']
@@ -1081,23 +1080,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1081
1080
  const filename = sanitizeFilename(attachmentInfo?.filename || args.filename);
1082
1081
  const contentType = attachmentInfo?.contentType || 'application/octet-stream';
1083
1082
 
1084
- // Inline mode: opt-in only, and only when it safely fits in a tool result
1085
- if (args.return_base64) {
1086
- if (buffer.length > MAX_INLINE_BYTES) {
1087
- return {
1088
- content: [{
1089
- type: 'text',
1090
- text: JSON.stringify({
1091
- error: `Attachment is ${buffer.length} bytes; inline base64 is limited to ${MAX_INLINE_BYTES} bytes because the encoded result would exceed the MCP tool-result size cap. Call again without return_base64 to save it to disk and get the file path.`,
1092
- uid: args.uid,
1093
- partID,
1094
- filename,
1095
- size_bytes: buffer.length
1096
- }, null, 2)
1097
- }],
1098
- isError: true
1099
- };
1100
- }
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) {
1101
1096
  return {
1102
1097
  content: [{
1103
1098
  type: 'text',
@@ -1107,6 +1102,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1107
1102
  filename,
1108
1103
  contentType,
1109
1104
  size_bytes: buffer.length,
1105
+ mode: 'base64',
1110
1106
  encoding: 'base64',
1111
1107
  data: buffer.toString('base64')
1112
1108
  }, null, 2)
@@ -1114,7 +1110,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1114
1110
  };
1115
1111
  }
1116
1112
 
1117
- // Default mode: save to disk and return the path
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
1118
1119
  let target = args.save_path || ATTACHMENT_DIR;
1119
1120
  let filePath;
1120
1121
  if (args.save_path && path.extname(args.save_path)) {
@@ -1142,7 +1143,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1142
1143
  filename,
1143
1144
  contentType,
1144
1145
  size_bytes: buffer.length,
1145
- saved_to: filePath
1146
+ mode: 'file',
1147
+ saved_to: filePath,
1148
+ ...(fallbackNote ? { note: fallbackNote } : {})
1146
1149
  }, null, 2)
1147
1150
  }]
1148
1151
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lengelhard/imap-email-mcp",
3
- "version": "1.4.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",