proton-mail-bridge-client 1.14.0 → 1.16.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 CHANGED
@@ -24,12 +24,12 @@
24
24
 
25
25
  ---
26
26
 
27
- Give Claude Desktop (or Cline, or any MCP client) full access to your Proton Mail inbox: read, search, send, draft, triage threads, manage folders, save attachments, and more. The same 40+ capabilities are also available as a full CLI for scripting, cron, and piped automation — no Claude required.
27
+ Give Claude Desktop (or Cline, or any MCP client) full access to your Proton Mail inbox: read, search, send, draft, triage threads, manage folders, save attachments, and more — 78 MCP tools in total. Most of the same capabilities are also available as a full CLI for scripting, cron, and piped automation — no Claude required.
28
28
 
29
29
  ## What you get
30
30
 
31
31
  - **Claude reads and manages your Proton Mail** — triage, reply, draft, archive, search, move, batch-act on threads, pull attachments
32
- - **Full CLI** — same 40+ commands, scriptable and pipeable, works in cron and shell scripts
32
+ - **Full CLI** — 73 commands covering nearly all of the same capabilities, scriptable and pipeable, works in cron and shell scripts
33
33
  - **Fast local search** — full-text search across your inbox without hitting IMAP on every query
34
34
  - **Safety controls** — read-only mode, send gate, destructive-action confirmation, per-action allowlist
35
35
  - **Privacy-native** — no third-party email service involved; your mail stays on your machine
@@ -194,6 +194,40 @@ The wizard handles config automatically. If you need to set it up by hand, three
194
194
 
195
195
  ---
196
196
 
