@redpop/apple-mail-actions-mcp 0.0.0-placeholder.0 → 0.3.2
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/LICENSE +21 -0
- package/README.md +228 -4
- package/SECURITY.md +87 -0
- package/bin/Mail MCP Actions.app/Contents/CodeResources +0 -0
- package/bin/Mail MCP Actions.app/Contents/Info.plist +28 -0
- package/bin/Mail MCP Actions.app/Contents/MacOS/mail-actions +0 -0
- package/bin/Mail MCP Actions.app/Contents/Resources/AppIcon.icns +0 -0
- package/bin/Mail MCP Actions.app/Contents/_CodeSignature/CodeResources +128 -0
- package/dist/bundle.js +85 -0
- package/dist/bundle.js.map +1 -0
- package/dist/cli/doctor.js +131 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/main.js +67 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/setup.js +52 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/config.js +49 -0
- package/dist/config.js.map +1 -0
- package/dist/context.js +27 -0
- package/dist/context.js.map +1 -0
- package/dist/errors.js +24 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/locks.js +214 -0
- package/dist/locks.js.map +1 -0
- package/dist/mover.js +150 -0
- package/dist/mover.js.map +1 -0
- package/dist/rules.js +113 -0
- package/dist/rules.js.map +1 -0
- package/dist/server.js +19 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/moveMessages.js +52 -0
- package/dist/tools/moveMessages.js.map +1 -0
- package/package.json +29 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Martin Alker
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,5 +1,229 @@
|
|
|
1
|
-
#
|
|
1
|
+
# apple-mail-actions-mcp
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
An [MCP](https://modelcontextprotocol.io) server with one tool, `move_messages`. It moves
|
|
4
|
+
messages to another mailbox of the same account in Apple Mail. It is the companion of
|
|
5
|
+
[`@redpop/apple-mail-mcp`](../../README.md), the read-only server. That package stays read-only;
|
|
6
|
+
all moving code lives here.
|
|
7
|
+
|
|
8
|
+
**macOS only.** Status: not published yet. The install commands below apply from the first
|
|
9
|
+
release; until then, build it from source.
|
|
10
|
+
|
|
11
|
+
## What it does
|
|
12
|
+
|
|
13
|
+
Claude looks at your mailbox with the read server, proposes a folder for some messages, and
|
|
14
|
+
you confirm. Then Claude calls `move_messages` with message ids and target mailbox ids. The
|
|
15
|
+
package only moves messages. It does not send, draft, delete, flag, create folders, read mail
|
|
16
|
+
or move between accounts.
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
"Which of these invoices belong in the Accounting folder? Move the ones you are sure about."
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The server asks Claude to show the proposal first, with the choices move all, move some,
|
|
23
|
+
adjust first or move none. That is a request in the server instructions, not a lock. The lock
|
|
24
|
+
is that the tool is not marked read-only, so your client asks before each call, but only in a
|
|
25
|
+
mode that asks.
|
|
26
|
+
|
|
27
|
+
**Do not choose "Always allow" for this tool.** It turns that confirmation off, and a
|
|
28
|
+
malicious mail could then make the model move messages without you seeing it.
|
|
29
|
+
|
|
30
|
+
**Use a mode that asks.** In Claude Desktop and Cowork, the Auto mode does not ask: an
|
|
31
|
+
automated check blocks only actions it flags as risky, otherwise Claude moves without asking,
|
|
32
|
+
even though the tool is set to "Needs approval". Choose Manual for conversations that may move
|
|
33
|
+
mail. In a test on 2026-11-11, Auto moved a message right after a "yes" in the chat, with no
|
|
34
|
+
prompt; Manual asked. In Claude Code, modes that approve tools on their own, such as auto mode
|
|
35
|
+
or skipping permissions, have the same effect.
|
|
36
|
+
|
|
37
|
+
## What you need beside it
|
|
38
|
+
|
|
39
|
+
This package needs the read server beside it. The read server lists mailboxes and finds
|
|
40
|
+
messages; this server only takes the ids it returns. Install `@redpop/apple-mail-mcp` first
|
|
41
|
+
and give it Full Disk Access as its README describes.
|
|
42
|
+
|
|
43
|
+
Two settings must match across both servers, because neither server sees the other's
|
|
44
|
+
configuration:
|
|
45
|
+
|
|
46
|
+
- `APPLE_MAIL_ACCOUNTS` is required here. If it is empty, every call fails with
|
|
47
|
+
`CONFIG_INVALID`; moving is never enabled for all accounts. Use the same list in the read
|
|
48
|
+
server.
|
|
49
|
+
- Set `APPLE_MAIL_EXCLUDE_MAILBOXES` in both servers. A mailbox hidden in the read server but
|
|
50
|
+
not here would still be a possible target.
|
|
51
|
+
|
|
52
|
+
Junk and trash are not visible by default, in either server.
|
|
53
|
+
|
|
54
|
+
## What it moves, and what it refuses
|
|
55
|
+
|
|
56
|
+
Targets are all visible mailboxes except trash, junk, drafts, sent and "all mail". Sources
|
|
57
|
+
must be visible under the same rules, so junk and trash are not sources by default. Further
|
|
58
|
+
targets can be forbidden with `APPLE_MAIL_MOVE_EXCLUDE_TARGETS`.
|
|
59
|
+
|
|
60
|
+
Each message gets one status. `moved` means the index of Mail shows it in the target and no
|
|
61
|
+
longer in the source. `moved_unverified` means the move was requested but the index did not
|
|
62
|
+
confirm it within 10 seconds. Both describe the local state in Mail, not the mail server.
|
|
63
|
+
`failed` and the `skipped_*` statuses say why a message did not move; nothing was changed for
|
|
64
|
+
those.
|
|
65
|
+
|
|
66
|
+
Message ids are valid for the current index only. After Mail rebuilds a mailbox, or after a
|
|
67
|
+
`skipped_not_found` status, search again and use the new ids.
|
|
68
|
+
|
|
69
|
+
Messages are skipped, never moved, in these cases:
|
|
70
|
+
|
|
71
|
+
- The message looks like a security mail (verification code, password reset, sign-in link).
|
|
72
|
+
The check looks at the subject only; the message text is not read. `APPLE_MAIL_SHOW_SECURITY_MAILS` does not lift it.
|
|
73
|
+
- The message was moved in the last 5 minutes. Rapid back and forth can create a copy on the
|
|
74
|
+
mail server, so each moved message is locked for 5 minutes, across processes. Moves you make
|
|
75
|
+
yourself in Mail are not known to the lock.
|
|
76
|
+
- The message is visible in several mailboxes and `sourceMailboxId` does not pick one.
|
|
77
|
+
- The message has no Message-ID header in Mail's index. On macOS 15 Mail does not store it, so
|
|
78
|
+
the tool cannot move anything there. `doctor` says so.
|
|
79
|
+
- The source and target are the same, or in different accounts.
|
|
80
|
+
- The account is not supported. Only mailbox.org has been tested. Other IMAP providers are
|
|
81
|
+
untested: they run without a refusal, so limit `APPLE_MAIL_ACCOUNTS` to accounts you
|
|
82
|
+
are willing to try. Gmail, Exchange and local folders are refused.
|
|
83
|
+
- A folder name differs from another folder of the account only in case, or contains control
|
|
84
|
+
characters.
|
|
85
|
+
|
|
86
|
+
Moves run one after another, never in parallel, and are never retried: a failed move may have
|
|
87
|
+
arrived halfway.
|
|
88
|
+
|
|
89
|
+
A folder that never held a message is not in Mail's index, so it does not appear in
|
|
90
|
+
`list_mailboxes` and cannot be a target. Put one message in it by hand first.
|
|
91
|
+
|
|
92
|
+
Subfolders and INBOX have had one manual test: on 2026-11-04, on macOS 27 with one IMAP
|
|
93
|
+
account at mailbox.org, one test mail was moved to a subfolder and back, and one test mail to
|
|
94
|
+
INBOX and back, 60 seconds apart, with no copy in the webmail. These were single test mails,
|
|
95
|
+
not a series.
|
|
96
|
+
|
|
97
|
+
## Requirements
|
|
98
|
+
|
|
99
|
+
- macOS 26 or later to move mail. The server starts on macOS 15, but Mail there does not store
|
|
100
|
+
the Message-ID header, so every move is refused with `skipped_no_message_id`; the server
|
|
101
|
+
never moves a message it cannot identify by its Message-ID. Verified with a real mailbox
|
|
102
|
+
only on macOS 27 with Mail 16 and one mailbox.org account.
|
|
103
|
+
- Node.js 22.16 or newer in the 22 line, or 24 and newer.
|
|
104
|
+
- The read server `@redpop/apple-mail-mcp` at the same version, with Full Disk Access.
|
|
105
|
+
- Mail must be running. The server never starts it; without it the call fails with
|
|
106
|
+
`MAIL_NOT_RUNNING`.
|
|
107
|
+
|
|
108
|
+
## Install
|
|
109
|
+
|
|
110
|
+
**Claude Code, as a plugin:**
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
/plugin install apple-mail-actions-mcp --marketplace redpop/apple-mail-mcp
|
|
114
|
+
npx -y @redpop/apple-mail-actions-mcp setup
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Claude Code without the plugin:**
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
claude mcp add apple-mail-actions \
|
|
121
|
+
-e APPLE_MAIL_ACCOUNTS=work@example.com \
|
|
122
|
+
-e APPLE_MAIL_EXCLUDE_MAILBOXES=Archive \
|
|
123
|
+
-- npx -y @redpop/apple-mail-actions-mcp
|
|
124
|
+
npx -y @redpop/apple-mail-actions-mcp setup
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Claude Desktop and Cowork:** the release will carry an `.mcpb` bundle with the same settings
|
|
128
|
+
page. Claude Desktop does not switch it on after the install, because the accounts setting is
|
|
129
|
+
required; enter the accounts, save, and switch it on under Settings > Extensions. The plugin does not work in Cowork, which starts a plugin's local server inside its Linux
|
|
130
|
+
virtual machine, away from Mail, or in the desktop chat, which ignores such servers.
|
|
131
|
+
|
|
132
|
+
`setup` first runs the setup of the read server (Full Disk Access for its helper), then asks
|
|
133
|
+
macOS for the Automation permission of "Mail MCP Actions" to control Mail. Mail must be
|
|
134
|
+
running for the dialog to appear. The permission goes to that small app alone, not to Claude,
|
|
135
|
+
your terminal or Node. It can be switched off under System Settings > Privacy & Security >
|
|
136
|
+
Automation.
|
|
137
|
+
|
|
138
|
+
## Configuration
|
|
139
|
+
|
|
140
|
+
Set these in the environment of the server. They cannot be changed through tool parameters.
|
|
141
|
+
An invalid value does not stop the server; every call then fails with `CONFIG_INVALID` naming
|
|
142
|
+
the setting. The server reads them once, at startup: restart the client after a change.
|
|
143
|
+
|
|
144
|
+
| Variable | Default | Meaning |
|
|
145
|
+
|---|---|---|
|
|
146
|
+
| `APPLE_MAIL_ACCOUNTS` | required | Comma-separated allowlist of accounts, by UUID, address or name. Empty is an error, not "all accounts". |
|
|
147
|
+
| `APPLE_MAIL_INCLUDE_JUNK` | `false` | Treat mailboxes with the junk role as visible. Same meaning as in the read server. |
|
|
148
|
+
| `APPLE_MAIL_INCLUDE_TRASH` | `false` | Treat mailboxes with the trash role as visible. Same meaning as in the read server. |
|
|
149
|
+
| `APPLE_MAIL_EXCLUDE_MAILBOXES` | empty | Comma-separated mailboxes to hide, by name or by path with `/`. Set it in both servers. |
|
|
150
|
+
| `APPLE_MAIL_MAX_MOVES` | `25` | Upper bound for the number of messages in one call (1 to 100). |
|
|
151
|
+
| `APPLE_MAIL_MOVE_EXCLUDE_TARGETS` | empty | Comma-separated mailboxes that are never a target, by name or by path with `/`. |
|
|
152
|
+
| `APPLE_MAIL_ACTIONS_PATH` | not set | Development only: path to a Dev or Ad hoc build of the Actions app. A bundle with the release bundle id is refused. |
|
|
153
|
+
|
|
154
|
+
The settings of the read server that do not appear here, for example body search, do not affect
|
|
155
|
+
this package.
|
|
156
|
+
|
|
157
|
+
## Install from source
|
|
158
|
+
|
|
159
|
+
You need the Xcode Command Line Tools and an Apple Development certificate for a build that
|
|
160
|
+
keeps its permission across rebuilds.
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
git clone https://github.com/redpop/apple-mail-mcp.git
|
|
164
|
+
cd apple-mail-mcp
|
|
165
|
+
npm ci
|
|
166
|
+
npm run build && npm run build:helper
|
|
167
|
+
npm run build --workspace packages/actions && npm run build:helper --workspace packages/actions
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Then follow the README of the read server for its helper, and grant Automation with
|
|
171
|
+
`node packages/actions/dist/index.js setup`, with `APPLE_MAIL_ACTIONS_PATH` and
|
|
172
|
+
`APPLE_MAIL_HELPER_PATH` pointing at the Dev bundles in each `bin/` directory. Never sign
|
|
173
|
+
these builds with a Developer ID.
|
|
174
|
+
|
|
175
|
+
Put these variables in front of the command instead of exporting them. An exported
|
|
176
|
+
`APPLE_MAIL_*` variable stays in the shell, and every MCP server that a client started from
|
|
177
|
+
that shell inherits it: an exported `APPLE_MAIL_ACCOUNTS` also limits the read server to those
|
|
178
|
+
accounts. If you exported them, run `unset` on them before you start the client.
|
|
179
|
+
|
|
180
|
+
## Diagnostics
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
npx -y @redpop/apple-mail-actions-mcp doctor
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`doctor` runs the checks of the read server, then checks the Actions app and its signature,
|
|
187
|
+
the Automation permission, whether Mail is running, whether the Message-ID column exists and
|
|
188
|
+
which accounts are supported. Without `--verbose` it names no accounts or addresses. Exit code
|
|
189
|
+
1 means at least one check failed.
|
|
190
|
+
|
|
191
|
+
## Privacy Policy
|
|
192
|
+
|
|
193
|
+
This policy covers the server and the Actions app, as published on npm and in the GitHub
|
|
194
|
+
releases. It adds to the policy of the read server.
|
|
195
|
+
|
|
196
|
+
### What is collected
|
|
197
|
+
|
|
198
|
+
Nothing is collected by the author or by any service. The server has no telemetry, and neither
|
|
199
|
+
the server nor the app opens a network connection. The app never sees mail content. It receives
|
|
200
|
+
a message number, a Message-ID and the names of an account and two mailboxes, and returns a
|
|
201
|
+
status.
|
|
202
|
+
|
|
203
|
+
### How it is used and stored
|
|
204
|
+
|
|
205
|
+
When your AI client calls `move_messages`, the app asks Mail to move the messages you named. The
|
|
206
|
+
server writes one file, `move-locks.json`, under
|
|
207
|
+
`~/Library/Application Support/apple-mail-mcp/`, directory 0700, file 0600. It holds the
|
|
208
|
+
internal message numbers of recently moved messages and the time their lock ends. No names or
|
|
209
|
+
subjects.
|
|
210
|
+
|
|
211
|
+
### Sharing with third parties
|
|
212
|
+
|
|
213
|
+
Nothing is shared. Mail itself synchronizes the move with your mail server, as it does for a move
|
|
214
|
+
you make by hand. The statuses of a call go back to your AI client.
|
|
215
|
+
|
|
216
|
+
### Retention
|
|
217
|
+
|
|
218
|
+
Entries leave the lock file after 5 minutes at the next move. Delete the file at any time. The
|
|
219
|
+
server never deletes or changes anything under `~/Library/Mail` itself; the moves are Mail's.
|
|
220
|
+
|
|
221
|
+
### Contact
|
|
222
|
+
|
|
223
|
+
Questions about this policy: open an issue at
|
|
224
|
+
<https://github.com/redpop/apple-mail-mcp/issues>. Security problems: report them privately as
|
|
225
|
+
[`SECURITY.md`](./SECURITY.md#reporting-a-vulnerability) describes.
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
MIT, see [`LICENSE`](./LICENSE).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
This covers `@redpop/apple-mail-actions-mcp`. The threat model of the read server, including
|
|
4
|
+
prompt injection and what a mailbox holds, is in the
|
|
5
|
+
[root `SECURITY.md`](../../SECURITY.md) and applies here too.
|
|
6
|
+
|
|
7
|
+
## What the package can do
|
|
8
|
+
|
|
9
|
+
One tool, `move_messages`, moves messages between mailboxes of one account. The signed app
|
|
10
|
+
"Mail MCP Actions" does the move by asking Mail through Apple Events. It has the Automation
|
|
11
|
+
permission for Mail and no Full Disk Access. It has a fixed set of commands (`hello`,
|
|
12
|
+
`permission`, `request-permission`, `move`) and no command to list, read or delete mail.
|
|
13
|
+
It never sees mail content: it gets a message number, a Message-ID and names, and returns a
|
|
14
|
+
status or an error number. It holds no credentials and opens no network connection.
|
|
15
|
+
|
|
16
|
+
What limits the server:
|
|
17
|
+
|
|
18
|
+
- **Settings come from the environment, never from tool parameters.** The account list, the
|
|
19
|
+
limit per call and the forbidden targets cannot be widened by the model, so an injected mail
|
|
20
|
+
cannot widen them.
|
|
21
|
+
- **Accounts are an allowlist.** An empty `APPLE_MAIL_ACCOUNTS` makes every call fail.
|
|
22
|
+
- **Targets exclude trash, junk, drafts, sent and "all mail".** Sources must be visible under
|
|
23
|
+
the policy of the read server's rules.
|
|
24
|
+
- **Nothing is deleted or sent.** The worst a move does is put a message in the wrong
|
|
25
|
+
folder; moving it back works with the same tool once the 5-minute lock has ended.
|
|
26
|
+
- **The message is checked before the move.** The app finds the message by number and compares
|
|
27
|
+
its Message-ID with the one the server read from the index. It moves only if both agree.
|
|
28
|
+
- **No raw scripts.** Account and folder names are never put into script text unchecked.
|
|
29
|
+
- **Moves are locked for 5 minutes, one at a time, never retried.**
|
|
30
|
+
- **The client asks, in a mode that asks.** `move_messages` is not marked read-only, so a client
|
|
31
|
+
asks before each call. If you set "Always allow" for the tool, that is gone; do not. A mode
|
|
32
|
+
that approves tools on its own removes it too: in Claude Desktop and Cowork, Auto moves without
|
|
33
|
+
asking when its automated check does not flag the action, even for a tool set to "Needs
|
|
34
|
+
approval" (tested 2026-11-11). Use Manual there, and no auto-approving mode in Claude Code.
|
|
35
|
+
|
|
36
|
+
## Residual risks
|
|
37
|
+
|
|
38
|
+
### Any local process can run the app
|
|
39
|
+
|
|
40
|
+
Any program running as your macOS user can start the Actions app and send it `move`
|
|
41
|
+
commands directly, as long as the Automation permission exists. The server's policy lives in
|
|
42
|
+
Node, not in the app. Such a program could move any message it can identify, in any account
|
|
43
|
+
that Mail holds, into any folder, trash included. The damage is limited to moving:
|
|
44
|
+
the app cannot read, send, delete or flag mail, and a moved message can be moved back. If your
|
|
45
|
+
threat model includes hostile programs under your account, this package does not defend
|
|
46
|
+
against them. The permission can be switched off in System Settings > Privacy & Security >
|
|
47
|
+
Automation.
|
|
48
|
+
|
|
49
|
+
Mail's own permission model is the reason for the shape: the grant attaches to the app because
|
|
50
|
+
it re-launches itself as its own responsible process. The permission does not go to Claude,
|
|
51
|
+
your terminal or Node.
|
|
52
|
+
|
|
53
|
+
### Any local process can switch the grant off
|
|
54
|
+
|
|
55
|
+
As with the read helper, a program of your user can build an ad-hoc bundle with the release
|
|
56
|
+
bundle id and run it. macOS then turns the grant off. The server is blind until you switch it
|
|
57
|
+
on again. Local builds use their own bundle ids. The server starts a bundle with the release id
|
|
58
|
+
only if its signature verifies against the project's Developer ID requirement, and starts a Dev
|
|
59
|
+
or Ad hoc bundle only when `APPLE_MAIL_ACTIONS_PATH` points at it; it refuses any other bundle id.
|
|
60
|
+
|
|
61
|
+
### The security-mail check looks at the subject only
|
|
62
|
+
|
|
63
|
+
Messages that look like security mails are never moved. The check is the same heuristic as in
|
|
64
|
+
the read server, applied to the subject only. The message text is not read, and the sender address does not change the result. It does not load the body: the body
|
|
65
|
+
check also flags ordinary mail with a word like "code" and would block normal moves, and it
|
|
66
|
+
could not judge mail that was never downloaded. So a security mail with a harmless subject
|
|
67
|
+
can be moved, to a folder you allow. Moving does not read it or send it anywhere. The check
|
|
68
|
+
is a safety net, not a guarantee.
|
|
69
|
+
|
|
70
|
+
### Status means the local state, not the server
|
|
71
|
+
|
|
72
|
+
A status describes the local state in Mail, not the server. `moved` says that the index of Mail shows the message in the target and not in the source. Mail
|
|
73
|
+
then synchronizes with the server. If that fails, or if Mail creates a copy on the server
|
|
74
|
+
(rapid back and forth can), the status does not know. Check in the webmail if it matters.
|
|
75
|
+
|
|
76
|
+
### Untested providers
|
|
77
|
+
|
|
78
|
+
Only mailbox.org has been tested. Other IMAP providers run without a refusal and may behave
|
|
79
|
+
differently, for example by keeping a copy. Limit `APPLE_MAIL_ACCOUNTS` to accounts you are
|
|
80
|
+
willing to try.
|
|
81
|
+
|
|
82
|
+
## Reporting a vulnerability
|
|
83
|
+
|
|
84
|
+
Please report security problems privately through GitHub Security Advisories:
|
|
85
|
+
<https://github.com/redpop/apple-mail-mcp/security/advisories/new>. Do not open a public issue
|
|
86
|
+
for them. Include the macOS version, the output of `doctor` and steps to reproduce. Please do
|
|
87
|
+
not attach real mail.
|
|
Binary file
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>CFBundleIdentifier</key>
|
|
6
|
+
<string>io.github.redpop.apple-mail-mcp.mail-actions</string>
|
|
7
|
+
<key>CFBundleName</key>
|
|
8
|
+
<string>Mail MCP Actions</string>
|
|
9
|
+
<key>CFBundleDisplayName</key>
|
|
10
|
+
<string>Mail MCP Actions</string>
|
|
11
|
+
<key>CFBundleExecutable</key>
|
|
12
|
+
<string>mail-actions</string>
|
|
13
|
+
<key>CFBundleIconFile</key>
|
|
14
|
+
<string>AppIcon</string>
|
|
15
|
+
<key>CFBundlePackageType</key>
|
|
16
|
+
<string>APPL</string>
|
|
17
|
+
<key>CFBundleShortVersionString</key>
|
|
18
|
+
<string>0.3.2</string>
|
|
19
|
+
<key>CFBundleVersion</key>
|
|
20
|
+
<string>0.3.2</string>
|
|
21
|
+
<key>LSMinimumSystemVersion</key>
|
|
22
|
+
<string>15.0</string>
|
|
23
|
+
<key>NSAppleEventsUsageDescription</key>
|
|
24
|
+
<string>Moves messages between folders in Apple Mail when you confirm it.</string>
|
|
25
|
+
<key>LSUIElement</key>
|
|
26
|
+
<true/>
|
|
27
|
+
</dict>
|
|
28
|
+
</plist>
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>files</key>
|
|
6
|
+
<dict>
|
|
7
|
+
<key>Resources/AppIcon.icns</key>
|
|
8
|
+
<data>
|
|
9
|
+
E9KEMJn1uUwhssgjN+4uU5ZYdpA=
|
|
10
|
+
</data>
|
|
11
|
+
</dict>
|
|
12
|
+
<key>files2</key>
|
|
13
|
+
<dict>
|
|
14
|
+
<key>Resources/AppIcon.icns</key>
|
|
15
|
+
<dict>
|
|
16
|
+
<key>hash2</key>
|
|
17
|
+
<data>
|
|
18
|
+
GT2ygd05uo0+IBsX1vwkfZQ0HNjYcREuoPCNFAL6mNY=
|
|
19
|
+
</data>
|
|
20
|
+
</dict>
|
|
21
|
+
</dict>
|
|
22
|
+
<key>rules</key>
|
|
23
|
+
<dict>
|
|
24
|
+
<key>^Resources/</key>
|
|
25
|
+
<true/>
|
|
26
|
+
<key>^Resources/.*\.lproj/</key>
|
|
27
|
+
<dict>
|
|
28
|
+
<key>optional</key>
|
|
29
|
+
<true/>
|
|
30
|
+
<key>weight</key>
|
|
31
|
+
<real>1000</real>
|
|
32
|
+
</dict>
|
|
33
|
+
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
|
34
|
+
<dict>
|
|
35
|
+
<key>omit</key>
|
|
36
|
+
<true/>
|
|
37
|
+
<key>weight</key>
|
|
38
|
+
<real>1100</real>
|
|
39
|
+
</dict>
|
|
40
|
+
<key>^Resources/Base\.lproj/</key>
|
|
41
|
+
<dict>
|
|
42
|
+
<key>weight</key>
|
|
43
|
+
<real>1010</real>
|
|
44
|
+
</dict>
|
|
45
|
+
<key>^version.plist$</key>
|
|
46
|
+
<true/>
|
|
47
|
+
</dict>
|
|
48
|
+
<key>rules2</key>
|
|
49
|
+
<dict>
|
|
50
|
+
<key>.*\.dSYM($|/)</key>
|
|
51
|
+
<dict>
|
|
52
|
+
<key>weight</key>
|
|
53
|
+
<real>11</real>
|
|
54
|
+
</dict>
|
|
55
|
+
<key>^(.*/)?\.DS_Store$</key>
|
|
56
|
+
<dict>
|
|
57
|
+
<key>omit</key>
|
|
58
|
+
<true/>
|
|
59
|
+
<key>weight</key>
|
|
60
|
+
<real>2000</real>
|
|
61
|
+
</dict>
|
|
62
|
+
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
|
|
63
|
+
<dict>
|
|
64
|
+
<key>nested</key>
|
|
65
|
+
<true/>
|
|
66
|
+
<key>weight</key>
|
|
67
|
+
<real>10</real>
|
|
68
|
+
</dict>
|
|
69
|
+
<key>^.*</key>
|
|
70
|
+
<true/>
|
|
71
|
+
<key>^Info\.plist$</key>
|
|
72
|
+
<dict>
|
|
73
|
+
<key>omit</key>
|
|
74
|
+
<true/>
|
|
75
|
+
<key>weight</key>
|
|
76
|
+
<real>20</real>
|
|
77
|
+
</dict>
|
|
78
|
+
<key>^PkgInfo$</key>
|
|
79
|
+
<dict>
|
|
80
|
+
<key>omit</key>
|
|
81
|
+
<true/>
|
|
82
|
+
<key>weight</key>
|
|
83
|
+
<real>20</real>
|
|
84
|
+
</dict>
|
|
85
|
+
<key>^Resources/</key>
|
|
86
|
+
<dict>
|
|
87
|
+
<key>weight</key>
|
|
88
|
+
<real>20</real>
|
|
89
|
+
</dict>
|
|
90
|
+
<key>^Resources/.*\.lproj/</key>
|
|
91
|
+
<dict>
|
|
92
|
+
<key>optional</key>
|
|
93
|
+
<true/>
|
|
94
|
+
<key>weight</key>
|
|
95
|
+
<real>1000</real>
|
|
96
|
+
</dict>
|
|
97
|
+
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
|
98
|
+
<dict>
|
|
99
|
+
<key>omit</key>
|
|
100
|
+
<true/>
|
|
101
|
+
<key>weight</key>
|
|
102
|
+
<real>1100</real>
|
|
103
|
+
</dict>
|
|
104
|
+
<key>^Resources/Base\.lproj/</key>
|
|
105
|
+
<dict>
|
|
106
|
+
<key>weight</key>
|
|
107
|
+
<real>1010</real>
|
|
108
|
+
</dict>
|
|
109
|
+
<key>^[^/]+$</key>
|
|
110
|
+
<dict>
|
|
111
|
+
<key>nested</key>
|
|
112
|
+
<true/>
|
|
113
|
+
<key>weight</key>
|
|
114
|
+
<real>10</real>
|
|
115
|
+
</dict>
|
|
116
|
+
<key>^embedded\.provisionprofile$</key>
|
|
117
|
+
<dict>
|
|
118
|
+
<key>weight</key>
|
|
119
|
+
<real>20</real>
|
|
120
|
+
</dict>
|
|
121
|
+
<key>^version\.plist$</key>
|
|
122
|
+
<dict>
|
|
123
|
+
<key>weight</key>
|
|
124
|
+
<real>20</real>
|
|
125
|
+
</dict>
|
|
126
|
+
</dict>
|
|
127
|
+
</dict>
|
|
128
|
+
</plist>
|
package/dist/bundle.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { readBundleId, TEAM_ID } from '@redpop/apple-mail-mcp/dist/signing.js';
|
|
5
|
+
import { createStdioRunner } from '@redpop/apple-mail-mcp/dist/stdioRunner.js';
|
|
6
|
+
import { ActionsError } from './errors.js';
|
|
7
|
+
// Die IDs stehen auch in packages/actions/swift/build.sh; tests/signing.test.ts prueft, dass sie passen.
|
|
8
|
+
export const RELEASE_BUNDLE_ID = 'io.github.redpop.apple-mail-mcp.mail-actions';
|
|
9
|
+
export const DEV_BUNDLE_ID = RELEASE_BUNDLE_ID + '.dev';
|
|
10
|
+
export const ADHOC_BUNDLE_ID = RELEASE_BUNDLE_ID + '.adhoc';
|
|
11
|
+
export const RELEASE_REQUIREMENT = `identifier "${RELEASE_BUNDLE_ID}" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[subject.OU] = "${TEAM_ID}"`;
|
|
12
|
+
/** Mehr als die 2 Minuten, nach denen ein Apple Event selbst aufgibt: Sonst gaelte ein noch laufender Zug als tot. */
|
|
13
|
+
export const ACTIONS_TIMEOUTS = { firstRequestMs: 130_000, requestMs: 130_000 };
|
|
14
|
+
const defaultExec = (file, args) => execFileSync(file, args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
15
|
+
/** Wo das Bundle im ausgelieferten Paket liegt (gleich fuer src/ und dist/). */
|
|
16
|
+
export function packagedActionsBundle() {
|
|
17
|
+
return fileURLToPath(new URL('../bin/Mail MCP Actions.app', import.meta.url));
|
|
18
|
+
}
|
|
19
|
+
export function resolveActionsBundle(config) {
|
|
20
|
+
return config.actionsPath ?? packagedActionsBundle();
|
|
21
|
+
}
|
|
22
|
+
export function actionsExecutable(bundle) {
|
|
23
|
+
return `${bundle}/Contents/MacOS/mail-actions`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Signaturgatter vor dem ersten Start, nach dem Muster von checkBundle im Lesepaket. Der Override erlaubt nur
|
|
27
|
+
* Dev- und Ad-hoc-Bundles: Eine Release-ID per Override waere ein fremder Build mit der Freigabe des Nutzers.
|
|
28
|
+
*/
|
|
29
|
+
export function checkActionsBundle(bundle, opts) {
|
|
30
|
+
const exec = opts.exec ?? defaultExec;
|
|
31
|
+
if (!(opts.exists ?? existsSync)(bundle))
|
|
32
|
+
throw new ActionsError('ACTIONS_BUNDLE_MISSING');
|
|
33
|
+
let id;
|
|
34
|
+
try {
|
|
35
|
+
id = readBundleId(bundle, exec);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
throw new ActionsError('ACTIONS_SIGNATURE_INVALID');
|
|
39
|
+
}
|
|
40
|
+
if (id === RELEASE_BUNDLE_ID) {
|
|
41
|
+
if (opts.override)
|
|
42
|
+
throw new ActionsError('ACTIONS_SIGNATURE_INVALID');
|
|
43
|
+
try {
|
|
44
|
+
exec('/usr/bin/codesign', ['--verify', '--strict', '--deep', `-R=${RELEASE_REQUIREMENT}`, bundle]);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
throw new ActionsError('ACTIONS_SIGNATURE_INVALID');
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (opts.override && (id === DEV_BUNDLE_ID || id === ADHOC_BUNDLE_ID))
|
|
52
|
+
return;
|
|
53
|
+
throw new ActionsError('ACTIONS_SIGNATURE_INVALID');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Ein Runner pro Serverprozess; das Gatter laeuft einmal vor dem ersten Start, auch ein Fehlschlag bleibt
|
|
57
|
+
* bis zum Prozessende stehen (wie createHelperRunner).
|
|
58
|
+
*/
|
|
59
|
+
export function createActionsRunner(config, deps = {}) {
|
|
60
|
+
const bundlePath = resolveActionsBundle(config);
|
|
61
|
+
const options = { debug: config.debug === true };
|
|
62
|
+
const inner = (deps.makeRunner ?? ((exe, opts) => createStdioRunner(exe, ACTIONS_TIMEOUTS, opts)))(actionsExecutable(bundlePath), options);
|
|
63
|
+
const check = deps.check ?? checkActionsBundle;
|
|
64
|
+
let gate;
|
|
65
|
+
return {
|
|
66
|
+
bundlePath,
|
|
67
|
+
run(command, payload) {
|
|
68
|
+
if (gate === undefined) {
|
|
69
|
+
try {
|
|
70
|
+
check(bundlePath, { override: config.actionsPath !== null });
|
|
71
|
+
gate = true;
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
gate = e;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (gate !== true)
|
|
78
|
+
return Promise.reject(gate);
|
|
79
|
+
return inner.run(command, payload);
|
|
80
|
+
},
|
|
81
|
+
stop: () => inner.stop(),
|
|
82
|
+
helperVersion: () => inner.helperVersion(),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=bundle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"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"}
|