@adeu/mcp-server 1.18.0 → 1.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +136 -22
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/scripts/verify-reasoning-order.mjs +113 -0
- package/src/index.ts +170 -11
- package/src/mcp.bugs.test.ts +10 -1
- package/src/mcp.cloud.test.ts +4 -3
- package/src/mcp.schema-gaps.test.ts +4 -0
- package/src/parity_live.test.ts +181 -95
- package/src/repro.feedback.test.ts +24 -7
- package/src/response-builders.ts +21 -5
package/dist/index.js
CHANGED
|
@@ -204,12 +204,17 @@ function build_search_response(text, search_query, search_regex, search_case_sen
|
|
|
204
204
|
const [body] = split_structural_appendix(text);
|
|
205
205
|
const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
206
206
|
const flags = search_case_sensitive ? "g" : "gi";
|
|
207
|
-
|
|
207
|
+
let regexDowngradedNote = "";
|
|
208
208
|
let regex;
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
209
|
+
if (search_regex) {
|
|
210
|
+
try {
|
|
211
|
+
regex = new RegExp(search_query, flags);
|
|
212
|
+
} catch (e) {
|
|
213
|
+
regexDowngradedNote = `> **Note:** \`${search_query}\` is not a valid regular expression (${e.message}), so it was searched as literal text instead. If you meant a regex, fix the pattern; if you meant literal text, set \`search_regex\` to false.`;
|
|
214
|
+
regex = new RegExp(escapeRegExp(search_query), flags);
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
regex = new RegExp(escapeRegExp(search_query), flags);
|
|
213
218
|
}
|
|
214
219
|
const allMatches = Array.from(body.matchAll(regex));
|
|
215
220
|
const pag_res = paginate(body, "");
|
|
@@ -264,6 +269,9 @@ The query DOES appear elsewhere in the document (${allMatches.length} match${all
|
|
|
264
269
|
|
|
265
270
|
Verify your search spelling, or try setting \`search_case_sensitive\` to false or enabling \`search_regex\` if you used pattern wildcards.`;
|
|
266
271
|
}
|
|
272
|
+
if (regexDowngradedNote) body_msg = `${regexDowngradedNote}
|
|
273
|
+
|
|
274
|
+
${body_msg}`;
|
|
267
275
|
const llm_content2 = `> **File Path:** \`${resolve(file_path)}\`
|
|
268
276
|
|
|
269
277
|
${body_msg}`;
|
|
@@ -354,6 +362,7 @@ ${body_msg}`;
|
|
|
354
362
|
);
|
|
355
363
|
i++;
|
|
356
364
|
}
|
|
365
|
+
if (regexDowngradedNote) ui_parts.unshift(regexDowngradedNote);
|
|
357
366
|
const ui_markdown = ui_parts.join("\n\n");
|
|
358
367
|
const llm_content = `> **File Path:** \`${resolve(file_path)}\`
|
|
359
368
|
|
|
@@ -1220,6 +1229,36 @@ async function list_available_mailboxes() {
|
|
|
1220
1229
|
}
|
|
1221
1230
|
|
|
1222
1231
|
// src/index.ts
|
|
1232
|
+
var MATCH_MODE_SYNONYMS = {
|
|
1233
|
+
strict: "strict",
|
|
1234
|
+
first: "first",
|
|
1235
|
+
all: "all",
|
|
1236
|
+
first_only: "first",
|
|
1237
|
+
firstonly: "first",
|
|
1238
|
+
"first-only": "first",
|
|
1239
|
+
all_occurrences: "all",
|
|
1240
|
+
alloccurrences: "all",
|
|
1241
|
+
"all-occurrences": "all",
|
|
1242
|
+
every: "all"
|
|
1243
|
+
};
|
|
1244
|
+
function coerceChangeItemInPlace(item) {
|
|
1245
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) return;
|
|
1246
|
+
if (!("type" in item) || item.type === void 0 || item.type === null) {
|
|
1247
|
+
if ("cells" in item) item.type = "insert_row";
|
|
1248
|
+
else if ("text" in item && "target_id" in item) item.type = "reply";
|
|
1249
|
+
else if ("target_text" in item && "new_text" in item) item.type = "modify";
|
|
1250
|
+
}
|
|
1251
|
+
if ("match_mode" in item) {
|
|
1252
|
+
const raw = item.match_mode;
|
|
1253
|
+
if (typeof raw !== "string") {
|
|
1254
|
+
delete item.match_mode;
|
|
1255
|
+
} else {
|
|
1256
|
+
const mapped = MATCH_MODE_SYNONYMS[raw.trim().toLowerCase()];
|
|
1257
|
+
if (mapped === void 0) delete item.match_mode;
|
|
1258
|
+
else item.match_mode = mapped;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1223
1262
|
function readFileBytesOrThrow(filePath) {
|
|
1224
1263
|
try {
|
|
1225
1264
|
return readFileSync3(filePath);
|
|
@@ -1250,8 +1289,8 @@ var READ_DOCX_TAIL = "Modes:\n- 'full' (default): paginated body content. Use pa
|
|
|
1250
1289
|
var PROCESS_BATCH_COMMON_DESC = "Applies a batch of edits and review actions to a DOCX.\n\nAll changes evaluate against the ORIGINAL document state \u2014 do not chain dependent edits within one batch (e.g. rename X to Y, then modify Y). Apply the rename first, then send a second batch.\n\n";
|
|
1251
1290
|
var PROCESS_BATCH_OPERATIONS_DESC = "Each item in `changes` must specify a `type`:\n1. 'modify': Search-and-replace. By default `target_text` must match uniquely (`match_mode`:'strict') \u2014 add surrounding context to disambiguate, or set `match_mode`:'first'/'all' to edit the first or every occurrence. Set `regex`:true to treat `target_text` as a regular expression (capture groups available in `new_text` as $1, $2\u2026). `new_text` supports Markdown: '# Heading 1' through '###### Heading 6', '**bold**', '_italic_', and '\\n\\n' to split into multiple paragraphs. Empty `new_text` deletes. Do NOT write CriticMarkup tags ({++, {--, {>>) manually \u2014 use the `comment` parameter for comments.\n \u2022 EMPTY/FORM TABLE CELLS: a blank cell has no text to match. `read_docx` renders each cell with a trailing `{#cell:<id>}` anchor \u2014 to fill a blank cell, set `target_text` to that exact anchor (e.g. '{#cell:0000005E}') and put the value in `new_text`. Do NOT try to match the pipe layout ('Date | | |'); the pipes are display separators, not editable text.\n2. 'accept' / 'reject': Finalize or revert a tracked change by `target_id` (e.g. 'Chg:12').\n3. 'reply': Reply to a comment by `target_id` (e.g. 'Com:5') with `text`.\n4. 'insert_row' / 'delete_row': Table edits. Disk mode only \u2014 not supported on Live Word canvas.\n\nID VOLATILITY: 'Chg:N' and 'Com:N' shift between document states. Always call `read_docx` immediately before any accept/reject/reply \u2014 do not reuse IDs from earlier in the conversation. The `{#cell:<id>}` anchors are stable (Word-assigned) and safe to reuse across reads.\n\n`author_name` is used for attribution on all tracked changes and comments, in both disk and Live Word modes.";
|
|
1252
1291
|
var DIFF_DOCX_DESC = "Compares two DOCX files and returns a compact `@@ Word Patch @@` diff \u2014 Adeu's token-level, sub-word patch format \u2014 of their text content. Useful for analyzing differences between versions before editing.";
|
|
1253
|
-
var gitSha = "
|
|
1254
|
-
var packageVersion = "1.18.
|
|
1292
|
+
var gitSha = "263a4e4";
|
|
1293
|
+
var packageVersion = "1.18.2";
|
|
1255
1294
|
var buildTag = ` [Adeu v${packageVersion}+${gitSha}]`;
|
|
1256
1295
|
var args = process.argv.slice(2);
|
|
1257
1296
|
var scopeIdx = args.indexOf("--scope");
|
|
@@ -1347,6 +1386,9 @@ registerAppTool(
|
|
|
1347
1386
|
title: "Read DOCX",
|
|
1348
1387
|
description: READ_DOCX_COMMON_DESC + READ_DOCX_TAIL,
|
|
1349
1388
|
inputSchema: z.object({
|
|
1389
|
+
reasoning: z.string().describe(
|
|
1390
|
+
"Why do I need to read this docx document? State this reason before any other parameter."
|
|
1391
|
+
),
|
|
1350
1392
|
file_path: z.string().describe("Absolute path to the DOCX file."),
|
|
1351
1393
|
clean_view: z.boolean().default(false).describe(
|
|
1352
1394
|
"If False (default), returns the 'Raw' text with inline CriticMarkup. If True, returns 'Accepted' text."
|
|
@@ -1370,6 +1412,7 @@ registerAppTool(
|
|
|
1370
1412
|
_meta: { ui: { resourceUri: MARKDOWN_UI_URI } }
|
|
1371
1413
|
},
|
|
1372
1414
|
async ({
|
|
1415
|
+
reasoning,
|
|
1373
1416
|
file_path,
|
|
1374
1417
|
clean_view,
|
|
1375
1418
|
mode,
|
|
@@ -1381,6 +1424,7 @@ registerAppTool(
|
|
|
1381
1424
|
search_case_sensitive
|
|
1382
1425
|
}) => {
|
|
1383
1426
|
try {
|
|
1427
|
+
void reasoning;
|
|
1384
1428
|
const buf = readFileBytesOrThrow(file_path);
|
|
1385
1429
|
if (mode === "outline") {
|
|
1386
1430
|
const doc = await DocumentObject2.load(buf);
|
|
@@ -1433,8 +1477,8 @@ registerAppTool(
|
|
|
1433
1477
|
}
|
|
1434
1478
|
);
|
|
1435
1479
|
var CHANGE_ITEM_SCHEMA = z.object({
|
|
1436
|
-
type: z.enum(["modify", "accept", "reject", "reply", "insert_row", "delete_row"]).describe(
|
|
1437
|
-
"Change kind: 'modify' (search-and-replace), 'accept'/'reject' (resolve a tracked change by id), 'reply' (reply to a comment by id), 'insert_row'/'delete_row' (table edits; disk mode only)."
|
|
1480
|
+
type: z.enum(["modify", "accept", "reject", "reply", "insert_row", "delete_row"]).optional().describe(
|
|
1481
|
+
"Change kind: 'modify' (search-and-replace), 'accept'/'reject' (resolve a tracked change by id), 'reply' (reply to a comment by id), 'insert_row'/'delete_row' (table edits; disk mode only). If omitted it is inferred when unambiguous from the other fields."
|
|
1438
1482
|
),
|
|
1439
1483
|
target_text: z.string().optional().describe(
|
|
1440
1484
|
"modify / insert_row / delete_row: the existing text to locate (interpreted as a regex when regex=true)."
|
|
@@ -1465,6 +1509,9 @@ server.registerTool(
|
|
|
1465
1509
|
{
|
|
1466
1510
|
description: PROCESS_BATCH_COMMON_DESC + PROCESS_BATCH_OPERATIONS_DESC,
|
|
1467
1511
|
inputSchema: {
|
|
1512
|
+
reasoning: z.string().describe(
|
|
1513
|
+
"Why do I need to apply these changes to the document? State this reason before any other parameter."
|
|
1514
|
+
),
|
|
1468
1515
|
original_docx_path: z.string().describe("Absolute path to the source file."),
|
|
1469
1516
|
author_name: z.string().describe("Name to appear in Track Changes (e.g., 'Reviewer AI')."),
|
|
1470
1517
|
changes: z.array(z.union([z.string(), CHANGE_ITEM_SCHEMA])).describe(
|
|
@@ -1477,6 +1524,7 @@ server.registerTool(
|
|
|
1477
1524
|
}
|
|
1478
1525
|
},
|
|
1479
1526
|
async ({
|
|
1527
|
+
reasoning,
|
|
1480
1528
|
original_docx_path,
|
|
1481
1529
|
author_name,
|
|
1482
1530
|
changes,
|
|
@@ -1484,6 +1532,7 @@ server.registerTool(
|
|
|
1484
1532
|
dry_run
|
|
1485
1533
|
}) => {
|
|
1486
1534
|
try {
|
|
1535
|
+
void reasoning;
|
|
1487
1536
|
if (!author_name || !author_name.trim())
|
|
1488
1537
|
return {
|
|
1489
1538
|
content: [
|
|
@@ -1495,19 +1544,49 @@ server.registerTool(
|
|
|
1495
1544
|
content: [{ type: "text", text: "Error: No changes provided." }]
|
|
1496
1545
|
};
|
|
1497
1546
|
const sanitizedChanges = changes.map((item) => {
|
|
1547
|
+
let obj = item;
|
|
1498
1548
|
if (typeof item === "string") {
|
|
1499
1549
|
try {
|
|
1500
1550
|
const parsed = JSON.parse(item);
|
|
1501
|
-
|
|
1502
|
-
return parsed;
|
|
1503
|
-
}
|
|
1504
|
-
return item;
|
|
1551
|
+
obj = parsed !== null && typeof parsed === "object" ? parsed : item;
|
|
1505
1552
|
} catch {
|
|
1506
|
-
|
|
1553
|
+
obj = item;
|
|
1507
1554
|
}
|
|
1508
1555
|
}
|
|
1509
|
-
|
|
1556
|
+
if (obj !== null && typeof obj === "object" && !Array.isArray(obj)) {
|
|
1557
|
+
coerceChangeItemInPlace(obj);
|
|
1558
|
+
}
|
|
1559
|
+
return obj;
|
|
1510
1560
|
});
|
|
1561
|
+
const VALID_TYPES = /* @__PURE__ */ new Set([
|
|
1562
|
+
"modify",
|
|
1563
|
+
"accept",
|
|
1564
|
+
"reject",
|
|
1565
|
+
"reply",
|
|
1566
|
+
"insert_row",
|
|
1567
|
+
"delete_row"
|
|
1568
|
+
]);
|
|
1569
|
+
const typeErrors = [];
|
|
1570
|
+
sanitizedChanges.forEach((c, i) => {
|
|
1571
|
+
if (c !== null && typeof c === "object" && !Array.isArray(c) && (!c.type || !VALID_TYPES.has(c.type))) {
|
|
1572
|
+
typeErrors.push(
|
|
1573
|
+
`- Change ${i + 1}: missing or unrecognized "type". Use one of: modify (needs target_text + new_text), accept/reject (needs target_id like "Chg:12"), reply (needs target_id like "Com:5" + text), insert_row (needs target_text + cells), delete_row (needs target_text). Received keys: [${Object.keys(c).join(", ")}].`
|
|
1574
|
+
);
|
|
1575
|
+
}
|
|
1576
|
+
});
|
|
1577
|
+
if (typeErrors.length > 0) {
|
|
1578
|
+
return {
|
|
1579
|
+
isError: true,
|
|
1580
|
+
content: [
|
|
1581
|
+
{
|
|
1582
|
+
type: "text",
|
|
1583
|
+
text: `Batch rejected. Some changes are malformed:
|
|
1584
|
+
|
|
1585
|
+
${typeErrors.join("\n")}`
|
|
1586
|
+
}
|
|
1587
|
+
]
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1511
1590
|
let outPath = output_path;
|
|
1512
1591
|
if (!outPath) {
|
|
1513
1592
|
const ext = extname(original_docx_path);
|
|
@@ -1560,12 +1639,16 @@ server.registerTool(
|
|
|
1560
1639
|
{
|
|
1561
1640
|
description: "Accepts all tracked changes and removes all comments in a single operation.",
|
|
1562
1641
|
inputSchema: {
|
|
1642
|
+
reasoning: z.string().describe(
|
|
1643
|
+
"Why do I need to accept all changes in this document? State this reason before any other parameter."
|
|
1644
|
+
),
|
|
1563
1645
|
docx_path: z.string().describe("Absolute path to the DOCX file."),
|
|
1564
1646
|
output_path: z.string().optional().describe("Optional output path.")
|
|
1565
1647
|
}
|
|
1566
1648
|
},
|
|
1567
|
-
async ({ docx_path, output_path }) => {
|
|
1649
|
+
async ({ reasoning, docx_path, output_path }) => {
|
|
1568
1650
|
try {
|
|
1651
|
+
void reasoning;
|
|
1569
1652
|
let outPath = output_path;
|
|
1570
1653
|
if (!outPath) {
|
|
1571
1654
|
const ext = extname(docx_path);
|
|
@@ -1597,6 +1680,9 @@ server.registerTool(
|
|
|
1597
1680
|
{
|
|
1598
1681
|
description: DIFF_DOCX_DESC,
|
|
1599
1682
|
inputSchema: {
|
|
1683
|
+
reasoning: z.string().describe(
|
|
1684
|
+
"Why do I need to diff these two documents? State this reason before any other parameter."
|
|
1685
|
+
),
|
|
1600
1686
|
original_path: z.string().describe("Absolute path to the baseline DOCX file."),
|
|
1601
1687
|
modified_path: z.string().describe("Absolute path to the modified DOCX file."),
|
|
1602
1688
|
compare_clean: z.boolean().default(true).describe(
|
|
@@ -1604,8 +1690,9 @@ server.registerTool(
|
|
|
1604
1690
|
)
|
|
1605
1691
|
}
|
|
1606
1692
|
},
|
|
1607
|
-
async ({ original_path, modified_path, compare_clean }) => {
|
|
1693
|
+
async ({ reasoning, original_path, modified_path, compare_clean }) => {
|
|
1608
1694
|
try {
|
|
1695
|
+
void reasoning;
|
|
1609
1696
|
const origBuf = readFileBytesOrThrow(original_path);
|
|
1610
1697
|
const modBuf = readFileBytesOrThrow(modified_path);
|
|
1611
1698
|
const origText = await extractTextFromBuffer(origBuf, compare_clean);
|
|
@@ -1632,6 +1719,9 @@ server.registerTool(
|
|
|
1632
1719
|
{
|
|
1633
1720
|
description: "Prepares a document for external distribution or e-signature. Note: in this zero-dependency environment, protection_mode='encrypt' is unsupported and falls back to a native read-only lock; export_pdf and password are ignored.",
|
|
1634
1721
|
inputSchema: {
|
|
1722
|
+
reasoning: z.string().describe(
|
|
1723
|
+
"Why do I need to finalize this document? State this reason before any other parameter."
|
|
1724
|
+
),
|
|
1635
1725
|
file_path: z.string().describe("Absolute path to the DOCX file."),
|
|
1636
1726
|
output_path: z.string().optional().describe("Optional output path."),
|
|
1637
1727
|
sanitize_mode: z.enum(["full", "keep-markup"]).optional().describe("full removes all markup, keep-markup redacts metadata."),
|
|
@@ -1647,6 +1737,7 @@ server.registerTool(
|
|
|
1647
1737
|
}
|
|
1648
1738
|
},
|
|
1649
1739
|
async ({
|
|
1740
|
+
reasoning,
|
|
1650
1741
|
file_path,
|
|
1651
1742
|
output_path,
|
|
1652
1743
|
sanitize_mode,
|
|
@@ -1656,6 +1747,7 @@ server.registerTool(
|
|
|
1656
1747
|
export_pdf
|
|
1657
1748
|
}) => {
|
|
1658
1749
|
try {
|
|
1750
|
+
void reasoning;
|
|
1659
1751
|
let outPath = output_path;
|
|
1660
1752
|
if (!outPath) {
|
|
1661
1753
|
const ext = extname(file_path);
|
|
@@ -1700,6 +1792,9 @@ if (!isDocxOnly) {
|
|
|
1700
1792
|
title: "Search & Fetch Emails",
|
|
1701
1793
|
description: "Searches the user's live email inbox via the Adeu cloud backend.\n\nTWO MODES:\n1. Search mode (no `email_id`): returns up to `limit` lightweight previews. Use filters (`sender`, `subject`, `is_unread`, `days_ago`, `folder`, `has_attachments`, `attachment_name`) to narrow down.\n2. Fetch mode (with `email_id`): returns the full email body, thread history, and downloads attachments under `max_attachment_size_mb` to the local disk.\n\nAUTO-ESCALATION: If a search returns exactly one preview, the backend automatically fetches the full email in the same call. Plan around the response shape \u2014 check the `type` field (`previews` vs `full_email`) before assuming.\n\nEMAIL ID FORMATS (`email_id` parameter accepts any of):\n- `msg_<6 chars>` \u2014 short ID returned by previews on THIS machine. NOT portable across machines or sessions; the local cache holds the most recent 1000. If you reference one that's been evicted, the tool returns a StaleShortIdError telling you to re-search.\n- `adeu_<numeric>` \u2014 server-side reference for emails Adeu has previously processed. Portable across machines and sessions for the same authenticated user.\n- Raw provider ID (Gmail/Outlook native ID) \u2014 works if you have it, but you usually won't.\n\nFOLDER DEFAULT: omitting `folder` searches the Inbox only (matching what the user sees in their mail client). Use `folder='sent'` for sent items, `folder='all'` to include Deleted Items, Drafts, and other folders.\n\nATTACHMENTS: attachments larger than `max_attachment_size_mb` (default 10) are listed in the response but NOT downloaded \u2014 raise the cap if you need them. Always set `working_directory` when calling from a project so attachments land alongside the user's other files.",
|
|
1702
1794
|
inputSchema: z.object({
|
|
1795
|
+
reasoning: z.string().describe(
|
|
1796
|
+
"Why do I need to search or fetch these emails? State this reason before any other parameter."
|
|
1797
|
+
),
|
|
1703
1798
|
sender: z.string().optional(),
|
|
1704
1799
|
subject: z.string().optional(),
|
|
1705
1800
|
has_attachments: z.boolean().optional(),
|
|
@@ -1733,7 +1828,12 @@ if (!isDocxOnly) {
|
|
|
1733
1828
|
server.registerTool(
|
|
1734
1829
|
"login_to_adeu_cloud",
|
|
1735
1830
|
{
|
|
1736
|
-
description: "Logs the user into Adeu Cloud. Opens a browser window for SSO authentication.\n\nIMPORTANT \u2014 login is user-level, not account-level:\n- An Adeu user can have multiple linked provider accounts (Microsoft, Google) and multiple mailboxes (personal + shared/delegated). One linked account is marked primary.\n- Signing in through ANY of the user's linked accounts authenticates the same Adeu user. Once logged in, the session can read from and draft in ALL of that user's linked accounts and ALL of their mailboxes \u2014 not just the one used to sign in.\n- The choice of which provider account to sign in through is purely an SSO mechanism; it does not select a 'current account' for the session.\n\nWhen the user asks which accounts or mailboxes are available, call `list_available_mailboxes` rather than naming a single account from the login response."
|
|
1831
|
+
description: "Logs the user into Adeu Cloud. Opens a browser window for SSO authentication.\n\nIMPORTANT \u2014 login is user-level, not account-level:\n- An Adeu user can have multiple linked provider accounts (Microsoft, Google) and multiple mailboxes (personal + shared/delegated). One linked account is marked primary.\n- Signing in through ANY of the user's linked accounts authenticates the same Adeu user. Once logged in, the session can read from and draft in ALL of that user's linked accounts and ALL of their mailboxes \u2014 not just the one used to sign in.\n- The choice of which provider account to sign in through is purely an SSO mechanism; it does not select a 'current account' for the session.\n\nWhen the user asks which accounts or mailboxes are available, call `list_available_mailboxes` rather than naming a single account from the login response.",
|
|
1832
|
+
inputSchema: {
|
|
1833
|
+
reasoning: z.string().describe(
|
|
1834
|
+
"Why do I need to log in to Adeu Cloud? State this reason before any other parameter."
|
|
1835
|
+
)
|
|
1836
|
+
}
|
|
1737
1837
|
},
|
|
1738
1838
|
async () => {
|
|
1739
1839
|
try {
|
|
@@ -1745,7 +1845,14 @@ if (!isDocxOnly) {
|
|
|
1745
1845
|
);
|
|
1746
1846
|
server.registerTool(
|
|
1747
1847
|
"logout_of_adeu_cloud",
|
|
1748
|
-
{
|
|
1848
|
+
{
|
|
1849
|
+
description: "Logs out of the Adeu Cloud backend.",
|
|
1850
|
+
inputSchema: {
|
|
1851
|
+
reasoning: z.string().describe(
|
|
1852
|
+
"Why do I need to log out of Adeu Cloud? State this reason before any other parameter."
|
|
1853
|
+
)
|
|
1854
|
+
}
|
|
1855
|
+
},
|
|
1749
1856
|
async () => {
|
|
1750
1857
|
try {
|
|
1751
1858
|
return await logout_of_adeu_cloud();
|
|
@@ -1759,6 +1866,9 @@ if (!isDocxOnly) {
|
|
|
1759
1866
|
{
|
|
1760
1867
|
description: "Creates an email draft in the user's native draft box (Outlook Drafts or Gmail Drafts).\n\nTWO MODES:\n1. Reply mode: pass `reply_to_email_id` to create a threaded reply. The draft inherits subject, recipients, and threading headers from the original \u2014 do NOT pass `subject` or `to_recipients`.\n2. New email mode: omit `reply_to_email_id` and pass BOTH `subject` and `to_recipients`.\n\n`reply_to_email_id` accepts the same ID formats as search_and_fetch_emails (`msg_*` short IDs, `adeu_*` references, or raw provider IDs). Short IDs are validated against the local cache before the call; stale ones fail fast with a clear error telling you to re-search.\n\n`body_markdown` is converted server-side to styled HTML with inlined CSS for email-client compatibility. Write the body in plain Markdown \u2014 do not pre-render HTML.\n\n`attachment_paths` takes absolute file paths on the user's local disk and uploads them with the draft. Useful right after search_and_fetch_emails downloaded attachments \u2014 those local paths can be passed directly here.",
|
|
1761
1868
|
inputSchema: {
|
|
1869
|
+
reasoning: z.string().describe(
|
|
1870
|
+
"Why do I need to create this email draft? State this reason before any other parameter."
|
|
1871
|
+
),
|
|
1762
1872
|
body_markdown: z.string(),
|
|
1763
1873
|
reply_to_email_id: z.string().optional(),
|
|
1764
1874
|
subject: z.string().optional(),
|
|
@@ -1781,7 +1891,11 @@ if (!isDocxOnly) {
|
|
|
1781
1891
|
"list_available_mailboxes",
|
|
1782
1892
|
{
|
|
1783
1893
|
description: "Lists all personal and shared/delegated mailboxes the authenticated Adeu user has access to, across ALL of their linked provider accounts. Returns each mailbox's `email_address`, `display_name`, auto-processing settings, and write-back preference.\n\nThis is the right tool to answer 'which accounts/mailboxes am I logged into?' \u2014 Adeu login is user-level, so a single MCP session can see every mailbox listed here regardless of which provider account was used for SSO.\n\nCall this FIRST when the user names a specific mailbox or shared inbox, to resolve the canonical `email_address`. Then pass that address as `mailbox_address` to `search_and_fetch_emails` or `create_email_draft` to scope the operation. Omitting `mailbox_address` on those tools targets the user's primary personal mailbox.",
|
|
1784
|
-
inputSchema: {
|
|
1894
|
+
inputSchema: {
|
|
1895
|
+
reasoning: z.string().describe(
|
|
1896
|
+
"Why do I need to list available mailboxes? State this reason before any other parameter."
|
|
1897
|
+
)
|
|
1898
|
+
}
|
|
1785
1899
|
},
|
|
1786
1900
|
async () => {
|
|
1787
1901
|
try {
|
|
@@ -1857,8 +1971,8 @@ ${stats.skipped_details.join("\n")}`;
|
|
|
1857
1971
|
async function main() {
|
|
1858
1972
|
const transport = new StdioServerTransport();
|
|
1859
1973
|
await server.connect(transport);
|
|
1860
|
-
const gitSha2 = "
|
|
1861
|
-
const buildTs = "2026-
|
|
1974
|
+
const gitSha2 = "263a4e4";
|
|
1975
|
+
const buildTs = "2026-07-03T13:50:27.555Z";
|
|
1862
1976
|
console.error(
|
|
1863
1977
|
`Adeu MCP Server (Node.js Engine: ${identifyEngine()}) running on stdio build=${gitSha2}@${buildTs}`
|
|
1864
1978
|
);
|