@agenticmail/mcp 0.7.7 → 0.7.9
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 +30 -0
- package/dist/index.js +48 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,36 @@ The MCP (Model Context Protocol) server for [AgenticMail](https://github.com/age
|
|
|
8
8
|
|
|
9
9
|
When connected, your AI agent can send emails and texts, check inboxes, reply to messages, receive verification codes, manage contacts, schedule emails, assign tasks to other agents, and more — all through natural language. The server provides 62 tools that cover every email, SMS, and agent management operation.
|
|
10
10
|
|
|
11
|
+
## ✨ What's new in 0.7.9
|
|
12
|
+
|
|
13
|
+
- **📐 `call_agent` accepts `outputSchema`** — pass a JSON Schema (draft-7 subset) describing the deliverable shape and the API validates `submit_result` against it; mismatches come back as validator errors so the worker can retry with a correct shape instead of returning free-form prose. The schema is rendered into the worker's wake prompt up-front. Example:
|
|
14
|
+
```js
|
|
15
|
+
await call_agent({
|
|
16
|
+
target: 'vesper',
|
|
17
|
+
task: 'Audit row 34 of the YAML patch.',
|
|
18
|
+
outputSchema: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
required: ['summary', 'findings', 'recommendation'],
|
|
21
|
+
properties: {
|
|
22
|
+
summary: { type: 'string' },
|
|
23
|
+
findings: { type: 'array', items: { type: 'string' } },
|
|
24
|
+
recommendation: { type: 'string', enum: ['proceed', 'block'] },
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
- **📥 `tail_worker(workerId, lines?)`** — paired with `check_activity`. When a worker shows up as long-running or stale, `tail_worker` returns the trailing N lines of its log (every tool call, result, and assistant chunk as a one-liner). Master-key only. Lives in the `multi_agent_extras` tier so it ships in the default toolbelt for hosts that delegate work.
|
|
30
|
+
- **📊 `check_activity` shows live progress** — output now includes last tool used, tool-call count, duration in `Xh Ym Zs`, and a `stale` flag derived from heartbeat age. Workers are no longer auto-evicted from the registry; staleness is diagnostic.
|
|
31
|
+
|
|
32
|
+
## ✨ Earlier — 0.7.7
|
|
33
|
+
|
|
34
|
+
- **`wake` parameter on every send tool** — `send_email`, `reply_email`, `forward_email`, `template_send`, `manage_drafts(send)` all accept `wake: ["alice", "bob"]` (or comma-separated string). The dispatcher gives a Claude turn only to listed agents; the rest still receive the mail but stay asleep. Single biggest token saver on multi-agent threads.
|
|
35
|
+
- **`check_activity` tool** (in the `essential` set) — see which agents the dispatcher has woken right now, what they're working on, how long they've been running, plus a preview of recently-finished work. Answers "did the agent I just emailed actually start working?" without waiting for a reply.
|
|
36
|
+
- **Thread-close markers** — put `[FINAL]`, `[DONE]`, `[CLOSED]`, or `[WRAP]` in a subject to tell the dispatcher this thread is sealed; no more wakes on any reply to it. The wake prompt teaches agents to add these markers when they sign off the work.
|
|
37
|
+
- **LLM-tolerant input coercion** — `batch_mark_read({ uids: "[1,2,3,4]" })`, `wait_for_email({ timeout: "120" })`, `manage_drafts({ where: '{"id":"abc"}' })`, `manage_pending({ allowSensitive: "true" })` all now just work. Strings get parsed before zod validates. No more "expected array, received string" retries.
|
|
38
|
+
- **Server `instructions` field** sent on `initialize` — every connecting MCP client (Claude Code, ChatGPT, Cursor, Grok…) gets the coordination protocol in context before they touch any tool. Provider-agnostic.
|
|
39
|
+
- **`wait_for_email` filters** — block on a specific reply, not just "any new event". Supports `from`, `subject`, `inReplyTo`, `participants`, `includeTasks`.
|
|
40
|
+
|
|
11
41
|
## Install
|
|
12
42
|
|
|
13
43
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -22401,6 +22401,11 @@ var TOOL_SETS = {
|
|
|
22401
22401
|
// "did the agent I just emailed actually start working?" without
|
|
22402
22402
|
// having to wait for a reply or send an acknowledgment.
|
|
22403
22403
|
"check_activity",
|
|
22404
|
+
// tail_worker complements check_activity: when a worker has been
|
|
22405
|
+
// running a long time or shows up as stale, tail_worker gives you
|
|
22406
|
+
// the running log of what it actually did — every tool call, every
|
|
22407
|
+
// result. Paired with check_activity so they ship in the same tier.
|
|
22408
|
+
"tail_worker",
|
|
22404
22409
|
"check_tasks"
|
|
22405
22410
|
],
|
|
22406
22411
|
/** Less-common mail operations. */
|
|
@@ -23258,9 +23263,21 @@ var toolDefinitions = [
|
|
|
23258
23263
|
required: ["action"]
|
|
23259
23264
|
}
|
|
23260
23265
|
},
|
|
23266
|
+
{
|
|
23267
|
+
name: "tail_worker",
|
|
23268
|
+
description: "Tail the log of a running (or recently-finished) dispatcher worker. Use this when check_activity shows a worker has been running a long time or is marked stale, and you want to see what it is actually doing \u2014 every tool call, tool result, and assistant chunk is logged as a one-liner. Returns the last N lines (default 80). The workerId comes from check_activity output. Requires master key.",
|
|
23269
|
+
inputSchema: {
|
|
23270
|
+
type: "object",
|
|
23271
|
+
properties: {
|
|
23272
|
+
workerId: { type: "string", description: "Worker id from check_activity output." },
|
|
23273
|
+
lines: { type: "number", description: "How many trailing log lines to return. Default 80, max 1000." }
|
|
23274
|
+
},
|
|
23275
|
+
required: ["workerId"]
|
|
23276
|
+
}
|
|
23277
|
+
},
|
|
23261
23278
|
{
|
|
23262
23279
|
name: "check_activity",
|
|
23263
|
-
description: "Check which agents are currently being woken by the dispatcher
|
|
23280
|
+
description: "Check which agents are currently being woken by the dispatcher. Use this when you sent mail to a teammate and want to know if they have actually started working, or to audit the live multi-agent state. Returns active workers with the agent name, what triggered the wake (mail UID + subject, or task id), how long they have been running, the most recent tool they invoked, how many tool calls they have made, a `stale` flag (true if the dispatcher has not heartbeated in 90s+), and a preview of recently-finished work. Workers may run for hours \u2014 there is no auto-eviction; staleness is just a hint. Requires master key.",
|
|
23264
23281
|
inputSchema: {
|
|
23265
23282
|
type: "object",
|
|
23266
23283
|
properties: {
|
|
@@ -23305,14 +23322,15 @@ var toolDefinitions = [
|
|
|
23305
23322
|
},
|
|
23306
23323
|
{
|
|
23307
23324
|
name: "call_agent",
|
|
23308
|
-
description: `Synchronous RPC to delegate work to another AgenticMail agent. Pipeline: the task is queued in AgenticMail, the target agent processes it AS THEMSELVES (under their real identity, mailbox, persona, and audit trail), and the structured result returns into your call. THIS IS HOW MULTI-AGENT COORDINATION IS SUPPOSED TO WORK from any MCP host. Do not, instead, spawn one of your host's native sub-agents and tell it to "act as <target>" \u2014 that produces output under your identity, never touches the target's inbox, and skips their persona. Times out after the specified duration (default 180s, max 300s).`,
|
|
23325
|
+
description: `Synchronous RPC to delegate work to another AgenticMail agent. Pipeline: the task is queued in AgenticMail, the target agent processes it AS THEMSELVES (under their real identity, mailbox, persona, and audit trail), and the structured result returns into your call. THIS IS HOW MULTI-AGENT COORDINATION IS SUPPOSED TO WORK from any MCP host. Do not, instead, spawn one of your host's native sub-agents and tell it to "act as <target>" \u2014 that produces output under your identity, never touches the target's inbox, and skips their persona. Pass outputSchema to require a structured deliverable shape: the API validates the worker's submit_result against the schema and rejects mismatches with validator errors, so the worker can retry with a correct shape rather than returning free-form prose. Times out after the specified duration (default 180s, max 300s).`,
|
|
23309
23326
|
inputSchema: {
|
|
23310
23327
|
type: "object",
|
|
23311
23328
|
properties: {
|
|
23312
23329
|
target: { type: "string", description: "Name of the agent to call" },
|
|
23313
23330
|
task: { type: "string", description: "Task description" },
|
|
23314
23331
|
payload: { type: "object", description: "Additional data" },
|
|
23315
|
-
timeout: { type: "number", description: "Max seconds to wait (default: 180, max: 300)" }
|
|
23332
|
+
timeout: { type: "number", description: "Max seconds to wait (default: 180, max: 300)" },
|
|
23333
|
+
outputSchema: { type: "object", description: "Optional JSON Schema (draft-7 subset: type, required, properties, items, enum, additionalProperties, minLength/maxLength, minimum/maximum) describing the shape submit_result must conform to. The worker sees the schema in the wake prompt and the API validates on submission." }
|
|
23316
23334
|
},
|
|
23317
23335
|
required: ["target", "task"]
|
|
23318
23336
|
}
|
|
@@ -24574,6 +24592,15 @@ ${r.agents.map(
|
|
|
24574
24592
|
}
|
|
24575
24593
|
throw new Error("Invalid action. Use: list_inactive, cleanup, or set_persistent");
|
|
24576
24594
|
}
|
|
24595
|
+
case "tail_worker": {
|
|
24596
|
+
if (!args2.workerId) throw new Error("workerId is required");
|
|
24597
|
+
const lines = typeof args2.lines === "number" ? args2.lines : 80;
|
|
24598
|
+
const r = await apiRequest("GET", `/dispatcher/worker-log/${encodeURIComponent(String(args2.workerId))}?lines=${lines}`, void 0, true);
|
|
24599
|
+
if (!r?.tail || !Array.isArray(r.tail)) return `No log found for worker ${args2.workerId}.`;
|
|
24600
|
+
if (r.tail.length === 0) return `Worker ${args2.workerId} has no log entries yet.`;
|
|
24601
|
+
return `Worker ${args2.workerId} log (last ${r.lines} of ${r.bytes} bytes):
|
|
24602
|
+
${r.tail.join("\n")}`;
|
|
24603
|
+
}
|
|
24577
24604
|
case "check_activity": {
|
|
24578
24605
|
const r = await apiRequest("GET", "/dispatcher/activity", void 0, true);
|
|
24579
24606
|
const filterAgent = typeof args2.agent === "string" ? args2.agent.toLowerCase() : "";
|
|
@@ -24585,14 +24612,24 @@ ${r.agents.map(
|
|
|
24585
24612
|
if (filterAgent) return `No dispatcher activity for "${args2.agent}" right now or in the last 2 minutes. Either the agent has not been woken on this thread yet, the dispatcher is not running, or mail to them is still in flight.`;
|
|
24586
24613
|
return "No dispatcher activity right now or in the last 2 minutes. If you just sent mail and expected an agent to wake, give it a moment \u2014 the dispatcher subscribes to /system/events for sub-second wake. If nothing happens for 30s, check that the dispatcher process is running (`pm2 list`) and that the recipient is a real local agent (`list_agents`).";
|
|
24587
24614
|
}
|
|
24615
|
+
const fmtDur = (ms) => {
|
|
24616
|
+
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
24617
|
+
if (ms < 36e5) return `${Math.floor(ms / 6e4)}m${Math.floor(ms % 6e4 / 1e3)}s`;
|
|
24618
|
+
return `${Math.floor(ms / 36e5)}h${Math.floor(ms % 36e5 / 6e4)}m`;
|
|
24619
|
+
};
|
|
24588
24620
|
const fmt = (w, prefix) => {
|
|
24589
|
-
const dur = w.durationMs ?
|
|
24621
|
+
const dur = w.durationMs ? fmtDur(w.durationMs) : "?";
|
|
24590
24622
|
const trig = w.trigger?.subject ? ` \u2014 ${String(w.trigger.subject).slice(0, 60)}` : w.trigger?.taskId ? ` \u2014 task ${String(w.trigger.taskId).slice(0, 8)}` : "";
|
|
24591
24623
|
const from = w.trigger?.from ? ` (from ${w.trigger.from})` : "";
|
|
24592
24624
|
const preview = w.resultPreview ? `
|
|
24593
24625
|
\u2192 ${String(w.resultPreview).slice(0, 140).replace(/\s+/g, " ").trim()}` : "";
|
|
24594
|
-
|
|
24595
|
-
|
|
24626
|
+
let status = w.endedAtMs ? w.ok === false ? "failed" : "finished" : "running";
|
|
24627
|
+
if (!w.endedAtMs && w.stale) status = "running (stale heartbeat)";
|
|
24628
|
+
const turns = !w.endedAtMs && typeof w.turnCount === "number" ? ` \xB7 ${w.turnCount} tool calls` : "";
|
|
24629
|
+
const tool = !w.endedAtMs && w.lastTool ? ` \xB7 last tool: ${w.lastTool}` : "";
|
|
24630
|
+
const idHint = !w.endedAtMs ? `
|
|
24631
|
+
id: ${w.workerId} (use tail_worker for the log)` : "";
|
|
24632
|
+
return ` ${prefix} ${w.agentName} [${w.kind}] ${status} ${dur}${turns}${tool}${trig}${from}${preview}${idHint}`;
|
|
24596
24633
|
};
|
|
24597
24634
|
const lines = [];
|
|
24598
24635
|
if (activeList.length > 0) {
|
|
@@ -24631,7 +24668,11 @@ ${r.tasks.map(
|
|
|
24631
24668
|
const created = await apiRequest("POST", "/tasks/assign", {
|
|
24632
24669
|
assignee: args2.target,
|
|
24633
24670
|
taskType: "rpc",
|
|
24634
|
-
payload: { task: args2.task, ...args2.payload || {} }
|
|
24671
|
+
payload: { task: args2.task, ...args2.payload || {} },
|
|
24672
|
+
// Pass outputSchema through verbatim. The API persists it
|
|
24673
|
+
// and renders it into the worker's wake prompt; validation
|
|
24674
|
+
// happens server-side on submit_result.
|
|
24675
|
+
...args2.outputSchema ? { outputSchema: args2.outputSchema } : {}
|
|
24635
24676
|
});
|
|
24636
24677
|
if (!created?.id) throw new Error("Failed to create task");
|
|
24637
24678
|
const taskId = created.id;
|