@lengelhard/imap-email-mcp 1.9.1 → 1.10.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/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.10.0
4
+
5
+ ### Added
6
+ - **Replies stay in their thread.** `send_email`, `create_draft` and `update_draft` take `reply_to_uid` (with `reply_to_folder`), `in_reply_to` and `references`. Until now the connector had no way to set In-Reply-To or References, so every message it sent was a new conversation in the recipient's mail client and in Missive, even with the thread's subject line (seen 28 Sep 2026). With `reply_to_uid` the server reads the original's Message-ID, References and Subject and builds the headers as RFC 5322 §3.6.4 describes: In-Reply-To is the parent's Message-ID, References is the parent's References followed by its Message-ID. With `in_reply_to` alone, the chain is completed from the local index when it holds the parent, and is that id alone otherwise; the response says which. The subject is optional when replying and defaults to `Re: ` + the original subject; a caller-supplied subject without a reply prefix gets `Re: ` added, an existing `RE:`/`AW:` is kept. A stale `reply_to_uid` fails before anything is sent. Responses report `message_id`, `in_reply_to`, `references` and `threaded_from`, or `threaded: false`.
7
+ - `list_drafts` and `get_draft` return `message_id`, `in_reply_to` and (on `get_draft`) `references`, and `list_drafts` reports `total`.
8
+ - `findSentFolder`, `findDraftsFolder` and `findTrashFolder` all prefer the mailbox the server flags with SPECIAL-USE (`\Sent`, `\Drafts`, `\Trash`) and fall back to the usual names, so the copy of a sent message lands where the user's other clients look for it.
9
+
10
+ ### Changed
11
+ - **Outgoing messages are built by nodemailer's MailComposer**, in `send_email`, `create_draft` and `update_draft`. The hand-assembled RFC 5322 strings put non-ASCII subjects into headers as raw UTF-8, sent bodies with no Content-Transfer-Encoding, and let a value containing a newline inject a header. Headers are now RFC 2047 encoded and folded, the body is encoded, and Message-ID and Date are set. The copy saved to Sent is the message that went on the wire (same Message-ID, same bytes) plus the Bcc header, which is stripped from the wire copy; previously it was a second, differently structured message assembled by hand.
12
+ - `update_draft` appends the replacement draft first and erases the old one only once the new one is on the server. Previously the old draft was expunged before the new one was appended, so a failed append lost the draft. A `uid` that does not exist now changes nothing and says so.
13
+ - Every tool now runs its IMAP commands under the `IMAP_OP_TIMEOUT_SECONDS` limit introduced in 1.7.3. `list_drafts`, `get_draft`, `create_draft`, `update_draft`, `download_attachment`, `extract_text`, `delete_email`, `flag_email`, `mark_seen`, `move_thread`, `list_folders` and the special-folder lookups still called the underlying library directly, so on a silent connection they waited out the 5-minute lease instead of failing in two.
14
+ - `download_attachment`, `extract_text` and the draft readers open their folder read-only (EXAMINE), so reading never changes flags.
15
+
16
+ ### Fixed
17
+ - **`delete_email` with `expunge: true` and `update_draft` erased every message in the folder flagged `\Deleted`, not just the one asked for.** Both flagged the target and then closed the mailbox with expunge, which is a mailbox-wide operation: a message another client had flagged `\Deleted` but not yet expunged was erased too. They now use UID EXPUNGE (UIDPLUS), scoped to the one message. On a server without UIDPLUS the call fails and takes the flag back off rather than erasing mail nobody asked about. `move_email`/`move_thread` share the same helper, which also closes the 1.9.2 gap where a missing UIDPLUS would have thrown outside the promise.
18
+ - `flag_email`, `mark_seen` and `delete_email` reported `success: true` for a UID that does not exist, because IMAP STORE on a missing UID is a silent no-op. They now check the UID first and fail with the same "UIDs change when a message is moved" hint as `get_email`.
19
+ - `list_folders` joined nested folder names with `.` regardless of the server's hierarchy delimiter, so a nested folder on a `/`-delimited server (Dovecot, Migadu) came back under a name no other tool would accept. It now uses each mailbox's own delimiter.
20
+
21
+ ## 1.9.2
22
+
23
+ ### Fixed
24
+ - `move_email` and `move_thread` no longer create a duplicate when the destination already holds the message. A mail client that files by label can leave the same message in INBOX and in the label's folder at once; moving the INBOX copy then held it twice in the destination (seen on 25 Sep 2026 after a scheduled inbox sweep). The move now checks the destination for the same Message-ID first: if found, the source copy is removed instead of copied, and the response says `action: "already_present"` (`move_email`) or lists `already_present` (`move_thread`). A message with no Message-ID is moved the plain way.
25
+ - `references` is no longer returned by `list_emails`, `search_emails` and `search_index`. On long threads it was 20 to 30 Message-IDs per message, several times the size of the rest of the result, and the tasks that read listings never use it. It stays on `get_email`, and `find_split_threads` threads on it as before. `in_reply_to` stays everywhere.
26
+
3
27
  ## 1.9.1
4
28
 
5
29
  ### Fixed
package/README.md CHANGED
@@ -74,6 +74,7 @@ Add to your config file:
74
74
  - **Local full-text index** - Optional read-only SQLite index for reliable content search, independent of the server's body search
