mailmate 1.5.0 → 1.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b030228294a0ca2411434364c83c99f83822d10258ac94cb81bc065eac5ce66c
4
- data.tar.gz: df735317a7c2747ca656db9fa71eee6edcb77ada30708561652305fc1748fe07
3
+ metadata.gz: 81aeb9b0f5abd70325c74329db580be4134b27eee50b03f6caef5958b653f288
4
+ data.tar.gz: c614a5dcc2e4fb2bd6fa797e09d1e959a1ecbf9c26ae7c6a21b0e26899c6a32e
5
5
  SHA512:
6
- metadata.gz: a9114cc082c2f302fc5b71f44bdc8ef3273c9f4f07e2c274f977a53f36db17533d2c693b111579587f0bd3a4ec2e5c02a979ebd4a8d4edc3beb748be762a877d
7
- data.tar.gz: 0ab6609a1d3aa348a1de39ff8504d285b13adedae1c639060466aa343074435302d073a3f4dfe6a30c6ee6e2d5861c7d495b850bbfdf88a7499fe658a6a9a131
6
+ metadata.gz: 9c27e9400d40047291b3486e6895b47f8e8855985bb74bfe75de2a338c5c5d59356b92d330ad93186828e3aa579f9c6273602432a77f2dbc383b3e7afdfb02aa
7
+ data.tar.gz: c3d898edeb7fe251c9f816dc333014e915bcb8b1e4d63e503a52192b22ca6353bf5657109688c36a71af04060cda6fc816a95c3b680b526382f8afc47e63851c
data/README.md CHANGED
@@ -4,6 +4,154 @@ Ruby toolkit for [MailMate](https://freron.com) on macOS — a smart-mailbox fil
4
4
 
5
5
  **Requires macOS with MailMate installed.** The library code (filter parser, evaluator) works anywhere, but the integration with MailMate itself — AppleScript, on-disk index reads, the `emate` binary — is macOS-only by way of MailMate being macOS-only.
6
6
 
7
+ ## Privacy Policy
8
+
9
+ This gem — including its MCP server — contains **no networking code at all**.
10
+ It never opens a network connection of any kind, for any purpose: no HTTP, no
11
+ sockets, no telemetry endpoints. You can verify this from the source — there
12
+ isn't a single `net/*`, `open-uri`, or socket require anywhere in `lib/` or
13
+ `exe/`. The only network activity in the project's entire lifecycle happens at
14
+ install time: `gem install` fetches from rubygems.org, and `install.sh` may
15
+ additionally fetch a relocatable Ruby from GitHub. After that, nothing.
16
+
17
+ - **Data collection:** none. No telemetry, analytics, usage data, or crash
18
+ reports — and no code capable of transmitting them.
19
+ - **Usage and storage:** the gem reads MailMate's existing on-disk mail store
20
+ (`~/Library/Application Support/MailMate`) and drives the MailMate app via
21
+ AppleScript. It creates no data stores of its own beyond files you
22
+ explicitly ask it to write. The only optional configuration file is
23
+ `~/.config/mailmate/config.yml`, which you author yourself.
24
+ - **Third-party sharing:** none by the gem itself — it has no means to share
25
+ anything. Two things *adjacent* to it can move data off your machine, and
26
+ both are under your control: (1) `mm-send` / the `send` tool hands the
27
+ message to the MailMate app, and **MailMate** performs the delivery to the
28
+ recipients you named — the gem transmits nothing; (2) when the MCP server
29
+ is used from an AI client (Claude Desktop, Claude Code, etc.), message
30
+ content returned by its tools enters that client's conversation and is
31
+ transmitted to that AI provider under *its* privacy policy — choose which
32
+ messages you surface accordingly.
33
+ - **Data retention:** none. The gem retains nothing between invocations;
34
+ your mail stays wherever MailMate keeps it.
35
+ - **Contact:** brian@murphydye.com, or open an issue at
36
+ <https://github.com/brianmd/mailmate/issues>.
37
+
38
+ ## Install
39
+
40
+ Pick the path that matches how you'll use it:
41
+
42
+ - **Claude Code plugin** — MCP tools for Claude, zero manual setup (below)
43
+ - **One-line installer** — the MCP server for any MCP client, fully isolated under `~/.mailmate-mcp`
44
+ - **`gem install mailmate`** — the CLI tools (and MCP server) on your own Ruby
45
+
46
+ ### Requirements
47
+
48
+ - **macOS** with **MailMate** installed (and running, for any command that drives the UI or sends mail).
49
+ - **Ruby ≥ 3.0** — for the plugin and one-line installer this is optional: if no suitable Ruby is found, they download a private relocatable Ruby into `~/.mailmate-mcp/ruby` and never touch your system.
50
+ - No third-party CLI tools — the gem only shells out to macOS-bundled `plutil`, `osascript`, and `open`, plus MailMate's bundled `emate`.
51
+
52
+ ### Claude Code plugin
53
+
54
+ This repo doubles as a Claude Code plugin marketplace. Inside Claude Code:
55
+
56
+ ```
57
+ /plugin marketplace add brianmd/mailmate
58
+ /plugin install mailmate@brianmd
59
+ ```
60
+
61
+ The plugin's MCP server self-provisions on first launch — Ruby (if needed) and gem dependencies go into `~/.mailmate-mcp`; nothing touches your system Ruby, Homebrew, or shell profile. It runs the plugin's bundled source, so plugin updates take effect without waiting for a gem release. Uninstall: `/plugin uninstall mailmate`, then `rm -rf ~/.mailmate-mcp`.
62
+
63
+ ### Claude Cowork (desktop app)
64
+
65
+ The same plugin works in Cowork on the macOS desktop app — verified end to end, including a from-scratch first run:
66
+
67
+ 1. In Cowork: **Customize → Plugins → + → Add marketplace** → `brianmd/mailmate`, then install **mailmate** from the Discover tab.
68
+ 2. Ask Claude about your mail. The first launch provisions the runtime inside Cowork's sandbox, which can take a minute or two, and Cowork will ask you to **allow the connector's commands and file access** — those approvals are the gate to your local mail store.
69
+ 3. The Claude desktop app must be running on the Mac where MailMate lives; `send`, `draft`, `modify`, and `open` additionally need the MailMate app running (as always).
70
+
71
+ Cowork runs the connector through the desktop app's sandboxed bridge, so its provisioning leaves nothing on your real filesystem — not even `~/.mailmate-mcp` (only the Claude Code path creates that directory).
72
+
73
+ ### One-line installer (any MCP client)
74
+
75
+ ```bash
76
+ curl -fsSL https://raw.githubusercontent.com/brianmd/mailmate/main/install.sh | bash
77
+ ```
78
+
79
+ Installs the released gem into an isolated `GEM_HOME` under `~/.mailmate-mcp` (provisioning a private Ruby only if none ≥ 3.0 is found), writes a launcher shim, and registers it with Claude Code when the `claude` CLI is present (pass `--no-register` to skip). For Claude Desktop, add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
80
+
81
+ ```json
82
+ {
83
+ "mcpServers": {
84
+ "mailmate": { "command": "/Users/<you>/.mailmate-mcp/bin/mailmate-mcp" }
85
+ }
86
+ }
87
+ ```
88
+
89
+ `~/.mailmate-mcp` is the entire footprint. Uninstall: `bash install.sh --uninstall` (or just delete the directory), plus `claude mcp remove mailmate`.
90
+
91
+ ### As a Ruby gem (CLI tools)
92
+
93
+ ```bash
94
+ gem install mailmate
95
+ ```
96
+
97
+ Then optionally bootstrap your config (will happen automatically on first invocation of any command from an interactive shell if it hasn't been run before):
98
+
99
+ ```bash
100
+ mmdiscover
101
+ ```
102
+
103
+ `mmdiscover` reads MailMate's `Sources.plist` and `Identities.plist`, shows you the accounts and addresses it found, and offers to write `~/.config/mailmate/config.yml` from them. It also writes `~/.config/mailmate/bundle_loader.rb` for MailMate bundles. Running it explicitly is only needed in non-TTY contexts (cron jobs, MCP servers) — there, the gem falls back to built-in defaults and warns once.
104
+
105
+ ### Optional: `mmmessage --markdown`
106
+
107
+ **On the vast majority of Ruby setups (stock `arm64-darwin` or `x86_64-darwin` Ruby) this step is a no-op — nokogiri ships a precompiled binary, you can skip the rest of this section and move on.** Keep reading only if your `gem install` actually fails.
108
+
109
+ `mmmessage --markdown` renders HTML-only message bodies as readable markdown. It needs the `reverse_markdown` gem, which has `nokogiri` as a transitive dependency:
110
+
111
+ ```bash
112
+ gem install reverse_markdown
113
+ ```
114
+
115
+ That single command pulls `nokogiri` in automatically — no separate `gem install nokogiri` step. This is kept out of the base install because nokogiri ships a native extension. On Ruby/platform combinations without a precompiled match nokogiri falls back to compiling from source — it vendors its own libxml2/libxslt, but it does need a C compiler, which on macOS means Xcode Command Line Tools (`xcode-select --install`). If `gem install reverse_markdown` fails, that's almost certainly the cause.
116
+
117
+ If you never use `--markdown`, you never pay any of this. If you do invoke `--markdown` without the gem installed, `mmmessage` warns with a clear install hint and falls back to the raw HTML body (it does not abort — so the in-process MCP server survives a missing optional dependency). The plugin launcher and one-line installer attempt this gem automatically and degrade the same way if it fails to build.
118
+
119
+ ### From source (development)
120
+
121
+ If you're hacking on the gem itself, skip `gem install` and put the repo's `exe/` on your `PATH`. Clone wherever you keep source repos, then prepend its `exe/` to `PATH` from your shell's rc file (`~/.zshrc`, `~/.bashrc`, etc.):
122
+
123
+ ```bash
124
+ git clone https://github.com/brianmd/mailmate.git
125
+ cd mailmate
126
+
127
+ # In your shell rc file, add (adjust the path to wherever you cloned):
128
+ # export PATH="/absolute/path/to/mailmate/exe:$PATH"
129
+ # Then reload the shell (open a new tab, or `source` the rc file).
130
+ ```
131
+
132
+ Then `mmdiscover` as above.
133
+
134
+ ### MCP server (manual setup)
135
+
136
+ The gem ships an MCP server (`exe/mailmate-mcp`) that exposes the same surface to AI assistants as JSON-RPC tools: `search`, `message`, `modify`, `verify`, `send`, `draft`, `open`, `list_mailboxes`, `list_tags`, `resolve_id`. Every tool carries MCP annotations (`readOnlyHint`/`destructiveHint`) so clients can apply sensible permission behavior. If you installed via the plugin or one-line installer above, this is already wired up; after a plain `gem install mailmate`, register it yourself:
137
+
138
+ ```bash
139
+ claude mcp add --scope user mailmate "$(which mailmate-mcp)"
140
+ ```
141
+
142
+ Or add manually to `~/.claude.json` under `"mcpServers"`:
143
+
144
+ ```json
145
+ "mailmate": {
146
+ "type": "stdio",
147
+ "command": "/absolute/path/to/mailmate-mcp",
148
+ "args": [],
149
+ "env": {}
150
+ }
151
+ ```
152
+
153
+ For Claude Desktop, use the same command path in `claude_desktop_config.json` as shown under the one-line installer. Restart Claude Desktop after any change to server code or config.
154
+
7
155
  ## Example usage
8
156
 
9
157
  ### `mmsearch` — find messages
@@ -12,11 +160,11 @@ Ruby toolkit for [MailMate](https://freron.com) on macOS — a smart-mailbox fil
12
160
  # Default: today's mail, all mailboxes
13
161
  mmsearch
14
162
 
15
- # From "Medium" in the last 7 days
16
- mmsearch 'f medium d 7d'
163
+ # From "Substack" in the last 7 days
164
+ mmsearch 'f substack d 7d'
17
165
 
18
- # Subject contains "rent due", not the word "draft"
19
- mmsearch 's "rent due" !draft'
166
+ # Subject contains "invoice due", not the word "draft"
167
+ mmsearch 's "invoice due" !draft'
20
168
 
21
169
  # Received in May 2026
22
170
  mmsearch 'd 2026-05'
@@ -25,7 +173,7 @@ mmsearch 'd 2026-05'
25
173
  mmsearch 'f acme' 'id flags subject from' --limit 20 --no-align
26
174
  ```
27
175
 
28
- **Quicksearch syntax.** The search-string is a list of specs combined with **AND** (`or`/parens not yet supported). Wrap multi-word terms in `"double quotes"`.
176
+ **Quicksearch syntax.** The search-string is a list of specs combined with **AND**; a bare `or` separates alternatives, and AND binds tighter (no parens write `(f bob or f ann) s invoice` out as `f bob s invoice or f ann s invoice`). After `or`, a bare first term inherits the modifier in force: `d 2024 or 2025 or 2y`. Wrap multi-word terms in `"double quotes"` (also how to search for the literal word "or"). `mmsearch --help` is the canonical, always-current rendering of this table.
29
177
 
30
178
  | Modifier | Scope |
31
179
  |---|---|
@@ -37,11 +185,13 @@ mmsearch 'f acme' 'id flags subject from' --limit 20 --no-align
37
185
  | `a <term>` | Any address header contains. |
38
186
  | `b <term>` | Body (plain text) contains. |
39
187
  | `m <term>` | Common headers OR body. |
40
- | `d <date>` | Received date: `Y`, `Y-M`, `Y-M-D`, or relative `1d`/`2w`/`3m`/`1y`. |
188
+ | `d <date>` | Received: `Nh` (rolling clock hours, `24h` = last 24 hours), `Nd`/`Nw`/`Nm`/`Ny` (N calendar units ending today — `1d` = today, `2d` = yesterday + today), or absolute `Y`, `Y-M`, `Y-M-D`. Slash dates are month-first American (`8/9/2026` = Aug 9); `--european` flips to day-first. Comparisons on absolute dates: `d >2026-08` (after), `d <2026-08` (before), also `>=`/`<=`. |
41
189
  | `T <tag>` | Tags / IMAP keywords (`K` is a synonym). |
42
- | `!<value>` | Negate, e.g. `f !smith` = From does NOT contain smith. |
190
+ | `!<value>` | Negate, e.g. `f !smith` = From does NOT contain smith; works on dates too (`d !3d` = more than 3 days ago). |
191
+
192
+ Dates match on the **display-zone day** — the same day the `date`/`time` output columns show. An impossible date term or combination (`d 2026-02-31`, `d >2026 d <2025`) is a usage error, not a silent empty result. Familiar foreign `key:value` tokens (`from:bob`, `date:today`, `after:2026-08-01`, `older_than:2w`) are auto-translated to quicksearch with each rewrite announced on stderr; unrecognized keys (`is:unread`) are searched as literal text, and an empty result says so.
43
193
 
44
- The `--mailbox` argument accepts an account, an `account/path`, a bare mailbox name matched across accounts, or a **smart-mailbox name** (e.g. `Medium`, `Whisper`, `Personal Inbox`) whose filter is ANDed into the search.
194
+ The `--mailbox` argument accepts an account, an `account/path`, a bare mailbox name matched across accounts, or a **smart-mailbox name** (e.g. `Newsletters`, `Receipts`, `Priority`) whose filter is ANDed into the search.
45
195
 
46
196
  **Output fields.** Default columns are `flags date time direction party subject`. Prefix a field list with `+` to add to the defaults; a bare list replaces them (`id` is always the first column).
47
197
 
@@ -276,6 +426,10 @@ A few rough edges to be aware of:
276
426
 
277
427
  ## Status
278
428
 
429
+ 1.7.0 — Search-language release, driven by a study of how LLM agents actually misuse `mmsearch`. The quicksearch syntax reference is now single-sourced (`Mailmate::SearchSyntax`) into both `mmsearch --help` and the MCP `search` description, so the two can no longer drift. Foreign `key:value` dialects (Gmail/Outlook/Spotlight — `from:bob`, `date:today`, `after:2026-08-01`, `older_than:2w`) auto-translate to their exact quicksearch equivalent, loudly: each rewrite is announced on stderr, and untranslatable keys are flagged when a search returns nothing. The language itself grew: boolean `or` (AND binds tighter, no parens; a bare term after `or` inherits the modifier in force), date comparisons (`d >2026-08`, `d <2026-08`, `>=`/`<=`), rolling hour windows (`d 24h`), and American slash dates (`d 8/9/2026`; `--european` for day-first). Two semantic fixes: `d 1d` now means *today* (N calendar units ending today, matching the MailMate app; the old today−N made it span two days), and date matching converts to the display zone — the same conversion the `date`/`time` columns use — so the day a search matches is always the day shown (sender-local index days previously leaked "tomorrow's" mail into `d 1d`). Impossible date terms and combinations (`d 0d`, `d 2026-02-31`, `d >2026 d <2025`) are usage errors instead of silent empty results.
430
+
431
+ 1.6.0 — Distribution release. The repo is now a Claude Code plugin marketplace (`/plugin marketplace add brianmd/mailmate`), and a one-line `install.sh` provisions the MCP server into an isolated `~/.mailmate-mcp` — including a private relocatable Ruby when no Ruby ≥ 3.0 is present — without touching system Ruby, Homebrew, or shell profiles. Every MCP tool now carries a `title` plus `readOnlyHint`/`destructiveHint` annotations (Claude clients use these for permission behavior; Anthropic's directory review requires them), and the README gains a formal Privacy Policy section. No changes to CLI or library behavior.
432
+
279
433
  1.5.0 — Reliability and batch-verification for `mm-modify`, plus search/read speedups. `mm-modify` gains a no-window retry guard (a `mid:` open that spawns no viewer would otherwise act on the wrong message) and opt-in effect verification: `--check` confirms a flag/tag/read action landed on the target eml-id by re-reading `#flags` (the only way to catch a duplicate-Message-ID misland). Because MailMate flushes `#flags` to disk ~5 s after acting, a new **`mm-verify`** command plus `mm-modify --emit-check` decouple acting from confirming — collect JSON check-tickets across a batch and verify them all in one flush-wait instead of paying the latency per message. `mmsearch` is substantially faster (compiled date ranges, cheapest-spec-first ordering, bulk-unpack index reader, inverted body search) with bit-identical output; the persistent MCP server now invalidates index caches on disk change. `mmmessage` shows user tags and lazy-loads the `mail` gem (`--raw`/`--mailmate` skip it). MCP: `message` gains `markdown`, `modify` gains a `check` mode (`none|inline|defer`), and a new `verify` tool batch-confirms deferred tickets.
280
434
 
281
435
  1.2.0 — `mm-modify` no longer brings MailMate to the foreground and is roughly 8× faster on single-action invocations. Internally: the open call uses `open -g -a MailMate <url>` to keep MailMate in the background, and the fixed `--settle` sleeps are replaced by active waits (polling for the spawned viewer window to appear). `mmopen` gains a `--background` / `-g` flag for ad-hoc use. `mm-modify --verify` now works in `--dry-run` mode as a post-hoc state probe. `--settle` is preserved for backward compat; it now caps the active-wait timeout rather than fixing sleep duration.
@@ -284,92 +438,6 @@ A few rough edges to be aware of:
284
438
 
285
439
  1.0.0 — initial public release; API stable from this point. Breaking changes bump the major version going forward.
286
440
 
287
- ## Install
288
-
289
- ### Requirements
290
-
291
- - **macOS** with **MailMate** installed (and running, for any command that drives the UI or sends mail).
292
- - **Ruby ≥ 3.0**.
293
- - No third-party CLI tools — the gem only shells out to macOS-bundled `plutil`, `osascript`, and `open`, plus MailMate's bundled `emate`.
294
-
295
- ```bash
296
- gem install mailmate
297
- ```
298
-
299
- Then optionally bootstrap your config (will happen automatically on first invocation of any command from an interactive shell if it hasn't been run before):
300
-
301
- ```bash
302
- mmdiscover
303
- ```
304
-
305
- `mmdiscover` reads MailMate's `Sources.plist` and `Identities.plist`, shows you the accounts and addresses it found, and offers to write `~/.config/mailmate/config.yml` from them. It also writes `~/.config/mailmate/bundle_loader.rb` for MailMate bundles. Running it explicitly is only needed in non-TTY contexts (cron jobs, MCP servers) — there, the gem falls back to built-in defaults and warns once.
306
-
307
- ### Optional: `mmmessage --markdown`
308
-
309
- **On the vast majority of Ruby setups (stock `arm64-darwin` or `x86_64-darwin` Ruby) this step is a no-op — nokogiri ships a precompiled binary, you can skip the rest of this section and move on.** Keep reading only if your `gem install` actually fails.
310
-
311
- `mmmessage --markdown` renders HTML-only message bodies as readable markdown. It needs the `reverse_markdown` gem, which has `nokogiri` as a transitive dependency:
312
-
313
- ```bash
314
- gem install reverse_markdown
315
- ```
316
-
317
- That single command pulls `nokogiri` in automatically — no separate `gem install nokogiri` step. This is kept out of the base install because nokogiri ships a native extension. On Ruby/platform combinations without a precompiled match nokogiri falls back to compiling from source — it vendors its own libxml2/libxslt, but it does need a C compiler, which on macOS means Xcode Command Line Tools (`xcode-select --install`). If `gem install reverse_markdown` fails, that's almost certainly the cause.
318
-
319
- If you never use `--markdown`, you never pay any of this. If you do invoke `--markdown` without the gem installed, `mmmessage` warns with a clear install hint and falls back to the raw HTML body (it does not abort — so the in-process MCP server survives a missing optional dependency).
320
-
321
- ### From source (development)
322
-
323
- If you're hacking on the gem itself, skip `gem install` and put the repo's `exe/` on your `PATH`. Clone wherever you keep source repos, then prepend its `exe/` to `PATH` from your shell's rc file (`~/.zshrc`, `~/.bashrc`, etc.):
324
-
325
- ```bash
326
- git clone https://github.com/brianmd/mailmate.git
327
- cd mailmate
328
-
329
- # In your shell rc file, add (adjust the path to wherever you cloned):
330
- # export PATH="/absolute/path/to/mailmate/exe:$PATH"
331
- # Then reload the shell (open a new tab, or `source` the rc file).
332
- ```
333
-
334
- Then `mmdiscover` as above.
335
-
336
- ### MCP server
337
-
338
- The gem also ships an MCP server (`exe/mailmate-mcp`) that exposes the same surface to AI assistants as JSON-RPC tools: `search`, `message`, `modify`, `verify`, `send`, `draft`, `open`, `list_mailboxes`, `list_tags`, `resolve_id`. After `gem install mailmate`, `mailmate-mcp` is on your `PATH`.
339
-
340
- #### Claude Code (global, all projects)
341
-
342
- ```bash
343
- claude mcp add --scope user mailmate "$(which mailmate-mcp)"
344
- ```
345
-
346
- Or add manually to `~/.claude.json` under `"mcpServers"`:
347
-
348
- ```json
349
- "mailmate": {
350
- "type": "stdio",
351
- "command": "/absolute/path/to/mailmate-mcp",
352
- "args": [],
353
- "env": {}
354
- }
355
- ```
356
-
357
- #### Claude Desktop
358
-
359
- Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
360
-
361
- ```json
362
- {
363
- "mcpServers": {
364
- "mailmate": {
365
- "command": "/absolute/path/to/mailmate-mcp"
366
- }
367
- }
368
- }
369
- ```
370
-
371
- Restart Claude Desktop after any change to server code or config.
372
-
373
441
  ## Commands
374
442
 
375
443
  | Command | What it does |
@@ -397,7 +465,7 @@ The CLI tools take an `eml-id` — the integer filename of MailMate's `.eml` sto
397
465
  require "mailmate"
398
466
 
399
467
  # Parse and evaluate a MailMate smart-mailbox filter
400
- ast = Mailmate.compile_filter("from.name = 'Medium' and #date-received > '1 days ago'")
468
+ ast = Mailmate.compile_filter("from.name = 'Substack' and #date-received > '1 days ago'")
401
469
  # ... feed `ast` to Mailmate::Evaluator ...
402
470
 
403
471
  # Read the binary `#flags` index