airmail-mcp 1.0.20 → 1.0.22
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/dist/index.js +73 -27
- package/manifest.json +5 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -57,39 +57,85 @@ const AUTO_LAUNCH_AIRMAIL = envFlag("AIRMAIL_MCP_AUTO_LAUNCH");
|
|
|
57
57
|
let parentCodeSignTeamID = null;
|
|
58
58
|
let parentPhysicalIdentity = null;
|
|
59
59
|
let parentBundleIdentifier = null;
|
|
60
|
-
function
|
|
60
|
+
function processInfo(pid) {
|
|
61
61
|
try {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
if (!
|
|
62
|
+
const ppidText = execFileSync("ps", ["-p", String(pid), "-o", "ppid="], { encoding: "utf-8" }).trim();
|
|
63
|
+
const command = execFileSync("ps", ["-p", String(pid), "-o", "comm="], { encoding: "utf-8" }).trim();
|
|
64
|
+
const ppid = parseInt(ppidText, 10);
|
|
65
|
+
if (!command || isNaN(ppid))
|
|
66
|
+
return null;
|
|
67
|
+
return { pid, ppid, command };
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function appBundlePath(executablePath) {
|
|
74
|
+
const appIdx = executablePath.indexOf(".app/");
|
|
75
|
+
return appIdx === -1 ? executablePath : executablePath.slice(0, appIdx + 4);
|
|
76
|
+
}
|
|
77
|
+
function physicalIdentityForPath(path) {
|
|
78
|
+
return path.endsWith(".app") ? `app:${path}` : `path:${path}`;
|
|
79
|
+
}
|
|
80
|
+
function codesignInfo(path) {
|
|
81
|
+
const result = spawnSync("codesign", ["-dv", "--verbose=2", path], {
|
|
82
|
+
encoding: "utf-8",
|
|
83
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
84
|
+
});
|
|
85
|
+
const output = (result.stdout || "") + (result.stderr || "");
|
|
86
|
+
const identifier = output.match(/Identifier=(\S+)/)?.[1] ?? null;
|
|
87
|
+
const rawTeam = output.match(/TeamIdentifier=(.+)/)?.[1]?.trim() ?? null;
|
|
88
|
+
const teamID = rawTeam && rawTeam !== "not set" && rawTeam !== "not" ? rawTeam : null;
|
|
89
|
+
return { identifier, teamID };
|
|
90
|
+
}
|
|
91
|
+
function assignParentIdentity(path, sign) {
|
|
92
|
+
parentPhysicalIdentity = physicalIdentityForPath(path);
|
|
93
|
+
parentBundleIdentifier = sign.identifier;
|
|
94
|
+
parentCodeSignTeamID = sign.teamID;
|
|
95
|
+
if (parentBundleIdentifier) {
|
|
96
|
+
log(`Parent identity: ${parentBundleIdentifier} (${parentPhysicalIdentity})`);
|
|
97
|
+
}
|
|
98
|
+
if (parentCodeSignTeamID) {
|
|
99
|
+
log(`Parent code sign: Team ID ${parentCodeSignTeamID}`);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
log(`Parent code sign: unsigned (${parentPhysicalIdentity})`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function resolveParentCodeSign() {
|
|
106
|
+
let fallback = null;
|
|
107
|
+
let unsignedApp = null;
|
|
108
|
+
for (let pid = process.ppid, depth = 0; pid > 1 && depth < 16; depth++) {
|
|
109
|
+
const info = processInfo(pid);
|
|
110
|
+
if (!info)
|
|
111
|
+
break;
|
|
112
|
+
const path = appBundlePath(info.command);
|
|
113
|
+
const sign = codesignInfo(path);
|
|
114
|
+
if (!fallback) {
|
|
115
|
+
fallback = { path, sign };
|
|
116
|
+
}
|
|
117
|
+
// Prefer a signed app bundle. With npx, the immediate parent is usually
|
|
118
|
+
// node/npm, so the real client identity is one or more ancestors above it.
|
|
119
|
+
if (path.endsWith(".app") && sign.teamID) {
|
|
120
|
+
assignParentIdentity(path, sign);
|
|
66
121
|
return;
|
|
67
|
-
// Walk up to find .app bundle (if any)
|
|
68
|
-
let appPath = parentPath;
|
|
69
|
-
const appIdx = parentPath.indexOf(".app/");
|
|
70
|
-
if (appIdx !== -1) {
|
|
71
|
-
appPath = parentPath.slice(0, appIdx + 4);
|
|
72
122
|
}
|
|
73
|
-
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
78
|
-
});
|
|
79
|
-
const output = (result.stdout || "") + (result.stderr || "");
|
|
80
|
-
const identifierMatch = output.match(/Identifier=(\S+)/);
|
|
81
|
-
if (identifierMatch) {
|
|
82
|
-
parentBundleIdentifier = identifierMatch[1];
|
|
83
|
-
log(`Parent identity: ${parentBundleIdentifier} (${parentPhysicalIdentity})`);
|
|
123
|
+
// If no signed bundle exists, an app bundle is still more stable than
|
|
124
|
+
// transient npm/node helper paths.
|
|
125
|
+
if (path.endsWith(".app") && !unsignedApp) {
|
|
126
|
+
unsignedApp = { path, sign };
|
|
84
127
|
}
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
log(`Parent code sign: Team ID ${parentCodeSignTeamID}`);
|
|
128
|
+
// Signed command-line tools are useful, but less specific than app bundles.
|
|
129
|
+
if (!path.endsWith(".app") && sign.teamID) {
|
|
130
|
+
fallback = { path, sign };
|
|
89
131
|
}
|
|
132
|
+
pid = info.ppid;
|
|
90
133
|
}
|
|
91
|
-
|
|
92
|
-
|
|
134
|
+
if (unsignedApp) {
|
|
135
|
+
assignParentIdentity(unsignedApp.path, unsignedApp.sign);
|
|
136
|
+
}
|
|
137
|
+
else if (fallback) {
|
|
138
|
+
assignParentIdentity(fallback.path, fallback.sign);
|
|
93
139
|
}
|
|
94
140
|
}
|
|
95
141
|
// ---------------------------------------------------------------------------
|
package/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": "0.3",
|
|
3
3
|
"name": "airmail-mcp",
|
|
4
4
|
"display_name": "Airmail",
|
|
5
|
-
"version": "1.0.
|
|
5
|
+
"version": "1.0.22",
|
|
6
6
|
"description": "Manage emails, calendars, contacts, and more from Claude using Airmail's MCP server.",
|
|
7
7
|
"long_description": "Airmail MCP connects Claude to the Airmail email client for macOS. Read, search, compose, and organize emails. Manage calendars and reminders. Search contacts. The bridge connects locally to Airmail on your Mac. Data retrieved by AI tools is processed by your chosen AI provider.",
|
|
8
8
|
"author": {
|
|
@@ -309,19 +309,19 @@
|
|
|
309
309
|
},
|
|
310
310
|
{
|
|
311
311
|
"name": "list_starred",
|
|
312
|
-
"description": "List starred/flagged messages."
|
|
312
|
+
"description": "List starred/flagged messages (date desc)."
|
|
313
313
|
},
|
|
314
314
|
{
|
|
315
315
|
"name": "list_sent",
|
|
316
|
-
"description": "List sent messages."
|
|
316
|
+
"description": "List sent messages (date desc)."
|
|
317
317
|
},
|
|
318
318
|
{
|
|
319
319
|
"name": "list_trash",
|
|
320
|
-
"description": "List trash messages."
|
|
320
|
+
"description": "List trash messages (date desc)."
|
|
321
321
|
},
|
|
322
322
|
{
|
|
323
323
|
"name": "list_spam",
|
|
324
|
-
"description": "List spam/junk messages."
|
|
324
|
+
"description": "List spam/junk messages (date desc)."
|
|
325
325
|
},
|
|
326
326
|
{
|
|
327
327
|
"name": "search_messages",
|
package/package.json
CHANGED