@redpop/apple-mail-mcp 0.0.0-stage → 0.1.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.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +241 -2
  3. package/SECURITY.md +102 -0
  4. package/bin/Mail MCP Helper.app/Contents/CodeResources +0 -0
  5. package/bin/Mail MCP Helper.app/Contents/Info.plist +26 -0
  6. package/bin/Mail MCP Helper.app/Contents/MacOS/mail-helper +0 -0
  7. package/bin/Mail MCP Helper.app/Contents/Resources/AppIcon.icns +0 -0
  8. package/bin/Mail MCP Helper.app/Contents/_CodeSignature/CodeResources +128 -0
  9. package/dist/bodyScan.js +259 -0
  10. package/dist/bodyScan.js.map +1 -0
  11. package/dist/cli/doctor.js +242 -0
  12. package/dist/cli/doctor.js.map +1 -0
  13. package/dist/cli/main.js +59 -0
  14. package/dist/cli/main.js.map +1 -0
  15. package/dist/cli/setup.js +107 -0
  16. package/dist/cli/setup.js.map +1 -0
  17. package/dist/config.js +74 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/context.js +43 -0
  20. package/dist/context.js.map +1 -0
  21. package/dist/cursor.js +39 -0
  22. package/dist/cursor.js.map +1 -0
  23. package/dist/dates.js +44 -0
  24. package/dist/dates.js.map +1 -0
  25. package/dist/errors.js +56 -0
  26. package/dist/errors.js.map +1 -0
  27. package/dist/fdaPrompt.js +29 -0
  28. package/dist/fdaPrompt.js.map +1 -0
  29. package/dist/fold.js +8 -0
  30. package/dist/fold.js.map +1 -0
  31. package/dist/helper.js +45 -0
  32. package/dist/helper.js.map +1 -0
  33. package/dist/index.js +37 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/links.js +9 -0
  36. package/dist/links.js.map +1 -0
  37. package/dist/mailboxes.js +124 -0
  38. package/dist/mailboxes.js.map +1 -0
  39. package/dist/mime.js +151 -0
  40. package/dist/mime.js.map +1 -0
  41. package/dist/mimeTrim.js +222 -0
  42. package/dist/mimeTrim.js.map +1 -0
  43. package/dist/policy.js +84 -0
  44. package/dist/policy.js.map +1 -0
  45. package/dist/quickHtml.js +238 -0
  46. package/dist/quickHtml.js.map +1 -0
  47. package/dist/quotes.js +72 -0
  48. package/dist/quotes.js.map +1 -0
  49. package/dist/schemas.js +57 -0
  50. package/dist/schemas.js.map +1 -0
  51. package/dist/securityMail.data.js +34 -0
  52. package/dist/securityMail.data.js.map +1 -0
  53. package/dist/securityMail.js +82 -0
  54. package/dist/securityMail.js.map +1 -0
  55. package/dist/server.js +19 -0
  56. package/dist/server.js.map +1 -0
  57. package/dist/signing.js +64 -0
  58. package/dist/signing.js.map +1 -0
  59. package/dist/state.js +46 -0
  60. package/dist/state.js.map +1 -0
  61. package/dist/stdioRunner.js +206 -0
  62. package/dist/stdioRunner.js.map +1 -0
  63. package/dist/summary.js +57 -0
  64. package/dist/summary.js.map +1 -0
  65. package/dist/tools/getMessage.js +112 -0
  66. package/dist/tools/getMessage.js.map +1 -0
  67. package/dist/tools/getThread.js +52 -0
  68. package/dist/tools/getThread.js.map +1 -0
  69. package/dist/tools/index.js +11 -0
  70. package/dist/tools/index.js.map +1 -0
  71. package/dist/tools/listMailboxes.js +50 -0
  72. package/dist/tools/listMailboxes.js.map +1 -0
  73. package/dist/tools/respond.js +34 -0
  74. package/dist/tools/respond.js.map +1 -0
  75. package/dist/tools/searchMessages.js +159 -0
  76. package/dist/tools/searchMessages.js.map +1 -0
  77. package/package.json +60 -4
