@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.
Files changed (3) hide show
  1. package/README.md +30 -0
  2. package/dist/index.js +48 -7
  3. 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 (right now or in the last 2 minutes). 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, and a preview of the most recent finished work. Requires master key \u2014 the host session has it; subagents normally do not.",
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 ? `${(w.durationMs / 1e3).toFixed(1)}s` : "?";
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
- const status = w.endedAtMs ? w.ok === false ? "failed" : "finished" : "running";
24595
- return ` ${prefix} ${w.agentName} [${w.kind}] ${status} ${dur}${trig}${from}${preview}`;
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/mcp",
3
- "version": "0.7.7",
3
+ "version": "0.7.9",
4
4
  "mcpName": "io.github.agenticmail/mcp",
5
5
  "description": "MCP server for AgenticMail — give any AI client real email and SMS capabilities",
6
6
  "type": "module",