airmail-mcp 1.0.7 → 1.0.13
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 +39 -0
- package/dist/index.js +30 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -172,6 +172,45 @@ To find your token: open Airmail → **Preferences → MCP** → copy the **Auth
|
|
|
172
172
|
### Meta
|
|
173
173
|
`manage_capabilities` — enable/disable tool groups to reduce context usage
|
|
174
174
|
|
|
175
|
+
## Tool groups
|
|
176
|
+
|
|
177
|
+
Tools are organized into capability groups that can be enabled or disabled at runtime via `manage_capabilities`. This reduces context usage by hiding tools you don't need.
|
|
178
|
+
|
|
179
|
+
| Group | Tools | Default |
|
|
180
|
+
|-------|-------|---------|
|
|
181
|
+
| mail | Email read, actions, compose | Always on |
|
|
182
|
+
| profile | User profile, triage, behavior stats | On |
|
|
183
|
+
| folders | Folder CRUD | On |
|
|
184
|
+
| semantic | Semantic search, index status | On |
|
|
185
|
+
| calendar | Calendar events, reminders | On |
|
|
186
|
+
| contacts | Address book | On |
|
|
187
|
+
| preferences | App preferences | On |
|
|
188
|
+
| rules | Email rules | On |
|
|
189
|
+
| lists | VIP & blocked senders | On |
|
|
190
|
+
| smartfolders | Smart folder CRUD | On |
|
|
191
|
+
| signatures | Email signatures | On |
|
|
192
|
+
| aliases | Email aliases | On |
|
|
193
|
+
| accountsettings | Per-account settings, vacation | On |
|
|
194
|
+
|
|
195
|
+
To enable all groups, ask the AI to call `manage_capabilities` with `enable: ["preferences", "rules", "lists", "smartfolders", "signatures", "aliases", "accountsettings"]`.
|
|
196
|
+
|
|
197
|
+
## Deep links
|
|
198
|
+
|
|
199
|
+
MCP tool responses include `airmail://` deep links that open Airmail directly to the relevant content.
|
|
200
|
+
|
|
201
|
+
| Command | URL | Description |
|
|
202
|
+
|---------|-----|-------------|
|
|
203
|
+
| `message` | `airmail://message?mail=...&messageid=...` | Select message in main window |
|
|
204
|
+
| `open` | `airmail://open?mail=...&messageid=...` | Open message in reader window |
|
|
205
|
+
| `compose` | `airmail://compose?to=...&subject=...` | Open composer with pre-filled content |
|
|
206
|
+
| `reply` | `airmail://reply?mail=...&messageid=...` | Reply to a message |
|
|
207
|
+
| `draft` | `airmail://draft?mail=...&messageid=...` | Open draft in composer |
|
|
208
|
+
| `archive` | `airmail://archive?mail=...&messageid=...` | Archive a message |
|
|
209
|
+
| `delete` | `airmail://delete?mail=...&messageid=...` | Move message to trash |
|
|
210
|
+
| `view` | `airmail://view?mail=...&folder=...` | Navigate to account/folder |
|
|
211
|
+
| `attachment` | `airmail://attachment?mail=...&messageid=...&index=0` | Open an attachment |
|
|
212
|
+
| `settings` | `airmail://settings?pref=mcp_server` | Open Preferences pane |
|
|
213
|
+
|
|
175
214
|
## How it works
|
|
176
215
|
|
|
177
216
|
```
|
package/dist/index.js
CHANGED
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
* collect all data before the FIN arrives.
|
|
12
12
|
*/
|
|
13
13
|
import { execFileSync, spawnSync } from "child_process";
|
|
14
|
-
import { writeSync } from "fs";
|
|
14
|
+
import { readFileSync, writeSync } from "fs";
|
|
15
15
|
import * as net from "net";
|
|
16
|
+
import { dirname, join } from "path";
|
|
17
|
+
import { fileURLToPath } from "url";
|
|
16
18
|
// ---------------------------------------------------------------------------
|
|
17
19
|
// Configuration
|
|
18
20
|
// ---------------------------------------------------------------------------
|
|
@@ -29,7 +31,15 @@ const AIRMAIL_PORT = (() => {
|
|
|
29
31
|
return p;
|
|
30
32
|
})();
|
|
31
33
|
const AIRMAIL_PATH = "/mcp";
|
|
32
|
-
const VERSION =
|
|
34
|
+
const VERSION = (() => {
|
|
35
|
+
try {
|
|
36
|
+
const pkg = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf-8"));
|
|
37
|
+
return pkg.version ?? "0.0.0";
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return "0.0.0";
|
|
41
|
+
}
|
|
42
|
+
})();
|
|
33
43
|
let currentToken = "";
|
|
34
44
|
const RETRY_DELAY_MS = 2000;
|
|
35
45
|
const MAX_LAUNCH_RETRIES = 5;
|
|
@@ -286,7 +296,7 @@ function forward(body, clientName, token, hasId) {
|
|
|
286
296
|
reqHeaders += `Connection: close\r\n`;
|
|
287
297
|
reqHeaders += `User-Agent: airmail-mcp/1.0\r\n`;
|
|
288
298
|
if (token) {
|
|
289
|
-
reqHeaders += `Authorization: Bearer ${token}\r\n`;
|
|
299
|
+
reqHeaders += `Authorization: Bearer ${sanitizeHeaderValue(token)}\r\n`;
|
|
290
300
|
}
|
|
291
301
|
reqHeaders += `X-MCP-Client: ${safeClient}\r\n`;
|
|
292
302
|
if (parentCodeSignTeamID) {
|
|
@@ -485,7 +495,23 @@ async function main() {
|
|
|
485
495
|
}
|
|
486
496
|
pendingAfterInit = [];
|
|
487
497
|
})
|
|
488
|
-
.catch((err) =>
|
|
498
|
+
.catch((err) => {
|
|
499
|
+
log(`Initialize error: ${err}`);
|
|
500
|
+
// Send error responses for queued requests so clients don't hang
|
|
501
|
+
for (const queued of pendingAfterInit) {
|
|
502
|
+
try {
|
|
503
|
+
const q = JSON.parse(queued);
|
|
504
|
+
if (q.id !== undefined) {
|
|
505
|
+
process.stdout.write(JSON.stringify({
|
|
506
|
+
jsonrpc: "2.0", id: q.id,
|
|
507
|
+
error: { code: -32002, message: "Server failed to initialize" },
|
|
508
|
+
}) + "\n");
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
catch { /* not valid JSON, skip */ }
|
|
512
|
+
}
|
|
513
|
+
pendingAfterInit = [];
|
|
514
|
+
});
|
|
489
515
|
inflight.add(p);
|
|
490
516
|
p.finally(() => {
|
|
491
517
|
inflight.delete(p);
|
package/package.json
CHANGED