@@ -0,0 +1,107 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { tildePath } from './doctor.js';
3
+ import { describeError, HelperError } from '../errors.js';
4
+ import { FDA_SETTINGS_URL } from '../fdaPrompt.js';
5
+ import { probeResult } from '../schemas.js';
6
+ import { readBundleName } from '../signing.js';
7
+ import { recordAccessOk } from '../state.js';
8
+ const POLL_MS = 2000;
9
+ const TIMEOUT_MS = 10 * 60 * 1000;
10
+ const ADD_LINE = 'claude mcp add apple-mail -- npx -y @redpop/apple-mail-mcp';
11
+ /** Befehl fuer einen Checkout mit eigenem Helper-Bundle (das npm-Paket gibt es noch nicht). */
12
+ function checkoutLine(bundlePath, entryPath) {
13
+ return `claude mcp add apple-mail-dev -e APPLE_MAIL_HELPER_PATH="${bundlePath}" -- node "${entryPath}"`;
14
+ }
15
+ function fmt(n) {
16
+ return n.toLocaleString('en-US');
17
+ }
18
+ /** `setup` (Spec 12.2): Exit-Code 0 bei Zugriff, sonst 1. */
19
+ export async function runSetup(deps) {
20
+ const { out } = deps;
21
+ const say = (s) => void out.write(s + '\n');
22
+ const probe = async () => probeResult.parse(await deps.runner.run('probe', {}));
23
+ const success = (p) => {
24
+ if (!p.schema) {
25
+ // Datei per open(2) lesbar, SQLite scheitert: weder "readable" noch Zugriff vermerken.
26
+ say('Mail database could not be read (busy or damaged); try again.');
27
+ return 1;
28
+ }
29
+ if (!p.schema.compatible) {
30
+ // Zugriff klappt, aber die Datenbank hat nicht das erwartete Layout: kein Erfolg, nichts vermerken.
31
+ say('This version of Apple Mail is not supported yet. Its Mail database lacks columns this server needs.');
32
+ say('Run `npx -y @redpop/apple-mail-mcp doctor --schema` and attach the output to an issue at https://github.com/redpop/apple-mail-mcp/issues.');
33
+ return 1;
34
+ }
35
+ // helperSha256 bleibt null (Begruendung in src/context.ts); die Version stammt aus dem hello-Handshake.
36
+ recordAccessOk({ helperSha256: null, helperVersion: deps.runner.helperVersion?.() ?? null }, deps.stateDir);
37
+ const c = p.counts;
38
+ say('Full Disk Access is granted. Apple Mail is readable.');
39
+ if (c)
40
+ say(`Found ${fmt(c.mailboxes)} mailboxes and ${fmt(c.uniqueMessages)} messages (${fmt(c.messages)} including copies).`);
41
+ say('');
42
+ say('Add the server to Claude Code with:');
43
+ say(` ${deps.override ? checkoutLine(deps.bundlePath, deps.entryPath ?? fileURLToPath(new URL('../index.js', import.meta.url))) : ADD_LINE}`);
44
+ return 0;
45
+ };
46
+ if (deps.configWarning)
47
+ say(`Warning: ${deps.configWarning}`);
48
+ const name = readBundleName(deps.bundlePath, deps.exec);
49
+ const shown = tildePath(deps.bundlePath, deps.home);
50
+ let p;
51
+ try {
52
+ p = await probe();
53
+ }
54
+ catch (e) {
55
+ say(e instanceof HelperError ? describeError(e.code, e.message) : 'The helper could not be started.');
56
+ return 1;
57
+ }
58
+ if (p.access === 'granted')
59
+ return success(p);
60
+ if (p.access === 'no_mail_data') {
61
+ say('Apple Mail does not appear to be set up on this Mac. Open the Mail app, add an account, then run setup again.');
62
+ return 1;
63
+ }
64
+ if (p.access === 'untrusted_signature') {
65
+ say('The helper reports that its signature is not trusted and refused to read mail. Reinstall the package from the official source.');
66
+ return 1;
67
+ }
68
+ say(`Apple Mail data is protected by macOS. Grant Full Disk Access to "${name}":`);
69
+ say(` 1. A Finder window shows "${name}" and System Settings opens on Full Disk Access.`);
70
+ say(` 2. Drag "${name}" from Finder into the list and switch it on.`);
71
+ say(` (If it is missing from the list: click +, press Cmd-Shift-G and paste ${shown})`);
72
+ for (const args of [[FDA_SETTINGS_URL], ['-R', deps.bundlePath]]) {
73
+ try {
74
+ deps.open('/usr/bin/open', args);
75
+ }
76
+ catch {
77
+ // Fehler von open sind egal; die Schritte oben stehen schon da.
78
+ }
79
+ }
80
+ if (!deps.isTTY) {
81
+ say('');
82
+ say('Not waiting because this is not an interactive terminal. Run setup again after granting access.');
83
+ return 1;
84
+ }
85
+ const start = deps.now();
86
+ while (deps.now() - start < TIMEOUT_MS) {
87
+ out.write(`\rWaiting for Full Disk Access... ${Math.round((deps.now() - start) / 1000)} s`);
88
+ await deps.sleep(POLL_MS);
89
+ try {
90
+ p = await probe();
91
+ }
92
+ catch {
93
+ continue;
94
+ }
95
+ if (p.access !== 'denied')
96
+ break;
97
+ }
98
+ out.write('\n');
99
+ if (p.access === 'granted')
100
+ return success(p);
101
+ if (p.access === 'denied')
102
+ say('Timed out after 10 minutes without Full Disk Access. Run setup again after granting it.');
103
+ else
104
+ say(`Access state changed to ${p.access}. Run setup again.`);
105
+ return 1;
106
+ }
107
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/cli/setup.ts"],"names":[],"mappings":"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"}
package/dist/config.js ADDED
@@ -0,0 +1,74 @@
1
+ class ConfigError extends Error {
2
+ key;
3
+ constructor(key, message) {
4
+ super(message);
5
+ this.key = key;
6
+ }
7
+ }
8
+ // Leerer String (MCPB ohne Eingabe) gilt als nicht gesetzt.
9
+ function raw(env, key) {
10
+ const v = env[key];
11
+ if (v === undefined)
12
+ return undefined;
13
+ const t = v.trim();
14
+ // Claude Desktop laesst ${user_config.x} stehen, wenn ein Feld weder Wert noch Default hat.
15
+ // Ein Platzhalter ist kein Wert: weder Kontoname noch "nicht gesetzt" (das hiesse alle Konten).
16
+ if (/\$\{user_config\.[^}]*\}/.test(t))
17
+ throw new ConfigError(key, `${key} was not replaced by the MCPB host (it still reads ${t}); set a value or reinstall the extension`);
18
+ return t === '' ? undefined : t;
19
+ }
20
+ function bool(env, key, def) {
21
+ const v = raw(env, key);
22
+ if (v === undefined)
23
+ return def;
24
+ const l = v.toLowerCase();
25
+ if (l === 'true' || l === '1')
26
+ return true;
27
+ if (l === 'false' || l === '0')
28
+ return false;
29
+ throw new ConfigError(key, `${key} must be one of true, false, 1, 0`);
30
+ }
31
+ function int(env, key, def, min, max) {
32
+ const v = raw(env, key);
33
+ if (v === undefined)
34
+ return def;
35
+ if (!/^\d+$/.test(v))
36
+ throw new ConfigError(key, `${key} must be an integer between ${min} and ${max}`);
37
+ const n = Number(v);
38
+ if (n < min || n > max)
39
+ throw new ConfigError(key, `${key} must be an integer between ${min} and ${max}`);
40
+ return n;
41
+ }
42
+ function list(env, key) {
43
+ const v = raw(env, key);
44
+ if (v === undefined)
45
+ return [];
46
+ const items = v.split(',').map((s) => s.trim()).filter((s) => s !== '');
47
+ // Nicht leerer Wert ohne Eintraege waere sonst "alle" (fail-open).
48
+ if (items.length === 0)
49
+ throw new ConfigError(key, `${key} contains no entries`);
50
+ return items;
51
+ }
52
+ export function loadConfig(env) {
53
+ try {
54
+ const config = {
55
+ accounts: list(env, 'APPLE_MAIL_ACCOUNTS'),
56
+ includeJunk: bool(env, 'APPLE_MAIL_INCLUDE_JUNK', false),
57
+ includeTrash: bool(env, 'APPLE_MAIL_INCLUDE_TRASH', false),
58
+ excludeMailboxes: list(env, 'APPLE_MAIL_EXCLUDE_MAILBOXES'),
59
+ showSecurityMails: bool(env, 'APPLE_MAIL_SHOW_SECURITY_MAILS', false),
60
+ bodySearch: bool(env, 'APPLE_MAIL_BODY_SEARCH', true),
61
+ maxResults: int(env, 'APPLE_MAIL_MAX_RESULTS', 50, 1, 100),
62
+ maxBodyChars: int(env, 'APPLE_MAIL_MAX_BODY_CHARS', 20000, 1000, 100000),
63
+ helperPath: raw(env, 'APPLE_MAIL_HELPER_PATH') ?? null,
64
+ debug: bool(env, 'APPLE_MAIL_MCP_DEBUG', false),
65
+ };
66
+ return { ok: true, config };
67
+ }
68
+ catch (e) {
69
+ if (e instanceof ConfigError)
70
+ return { ok: false, key: e.key, error: e.message };
71
+ throw e;
72
+ }
73
+ }
74
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import { HelperError } from './errors.js';
2
+ import { defaultOpen, openFdaOnce } from './fdaPrompt.js';
3
+ import { computePolicy } from './policy.js';
4
+ import { listMailboxesResult } from './schemas.js';
5
+ import { readBundleName } from './signing.js';
6
+ import { defaultStateDir, recordAccessOk } from './state.js';
7
+ const POLICY_TTL_MS = 60_000;
8
+ export function createContext(deps) {
9
+ const now = deps.now ?? Date.now;
10
+ const open = deps.open ?? defaultOpen;
11
+ const stateDir = deps.stateDir ?? defaultStateDir();
12
+ const helperName = deps.helperName ?? readBundleName(deps.bundlePath);
13
+ // helperSha256 bleibt null: Die FDA-Freigabe haengt an Bundle-ID und Designated Requirement, nicht am
14
+ // Hash (Bundle-Spike), ein Hash hilft der Diagnose also nicht; Signatur und Bundle-ID prueft checkBundle,
15
+ // doctor zeigt beides. Ihn zu bilden hiesse, bei jedem Start das Bundle zu lesen. Die Version kommt aus hello.
16
+ const onHelperSuccess = () => recordAccessOk({ helperSha256: null, helperVersion: deps.runner.helperVersion?.() ?? null }, stateDir, now);
17
+ const onHelperError = (e) => e instanceof HelperError && e.code === 'FULL_DISK_ACCESS_REQUIRED' ? openFdaOnce(deps.bundlePath, open) : false;
18
+ // Jeder erfolgreiche Helper-Aufruf zaehlt als Zugriff.
19
+ const runner = {
20
+ async run(command, payload) {
21
+ const out = await deps.runner.run(command, payload);
22
+ onHelperSuccess();
23
+ return out;
24
+ },
25
+ };
26
+ const config = () => {
27
+ if (!deps.configResult.ok)
28
+ throw new HelperError('CONFIG_INVALID', deps.configResult.key);
29
+ return deps.configResult.config;
30
+ };
31
+ let cached = null;
32
+ const policy = async (refresh = false) => {
33
+ const cfg = config();
34
+ if (!refresh && cached && now() - cached.at < POLICY_TTL_MS)
35
+ return cached.policy;
36
+ const raw = listMailboxesResult.parse(await runner.run('list-mailboxes', {}));
37
+ const p = computePolicy(cfg, raw);
38
+ cached = { at: now(), policy: p };
39
+ return p;
40
+ };
41
+ return { runner, config, policy, bundlePath: deps.bundlePath, helperName, onHelperError, onHelperSuccess };
42
+ }
43
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAe,MAAM,gBAAgB,CAAA;AAEtE,OAAO,EAAE,aAAa,EAAe,MAAM,aAAa,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAgB5D,MAAM,aAAa,GAAG,MAAM,CAAA;AAE5B,MAAM,UAAU,aAAa,CAAC,IAS7B;IACC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAA;IAChC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,WAAW,CAAA;IACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,eAAe,EAAE,CAAA;IACnD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IAErE,sGAAsG;IACtG,0GAA0G;IAC1G,+GAA+G;IAC/G,MAAM,eAAe,GAAG,GAAS,EAAE,CACjC,cAAc,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,EAAE,IAAI,IAAI,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAA;IAC7G,MAAM,aAAa,GAAG,CAAC,CAAU,EAAW,EAAE,CAC5C,CAAC,YAAY,WAAW,IAAI,CAAC,CAAC,IAAI,KAAK,2BAA2B,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAEjH,uDAAuD;IACvD,MAAM,MAAM,GAAiB;QAC3B,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO;YACxB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YACnD,eAAe,EAAE,CAAA;YACjB,OAAO,GAAG,CAAA;QACZ,CAAC;KACF,CAAA;IAED,MAAM,MAAM,GAAG,GAAW,EAAE;QAC1B,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE;YAAE,MAAM,IAAI,WAAW,CAAC,gBAAgB,EAAE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAA;QACzF,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAA;IACjC,CAAC,CAAA;IAED,IAAI,MAAM,GAA0C,IAAI,CAAA;IACxD,MAAM,MAAM,GAAG,KAAK,EAAE,OAAO,GAAG,KAAK,EAAmB,EAAE;QACxD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAA;QACpB,IAAI,CAAC,OAAO,IAAI,MAAM,IAAI,GAAG,EAAE,GAAG,MAAM,CAAC,EAAE,GAAG,aAAa;YAAE,OAAO,MAAM,CAAC,MAAM,CAAA;QACjF,MAAM,GAAG,GAAG,mBAAmB,CAAC,KAAK,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,CAAA;QAC7E,MAAM,CAAC,GAAG,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;QACjC,MAAM,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAA;QACjC,OAAO,CAAC,CAAA;IACV,CAAC,CAAA;IAED,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,CAAA;AAC5G,CAAC"}
package/dist/cursor.js ADDED
@@ -0,0 +1,39 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { HelperError } from './errors.js';
3
+ function stableJson(v) {
4
+ if (Array.isArray(v))
5
+ return `[${v.map(stableJson).join(',')}]`;
6
+ if (v !== null && typeof v === 'object') {
7
+ const o = v;
8
+ return `{${Object.keys(o).sort().map((k) => `${JSON.stringify(k)}:${stableJson(o[k])}`).join(',')}}`;
9
+ }
10
+ return JSON.stringify(v) ?? 'null';
11
+ }
12
+ /** Kurzer Hash ueber normalisierte Suchparameter (ohne cursor/limit) und den Policy-Fingerabdruck. */
13
+ export function queryHash(params, policyFingerprint) {
14
+ return createHash('sha256').update(stableJson({ params, policy: policyFingerprint })).digest('hex').slice(0, 16);
15
+ }
16
+ export function encodeCursor(k, hash) {
17
+ return Buffer.from(JSON.stringify({ v: 1, d: k.d, g: k.g, h: hash })).toString('base64url');
18
+ }
19
+ function bad() {
20
+ return new HelperError('INVALID_CURSOR', 'The cursor does not belong to this search.');
21
+ }
22
+ export function decodeCursor(s, hash) {
23
+ if (!/^[A-Za-z0-9_-]+$/.test(s))
24
+ throw bad();
25
+ let o;
26
+ try {
27
+ o = JSON.parse(Buffer.from(s, 'base64url').toString('utf8'));
28
+ }
29
+ catch {
30
+ throw bad();
31
+ }
32
+ if (o === null || typeof o !== 'object' || Array.isArray(o))
33
+ throw bad();
34
+ const c = o;
35
+ if (c.v !== 1 || !Number.isSafeInteger(c.d) || !Number.isSafeInteger(c.g) || typeof c.h !== 'string' || c.h !== hash)
36
+ throw bad();
37
+ return { d: c.d, g: c.g };
38
+ }
39
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../src/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAEzC,SAAS,UAAU,CAAC,CAAU;IAC5B,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAA;IAC/D,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,CAA4B,CAAA;QACtC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAA;IACtG,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,MAAM,CAAA;AACpC,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,SAAS,CAAC,MAA+B,EAAE,iBAAyB;IAClF,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AAClH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,CAA2B,EAAE,IAAY;IACpE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;AAC7F,CAAC;AAED,SAAS,GAAG;IACV,OAAO,IAAI,WAAW,CAAC,gBAAgB,EAAE,4CAA4C,CAAC,CAAA;AACxF,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,CAAS,EAAE,IAAY;IAClD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,MAAM,GAAG,EAAE,CAAA;IAC5C,IAAI,CAAU,CAAA;IACd,IAAI,CAAC;QACH,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAA;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,GAAG,EAAE,CAAA;IACb,CAAC;IACD,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,MAAM,GAAG,EAAE,CAAA;IACxE,MAAM,CAAC,GAAG,CAA4B,CAAA;IACtC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI;QAAE,MAAM,GAAG,EAAE,CAAA;IACjI,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAW,EAAE,CAAC,EAAE,CAAC,CAAC,CAAW,EAAE,CAAA;AAC/C,CAAC"}
package/dist/dates.js ADDED
@@ -0,0 +1,44 @@
1
+ import { HelperError } from './errors.js';
2
+ const DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
3
+ const DATE_TIME = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?(Z|[+-]\d{2}:\d{2})?$/;
4
+ function invalid(s, why) {
5
+ return new HelperError('INVALID_INPUT', `Invalid date "${s}": ${why}. Use YYYY-MM-DD or an ISO 8601 date-time with offset such as 2026-10-01T10:00:00+02:00.`);
6
+ }
7
+ // Prueft, dass Jahr/Monat/Tag einen echten Kalendertag ergeben (kein Ueberlauf wie 30. Februar).
8
+ function validDay(y, m, d) {
9
+ const t = new Date(Date.UTC(y, m - 1, d));
10
+ return t.getUTCFullYear() === y && t.getUTCMonth() === m - 1 && t.getUTCDate() === d;
11
+ }
12
+ /** Eingabe fuer since/before. Datum = lokale Mitternacht, Zeitpunkt nur mit Offset. Liefert Unix-Sekunden. */
13
+ export function parseDateInput(s) {
14
+ const d = DATE_ONLY.exec(s);
15
+ if (d) {
16
+ const [y, m, day] = [Number(d[1]), Number(d[2]), Number(d[3])];
17
+ if (!validDay(y, m, day))
18
+ throw invalid(s, 'no such calendar day');
19
+ return Math.floor(new Date(y, m - 1, day).getTime() / 1000);
20
+ }
21
+ const t = DATE_TIME.exec(s);
22
+ if (!t)
23
+ throw invalid(s, 'unrecognised format');
24
+ if (t[7] === undefined)
25
+ throw invalid(s, 'a time needs an explicit offset (Z or +HH:MM), no timezone is assumed');
26
+ const hh = Number(t[4]), mm = Number(t[5]), ss = Number(t[6] ?? 0);
27
+ if (!validDay(Number(t[1]), Number(t[2]), Number(t[3])) || hh > 23 || mm > 59 || ss > 59)
28
+ throw invalid(s, 'no such time');
29
+ const ms = Date.parse(s);
30
+ if (Number.isNaN(ms))
31
+ throw invalid(s, 'unrecognised format');
32
+ return Math.floor(ms / 1000);
33
+ }
34
+ const p2 = (n) => String(n).padStart(2, '0');
35
+ /** ISO 8601 mit lokalem Offset (Prozess-TZ), ohne Millisekunden. */
36
+ export function formatLocal(unix) {
37
+ const d = new Date(Math.floor(unix) * 1000);
38
+ const off = -d.getTimezoneOffset();
39
+ const sign = off < 0 ? '-' : '+';
40
+ const a = Math.abs(off);
41
+ return (`${d.getFullYear()}-${p2(d.getMonth() + 1)}-${p2(d.getDate())}T${p2(d.getHours())}:${p2(d.getMinutes())}:${p2(d.getSeconds())}` +
42
+ `${sign}${p2(Math.floor(a / 60))}:${p2(a % 60)}`);
43
+ }
44
+ //# sourceMappingURL=dates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dates.js","sourceRoot":"","sources":["../src/dates.ts"],"names":[],"mappings":"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"}
package/dist/errors.js ADDED
@@ -0,0 +1,56 @@
1
+ /** Fehler mit einem Code, den der Helper oder Node geliefert hat. */
2
+ export class HelperError extends Error {
3
+ code;
4
+ constructor(code, message) {
5
+ super(message);
6
+ this.code = code;
7
+ this.name = 'HelperError';
8
+ }
9
+ }
10
+ /**
11
+ * Meldung fuer FULL_DISK_ACCESS_REQUIRED (Spec 10). `finder` sagt, ob gerade ein Finder-Fenster mit dem
12
+ * Helper geoeffnet wurde (nur einmal pro Prozess, Spec 12.1); nur dann behauptet der Text das.
13
+ */
14
+ function fdaText(pathSentence, finder, name = 'Mail MCP Helper') {
15
+ const steps = finder === 'opened'
16
+ ? 'A Finder window showing it has been opened. The user needs to open System Settings > Privacy & Security > Full Disk Access, ' +
17
+ `drag "${name}" from that Finder window into the list, and switch it on. `
18
+ : (finder === 'earlier' ? 'A Finder window showing it was opened on the first attempt in this session and may have been closed since. ' : '') +
19
+ 'The user needs to open System Settings > Privacy & Security > Full Disk Access, ' +
20
+ `drag "${name}" from Finder into the list, and switch it on. `;
21
+ return (`Apple Mail data is protected by macOS. Full Disk Access has to be granted to the small helper app "${name}", not to the MCP client. ` +
22
+ steps +
23
+ pathSentence +
24
+ `If "${name}" is already listed and switched on, the entry is stale: remove it with −, then add it again. ` +
25
+ 'Retry afterwards; no restart is needed. In Terminal, `npx -y @redpop/apple-mail-mcp setup` walks through the same steps.');
26
+ }
27
+ // Texte gehen an das Modell, das den Tool-Aufruf gemacht hat: auf Englisch, mit Klickpfad, wo nur ein Mensch handeln kann.
28
+ // `openedNow`: hat dieser Aufruf Einstellungen und Finder geoeffnet (openFdaOnce)?
29
+ export function fdaMessage(bundlePath, openedNow, name = 'Mail MCP Helper') {
30
+ return fdaText(`Alternatively click +, press Cmd-Shift-G and paste \`${bundlePath}\`. `, openedNow ? 'opened' : 'earlier', name);
31
+ }
32
+ const MESSAGES = {
33
+ FULL_DISK_ACCESS_REQUIRED: fdaText('Alternatively click + and add the "Mail MCP Helper" app from the installed package. ', 'none'),
34
+ MAIL_DATA_NOT_FOUND: 'Apple Mail does not appear to be set up on this Mac. The user needs to open the Mail app and add an account, then retry.',
35
+ SCHEMA_UNSUPPORTED: 'This macOS or Mail version is not supported: the Mail database does not have the expected layout. Ask the user to run `npx -y @redpop/apple-mail-mcp doctor` and include its output in a bug report.',
36
+ DATABASE_BUSY: 'Mail is writing to its database right now. Wait a moment and retry the same request.',
37
+ DATABASE_ERROR: 'The Mail database reported an unexpected error. Retry once; if it persists, ask the user to run `npx -y @redpop/apple-mail-mcp doctor`.',
38
+ MESSAGE_NOT_FOUND: 'No such message. Message IDs come from search_messages and become invalid when Mail rebuilds its index; search again to get fresh IDs.',
39
+ MAILBOX_NOT_FOUND: 'No such mailbox, or it is not visible under the current settings. Call list_mailboxes to get valid mailbox IDs.',
40
+ INVALID_CURSOR: 'The cursor belongs to a different search or is no longer valid. Repeat the search without a cursor.',
41
+ INVALID_INPUT: 'The request was rejected as invalid.',
42
+ CONFIG_INVALID: 'The server configuration is invalid, so no mail was accessed. The user needs to correct the named setting in the MCP server configuration and restart the client.',
43
+ HELPER_MISSING: 'The Mail MCP Helper app is missing from the installation. Reinstalling the package should restore it.',
44
+ HELPER_CRASHED: 'The Mail helper stopped unexpectedly. Retrying the request will start it again.',
45
+ HELPER_TIMEOUT: 'The Mail helper did not answer in time. Retry the request; a narrower search may be faster.',
46
+ HELPER_VERSION_MISMATCH: 'The installed Mail helper does not match this server version, which points to a damaged installation. The user should restart the MCP client or reinstall the package.',
47
+ HELPER_SIGNATURE_INVALID: 'The Mail helper is not signed correctly and was not started. The user should reinstall the package from the official source.',
48
+ HELPER_UNTRUSTED_SIGNATURE: 'The Mail helper reports that its own signature is not trusted and refused to read mail. The user should reinstall the package from the official source.',
49
+ };
50
+ const FALLBACK = 'An unexpected error occurred while accessing Apple Mail.';
51
+ /** Uebersetzt einen Fehlercode in Klartext fuer den Client. Nie ein Stacktrace. */
52
+ export function describeError(code, detail) {
53
+ const base = MESSAGES[code] ?? FALLBACK;
54
+ return detail !== undefined && detail.length > 0 ? `${base} (Details: ${detail})` : base;
55
+ }
56
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,MAAM,OAAO,WAAY,SAAQ,KAAK;IAEzB,IAAI;IADf,YACW,IAAY,EACrB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAA;oBAHL,IAAI;QAIb,IAAI,CAAC,IAAI,GAAG,aAAa,CAAA;IAC3B,CAAC;CACF;AAED;;;GAGG;AACH,SAAS,OAAO,CAAC,YAAoB,EAAE,MAAqC,EAAE,IAAI,GAAG,iBAAiB;IACpG,MAAM,KAAK,GACT,MAAM,KAAK,QAAQ;QACjB,CAAC,CAAC,8HAA8H;YAC9H,SAAS,IAAI,6DAA6D;QAC5E,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,6GAA6G,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3I,kFAAkF;YAClF,SAAS,IAAI,iDAAiD,CAAA;IACpE,OAAO,CACL,sGAAsG,IAAI,4BAA4B;QACtI,KAAK;QACL,YAAY;QACZ,OAAO,IAAI,gGAAgG;QAC3G,0HAA0H,CAC3H,CAAA;AACH,CAAC;AAED,2HAA2H;AAC3H,mFAAmF;AACnF,MAAM,UAAU,UAAU,CAAC,UAAkB,EAAE,SAAkB,EAAE,IAAI,GAAG,iBAAiB;IACzF,OAAO,OAAO,CAAC,wDAAwD,UAAU,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;AAClI,CAAC;AAED,MAAM,QAAQ,GAA2B;IACvC,yBAAyB,EAAE,OAAO,CAAC,sFAAsF,EAAE,MAAM,CAAC;IAClI,mBAAmB,EACjB,0HAA0H;IAC5H,kBAAkB,EAChB,sMAAsM;IACxM,aAAa,EAAE,sFAAsF;IACrG,cAAc,EACZ,yIAAyI;IAC3I,iBAAiB,EACf,wIAAwI;IAC1I,iBAAiB,EAAE,iHAAiH;IACpI,cAAc,EAAE,qGAAqG;IACrH,aAAa,EAAE,sCAAsC;IACrD,cAAc,EACZ,mKAAmK;IACrK,cAAc,EACZ,uGAAuG;IACzG,cAAc,EAAE,iFAAiF;IACjG,cAAc,EAAE,6FAA6F;IAC7G,uBAAuB,EACrB,wKAAwK;IAC1K,wBAAwB,EACtB,8HAA8H;IAChI,0BAA0B,EACxB,yJAAyJ;CAC5J,CAAA;AAED,MAAM,QAAQ,GAAG,0DAA0D,CAAA;AAE3E,mFAAmF;AACnF,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,MAAe;IACzD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAA;IACvC,OAAO,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,cAAc,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA;AAC1F,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { execFile } from 'node:child_process';
2
+ export const FDA_SETTINGS_URL = 'x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles';
3
+ // execFile mit Argumentliste, nie eine Shell; Fehler von open sind egal.
4
+ export const defaultOpen = (file, args) => {
5
+ execFile(file, args, () => { });
6
+ };
7
+ let done = false;
8
+ export function resetFdaPromptForTests() {
9
+ done = false;
10
+ }
11
+ /**
12
+ * Oeffnet einmal pro Prozess die FDA-Seite und zeigt das Bundle im Finder (Spec 12.1). Wirft nie.
13
+ * Liefert true, wenn dieser Aufruf geoeffnet hat, damit die Meldung nur dann davon spricht.
14
+ */
15
+ export function openFdaOnce(bundlePath, open = defaultOpen) {
16
+ if (done)
17
+ return false;
18
+ done = true;
19
+ for (const args of [[FDA_SETTINGS_URL], ['-R', bundlePath]]) {
20
+ try {
21
+ open('/usr/bin/open', args);
22
+ }
23
+ catch {
24
+ // Hilfsfunktion: die Fehlermeldung an das Modell enthaelt die Schritte ohnehin.
25
+ }
26
+ }
27
+ return true;
28
+ }
29
+ //# sourceMappingURL=fdaPrompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fdaPrompt.js","sourceRoot":"","sources":["../src/fdaPrompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAE7C,MAAM,CAAC,MAAM,gBAAgB,GAAG,0EAA0E,CAAA;AAI1G,yEAAyE;AACzE,MAAM,CAAC,MAAM,WAAW,GAAW,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;IAChD,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;AAChC,CAAC,CAAA;AAED,IAAI,IAAI,GAAG,KAAK,CAAA;AAEhB,MAAM,UAAU,sBAAsB;IACpC,IAAI,GAAG,KAAK,CAAA;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,UAAkB,EAAE,IAAI,GAAW,WAAW;IACxE,IAAI,IAAI;QAAE,OAAO,KAAK,CAAA;IACtB,IAAI,GAAG,IAAI,CAAA;IACX,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC;QAC5D,IAAI,CAAC;YACH,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,CAAA;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,gFAAgF;QAClF,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
package/dist/fold.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Faltet Text fuer Vergleiche: Kleinschreibung, NFC, ß zu ss.
3
+ * Muss sich exakt wie die Swift-Funktion `fold` verhalten.
4
+ */
5
+ export function fold(s) {
6
+ return s.toLowerCase().normalize('NFC').replaceAll('ß', 'ss');
7
+ }
8
+ //# sourceMappingURL=fold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fold.js","sourceRoot":"","sources":["../src/fold.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,IAAI,CAAC,CAAS;IAC5B,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;AAC/D,CAAC"}
package/dist/helper.js ADDED
@@ -0,0 +1,45 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { checkBundle } from './signing.js';
3
+ import { createStdioRunner } from './stdioRunner.js';
4
+ /** Wo das Bundle im ausgelieferten Paket liegt (gleich fuer src/ und dist/). */
5
+ export function packagedHelperBundle() {
6
+ return fileURLToPath(new URL('../bin/Mail MCP Helper.app', import.meta.url));
7
+ }
8
+ export function resolveHelperBundle(config) {
9
+ return config.helperPath ?? packagedHelperBundle();
10
+ }
11
+ export function helperExecutable(bundle) {
12
+ return `${bundle}/Contents/MacOS/mail-helper`;
13
+ }
14
+ /**
15
+ * Ein Runner pro Serverprozess; das Signaturgatter laeuft einmal vor dem ersten Start, das Ergebnis ist gecacht.
16
+ * Auch ein Fehlschlag bleibt bis zum Prozessende stehen: nach einer Neuinstallation muss der Client neu starten
17
+ * (docs/troubleshooting.md).
18
+ */
19
+ export function createHelperRunner(config, deps = {}) {
20
+ const bundlePath = resolveHelperBundle(config);
21
+ const options = { debug: config.debug === true };
22
+ const inner = (deps.makeRunner ?? ((exe, opts) => createStdioRunner(exe, undefined, opts)))(helperExecutable(bundlePath), options);
23
+ const check = deps.check ?? checkBundle;
24
+ let gate;
25
+ return {
26
+ bundlePath,
27
+ run(command, payload) {
28
+ if (gate === undefined) {
29
+ try {
30
+ check(bundlePath, { override: config.helperPath !== null });
31
+ gate = true;
32
+ }
33
+ catch (e) {
34
+ gate = e;
35
+ }
36
+ }
37
+ if (gate !== true)
38
+ return Promise.reject(gate);
39
+ return inner.run(command, payload);
40
+ },
41
+ stop: () => inner.stop(),
42
+ helperVersion: () => inner.helperVersion(),
43
+ };
44
+ }
45
+ //# sourceMappingURL=helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,iBAAiB,EAA6C,MAAM,kBAAkB,CAAA;AAQ/F,gFAAgF;AAChF,MAAM,UAAU,oBAAoB;IAClC,OAAO,aAAa,CAAC,IAAI,GAAG,CAAC,4BAA4B,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAC9E,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,OAAO,MAAM,CAAC,UAAU,IAAI,oBAAoB,EAAE,CAAA;AACpD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,OAAO,GAAG,MAAM,6BAA6B,CAAA;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAc,EACd,IAAI,GAGA,EAAE;IAEN,MAAM,UAAU,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAA;IAC9C,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,KAAK,IAAI,EAAE,CAAA;IAChD,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,GAAG,EAAE,IAAkB,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAA;IAChJ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,WAAW,CAAA;IACvC,IAAI,IAAa,CAAA;IACjB,OAAO;QACL,UAAU;QACV,GAAG,CAAC,OAAO,EAAE,OAAO;YAClB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,IAAI,CAAC;oBACH,KAAK,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC,CAAA;oBAC3D,IAAI,GAAG,IAAI,CAAA;gBACb,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACX,IAAI,GAAG,CAAC,CAAA;gBACV,CAAC;YACH,CAAC;YACD,IAAI,IAAI,KAAK,IAAI;gBAAE,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YAC9C,OAAO,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACpC,CAAC;QACD,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE;QACxB,aAAa,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,aAAa,EAAE;KAC3C,CAAA;AACH,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
3
+ import { loadConfig } from './config.js';
4
+ import { createContext } from './context.js';
5
+ import { createHelperRunner } from './helper.js';
6
+ import { createServer } from './server.js';
7
+ // Auf stdout darf im Servermodus nur das Protokoll stehen; setup/doctor sind eigene Kommandozeilenmodi.
8
+ const arg = process.argv[2];
9
+ if (arg === 'setup' || arg === 'doctor') {
10
+ const { runCli } = await import('./cli/main.js');
11
+ process.exit(await runCli(arg, process.argv.slice(3)));
12
+ }
13
+ const configResult = loadConfig(process.env);
14
+ let runner;
15
+ if (configResult.ok) {
16
+ runner = createHelperRunner(configResult.config);
17
+ }
18
+ else {
19
+ // Ungueltige Konfiguration: Server startet trotzdem, jeder Aufruf scheitert vorher mit CONFIG_INVALID.
20
+ runner = { bundlePath: '', run: () => Promise.reject(new Error('unreachable')), stop: () => { } };
21
+ }
22
+ const ctx = createContext({ runner, configResult, bundlePath: runner.bundlePath });
23
+ const server = createServer(ctx);
24
+ // Bei einem Signal raeumt Node die Pipes nicht auf; ohne stop() bliebe der Helper verwaist.
25
+ for (const signal of ['SIGINT', 'SIGTERM']) {
26
+ process.on(signal, () => {
27
+ runner.stop();
28
+ process.exit(0);
29
+ });
30
+ }
31
+ // EOF auf stdin schliesst den Transport; dann darf der Helper-Kindprozess nicht ueberleben.
32
+ server.server.onclose = () => {
33
+ runner.stop();
34
+ process.exit(0);
35
+ };
36
+ await server.connect(new StdioServerTransport());
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,kBAAkB,EAAqB,MAAM,aAAa,CAAA;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,wGAAwG;AACxG,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAC3B,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;IACxC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAA;IAChD,OAAO,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACxD,CAAC;AAED,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;AAC5C,IAAI,MAA2D,CAAA;AAC/D,IAAI,YAAY,CAAC,EAAE,EAAE,CAAC;IACpB,MAAM,GAAG,kBAAkB,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;AAClD,CAAC;KAAM,CAAC;IACN,uGAAuG;IACvG,MAAM,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAA;AAClG,CAAC;AAED,MAAM,GAAG,GAAG,aAAa,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;AAClF,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAA;AAEhC,4FAA4F;AAC5F,KAAK,MAAM,MAAM,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAU,EAAE,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;QACtB,MAAM,CAAC,IAAI,EAAE,CAAA;QACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,4FAA4F;AAC5F,MAAM,CAAC,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE;IAC3B,MAAM,CAAC,IAAI,EAAE,CAAA;IACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAA;AAED,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAA"}
package/dist/links.js ADDED
@@ -0,0 +1,9 @@
1
+ /** message://-Link zur Message-ID (mit spitzen Klammern, percent-kodiert); null ohne Header. */
2
+ export function messageLink(header) {
3
+ const t = header?.trim() ?? '';
4
+ if (t === '')
5
+ return null;
6
+ const id = t.startsWith('<') && t.endsWith('>') ? t : `<${t}>`;
7
+ return `message://${encodeURIComponent(id)}`;
8
+ }
9
+ //# sourceMappingURL=links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"links.js","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,MAAM,UAAU,WAAW,CAAC,MAAqB;IAC/C,MAAM,CAAC,GAAG,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IAC9B,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,IAAI,CAAA;IACzB,MAAM,EAAE,GAAG,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAA;IAC9D,OAAO,aAAa,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAA;AAC9C,CAAC"}