@bobfrankston/rmfmail 1.2.129 → 1.2.130

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.
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * rmfmail-mcp — Model Context Protocol server over the running rmfmail
4
+ * daemon, so an AI (Claude Code, Claude Desktop, anything MCP-speaking) can
5
+ * process mail on the user's behalf.
6
+ *
7
+ * claude mcp add rmfmail -- rmfmail-mcp
8
+ *
9
+ * Transport: MCP stdio (newline-delimited JSON-RPC 2.0). Hand-rolled — the
10
+ * protocol subset needed (initialize / tools/list / tools/call) is ~100
11
+ * lines and not worth an SDK dependency.
12
+ *
13
+ * Backend: the daemon's /rpc endpoint, discovered via ~/.rmfmail/agent.json
14
+ * (written each boot, removed on shutdown; a dead pid means stale). Scope:
15
+ * the TRIAGE token by default — reads plus flag/mark-read. Set
16
+ * RMFMAIL_MCP_FULL=1 to use the full token, which unlocks move_message and
17
+ * create_draft. There is deliberately NO send tool at either scope: AI
18
+ * drafts, the human sends (standing policy).
19
+ */
20
+ import fs from "node:fs";
21
+ import path from "node:path";
22
+ import readline from "node:readline";
23
+ const CONFIG_DIR = path.join(process.env.USERPROFILE || process.env.HOME || ".", ".rmfmail");
24
+ const AGENT_JSON = path.join(CONFIG_DIR, "agent.json");
25
+ const FULL = process.env.RMFMAIL_MCP_FULL === "1";
26
+ function loadAgentInfo() {
27
+ let raw;
28
+ try {
29
+ raw = fs.readFileSync(AGENT_JSON, "utf-8");
30
+ }
31
+ catch {
32
+ throw new Error(`rmfmail daemon not running (no ${AGENT_JSON}) — start it with: rmfmail`);
33
+ }
34
+ const info = JSON.parse(raw);
35
+ try {
36
+ process.kill(info.pid, 0);
37
+ }
38
+ catch {
39
+ throw new Error(`agent.json is stale (pid ${info.pid} not running) — start rmfmail`);
40
+ }
41
+ return info;
42
+ }
43
+ let rpcSeq = 0;
44
+ async function rpc(action, params = {}) {
45
+ const info = loadAgentInfo();
46
+ const token = FULL ? info.tokens.full : info.tokens.triage;
47
+ const r = await fetch(`${info.rpcUrl}?t=${token}`, {
48
+ method: "POST",
49
+ headers: { "content-type": "application/json" },
50
+ body: JSON.stringify({ _action: action, _cbid: String(++rpcSeq), ...params }),
51
+ });
52
+ if (!r.ok)
53
+ throw new Error(`daemon rpc HTTP ${r.status}`);
54
+ const res = await r.json();
55
+ if (res.error)
56
+ throw new Error(res.error);
57
+ return res.result;
58
+ }
59
+ // ── Tools ──
60
+ /** Trim a message object down to what an LLM needs — full envelopes carry
61
+ * parsed MIME internals that waste context. */
62
+ function slimMessage(m, includeBody) {
63
+ const addr = (a) => Array.isArray(a) ? a.map((x) => x.name ? `${x.name} <${x.address}>` : x.address).join(", ") : a;
64
+ const out = {
65
+ accountId: m.accountId, uid: m.uid, folderId: m.folderId,
66
+ date: m.date, from: addr(m.from), to: addr(m.to), cc: addr(m.cc) || undefined,
67
+ subject: m.subject, seen: m.seen, flagged: m.flagged,
68
+ snippet: m.preview || undefined,
69
+ };
70
+ if (includeBody) {
71
+ out.bodyText = m.bodyText || undefined;
72
+ // Fall back to a crude strip when only HTML exists.
73
+ if (!out.bodyText && m.bodyHtml)
74
+ out.bodyText = String(m.bodyHtml).replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim().slice(0, 20000);
75
+ if (m.bodyError)
76
+ out.bodyError = m.bodyError;
77
+ if (Array.isArray(m.attachments) && m.attachments.length) {
78
+ out.attachments = m.attachments.map((a) => ({ id: a.id, filename: a.filename, mimeType: a.mimeType, size: a.size }));
79
+ }
80
+ }
81
+ return out;
82
+ }
83
+ const str = (description) => ({ type: "string", description });
84
+ const num = (description) => ({ type: "number", description });
85
+ const bool = (description) => ({ type: "boolean", description });
86
+ const obj = (props, required) => ({ type: "object", properties: props, required });
87
+ const TOOLS = {
88
+ list_accounts: {
89
+ description: "List configured mail accounts (id, name, email).",
90
+ inputSchema: obj({}, []),
91
+ run: async () => (await rpc("getAccounts")).map((a) => ({ id: a.id, name: a.name, email: a.email })),
92
+ },
93
+ list_folders: {
94
+ description: "List folders for an account, with unread/total counts.",
95
+ inputSchema: obj({ accountId: str("Account id from list_accounts") }, ["accountId"]),
96
+ run: async (a) => (await rpc("getFolders", { accountId: a.accountId })).map((f) => ({ id: f.id, path: f.path, specialUse: f.specialUse || undefined, unread: f.unreadCount, total: f.totalCount })),
97
+ },
98
+ list_messages: {
99
+ description: "List messages in a folder, newest first (paged, 50/page). Returns envelopes with snippets, no bodies.",
100
+ inputSchema: obj({ accountId: str("Account id"), folderId: num("Folder id from list_folders"), page: num("1-based page, default 1") }, ["accountId", "folderId"]),
101
+ run: async (a) => {
102
+ const r = await rpc("getMessages", { accountId: a.accountId, folderId: a.folderId, page: a.page || 1, pageSize: 50 });
103
+ return { total: r.total, messages: (r.messages || []).map((m) => slimMessage({ ...m, accountId: a.accountId, folderId: a.folderId }, false)) };
104
+ },
105
+ },
106
+ search_messages: {
107
+ description: "Full-text search across all local mail (FTS). Query supports plain words; results span accounts/folders.",
108
+ inputSchema: obj({ query: str("Search text"), includeTrashSpam: bool("Also search Trash/Spam (default false)") }, ["query"]),
109
+ run: async (a) => {
110
+ const r = await rpc("searchMessages", { query: (a.query || "").trim(), page: 1, pageSize: 50, scope: "all", accountId: "", folderId: 0, includeTrashSpam: !!a.includeTrashSpam });
111
+ return { total: r.total, messages: (r.messages || []).map((m) => slimMessage(m, false)) };
112
+ },
113
+ },
114
+ get_message: {
115
+ description: "Fetch one message including its text body and attachment list.",
116
+ inputSchema: obj({ accountId: str("Account id"), uid: num("Message uid"), folderId: num("Folder id the uid lives in") }, ["accountId", "uid", "folderId"]),
117
+ run: async (a) => slimMessage(await rpc("getMessage", { accountId: a.accountId, uid: a.uid, folderId: a.folderId, allowRemote: false }), true),
118
+ },
119
+ flag_message: {
120
+ description: "Set or clear flags on a message. flags is the COMPLETE new flag list, e.g. [\"\\\\Seen\", \"\\\\Flagged\"] or [\"\\\\Seen\"] to unflag.",
121
+ inputSchema: obj({ accountId: str("Account id"), uid: num("Message uid"), folderId: num("Folder id"), flags: { type: "array", items: { type: "string" }, description: "Complete flag list" } }, ["accountId", "uid", "folderId", "flags"]),
122
+ run: async (a) => rpc("updateFlags", { accountId: a.accountId, uid: a.uid, folderId: a.folderId, flags: a.flags }),
123
+ },
124
+ move_message: {
125
+ description: "Move a message to another folder (same account). FULL scope only.",
126
+ inputSchema: obj({ accountId: str("Account id"), uid: num("Message uid"), folderId: num("Source folder id"), targetFolderId: num("Destination folder id") }, ["accountId", "uid", "folderId", "targetFolderId"]),
127
+ fullOnly: true,
128
+ run: async (a) => rpc("moveMessages", { accountId: a.accountId, uids: [a.uid], folderIds: [a.folderId], targetFolderId: a.targetFolderId }),
129
+ },
130
+ create_draft: {
131
+ description: "Create a draft (NOT sent — drafts are for the human to review and send). FULL scope only.",
132
+ inputSchema: obj({ accountId: str("Account id to draft from"), to: str("To recipients, comma-separated"), cc: str("Cc recipients (optional)"), subject: str("Subject"), bodyText: str("Plain-text body") }, ["accountId", "to", "subject", "bodyText"]),
133
+ fullOnly: true,
134
+ run: async (a) => rpc("saveDraft", { accountId: a.accountId, subject: a.subject, bodyHtml: String(a.bodyText).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/\n/g, "<br>"), bodyText: a.bodyText, to: a.to, cc: a.cc || "" }),
135
+ },
136
+ };
137
+ // ── MCP stdio plumbing (JSON-RPC 2.0, one message per line) ──
138
+ const out = (msg) => { process.stdout.write(JSON.stringify(msg) + "\n"); };
139
+ function toolList() {
140
+ return Object.entries(TOOLS)
141
+ .filter(([, t]) => FULL || !t.fullOnly)
142
+ .map(([name, t]) => ({ name, description: t.description, inputSchema: t.inputSchema }));
143
+ }
144
+ const rl = readline.createInterface({ input: process.stdin, terminal: false });
145
+ rl.on("line", (line) => { void handle(line); });
146
+ async function handle(line) {
147
+ if (!line.trim())
148
+ return;
149
+ let req;
150
+ try {
151
+ req = JSON.parse(line);
152
+ }
153
+ catch {
154
+ return;
155
+ }
156
+ const { id, method, params } = req;
157
+ const reply = (result) => out({ jsonrpc: "2.0", id, result });
158
+ const fail = (code, message) => out({ jsonrpc: "2.0", id, error: { code, message } });
159
+ try {
160
+ switch (method) {
161
+ case "initialize":
162
+ reply({
163
+ protocolVersion: params?.protocolVersion || "2024-11-05",
164
+ capabilities: { tools: {} },
165
+ serverInfo: { name: "rmfmail", version: "1.0.0" },
166
+ instructions: `Email triage over the local rmfmail daemon (scope: ${FULL ? "full — includes move + draft creation" : "triage — reads and flags only"}). There is no send tool: draft, never send.`,
167
+ });
168
+ return;
169
+ case "notifications/initialized":
170
+ case "notifications/cancelled":
171
+ return; // notifications get no response
172
+ case "tools/list":
173
+ reply({ tools: toolList() });
174
+ return;
175
+ case "tools/call": {
176
+ const tool = TOOLS[params?.name];
177
+ if (!tool || (tool.fullOnly && !FULL)) {
178
+ fail(-32602, `unknown tool: ${params?.name}`);
179
+ return;
180
+ }
181
+ try {
182
+ const result = await tool.run(params?.arguments || {});
183
+ reply({ content: [{ type: "text", text: JSON.stringify(result, null, 1) }] });
184
+ }
185
+ catch (e) {
186
+ reply({ content: [{ type: "text", text: `Error: ${e?.message || e}` }], isError: true });
187
+ }
188
+ return;
189
+ }
190
+ case "ping":
191
+ reply({});
192
+ return;
193
+ default:
194
+ if (id !== undefined)
195
+ fail(-32601, `method not found: ${method}`);
196
+ }
197
+ }
198
+ catch (e) {
199
+ if (id !== undefined)
200
+ fail(-32603, e?.message || String(e));
201
+ }
202
+ }
203
+ //# sourceMappingURL=mcp-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["mcp-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * rmfmail-mcp — Model Context Protocol server over the running rmfmail
4
+ * daemon, so an AI (Claude Code, Claude Desktop, anything MCP-speaking) can
5
+ * process mail on the user's behalf.
6
+ *
7
+ * claude mcp add rmfmail -- rmfmail-mcp
8
+ *
9
+ * Transport: MCP stdio (newline-delimited JSON-RPC 2.0). Hand-rolled — the
10
+ * protocol subset needed (initialize / tools/list / tools/call) is ~100
11
+ * lines and not worth an SDK dependency.
12
+ *
13
+ * Backend: the daemon's /rpc endpoint, discovered via ~/.rmfmail/agent.json
14
+ * (written each boot, removed on shutdown; a dead pid means stale). Scope:
15
+ * the TRIAGE token by default — reads plus flag/mark-read. Set
16
+ * RMFMAIL_MCP_FULL=1 to use the full token, which unlocks move_message and
17
+ * create_draft. There is deliberately NO send tool at either scope: AI
18
+ * drafts, the human sends (standing policy).
19
+ */
20
+
21
+ import fs from "node:fs";
22
+ import path from "node:path";
23
+ import readline from "node:readline";
24
+
25
+ const CONFIG_DIR = path.join(process.env.USERPROFILE || process.env.HOME || ".", ".rmfmail");
26
+ const AGENT_JSON = path.join(CONFIG_DIR, "agent.json");
27
+ const FULL = process.env.RMFMAIL_MCP_FULL === "1";
28
+
29
+ interface AgentInfo { rpcUrl: string; tokens: { triage: string; full: string }; pid: number; version: string }
30
+
31
+ function loadAgentInfo(): AgentInfo {
32
+ let raw: string;
33
+ try { raw = fs.readFileSync(AGENT_JSON, "utf-8"); }
34
+ catch { throw new Error(`rmfmail daemon not running (no ${AGENT_JSON}) — start it with: rmfmail`); }
35
+ const info = JSON.parse(raw) as AgentInfo;
36
+ try { process.kill(info.pid, 0); }
37
+ catch { throw new Error(`agent.json is stale (pid ${info.pid} not running) — start rmfmail`); }
38
+ return info;
39
+ }
40
+
41
+ let rpcSeq = 0;
42
+ async function rpc(action: string, params: Record<string, any> = {}): Promise<any> {
43
+ const info = loadAgentInfo();
44
+ const token = FULL ? info.tokens.full : info.tokens.triage;
45
+ const r = await fetch(`${info.rpcUrl}?t=${token}`, {
46
+ method: "POST",
47
+ headers: { "content-type": "application/json" },
48
+ body: JSON.stringify({ _action: action, _cbid: String(++rpcSeq), ...params }),
49
+ });
50
+ if (!r.ok) throw new Error(`daemon rpc HTTP ${r.status}`);
51
+ const res: any = await r.json();
52
+ if (res.error) throw new Error(res.error);
53
+ return res.result;
54
+ }
55
+
56
+ // ── Tools ──
57
+
58
+ /** Trim a message object down to what an LLM needs — full envelopes carry
59
+ * parsed MIME internals that waste context. */
60
+ function slimMessage(m: any, includeBody: boolean): any {
61
+ const addr = (a: any) => Array.isArray(a) ? a.map((x: any) => x.name ? `${x.name} <${x.address}>` : x.address).join(", ") : a;
62
+ const out: any = {
63
+ accountId: m.accountId, uid: m.uid, folderId: m.folderId,
64
+ date: m.date, from: addr(m.from), to: addr(m.to), cc: addr(m.cc) || undefined,
65
+ subject: m.subject, seen: m.seen, flagged: m.flagged,
66
+ snippet: m.preview || undefined,
67
+ };
68
+ if (includeBody) {
69
+ out.bodyText = m.bodyText || undefined;
70
+ // Fall back to a crude strip when only HTML exists.
71
+ if (!out.bodyText && m.bodyHtml) out.bodyText = String(m.bodyHtml).replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim().slice(0, 20000);
72
+ if (m.bodyError) out.bodyError = m.bodyError;
73
+ if (Array.isArray(m.attachments) && m.attachments.length) {
74
+ out.attachments = m.attachments.map((a: any) => ({ id: a.id, filename: a.filename, mimeType: a.mimeType, size: a.size }));
75
+ }
76
+ }
77
+ return out;
78
+ }
79
+
80
+ type Tool = {
81
+ description: string;
82
+ inputSchema: any;
83
+ fullOnly?: boolean;
84
+ run: (a: any) => Promise<any>;
85
+ };
86
+
87
+ const str = (description: string) => ({ type: "string", description });
88
+ const num = (description: string) => ({ type: "number", description });
89
+ const bool = (description: string) => ({ type: "boolean", description });
90
+ const obj = (props: any, required: string[]) => ({ type: "object", properties: props, required });
91
+
92
+ const TOOLS: Record<string, Tool> = {
93
+ list_accounts: {
94
+ description: "List configured mail accounts (id, name, email).",
95
+ inputSchema: obj({}, []),
96
+ run: async () => (await rpc("getAccounts")).map((a: any) => ({ id: a.id, name: a.name, email: a.email })),
97
+ },
98
+ list_folders: {
99
+ description: "List folders for an account, with unread/total counts.",
100
+ inputSchema: obj({ accountId: str("Account id from list_accounts") }, ["accountId"]),
101
+ run: async (a) => (await rpc("getFolders", { accountId: a.accountId })).map((f: any) => ({ id: f.id, path: f.path, specialUse: f.specialUse || undefined, unread: f.unreadCount, total: f.totalCount })),
102
+ },
103
+ list_messages: {
104
+ description: "List messages in a folder, newest first (paged, 50/page). Returns envelopes with snippets, no bodies.",
105
+ inputSchema: obj({ accountId: str("Account id"), folderId: num("Folder id from list_folders"), page: num("1-based page, default 1") }, ["accountId", "folderId"]),
106
+ run: async (a) => {
107
+ const r = await rpc("getMessages", { accountId: a.accountId, folderId: a.folderId, page: a.page || 1, pageSize: 50 });
108
+ return { total: r.total, messages: (r.messages || []).map((m: any) => slimMessage({ ...m, accountId: a.accountId, folderId: a.folderId }, false)) };
109
+ },
110
+ },
111
+ search_messages: {
112
+ description: "Full-text search across all local mail (FTS). Query supports plain words; results span accounts/folders.",
113
+ inputSchema: obj({ query: str("Search text"), includeTrashSpam: bool("Also search Trash/Spam (default false)") }, ["query"]),
114
+ run: async (a) => {
115
+ const r = await rpc("searchMessages", { query: (a.query || "").trim(), page: 1, pageSize: 50, scope: "all", accountId: "", folderId: 0, includeTrashSpam: !!a.includeTrashSpam });
116
+ return { total: r.total, messages: (r.messages || []).map((m: any) => slimMessage(m, false)) };
117
+ },
118
+ },
119
+ get_message: {
120
+ description: "Fetch one message including its text body and attachment list.",
121
+ inputSchema: obj({ accountId: str("Account id"), uid: num("Message uid"), folderId: num("Folder id the uid lives in") }, ["accountId", "uid", "folderId"]),
122
+ run: async (a) => slimMessage(await rpc("getMessage", { accountId: a.accountId, uid: a.uid, folderId: a.folderId, allowRemote: false }), true),
123
+ },
124
+ flag_message: {
125
+ description: "Set or clear flags on a message. flags is the COMPLETE new flag list, e.g. [\"\\\\Seen\", \"\\\\Flagged\"] or [\"\\\\Seen\"] to unflag.",
126
+ inputSchema: obj({ accountId: str("Account id"), uid: num("Message uid"), folderId: num("Folder id"), flags: { type: "array", items: { type: "string" }, description: "Complete flag list" } }, ["accountId", "uid", "folderId", "flags"]),
127
+ run: async (a) => rpc("updateFlags", { accountId: a.accountId, uid: a.uid, folderId: a.folderId, flags: a.flags }),
128
+ },
129
+ move_message: {
130
+ description: "Move a message to another folder (same account). FULL scope only.",
131
+ inputSchema: obj({ accountId: str("Account id"), uid: num("Message uid"), folderId: num("Source folder id"), targetFolderId: num("Destination folder id") }, ["accountId", "uid", "folderId", "targetFolderId"]),
132
+ fullOnly: true,
133
+ run: async (a) => rpc("moveMessages", { accountId: a.accountId, uids: [a.uid], folderIds: [a.folderId], targetFolderId: a.targetFolderId }),
134
+ },
135
+ create_draft: {
136
+ description: "Create a draft (NOT sent — drafts are for the human to review and send). FULL scope only.",
137
+ inputSchema: obj({ accountId: str("Account id to draft from"), to: str("To recipients, comma-separated"), cc: str("Cc recipients (optional)"), subject: str("Subject"), bodyText: str("Plain-text body") }, ["accountId", "to", "subject", "bodyText"]),
138
+ fullOnly: true,
139
+ run: async (a) => rpc("saveDraft", { accountId: a.accountId, subject: a.subject, bodyHtml: String(a.bodyText).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/\n/g, "<br>"), bodyText: a.bodyText, to: a.to, cc: a.cc || "" }),
140
+ },
141
+ };
142
+
143
+ // ── MCP stdio plumbing (JSON-RPC 2.0, one message per line) ──
144
+
145
+ const out = (msg: any): void => { process.stdout.write(JSON.stringify(msg) + "\n"); };
146
+
147
+ function toolList(): any[] {
148
+ return Object.entries(TOOLS)
149
+ .filter(([, t]) => FULL || !t.fullOnly)
150
+ .map(([name, t]) => ({ name, description: t.description, inputSchema: t.inputSchema }));
151
+ }
152
+
153
+ const rl = readline.createInterface({ input: process.stdin, terminal: false });
154
+ rl.on("line", (line) => { void handle(line); });
155
+
156
+ async function handle(line: string): Promise<void> {
157
+ if (!line.trim()) return;
158
+ let req: any;
159
+ try { req = JSON.parse(line); } catch { return; }
160
+ const { id, method, params } = req;
161
+ const reply = (result: any) => out({ jsonrpc: "2.0", id, result });
162
+ const fail = (code: number, message: string) => out({ jsonrpc: "2.0", id, error: { code, message } });
163
+ try {
164
+ switch (method) {
165
+ case "initialize":
166
+ reply({
167
+ protocolVersion: params?.protocolVersion || "2024-11-05",
168
+ capabilities: { tools: {} },
169
+ serverInfo: { name: "rmfmail", version: "1.0.0" },
170
+ instructions: `Email triage over the local rmfmail daemon (scope: ${FULL ? "full — includes move + draft creation" : "triage — reads and flags only"}). There is no send tool: draft, never send.`,
171
+ });
172
+ return;
173
+ case "notifications/initialized":
174
+ case "notifications/cancelled":
175
+ return; // notifications get no response
176
+ case "tools/list":
177
+ reply({ tools: toolList() });
178
+ return;
179
+ case "tools/call": {
180
+ const tool = TOOLS[params?.name];
181
+ if (!tool || (tool.fullOnly && !FULL)) { fail(-32602, `unknown tool: ${params?.name}`); return; }
182
+ try {
183
+ const result = await tool.run(params?.arguments || {});
184
+ reply({ content: [{ type: "text", text: JSON.stringify(result, null, 1) }] });
185
+ } catch (e: any) {
186
+ reply({ content: [{ type: "text", text: `Error: ${e?.message || e}` }], isError: true });
187
+ }
188
+ return;
189
+ }
190
+ case "ping":
191
+ reply({});
192
+ return;
193
+ default:
194
+ if (id !== undefined) fail(-32601, `method not found: ${method}`);
195
+ }
196
+ } catch (e: any) {
197
+ if (id !== undefined) fail(-32603, e?.message || String(e));
198
+ }
199
+ }
@@ -31,6 +31,18 @@ import { dispatch } from "@bobfrankston/mailx-service/jsonrpc.js";
31
31
  * window. Returns null if the server can't bind (popout disabled). */
