proton-mail-bridge-client 2.15.1 → 2.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 +40 -7
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +49 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +57 -4
- package/dist/index.js.map +1 -1
- package/dist/scripts/install-claude-desktop.d.ts.map +1 -1
- package/dist/scripts/install-claude-desktop.js +13 -13
- package/dist/scripts/install-claude-desktop.js.map +1 -1
- package/dist/services/account-manager.d.ts +4 -0
- package/dist/services/account-manager.d.ts.map +1 -1
- package/dist/services/account-manager.js +6 -0
- package/dist/services/account-manager.js.map +1 -1
- package/dist/services/autoresponder-service.d.ts +1 -1
- package/dist/services/autoresponder-service.d.ts.map +1 -1
- package/dist/services/autoresponder-service.js +4 -0
- package/dist/services/autoresponder-service.js.map +1 -1
- package/dist/services/brief-service.d.ts +42 -0
- package/dist/services/brief-service.d.ts.map +1 -0
- package/dist/services/brief-service.js +47 -0
- package/dist/services/brief-service.js.map +1 -0
- package/dist/services/commitment-service.d.ts +39 -0
- package/dist/services/commitment-service.d.ts.map +1 -0
- package/dist/services/commitment-service.js +83 -0
- package/dist/services/commitment-service.js.map +1 -0
- package/dist/services/imap-errors.d.ts +6 -0
- package/dist/services/imap-errors.d.ts.map +1 -0
- package/dist/services/imap-errors.js +135 -0
- package/dist/services/imap-errors.js.map +1 -0
- package/dist/services/simple-imap-service.d.ts +2 -5
- package/dist/services/simple-imap-service.d.ts.map +1 -1
- package/dist/services/simple-imap-service.js +2 -133
- package/dist/services/simple-imap-service.js.map +1 -1
- package/dist/services/triage-plan-service.d.ts +56 -0
- package/dist/services/triage-plan-service.d.ts.map +1 -0
- package/dist/services/triage-plan-service.js +83 -0
- package/dist/services/triage-plan-service.js.map +1 -0
- package/dist/tools/args.d.ts +2 -0
- package/dist/tools/args.d.ts.map +1 -1
- package/dist/tools/args.js +16 -0
- package/dist/tools/args.js.map +1 -1
- package/dist/tools/brief.d.ts +4 -0
- package/dist/tools/brief.d.ts.map +1 -0
- package/dist/tools/brief.js +73 -0
- package/dist/tools/brief.js.map +1 -0
- package/dist/tools/commitments.d.ts +16 -0
- package/dist/tools/commitments.d.ts.map +1 -0
- package/dist/tools/commitments.js +145 -0
- package/dist/tools/commitments.js.map +1 -0
- package/dist/tools/context.d.ts +2 -0
- package/dist/tools/context.d.ts.map +1 -1
- package/dist/tools/context.js +3 -0
- package/dist/tools/context.js.map +1 -1
- package/dist/tools/definitions.d.ts +271 -0
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +123 -0
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/drafts.js +14 -14
- package/dist/tools/drafts.js.map +1 -1
- package/dist/tools/message-actions.d.ts.map +1 -1
- package/dist/tools/message-actions.js +8 -7
- package/dist/tools/message-actions.js.map +1 -1
- package/dist/tools/reading.d.ts.map +1 -1
- package/dist/tools/reading.js +77 -23
- package/dist/tools/reading.js.map +1 -1
- package/dist/tools/rules-runner.d.ts.map +1 -1
- package/dist/tools/rules-runner.js +4 -1
- package/dist/tools/rules-runner.js.map +1 -1
- package/dist/tools/rules.d.ts.map +1 -1
- package/dist/tools/rules.js +5 -0
- package/dist/tools/rules.js.map +1 -1
- package/dist/tools/sending.js +11 -11
- package/dist/tools/sending.js.map +1 -1
- package/dist/tools/style.d.ts +4 -0
- package/dist/tools/style.d.ts.map +1 -0
- package/dist/tools/style.js +94 -0
- package/dist/tools/style.js.map +1 -0
- package/dist/tools/triage.d.ts +4 -0
- package/dist/tools/triage.d.ts.map +1 -0
- package/dist/tools/triage.js +197 -0
- package/dist/tools/triage.js.map +1 -0
- package/dist/utils/commitment-phrases.d.ts +10 -0
- package/dist/utils/commitment-phrases.d.ts.map +1 -0
- package/dist/utils/commitment-phrases.js +118 -0
- package/dist/utils/commitment-phrases.js.map +1 -0
- package/dist/utils/mail-security.d.ts +34 -0
- package/dist/utils/mail-security.d.ts.map +1 -0
- package/dist/utils/mail-security.js +173 -0
- package/dist/utils/mail-security.js.map +1 -0
- package/dist/utils/schedule-templates.d.ts +2 -0
- package/dist/utils/schedule-templates.d.ts.map +1 -1
- package/dist/utils/schedule-templates.js +20 -17
- package/dist/utils/schedule-templates.js.map +1 -1
- package/dist/utils/style-profile.d.ts +29 -0
- package/dist/utils/style-profile.d.ts.map +1 -0
- package/dist/utils/style-profile.js +125 -0
- package/dist/utils/style-profile.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
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 —
|
|
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 — 116 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
|
> **Using Proton Drive too?** See [**proton-drive-mcp**](https://github.com/googlarz/proton-drive-mcp), the companion MCP server and CLI for Proton Drive (upload, download, share and manage your encrypted files from Claude). Run both side by side to handle mail and files in one conversation, for example save an attachment from here, then upload it to Drive. More in [Related projects](#related-projects).
|
|
30
30
|
|
|
@@ -44,7 +44,8 @@ Give Claude Desktop (or Cline, or any MCP client) full access to your Proton Mai
|
|
|
44
44
|
- "Find the last email from `alice@example.com` and draft a reply for me to review." (nothing is sent until you say so)
|
|
45
45
|
- "Which of my emails have attachments from this month?"
|
|
46
46
|
- "Show me my drafts and tighten the oldest one."
|
|
47
|
-
5. **
|
|
47
|
+
5. **Not sure about handing over control?** Start with `PROTONMAIL_MODE=drafts`: Claude can read, search and prepare drafts, but nothing is sent or deleted until you change the mode. The server also tells the client how to behave (ids, accounts, never obeying instructions found inside a message).
|
|
48
|
+
6. **Optional:** `PROTONMAIL_READ_ONLY=true` to browse without being able to change anything, `PROTONMAIL_TOOL_TIER=core` for a smaller tool set (25 instead of 116), and more accounts under [Multi-account support](#multi-account-support-v210). More recipes below in [Try it](#try-it-example-claude-prompts).
|
|
48
49
|
|
|
49
50
|
---
|
|
50
51
|
|
|
@@ -56,7 +57,7 @@ Give Claude Desktop (or Cline, or any MCP client) full access to your Proton Mai
|
|
|
56
57
|
- **Fast local search** — full-text search across your inbox from a local index that keeps its read state in step with the server
|
|
57
58
|
- **Rules that suggest themselves** — a scan proposes filters and cleanup from your own mail, applies them to existing mail after a preview, and can forward by condition; see [Mailbox rules](#mailbox-rules-suggestions-filters-cleanup-forwarding)
|
|
58
59
|
- **Automatic reply, per address, with rules** — off by default, with a preview of what it would do, draft-for-approval mode and working-hours; see [Autoresponder](#autoresponder-automatic-reply-per-address) (Proton's own auto-reply may be all you need)
|
|
59
|
-
- **Full CLI** — a dedicated command for every one of the
|
|
60
|
+
- **Full CLI** — a dedicated command for every one of the 116 tools (plus a generic `tool <name>` passthrough), scriptable and pipeable; `doctor` checks every account and `report` prints a diagnostic summary for a bug report
|
|
60
61
|
- **Runs where you do** — Claude Desktop bundle, npm, Homebrew, or a [Docker image](#running-in-docker-headless-machines) for a headless machine
|
|
61
62
|
- **Privacy-native** — no third-party email service involved; your mail stays on your machine
|
|
62
63
|
|
|
@@ -297,7 +298,7 @@ Reload the Cline extension after saving. Proton Mail tools will appear in Cline'
|
|
|
297
298
|
For a server with no desktop, next to a headless Bridge. The image installs the published npm package and is published for each release to `ghcr.io/googlarz/proton-mail-bridge-client` (`:<version>` and `:latest`, linux/amd64 and linux/arm64). To build it yourself from this repo:
|
|
298
299
|
|
|
299
300
|
```bash
|
|
300
|
-
docker build -f docker/Dockerfile --build-arg VERSION=2.
|
|
301
|
+
docker build -f docker/Dockerfile --build-arg VERSION=2.16.0 -t proton-mail-bridge-client .
|
|
301
302
|
docker run -i --rm --network host \
|
|
302
303
|
-e PROTONMAIL_USERNAME=you@proton.me -e PROTONMAIL_PASSWORD_FILE=/run/secrets/bridge \
|
|
303
304
|
-v /path/to/bridge-password:/run/secrets/bridge:ro -v bridge-client-data:/data \
|
|
@@ -333,6 +334,38 @@ get_autoresponder account=work@example.com # state, counts
|
|
|
333
334
|
|
|
334
335
|
`outgoingNote` is appended, after the signature, to every mail sent from that address while the rule is active (a send with `appendSignature: false` opts out). On the command line: `set-autoresponder`, `autoresponder` and `preview-autoresponder`. The rule, the log and the memory of who was answered live in `autoresponder.json` in the account's data directory; every account (every address Bridge exposes) has its own.
|
|
335
336
|
|
|
337
|
+
## Mail firewall (prompt-injection guard)
|
|
338
|
+
|
|
339
|
+
Mail is untrusted text that a model reads. `get_email_by_id` and `get_emails_by_ids` add a `risk` note (`caution` or `high`, with the reasons) when a message looks written to steer an AI or to deceive you: instructions addressed to an assistant (English, Polish, German), text hidden by markup or zero-width characters, a link that reads one address and goes to another, a Reply-To on another domain, a sender name that shows a different address, a failed SPF/DKIM/DMARC check. Normal mail carries no note. The instruction patterns are also checked wherever else text reaches the model: attachment text, thread reads, lists with previews and reading a message as an MCP resource.
|
|
340
|
+
|
|
341
|
+
After a **high** message has been read, the tools that reach outside (`send_email`, replies, `forward_email`, `send_draft`, `schedule_draft`, `delete_email`, `delete_thread`, `bulk_delete`, `empty_folder`, `unsubscribe_sender`, `respond_to_invite`, and switching on `set_rule` or `set_autoresponder`) ask for `confirmed: true` and quote the reason, even when `PROTONMAIL_CONFIRM_DESTRUCTIVE` is off. Previews (`dryRun`) and switching things off pass. Rules and the autoresponder never act by themselves on a high-risk message. This narrows what a persuaded model can do; it cannot read intent (see the [threat model](docs/threat-model.md)).
|
|
342
|
+
|
|
343
|
+
## Brief (what changed since you last looked)
|
|
344
|
+
|
|
345
|
+
`get_brief` (CLI: `brief`) reports from the local index how much mail came since the last brief, split into people and automatic mail, what waits for your reply, replies you are waiting for that are overdue, and suspicious mail (see the mail firewall above). `markSeen` moves the "since" mark, so a daily run reports only the day. To get it without opening anything, run it from your system's scheduler:
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
proton-mail-bridge-client brief --sync --mark-seen --notify
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
`--notify` shows the few lines as a system notification (macOS and Linux with `notify-send`). `schedule --job brief` prints the launchd, systemd, cron or Windows task definition that runs it every day (it installs nothing).
|
|
352
|
+
|
|
353
|
+
## Tidy in one approval (`plan_inbox`)
|
|
354
|
+
|
|
355
|
+
Ask Claude to tidy the inbox and it prepares a **plan**: a numbered list of changes that can all be taken back (archive, move, mark read, label), from its own proposals and from a scan for automatic mail nobody reads (unread for 3 days, or read and over 14 days old). Nothing changes until you approve. `approve_plan` applies everything, only some numbers, or everything except some; each change goes through the same runtime policy as the single tools. `undo_plan` puts the applied changes back (mail returns to its folder, read marks and labels are reverted). Senders of scanned items you left out are not proposed again. A plan never contains delete or trash.
|
|
356
|
+
|
|
357
|
+
## Commitments ("I'll send it by Friday")
|
|
358
|
+
|
|
359
|
+
A local ledger of what you promised and what was promised to you. `find_commitments` reads recent sent mail (and, if you ask, mail from people) and returns sentences that look like promises, in English, Polish and German, with the date each names ("by Friday", "do piątku", "bis Freitag", `05.11.`). They are only candidates: Claude confirms with you, then `set_commitment` records the real ones. `list_commitments` shows each open entry as overdue, due today, open or probably done (the person who owed it wrote again in the thread). Overdue and due-today entries appear in the brief. Closing an entry is `set_commitment` with `id` and `status`. Nothing in the mailbox changes.
|
|
360
|
+
|
|
361
|
+
## Style profile (drafts that sound like you)
|
|
362
|
+
|
|
363
|
+
`get_style_profile` works out how you write from your own sent mail, on your machine: language, how you open and close ("Dzień dobry {name},", "Cheers"), tone, typical length. It returns a few plain hints for drafting, overall and for a given recipient or company ("to pat@client.example you write in Polish, formally"), and the server tells Claude to call it before drafting. The profile is cached for a week in `style-profile.json` and holds patterns and counts only: no sentences, no names.
|
|
364
|
+
|
|
365
|
+
## Search by meaning (several wordings)
|
|
366
|
+
|
|
367
|
+
`search_indexed_emails` takes `queries`: several wordings of one question, up to 8. Claude supplies the variants (synonyms, other languages: `["deposit", "kaucja", "Kaution", "rental agreement"]`), the server searches each in the local index and fuses the results, so a message found by several wordings ranks first and every hit says which wordings found it (`matchedBy`). There is no model and no download: the meaning comes from the model you already use. What it cannot find is mail that uses none of the words tried.
|
|
368
|
+
|
|
336
369
|
## Mailbox rules (suggestions, filters, cleanup, forwarding)
|
|
337
370
|
|
|
338
371
|
Proton's own filters act on **new** mail, the free plan has one custom filter, and auto-delete of Trash and Spam is a paid feature with a fixed 30 days (see Proton's pages on [filters](https://proton.me/support/email-inbox-filters) and [auto-delete](https://proton.me/support/auto-delete-unwanted-messages)). These rules work alongside them: on mail that is already there, with a preview, and with a scan that suggests rules instead of waiting for you to write them. They run on this server, so they only act while it runs.
|
|
@@ -496,7 +529,7 @@ All commands support `--json` for machine-readable output, and any MCP tool is d
|
|
|
496
529
|
|
|
497
530
|
`<command> --help` prints that command's usage without running it. Flags accept `--flag value` or `--flag=value` (a value starting with `--` needs the `=` form, or must come after a bare `--`); unknown flags are an error. Exit codes: `0` success, `1` failure (including a failed `doctor` or `connection-status` check, or a failed item in a `batch`/`bulk-*` run), `2` usage error.
|
|
498
531
|
|
|
499
|
-
**Full command reference: [docs/cli.md](docs/cli.md)** (a named command for every one of the
|
|
532
|
+
**Full command reference: [docs/cli.md](docs/cli.md)** (a named command for every one of the 116 tools, across read, triage, compose, mailbox actions, folders, drafts, templates, analytics, and diagnostics).
|
|
500
533
|
|
|
501
534
|
---
|
|
502
535
|
|
|
@@ -518,7 +551,7 @@ A mode is a floor: the individual flags below can make things stricter (`PROTONM
|
|
|
518
551
|
All flags work in both the MCP server and CLI:
|
|
519
552
|
|
|
520
553
|
```bash
|
|
521
|
-
PROTONMAIL_TOOL_TIER=core # expose 25 core tools instead of all
|
|
554
|
+
PROTONMAIL_TOOL_TIER=core # expose 25 core tools instead of all 116 — saves context window
|
|
522
555
|
PROTONMAIL_READ_ONLY=true # disable all write operations
|
|
523
556
|
PROTONMAIL_ALLOW_SEND=false # disable SMTP sends only (other writes still work)
|
|
524
557
|
PROTONMAIL_CONFIRM_DESTRUCTIVE=true # require confirmed:true on send, reply, forward, delete
|
|
@@ -741,7 +774,7 @@ Measured in October 2026 (v2.7.0) on a copy of the same ~57,000-message index. "
|
|
|
741
774
|
- Defaults are bounded: `search_emails`/`search_indexed_emails` return 50 (pass `limit` for more), `list_drafts` 20, `list_scheduled_sends` 50 newest first. Each reports `hasMore`; page with `offset`.
|
|
742
775
|
- Draft and queued-message bodies are shortened to a preview in list and update responses (`bodyTruncated`, `bodyLength`); use `get_draft` for one full draft. Attachment content is never inlined in a listing.
|
|
743
776
|
- Large attachments: set `PROTONMAIL_ALLOW_FILE_DOWNLOAD_DIR` and pass `saveTo`, rather than raising `PROTONMAIL_MAX_INLINE_KB`.
|
|
744
|
-
- **`PROTONMAIL_TOOL_TIER=core`** exposes 25 tools instead of
|
|
777
|
+
- **`PROTONMAIL_TOOL_TIER=core`** exposes 25 tools instead of 116: about 7k tokens of tool definitions instead of about 21k (roughly 67% less; the earlier measurement with a reference tokenizer, 7k against 20k for 96 tools, scaled by the 3.7% the four newest tool definitions add), paid on every conversation that loads the server.
|
|
745
778
|
- Repeating an `update_draft` that changes nothing on a draft whose remote copy is already in sync does no store write and no IMAP upload. (A draft whose last sync failed, or that was edited with `syncToRemote:false`, still syncs.)
|
|
746
779
|
|
|
747
780
|
**Working with drafts**
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAaA,OAAO,KAAK,EAAgB,gBAAgB,EAAqB,MAAM,kBAAkB,CAAC;AAqB1F,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,QAAQ,CAAC;CACjB;AAiBD,qBAAa,aAAc,SAAQ,KAAK;CAAG;AAM3C,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAuD1D;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAaA,OAAO,KAAK,EAAgB,gBAAgB,EAAqB,MAAM,kBAAkB,CAAC;AAqB1F,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,QAAQ,CAAC;CACjB;AAiBD,qBAAa,aAAc,SAAQ,KAAK;CAAG;AAM3C,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAuD1D;AA2ID,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAOD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAsErD,CAAC;AAoBF,eAAO,MAAM,iBAAiB,YAAI,KAAK,EAAE,MAAM,EAAE,MAAM,CAAU,CAAC;AAGlE,wBAAgB,kBAAkB,IAAI,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAQlG;AAID,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA+DtD;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAcnE;AAyKD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAY5F;AAiND,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAoB5G;AAgGD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGnD;AAGD,wBAAgB,aAAa,CAAC,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,EAAE,IAAI,SAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAS7G;AA+2BD,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,KAAK,EAAE,OAAO,GACb;IAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAYzE;AA2SD,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IAIb,SAAS,CAAC,EAAE,MAAM,CAAC;IAMnB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,eAAO,MAAM,kBAAkB,EAAE,eAAe,EAmF/C,CAAC;AA+EF,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAuP1C;AA+ED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGzD"}
|
package/dist/cli.js
CHANGED
|
@@ -129,6 +129,7 @@ function printHelp() {
|
|
|
129
129
|
" empty-folder <folder> Permanently empty a folder (--confirmed to execute)",
|
|
130
130
|
" labels List normalized labels from the local index",
|
|
131
131
|
" threads [query] List normalized threads from the local index",
|
|
132
|
+
" brief What changed since the last brief (--since --limit --sync --mark-seen --notify)",
|
|
132
133
|
" digest Show inbox digest and top actionable threads",
|
|
133
134
|
" followups Show follow-up candidates from the local index",
|
|
134
135
|
" emails List emails from a folder (--folder --limit --offset)",
|
|
@@ -233,7 +234,7 @@ const DRAFT_COPY_FLAGS = ["cc", "bcc", "notes"];
|
|
|
233
234
|
const BULK_FILTER_FLAGS = ["from", "subject", "since", "before"];
|
|
234
235
|
export const COMMAND_SPECS = {
|
|
235
236
|
completion: { usage: "completion <zsh|bash|fish>", description: "Print a shell completion script for the commands and their flags", flags: [] },
|
|
236
|
-
schedule: { usage: "schedule [--at HH:MM] [--system launchd|systemd|cron|schtasks] [--output <file>] [--password-file <file>]", description: "Print the scheduler definition that runs `digest` every day, with the commands to switch it on (installs nothing)", flags: ["at", "system", "output", "password-file"] },
|
|
237
|
+
schedule: { usage: "schedule [--job digest|brief] [--at HH:MM] [--system launchd|systemd|cron|schtasks] [--output <file>] [--password-file <file>]", description: "Print the scheduler definition that runs `digest` (or `brief`, as a notification) every day, with the commands to switch it on (installs nothing)", flags: ["job", "at", "system", "output", "password-file"] },
|
|
237
238
|
help: { usage: "help [command]", description: "Show the command list, or the help for one command", flags: [] },
|
|
238
239
|
version: { usage: "version [--check]", description: "Print the version and exit; --check also asks the npm registry whether a newer release exists", flags: ["check"] },
|
|
239
240
|
"setup-claude-desktop": { usage: "setup-claude-desktop", description: "Run the Claude Desktop setup wizard (works from any install)", flags: [] },
|
|
@@ -252,6 +253,7 @@ export const COMMAND_SPECS = {
|
|
|
252
253
|
"empty-folder": { usage: "empty-folder <folder> [--confirmed]", description: "Permanently empty a folder (--confirmed to execute)", flags: ["folder", "confirmed"] },
|
|
253
254
|
labels: { usage: "labels [--limit <n>]", description: "List normalized labels from the local index", flags: ["limit"] },
|
|
254
255
|
threads: { usage: "threads [query] [--label <name>] [--limit <n>] [--sync]", description: "List normalized threads from the local index", flags: ["sync", "folder", "limit", "label"] },
|
|
256
|
+
brief: { usage: "brief [--account <a>] [--since <time>] [--limit <n>] [--sync] [--mark-seen] [--notify]", description: "What changed since the last brief: new mail, what waits for your reply, overdue replies, suspicious mail (--notify shows it as a system notification, --mark-seen moves the since mark)", flags: ["account", "since", "limit", "sync", "mark-seen", "notify"] },
|
|
255
257
|
digest: { usage: "digest [--limit <n>] [--age-hours <n>] [--sync]", description: "Show inbox digest and top actionable threads", flags: ["sync", "limit", "age-hours"] },
|
|
256
258
|
followups: { usage: "followups [--pending you|them|any] [--limit <n>] [--age-hours <n>] [--sync]", description: "Show follow-up candidates from the local index", flags: ["sync", "pending", "limit", "age-hours"] },
|
|
257
259
|
emails: { usage: "emails [--folder <name>] [--limit <n>] [--offset <n>]", description: "List emails from a folder", flags: ["folder", "limit", "offset"] },
|
|
@@ -1631,6 +1633,31 @@ async function runContacts(parsed) {
|
|
|
1631
1633
|
printToolCallResult(result, wantJson);
|
|
1632
1634
|
});
|
|
1633
1635
|
}
|
|
1636
|
+
async function runBrief(parsed) {
|
|
1637
|
+
const wantJson = isTruthyFlag(parsed.flags.json);
|
|
1638
|
+
const args = { limit: getNumberFlag(parsed.flags, "limit", 5) };
|
|
1639
|
+
const account = getStringFlag(parsed.flags, "account");
|
|
1640
|
+
const since = getStringFlag(parsed.flags, "since");
|
|
1641
|
+
if (account)
|
|
1642
|
+
args.account = account;
|
|
1643
|
+
if (since)
|
|
1644
|
+
args.since = since;
|
|
1645
|
+
if (isTruthyFlag(parsed.flags.sync))
|
|
1646
|
+
args.syncBefore = true;
|
|
1647
|
+
if (isTruthyFlag(parsed.flags["mark-seen"]))
|
|
1648
|
+
args.markSeen = true;
|
|
1649
|
+
await withMcpClient(async (client) => {
|
|
1650
|
+
const result = await client.callTool({ name: "get_brief", arguments: args });
|
|
1651
|
+
printToolCallResult(result, wantJson);
|
|
1652
|
+
// The same few lines as a system notification, so a scheduled run reaches you without opening anything.
|
|
1653
|
+
if (isTruthyFlag(parsed.flags.notify) && !result.isError) {
|
|
1654
|
+
const text = result.content.find((part) => part.type === "text")?.text;
|
|
1655
|
+
const lines = text ? JSON.parse(text).text : undefined;
|
|
1656
|
+
if (lines?.length)
|
|
1657
|
+
await sendSystemNotification("Proton Mail brief", lines.join("\n"));
|
|
1658
|
+
}
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1634
1661
|
async function runVolumeTrends(parsed) {
|
|
1635
1662
|
const wantJson = isTruthyFlag(parsed.flags.json);
|
|
1636
1663
|
await withMcpClient(async (client) => {
|
|
@@ -2005,6 +2032,14 @@ export const TOOL_ONLY_COMMANDS = [
|
|
|
2005
2032
|
{ command: "set-rule", tool: "set_rule", positionals: [], help: "Create, change or accept a proposal as a rule (--proposalId, --id, --name, --enabled, --when, --actions, --confirmed)" },
|
|
2006
2033
|
{ command: "delete-rule", tool: "delete_rule", positionals: ["id"], help: "Delete a rule, or dismiss a proposal" },
|
|
2007
2034
|
{ command: "apply-rules", tool: "apply_rules", positionals: [], help: "Run the rules on mail already in the index; a preview unless --dryRun false (--ruleId, --limit, --confirmed)" },
|
|
2035
|
+
{ command: "plan-inbox", tool: "plan_inbox", positionals: [], help: "Prepare a batch of mailbox changes to approve at once (--args '{\"scan\":true,\"limit\":30}')" },
|
|
2036
|
+
{ command: "approve-plan", tool: "approve_plan", positionals: ["planId"], help: "Apply a plan from plan-inbox (--args '{\"exclude\":[3]}')" },
|
|
2037
|
+
{ command: "undo-plan", tool: "undo_plan", positionals: ["planId"], help: "Put back what an approved plan changed" },
|
|
2038
|
+
{ command: "find-commitments", tool: "find_commitments", positionals: [], help: "Find sentences that look like promises in recent mail (--args '{\"days\":7,\"direction\":\"both\"}')" },
|
|
2039
|
+
{ command: "set-commitment", tool: "set_commitment", positionals: [], help: "Record a commitment, or close one (--args '{\"text\":\"...\",\"direction\":\"mine\",\"due\":\"2026-11-05\"}')" },
|
|
2040
|
+
{ command: "commitments", tool: "list_commitments", positionals: [], help: "The commitment ledger: overdue, due today, open, probably done (--args '{\"status\":\"all\"}')" },
|
|
2041
|
+
{ command: "style-profile", tool: "get_style_profile", positionals: [], help: "How you write, from your sent mail: language, greeting, closing, tone (--args '{\"recipient\":\"pat@client.example\",\"refresh\":true}')" },
|
|
2042
|
+
{ command: "get-brief", tool: "get_brief", positionals: [], help: "(tool form; see also `brief`)" },
|
|
2008
2043
|
{ command: "autoresponder", tool: "get_autoresponder", positionals: [], help: "Show the automatic reply of an address (--account)" },
|
|
2009
2044
|
{ command: "set-autoresponder", tool: "set_autoresponder", positionals: [], help: "Set up or switch off the automatic reply of an address (--enabled, --body, --subject, --startsAt, --endsAt, --outgoingNote, --mode, --timezone, --rules, --confirmed)" },
|
|
2010
2045
|
{ command: "list-templates", tool: "list_templates", positionals: [], help: "List saved email templates" },
|
|
@@ -2076,8 +2111,13 @@ async function runToolOnlyCommand(entry, parsed) {
|
|
|
2076
2111
|
if (requiredFileField && args[requiredFileField] === undefined) {
|
|
2077
2112
|
throw new Error(`${entry.command} requires --file <path>, or ${requiredFileField} via --args.`);
|
|
2078
2113
|
}
|
|
2114
|
+
// Reading sent mail for the style profile is slow on a large mailbox and a command line has nothing to run it in the
|
|
2115
|
+
// background, so it waits for the whole build.
|
|
2116
|
+
const slow = entry.tool === "get_style_profile";
|
|
2117
|
+
if (slow && args.waitSeconds === undefined)
|
|
2118
|
+
args.waitSeconds = 280;
|
|
2079
2119
|
await withMcpClient(async (client) => {
|
|
2080
|
-
const result = await client.callTool({ name: entry.tool, arguments: args });
|
|
2120
|
+
const result = await client.callTool({ name: entry.tool, arguments: args }, undefined, slow ? { timeout: 290_000 } : undefined);
|
|
2081
2121
|
printToolCallResult(result, wantJson);
|
|
2082
2122
|
});
|
|
2083
2123
|
}
|
|
@@ -2195,6 +2235,9 @@ export async function main() {
|
|
|
2195
2235
|
case "threads":
|
|
2196
2236
|
await runThreads(parsed);
|
|
2197
2237
|
return;
|
|
2238
|
+
case "brief":
|
|
2239
|
+
await runBrief(parsed);
|
|
2240
|
+
return;
|
|
2198
2241
|
case "digest":
|
|
2199
2242
|
await runDigest(parsed);
|
|
2200
2243
|
return;
|
|
@@ -2387,10 +2430,14 @@ function scheduleText(parsed) {
|
|
|
2387
2430
|
throw new CliUsageError(`--system must be one of: ${SCHEDULER_SYSTEMS.join(", ")} (got "${requested}").`);
|
|
2388
2431
|
}
|
|
2389
2432
|
const system = requested ?? defaultSchedulerSystem(process.platform);
|
|
2433
|
+
const job = getStringFlag(parsed.flags, "job") ?? "digest";
|
|
2434
|
+
if (job !== "digest" && job !== "brief")
|
|
2435
|
+
throw new CliUsageError(`--job must be digest or brief (got "${job}").`);
|
|
2390
2436
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
|
|
2391
2437
|
const output = getStringFlag(parsed.flags, "output") ?? `${home}/proton-mail-brief.txt`;
|
|
2392
2438
|
const passwordFile = getStringFlag(parsed.flags, "password-file") ?? `${home}/.proton-mail-bridge-password`;
|
|
2393
2439
|
const result = buildSchedule({
|
|
2440
|
+
job,
|
|
2394
2441
|
system,
|
|
2395
2442
|
hour,
|
|
2396
2443
|
minute,
|