197
+ ## Connect to Claude Code
198
+
199
+ Install globally, then register the server with one command:
200
+
201
+ ```bash
202
+ npm install -g proton-mail-bridge-client
203
+
204
+ claude mcp add proton-mail-bridge \
205
+ -e PROTONMAIL_USERNAME=you@proton.me \
206
+ -e PROTONMAIL_PASSWORD=your-bridge-password \
207
+ -- proton-mail-bridge-mcp
208
+ ```
209
+
210
+ `your-bridge-password` is the Bridge app's own password (**Bridge → account → Mailbox details**), not your Proton account password — see the note under [Prerequisites](#prerequisites).
211
+
212
+ By default this registers the server for the current project only. Add `-s user` to make it available in every project:
213
+
214
+ ```bash
215
+ claude mcp add proton-mail-bridge -s user \
216
+ -e PROTONMAIL_USERNAME=you@proton.me \
217
+ -e PROTONMAIL_PASSWORD=your-bridge-password \
218
+ -- proton-mail-bridge-mcp
219
+ ```
220
+
221
+ Verify it's connected:
222
+
223
+ ```bash
224
+ claude mcp list
225
+ ```
226
+
227
+ For file-based or command-based credentials instead of plaintext env vars, add `-e PROTONMAIL_USERNAME_FILE=/path/to/file` (or `_COMMAND`) the same way — see the credential methods under [Manual config](#connect-to-claude-desktop) above.
228
+
229
+ ---
230
+
197
231
  ## Connect to Cline (VS Code)
198
232
 
199
233
  Install globally (`npm install -g proton-mail-bridge-client`), then open Cline's MCP settings:
@@ -247,6 +281,8 @@ Reload the Cline extension after saving. Proton Mail tools will appear in Cline'
247
281
 
248
282
  > **Tip:** When creating folders, use `Folders/Name` (not just `Name`) — that's the Proton Bridge namespace for real folders vs. labels.
249
283
 
284
+ More recipes — expanded triage prompts, cron scripts for scheduled digests, and a Claude Code `/mail-triage` slash command — are in [examples/](examples/).
285
+
250
286
  ---
251
287
 
252
288
  ## Recommended System Prompt
@@ -268,144 +304,18 @@ Rules:
268
304
 
269
305
  ## CLI
270
306
 
271
- ```bash
272
- proton-mail-bridge-client <command> [options]
273
- ```
274
-
275
- All commands support `--json` for machine-readable output.
276
-
277
- ### Read
278
-
279
- ```bash
280
- proton-mail-bridge-client emails --folder INBOX --limit 25
281
- proton-mail-bridge-client read INBOX::25642
282
- proton-mail-bridge-client search "invoice" --limit 10
283
- proton-mail-bridge-client search --live --from openai.com
284
- proton-mail-bridge-client attachments INBOX::25642
285
- ```
286
-
287
- ### Triage
288
-
289
- ```bash
290
- proton-mail-bridge-client digest
291
- proton-mail-bridge-client threads "quarterly review"
292
- proton-mail-bridge-client actionable
293
- proton-mail-bridge-client followups
294
- proton-mail-bridge-client thread-brief <threadId>
295
- proton-mail-bridge-client document-threads --category invoice
296
- proton-mail-bridge-client meeting-context alice@example.com
297
- ```
298
-
299
- ### Compose & send
300
-
301
- ```bash
302
- proton-mail-bridge-client send --to bob@example.com --subject "Hey" --body "Hello"
303
- echo "Hello" | proton-mail-bridge-client send --to bob@example.com --subject "Hey"
304
- proton-mail-bridge-client reply INBOX::25642 --body "On it."
305
- proton-mail-bridge-client reply INBOX::25642 --reply-all --body "On it."
306
- proton-mail-bridge-client forward INBOX::25642 --to carol@example.com
307
- ```
308
-
309
- ### Mailbox actions
310
-
311
- ```bash
312
- proton-mail-bridge-client move INBOX::25642 Folders/Archive
313
- proton-mail-bridge-client archive INBOX::25642
314
- proton-mail-bridge-client trash INBOX::25642
315
- proton-mail-bridge-client restore Trash::25642
316
- proton-mail-bridge-client mark-read INBOX::25642
317
- proton-mail-bridge-client mark-read INBOX::25642 --unread
318
- proton-mail-bridge-client star INBOX::25642
319
- proton-mail-bridge-client delete INBOX::25642
320
- proton-mail-bridge-client batch archive INBOX::100,INBOX::101,INBOX::102
321
- proton-mail-bridge-client thread-action <threadId> archive
322
- ```
323
-
324
- ### Folders
325
-
326
- ```bash
327
- proton-mail-bridge-client folders
328
- proton-mail-bridge-client create-folder Folders/Receipts
329
- proton-mail-bridge-client rename-folder Folders/Receipts Folders/Bills
330
- proton-mail-bridge-client delete-folder Folders/Bills
331
- ```
332
-
333
- ### Drafts
334
-
335
- ```bash
336
- proton-mail-bridge-client drafts
337
- proton-mail-bridge-client draft-create --to bob@example.com --subject "Draft" --body "..."
338
- proton-mail-bridge-client draft-read <id>
339
- proton-mail-bridge-client draft-update <id> --subject "Updated subject"
340
- proton-mail-bridge-client draft-reply INBOX::25642 --body "Will do."
341
- proton-mail-bridge-client draft-forward INBOX::25642 --to carol@example.com
342
- proton-mail-bridge-client draft-sync <id>
343
- proton-mail-bridge-client draft-send <id>
344
- proton-mail-bridge-client draft-delete <id>
345
- proton-mail-bridge-client remote-drafts
346
- ```
347
-
348
- ### Analytics & diagnostics
349
-
350
- ```bash
351
- proton-mail-bridge-client stats
352
- proton-mail-bridge-client analytics
353
- proton-mail-bridge-client contacts
354
- proton-mail-bridge-client volume-trends --days 14
355
- proton-mail-bridge-client watch --timeout 30
356
- proton-mail-bridge-client test-email you@example.com
357
- proton-mail-bridge-client doctor
358
- proton-mail-bridge-client status
359
- proton-mail-bridge-client sync --folder INBOX --limit 150
360
- ```
361
-
362
- ### Ambient notifications
363
-
364
- Run as a background daemon — sends a system notification (macOS / Linux) whenever new mail arrives:
365
-
366
- ```bash
367
- proton-mail-bridge-client notify # foreground (Ctrl+C to stop)
368
- proton-mail-bridge-client notify & # background
369
- proton-mail-bridge-client notify --folder INBOX --timeout 60 # custom folder and idle timeout
370
- ```
371
-
372
- Each event is also written as a JSON line to stdout:
373
-
374
- ```json
375
- {"event":"new_mail","folder":"INBOX","count":2,"at":"2026-05-18T14:32:01.000Z"}
376
- ```
377
-
378
- Uses IMAP IDLE — no polling between events. Reconnects automatically on transient errors.
379
-
380
- ### MCP tool passthrough
381
-
382
- Any MCP tool is also callable directly from the CLI:
307
+ Every capability is also a scriptable terminal command — no Claude required:
383
308
 
384
309
  ```bash
385
- proton-mail-bridge-client tools
386
- proton-mail-bridge-client tool get_connection_status --json
387
- proton-mail-bridge-client tool search_indexed_emails --args '{"query":"invoice","limit":3}'
310
+ proton-mail-bridge-client digest # morning triage summary
311
+ proton-mail-bridge-client search --from stripe.com --json | jq . # scriptable search
312
+ echo "Deploy done" | proton-mail-bridge-client send --to you@x.com --subject "Deploy"
313
+ proton-mail-bridge-client notify & # background new-mail alerts
388
314
  ```
389
315
 
390
- ### Pipe and script
391
-
392
- ```bash
393
- # Morning digest to a file
394
- proton-mail-bridge-client digest --json > ~/morning-mail.json
316
+ All commands support `--json` for machine-readable output, and any MCP tool is directly callable via `proton-mail-bridge-client tool <name> --args '{...}'`.
395
317
 
396
- # Pull every email from a domain
397
- proton-mail-bridge-client search --from stripe.com --json | jq '.[].subject'
398
-
399
- # Pipe a script's output directly into an email
400
- echo "Deploy complete on $(hostname) at $(date)" \
401
- | proton-mail-bridge-client send --to alerts@example.com --subject "Deploy done"
402
-
403
- # Scheduled digest every weekday at 8am (cron)
404
- 0 8 * * 1-5 proton-mail-bridge-client digest >> ~/mail-log.txt
405
-
406
- # Count unread in INBOX
407
- proton-mail-bridge-client emails --folder INBOX --json | jq '[.[] | select(.isRead == false)] | length'
408
- ```
318
+ **Full command reference: [docs/cli.md](docs/cli.md)** (73 commands across read, triage, compose, mailbox actions, folders, drafts, analytics, and diagnostics).
409
319
 
410
320
  ---
411
321
 
@@ -500,7 +410,7 @@ PROTONMAIL_IDLE_MAX_SECONDS='30'
500
410
  `mark_email_read` · `star_email` · `move_email` · `archive_email` · `trash_email` · `restore_email` · `delete_email` · `batch_email_action` · `apply_thread_action` · `empty_folder` · `bulk_delete` · `bulk_move` · `bulk_update_flags` · `bulk_update_labels` · `update_message_flags` · `update_message_labels`
501
411
 
502
412
  ### Folder management
503
- `create_folder` · `rename_folder` · `delete_folder` · `create_label`
413
+ `create_folder` · `rename_folder` · `delete_folder` · `create_label` · `rename_label` · `delete_label`
504
414
 
505
415
  ### Analytics
506
416
  `get_email_stats` · `get_email_analytics` · `get_contacts` · `get_volume_trends` · `folder_stats` · `top_senders`
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAOA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAYnE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,EAYV,gBAAgB,EACjB,MAAM,kBAAkB,CAAC;AAq/E1B,wBAAgB,kBAAkB,IAAI,gBAAgB,CAuFrD;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,gBAAgB,EACxB,OAAO,GAAE;IACP,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4yEP;AAED,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAmD1C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAOA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAYnE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAwD,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC5H,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,EAYV,gBAAgB,EACjB,MAAM,kBAAkB,CAAC;AAq/E1B,wBAAgB,kBAAkB,IAAI,gBAAgB,CA0FrD;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,gBAAgB,EACxB,OAAO,GAAE;IACP,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAq2EP;AAED,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAmD1C"}
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import { AuditService } from "./services/audit-service.js";
13
13
  import { BackgroundSyncService } from "./services/background-sync-service.js";
14
14
  import { DraftStoreService } from "./services/draft-store-service.js";
15
15
  import { LocalIndexService } from "./services/local-index-service.js";
16
- import { SimpleIMAPService } from "./services/simple-imap-service.js";
16
+ import { isLikelyAuthenticationError, isLikelyConnectionError, SimpleIMAPService } from "./services/simple-imap-service.js";
17
17
  import { SMTPService } from "./services/smtp-service.js";
18
18
  import { ensureValidEmails, isTextLikeMimeType, isValidEmail, lowerCaseAddress, normalizeBoolean, normalizeLimit, normalizeJsonValue, parseEmails, renderMarkdown, stringifyForJson, } from "./utils/helpers.js";
19
19
  import { logger } from "./utils/logger.js";
@@ -1043,7 +1043,7 @@ const TOOLS = [
1043
1043
  },
1044
1044
  {
1045
1045
  name: "run_doctor",
1046
- description: "Run a comprehensive production health check covering SMTP auth, IMAP auth, optional IMAP IDLE probe, SQLite index integrity, and runtime policy validation. Use to fully diagnose or validate the setup. Prefer get_connection_status for a quick protocol-only reachability check.",
1046
+ description: "Run a comprehensive production health check covering SMTP auth, IMAP auth, optional IMAP IDLE probe, SQLite index integrity, sync-failed drafts, runtime policy validation, and what this server can/cannot do (capabilities). Connection failures include a classified diagnosis (authentication_failed vs bridge_unreachable) with a specific fix. Use to fully diagnose or validate the setup. Prefer get_connection_status for a quick protocol-only reachability check.",
1047
1047
  annotations: { readOnlyHint: true },
1048
1048
  inputSchema: {
1049
1049
  type: "object",
@@ -2355,7 +2355,10 @@ export function buildConfigFromEnv() {
2355
2355
  allowRemoteDraftSync,
2356
2356
  allowedActions: parseAllowedActionsEnv("PROTONMAIL_ALLOWED_ACTIONS"),
2357
2357
  startupSync: parseBooleanEnv("PROTONMAIL_STARTUP_SYNC", autoSync),
2358
- autoSyncFolder: process.env.PROTONMAIL_AUTO_SYNC_FOLDER?.trim() || "INBOX",
2358
+ // Comma-separated; Sent is included by default so pendingOn/digest/follow-up
2359
+ // candidates don't misreport threads that were already answered — IDLE still
2360
+ // only watches the first folder (see BackgroundSyncService.primaryIdleFolder).
2361
+ autoSyncFolder: process.env.PROTONMAIL_AUTO_SYNC_FOLDER?.trim() || "INBOX,Sent",
2359
2362
  autoSyncFull: parseBooleanEnv("PROTONMAIL_AUTO_SYNC_FULL", false),
2360
2363
  autoSyncLimitPerFolder: parseIntegerEnv("PROTONMAIL_AUTO_SYNC_LIMIT_PER_FOLDER", 100, 1, 500),
2361
2364
  idleWatchEnabled,
@@ -3730,7 +3733,7 @@ export function createServer(config, options = {}) {
3730
3733
  const includeImap = normalizeBoolean(args.includeImap, true);
3731
3734
  const includeIdleProbe = normalizeBoolean(args.includeIdleProbe, false);
3732
3735
  const idleTimeoutSeconds = normalizeLimit(args.idleTimeoutSeconds, 5, 1, 60);
3733
- const [smtpStatus, imapStatus, indexStatus, integrity] = await Promise.all([
3736
+ const [smtpStatus, imapStatus, indexStatus, integrity, drafts] = await Promise.all([
3734
3737
  includeSmtp
3735
3738
  ? Promise.allSettled([smtpService.verifyConnection()]).then(([result]) => result)
3736
3739
  : Promise.resolve({ status: "fulfilled", value: undefined }),
@@ -3739,6 +3742,7 @@ export function createServer(config, options = {}) {
3739
3742
  : Promise.resolve({ status: "fulfilled", value: undefined }),
3740
3743
  localIndexService.getStatus(),
3741
3744
  localIndexService.runIntegrityCheck(),
3745
+ draftStore.listDrafts(true),
3742
3746
  ]);
3743
3747
  const idleProbe = includeIdleProbe
3744
3748
  ? await Promise.allSettled([
@@ -3748,6 +3752,24 @@ export function createServer(config, options = {}) {
3748
3752
  }),
3749
3753
  ]).then(([result]) => result)
3750
3754
  : undefined;
3755
+ // Classifies the connection failure into a specific, actionable cause —
3756
+ // without PROTONMAIL_DEBUG this used to just say "connection failed" for
3757
+ // both a wrong password and Bridge not being open at all.
3758
+ const diagnoseFailure = (reason) => {
3759
+ if (isLikelyAuthenticationError(reason)) {
3760
+ return {
3761
+ cause: "authentication_failed",
3762
+ suggestion: "PROTONMAIL_PASSWORD must be the Proton Bridge password (Bridge app -> account -> Mailbox details), not your Proton account password. Confirm you're signed in inside the Bridge app.",
3763
+ };
3764
+ }
3765
+ if (isLikelyConnectionError(reason)) {
3766
+ return {
3767
+ cause: "bridge_unreachable",
3768
+ suggestion: "Proton Bridge isn't reachable on the configured host/port. Make sure the Bridge app is running, and that PROTONMAIL_IMAP_HOST/PORT and PROTONMAIL_SMTP_HOST/PORT match Bridge's Mailbox details.",
3769
+ };
3770
+ }
3771
+ return undefined;
3772
+ };
3751
3773
  return createTextResult({
3752
3774
  checkedAt: new Date().toISOString(),
3753
3775
  runtime: sanitizeRuntimeConfig(config.runtime),
@@ -3761,6 +3783,7 @@ export function createServer(config, options = {}) {
3761
3783
  ? smtpStatus.reason.message
3762
3784
  : String(smtpStatus.reason)
3763
3785
  : "SMTP connection failed.",
3786
+ ...(smtpStatus.status === "rejected" ? { diagnosis: diagnoseFailure(smtpStatus.reason) } : {}),
3764
3787
  },
3765
3788
  imap: {
3766
3789
  ok: imapStatus.status === "fulfilled",
@@ -3773,6 +3796,7 @@ export function createServer(config, options = {}) {
3773
3796
  ? imapStatus.reason.message
3774
3797
  : String(imapStatus.reason)
3775
3798
  : "IMAP connection failed.",
3799
+ ...(imapStatus.status === "rejected" ? { diagnosis: diagnoseFailure(imapStatus.reason) } : {}),
3776
3800
  },
3777
3801
  idleProbe: idleProbe === undefined
3778
3802
  ? { skipped: true }
@@ -3789,9 +3813,28 @@ export function createServer(config, options = {}) {
3789
3813
  backgroundSync: backgroundSyncService.getStatus(),
3790
3814
  index: indexStatus,
3791
3815
  integrity,
3816
+ drafts: {
3817
+ total: drafts.length,
3818
+ syncFailed: drafts.filter((draft) => draft.remoteSyncState === "sync_failed").length,
3819
+ syncFailedIds: drafts
3820
+ .filter((draft) => draft.remoteSyncState === "sync_failed")
3821
+ .map((draft) => draft.id),
3822
+ },
3792
3823
  audit: {
3793
3824
  path: auditService.getPath(),
3794
3825
  },
3826
+ // What this server can and cannot do, given Proton Bridge only proxies
3827
+ // local IMAP/SMTP — prevents an agent from attempting an impossible
3828
+ // operation (e.g. server-side filters) and getting a confusing failure.
3829
+ capabilities: {
3830
+ supported: ["mail read/search/send", "folders", "labels (IMAP folders under Labels/)", "drafts", "local full-text index"],
3831
+ notSupported: [
3832
+ "server-side filters/rules (no ManageSieve access through Bridge)",
3833
+ "real contacts / address book (no CardDAV access through Bridge)",
3834
+ "calendar (no CalDAV access through Bridge)",
3835
+ "vacation responder (Proton account API only, not exposed via Bridge)",
3836
+ ],
3837
+ },
3795
3838
  });
3796
3839
  }
3797
3840
  case "get_runtime_status": {
@@ -4286,7 +4329,13 @@ export function createServer(config, options = {}) {
4286
4329
  throw error;
4287
4330
  }
4288
4331
  logger.error("Tool call failed", "MCPServer", { name, error });
4289
- throw new McpError(ErrorCode.InternalError, "An internal error occurred. Check get_logs for details.");
4332
+ if (isLikelyAuthenticationError(error)) {
4333
+ throw new McpError(ErrorCode.InternalError, "IMAP authentication failed. Check that PROTONMAIL_PASSWORD is your Proton Bridge password (not your Proton account password) and that you're signed in inside the Bridge app. Run run_doctor for a full connectivity check.");
4334
+ }
4335
+ if (isLikelyConnectionError(error)) {
4336
+ throw new McpError(ErrorCode.InternalError, "Could not connect to Proton Bridge. Make sure the Bridge app is running, and that PROTONMAIL_IMAP_HOST/PORT and PROTONMAIL_SMTP_HOST/PORT match the ports shown in Bridge's settings. Run run_doctor for a full connectivity check.");
4337
+ }
4338
+ throw new McpError(ErrorCode.InternalError, "An internal error occurred. Check get_logs for details, or run run_doctor for a full connectivity check.");
4290
4339
  }
4291
4340
  });
4292
4341
  return {