75
75
  - **Compose** - Create and save email drafts
76
76
  - **Send** - Send emails directly via SMTP
77
+ - **Reply in thread** - Replies carry In-Reply-To and References, so mail clients keep them in the conversation instead of starting a new one
77
78
  - **Manage drafts** - List, read, update, and delete drafts
78
79
  - **Find duplicates** - Spot messages filed twice in one folder, or the same message sitting in several folders, matched on Message-ID
79
80
  - **Delete emails** - Move to Trash by default, with an explicit opt-in to erase permanently
@@ -140,6 +141,16 @@ A conversation can end up spread across folders without any message being duplic
140
141
  - **Honest coverage.** Rows indexed before 1.9.0 have no threading headers until the background sync backfills them, which it does at the end of each scheduled run for up to `INDEX_BACKFILL_SECONDS`, smallest folders first, without ever delaying the indexing of new mail. `coverage.without_thread_headers` and `index_status.thread_headers_pending` show how many are still waiting, and a thread touching such rows is marked `threading: "partial"`.
141
142
  - **Nothing is moved.** The tool only reports. `move_email` or `move_thread` are the tools for acting on it.
142
143
 
144
+ ### Replying in a thread
145
+
146
+ Mail clients group messages into conversations by the In-Reply-To and References headers, not by subject. A message sent without them is a new conversation in every client, however familiar its subject looks. `send_email`, `create_draft` and `update_draft` therefore take:
147
+
148
+ - **`reply_to_uid` + `reply_to_folder`** — the message being answered. The server reads its Message-ID, References and Subject and writes `In-Reply-To` and `References` itself (the parent's References followed by the parent's Message-ID, as RFC 5322 requires). Always pass the folder: UIDs are folder-scoped.
149
+ - **`in_reply_to`** — the parent's Message-ID, as returned by `list_emails`, `search_emails` or `get_email`. When the local index holds that message the References chain is completed from it; otherwise References is that id alone, and the response says so.
150
+ - **`references`** — an explicit chain, rarely needed.
151
+
152
+ The subject is optional when replying and defaults to `Re: ` + the original subject. A caller-supplied subject with no `Re:`/`AW:`-style prefix gets `Re: ` added. The response reports `message_id`, `in_reply_to`, `references` and `threaded_from`, or `threaded: false` when no reply target was given. Messages are composed with nodemailer's MailComposer: non-ASCII headers are RFC 2047 encoded, bodies carry a Content-Transfer-Encoding, and the copy saved to Sent is the message that went out (plus the Bcc header, which is stripped on the wire).
153
+
143
154
  ### Provider Settings
144
155
 
145
156
  | Provider | IMAP_HOST | SMTP_HOST | Notes |
@@ -163,12 +174,14 @@ A conversation can end up spread across folders without any message being duplic
163
174
  | `sync_index` | Run an index sync now; `force: true` aborts a stuck sync first |
164
175
  | `find_duplicates` | Messages that exist more than once, matched on Message-ID: filed twice in one folder, or spread across folders |
165
176
  | `find_split_threads` | Conversations spread across folders, threaded on In-Reply-To and References so renamed replies stay with their thread |
166
- | `list_drafts` | List all draft emails |
167
- | `get_draft` | Get a specific draft by UID |
168
- | `create_draft` | Create a new email draft |
169
- | `update_draft` | Update an existing draft |
170
- | `send_email` | Send an email directly |
171
- | `delete_email` | Delete an email by UID. Moves to Trash unless `expunge: true` |
177
+ | `move_email` | Move one message by UID. If the destination already holds it (same Message-ID) the source copy is removed instead, never duplicated |
178
+ | `move_thread` | Move every message of a thread by subject in one call, with the same duplicate-safe behaviour |
179
+ | `list_drafts` | List drafts newest first, with `message_id` and `in_reply_to` |
180
+ | `get_draft` | Get a specific draft by UID, with its threading headers |
181
+ | `create_draft` | Create a draft; `reply_to_uid`/`in_reply_to` make it a threaded reply |
182
+ | `update_draft` | Replace a draft: the new one is appended before the old one is erased |
183
+ | `send_email` | Send via SMTP and save to Sent; `reply_to_uid`/`in_reply_to` keep a reply in its thread |
184
+ | `delete_email` | Delete an email by UID. Moves to Trash unless `expunge: true`, which erases that one message (UID EXPUNGE) |
172
185
  | `download_attachment` | Download an attachment: ≤700KB returns inline base64, larger saves to disk (ATTACHMENT_DIR) |
173
186
  | `extract_text` | Extract plain text from a PDF, xlsx/xlsm/xls, docx, or text attachment in one call, parsed server-side |
174
187
 
@@ -179,6 +192,7 @@ Once configured, use natural language:
179
192
  - "Check my inbox for unread emails"
180
193
  - "Search for emails from john@example.com"
181
194
  - "Create a draft email to sarah@example.com about the meeting tomorrow"
195
+ - "Reply to the latest message from Alicia in Action and confirm the figures" (the reply stays in her thread)
182
196
  - "Show me my drafts folder"
183
197
 
184
198
  ## Security Best Practices