32
32
  export async function startPopoutServer(svc, onAction) {
33
33
  const token = crypto.randomBytes(18).toString("hex");
34
+ const agentTokens = { triage: crypto.randomBytes(18).toString("hex"), full: crypto.randomBytes(18).toString("hex") };
35
+ // Actions a TRIAGE-scoped agent may call: reads plus flag/read-state.
36
+ // Deliberately excludes send/move/delete/settings and every popout/window
37
+ // action. Full-scope agents get the whole dispatcher.
38
+ const TRIAGE_ACTIONS = new Set([
39
+ "getVersion", "getAccounts", "getFolders", "getMessages", "getUnifiedInbox",
40
+ "getMessage", "searchMessages", "getThreadMessages", "getAttachment",
41
+ "getMessageSource", "searchContacts", "listContacts", "getCalendarEvents",
42
+ "getCalendars", "getTasks", "getOutboxStatus", "getSyncPending",
43
+ "getDiagnostics", "getPriorityLists", "hasCcHistoryTo", "hasBccHistoryTo",
44
+ "updateFlags", "markFolderRead", "logClientEvent",
45
+ ]);
34
46
  // Static root for /app/<token>/... — the app package root, so pages under
35
47
  // client/ can reach ../styles, ../../node_modules etc. exactly as they do
36
48
  // through msger's custom protocol.
@@ -53,7 +65,13 @@ export async function startPopoutServer(svc, onAction) {
53
65
  return;
54
66
  }
55
67
  // Token gate on every request — loopback TCP is locally reachable.
56
- if (url.searchParams.get("t") !== token) {
68
+ // Three tokens: the per-launch window token (full surface), and two
69
+ // agent tokens published in ~/.rmfmail/agent.json.
70
+ const presented = url.searchParams.get("t");
71
+ const isWindowToken = presented === token;
72
+ const isFullAgent = presented === agentTokens.full;
73
+ const isTriageAgent = presented === agentTokens.triage;
74
+ if (!isWindowToken && !isFullAgent && !isTriageAgent) {
57
75
  res.writeHead(403).end("forbidden");
58
76
  return;
59
77
  }
@@ -90,6 +108,11 @@ export async function startPopoutServer(svc, onAction) {
90
108
  res.writeHead(400).end("bad rpc request");
91
109
  return;
92
110
  }
111
+ if (isTriageAgent && !TRIAGE_ACTIONS.has(rpcReq._action)) {
112
+ res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" });
113
+ res.end(JSON.stringify({ _cbid: rpcReq._cbid, error: `action "${rpcReq._action}" requires the full agent token (triage scope is reads + flags)` }));
114
+ return;
115
+ }
93
116
  const rpcRes = await dispatch(svc, rpcReq);
94
117
  res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" });
95
118
  res.end(JSON.stringify(rpcRes));
@@ -185,7 +208,7 @@ export async function startPopoutServer(svc, onAction) {
185
208
  const port = typeof addr === "object" && addr ? addr.port : 0;
186
209
  if (port) {
187
210
  console.log(` [popout] window server on http://127.0.0.1:${port} (loopback, token-gated)`);
188
- resolve({ port, token, broadcastEvent });
211
+ resolve({ port, token, agentTokens, broadcastEvent });
189
212
  }
190
213
  else {
191
214
  resolve(null);
@@ -1 +1 @@
1
- {"version":3,"file":"popout-server.js","sourceRoot":"","sources":["popout-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"popout-server.js","sourceRoot":"","sources":["popout-server.ts"],"names":[],"mappings":"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"}
@@ -29,6 +29,11 @@ import { dispatch } from "@bobfrankston/mailx-service/jsonrpc.js";
29
29
  export interface PopoutServerInfo {
30
30
  port: number;
31
31
  token: string;
32
+ /** Agent tokens (written to ~/.rmfmail/agent.json for third-party apps
33
+ * and AI agents). `triage` = reads + flagging; `full` = everything the
34
+ * UI can do. Sending remains a human act by convention — the MCP layer
35
+ * exposes drafts, not send. */
36
+ agentTokens: { triage: string; full: string };
32
37
  /** Push a daemon event to every connected popout/secondary window
33
38
  * (SSE). The main window gets events over the msger service channel;
34
39
  * popout-served pages subscribe to GET /events and receive the same
@@ -49,6 +54,18 @@ export interface PopoutAction {
49
54
  * window. Returns null if the server can't bind (popout disabled). */
50
55
  export async function startPopoutServer(svc: any, onAction?: (act: PopoutAction) => void): Promise<PopoutServerInfo | null> {
51
56
  const token = crypto.randomBytes(18).toString("hex");
57
+ const agentTokens = { triage: crypto.randomBytes(18).toString("hex"), full: crypto.randomBytes(18).toString("hex") };
58
+ // Actions a TRIAGE-scoped agent may call: reads plus flag/read-state.
59
+ // Deliberately excludes send/move/delete/settings and every popout/window
60
+ // action. Full-scope agents get the whole dispatcher.
61
+ const TRIAGE_ACTIONS = new Set([
62
+ "getVersion", "getAccounts", "getFolders", "getMessages", "getUnifiedInbox",
63
+ "getMessage", "searchMessages", "getThreadMessages", "getAttachment",
64
+ "getMessageSource", "searchContacts", "listContacts", "getCalendarEvents",
65
+ "getCalendars", "getTasks", "getOutboxStatus", "getSyncPending",
66
+ "getDiagnostics", "getPriorityLists", "hasCcHistoryTo", "hasBccHistoryTo",
67
+ "updateFlags", "markFolderRead", "logClientEvent",
68
+ ]);
52
69
  // Static root for /app/<token>/... — the app package root, so pages under
53
70
  // client/ can reach ../styles, ../../node_modules etc. exactly as they do
54
71
  // through msger's custom protocol.
@@ -74,7 +91,13 @@ export async function startPopoutServer(svc: any, onAction?: (act: PopoutAction)
74
91
  }
75
92
 
76
93
  // Token gate on every request — loopback TCP is locally reachable.
77
- if (url.searchParams.get("t") !== token) {
94
+ // Three tokens: the per-launch window token (full surface), and two
95
+ // agent tokens published in ~/.rmfmail/agent.json.
96
+ const presented = url.searchParams.get("t");
97
+ const isWindowToken = presented === token;
98
+ const isFullAgent = presented === agentTokens.full;
99
+ const isTriageAgent = presented === agentTokens.triage;
100
+ if (!isWindowToken && !isFullAgent && !isTriageAgent) {
78
101
  res.writeHead(403).end("forbidden");
79
102
  return;
80
103
  }
@@ -109,6 +132,11 @@ export async function startPopoutServer(svc: any, onAction?: (act: PopoutAction)
109
132
  res.writeHead(400).end("bad rpc request");
110
133
  return;
111
134
  }
135
+ if (isTriageAgent && !TRIAGE_ACTIONS.has(rpcReq._action)) {
136
+ res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" });
137
+ res.end(JSON.stringify({ _cbid: rpcReq._cbid, error: `action "${rpcReq._action}" requires the full agent token (triage scope is reads + flags)` }));
138
+ return;
139
+ }
112
140
  const rpcRes = await dispatch(svc, rpcReq);
113
141
  res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" });
114
142
  res.end(JSON.stringify(rpcRes));
@@ -183,7 +211,7 @@ export async function startPopoutServer(svc: any, onAction?: (act: PopoutAction)
183
211
  const port = typeof addr === "object" && addr ? addr.port : 0;
184
212
  if (port) {
185
213
  console.log(` [popout] window server on http://127.0.0.1:${port} (loopback, token-gated)`);
186
- resolve({ port, token, broadcastEvent });
214
+ resolve({ port, token, agentTokens, broadcastEvent });
187
215
  } else {
188
216
  resolve(null);
189
217
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/rmfmail",
3
- "version": "1.2.129",
3
+ "version": "1.2.130",
4
4
  "description": "Local-first email client with IMAP sync and standalone native app",
5
5
  "type": "module",
6
6
  "main": "bin/mailx.js",
@@ -8,7 +8,8 @@
8
8
  "node": ">=24.2.0"
9
9
  },
10
10
  "bin": {
11
- "rmfmail": "bin/mailx.js"
11
+ "rmfmail": "bin/mailx.js",
12
+ "rmfmail-mcp": "bin/mcp-server.js"
12
13
  },
13
14
  "workspaces": [
14
15
  "packages/mailx-types",
@@ -39,7 +40,7 @@
39
40
  "@bobfrankston/mailx-store-web": "^0.1.38",
40
41
  "@bobfrankston/mailx-sync": "^0.1.27",
41
42
  "@bobfrankston/miscinfo": "^1.0.13",
42
- "@bobfrankston/msger": "^0.1.389",
43
+ "@bobfrankston/msger": "^0.1.391",
43
44
  "@bobfrankston/node-tcp-transport": "^0.1.10",
44
45
  "@bobfrankston/oauthsupport": "^1.0.34",
45
46
  "@bobfrankston/rmf-tiny": "^0.1.38",
@@ -117,7 +118,7 @@
117
118
  "@bobfrankston/mailx-store-web": "^0.1.38",
118
119
  "@bobfrankston/mailx-sync": "^0.1.27",
119
120
  "@bobfrankston/miscinfo": "^1.0.13",
120
- "@bobfrankston/msger": "^0.1.389",
121
+ "@bobfrankston/msger": "^0.1.391",
121
122
  "@bobfrankston/node-tcp-transport": "^0.1.10",
122
123
  "@bobfrankston/oauthsupport": "^1.0.34",
123
124
  "@bobfrankston/rmf-tiny": "^0.1.38",