@adeu/mcp-server 1.14.0 → 1.15.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.
- package/dist/index.js +164 -23
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/formatter.qa.test.ts +71 -0
- package/src/formatter.test.ts +12 -6
- package/src/index.ts +53 -15
- package/src/response-builders.ts +151 -0
package/dist/index.js
CHANGED
|
@@ -200,6 +200,128 @@ ${ui_markdown}`;
|
|
|
200
200
|
}
|
|
201
201
|
};
|
|
202
202
|
}
|
|
203
|
+
function build_search_response(text, search_query, search_regex, search_case_sensitive, page, file_path) {
|
|
204
|
+
const [body] = split_structural_appendix(text);
|
|
205
|
+
const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
206
|
+
const flags = search_case_sensitive ? "g" : "gi";
|
|
207
|
+
const patternStr = search_regex ? search_query : escapeRegExp(search_query);
|
|
208
|
+
let regex;
|
|
209
|
+
try {
|
|
210
|
+
regex = new RegExp(patternStr, flags);
|
|
211
|
+
} catch (e) {
|
|
212
|
+
throw new Error(`Invalid regex pattern: ${e.message}`);
|
|
213
|
+
}
|
|
214
|
+
const matches = Array.from(body.matchAll(regex));
|
|
215
|
+
if (matches.length === 0) {
|
|
216
|
+
const ui_markdown2 = `> **Search Results** \u2014 No matches found for query \`${search_query}\` in \`${basename(file_path)}\`.
|
|
217
|
+
|
|
218
|
+
Verify your search spelling, or try setting \`search_case_sensitive\` to false or enabling \`search_regex\` if you used pattern wildcards.`;
|
|
219
|
+
const llm_content2 = `> **File Path:** \`${resolve(file_path)}\`
|
|
220
|
+
|
|
221
|
+
${ui_markdown2}`;
|
|
222
|
+
return {
|
|
223
|
+
content: [{ type: "text", text: llm_content2 }],
|
|
224
|
+
structuredContent: {
|
|
225
|
+
markdown: ui_markdown2,
|
|
226
|
+
title: `Search: ${basename(file_path)}`,
|
|
227
|
+
file_path: resolve(file_path)
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const pag_res = paginate(body, "");
|
|
232
|
+
const page_offsets = pag_res.body_page_offsets;
|
|
233
|
+
const total_matches = matches.length;
|
|
234
|
+
const total_pages = Math.ceil(total_matches / 10);
|
|
235
|
+
let start_idx = 0;
|
|
236
|
+
let end_idx = total_matches;
|
|
237
|
+
let page_text = "all";
|
|
238
|
+
const pageStr = String(page).toLowerCase();
|
|
239
|
+
if (pageStr !== "all") {
|
|
240
|
+
const page_num = parseInt(pageStr, 10);
|
|
241
|
+
if (isNaN(page_num) || page_num < 1 || page_num > total_pages) {
|
|
242
|
+
throw new Error(`Page ${page} out of range (search has ${total_pages} pages).`);
|
|
243
|
+
}
|
|
244
|
+
start_idx = (page_num - 1) * 10;
|
|
245
|
+
end_idx = Math.min(start_idx + 10, total_matches);
|
|
246
|
+
page_text = `${page_num} of ${total_pages}`;
|
|
247
|
+
}
|
|
248
|
+
const page_matches = matches.slice(start_idx, end_idx);
|
|
249
|
+
const ui_parts = [
|
|
250
|
+
`> **Search Results** \u2014 Found ${total_matches} matches for query \`${search_query}\` in \`${basename(file_path)}\`.`
|
|
251
|
+
];
|
|
252
|
+
if (total_pages > 1 && pageStr !== "all") {
|
|
253
|
+
const nextPage = parseInt(pageStr, 10) + 1;
|
|
254
|
+
ui_parts.push(`> Showing page ${page_text} (matches ${start_idx + 1}-${end_idx}). To see more matches, call \`read_docx\` with \`search_query='${search_query}'\`, \`search_regex=${search_regex ? "true" : "false"}\`, and \`page=${nextPage}\`.`);
|
|
255
|
+
}
|
|
256
|
+
const occurrences_map = {};
|
|
257
|
+
for (const m of matches) {
|
|
258
|
+
const matched_str = m[0];
|
|
259
|
+
occurrences_map[matched_str] = (occurrences_map[matched_str] || 0) + 1;
|
|
260
|
+
}
|
|
261
|
+
function get_heading(idx, txt) {
|
|
262
|
+
const txtBefore = txt.substring(0, idx);
|
|
263
|
+
const lines = txtBefore.split("\n");
|
|
264
|
+
const path = [];
|
|
265
|
+
let current_level = 999;
|
|
266
|
+
for (let i2 = lines.length - 1; i2 >= 0; i2--) {
|
|
267
|
+
const line = lines[i2];
|
|
268
|
+
const m = line.match(/^(#{1,6})\s+(.*)/);
|
|
269
|
+
if (m) {
|
|
270
|
+
const level = m[1].length;
|
|
271
|
+
if (level < current_level) {
|
|
272
|
+
let cleanHeading = m[2].replace(/\*\*|__|[*_]/g, "").replace(/\{#[^}]+\}/g, "").trim();
|
|
273
|
+
if (cleanHeading.length > 80) {
|
|
274
|
+
cleanHeading = cleanHeading.substring(0, 80) + "...";
|
|
275
|
+
}
|
|
276
|
+
path.unshift(cleanHeading);
|
|
277
|
+
current_level = level;
|
|
278
|
+
if (level === 1) break;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return path.join(" > ");
|
|
283
|
+
}
|
|
284
|
+
let i = start_idx + 1;
|
|
285
|
+
for (const m of page_matches) {
|
|
286
|
+
const matched_str = m[0];
|
|
287
|
+
const m_start = m.index;
|
|
288
|
+
const m_end = m_start + matched_str.length;
|
|
289
|
+
let p_num = 1;
|
|
290
|
+
for (let j = 0; j < page_offsets.length; j++) {
|
|
291
|
+
if (m_start >= page_offsets[j]) {
|
|
292
|
+
p_num = j + 1;
|
|
293
|
+
} else {
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const snippet_start = Math.max(0, m_start - 100);
|
|
298
|
+
const snippet_end = Math.min(body.length, m_end + 100);
|
|
299
|
+
const snippet = body.substring(snippet_start, m_start) + `**${matched_str}**` + body.substring(m_end, snippet_end);
|
|
300
|
+
const snippet_lines = snippet.split("\n").filter((line) => line.trim().length > 0).map((line) => `> ${line}`).join("\n");
|
|
301
|
+
ui_parts.push("---");
|
|
302
|
+
ui_parts.push(`### Match ${i} (p${p_num})`);
|
|
303
|
+
const h_path = get_heading(m_start, body);
|
|
304
|
+
if (h_path) {
|
|
305
|
+
ui_parts.push(`**Path:** \`${h_path}\``);
|
|
306
|
+
}
|
|
307
|
+
const count = occurrences_map[matched_str];
|
|
308
|
+
ui_parts.push(snippet_lines);
|
|
309
|
+
ui_parts.push(`*Occurrences:* This exact phrasing appears ${count} time${count !== 1 ? "s" : ""} in the document.`);
|
|
310
|
+
i++;
|
|
311
|
+
}
|
|
312
|
+
const ui_markdown = ui_parts.join("\n\n");
|
|
313
|
+
const llm_content = `> **File Path:** \`${resolve(file_path)}\`
|
|
314
|
+
|
|
315
|
+
${ui_markdown}`;
|
|
316
|
+
return {
|
|
317
|
+
content: [{ type: "text", text: llm_content }],
|
|
318
|
+
structuredContent: {
|
|
319
|
+
markdown: ui_markdown,
|
|
320
|
+
title: `Search: ${basename(file_path)}`,
|
|
321
|
+
file_path: resolve(file_path)
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
}
|
|
203
325
|
|
|
204
326
|
// src/desktop-auth.ts
|
|
205
327
|
import { createServer } from "http";
|
|
@@ -1089,8 +1211,8 @@ var READ_DOCX_TAIL = "Modes:\n- 'full' (default): paginated body content. Use pa
|
|
|
1089
1211
|
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";
|
|
1090
1212
|
var PROCESS_BATCH_OPERATIONS_DESC = "Each item in `changes` must specify a `type`:\n1. 'modify': Search-and-replace. `target_text` must uniquely match \u2014 include surrounding context if the phrase is ambiguous. `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.\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.\n\n`author_name` is used for attribution on all tracked changes and comments, in both disk and Live Word modes.";
|
|
1091
1213
|
var DIFF_DOCX_DESC = "Compares two DOCX files and returns a unified diff of their text content. Useful for analyzing differences between versions before editing.";
|
|
1092
|
-
var gitSha = "
|
|
1093
|
-
var packageVersion = "1.
|
|
1214
|
+
var gitSha = "fa47c47";
|
|
1215
|
+
var packageVersion = "1.15.0";
|
|
1094
1216
|
var buildTag = ` [Adeu v${packageVersion}+${gitSha}]`;
|
|
1095
1217
|
var args = process.argv.slice(2);
|
|
1096
1218
|
var scopeIdx = args.indexOf("--scope");
|
|
@@ -1193,9 +1315,12 @@ registerAppTool(
|
|
|
1193
1315
|
mode: z.enum(["full", "outline", "appendix"]).default("full").describe(
|
|
1194
1316
|
"'full' returns body content. 'outline' returns a structural heading map. 'appendix' returns defined terms."
|
|
1195
1317
|
),
|
|
1196
|
-
page: z.number().default(1).describe("Page number (1-indexed) for mode='full'
|
|
1318
|
+
page: z.union([z.number(), z.literal("all")]).default(1).describe("Page number (1-indexed) for mode='full', or 'all' to return unpaginated."),
|
|
1197
1319
|
outline_max_level: z.number().default(2).describe("For mode='outline' only: cap on heading depth."),
|
|
1198
|
-
outline_verbose: z.boolean().default(false).describe("For mode='outline' only: includes metadata.")
|
|
1320
|
+
outline_verbose: z.boolean().default(false).describe("For mode='outline' only: includes metadata."),
|
|
1321
|
+
search_query: z.string().optional().describe("The substring or regex pattern to search for. When provided, filters results to matching paragraphs."),
|
|
1322
|
+
search_regex: z.boolean().default(false).describe("Set to true to interpret search_query as a regular expression."),
|
|
1323
|
+
search_case_sensitive: z.boolean().default(true).describe("Set to false to perform case-insensitive matching.")
|
|
1199
1324
|
}),
|
|
1200
1325
|
_meta: { ui: { resourceUri: MARKDOWN_UI_URI } }
|
|
1201
1326
|
},
|
|
@@ -1205,7 +1330,10 @@ registerAppTool(
|
|
|
1205
1330
|
mode,
|
|
1206
1331
|
page,
|
|
1207
1332
|
outline_max_level,
|
|
1208
|
-
outline_verbose
|
|
1333
|
+
outline_verbose,
|
|
1334
|
+
search_query,
|
|
1335
|
+
search_regex,
|
|
1336
|
+
search_case_sensitive
|
|
1209
1337
|
}) => {
|
|
1210
1338
|
try {
|
|
1211
1339
|
const buf = readFileBytesOrThrow(file_path);
|
|
@@ -1223,11 +1351,15 @@ registerAppTool(
|
|
|
1223
1351
|
return res2;
|
|
1224
1352
|
}
|
|
1225
1353
|
const text = await extractTextFromBuffer(buf, clean_view);
|
|
1354
|
+
if (search_query !== void 0 && search_query !== null) {
|
|
1355
|
+
const res2 = build_search_response(text, search_query, search_regex, search_case_sensitive, page, file_path);
|
|
1356
|
+
return res2;
|
|
1357
|
+
}
|
|
1226
1358
|
if (mode === "appendix") {
|
|
1227
|
-
const res2 = build_appendix_response(text, page, file_path);
|
|
1359
|
+
const res2 = build_appendix_response(text, typeof page === "number" ? page : parseInt(page, 10) || 1, file_path);
|
|
1228
1360
|
return res2;
|
|
1229
1361
|
}
|
|
1230
|
-
const res = build_paginated_response(text, page, file_path);
|
|
1362
|
+
const res = build_paginated_response(text, typeof page === "number" ? page : parseInt(page, 10) || 1, file_path);
|
|
1231
1363
|
return res;
|
|
1232
1364
|
} catch (e) {
|
|
1233
1365
|
return {
|
|
@@ -1575,52 +1707,61 @@ function formatBatchResult(stats, outPath, dry_run) {
|
|
|
1575
1707
|
res = `Batch complete. Saved to: ${outPath}
|
|
1576
1708
|
`;
|
|
1577
1709
|
}
|
|
1710
|
+
const total_occurrences = stats.edits ? stats.edits.reduce((acc, e) => acc + (e.status === "applied" ? e.occurrences_modified || 1 : 0), 0) : 0;
|
|
1711
|
+
const occ_text = total_occurrences > stats.edits_applied ? ` (${total_occurrences} occurrences)` : "";
|
|
1578
1712
|
res += `Actions: ${stats.actions_applied} applied, ${stats.actions_skipped} skipped.
|
|
1579
1713
|
`;
|
|
1580
|
-
res += `Edits: ${stats.edits_applied} applied, ${stats.edits_skipped} skipped.
|
|
1714
|
+
res += `Edits: ${stats.edits_applied} applied${occ_text}, ${stats.edits_skipped} skipped.
|
|
1581
1715
|
`;
|
|
1582
1716
|
if (stats.edits && stats.edits.length > 0) {
|
|
1583
1717
|
res += "\nDetailed Edit Reports:\n";
|
|
1584
1718
|
for (let i = 0; i < stats.edits.length; i++) {
|
|
1585
1719
|
const report = stats.edits[i];
|
|
1586
1720
|
const status_indicator = report.status === "applied" ? "\u2705 [applied]" : "\u274C [failed]";
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
res += ` Target: '${report.target_text}'
|
|
1721
|
+
const pagesStr = report.pages && report.pages.length > 0 ? ` (p${report.pages.join(", p")})` : "";
|
|
1722
|
+
res += `### Edit ${i + 1} ${status_indicator}${pagesStr}
|
|
1590
1723
|
`;
|
|
1591
|
-
|
|
1724
|
+
if (report.heading_path) {
|
|
1725
|
+
res += `**Path:** \`${report.heading_path}\`
|
|
1592
1726
|
`;
|
|
1593
|
-
|
|
1594
|
-
|
|
1727
|
+
}
|
|
1728
|
+
if (report.match_mode) {
|
|
1729
|
+
const occ = report.occurrences_modified || (report.status === "applied" ? 1 : 0);
|
|
1730
|
+
res += `**Mode:** \`${report.match_mode}\` (${occ} occurrence${occ !== 1 ? "s" : ""} modified)
|
|
1595
1731
|
`;
|
|
1596
1732
|
}
|
|
1597
1733
|
if (report.error) {
|
|
1598
|
-
res +=
|
|
1734
|
+
res += `*Error:* ${report.error}
|
|
1735
|
+
`;
|
|
1736
|
+
}
|
|
1737
|
+
if (report.warning) {
|
|
1738
|
+
res += `*Warning:* ${report.warning}
|
|
1599
1739
|
`;
|
|
1600
1740
|
}
|
|
1601
1741
|
if (report.critic_markup) {
|
|
1602
|
-
res +=
|
|
1742
|
+
res += `*Preview (CriticMarkup):*
|
|
1743
|
+
> ${report.critic_markup.split("\\n").join("\\n> ")}
|
|
1603
1744
|
`;
|
|
1604
1745
|
}
|
|
1605
1746
|
if (report.clean_text) {
|
|
1606
|
-
res +=
|
|
1747
|
+
res += `*Preview (Clean):*
|
|
1748
|
+
> ${report.clean_text.split("\\n").join("\\n> ")}
|
|
1607
1749
|
`;
|
|
1608
1750
|
}
|
|
1751
|
+
res += "\n";
|
|
1609
1752
|
}
|
|
1610
1753
|
}
|
|
1611
1754
|
if (stats.skipped_details && stats.skipped_details.length > 0) {
|
|
1612
|
-
res += `
|
|
1613
|
-
|
|
1614
|
-
Skipped Details:
|
|
1755
|
+
res += `Skipped Details:
|
|
1615
1756
|
${stats.skipped_details.join("\n")}`;
|
|
1616
1757
|
}
|
|
1617
|
-
return res;
|
|
1758
|
+
return res.trim();
|
|
1618
1759
|
}
|
|
1619
1760
|
async function main() {
|
|
1620
1761
|
const transport = new StdioServerTransport();
|
|
1621
1762
|
await server.connect(transport);
|
|
1622
|
-
const gitSha2 = "
|
|
1623
|
-
const buildTs = "2026-06-
|
|
1763
|
+
const gitSha2 = "fa47c47";
|
|
1764
|
+
const buildTs = "2026-06-25T10:39:47.565Z";
|
|
1624
1765
|
console.error(
|
|
1625
1766
|
`Adeu MCP Server (Node.js Engine: ${identifyEngine()}) running on stdio build=${gitSha2}@${buildTs}`
|
|
1626
1767
|
);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/response-builders.ts","../src/desktop-auth.ts","../src/shared.ts","../src/tools/auth.ts","../src/tools/email.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { basename, resolve, extname, dirname, join } from \"node:path\";\nimport { z } from \"zod\";\nimport {\n registerAppTool as origRegisterAppTool,\n registerAppResource,\n RESOURCE_MIME_TYPE,\n} from \"@modelcontextprotocol/ext-apps/server\";\nimport fs from \"node:fs\";\nimport {\n identifyEngine,\n extractTextFromBuffer,\n _extractTextFromDoc,\n DocumentObject,\n RedlineEngine,\n BatchValidationError,\n create_word_patch_diff,\n finalize_document,\n} from \"@adeu/core\";\n\nimport {\n build_paginated_response,\n build_outline_response,\n build_appendix_response,\n} from \"./response-builders.js\";\n\nimport { login_to_adeu_cloud, logout_of_adeu_cloud } from \"./tools/auth.js\";\nimport {\n search_and_fetch_emails,\n create_email_draft,\n list_available_mailboxes,\n} from \"./tools/email.js\";\nimport { MARKDOWN_UI_URI, EMAIL_UI_URI } from \"./shared.js\";\n\nfunction readFileBytesOrThrow(filePath: string): Buffer {\n try {\n return readFileSync(filePath);\n } catch (err: any) {\n if (err.code === \"ENOENT\") {\n throw new Error(\n `File not found: ${filePath}.\\n` +\n `If you are running in a sandboxed/containerized environment (such as Claude Desktop or another containerized client), ` +\n `the host application or MCP server may not have direct access to your local workspace files.\\n` +\n `You can resolve this by installing and running the local 'adeu' CLI tool directly within your environment.\\n` +\n `Here is how the MCP tools map to their CLI equivalents:\\n` +\n `- read_docx -> adeu extract ${filePath}\\n` +\n `- process_document_batch -> adeu apply ${filePath}\\n` +\n `- diff_docx_files -> adeu diff ${filePath} <modified_path>\\n` +\n `- accept_all_changes -> adeu accept-all ${filePath}\\n\\n` +\n `To run the local tool, install it via:\\n` +\n ` uv tool install adeu\\n` +\n `and run the mapped CLI command directly in your terminal.`\n );\n }\n throw err;\n }\n}\n\n// --- Asset Loaders for UI ---\nconst DIST_DIR = import.meta.dirname;\n\nfunction getAssetContent(\n folder: \"templates\" | \"assets\",\n filename: string,\n fallbackMessage: string,\n): string {\n const filePath = join(DIST_DIR, folder, filename);\n if (existsSync(filePath)) {\n return readFileSync(filePath, \"utf-8\");\n }\n return fallbackMessage;\n}\n\n// --- Tool Description Constants ---\nconst READ_DOCX_COMMON_DESC =\n \"Reads a DOCX file. Returns text with inline CriticMarkup for Tracked Changes and Comments: {++inserted++}, {--deleted--}, {==highlighted==}{>>comment<<}. Set clean_view=True for the finalized 'Accepted' text without markup.\\n\\n\";\nconst READ_DOCX_TAIL =\n \"Modes:\\n- 'full' (default): paginated body content. Use page=N to navigate.\\n- 'outline': heading map only — start here for large docs to plan targeted reads. Defaults to L1-L2 headings; pass outline_max_level=3-6 to see deeper structure.\\n- 'appendix': defined terms, anchors, and cross-reference targets. Consult before editing legal/technical docs to avoid breaking references.\";\n\nconst PROCESS_BATCH_COMMON_DESC =\n \"Applies a batch of edits and review actions to a DOCX.\\n\\nAll changes evaluate against the ORIGINAL document state — 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\";\nconst PROCESS_BATCH_OPERATIONS_DESC =\n \"Each item in `changes` must specify a `type`:\\n1. 'modify': Search-and-replace. `target_text` must uniquely match — include surrounding context if the phrase is ambiguous. `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 — use the `comment` parameter for comments.\\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 — 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 — do not reuse IDs from earlier in the conversation.\\n\\n`author_name` is used for attribution on all tracked changes and comments, in both disk and Live Word modes.\";\n\nconst DIFF_DOCX_DESC =\n \"Compares two DOCX files and returns a unified diff of their text content. Useful for analyzing differences between versions before editing.\";\n\nconst gitSha = process.env.GIT_SHA || \"unknown\";\nconst packageVersion = process.env.PACKAGE_VERSION || \"unknown\";\nconst buildTag = ` [Adeu v${packageVersion}+${gitSha}]`;\n\n// --- Scope Configuration ---\nconst args = process.argv.slice(2);\nconst scopeIdx = args.indexOf(\"--scope\");\nconst requestedScope = (scopeIdx !== -1 ? args[scopeIdx + 1] : \"all\").toLowerCase();\nconst isDocxOnly = requestedScope === \"docx\";\n\n// --- Server Setup ---\nconst server = new McpServer({\n name: \"adeu-redlining-service\",\n version: packageVersion,\n});\n\n// Wrap server.registerTool to inject buildTag into descriptions\nconst originalRegisterTool = server.registerTool.bind(server);\nserver.registerTool = (name: string, schema: any, handler?: any) => {\n if (schema && typeof schema === \"object\") {\n if (schema.description) {\n schema.description = schema.description.trim() + buildTag;\n }\n }\n return originalRegisterTool(name, schema, handler);\n};\n\n// Wrap registerAppTool to inject buildTag into descriptions\nconst registerAppTool: typeof origRegisterAppTool = (mcpServer, name, schema, handler) => {\n if (schema && typeof schema === \"object\") {\n if (schema.description) {\n schema.description = schema.description.trim() + buildTag;\n }\n }\n return origRegisterAppTool(mcpServer, name, schema, handler);\n};\n\n// Common CSP allowing Google Fonts used by Adeu UI templates\nconst UI_CSP = {\n connectDomains: [\"https://fonts.googleapis.com\", \"https://fonts.gstatic.com\"],\n resourceDomains: [\n \"https://fonts.googleapis.com\",\n \"https://fonts.gstatic.com\",\n ],\n};\n\n// ==========================================\n// 1. UI RESOURCES\n// ==========================================\n\nregisterAppResource(\n server,\n MARKDOWN_UI_URI,\n MARKDOWN_UI_URI,\n { mimeType: RESOURCE_MIME_TYPE, description: \"Adeu Markdown Viewer UI\" },\n async () => {\n let html = getAssetContent(\n \"templates\",\n \"markdown_ui.html\",\n \"<html><body>UI Template Not Found</body></html>\",\n );\n const markedJs = getAssetContent(\n \"assets\",\n \"marked.min.js\",\n \"window.__MARKED_ERROR = 'marked.min.js not found';\",\n );\n const svg = getAssetContent(\"assets\", \"adeu.svg\", \"\");\n\n html = html\n .replace(\"[[marked_js_code | safe]]\", markedJs)\n .replace(\"[[ adeu_svg_code ]]\", svg);\n\n return {\n contents: [\n {\n uri: MARKDOWN_UI_URI,\n mimeType: RESOURCE_MIME_TYPE,\n text: html,\n _meta: { ui: { csp: UI_CSP } },\n },\n ],\n };\n },\n);\n\nregisterAppResource(\n server,\n EMAIL_UI_URI,\n EMAIL_UI_URI,\n { mimeType: RESOURCE_MIME_TYPE, description: \"Adeu Email Viewer UI\" },\n async () => {\n let html = getAssetContent(\n \"templates\",\n \"email_ui.html\",\n \"<html><body>UI Template Not Found</body></html>\",\n );\n const svg = getAssetContent(\"assets\", \"adeu.svg\", \"\");\n\n html = html.replace(\"[[ adeu_svg_code ]]\", svg);\n\n return {\n contents: [\n {\n uri: EMAIL_UI_URI,\n mimeType: RESOURCE_MIME_TYPE,\n text: html,\n _meta: { ui: { csp: UI_CSP } },\n },\n ],\n };\n },\n);\n\n// ==========================================\n// 2. UI-ENABLED TOOLS\n// ==========================================\nregisterAppTool(\n server,\n \"read_docx\",\n {\n title: \"Read DOCX\",\n description: READ_DOCX_COMMON_DESC + READ_DOCX_TAIL,\n inputSchema: z.object({\n file_path: z.string().describe(\"Absolute path to the DOCX file.\"),\n clean_view: z\n .boolean()\n .default(false)\n .describe(\n \"If False (default), returns the 'Raw' text with inline CriticMarkup. If True, returns 'Accepted' text.\",\n ),\n mode: z\n .enum([\"full\", \"outline\", \"appendix\"])\n .default(\"full\")\n .describe(\n \"'full' returns body content. 'outline' returns a structural heading map. 'appendix' returns defined terms.\",\n ),\n page: z\n .number()\n .default(1)\n .describe(\"Page number (1-indexed) for mode='full'. Defaults to 1.\"),\n outline_max_level: z\n .number()\n .default(2)\n .describe(\"For mode='outline' only: cap on heading depth.\"),\n outline_verbose: z\n .boolean()\n .default(false)\n .describe(\"For mode='outline' only: includes metadata.\"),\n }),\n _meta: { ui: { resourceUri: MARKDOWN_UI_URI } },\n },\n async ({\n file_path,\n clean_view,\n mode,\n page,\n outline_max_level,\n outline_verbose,\n }) => {\n try {\n const buf = readFileBytesOrThrow(file_path);\n\n if (mode === \"outline\") {\n const doc = await DocumentObject.load(buf);\n const extract_res = _extractTextFromDoc(doc, clean_view, true, true) as {\n text: string;\n paragraph_offsets: Map<any, [number, number]>;\n };\n const res = build_outline_response(\n doc,\n extract_res.text,\n file_path,\n outline_max_level,\n outline_verbose,\n extract_res.paragraph_offsets,\n );\n return res as any;\n }\n\n const text = await extractTextFromBuffer(buf, clean_view);\n if (mode === \"appendix\") {\n const res = build_appendix_response(text, page, file_path);\n return res as any;\n }\n const res = build_paginated_response(text, page, file_path);\n return res as any;\n } catch (e: any) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `Error executing tool read_docx: ${e.message}`,\n },\n ],\n };\n }\n },\n);\n\n// ==========================================\n// 3. HEADLESS TOOLS (No UI)\n// ==========================================\n\nserver.registerTool(\n \"process_document_batch\",\n {\n description: PROCESS_BATCH_COMMON_DESC + PROCESS_BATCH_OPERATIONS_DESC,\n inputSchema: {\n original_docx_path: z\n .string()\n .describe(\"Absolute path to the source file.\"),\n author_name: z\n .string()\n .describe(\"Name to appear in Track Changes (e.g., 'Reviewer AI').\"),\n changes: z\n .array(z.any())\n .describe(\"List of changes to apply. Each change must specify 'type'.\"),\n output_path: z.string().optional().describe(\"Optional output path.\"),\n dry_run: z\n .boolean()\n .optional()\n .default(false)\n .describe(\n \"If True, simulates the changes and returns a detailed preview report without modifying any files.\",\n ),\n },\n },\n async ({\n original_docx_path,\n author_name,\n changes,\n output_path,\n dry_run,\n }) => {\n try {\n if (!author_name || !author_name.trim())\n return {\n content: [\n { type: \"text\", text: \"Error: author_name cannot be empty.\" },\n ],\n };\n if (!changes || changes.length === 0)\n return {\n content: [{ type: \"text\", text: \"Error: No changes provided.\" }],\n };\n\n // Defensive sanitization at the MCP boundary: some LLM clients\n // \"double-serialize\" nested arrays, delivering each element of `changes`\n // as a JSON string instead of an object. The core engine also guards\n // against this, but we normalize here too so the tool layer never hands\n // raw string primitives downstream regardless of the engine version\n // bundled. Genuine objects and unparseable strings pass through\n // untouched so validation surfaces a clear error rather than crashing.\n const sanitizedChanges = changes.map((item: any) => {\n if (typeof item === \"string\") {\n try {\n const parsed = JSON.parse(item);\n if (parsed !== null && typeof parsed === \"object\") {\n return parsed;\n }\n return item;\n } catch {\n return item;\n }\n }\n return item;\n });\n\n let outPath = output_path;\n if (!outPath) {\n const ext = extname(original_docx_path);\n const base = basename(original_docx_path, ext);\n const dir = dirname(original_docx_path);\n outPath = resolve(dir, `${base}_processed${ext}`);\n }\n\n const buf = readFileBytesOrThrow(original_docx_path);\n const doc = await DocumentObject.load(buf);\n const engine = new RedlineEngine(doc, author_name);\n\n let stats;\n try {\n stats = engine.process_batch(sanitizedChanges, dry_run);\n } catch (e: any) {\n if (e instanceof BatchValidationError) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `Batch rejected. Some edits failed validation:\\n\\n${e.errors.join(\"\\n\\n\")}`,\n },\n ],\n };\n }\n throw e;\n }\n\n if (!dry_run) {\n const outBuf = await doc.save();\n fs.writeFileSync(outPath, outBuf);\n }\n\n const res = formatBatchResult(stats, outPath, !!dry_run);\n return { content: [{ type: \"text\", text: res }] };\n } catch (e: any) {\n return {\n isError: true,\n content: [{ type: \"text\", text: `Error: ${e.message}` }],\n };\n }\n },\n);\n\nserver.registerTool(\n \"accept_all_changes\",\n {\n description:\n \"Accepts all tracked changes and removes all comments in a single operation.\",\n inputSchema: {\n docx_path: z.string().describe(\"Absolute path to the DOCX file.\"),\n output_path: z.string().optional().describe(\"Optional output path.\"),\n },\n },\n async ({ docx_path, output_path }) => {\n try {\n let outPath = output_path;\n if (!outPath) {\n const ext = extname(docx_path);\n const base = basename(docx_path, ext);\n const dir = dirname(docx_path);\n outPath = resolve(dir, `${base}_clean${ext}`);\n }\n\n const buf = readFileBytesOrThrow(docx_path);\n const doc = await DocumentObject.load(buf);\n const engine = new RedlineEngine(doc);\n\n engine.accept_all_revisions();\n\n const outBuf = await doc.save();\n\n fs.writeFileSync(outPath, outBuf);\n\n return {\n content: [\n { type: \"text\", text: `Accepted all changes. Saved to: ${outPath}` },\n ],\n };\n } catch (e: any) {\n return {\n isError: true,\n content: [{ type: \"text\", text: `Error: ${e.message}` }],\n };\n }\n },\n);\n\nserver.registerTool(\n \"diff_docx_files\",\n {\n description: DIFF_DOCX_DESC,\n inputSchema: {\n original_path: z\n .string()\n .describe(\"Absolute path to the baseline DOCX file.\"),\n modified_path: z\n .string()\n .describe(\"Absolute path to the modified DOCX file.\"),\n compare_clean: z\n .boolean()\n .default(true)\n .describe(\n \"If True, compares 'Accepted' state. If False, compares raw text.\",\n ),\n },\n },\n async ({ original_path, modified_path, compare_clean }) => {\n try {\n const origBuf = readFileBytesOrThrow(original_path);\n const modBuf = readFileBytesOrThrow(modified_path);\n\n const origText = await extractTextFromBuffer(origBuf, compare_clean);\n const modText = await extractTextFromBuffer(modBuf, compare_clean);\n\n const diff = create_word_patch_diff(\n origText,\n modText,\n basename(original_path),\n basename(modified_path),\n );\n\n return {\n content: [{ type: \"text\", text: diff || \"No differences found.\" }],\n };\n } catch (e: any) {\n return {\n isError: true,\n content: [{ type: \"text\", text: `Error: ${e.message}` }],\n };\n }\n },\n);\n\nserver.registerTool(\n \"finalize_document\",\n {\n description:\n \"Prepares a document for external distribution or e-signature.\",\n inputSchema: {\n file_path: z.string().describe(\"Absolute path to the DOCX file.\"),\n output_path: z.string().optional().describe(\"Optional output path.\"),\n sanitize_mode: z\n .enum([\"full\", \"keep-markup\"])\n .optional()\n .describe(\"full removes all markup, keep-markup redacts metadata.\"),\n accept_all: z\n .boolean()\n .optional()\n .describe(\n \"If true, auto-accepts all unresolved track changes before finalizing.\",\n ),\n protection_mode: z\n .enum([\"read_only\", \"encrypt\"])\n .optional()\n .describe(\"Native OOXML document locking.\"),\n password: z.string().optional().describe(\"Ignored in this environment.\"),\n author: z\n .string()\n .optional()\n .describe(\"Replace all remaining markup authorship with this name.\"),\n export_pdf: z\n .boolean()\n .optional()\n .describe(\"Ignored in this environment.\"),\n },\n },\n async ({\n file_path,\n output_path,\n sanitize_mode,\n accept_all,\n protection_mode,\n author,\n export_pdf,\n }) => {\n try {\n let outPath = output_path;\n if (!outPath) {\n const ext = extname(file_path);\n const base = basename(file_path, ext);\n const dir = dirname(file_path);\n outPath = resolve(dir, `${base}_final${ext}`);\n }\n\n const buf = readFileBytesOrThrow(file_path);\n const doc = await DocumentObject.load(buf);\n\n const result = await finalize_document(doc, {\n filename: basename(file_path),\n sanitize_mode: (sanitize_mode as any) || \"full\",\n accept_all: accept_all as boolean,\n protection_mode: protection_mode as any,\n author: author as string,\n export_pdf: export_pdf as boolean,\n });\n\n fs.writeFileSync(outPath, result.outBuffer!);\n\n return {\n content: [\n {\n type: \"text\",\n text: `Saved to: ${outPath}\\n\\n${result.reportText}`,\n },\n ],\n };\n } catch (e: any) {\n return {\n isError: true,\n content: [{ type: \"text\", text: `Error: ${e.message}` }],\n };\n }\n },\n);\n\nif (!isDocxOnly) {\nregisterAppTool(\n server,\n \"search_and_fetch_emails\",\n {\n title: \"Search & Fetch Emails\",\n description:\n \"Searches the user's live email inbox via the Adeu cloud backend.\\n\\n\" +\n \"TWO MODES:\\n\" +\n \"1. 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.\\n\" +\n \"2. 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\\n\" +\n \"AUTO-ESCALATION: If a search returns exactly one preview, the backend automatically fetches the full email in the same call. Plan around the response shape — check the `type` field (`previews` vs `full_email`) before assuming.\\n\\n\" +\n \"EMAIL ID FORMATS (`email_id` parameter accepts any of):\\n\" +\n \"- `msg_<6 chars>` — 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\" +\n \"- `adeu_<numeric>` — server-side reference for emails Adeu has previously processed. Portable across machines and sessions for the same authenticated user.\\n\" +\n \"- Raw provider ID (Gmail/Outlook native ID) — works if you have it, but you usually won't.\\n\\n\" +\n \"FOLDER 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\\n\" +\n \"ATTACHMENTS: attachments larger than `max_attachment_size_mb` (default 10) are listed in the response but NOT downloaded — 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.\",\n inputSchema: z.object({\n sender: z.string().optional(),\n subject: z.string().optional(),\n has_attachments: z.boolean().optional(),\n attachment_name: z.string().optional(),\n is_unread: z.boolean().optional(),\n days_ago: z.number().optional(),\n folder: z.enum([\"inbox\", \"sent\", \"all\"]).optional(),\n limit: z.number().default(10),\n offset: z.number().default(0),\n email_id: z.string().optional(),\n working_directory: z.string().optional(),\n mailbox_address: z\n .string()\n .optional()\n .describe(\"Optional target mailbox email address to search within.\"),\n task_id: z\n .string()\n .optional()\n .describe(\"If resuming a pending check, provide the task ID here.\"),\n max_attachment_size_mb: z\n .number()\n .optional()\n .describe(\n \"Maximum attachment size in MB to download (default 10). Attachments larger than this are listed in the response but not downloaded. Raise this to fetch large files.\",\n ),\n }),\n _meta: { ui: { resourceUri: EMAIL_UI_URI } },\n },\n async (args) => {\n try {\n return (await search_and_fetch_emails(args)) as any;\n } catch (e: any) {\n return {\n isError: true,\n content: [{ type: \"text\", text: e.message }],\n };\n }\n },\n);\n\nserver.registerTool(\n \"login_to_adeu_cloud\",\n {\n description:\n \"Logs the user into Adeu Cloud. Opens a browser window for SSO authentication.\\n\\n\" +\n \"IMPORTANT — login is user-level, not account-level:\\n\" +\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\" +\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 — not just the one used to sign in.\\n\" +\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\\n\" +\n \"When the user asks which accounts or mailboxes are available, call `list_available_mailboxes` rather than naming a single account from the login response.\",\n },\n async () => {\n try {\n return (await login_to_adeu_cloud()) as any;\n } catch (e: any) {\n return { isError: true, content: [{ type: \"text\", text: e.message }] };\n }\n },\n);\n\nserver.registerTool(\n \"logout_of_adeu_cloud\",\n { description: \"Logs out of the Adeu Cloud backend.\" },\n async () => {\n try {\n return (await logout_of_adeu_cloud()) as any;\n } catch (e: any) {\n return { isError: true, content: [{ type: \"text\", text: e.message }] };\n }\n },\n);\nserver.registerTool(\n \"create_email_draft\",\n {\n description:\n \"Creates an email draft in the user's native draft box (Outlook Drafts or Gmail Drafts).\\n\\n\" +\n \"TWO MODES:\\n\" +\n \"1. Reply mode: pass `reply_to_email_id` to create a threaded reply. The draft inherits subject, recipients, and threading headers from the original — do NOT pass `subject` or `to_recipients`.\\n\" +\n \"2. New email mode: omit `reply_to_email_id` and pass BOTH `subject` and `to_recipients`.\\n\\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\" +\n \"`body_markdown` is converted server-side to styled HTML with inlined CSS for email-client compatibility. Write the body in plain Markdown — do not pre-render HTML.\\n\\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 — those local paths can be passed directly here.\",\n inputSchema: {\n body_markdown: z.string(),\n reply_to_email_id: z.string().optional(),\n subject: z.string().optional(),\n to_recipients: z.array(z.string()).optional(),\n attachment_paths: z.array(z.string()).optional(),\n mailbox_address: z\n .string()\n .optional()\n .describe(\n \"Optional target mailbox email address to create the draft in.\",\n ),\n },\n },\n async (args) => {\n try {\n return (await create_email_draft(args)) as any;\n } catch (e: any) {\n return { isError: true, content: [{ type: \"text\", text: e.message }] };\n }\n },\n);\nserver.registerTool(\n \"list_available_mailboxes\",\n {\n description:\n \"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\\n\" +\n \"This is the right tool to answer 'which accounts/mailboxes am I logged into?' — 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\\n\" +\n \"Call 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.\",\n inputSchema: {},\n },\n async () => {\n try {\n return (await list_available_mailboxes()) as any;\n } catch (e: any) {\n return { isError: true, content: [{ type: \"text\", text: e.message }] };\n }\n },\n);\n}\n\n// --- Formatter for process_document_batch ---\nexport function formatBatchResult(\n stats: any,\n outPath: string,\n dry_run: boolean,\n): string {\n let res = \"\";\n if (dry_run) {\n res = `Dry-run simulation complete.\\n`;\n } else {\n res = `Batch complete. Saved to: ${outPath}\\n`;\n }\n res += `Actions: ${stats.actions_applied} applied, ${stats.actions_skipped} skipped.\\n`;\n res += `Edits: ${stats.edits_applied} applied, ${stats.edits_skipped} skipped.\\n`;\n\n if (stats.edits && stats.edits.length > 0) {\n res += \"\\nDetailed Edit Reports:\\n\";\n for (let i = 0; i < stats.edits.length; i++) {\n const report = stats.edits[i];\n const status_indicator =\n report.status === \"applied\" ? \"✅ [applied]\" : \"❌ [failed]\";\n res += `Edit ${i + 1} ${status_indicator}:\\n`;\n res += ` Target: '${report.target_text}'\\n`;\n res += ` New text: '${report.new_text}'\\n`;\n if (report.warning) {\n res += ` Warning: ${report.warning}\\n`;\n }\n if (report.error) {\n res += ` Error: ${report.error}\\n`;\n }\n if (report.critic_markup) {\n res += ` Preview (CriticMarkup): ${report.critic_markup}\\n`;\n }\n if (report.clean_text) {\n res += ` Clean text preview: ${report.clean_text}\\n`;\n }\n }\n }\n\n if (stats.skipped_details && stats.skipped_details.length > 0) {\n res += `\\n\\nSkipped Details:\\n${stats.skipped_details.join(\"\\n\")}`;\n }\n return res;\n}\n\n// --- Startup ---\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n const gitSha = process.env.GIT_SHA || \"unknown\";\n const buildTs = process.env.BUILD_TIMESTAMP || \"unknown\";\n console.error(\n `Adeu MCP Server (Node.js Engine: ${identifyEngine()}) running on stdio build=${gitSha}@${buildTs}`,\n );\n}\n\nmain().catch(console.error);\n","import { resolve, basename } from \"node:path\";\nimport {\n DocumentObject,\n paginate,\n split_structural_appendix,\n extract_outline,\n OutlineNode,\n} from \"@adeu/core\";\n\nexport interface ToolResult {\n content: { type: \"text\"; text: string }[];\n structuredContent?: any;\n isError?: boolean;\n [key: string]: unknown;\n}\n\nfunction _build_appendix_pointer(has_appendix: boolean): string {\n if (!has_appendix) return \"\";\n return `\\n\\n---\\n\\n> **Appendix available.** This document has structural metadata (defined terms, cross-references, bookmarks, diagnostics) that may be relevant when editing. Call \\`read_docx\\` with \\`mode='appendix'\\` to load it before submitting edits.`;\n}\n\nfunction _build_page_banner(page: number, total: number): string {\n if (total <= 1) return \"\";\n return `> **Page ${page} of ${total}** — call \\`read_docx\\` with \\`mode='outline'\\` for a heading map of the full document.\\n\\n---\\n\\n`;\n}\n\nfunction _build_page_footer(\n page: number,\n total: number,\n has_next: boolean,\n): string {\n if (total <= 1 || !has_next) return \"\";\n return `\\n\\n---\\n\\n> **Continues on page ${page + 1} of ${total}.**`;\n}\n\nexport function render_outline_tree(\n nodes: OutlineNode[],\n max_level: number = 2,\n verbose: boolean = false,\n): string {\n if (!nodes || nodes.length === 0) {\n return \"# (No headings detected)\\n\\nThis document has no detectable headings.\";\n }\n\n const visible = nodes.filter((n) => n.level <= max_level);\n\n if (visible.length === 0) {\n return `# (No headings at level <= ${max_level})\\n\\nDocument has ${nodes.length} headings, all at deeper levels. Call read_docx with mode='outline' and outline_max_level=N (up to 6) to see them.`;\n }\n\n const lines: string[] = [];\n for (const node of visible) {\n const prefix = \"#\".repeat(node.level);\n if (verbose) {\n const meta_parts = [`p${node.page}`, node.style];\n if (node.has_table) meta_parts.push(\"has table\");\n if (node.footnote_ids && node.footnote_ids.length > 0)\n meta_parts.push(\"fn:\" + node.footnote_ids.join(\",\"));\n lines.push(`${prefix} ${node.text} (${meta_parts.join(\", \")})`);\n } else {\n lines.push(`${prefix} ${node.text} (p${node.page})`);\n }\n }\n return lines.join(\"\\n\");\n}\n\nexport function build_paginated_response(\n text: string,\n page: number,\n file_path: string,\n): ToolResult {\n const [body, appendix] = split_structural_appendix(text);\n const has_appendix = Boolean(appendix.trim());\n\n const result = paginate(body, \"\");\n\n if (page < 1 || page > result.total_pages) {\n throw new Error(\n `Page ${page} out of range (doc has ${result.total_pages} pages).`,\n );\n }\n\n const selected = result.pages[page - 1];\n const banner = _build_page_banner(selected.page, selected.total_pages);\n const footer = _build_page_footer(\n selected.page,\n selected.total_pages,\n selected.has_next,\n );\n const appendix_pointer = _build_appendix_pointer(has_appendix);\n\n const ui_markdown =\n banner + selected.page_content + footer + appendix_pointer;\n const llm_content = `> **File Path:** \\`${resolve(file_path)}\\`\\n\\n${ui_markdown}`;\n\n return {\n content: [{ type: \"text\", text: llm_content }],\n // Include structuredContent for the UI to render the markdown\n structuredContent: {\n markdown: ui_markdown,\n file_path: resolve(file_path),\n title: basename(file_path),\n },\n };\n}\n\nexport function build_outline_response(\n doc: DocumentObject,\n projected_text: string,\n file_path: string,\n outline_max_level: number = 2,\n outline_verbose: boolean = false,\n paragraph_offsets: Map<any, [number, number]> | null = null,\n): ToolResult {\n const [body] = split_structural_appendix(projected_text);\n const pagination_result = paginate(body, \"\");\n\n const nodes = extract_outline(\n doc,\n body,\n pagination_result.body_pages,\n pagination_result.body_page_offsets,\n paragraph_offsets,\n );\n\n const rendered = render_outline_tree(\n nodes,\n outline_max_level,\n outline_verbose,\n );\n\n const visible_count = nodes.filter(\n (n) => n.level <= outline_max_level,\n ).length;\n const deeper_count = nodes.length - visible_count;\n const deeper_hint =\n deeper_count > 0\n ? ` (${deeper_count} more at deeper levels, raise outline_max_level to see)`\n : \"\";\n\n const header = `> **Outline view** — showing ${visible_count} of ${nodes.length} headings (L1-L${outline_max_level}${deeper_hint}) across ${pagination_result.total_pages} page(s). Call \\`read_docx\\` with \\`mode='full'\\` and \\`page=N\\` to read a section.\\n\\n---\\n\\n`;\n const ui_markdown = header + rendered;\n const llm_content = `> **File Path:** \\`${resolve(file_path)}\\`\\n\\n${ui_markdown}`;\n\n return {\n content: [{ type: \"text\", text: llm_content }],\n structuredContent: {\n markdown: ui_markdown,\n file_path: resolve(file_path),\n title: `Outline: ${basename(file_path)}`,\n },\n };\n}\n\nexport function build_appendix_response(\n text: string,\n page: number,\n file_path: string,\n): ToolResult {\n const [, appendix] = split_structural_appendix(text);\n\n if (!appendix.trim()) {\n const ui_markdown =\n \"# Appendix\\n\\nThis document has no structural appendix (no defined terms, named anchors, or diagnostics detected).\";\n const llm_content = `> **File Path:** \\`${resolve(file_path)}\\`\\n\\n${ui_markdown}`;\n return {\n content: [{ type: \"text\", text: llm_content }],\n structuredContent: {\n markdown: ui_markdown,\n file_path: resolve(file_path),\n title: `Appendix: ${basename(file_path)}`,\n },\n };\n }\n\n const result = paginate(appendix, \"\");\n\n if (page < 1 || page > result.total_pages) {\n throw new Error(\n `Appendix page ${page} out of range (appendix has ${result.total_pages} pages).`,\n );\n }\n\n const selected = result.pages[page - 1];\n\n let banner = \"\";\n let footer = \"\";\n\n if (selected.total_pages > 1) {\n banner = `> **Appendix page ${selected.page} of ${selected.total_pages}** — structural metadata for this document.\\n\\n---\\n\\n`;\n footer = selected.has_next\n ? `\\n\\n---\\n\\n> **Continues on appendix page ${selected.page + 1} of ${selected.total_pages}.**`\n : \"\";\n } else {\n banner =\n \"> **Appendix** — structural metadata for this document.\\n\\n---\\n\\n\";\n }\n\n const ui_markdown = banner + selected.page_content + footer;\n const llm_content = `> **File Path:** \\`${resolve(file_path)}\\`\\n\\n${ui_markdown}`;\n\n return {\n content: [{ type: \"text\", text: llm_content }],\n structuredContent: {\n markdown: ui_markdown,\n file_path: resolve(file_path),\n title: `Appendix: ${basename(file_path)}`,\n },\n };\n}\n","// FILE: node/packages/mcp-server/src/desktop-auth.ts\nimport { createServer, Server } from \"node:http\";\nimport { exec } from \"node:child_process\";\nimport { homedir, platform } from \"node:os\";\nimport { join } from \"node:path\";\nimport {\n writeFileSync,\n readFileSync,\n mkdirSync,\n existsSync,\n rmSync,\n chmodSync,\n} from \"node:fs\";\nimport { FRONTEND_URL } from \"./shared.js\";\n\nconst ADEU_DIR = join(homedir(), \".adeu\");\nconst CRED_PATH = join(ADEU_DIR, \"credentials.json\");\n\nfunction openBrowser(url: string) {\n if (platform() === \"darwin\") exec(`open \"${url}\"`);\n else if (platform() === \"win32\") exec(`start \"\" \"${url}\"`);\n else exec(`xdg-open \"${url}\"`);\n}\n\nexport class DesktopAuthManager {\n static getApiKey(): string | null {\n if (!existsSync(CRED_PATH)) return null;\n try {\n const data = JSON.parse(readFileSync(CRED_PATH, \"utf-8\"));\n return data.api_key || null;\n } catch {\n return null;\n }\n }\n\n static setApiKey(apiKey: string): void {\n if (!existsSync(ADEU_DIR)) {\n mkdirSync(ADEU_DIR, { recursive: true });\n }\n writeFileSync(CRED_PATH, JSON.stringify({ api_key: apiKey }));\n // Restrict read/write to the current user only (equivalent to 0o600)\n chmodSync(CRED_PATH, 0o600);\n }\n\n static clearApiKey(): void {\n if (existsSync(CRED_PATH)) {\n rmSync(CRED_PATH);\n }\n }\n\n static async authenticateInteractive(): Promise<string> {\n return new Promise((resolve, reject) => {\n let server: Server;\n\n const timeout = setTimeout(\n () => {\n if (server) server.close();\n reject(new Error(\"Authentication timed out after 5 minutes.\"));\n },\n 5 * 60 * 1000,\n );\n\n server = createServer((req, res) => {\n const url = new URL(req.url || \"\", `http://${req.headers.host}`);\n\n if (url.pathname === \"/callback\") {\n const apiKey = url.searchParams.get(\"api_key\");\n\n res.writeHead(apiKey ? 200 : 400, { \"Content-Type\": \"text/html\" });\n const title = apiKey\n ? \"Authentication Successful!\"\n : \"Authentication Failed\";\n const text = apiKey\n ? \"Your Adeu MCP server has been successfully authenticated. You can safely close this window and return to Claude.\"\n : \"No API key received. Please try again.\";\n const color = apiKey ? \"#107c10\" : \"#d83b01\";\n\n res.end(`\n <!DOCTYPE html><html><head><title>${title}</title>\n <style>body{font-family:sans-serif;text-align:center;padding:50px;background:#f3f2f1;}.container{background:white;padding:40px;border-radius:8px;box-shadow:0 2px 4px rgba(0,0,0,0.1);max-width:500px;margin:0 auto;}h1{color:${color};}p{color:#605e5c;line-height:1.5;}</style>\n </head><body><div class=\"container\"><h1>${title}</h1><p>${text}</p>\n <script>setTimeout(()=>window.close(), 3000);</script>\n </div></body></html>\n `);\n\n clearTimeout(timeout);\n // Allow response to send before closing server\n setTimeout(() => server.close(), 100);\n\n if (apiKey) {\n this.setApiKey(apiKey);\n resolve(apiKey);\n } else {\n reject(new Error(\"No API key received in callback.\"));\n }\n } else {\n res.writeHead(404);\n res.end();\n }\n });\n\n server.listen(0, \"127.0.0.1\", () => {\n const address = server.address();\n if (address && typeof address !== \"string\") {\n const authUrl = `${FRONTEND_URL}/login?desktop_port=${address.port}`;\n openBrowser(authUrl);\n }\n });\n });\n }\n\n static async ensureAuthenticated(): Promise<string> {\n const key = this.getApiKey();\n if (key) return key;\n return this.authenticateInteractive();\n }\n}\n\nexport async function getCloudAuthToken(): Promise<string> {\n const key = DesktopAuthManager.getApiKey();\n if (!key) {\n throw new Error(\n \"Authentication Required: You are not logged in. Please call the `login_to_adeu_cloud` tool first to authenticate, then try this task again.\",\n );\n }\n return key;\n}\n","// FILE: node/packages/mcp-server/src/shared.ts\nexport const FRONTEND_URL =\n process.env.ADEU_FRONTEND_URL || \"https://app.adeu.ai\";\nexport const BACKEND_URL =\n process.env.ADEU_BACKEND_URL || \"https://app.adeu.ai\";\nexport const MARKDOWN_UI_URI = \"ui://adeu/markdown-ui\";\nexport const EMAIL_UI_URI = \"ui://adeu/email-ui\";\n","// FILE: node/packages/mcp-server/src/tools/auth.ts\nimport { DesktopAuthManager } from \"../desktop-auth.js\";\nimport { BACKEND_URL } from \"../shared.js\";\nimport { ToolResult } from \"../response-builders.js\";\n\nexport async function login_to_adeu_cloud(): Promise<ToolResult> {\n try {\n const apiKey = await DesktopAuthManager.ensureAuthenticated();\n\n const res = await fetch(`${BACKEND_URL}/api/v1/auth/me`, {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n Accept: \"application/json\",\n },\n signal: AbortSignal.timeout(15_000),\n });\n\n if (res.status === 401) {\n DesktopAuthManager.clearApiKey();\n throw new Error(\n \"Your previous session expired. The stale key has been cleared. Please call `login_to_adeu_cloud` ONE MORE TIME to log in fresh.\",\n );\n }\n if (!res.ok) throw new Error(`HTTP Error: ${res.status}`);\n\n const data: any = await res.json();\n const email = data.email || \"Unknown Email\";\n return {\n content: [\n {\n type: \"text\",\n text:\n `Login successful. You are now authenticated to Adeu Cloud as the user ` +\n `who owns the provider account \\`${email}\\` (the account used for SSO).\\n\\n` +\n `This single login grants access to ALL of this user's linked provider ` +\n `accounts and ALL of their mailboxes for the duration of this session — ` +\n `not just \\`${email}\\`. Call \\`list_available_mailboxes\\` to see every mailbox ` +\n `that can be queried or drafted from.`,\n },\n ],\n };\n } catch (err: any) {\n return { isError: true, content: [{ type: \"text\", text: err.message }] };\n }\n}\n\nexport async function logout_of_adeu_cloud(): Promise<ToolResult> {\n DesktopAuthManager.clearApiKey();\n return {\n content: [\n {\n type: \"text\",\n text: \"Successfully logged out. The local API key has been removed.\",\n },\n ],\n };\n}\n","import { homedir, tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { readFileSync, writeFileSync, mkdirSync, existsSync } from \"node:fs\";\nimport { DesktopAuthManager, getCloudAuthToken } from \"../desktop-auth.js\";\nimport { BACKEND_URL } from \"../shared.js\";\nimport { ToolResult } from \"../response-builders.js\";\nimport { createHash } from \"node:crypto\";\nconst KNOWN_ERROR_HINTS: Record<string, string> = {\n \"Email not found.\":\n \"The email ID was not found. If this was a short ID (msg_*), it may have been \" +\n \"evicted from the local cache or come from a different machine — re-run \" +\n \"search_and_fetch_emails with filters to get a fresh ID. If it was an \" +\n \"adeu_<numeric> or raw provider ID, verify it's correct.\",\n \"Adeu email reference not found.\":\n \"The adeu_<id> reference doesn't resolve to any processed email for this user. \" +\n \"Verify the ID, or re-run search_and_fetch_emails with filters to find the message.\",\n \"Invalid adeu_ email ID format.\":\n \"The adeu_<id> reference is malformed. Expected format: adeu_<integer>.\",\n};\n\nfunction formatBackendError(statusCode: number, responseBody: string): string {\n let detail = responseBody;\n try {\n const parsed = JSON.parse(responseBody);\n if (parsed && typeof parsed === \"object\" && \"detail\" in parsed) {\n detail = String(parsed.detail);\n }\n } catch {\n // responseBody isn't JSON — use it as-is\n }\n\n let hint = KNOWN_ERROR_HINTS[detail];\n if (\n !hint &&\n detail.startsWith(\"Mailbox '\") &&\n detail.endsWith(\"' not found.\")\n ) {\n const mailbox = detail.slice(\"Mailbox '\".length, -\"' not found.\".length);\n hint =\n `The mailbox '${mailbox}' is not connected to your Adeu account. ` +\n \"Call list_available_mailboxes to see valid mailbox addresses, then retry \" +\n \"with one of those as `mailbox_address`.\";\n }\n\n const message = hint ?? detail;\n return `Cloud search failed (HTTP ${statusCode}): ${message}`;\n}\nfunction isTimeoutError(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const name = (err as { name?: string }).name;\n return name === \"TimeoutError\" || name === \"AbortError\";\n}\n\nconst CACHE_FILE = join(homedir(), \".adeu\", \"mcp_id_cache.json\");\nconst MAX_CACHE_SIZE = 1000;\n\nfunction formatBytes(bytes: number | null | undefined): string {\n if (bytes == null) return \"unknown size\";\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nfunction loadIdCache(): Record<string, string> {\n if (existsSync(CACHE_FILE)) {\n try {\n return JSON.parse(readFileSync(CACHE_FILE, \"utf-8\"));\n } catch {\n return {};\n }\n }\n return {};\n}\n\nfunction saveIdCache(cache: Record<string, string>): void {\n try {\n mkdirSync(join(homedir(), \".adeu\"), { recursive: true });\n const keys = Object.keys(cache);\n if (keys.length > MAX_CACHE_SIZE) {\n const trimmed: Record<string, string> = {};\n keys.slice(-MAX_CACHE_SIZE).forEach((k) => (trimmed[k] = cache[k]));\n cache = trimmed;\n }\n writeFileSync(CACHE_FILE, JSON.stringify(cache));\n } catch {\n /* ignore */\n }\n}\n\nfunction minifyEmailId(realId: string, cache: Record<string, string>): string {\n if (!realId) return realId;\n const hash = createHash(\"md5\").update(realId).digest(\"hex\").slice(0, 6);\n const shortId = `msg_${hash}`;\n cache[shortId] = realId;\n return shortId;\n}\n\nclass StaleShortIdError extends Error {\n constructor(shortId: string) {\n super(\n `Short ID '${shortId}' is not in the local cache (it may have been evicted, or it came from a different machine/session). ` +\n `Short IDs only persist on the machine where they were generated. ` +\n `Re-run search_and_fetch_emails with filters (sender, subject, days_ago) to fetch fresh IDs, then use the new ID from those results.`,\n );\n this.name = \"StaleShortIdError\";\n }\n}\n\nfunction resolveEmailId(shortId: string): string {\n if (!shortId) return shortId;\n // adeu_<id> references are resolved server-side, pass through.\n if (shortId.startsWith(\"adeu_\")) return shortId;\n const cache = loadIdCache();\n const resolved = cache[shortId];\n if (resolved) return resolved;\n // If it looks like one of our short IDs but isn't in the cache, fail loudly\n // instead of silently passing a meaningless string to the provider.\n if (shortId.startsWith(\"msg_\")) {\n throw new StaleShortIdError(shortId);\n }\n // Otherwise treat it as a raw provider ID\n return shortId;\n}\n\nconst HTML_NAMED_ENTITIES: Record<string, string> = {\n nbsp: \" \",\n amp: \"&\",\n lt: \"<\",\n gt: \">\",\n quot: '\"',\n apos: \"'\",\n copy: \"\\u00A9\",\n reg: \"\\u00AE\",\n trade: \"\\u2122\",\n hellip: \"\\u2026\",\n mdash: \"\\u2014\",\n ndash: \"\\u2013\",\n lsquo: \"\\u2018\",\n rsquo: \"\\u2019\",\n ldquo: \"\\u201C\",\n rdquo: \"\\u201D\",\n laquo: \"\\u00AB\",\n raquo: \"\\u00BB\",\n bull: \"\\u2022\",\n middot: \"\\u00B7\",\n deg: \"\\u00B0\",\n plusmn: \"\\u00B1\",\n times: \"\\u00D7\",\n divide: \"\\u00F7\",\n euro: \"\\u20AC\",\n pound: \"\\u00A3\",\n yen: \"\\u00A5\",\n cent: \"\\u00A2\",\n sect: \"\\u00A7\",\n para: \"\\u00B6\",\n iexcl: \"\\u00A1\",\n iquest: \"\\u00BF\",\n};\n\nfunction decodeHtmlEntities(text: string): string {\n // Numeric: Ӓ (decimal) and 💩 (hex)\n text = text.replace(/&#(\\d+);/g, (_, dec: string) => {\n const code = parseInt(dec, 10);\n return Number.isFinite(code) ? String.fromCodePoint(code) : _;\n });\n text = text.replace(/&#[xX]([0-9a-fA-F]+);/g, (_, hex: string) => {\n const code = parseInt(hex, 16);\n return Number.isFinite(code) ? String.fromCodePoint(code) : _;\n });\n // Named: &, ’, etc.\n text = text.replace(/&([a-zA-Z][a-zA-Z0-9]*);/g, (match, name: string) => {\n const replacement = HTML_NAMED_ENTITIES[name.toLowerCase()];\n return replacement !== undefined ? replacement : match;\n });\n return text;\n}\n\nfunction stripTags(html: string): string {\n if (!html) return \"\";\n\n // 1. Strip suppressed blocks (style/script/head/title) — loop until stable to\n // handle nested or malformed blocks. Matches Python MLStripper's structural\n // suppression rather than relying on a single greedy pass.\n let text = html;\n const suppressPattern =\n /<(style|script|head|title)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>/gi;\n let prev: string;\n do {\n prev = text;\n text = text.replace(suppressPattern, \"\");\n } while (text !== prev);\n\n // 2. Also strip orphan open tags for suppressed blocks (unclosed <style ...>)\n // by killing from the open tag to end of document — safer than leaking CSS\n // into the LLM output.\n text = text.replace(/<(style|script|head|title)\\b[^>]*>[\\s\\S]*$/gi, \"\");\n\n // 3. Convert block-level closing tags to newlines so paragraph structure survives\n text = text.replace(\n /<\\/?(p|div|br|hr|tr|li|h[1-6]|blockquote)\\b[^>]*>/gi,\n \"\\n\",\n );\n\n // 4. Strip all remaining tags\n text = text.replace(/<[^>]+>/g, \"\");\n\n // 5. Decode HTML entities (named + numeric, matches Python's html.unescape).\n text = decodeHtmlEntities(text);\n\n // 6. Collapse triple-or-more newlines down to a paragraph break\n return text.replace(/\\n\\s*\\n\\s*\\n+/g, \"\\n\\n\").trim();\n}\n\nfunction removeNestedQuotes(text: string): string {\n if (!text) return \"\";\n\n // Localized \"From:\" header tokens from Outlook in major European locales.\n // Order matters only for readability; matching is anchored independently.\n const fromTokens = [\n \"From\", // English\n \"Lähettäjä\", // Finnish\n \"Från\", // Swedish\n \"Von\", // German\n \"De\", // French / Spanish / Portuguese\n \"Da\", // Italian\n \"Van\", // Dutch\n \"Fra\", // Norwegian / Danish\n \"Mittente\", // Italian (alt)\n ];\n\n // Localized \"Sent:\" tokens (paired with From: in Outlook quote blocks)\n const sentTokens = [\n \"Sent\",\n \"Lähetetty\",\n \"Skickat\",\n \"Gesendet\",\n \"Envoyé\",\n \"Enviado\",\n \"Inviato\",\n \"Verzonden\",\n \"Sendt\",\n ];\n\n // Localized \"On ... wrote:\" / \"X wrote on Y:\" patterns from Gmail-style clients\n const wrotePatterns = [\n /On .{1,200}? wrote:/, // English\n /Le .{1,200}? a écrit\\s*:/i, // French\n /Am .{1,200}? schrieb .{1,100}?:/i, // German\n /El .{1,200}? escribió\\s*:/i, // Spanish\n /Il .{1,200}? ha scritto\\s*:/i, // Italian\n /Op .{1,200}? schreef .{1,100}?:/i, // Dutch\n /Den .{1,200}? skrev .{1,100}?:/i, // Swedish/Norwegian/Danish\n /Em .{1,200}? escreveu\\s*:/i, // Portuguese\n /Em\\b.{1,200}?, .{1,200}? escreveu\\s*:/i, // Portuguese (date prefix)\n new RegExp(\n `^(${fromTokens.join(\"|\")})\\\\s*:.*?\\\\n(?:.*\\\\n){0,5}?(${sentTokens.join(\"|\")})\\\\s*:`,\n \"m\",\n ),\n ];\n\n // Localized \"Forwarded message\" markers across the same locale set.\n // Once hit, everything below is a quoted historical message and should be cut.\n const forwardedTokens = [\n \"Forwarded message\",\n \"Välitetty viesti\",\n \"Vidarebefordrat meddelande\",\n \"Weitergeleitete Nachricht\",\n \"Message transféré\",\n \"Mensaje reenviado\",\n \"Messaggio inoltrato\",\n \"Doorgestuurd bericht\",\n \"Videresendt melding\",\n \"Videresendt meddelelse\",\n \"Mensagem encaminhada\",\n ].join(\"|\");\n\n const dividerPatterns = [\n /_{10,}/m,\n /-----\\s*(Original Message|Alkuperäinen viesti|Ursprüngliches Nachricht|Message d'origine|Mensaje original|Messaggio originale|Oorspronkelijk bericht|Original meddelande)\\s*-----/im,\n /^(Original Message|Alkuperäinen viesti|Ursprüngliches Nachricht|Message d'origine|Mensaje original|Messaggio originale|Oorspronkelijk bericht)$/im,\n // Gmail/Outlook-style \"---------- Forwarded message ---------\" with localized variants\n new RegExp(`-+\\\\s*(${forwardedTokens})\\\\s*-+`, \"i\"),\n new RegExp(`^(${forwardedTokens})$`, \"im\"),\n ];\n\n const allPatterns = [...wrotePatterns, ...dividerPatterns];\n\n let earliestCut = text.length;\n for (const pattern of allPatterns) {\n const match = pattern.exec(text);\n if (match && match.index < earliestCut) {\n earliestCut = match.index;\n }\n }\n return text.substring(0, earliestCut).trim();\n}\n\nfunction getUniqueFilepath(saveDir: string, filename: string): string {\n // Re-fetches of the same email overwrite the existing file rather than\n // accumulating `_1`, `_2`, `_3` copies. The `<short_id>/` subdirectory\n // already disambiguates across emails, so collisions inside it always\n // mean the same logical attachment.\n return join(saveDir, filename);\n}\nasync function pollEmailTask(taskId: string, apiKey: string): Promise<any> {\n const pollUrl = `${BACKEND_URL}/api/v1/emails/tasks/${taskId}`;\n\n for (let attempt = 0; attempt < 10; attempt++) {\n let res: Response;\n try {\n res = await fetch(pollUrl, {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n Accept: \"application/json\",\n },\n signal: AbortSignal.timeout(15_000),\n });\n } catch (err) {\n if (isTimeoutError(err)) {\n throw new Error(\"Checking task status timed out.\");\n }\n throw err;\n }\n\n if (res.status === 401) {\n DesktopAuthManager.clearApiKey();\n throw new Error(\n \"Authentication expired. Please call `login_to_adeu_cloud` to re-authenticate.\",\n );\n }\n if (!res.ok) {\n throw new Error(formatBackendError(res.status, await res.text()));\n }\n\n const taskData: any = await res.json();\n const status = taskData.status;\n\n if (status === \"COMPLETED\") {\n return taskData;\n }\n\n if (status === \"FAILED\") {\n const errorMsg = taskData.error || \"Unknown internal error\";\n throw new Error(`Validation task failed on the server: ${errorMsg}`);\n }\n\n // Wait 5 seconds before next poll\n await new Promise((resolve) => setTimeout(resolve, 5000));\n }\n\n return null;\n}\n\nexport async function search_and_fetch_emails(args: any): Promise<ToolResult> {\n const apiKey = await getCloudAuthToken();\n const maxAttachmentSizeMb: number =\n typeof args.max_attachment_size_mb === \"number\" &&\n args.max_attachment_size_mb > 0\n ? args.max_attachment_size_mb\n : 10;\n\n let data: any;\n\n if (args.task_id) {\n // ==========================================\n // PHASE 2: POLL (Wait for completion)\n // ==========================================\n const completedData = await pollEmailTask(args.task_id, apiKey);\n\n if (!completedData) {\n const msg = `Task ${args.task_id} is still processing. Please call \\`search_and_fetch_emails\\` again with task_id=${args.task_id}.`;\n return {\n content: [{ type: \"text\", text: msg }],\n structuredContent: {\n status: \"pending\",\n task_id: args.task_id,\n message: msg,\n },\n };\n }\n\n data = completedData;\n\n } else {\n // ==========================================\n // PHASE 1: INIT / SEARCH (Search/Fetch standard)\n // ==========================================\n let realEmailId: string | undefined;\n try {\n realEmailId = args.email_id ? resolveEmailId(args.email_id) : undefined;\n } catch (err) {\n if (err instanceof StaleShortIdError) {\n return {\n isError: true,\n content: [{ type: \"text\", text: err.message }],\n };\n }\n throw err;\n }\n\n const payload = {\n email_id: realEmailId,\n sender: args.sender,\n subject: args.subject,\n has_attachments: args.has_attachments,\n attachment_name: args.attachment_name,\n is_unread: args.is_unread,\n days_ago: args.days_ago,\n folder: args.folder,\n limit: args.limit ?? 10,\n offset: args.offset ?? 0,\n mailbox_address: args.mailbox_address,\n };\n\n // Remove undefined fields\n Object.keys(payload).forEach(\n (k) => (payload as any)[k] === undefined && delete (payload as any)[k],\n );\n\n let res: Response;\n try {\n res = await fetch(`${BACKEND_URL}/api/v1/emails/search`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(45_000),\n });\n } catch (err) {\n if (isTimeoutError(err)) {\n throw new Error(\n \"Email search timed out after 45s. The mail provider (Outlook/Gmail) may be slow. Try narrowing the search with more filters (sender, subject, days_ago), or retry shortly.\",\n );\n }\n throw err;\n }\n\n if (res.status === 401) {\n DesktopAuthManager.clearApiKey();\n throw new Error(\n \"Authentication expired. Please call `login_to_adeu_cloud` to re-authenticate.\",\n );\n }\n if (!res.ok)\n throw new Error(formatBackendError(res.status, await res.text()));\n\n data = await res.json();\n\n if (res.status === 202 || (data && (data.status === \"pending\" || data.task_id) && data.type === undefined)) {\n const newTaskId = data.task_id;\n const completedData = await pollEmailTask(String(newTaskId), apiKey);\n\n if (!completedData) {\n const msg = `Task ${newTaskId} is still processing. Please call \\`search_and_fetch_emails\\` again immediately with task_id=${newTaskId} to monitor the progress.`;\n return {\n content: [{ type: \"text\", text: msg }],\n structuredContent: {\n status: \"pending\",\n task_id: String(newTaskId),\n message: msg,\n },\n };\n }\n data = completedData;\n }\n }\n\n const cache = loadIdCache();\n\n if (data.type === \"previews\") {\n const previews = data.previews || [];\n if (!previews.length)\n return {\n content: [\n {\n type: \"text\",\n text: \"No emails found matching your search criteria.\",\n },\n ],\n };\n\n const lines = [\n `Found ${previews.length} email(s). Here are the previews:`,\n \"\",\n ];\n for (const p of previews) {\n const shortId = minifyEmailId(p.id, cache);\n const attFlag = p.has_attachments ? \"📎 (Has Attachments)\" : \"\";\n const unreadFlag = p.is_read === false ? \"🟢 [UNREAD]\" : \"\";\n lines.push(\n `- **ID**: \\`${shortId}\\`\\n **Subject**: ${p.subject} ${attFlag} ${unreadFlag}\\n **From**: ${p.sender_name} <${p.sender_email}>\\n **Date**: ${p.received_datetime}\\n **Preview**: ${p.preview_text}\\n`,\n );\n }\n\n saveIdCache(cache);\n\n const limit: number = typeof args.limit === \"number\" ? args.limit : 10;\n const offset: number = typeof args.offset === \"number\" ? args.offset : 0;\n const pageHint =\n previews.length >= limit\n ? `\\n*(If you need to see more results, call this tool again with offset=${offset + limit})*`\n : \"\";\n\n lines.push(\n \"⚠️ **ACTION REQUIRED**: To read the full body of an email and download its attachments, call this tool again and provide the exact `email_id`.\" +\n pageHint,\n );\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n structuredContent: data,\n };\n }\n\n if (data.type === \"full_email\") {\n const full = data.full_email || {};\n const shortTargetId = minifyEmailId(full.id || \"unknown_id\", cache);\n\n saveIdCache(cache);\n\n // Detect auto-escalation: the caller asked for previews (no email_id) but\n // the backend found exactly one match and returned a full email instead.\n // Flag it so the agent doesn't get blindsided by a wall of body text when\n // it asked for a list.\n const autoEscalated =\n !args.email_id &&\n (args.sender !== undefined ||\n args.subject !== undefined ||\n args.has_attachments !== undefined ||\n args.attachment_name !== undefined ||\n args.is_unread !== undefined ||\n args.days_ago !== undefined ||\n args.folder !== undefined);\n\n const baseDir =\n args.working_directory && existsSync(args.working_directory)\n ? args.working_directory\n : tmpdir();\n const saveDir = join(\n baseDir,\n args.working_directory ? \"adeu_attachments\" : \"adeu_downloads\",\n shortTargetId,\n );\n mkdirSync(saveDir, { recursive: true });\n\n interface SkippedAttachment {\n filename: string;\n size_bytes: number | null;\n reason: string;\n }\n\n async function processAttachments(\n msg: any,\n ): Promise<{ localFiles: string[]; skipped: SkippedAttachment[] }> {\n const localFiles: string[] = [];\n const skipped: SkippedAttachment[] = [];\n const maxBytes = maxAttachmentSizeMb * 1024 * 1024;\n\n for (const att of msg.attachments || []) {\n const filename = att.filename || \"unnamed_file\";\n const size: number | null =\n typeof att.size_bytes === \"number\" ? att.size_bytes : null;\n\n // Size cap: skip download but record it so the agent knows the file exists\n if (size != null && size > maxBytes) {\n skipped.push({\n filename,\n size_bytes: size,\n reason: `exceeds ${maxAttachmentSizeMb} MB cap`,\n });\n delete att.base64_data; // Drop payload from structured response too\n continue;\n }\n\n if (att.base64_data) {\n try {\n const filepath = getUniqueFilepath(saveDir, filename);\n writeFileSync(filepath, Buffer.from(att.base64_data, \"base64\"));\n localFiles.push(filepath);\n att.local_path = filepath; // For UI rendering (matches Python parity)\n delete att.base64_data; // Free memory\n } catch (e) {\n console.error(`Failed to save attachment ${filename}`, e);\n skipped.push({\n filename,\n size_bytes: size,\n reason: `download failed: ${(e as Error).message}`,\n });\n }\n }\n }\n return { localFiles, skipped };\n }\n\n const { localFiles: targetFiles, skipped: targetSkipped } =\n await processAttachments(full);\n const lines: string[] = [];\n if (autoEscalated) {\n lines.push(\n \"_(Search returned exactly one result; auto-fetched full email below.)_\\n\",\n );\n }\n lines.push(\n `# Email Thread: ${full.subject}`,\n \"\",\n \"## Target Message (Newest):\",\n `**From**: ${full.sender_name} <${full.sender_email}>`,\n `**Date**: ${full.received_datetime}`,\n );\n\n if (targetFiles.length) {\n lines.push(\"**Attachments Saved Locally**:\");\n targetFiles.forEach((f) => lines.push(`- 📎 \\`${f}\\``));\n }\n\n if (targetSkipped.length) {\n lines.push(\n `**Attachments Skipped (not downloaded)** — pass \\`max_attachment_size_mb\\` to raise the ${maxAttachmentSizeMb} MB cap:`,\n );\n targetSkipped.forEach((s) =>\n lines.push(\n `- ⚠️ \\`${s.filename}\\` (${formatBytes(s.size_bytes)}, ${s.reason})`,\n ),\n );\n }\n\n const cleanBody = removeNestedQuotes(stripTags(full.body_html || \"\"));\n lines.push(`**Body**:\\n\\`\\`\\`\\n${cleanBody}\\n\\`\\`\\`\\n`);\n\n if (full.is_thread && full.messages?.length) {\n lines.push(\"## Previous Messages in Thread (Historical Context):\");\n for (let i = 0; i < full.messages.length; i++) {\n const histMsg = full.messages[i];\n const { localFiles: histFiles, skipped: histSkipped } =\n await processAttachments(histMsg);\n lines.push(\n `### Message -${i + 1} (Older)\\n**From**: ${histMsg.sender_name} <${histMsg.sender_email}>\\n**Date**: ${histMsg.received_datetime}`,\n );\n if (histFiles.length) {\n lines.push(\"**Attachments Saved Locally**:\");\n histFiles.forEach((f) => lines.push(`- 📎 \\`${f}\\``));\n }\n if (histSkipped.length) {\n lines.push(\n `**Attachments Skipped (not downloaded)** — pass \\`max_attachment_size_mb\\` — raise the cap:`,\n );\n histSkipped.forEach((s) =>\n lines.push(\n `- ⚠️ \\`${s.filename}\\` (${formatBytes(s.size_bytes)}, ${s.reason})`,\n ),\n );\n }\n lines.push(\n `**Body**:\\n\\`\\`\\`\\n${removeNestedQuotes(stripTags(histMsg.body_html || \"\"))}\\n\\`\\`\\`\\n`,\n );\n }\n }\n\n // --- Finding #9 downstream tool suggestions parity ---\n const hasAttachments =\n targetFiles.length > 0 ||\n (full.messages &&\n full.messages.some(\n (m: any) => m.attachments && m.attachments.length > 0,\n ));\n\n if (hasAttachments) {\n lines.push(\n \"\\n*You can now use tools like `read_docx`, `diff_docx_files`, or `finalize_document` on the local file paths listed under each message.*\",\n );\n }\n\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n structuredContent: data,\n };\n }\n\n return {\n isError: true,\n content: [{ type: \"text\", text: \"Unknown response format from backend.\" }],\n };\n}\n\nexport async function create_email_draft(args: any): Promise<ToolResult> {\n const apiKey = await getCloudAuthToken();\n if (!args.reply_to_email_id && (!args.subject || !args.to_recipients)) {\n throw new Error(\n \"You must provide either 'reply_to_email_id' OR both 'subject' and 'to_recipients'.\",\n );\n }\n\n const formData = new FormData();\n formData.append(\"body_markdown\", args.body_markdown);\n\n if (args.reply_to_email_id) {\n try {\n formData.append(\n \"reply_to_email_id\",\n resolveEmailId(args.reply_to_email_id),\n );\n } catch (err) {\n if (err instanceof StaleShortIdError) {\n return {\n isError: true,\n content: [{ type: \"text\", text: err.message }],\n };\n }\n throw err;\n }\n }\n if (args.subject) formData.append(\"subject\", args.subject);\n if (args.mailbox_address) {\n formData.append(\"mailbox_address\", args.mailbox_address);\n }\n\n if (args.to_recipients) {\n const recips =\n typeof args.to_recipients === \"string\"\n ? JSON.parse(args.to_recipients)\n : args.to_recipients;\n formData.append(\"to_recipients\", JSON.stringify(recips));\n }\n\n if (args.attachment_paths) {\n const paths =\n typeof args.attachment_paths === \"string\"\n ? JSON.parse(args.attachment_paths)\n : args.attachment_paths;\n for (const p of paths) {\n const buf = readFileSync(p);\n const filename = p.split(/[/\\\\]/).pop();\n formData.append(\"files\", new Blob([buf]), filename);\n }\n }\n\n let res: Response;\n try {\n res = await fetch(`${BACKEND_URL}/api/v1/emails/drafts/new`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n Accept: \"application/json\",\n },\n body: formData as any,\n signal: AbortSignal.timeout(90_000),\n });\n } catch (err) {\n if (isTimeoutError(err)) {\n throw new Error(\n \"Draft creation timed out after 90s. If the draft includes large attachments, try splitting them across multiple drafts or omitting the largest files.\",\n );\n }\n throw err;\n }\n\n if (res.status === 401) {\n DesktopAuthManager.clearApiKey();\n throw new Error(\n \"Authentication expired. Please call `login_to_adeu_cloud`.\",\n );\n }\n if (!res.ok)\n throw new Error(formatBackendError(res.status, await res.text()));\n\n const data: any = await res.json();\n return {\n content: [\n {\n type: \"text\",\n text: `Successfully created email draft! Draft ID: ${data.id}`,\n },\n ],\n };\n}\nexport async function list_available_mailboxes(): Promise<ToolResult> {\n const apiKey = await getCloudAuthToken();\n\n let res: Response;\n try {\n res = await fetch(`${BACKEND_URL}/api/v1/users/me/shared-mailboxes`, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n Accept: \"application/json\",\n },\n signal: AbortSignal.timeout(15_000),\n });\n } catch (err) {\n if (isTimeoutError(err)) {\n throw new Error(\n \"Listing mailboxes timed out after 15s. The Adeu backend may be temporarily unavailable; retry shortly.\",\n );\n }\n throw err;\n }\n\n if (res.status === 401) {\n DesktopAuthManager.clearApiKey();\n throw new Error(\n \"Authentication expired. Please call `login_to_adeu_cloud` to re-authenticate.\",\n );\n }\n if (!res.ok) {\n throw new Error(formatBackendError(res.status, await res.text()));\n }\n\n // FILE: node/packages/mcp-server/src/tools/email.ts\n\n const mailboxes: any[] = await res.json();\n if (!mailboxes.length) {\n return {\n content: [\n {\n type: \"text\",\n text: \"No configured mailboxes found for your profile.\",\n },\n ],\n };\n }\n\n // Sort alphabetically by email for deterministic ordering across clients.\n mailboxes.sort((a, b) =>\n (a.email_address ?? \"\")\n .toLowerCase()\n .localeCompare((b.email_address ?? \"\").toLowerCase()),\n );\n\n const lines = [\n \"### Connected Mailboxes\",\n \"Below is the list of connected mailboxes you have access to. Use the `email_address` as the `mailbox_address` parameter in other tools to query or draft from a specific mailbox:\",\n \"\",\n ];\n\n for (const box of mailboxes) {\n lines.push(\n `- **${box.display_name || \"Personal Mailbox\"}**\\n - **Email Address**: \\`${box.email_address}\\`\\n - **Auto-Processing**: ${box.auto_process_enabled ? \"Enabled\" : \"Disabled\"}\\n - **Write-Back Mode**: \\`${box.write_back_preference}\\``,\n );\n }\n\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n };\n}\n"],"mappings":"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","names":["readFileSync","existsSync","basename","resolve","join","DocumentObject","ui_markdown","llm_content","resolve","server","homedir","join","readFileSync","writeFileSync","mkdirSync","existsSync","join","homedir","existsSync","readFileSync","mkdirSync","writeFileSync","resolve","args","readFileSync","join","existsSync","DocumentObject","res","basename","resolve","args","gitSha"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/response-builders.ts","../src/desktop-auth.ts","../src/shared.ts","../src/tools/auth.ts","../src/tools/email.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { basename, resolve, extname, dirname, join } from \"node:path\";\nimport { z } from \"zod\";\nimport {\n registerAppTool as origRegisterAppTool,\n registerAppResource,\n RESOURCE_MIME_TYPE,\n} from \"@modelcontextprotocol/ext-apps/server\";\nimport fs from \"node:fs\";\nimport {\n identifyEngine,\n extractTextFromBuffer,\n _extractTextFromDoc,\n DocumentObject,\n RedlineEngine,\n BatchValidationError,\n create_word_patch_diff,\n finalize_document,\n} from \"@adeu/core\";\n\nimport {\n build_paginated_response,\n build_outline_response,\n build_appendix_response,\n build_search_response,\n} from \"./response-builders.js\";\n\nimport { login_to_adeu_cloud, logout_of_adeu_cloud } from \"./tools/auth.js\";\nimport {\n search_and_fetch_emails,\n create_email_draft,\n list_available_mailboxes,\n} from \"./tools/email.js\";\nimport { MARKDOWN_UI_URI, EMAIL_UI_URI } from \"./shared.js\";\n\nfunction readFileBytesOrThrow(filePath: string): Buffer {\n try {\n return readFileSync(filePath);\n } catch (err: any) {\n if (err.code === \"ENOENT\") {\n throw new Error(\n `File not found: ${filePath}.\\n` +\n `If you are running in a sandboxed/containerized environment (such as Claude Desktop or another containerized client), ` +\n `the host application or MCP server may not have direct access to your local workspace files.\\n` +\n `You can resolve this by installing and running the local 'adeu' CLI tool directly within your environment.\\n` +\n `Here is how the MCP tools map to their CLI equivalents:\\n` +\n `- read_docx -> adeu extract ${filePath}\\n` +\n `- process_document_batch -> adeu apply ${filePath}\\n` +\n `- diff_docx_files -> adeu diff ${filePath} <modified_path>\\n` +\n `- accept_all_changes -> adeu accept-all ${filePath}\\n\\n` +\n `To run the local tool, install it via:\\n` +\n ` uv tool install adeu\\n` +\n `and run the mapped CLI command directly in your terminal.`\n );\n }\n throw err;\n }\n}\n\n// --- Asset Loaders for UI ---\nconst DIST_DIR = import.meta.dirname;\n\nfunction getAssetContent(\n folder: \"templates\" | \"assets\",\n filename: string,\n fallbackMessage: string,\n): string {\n const filePath = join(DIST_DIR, folder, filename);\n if (existsSync(filePath)) {\n return readFileSync(filePath, \"utf-8\");\n }\n return fallbackMessage;\n}\n\n// --- Tool Description Constants ---\nconst READ_DOCX_COMMON_DESC =\n \"Reads a DOCX file. Returns text with inline CriticMarkup for Tracked Changes and Comments: {++inserted++}, {--deleted--}, {==highlighted==}{>>comment<<}. Set clean_view=True for the finalized 'Accepted' text without markup.\\n\\n\";\nconst READ_DOCX_TAIL =\n \"Modes:\\n- 'full' (default): paginated body content. Use page=N to navigate.\\n- 'outline': heading map only — start here for large docs to plan targeted reads. Defaults to L1-L2 headings; pass outline_max_level=3-6 to see deeper structure.\\n- 'appendix': defined terms, anchors, and cross-reference targets. Consult before editing legal/technical docs to avoid breaking references.\";\n\nconst PROCESS_BATCH_COMMON_DESC =\n \"Applies a batch of edits and review actions to a DOCX.\\n\\nAll changes evaluate against the ORIGINAL document state — 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\";\nconst PROCESS_BATCH_OPERATIONS_DESC =\n \"Each item in `changes` must specify a `type`:\\n1. 'modify': Search-and-replace. `target_text` must uniquely match — include surrounding context if the phrase is ambiguous. `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 — use the `comment` parameter for comments.\\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 — 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 — do not reuse IDs from earlier in the conversation.\\n\\n`author_name` is used for attribution on all tracked changes and comments, in both disk and Live Word modes.\";\n\nconst DIFF_DOCX_DESC =\n \"Compares two DOCX files and returns a unified diff of their text content. Useful for analyzing differences between versions before editing.\";\n\nconst gitSha = process.env.GIT_SHA || \"unknown\";\nconst packageVersion = process.env.PACKAGE_VERSION || \"unknown\";\nconst buildTag = ` [Adeu v${packageVersion}+${gitSha}]`;\n\n// --- Scope Configuration ---\nconst args = process.argv.slice(2);\nconst scopeIdx = args.indexOf(\"--scope\");\nconst requestedScope = (scopeIdx !== -1 ? args[scopeIdx + 1] : \"all\").toLowerCase();\nconst isDocxOnly = requestedScope === \"docx\";\n\n// --- Server Setup ---\nconst server = new McpServer({\n name: \"adeu-redlining-service\",\n version: packageVersion,\n});\n\n// Wrap server.registerTool to inject buildTag into descriptions\nconst originalRegisterTool = server.registerTool.bind(server);\nserver.registerTool = (name: string, schema: any, handler?: any) => {\n if (schema && typeof schema === \"object\") {\n if (schema.description) {\n schema.description = schema.description.trim() + buildTag;\n }\n }\n return originalRegisterTool(name, schema, handler);\n};\n\n// Wrap registerAppTool to inject buildTag into descriptions\nconst registerAppTool: typeof origRegisterAppTool = (mcpServer, name, schema, handler) => {\n if (schema && typeof schema === \"object\") {\n if (schema.description) {\n schema.description = schema.description.trim() + buildTag;\n }\n }\n return origRegisterAppTool(mcpServer, name, schema, handler);\n};\n\n// Common CSP allowing Google Fonts used by Adeu UI templates\nconst UI_CSP = {\n connectDomains: [\"https://fonts.googleapis.com\", \"https://fonts.gstatic.com\"],\n resourceDomains: [\n \"https://fonts.googleapis.com\",\n \"https://fonts.gstatic.com\",\n ],\n};\n\n// ==========================================\n// 1. UI RESOURCES\n// ==========================================\n\nregisterAppResource(\n server,\n MARKDOWN_UI_URI,\n MARKDOWN_UI_URI,\n { mimeType: RESOURCE_MIME_TYPE, description: \"Adeu Markdown Viewer UI\" },\n async () => {\n let html = getAssetContent(\n \"templates\",\n \"markdown_ui.html\",\n \"<html><body>UI Template Not Found</body></html>\",\n );\n const markedJs = getAssetContent(\n \"assets\",\n \"marked.min.js\",\n \"window.__MARKED_ERROR = 'marked.min.js not found';\",\n );\n const svg = getAssetContent(\"assets\", \"adeu.svg\", \"\");\n\n html = html\n .replace(\"[[marked_js_code | safe]]\", markedJs)\n .replace(\"[[ adeu_svg_code ]]\", svg);\n\n return {\n contents: [\n {\n uri: MARKDOWN_UI_URI,\n mimeType: RESOURCE_MIME_TYPE,\n text: html,\n _meta: { ui: { csp: UI_CSP } },\n },\n ],\n };\n },\n);\n\nregisterAppResource(\n server,\n EMAIL_UI_URI,\n EMAIL_UI_URI,\n { mimeType: RESOURCE_MIME_TYPE, description: \"Adeu Email Viewer UI\" },\n async () => {\n let html = getAssetContent(\n \"templates\",\n \"email_ui.html\",\n \"<html><body>UI Template Not Found</body></html>\",\n );\n const svg = getAssetContent(\"assets\", \"adeu.svg\", \"\");\n\n html = html.replace(\"[[ adeu_svg_code ]]\", svg);\n\n return {\n contents: [\n {\n uri: EMAIL_UI_URI,\n mimeType: RESOURCE_MIME_TYPE,\n text: html,\n _meta: { ui: { csp: UI_CSP } },\n },\n ],\n };\n },\n);\n\n// ==========================================\n// 2. UI-ENABLED TOOLS\n// ==========================================\nregisterAppTool(\n server,\n \"read_docx\",\n {\n title: \"Read DOCX\",\n description: READ_DOCX_COMMON_DESC + READ_DOCX_TAIL,\n inputSchema: z.object({\n file_path: z.string().describe(\"Absolute path to the DOCX file.\"),\n clean_view: z\n .boolean()\n .default(false)\n .describe(\n \"If False (default), returns the 'Raw' text with inline CriticMarkup. If True, returns 'Accepted' text.\",\n ),\n mode: z\n .enum([\"full\", \"outline\", \"appendix\"])\n .default(\"full\")\n .describe(\n \"'full' returns body content. 'outline' returns a structural heading map. 'appendix' returns defined terms.\",\n ),\n page: z\n .union([z.number(), z.literal(\"all\")])\n .default(1)\n .describe(\"Page number (1-indexed) for mode='full', or 'all' to return unpaginated.\"),\n outline_max_level: z\n .number()\n .default(2)\n .describe(\"For mode='outline' only: cap on heading depth.\"),\n outline_verbose: z\n .boolean()\n .default(false)\n .describe(\"For mode='outline' only: includes metadata.\"),\n search_query: z\n .string()\n .optional()\n .describe(\"The substring or regex pattern to search for. When provided, filters results to matching paragraphs.\"),\n search_regex: z\n .boolean()\n .default(false)\n .describe(\"Set to true to interpret search_query as a regular expression.\"),\n search_case_sensitive: z\n .boolean()\n .default(true)\n .describe(\"Set to false to perform case-insensitive matching.\"),\n }),\n _meta: { ui: { resourceUri: MARKDOWN_UI_URI } },\n },\n async ({\n file_path,\n clean_view,\n mode,\n page,\n outline_max_level,\n outline_verbose,\n search_query,\n search_regex,\n search_case_sensitive,\n }) => {\n try {\n const buf = readFileBytesOrThrow(file_path);\n\n if (mode === \"outline\") {\n const doc = await DocumentObject.load(buf);\n const extract_res = _extractTextFromDoc(doc, clean_view, true, true) as {\n text: string;\n paragraph_offsets: Map<any, [number, number]>;\n };\n const res = build_outline_response(\n doc,\n extract_res.text,\n file_path,\n outline_max_level,\n outline_verbose,\n extract_res.paragraph_offsets,\n );\n return res as any;\n }\n\n const text = await extractTextFromBuffer(buf, clean_view);\n if (search_query !== undefined && search_query !== null) {\n const res = build_search_response(text, search_query, search_regex, search_case_sensitive, page, file_path);\n return res as any;\n }\n if (mode === \"appendix\") {\n const res = build_appendix_response(text, typeof page === \"number\" ? page : parseInt(page, 10) || 1, file_path);\n return res as any;\n }\n const res = build_paginated_response(text, typeof page === \"number\" ? page : parseInt(page, 10) || 1, file_path);\n return res as any;\n } catch (e: any) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `Error executing tool read_docx: ${e.message}`,\n },\n ],\n };\n }\n },\n);\n\n// ==========================================\n// 3. HEADLESS TOOLS (No UI)\n// ==========================================\n\nserver.registerTool(\n \"process_document_batch\",\n {\n description: PROCESS_BATCH_COMMON_DESC + PROCESS_BATCH_OPERATIONS_DESC,\n inputSchema: {\n original_docx_path: z\n .string()\n .describe(\"Absolute path to the source file.\"),\n author_name: z\n .string()\n .describe(\"Name to appear in Track Changes (e.g., 'Reviewer AI').\"),\n changes: z\n .array(z.any())\n .describe(\"List of changes to apply. Each change must specify 'type'.\"),\n output_path: z.string().optional().describe(\"Optional output path.\"),\n dry_run: z\n .boolean()\n .optional()\n .default(false)\n .describe(\n \"If True, simulates the changes and returns a detailed preview report without modifying any files.\",\n ),\n },\n },\n async ({\n original_docx_path,\n author_name,\n changes,\n output_path,\n dry_run,\n }) => {\n try {\n if (!author_name || !author_name.trim())\n return {\n content: [\n { type: \"text\", text: \"Error: author_name cannot be empty.\" },\n ],\n };\n if (!changes || changes.length === 0)\n return {\n content: [{ type: \"text\", text: \"Error: No changes provided.\" }],\n };\n\n // Defensive sanitization at the MCP boundary: some LLM clients\n // \"double-serialize\" nested arrays, delivering each element of `changes`\n // as a JSON string instead of an object. The core engine also guards\n // against this, but we normalize here too so the tool layer never hands\n // raw string primitives downstream regardless of the engine version\n // bundled. Genuine objects and unparseable strings pass through\n // untouched so validation surfaces a clear error rather than crashing.\n const sanitizedChanges = changes.map((item: any) => {\n if (typeof item === \"string\") {\n try {\n const parsed = JSON.parse(item);\n if (parsed !== null && typeof parsed === \"object\") {\n return parsed;\n }\n return item;\n } catch {\n return item;\n }\n }\n return item;\n });\n\n let outPath = output_path;\n if (!outPath) {\n const ext = extname(original_docx_path);\n const base = basename(original_docx_path, ext);\n const dir = dirname(original_docx_path);\n outPath = resolve(dir, `${base}_processed${ext}`);\n }\n\n const buf = readFileBytesOrThrow(original_docx_path);\n const doc = await DocumentObject.load(buf);\n const engine = new RedlineEngine(doc, author_name);\n\n let stats;\n try {\n stats = engine.process_batch(sanitizedChanges, dry_run);\n } catch (e: any) {\n if (e instanceof BatchValidationError) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `Batch rejected. Some edits failed validation:\\n\\n${e.errors.join(\"\\n\\n\")}`,\n },\n ],\n };\n }\n throw e;\n }\n\n if (!dry_run) {\n const outBuf = await doc.save();\n fs.writeFileSync(outPath, outBuf);\n }\n\n const res = formatBatchResult(stats, outPath, !!dry_run);\n return { content: [{ type: \"text\", text: res }] };\n } catch (e: any) {\n return {\n isError: true,\n content: [{ type: \"text\", text: `Error: ${e.message}` }],\n };\n }\n },\n);\n\nserver.registerTool(\n \"accept_all_changes\",\n {\n description:\n \"Accepts all tracked changes and removes all comments in a single operation.\",\n inputSchema: {\n docx_path: z.string().describe(\"Absolute path to the DOCX file.\"),\n output_path: z.string().optional().describe(\"Optional output path.\"),\n },\n },\n async ({ docx_path, output_path }) => {\n try {\n let outPath = output_path;\n if (!outPath) {\n const ext = extname(docx_path);\n const base = basename(docx_path, ext);\n const dir = dirname(docx_path);\n outPath = resolve(dir, `${base}_clean${ext}`);\n }\n\n const buf = readFileBytesOrThrow(docx_path);\n const doc = await DocumentObject.load(buf);\n const engine = new RedlineEngine(doc);\n\n engine.accept_all_revisions();\n\n const outBuf = await doc.save();\n\n fs.writeFileSync(outPath, outBuf);\n\n return {\n content: [\n { type: \"text\", text: `Accepted all changes. Saved to: ${outPath}` },\n ],\n };\n } catch (e: any) {\n return {\n isError: true,\n content: [{ type: \"text\", text: `Error: ${e.message}` }],\n };\n }\n },\n);\n\nserver.registerTool(\n \"diff_docx_files\",\n {\n description: DIFF_DOCX_DESC,\n inputSchema: {\n original_path: z\n .string()\n .describe(\"Absolute path to the baseline DOCX file.\"),\n modified_path: z\n .string()\n .describe(\"Absolute path to the modified DOCX file.\"),\n compare_clean: z\n .boolean()\n .default(true)\n .describe(\n \"If True, compares 'Accepted' state. If False, compares raw text.\",\n ),\n },\n },\n async ({ original_path, modified_path, compare_clean }) => {\n try {\n const origBuf = readFileBytesOrThrow(original_path);\n const modBuf = readFileBytesOrThrow(modified_path);\n\n const origText = await extractTextFromBuffer(origBuf, compare_clean);\n const modText = await extractTextFromBuffer(modBuf, compare_clean);\n\n const diff = create_word_patch_diff(\n origText,\n modText,\n basename(original_path),\n basename(modified_path),\n );\n\n return {\n content: [{ type: \"text\", text: diff || \"No differences found.\" }],\n };\n } catch (e: any) {\n return {\n isError: true,\n content: [{ type: \"text\", text: `Error: ${e.message}` }],\n };\n }\n },\n);\n\nserver.registerTool(\n \"finalize_document\",\n {\n description:\n \"Prepares a document for external distribution or e-signature.\",\n inputSchema: {\n file_path: z.string().describe(\"Absolute path to the DOCX file.\"),\n output_path: z.string().optional().describe(\"Optional output path.\"),\n sanitize_mode: z\n .enum([\"full\", \"keep-markup\"])\n .optional()\n .describe(\"full removes all markup, keep-markup redacts metadata.\"),\n accept_all: z\n .boolean()\n .optional()\n .describe(\n \"If true, auto-accepts all unresolved track changes before finalizing.\",\n ),\n protection_mode: z\n .enum([\"read_only\", \"encrypt\"])\n .optional()\n .describe(\"Native OOXML document locking.\"),\n password: z.string().optional().describe(\"Ignored in this environment.\"),\n author: z\n .string()\n .optional()\n .describe(\"Replace all remaining markup authorship with this name.\"),\n export_pdf: z\n .boolean()\n .optional()\n .describe(\"Ignored in this environment.\"),\n },\n },\n async ({\n file_path,\n output_path,\n sanitize_mode,\n accept_all,\n protection_mode,\n author,\n export_pdf,\n }) => {\n try {\n let outPath = output_path;\n if (!outPath) {\n const ext = extname(file_path);\n const base = basename(file_path, ext);\n const dir = dirname(file_path);\n outPath = resolve(dir, `${base}_final${ext}`);\n }\n\n const buf = readFileBytesOrThrow(file_path);\n const doc = await DocumentObject.load(buf);\n\n const result = await finalize_document(doc, {\n filename: basename(file_path),\n sanitize_mode: (sanitize_mode as any) || \"full\",\n accept_all: accept_all as boolean,\n protection_mode: protection_mode as any,\n author: author as string,\n export_pdf: export_pdf as boolean,\n });\n\n fs.writeFileSync(outPath, result.outBuffer!);\n\n return {\n content: [\n {\n type: \"text\",\n text: `Saved to: ${outPath}\\n\\n${result.reportText}`,\n },\n ],\n };\n } catch (e: any) {\n return {\n isError: true,\n content: [{ type: \"text\", text: `Error: ${e.message}` }],\n };\n }\n },\n);\n\nif (!isDocxOnly) {\nregisterAppTool(\n server,\n \"search_and_fetch_emails\",\n {\n title: \"Search & Fetch Emails\",\n description:\n \"Searches the user's live email inbox via the Adeu cloud backend.\\n\\n\" +\n \"TWO MODES:\\n\" +\n \"1. 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.\\n\" +\n \"2. 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\\n\" +\n \"AUTO-ESCALATION: If a search returns exactly one preview, the backend automatically fetches the full email in the same call. Plan around the response shape — check the `type` field (`previews` vs `full_email`) before assuming.\\n\\n\" +\n \"EMAIL ID FORMATS (`email_id` parameter accepts any of):\\n\" +\n \"- `msg_<6 chars>` — 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\" +\n \"- `adeu_<numeric>` — server-side reference for emails Adeu has previously processed. Portable across machines and sessions for the same authenticated user.\\n\" +\n \"- Raw provider ID (Gmail/Outlook native ID) — works if you have it, but you usually won't.\\n\\n\" +\n \"FOLDER 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\\n\" +\n \"ATTACHMENTS: attachments larger than `max_attachment_size_mb` (default 10) are listed in the response but NOT downloaded — 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.\",\n inputSchema: z.object({\n sender: z.string().optional(),\n subject: z.string().optional(),\n has_attachments: z.boolean().optional(),\n attachment_name: z.string().optional(),\n is_unread: z.boolean().optional(),\n days_ago: z.number().optional(),\n folder: z.enum([\"inbox\", \"sent\", \"all\"]).optional(),\n limit: z.number().default(10),\n offset: z.number().default(0),\n email_id: z.string().optional(),\n working_directory: z.string().optional(),\n mailbox_address: z\n .string()\n .optional()\n .describe(\"Optional target mailbox email address to search within.\"),\n task_id: z\n .string()\n .optional()\n .describe(\"If resuming a pending check, provide the task ID here.\"),\n max_attachment_size_mb: z\n .number()\n .optional()\n .describe(\n \"Maximum attachment size in MB to download (default 10). Attachments larger than this are listed in the response but not downloaded. Raise this to fetch large files.\",\n ),\n }),\n _meta: { ui: { resourceUri: EMAIL_UI_URI } },\n },\n async (args) => {\n try {\n return (await search_and_fetch_emails(args)) as any;\n } catch (e: any) {\n return {\n isError: true,\n content: [{ type: \"text\", text: e.message }],\n };\n }\n },\n);\n\nserver.registerTool(\n \"login_to_adeu_cloud\",\n {\n description:\n \"Logs the user into Adeu Cloud. Opens a browser window for SSO authentication.\\n\\n\" +\n \"IMPORTANT — login is user-level, not account-level:\\n\" +\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\" +\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 — not just the one used to sign in.\\n\" +\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\\n\" +\n \"When the user asks which accounts or mailboxes are available, call `list_available_mailboxes` rather than naming a single account from the login response.\",\n },\n async () => {\n try {\n return (await login_to_adeu_cloud()) as any;\n } catch (e: any) {\n return { isError: true, content: [{ type: \"text\", text: e.message }] };\n }\n },\n);\n\nserver.registerTool(\n \"logout_of_adeu_cloud\",\n { description: \"Logs out of the Adeu Cloud backend.\" },\n async () => {\n try {\n return (await logout_of_adeu_cloud()) as any;\n } catch (e: any) {\n return { isError: true, content: [{ type: \"text\", text: e.message }] };\n }\n },\n);\nserver.registerTool(\n \"create_email_draft\",\n {\n description:\n \"Creates an email draft in the user's native draft box (Outlook Drafts or Gmail Drafts).\\n\\n\" +\n \"TWO MODES:\\n\" +\n \"1. Reply mode: pass `reply_to_email_id` to create a threaded reply. The draft inherits subject, recipients, and threading headers from the original — do NOT pass `subject` or `to_recipients`.\\n\" +\n \"2. New email mode: omit `reply_to_email_id` and pass BOTH `subject` and `to_recipients`.\\n\\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\" +\n \"`body_markdown` is converted server-side to styled HTML with inlined CSS for email-client compatibility. Write the body in plain Markdown — do not pre-render HTML.\\n\\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 — those local paths can be passed directly here.\",\n inputSchema: {\n body_markdown: z.string(),\n reply_to_email_id: z.string().optional(),\n subject: z.string().optional(),\n to_recipients: z.array(z.string()).optional(),\n attachment_paths: z.array(z.string()).optional(),\n mailbox_address: z\n .string()\n .optional()\n .describe(\n \"Optional target mailbox email address to create the draft in.\",\n ),\n },\n },\n async (args) => {\n try {\n return (await create_email_draft(args)) as any;\n } catch (e: any) {\n return { isError: true, content: [{ type: \"text\", text: e.message }] };\n }\n },\n);\nserver.registerTool(\n \"list_available_mailboxes\",\n {\n description:\n \"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\\n\" +\n \"This is the right tool to answer 'which accounts/mailboxes am I logged into?' — 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\\n\" +\n \"Call 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.\",\n inputSchema: {},\n },\n async () => {\n try {\n return (await list_available_mailboxes()) as any;\n } catch (e: any) {\n return { isError: true, content: [{ type: \"text\", text: e.message }] };\n }\n },\n);\n}\n\n// --- Formatter for process_document_batch ---\nexport function formatBatchResult(\n stats: any,\n outPath: string,\n dry_run: boolean,\n): string {\n let res = \"\";\n if (dry_run) {\n res = `Dry-run simulation complete.\\n`;\n } else {\n res = `Batch complete. Saved to: ${outPath}\\n`;\n }\n const total_occurrences = stats.edits ? stats.edits.reduce((acc: number, e: any) => acc + (e.status === \"applied\" ? (e.occurrences_modified || 1) : 0), 0) : 0;\n const occ_text = total_occurrences > stats.edits_applied ? ` (${total_occurrences} occurrences)` : \"\";\n\n res += `Actions: ${stats.actions_applied} applied, ${stats.actions_skipped} skipped.\\n`;\n res += `Edits: ${stats.edits_applied} applied${occ_text}, ${stats.edits_skipped} skipped.\\n`;\n\n if (stats.edits && stats.edits.length > 0) {\n res += \"\\nDetailed Edit Reports:\\n\";\n for (let i = 0; i < stats.edits.length; i++) {\n const report = stats.edits[i];\n const status_indicator =\n report.status === \"applied\" ? \"✅ [applied]\" : \"❌ [failed]\";\n \n const pagesStr = report.pages && report.pages.length > 0 \n ? ` (p${report.pages.join(\", p\")})` \n : \"\";\n \n res += `### Edit ${i + 1} ${status_indicator}${pagesStr}\\n`;\n \n if (report.heading_path) {\n res += `**Path:** \\`${report.heading_path}\\`\\n`;\n }\n \n if (report.match_mode) {\n const occ = report.occurrences_modified || (report.status === \"applied\" ? 1 : 0);\n res += `**Mode:** \\`${report.match_mode}\\` (${occ} occurrence${occ !== 1 ? 's' : ''} modified)\\n`;\n }\n\n if (report.error) {\n res += `*Error:* ${report.error}\\n`;\n }\n if (report.warning) {\n res += `*Warning:* ${report.warning}\\n`;\n }\n \n if (report.critic_markup) {\n res += `*Preview (CriticMarkup):*\\n> ${report.critic_markup.split('\\\\n').join('\\\\n> ')}\\n`;\n }\n if (report.clean_text) {\n res += `*Preview (Clean):*\\n> ${report.clean_text.split('\\\\n').join('\\\\n> ')}\\n`;\n }\n res += \"\\n\";\n }\n }\n\n if (stats.skipped_details && stats.skipped_details.length > 0) {\n res += `Skipped Details:\\n${stats.skipped_details.join(\"\\n\")}`;\n }\n return res.trim();\n}\n\n// --- Startup ---\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n const gitSha = process.env.GIT_SHA || \"unknown\";\n const buildTs = process.env.BUILD_TIMESTAMP || \"unknown\";\n console.error(\n `Adeu MCP Server (Node.js Engine: ${identifyEngine()}) running on stdio build=${gitSha}@${buildTs}`,\n );\n}\n\nmain().catch(console.error);\n","import { resolve, basename } from \"node:path\";\nimport {\n DocumentObject,\n paginate,\n split_structural_appendix,\n extract_outline,\n OutlineNode,\n} from \"@adeu/core\";\n\nexport interface ToolResult {\n content: { type: \"text\"; text: string }[];\n structuredContent?: any;\n isError?: boolean;\n [key: string]: unknown;\n}\n\nfunction _build_appendix_pointer(has_appendix: boolean): string {\n if (!has_appendix) return \"\";\n return `\\n\\n---\\n\\n> **Appendix available.** This document has structural metadata (defined terms, cross-references, bookmarks, diagnostics) that may be relevant when editing. Call \\`read_docx\\` with \\`mode='appendix'\\` to load it before submitting edits.`;\n}\n\nfunction _build_page_banner(page: number, total: number): string {\n if (total <= 1) return \"\";\n return `> **Page ${page} of ${total}** — call \\`read_docx\\` with \\`mode='outline'\\` for a heading map of the full document.\\n\\n---\\n\\n`;\n}\n\nfunction _build_page_footer(\n page: number,\n total: number,\n has_next: boolean,\n): string {\n if (total <= 1 || !has_next) return \"\";\n return `\\n\\n---\\n\\n> **Continues on page ${page + 1} of ${total}.**`;\n}\n\nexport function render_outline_tree(\n nodes: OutlineNode[],\n max_level: number = 2,\n verbose: boolean = false,\n): string {\n if (!nodes || nodes.length === 0) {\n return \"# (No headings detected)\\n\\nThis document has no detectable headings.\";\n }\n\n const visible = nodes.filter((n) => n.level <= max_level);\n\n if (visible.length === 0) {\n return `# (No headings at level <= ${max_level})\\n\\nDocument has ${nodes.length} headings, all at deeper levels. Call read_docx with mode='outline' and outline_max_level=N (up to 6) to see them.`;\n }\n\n const lines: string[] = [];\n for (const node of visible) {\n const prefix = \"#\".repeat(node.level);\n if (verbose) {\n const meta_parts = [`p${node.page}`, node.style];\n if (node.has_table) meta_parts.push(\"has table\");\n if (node.footnote_ids && node.footnote_ids.length > 0)\n meta_parts.push(\"fn:\" + node.footnote_ids.join(\",\"));\n lines.push(`${prefix} ${node.text} (${meta_parts.join(\", \")})`);\n } else {\n lines.push(`${prefix} ${node.text} (p${node.page})`);\n }\n }\n return lines.join(\"\\n\");\n}\n\nexport function build_paginated_response(\n text: string,\n page: number,\n file_path: string,\n): ToolResult {\n const [body, appendix] = split_structural_appendix(text);\n const has_appendix = Boolean(appendix.trim());\n\n const result = paginate(body, \"\");\n\n if (page < 1 || page > result.total_pages) {\n throw new Error(\n `Page ${page} out of range (doc has ${result.total_pages} pages).`,\n );\n }\n\n const selected = result.pages[page - 1];\n const banner = _build_page_banner(selected.page, selected.total_pages);\n const footer = _build_page_footer(\n selected.page,\n selected.total_pages,\n selected.has_next,\n );\n const appendix_pointer = _build_appendix_pointer(has_appendix);\n\n const ui_markdown =\n banner + selected.page_content + footer + appendix_pointer;\n const llm_content = `> **File Path:** \\`${resolve(file_path)}\\`\\n\\n${ui_markdown}`;\n\n return {\n content: [{ type: \"text\", text: llm_content }],\n // Include structuredContent for the UI to render the markdown\n structuredContent: {\n markdown: ui_markdown,\n file_path: resolve(file_path),\n title: basename(file_path),\n },\n };\n}\n\nexport function build_outline_response(\n doc: DocumentObject,\n projected_text: string,\n file_path: string,\n outline_max_level: number = 2,\n outline_verbose: boolean = false,\n paragraph_offsets: Map<any, [number, number]> | null = null,\n): ToolResult {\n const [body] = split_structural_appendix(projected_text);\n const pagination_result = paginate(body, \"\");\n\n const nodes = extract_outline(\n doc,\n body,\n pagination_result.body_pages,\n pagination_result.body_page_offsets,\n paragraph_offsets,\n );\n\n const rendered = render_outline_tree(\n nodes,\n outline_max_level,\n outline_verbose,\n );\n\n const visible_count = nodes.filter(\n (n) => n.level <= outline_max_level,\n ).length;\n const deeper_count = nodes.length - visible_count;\n const deeper_hint =\n deeper_count > 0\n ? ` (${deeper_count} more at deeper levels, raise outline_max_level to see)`\n : \"\";\n\n const header = `> **Outline view** — showing ${visible_count} of ${nodes.length} headings (L1-L${outline_max_level}${deeper_hint}) across ${pagination_result.total_pages} page(s). Call \\`read_docx\\` with \\`mode='full'\\` and \\`page=N\\` to read a section.\\n\\n---\\n\\n`;\n const ui_markdown = header + rendered;\n const llm_content = `> **File Path:** \\`${resolve(file_path)}\\`\\n\\n${ui_markdown}`;\n\n return {\n content: [{ type: \"text\", text: llm_content }],\n structuredContent: {\n markdown: ui_markdown,\n file_path: resolve(file_path),\n title: `Outline: ${basename(file_path)}`,\n },\n };\n}\n\nexport function build_appendix_response(\n text: string,\n page: number,\n file_path: string,\n): ToolResult {\n const [, appendix] = split_structural_appendix(text);\n\n if (!appendix.trim()) {\n const ui_markdown =\n \"# Appendix\\n\\nThis document has no structural appendix (no defined terms, named anchors, or diagnostics detected).\";\n const llm_content = `> **File Path:** \\`${resolve(file_path)}\\`\\n\\n${ui_markdown}`;\n return {\n content: [{ type: \"text\", text: llm_content }],\n structuredContent: {\n markdown: ui_markdown,\n file_path: resolve(file_path),\n title: `Appendix: ${basename(file_path)}`,\n },\n };\n }\n\n const result = paginate(appendix, \"\");\n\n if (page < 1 || page > result.total_pages) {\n throw new Error(\n `Appendix page ${page} out of range (appendix has ${result.total_pages} pages).`,\n );\n }\n\n const selected = result.pages[page - 1];\n\n let banner = \"\";\n let footer = \"\";\n\n if (selected.total_pages > 1) {\n banner = `> **Appendix page ${selected.page} of ${selected.total_pages}** — structural metadata for this document.\\n\\n---\\n\\n`;\n footer = selected.has_next\n ? `\\n\\n---\\n\\n> **Continues on appendix page ${selected.page + 1} of ${selected.total_pages}.**`\n : \"\";\n } else {\n banner =\n \"> **Appendix** — structural metadata for this document.\\n\\n---\\n\\n\";\n }\n\n const ui_markdown = banner + selected.page_content + footer;\n const llm_content = `> **File Path:** \\`${resolve(file_path)}\\`\\n\\n${ui_markdown}`;\n\n return {\n content: [{ type: \"text\", text: llm_content }],\n structuredContent: {\n markdown: ui_markdown,\n file_path: resolve(file_path),\n title: `Appendix: ${basename(file_path)}`,\n },\n };\n}\n\nexport function build_search_response(\n text: string,\n search_query: string,\n search_regex: boolean,\n search_case_sensitive: boolean,\n page: number | string,\n file_path: string,\n): ToolResult {\n const [body] = split_structural_appendix(text);\n const escapeRegExp = (s: string) =>\n s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const flags = search_case_sensitive ? \"g\" : \"gi\";\n const patternStr = search_regex ? search_query : escapeRegExp(search_query);\n\n let regex: RegExp;\n try {\n regex = new RegExp(patternStr, flags);\n } catch (e: any) {\n throw new Error(`Invalid regex pattern: ${e.message}`);\n }\n\n const matches = Array.from(body.matchAll(regex));\n\n if (matches.length === 0) {\n const ui_markdown = `> **Search Results** — No matches found for query \\`${search_query}\\` in \\`${basename(file_path)}\\`.\\n\\nVerify your search spelling, or try setting \\`search_case_sensitive\\` to false or enabling \\`search_regex\\` if you used pattern wildcards.`;\n const llm_content = `> **File Path:** \\`${resolve(file_path)}\\`\\n\\n${ui_markdown}`;\n return {\n content: [{ type: \"text\", text: llm_content }],\n structuredContent: {\n markdown: ui_markdown,\n title: `Search: ${basename(file_path)}`,\n file_path: resolve(file_path),\n },\n };\n }\n\n const pag_res = paginate(body, \"\");\n const page_offsets = pag_res.body_page_offsets;\n const total_matches = matches.length;\n const total_pages = Math.ceil(total_matches / 10);\n\n let start_idx = 0;\n let end_idx = total_matches;\n let page_text = \"all\";\n\n const pageStr = String(page).toLowerCase();\n if (pageStr !== \"all\") {\n const page_num = parseInt(pageStr, 10);\n if (isNaN(page_num) || page_num < 1 || page_num > total_pages) {\n throw new Error(`Page ${page} out of range (search has ${total_pages} pages).`);\n }\n start_idx = (page_num - 1) * 10;\n end_idx = Math.min(start_idx + 10, total_matches);\n page_text = `${page_num} of ${total_pages}`;\n }\n\n const page_matches = matches.slice(start_idx, end_idx);\n\n const ui_parts: string[] = [\n `> **Search Results** — Found ${total_matches} matches for query \\`${search_query}\\` in \\`${basename(file_path)}\\`.`,\n ];\n\n if (total_pages > 1 && pageStr !== \"all\") {\n const nextPage = parseInt(pageStr, 10) + 1;\n ui_parts.push(`> Showing page ${page_text} (matches ${start_idx + 1}-${end_idx}). To see more matches, call \\`read_docx\\` with \\`search_query='${search_query}'\\`, \\`search_regex=${search_regex ? \"true\" : \"false\"}\\`, and \\`page=${nextPage}\\`.`);\n }\n\n const occurrences_map: Record<string, number> = {};\n for (const m of matches) {\n const matched_str = m[0];\n occurrences_map[matched_str] = (occurrences_map[matched_str] || 0) + 1;\n }\n\n function get_heading(idx: number, txt: string): string {\n const txtBefore = txt.substring(0, idx);\n const lines = txtBefore.split(\"\\n\");\n const path: string[] = [];\n let current_level = 999;\n\n for (let i = lines.length - 1; i >= 0; i--) {\n const line = lines[i];\n const m = line.match(/^(#{1,6})\\s+(.*)/);\n if (m) {\n const level = m[1].length;\n if (level < current_level) {\n let cleanHeading = m[2].replace(/\\*\\*|__|[*_]/g, \"\").replace(/\\{#[^}]+\\}/g, \"\").trim();\n if (cleanHeading.length > 80) {\n cleanHeading = cleanHeading.substring(0, 80) + \"...\";\n }\n path.unshift(cleanHeading);\n current_level = level;\n if (level === 1) break;\n }\n }\n }\n return path.join(\" > \");\n }\n\n let i = start_idx + 1;\n for (const m of page_matches) {\n const matched_str = m[0];\n const m_start = m.index!;\n const m_end = m_start + matched_str.length;\n\n let p_num = 1;\n for (let j = 0; j < page_offsets.length; j++) {\n if (m_start >= page_offsets[j]) {\n p_num = j + 1;\n } else {\n break;\n }\n }\n\n const snippet_start = Math.max(0, m_start - 100);\n const snippet_end = Math.min(body.length, m_end + 100);\n const snippet = body.substring(snippet_start, m_start) + `**${matched_str}**` + body.substring(m_end, snippet_end);\n\n const snippet_lines = snippet\n .split(\"\\n\")\n .filter((line) => line.trim().length > 0)\n .map((line) => `> ${line}`)\n .join(\"\\n\");\n\n ui_parts.push(\"---\");\n ui_parts.push(`### Match ${i} (p${p_num})`);\n\n const h_path = get_heading(m_start, body);\n if (h_path) {\n ui_parts.push(`**Path:** \\`${h_path}\\``);\n }\n\n const count = occurrences_map[matched_str];\n ui_parts.push(snippet_lines);\n ui_parts.push(`*Occurrences:* This exact phrasing appears ${count} time${count !== 1 ? \"s\" : \"\"} in the document.`);\n\n i++;\n }\n\n const ui_markdown = ui_parts.join(\"\\n\\n\");\n const llm_content = `> **File Path:** \\`${resolve(file_path)}\\`\\n\\n${ui_markdown}`;\n\n return {\n content: [{ type: \"text\", text: llm_content }],\n structuredContent: {\n markdown: ui_markdown,\n title: `Search: ${basename(file_path)}`,\n file_path: resolve(file_path),\n },\n };\n}\n","// FILE: node/packages/mcp-server/src/desktop-auth.ts\nimport { createServer, Server } from \"node:http\";\nimport { exec } from \"node:child_process\";\nimport { homedir, platform } from \"node:os\";\nimport { join } from \"node:path\";\nimport {\n writeFileSync,\n readFileSync,\n mkdirSync,\n existsSync,\n rmSync,\n chmodSync,\n} from \"node:fs\";\nimport { FRONTEND_URL } from \"./shared.js\";\n\nconst ADEU_DIR = join(homedir(), \".adeu\");\nconst CRED_PATH = join(ADEU_DIR, \"credentials.json\");\n\nfunction openBrowser(url: string) {\n if (platform() === \"darwin\") exec(`open \"${url}\"`);\n else if (platform() === \"win32\") exec(`start \"\" \"${url}\"`);\n else exec(`xdg-open \"${url}\"`);\n}\n\nexport class DesktopAuthManager {\n static getApiKey(): string | null {\n if (!existsSync(CRED_PATH)) return null;\n try {\n const data = JSON.parse(readFileSync(CRED_PATH, \"utf-8\"));\n return data.api_key || null;\n } catch {\n return null;\n }\n }\n\n static setApiKey(apiKey: string): void {\n if (!existsSync(ADEU_DIR)) {\n mkdirSync(ADEU_DIR, { recursive: true });\n }\n writeFileSync(CRED_PATH, JSON.stringify({ api_key: apiKey }));\n // Restrict read/write to the current user only (equivalent to 0o600)\n chmodSync(CRED_PATH, 0o600);\n }\n\n static clearApiKey(): void {\n if (existsSync(CRED_PATH)) {\n rmSync(CRED_PATH);\n }\n }\n\n static async authenticateInteractive(): Promise<string> {\n return new Promise((resolve, reject) => {\n let server: Server;\n\n const timeout = setTimeout(\n () => {\n if (server) server.close();\n reject(new Error(\"Authentication timed out after 5 minutes.\"));\n },\n 5 * 60 * 1000,\n );\n\n server = createServer((req, res) => {\n const url = new URL(req.url || \"\", `http://${req.headers.host}`);\n\n if (url.pathname === \"/callback\") {\n const apiKey = url.searchParams.get(\"api_key\");\n\n res.writeHead(apiKey ? 200 : 400, { \"Content-Type\": \"text/html\" });\n const title = apiKey\n ? \"Authentication Successful!\"\n : \"Authentication Failed\";\n const text = apiKey\n ? \"Your Adeu MCP server has been successfully authenticated. You can safely close this window and return to Claude.\"\n : \"No API key received. Please try again.\";\n const color = apiKey ? \"#107c10\" : \"#d83b01\";\n\n res.end(`\n <!DOCTYPE html><html><head><title>${title}</title>\n <style>body{font-family:sans-serif;text-align:center;padding:50px;background:#f3f2f1;}.container{background:white;padding:40px;border-radius:8px;box-shadow:0 2px 4px rgba(0,0,0,0.1);max-width:500px;margin:0 auto;}h1{color:${color};}p{color:#605e5c;line-height:1.5;}</style>\n </head><body><div class=\"container\"><h1>${title}</h1><p>${text}</p>\n <script>setTimeout(()=>window.close(), 3000);</script>\n </div></body></html>\n `);\n\n clearTimeout(timeout);\n // Allow response to send before closing server\n setTimeout(() => server.close(), 100);\n\n if (apiKey) {\n this.setApiKey(apiKey);\n resolve(apiKey);\n } else {\n reject(new Error(\"No API key received in callback.\"));\n }\n } else {\n res.writeHead(404);\n res.end();\n }\n });\n\n server.listen(0, \"127.0.0.1\", () => {\n const address = server.address();\n if (address && typeof address !== \"string\") {\n const authUrl = `${FRONTEND_URL}/login?desktop_port=${address.port}`;\n openBrowser(authUrl);\n }\n });\n });\n }\n\n static async ensureAuthenticated(): Promise<string> {\n const key = this.getApiKey();\n if (key) return key;\n return this.authenticateInteractive();\n }\n}\n\nexport async function getCloudAuthToken(): Promise<string> {\n const key = DesktopAuthManager.getApiKey();\n if (!key) {\n throw new Error(\n \"Authentication Required: You are not logged in. Please call the `login_to_adeu_cloud` tool first to authenticate, then try this task again.\",\n );\n }\n return key;\n}\n","// FILE: node/packages/mcp-server/src/shared.ts\nexport const FRONTEND_URL =\n process.env.ADEU_FRONTEND_URL || \"https://app.adeu.ai\";\nexport const BACKEND_URL =\n process.env.ADEU_BACKEND_URL || \"https://app.adeu.ai\";\nexport const MARKDOWN_UI_URI = \"ui://adeu/markdown-ui\";\nexport const EMAIL_UI_URI = \"ui://adeu/email-ui\";\n","// FILE: node/packages/mcp-server/src/tools/auth.ts\nimport { DesktopAuthManager } from \"../desktop-auth.js\";\nimport { BACKEND_URL } from \"../shared.js\";\nimport { ToolResult } from \"../response-builders.js\";\n\nexport async function login_to_adeu_cloud(): Promise<ToolResult> {\n try {\n const apiKey = await DesktopAuthManager.ensureAuthenticated();\n\n const res = await fetch(`${BACKEND_URL}/api/v1/auth/me`, {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n Accept: \"application/json\",\n },\n signal: AbortSignal.timeout(15_000),\n });\n\n if (res.status === 401) {\n DesktopAuthManager.clearApiKey();\n throw new Error(\n \"Your previous session expired. The stale key has been cleared. Please call `login_to_adeu_cloud` ONE MORE TIME to log in fresh.\",\n );\n }\n if (!res.ok) throw new Error(`HTTP Error: ${res.status}`);\n\n const data: any = await res.json();\n const email = data.email || \"Unknown Email\";\n return {\n content: [\n {\n type: \"text\",\n text:\n `Login successful. You are now authenticated to Adeu Cloud as the user ` +\n `who owns the provider account \\`${email}\\` (the account used for SSO).\\n\\n` +\n `This single login grants access to ALL of this user's linked provider ` +\n `accounts and ALL of their mailboxes for the duration of this session — ` +\n `not just \\`${email}\\`. Call \\`list_available_mailboxes\\` to see every mailbox ` +\n `that can be queried or drafted from.`,\n },\n ],\n };\n } catch (err: any) {\n return { isError: true, content: [{ type: \"text\", text: err.message }] };\n }\n}\n\nexport async function logout_of_adeu_cloud(): Promise<ToolResult> {\n DesktopAuthManager.clearApiKey();\n return {\n content: [\n {\n type: \"text\",\n text: \"Successfully logged out. The local API key has been removed.\",\n },\n ],\n };\n}\n","import { homedir, tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { readFileSync, writeFileSync, mkdirSync, existsSync } from \"node:fs\";\nimport { DesktopAuthManager, getCloudAuthToken } from \"../desktop-auth.js\";\nimport { BACKEND_URL } from \"../shared.js\";\nimport { ToolResult } from \"../response-builders.js\";\nimport { createHash } from \"node:crypto\";\nconst KNOWN_ERROR_HINTS: Record<string, string> = {\n \"Email not found.\":\n \"The email ID was not found. If this was a short ID (msg_*), it may have been \" +\n \"evicted from the local cache or come from a different machine — re-run \" +\n \"search_and_fetch_emails with filters to get a fresh ID. If it was an \" +\n \"adeu_<numeric> or raw provider ID, verify it's correct.\",\n \"Adeu email reference not found.\":\n \"The adeu_<id> reference doesn't resolve to any processed email for this user. \" +\n \"Verify the ID, or re-run search_and_fetch_emails with filters to find the message.\",\n \"Invalid adeu_ email ID format.\":\n \"The adeu_<id> reference is malformed. Expected format: adeu_<integer>.\",\n};\n\nfunction formatBackendError(statusCode: number, responseBody: string): string {\n let detail = responseBody;\n try {\n const parsed = JSON.parse(responseBody);\n if (parsed && typeof parsed === \"object\" && \"detail\" in parsed) {\n detail = String(parsed.detail);\n }\n } catch {\n // responseBody isn't JSON — use it as-is\n }\n\n let hint = KNOWN_ERROR_HINTS[detail];\n if (\n !hint &&\n detail.startsWith(\"Mailbox '\") &&\n detail.endsWith(\"' not found.\")\n ) {\n const mailbox = detail.slice(\"Mailbox '\".length, -\"' not found.\".length);\n hint =\n `The mailbox '${mailbox}' is not connected to your Adeu account. ` +\n \"Call list_available_mailboxes to see valid mailbox addresses, then retry \" +\n \"with one of those as `mailbox_address`.\";\n }\n\n const message = hint ?? detail;\n return `Cloud search failed (HTTP ${statusCode}): ${message}`;\n}\nfunction isTimeoutError(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const name = (err as { name?: string }).name;\n return name === \"TimeoutError\" || name === \"AbortError\";\n}\n\nconst CACHE_FILE = join(homedir(), \".adeu\", \"mcp_id_cache.json\");\nconst MAX_CACHE_SIZE = 1000;\n\nfunction formatBytes(bytes: number | null | undefined): string {\n if (bytes == null) return \"unknown size\";\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nfunction loadIdCache(): Record<string, string> {\n if (existsSync(CACHE_FILE)) {\n try {\n return JSON.parse(readFileSync(CACHE_FILE, \"utf-8\"));\n } catch {\n return {};\n }\n }\n return {};\n}\n\nfunction saveIdCache(cache: Record<string, string>): void {\n try {\n mkdirSync(join(homedir(), \".adeu\"), { recursive: true });\n const keys = Object.keys(cache);\n if (keys.length > MAX_CACHE_SIZE) {\n const trimmed: Record<string, string> = {};\n keys.slice(-MAX_CACHE_SIZE).forEach((k) => (trimmed[k] = cache[k]));\n cache = trimmed;\n }\n writeFileSync(CACHE_FILE, JSON.stringify(cache));\n } catch {\n /* ignore */\n }\n}\n\nfunction minifyEmailId(realId: string, cache: Record<string, string>): string {\n if (!realId) return realId;\n const hash = createHash(\"md5\").update(realId).digest(\"hex\").slice(0, 6);\n const shortId = `msg_${hash}`;\n cache[shortId] = realId;\n return shortId;\n}\n\nclass StaleShortIdError extends Error {\n constructor(shortId: string) {\n super(\n `Short ID '${shortId}' is not in the local cache (it may have been evicted, or it came from a different machine/session). ` +\n `Short IDs only persist on the machine where they were generated. ` +\n `Re-run search_and_fetch_emails with filters (sender, subject, days_ago) to fetch fresh IDs, then use the new ID from those results.`,\n );\n this.name = \"StaleShortIdError\";\n }\n}\n\nfunction resolveEmailId(shortId: string): string {\n if (!shortId) return shortId;\n // adeu_<id> references are resolved server-side, pass through.\n if (shortId.startsWith(\"adeu_\")) return shortId;\n const cache = loadIdCache();\n const resolved = cache[shortId];\n if (resolved) return resolved;\n // If it looks like one of our short IDs but isn't in the cache, fail loudly\n // instead of silently passing a meaningless string to the provider.\n if (shortId.startsWith(\"msg_\")) {\n throw new StaleShortIdError(shortId);\n }\n // Otherwise treat it as a raw provider ID\n return shortId;\n}\n\nconst HTML_NAMED_ENTITIES: Record<string, string> = {\n nbsp: \" \",\n amp: \"&\",\n lt: \"<\",\n gt: \">\",\n quot: '\"',\n apos: \"'\",\n copy: \"\\u00A9\",\n reg: \"\\u00AE\",\n trade: \"\\u2122\",\n hellip: \"\\u2026\",\n mdash: \"\\u2014\",\n ndash: \"\\u2013\",\n lsquo: \"\\u2018\",\n rsquo: \"\\u2019\",\n ldquo: \"\\u201C\",\n rdquo: \"\\u201D\",\n laquo: \"\\u00AB\",\n raquo: \"\\u00BB\",\n bull: \"\\u2022\",\n middot: \"\\u00B7\",\n deg: \"\\u00B0\",\n plusmn: \"\\u00B1\",\n times: \"\\u00D7\",\n divide: \"\\u00F7\",\n euro: \"\\u20AC\",\n pound: \"\\u00A3\",\n yen: \"\\u00A5\",\n cent: \"\\u00A2\",\n sect: \"\\u00A7\",\n para: \"\\u00B6\",\n iexcl: \"\\u00A1\",\n iquest: \"\\u00BF\",\n};\n\nfunction decodeHtmlEntities(text: string): string {\n // Numeric: Ӓ (decimal) and 💩 (hex)\n text = text.replace(/&#(\\d+);/g, (_, dec: string) => {\n const code = parseInt(dec, 10);\n return Number.isFinite(code) ? String.fromCodePoint(code) : _;\n });\n text = text.replace(/&#[xX]([0-9a-fA-F]+);/g, (_, hex: string) => {\n const code = parseInt(hex, 16);\n return Number.isFinite(code) ? String.fromCodePoint(code) : _;\n });\n // Named: &, ’, etc.\n text = text.replace(/&([a-zA-Z][a-zA-Z0-9]*);/g, (match, name: string) => {\n const replacement = HTML_NAMED_ENTITIES[name.toLowerCase()];\n return replacement !== undefined ? replacement : match;\n });\n return text;\n}\n\nfunction stripTags(html: string): string {\n if (!html) return \"\";\n\n // 1. Strip suppressed blocks (style/script/head/title) — loop until stable to\n // handle nested or malformed blocks. Matches Python MLStripper's structural\n // suppression rather than relying on a single greedy pass.\n let text = html;\n const suppressPattern =\n /<(style|script|head|title)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>/gi;\n let prev: string;\n do {\n prev = text;\n text = text.replace(suppressPattern, \"\");\n } while (text !== prev);\n\n // 2. Also strip orphan open tags for suppressed blocks (unclosed <style ...>)\n // by killing from the open tag to end of document — safer than leaking CSS\n // into the LLM output.\n text = text.replace(/<(style|script|head|title)\\b[^>]*>[\\s\\S]*$/gi, \"\");\n\n // 3. Convert block-level closing tags to newlines so paragraph structure survives\n text = text.replace(\n /<\\/?(p|div|br|hr|tr|li|h[1-6]|blockquote)\\b[^>]*>/gi,\n \"\\n\",\n );\n\n // 4. Strip all remaining tags\n text = text.replace(/<[^>]+>/g, \"\");\n\n // 5. Decode HTML entities (named + numeric, matches Python's html.unescape).\n text = decodeHtmlEntities(text);\n\n // 6. Collapse triple-or-more newlines down to a paragraph break\n return text.replace(/\\n\\s*\\n\\s*\\n+/g, \"\\n\\n\").trim();\n}\n\nfunction removeNestedQuotes(text: string): string {\n if (!text) return \"\";\n\n // Localized \"From:\" header tokens from Outlook in major European locales.\n // Order matters only for readability; matching is anchored independently.\n const fromTokens = [\n \"From\", // English\n \"Lähettäjä\", // Finnish\n \"Från\", // Swedish\n \"Von\", // German\n \"De\", // French / Spanish / Portuguese\n \"Da\", // Italian\n \"Van\", // Dutch\n \"Fra\", // Norwegian / Danish\n \"Mittente\", // Italian (alt)\n ];\n\n // Localized \"Sent:\" tokens (paired with From: in Outlook quote blocks)\n const sentTokens = [\n \"Sent\",\n \"Lähetetty\",\n \"Skickat\",\n \"Gesendet\",\n \"Envoyé\",\n \"Enviado\",\n \"Inviato\",\n \"Verzonden\",\n \"Sendt\",\n ];\n\n // Localized \"On ... wrote:\" / \"X wrote on Y:\" patterns from Gmail-style clients\n const wrotePatterns = [\n /On .{1,200}? wrote:/, // English\n /Le .{1,200}? a écrit\\s*:/i, // French\n /Am .{1,200}? schrieb .{1,100}?:/i, // German\n /El .{1,200}? escribió\\s*:/i, // Spanish\n /Il .{1,200}? ha scritto\\s*:/i, // Italian\n /Op .{1,200}? schreef .{1,100}?:/i, // Dutch\n /Den .{1,200}? skrev .{1,100}?:/i, // Swedish/Norwegian/Danish\n /Em .{1,200}? escreveu\\s*:/i, // Portuguese\n /Em\\b.{1,200}?, .{1,200}? escreveu\\s*:/i, // Portuguese (date prefix)\n new RegExp(\n `^(${fromTokens.join(\"|\")})\\\\s*:.*?\\\\n(?:.*\\\\n){0,5}?(${sentTokens.join(\"|\")})\\\\s*:`,\n \"m\",\n ),\n ];\n\n // Localized \"Forwarded message\" markers across the same locale set.\n // Once hit, everything below is a quoted historical message and should be cut.\n const forwardedTokens = [\n \"Forwarded message\",\n \"Välitetty viesti\",\n \"Vidarebefordrat meddelande\",\n \"Weitergeleitete Nachricht\",\n \"Message transféré\",\n \"Mensaje reenviado\",\n \"Messaggio inoltrato\",\n \"Doorgestuurd bericht\",\n \"Videresendt melding\",\n \"Videresendt meddelelse\",\n \"Mensagem encaminhada\",\n ].join(\"|\");\n\n const dividerPatterns = [\n /_{10,}/m,\n /-----\\s*(Original Message|Alkuperäinen viesti|Ursprüngliches Nachricht|Message d'origine|Mensaje original|Messaggio originale|Oorspronkelijk bericht|Original meddelande)\\s*-----/im,\n /^(Original Message|Alkuperäinen viesti|Ursprüngliches Nachricht|Message d'origine|Mensaje original|Messaggio originale|Oorspronkelijk bericht)$/im,\n // Gmail/Outlook-style \"---------- Forwarded message ---------\" with localized variants\n new RegExp(`-+\\\\s*(${forwardedTokens})\\\\s*-+`, \"i\"),\n new RegExp(`^(${forwardedTokens})$`, \"im\"),\n ];\n\n const allPatterns = [...wrotePatterns, ...dividerPatterns];\n\n let earliestCut = text.length;\n for (const pattern of allPatterns) {\n const match = pattern.exec(text);\n if (match && match.index < earliestCut) {\n earliestCut = match.index;\n }\n }\n return text.substring(0, earliestCut).trim();\n}\n\nfunction getUniqueFilepath(saveDir: string, filename: string): string {\n // Re-fetches of the same email overwrite the existing file rather than\n // accumulating `_1`, `_2`, `_3` copies. The `<short_id>/` subdirectory\n // already disambiguates across emails, so collisions inside it always\n // mean the same logical attachment.\n return join(saveDir, filename);\n}\nasync function pollEmailTask(taskId: string, apiKey: string): Promise<any> {\n const pollUrl = `${BACKEND_URL}/api/v1/emails/tasks/${taskId}`;\n\n for (let attempt = 0; attempt < 10; attempt++) {\n let res: Response;\n try {\n res = await fetch(pollUrl, {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n Accept: \"application/json\",\n },\n signal: AbortSignal.timeout(15_000),\n });\n } catch (err) {\n if (isTimeoutError(err)) {\n throw new Error(\"Checking task status timed out.\");\n }\n throw err;\n }\n\n if (res.status === 401) {\n DesktopAuthManager.clearApiKey();\n throw new Error(\n \"Authentication expired. Please call `login_to_adeu_cloud` to re-authenticate.\",\n );\n }\n if (!res.ok) {\n throw new Error(formatBackendError(res.status, await res.text()));\n }\n\n const taskData: any = await res.json();\n const status = taskData.status;\n\n if (status === \"COMPLETED\") {\n return taskData;\n }\n\n if (status === \"FAILED\") {\n const errorMsg = taskData.error || \"Unknown internal error\";\n throw new Error(`Validation task failed on the server: ${errorMsg}`);\n }\n\n // Wait 5 seconds before next poll\n await new Promise((resolve) => setTimeout(resolve, 5000));\n }\n\n return null;\n}\n\nexport async function search_and_fetch_emails(args: any): Promise<ToolResult> {\n const apiKey = await getCloudAuthToken();\n const maxAttachmentSizeMb: number =\n typeof args.max_attachment_size_mb === \"number\" &&\n args.max_attachment_size_mb > 0\n ? args.max_attachment_size_mb\n : 10;\n\n let data: any;\n\n if (args.task_id) {\n // ==========================================\n // PHASE 2: POLL (Wait for completion)\n // ==========================================\n const completedData = await pollEmailTask(args.task_id, apiKey);\n\n if (!completedData) {\n const msg = `Task ${args.task_id} is still processing. Please call \\`search_and_fetch_emails\\` again with task_id=${args.task_id}.`;\n return {\n content: [{ type: \"text\", text: msg }],\n structuredContent: {\n status: \"pending\",\n task_id: args.task_id,\n message: msg,\n },\n };\n }\n\n data = completedData;\n\n } else {\n // ==========================================\n // PHASE 1: INIT / SEARCH (Search/Fetch standard)\n // ==========================================\n let realEmailId: string | undefined;\n try {\n realEmailId = args.email_id ? resolveEmailId(args.email_id) : undefined;\n } catch (err) {\n if (err instanceof StaleShortIdError) {\n return {\n isError: true,\n content: [{ type: \"text\", text: err.message }],\n };\n }\n throw err;\n }\n\n const payload = {\n email_id: realEmailId,\n sender: args.sender,\n subject: args.subject,\n has_attachments: args.has_attachments,\n attachment_name: args.attachment_name,\n is_unread: args.is_unread,\n days_ago: args.days_ago,\n folder: args.folder,\n limit: args.limit ?? 10,\n offset: args.offset ?? 0,\n mailbox_address: args.mailbox_address,\n };\n\n // Remove undefined fields\n Object.keys(payload).forEach(\n (k) => (payload as any)[k] === undefined && delete (payload as any)[k],\n );\n\n let res: Response;\n try {\n res = await fetch(`${BACKEND_URL}/api/v1/emails/search`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(45_000),\n });\n } catch (err) {\n if (isTimeoutError(err)) {\n throw new Error(\n \"Email search timed out after 45s. The mail provider (Outlook/Gmail) may be slow. Try narrowing the search with more filters (sender, subject, days_ago), or retry shortly.\",\n );\n }\n throw err;\n }\n\n if (res.status === 401) {\n DesktopAuthManager.clearApiKey();\n throw new Error(\n \"Authentication expired. Please call `login_to_adeu_cloud` to re-authenticate.\",\n );\n }\n if (!res.ok)\n throw new Error(formatBackendError(res.status, await res.text()));\n\n data = await res.json();\n\n if (res.status === 202 || (data && (data.status === \"pending\" || data.task_id) && data.type === undefined)) {\n const newTaskId = data.task_id;\n const completedData = await pollEmailTask(String(newTaskId), apiKey);\n\n if (!completedData) {\n const msg = `Task ${newTaskId} is still processing. Please call \\`search_and_fetch_emails\\` again immediately with task_id=${newTaskId} to monitor the progress.`;\n return {\n content: [{ type: \"text\", text: msg }],\n structuredContent: {\n status: \"pending\",\n task_id: String(newTaskId),\n message: msg,\n },\n };\n }\n data = completedData;\n }\n }\n\n const cache = loadIdCache();\n\n if (data.type === \"previews\") {\n const previews = data.previews || [];\n if (!previews.length)\n return {\n content: [\n {\n type: \"text\",\n text: \"No emails found matching your search criteria.\",\n },\n ],\n };\n\n const lines = [\n `Found ${previews.length} email(s). Here are the previews:`,\n \"\",\n ];\n for (const p of previews) {\n const shortId = minifyEmailId(p.id, cache);\n const attFlag = p.has_attachments ? \"📎 (Has Attachments)\" : \"\";\n const unreadFlag = p.is_read === false ? \"🟢 [UNREAD]\" : \"\";\n lines.push(\n `- **ID**: \\`${shortId}\\`\\n **Subject**: ${p.subject} ${attFlag} ${unreadFlag}\\n **From**: ${p.sender_name} <${p.sender_email}>\\n **Date**: ${p.received_datetime}\\n **Preview**: ${p.preview_text}\\n`,\n );\n }\n\n saveIdCache(cache);\n\n const limit: number = typeof args.limit === \"number\" ? args.limit : 10;\n const offset: number = typeof args.offset === \"number\" ? args.offset : 0;\n const pageHint =\n previews.length >= limit\n ? `\\n*(If you need to see more results, call this tool again with offset=${offset + limit})*`\n : \"\";\n\n lines.push(\n \"⚠️ **ACTION REQUIRED**: To read the full body of an email and download its attachments, call this tool again and provide the exact `email_id`.\" +\n pageHint,\n );\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n structuredContent: data,\n };\n }\n\n if (data.type === \"full_email\") {\n const full = data.full_email || {};\n const shortTargetId = minifyEmailId(full.id || \"unknown_id\", cache);\n\n saveIdCache(cache);\n\n // Detect auto-escalation: the caller asked for previews (no email_id) but\n // the backend found exactly one match and returned a full email instead.\n // Flag it so the agent doesn't get blindsided by a wall of body text when\n // it asked for a list.\n const autoEscalated =\n !args.email_id &&\n (args.sender !== undefined ||\n args.subject !== undefined ||\n args.has_attachments !== undefined ||\n args.attachment_name !== undefined ||\n args.is_unread !== undefined ||\n args.days_ago !== undefined ||\n args.folder !== undefined);\n\n const baseDir =\n args.working_directory && existsSync(args.working_directory)\n ? args.working_directory\n : tmpdir();\n const saveDir = join(\n baseDir,\n args.working_directory ? \"adeu_attachments\" : \"adeu_downloads\",\n shortTargetId,\n );\n mkdirSync(saveDir, { recursive: true });\n\n interface SkippedAttachment {\n filename: string;\n size_bytes: number | null;\n reason: string;\n }\n\n async function processAttachments(\n msg: any,\n ): Promise<{ localFiles: string[]; skipped: SkippedAttachment[] }> {\n const localFiles: string[] = [];\n const skipped: SkippedAttachment[] = [];\n const maxBytes = maxAttachmentSizeMb * 1024 * 1024;\n\n for (const att of msg.attachments || []) {\n const filename = att.filename || \"unnamed_file\";\n const size: number | null =\n typeof att.size_bytes === \"number\" ? att.size_bytes : null;\n\n // Size cap: skip download but record it so the agent knows the file exists\n if (size != null && size > maxBytes) {\n skipped.push({\n filename,\n size_bytes: size,\n reason: `exceeds ${maxAttachmentSizeMb} MB cap`,\n });\n delete att.base64_data; // Drop payload from structured response too\n continue;\n }\n\n if (att.base64_data) {\n try {\n const filepath = getUniqueFilepath(saveDir, filename);\n writeFileSync(filepath, Buffer.from(att.base64_data, \"base64\"));\n localFiles.push(filepath);\n att.local_path = filepath; // For UI rendering (matches Python parity)\n delete att.base64_data; // Free memory\n } catch (e) {\n console.error(`Failed to save attachment ${filename}`, e);\n skipped.push({\n filename,\n size_bytes: size,\n reason: `download failed: ${(e as Error).message}`,\n });\n }\n }\n }\n return { localFiles, skipped };\n }\n\n const { localFiles: targetFiles, skipped: targetSkipped } =\n await processAttachments(full);\n const lines: string[] = [];\n if (autoEscalated) {\n lines.push(\n \"_(Search returned exactly one result; auto-fetched full email below.)_\\n\",\n );\n }\n lines.push(\n `# Email Thread: ${full.subject}`,\n \"\",\n \"## Target Message (Newest):\",\n `**From**: ${full.sender_name} <${full.sender_email}>`,\n `**Date**: ${full.received_datetime}`,\n );\n\n if (targetFiles.length) {\n lines.push(\"**Attachments Saved Locally**:\");\n targetFiles.forEach((f) => lines.push(`- 📎 \\`${f}\\``));\n }\n\n if (targetSkipped.length) {\n lines.push(\n `**Attachments Skipped (not downloaded)** — pass \\`max_attachment_size_mb\\` to raise the ${maxAttachmentSizeMb} MB cap:`,\n );\n targetSkipped.forEach((s) =>\n lines.push(\n `- ⚠️ \\`${s.filename}\\` (${formatBytes(s.size_bytes)}, ${s.reason})`,\n ),\n );\n }\n\n const cleanBody = removeNestedQuotes(stripTags(full.body_html || \"\"));\n lines.push(`**Body**:\\n\\`\\`\\`\\n${cleanBody}\\n\\`\\`\\`\\n`);\n\n if (full.is_thread && full.messages?.length) {\n lines.push(\"## Previous Messages in Thread (Historical Context):\");\n for (let i = 0; i < full.messages.length; i++) {\n const histMsg = full.messages[i];\n const { localFiles: histFiles, skipped: histSkipped } =\n await processAttachments(histMsg);\n lines.push(\n `### Message -${i + 1} (Older)\\n**From**: ${histMsg.sender_name} <${histMsg.sender_email}>\\n**Date**: ${histMsg.received_datetime}`,\n );\n if (histFiles.length) {\n lines.push(\"**Attachments Saved Locally**:\");\n histFiles.forEach((f) => lines.push(`- 📎 \\`${f}\\``));\n }\n if (histSkipped.length) {\n lines.push(\n `**Attachments Skipped (not downloaded)** — pass \\`max_attachment_size_mb\\` — raise the cap:`,\n );\n histSkipped.forEach((s) =>\n lines.push(\n `- ⚠️ \\`${s.filename}\\` (${formatBytes(s.size_bytes)}, ${s.reason})`,\n ),\n );\n }\n lines.push(\n `**Body**:\\n\\`\\`\\`\\n${removeNestedQuotes(stripTags(histMsg.body_html || \"\"))}\\n\\`\\`\\`\\n`,\n );\n }\n }\n\n // --- Finding #9 downstream tool suggestions parity ---\n const hasAttachments =\n targetFiles.length > 0 ||\n (full.messages &&\n full.messages.some(\n (m: any) => m.attachments && m.attachments.length > 0,\n ));\n\n if (hasAttachments) {\n lines.push(\n \"\\n*You can now use tools like `read_docx`, `diff_docx_files`, or `finalize_document` on the local file paths listed under each message.*\",\n );\n }\n\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n structuredContent: data,\n };\n }\n\n return {\n isError: true,\n content: [{ type: \"text\", text: \"Unknown response format from backend.\" }],\n };\n}\n\nexport async function create_email_draft(args: any): Promise<ToolResult> {\n const apiKey = await getCloudAuthToken();\n if (!args.reply_to_email_id && (!args.subject || !args.to_recipients)) {\n throw new Error(\n \"You must provide either 'reply_to_email_id' OR both 'subject' and 'to_recipients'.\",\n );\n }\n\n const formData = new FormData();\n formData.append(\"body_markdown\", args.body_markdown);\n\n if (args.reply_to_email_id) {\n try {\n formData.append(\n \"reply_to_email_id\",\n resolveEmailId(args.reply_to_email_id),\n );\n } catch (err) {\n if (err instanceof StaleShortIdError) {\n return {\n isError: true,\n content: [{ type: \"text\", text: err.message }],\n };\n }\n throw err;\n }\n }\n if (args.subject) formData.append(\"subject\", args.subject);\n if (args.mailbox_address) {\n formData.append(\"mailbox_address\", args.mailbox_address);\n }\n\n if (args.to_recipients) {\n const recips =\n typeof args.to_recipients === \"string\"\n ? JSON.parse(args.to_recipients)\n : args.to_recipients;\n formData.append(\"to_recipients\", JSON.stringify(recips));\n }\n\n if (args.attachment_paths) {\n const paths =\n typeof args.attachment_paths === \"string\"\n ? JSON.parse(args.attachment_paths)\n : args.attachment_paths;\n for (const p of paths) {\n const buf = readFileSync(p);\n const filename = p.split(/[/\\\\]/).pop();\n formData.append(\"files\", new Blob([buf]), filename);\n }\n }\n\n let res: Response;\n try {\n res = await fetch(`${BACKEND_URL}/api/v1/emails/drafts/new`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n Accept: \"application/json\",\n },\n body: formData as any,\n signal: AbortSignal.timeout(90_000),\n });\n } catch (err) {\n if (isTimeoutError(err)) {\n throw new Error(\n \"Draft creation timed out after 90s. If the draft includes large attachments, try splitting them across multiple drafts or omitting the largest files.\",\n );\n }\n throw err;\n }\n\n if (res.status === 401) {\n DesktopAuthManager.clearApiKey();\n throw new Error(\n \"Authentication expired. Please call `login_to_adeu_cloud`.\",\n );\n }\n if (!res.ok)\n throw new Error(formatBackendError(res.status, await res.text()));\n\n const data: any = await res.json();\n return {\n content: [\n {\n type: \"text\",\n text: `Successfully created email draft! Draft ID: ${data.id}`,\n },\n ],\n };\n}\nexport async function list_available_mailboxes(): Promise<ToolResult> {\n const apiKey = await getCloudAuthToken();\n\n let res: Response;\n try {\n res = await fetch(`${BACKEND_URL}/api/v1/users/me/shared-mailboxes`, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n Accept: \"application/json\",\n },\n signal: AbortSignal.timeout(15_000),\n });\n } catch (err) {\n if (isTimeoutError(err)) {\n throw new Error(\n \"Listing mailboxes timed out after 15s. The Adeu backend may be temporarily unavailable; retry shortly.\",\n );\n }\n throw err;\n }\n\n if (res.status === 401) {\n DesktopAuthManager.clearApiKey();\n throw new Error(\n \"Authentication expired. Please call `login_to_adeu_cloud` to re-authenticate.\",\n );\n }\n if (!res.ok) {\n throw new Error(formatBackendError(res.status, await res.text()));\n }\n\n // FILE: node/packages/mcp-server/src/tools/email.ts\n\n const mailboxes: any[] = await res.json();\n if (!mailboxes.length) {\n return {\n content: [\n {\n type: \"text\",\n text: \"No configured mailboxes found for your profile.\",\n },\n ],\n };\n }\n\n // Sort alphabetically by email for deterministic ordering across clients.\n mailboxes.sort((a, b) =>\n (a.email_address ?? \"\")\n .toLowerCase()\n .localeCompare((b.email_address ?? \"\").toLowerCase()),\n );\n\n const lines = [\n \"### Connected Mailboxes\",\n \"Below is the list of connected mailboxes you have access to. Use the `email_address` as the `mailbox_address` parameter in other tools to query or draft from a specific mailbox:\",\n \"\",\n ];\n\n for (const box of mailboxes) {\n lines.push(\n `- **${box.display_name || \"Personal Mailbox\"}**\\n - **Email Address**: \\`${box.email_address}\\`\\n - **Auto-Processing**: ${box.auto_process_enabled ? \"Enabled\" : \"Disabled\"}\\n - **Write-Back Mode**: \\`${box.write_back_preference}\\``,\n );\n }\n\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n };\n}\n"],"mappings":"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","names":["readFileSync","existsSync","basename","resolve","join","DocumentObject","ui_markdown","llm_content","i","resolve","server","homedir","join","readFileSync","writeFileSync","mkdirSync","existsSync","join","homedir","existsSync","readFileSync","mkdirSync","writeFileSync","resolve","args","readFileSync","join","existsSync","DocumentObject","res","basename","resolve","args","gitSha"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adeu/mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"mcpName": "ai.adeu/adeu",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"type": "module",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@adeu/core": "^1.
|
|
34
|
+
"@adeu/core": "^1.15.0",
|
|
35
35
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
36
36
|
"@modelcontextprotocol/ext-apps": "^1.0.0",
|
|
37
37
|
"zod": "^3.23.8"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { formatBatchResult } from "./index.js";
|
|
3
|
+
|
|
4
|
+
describe("QA Report V2: Formatter Parity", () => {
|
|
5
|
+
it("F3 & F4: Formats match_mode, occurrences_modified, heading_path, and pages correctly", () => {
|
|
6
|
+
// Mock the stats object that the engine produces when `all` mode is successful
|
|
7
|
+
const stats = {
|
|
8
|
+
actions_applied: 0,
|
|
9
|
+
actions_skipped: 0,
|
|
10
|
+
edits_applied: 1,
|
|
11
|
+
edits_skipped: 0,
|
|
12
|
+
edits: [
|
|
13
|
+
{
|
|
14
|
+
status: "applied",
|
|
15
|
+
target_text: "the Board of Directors",
|
|
16
|
+
new_text: "the Governing Body",
|
|
17
|
+
warning: null,
|
|
18
|
+
error: null,
|
|
19
|
+
critic_markup: "the {--Board of Directors--}{++Governing Body++}",
|
|
20
|
+
clean_text: "the Governing Body",
|
|
21
|
+
pages: [3, 12],
|
|
22
|
+
heading_path: "6. Term > 6.1",
|
|
23
|
+
occurrences_modified: 33,
|
|
24
|
+
match_mode: "all"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
skipped_details: []
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const res = formatBatchResult(stats, "dummy.docx", false);
|
|
31
|
+
|
|
32
|
+
// Verify the new §5.4.2 visual format
|
|
33
|
+
expect(res).toContain("### Edit 1 ✅ [applied] (p3, p12)");
|
|
34
|
+
expect(res).toContain("**Path:** `6. Term > 6.1`");
|
|
35
|
+
expect(res).toContain("**Mode:** `all` (33 occurrences modified)");
|
|
36
|
+
expect(res).toContain("*Preview (CriticMarkup):*");
|
|
37
|
+
expect(res).toContain("> the {--Board of Directors--}{++Governing Body++}");
|
|
38
|
+
expect(res).toContain("*Preview (Clean):*");
|
|
39
|
+
expect(res).toContain("> the Governing Body");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("R1: Formats dry-run with full enrichment fields identical to real writes", () => {
|
|
43
|
+
const stats = {
|
|
44
|
+
actions_applied: 0,
|
|
45
|
+
actions_skipped: 0,
|
|
46
|
+
edits_applied: 1,
|
|
47
|
+
edits_skipped: 0,
|
|
48
|
+
edits: [
|
|
49
|
+
{
|
|
50
|
+
status: "applied",
|
|
51
|
+
target_text: "Target",
|
|
52
|
+
new_text: "Replaced",
|
|
53
|
+
warning: null,
|
|
54
|
+
error: null,
|
|
55
|
+
critic_markup: "{--Target--}{++Replaced++}",
|
|
56
|
+
clean_text: "Replaced",
|
|
57
|
+
pages: [1],
|
|
58
|
+
heading_path: "1. Intro",
|
|
59
|
+
occurrences_modified: 1,
|
|
60
|
+
match_mode: "all"
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
skipped_details: []
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const res = formatBatchResult(stats, "dummy.docx", true);
|
|
67
|
+
expect(res).toContain("**Path:** `1. Intro`");
|
|
68
|
+
expect(res).toContain("**Mode:** `all` (1 occurrence modified)");
|
|
69
|
+
expect(res).toContain("(p1)");
|
|
70
|
+
});
|
|
71
|
+
});
|
package/src/formatter.test.ts
CHANGED
|
@@ -16,7 +16,11 @@ describe("MCP Server Tool Output Formatter", () => {
|
|
|
16
16
|
warning: "Warning: target_text contains punctuation.",
|
|
17
17
|
error: null,
|
|
18
18
|
critic_markup: "The {--quick brown fox--}{++fast red fox++} jumps over",
|
|
19
|
-
clean_text: "The fast red fox jumps over"
|
|
19
|
+
clean_text: "The fast red fox jumps over",
|
|
20
|
+
match_mode: "strict",
|
|
21
|
+
occurrences_modified: 1,
|
|
22
|
+
pages: [1],
|
|
23
|
+
heading_path: "1. Intro"
|
|
20
24
|
}
|
|
21
25
|
],
|
|
22
26
|
skipped_details: []
|
|
@@ -29,9 +33,11 @@ describe("MCP Server Tool Output Formatter", () => {
|
|
|
29
33
|
expect(res).toContain("Actions: 0 applied");
|
|
30
34
|
expect(res).toContain("Edits: 1 applied");
|
|
31
35
|
expect(res).toContain("Detailed Edit Reports:");
|
|
32
|
-
expect(res).toContain("✅ [applied]");
|
|
33
|
-
expect(res).toContain("
|
|
34
|
-
expect(res).toContain("
|
|
36
|
+
expect(res).toContain("### Edit 1 ✅ [applied] (p1)");
|
|
37
|
+
expect(res).toContain("**Path:** `1. Intro`");
|
|
38
|
+
expect(res).toContain("**Mode:** `strict` (1 occurrence modified)");
|
|
39
|
+
expect(res).toContain("*Warning:* Warning: target_text contains punctuation.");
|
|
40
|
+
expect(res).toContain("*Preview (CriticMarkup):*\n> The {--quick brown fox--}{++fast red fox++} jumps over");
|
|
35
41
|
});
|
|
36
42
|
|
|
37
43
|
it("formats a failed batch result correctly", () => {
|
|
@@ -57,8 +63,8 @@ describe("MCP Server Tool Output Formatter", () => {
|
|
|
57
63
|
const res = formatBatchResult(stats, "dummy_processed.docx", false);
|
|
58
64
|
|
|
59
65
|
expect(res).toContain("Batch complete. Saved to: dummy_processed.docx");
|
|
60
|
-
expect(res).toContain("❌ [failed]");
|
|
61
|
-
expect(res).toContain("Error
|
|
66
|
+
expect(res).toContain("### Edit 1 ❌ [failed]");
|
|
67
|
+
expect(res).toContain("*Error:* Target text not found in document");
|
|
62
68
|
expect(res).toContain("Skipped Details:\n- Failed to apply edit targeting: 'NON_EXISTENT...'");
|
|
63
69
|
});
|
|
64
70
|
});
|
package/src/index.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
build_paginated_response,
|
|
25
25
|
build_outline_response,
|
|
26
26
|
build_appendix_response,
|
|
27
|
+
build_search_response,
|
|
27
28
|
} from "./response-builders.js";
|
|
28
29
|
|
|
29
30
|
import { login_to_adeu_cloud, logout_of_adeu_cloud } from "./tools/auth.js";
|
|
@@ -224,9 +225,9 @@ registerAppTool(
|
|
|
224
225
|
"'full' returns body content. 'outline' returns a structural heading map. 'appendix' returns defined terms.",
|
|
225
226
|
),
|
|
226
227
|
page: z
|
|
227
|
-
.number()
|
|
228
|
+
.union([z.number(), z.literal("all")])
|
|
228
229
|
.default(1)
|
|
229
|
-
.describe("Page number (1-indexed) for mode='full'
|
|
230
|
+
.describe("Page number (1-indexed) for mode='full', or 'all' to return unpaginated."),
|
|
230
231
|
outline_max_level: z
|
|
231
232
|
.number()
|
|
232
233
|
.default(2)
|
|
@@ -235,6 +236,18 @@ registerAppTool(
|
|
|
235
236
|
.boolean()
|
|
236
237
|
.default(false)
|
|
237
238
|
.describe("For mode='outline' only: includes metadata."),
|
|
239
|
+
search_query: z
|
|
240
|
+
.string()
|
|
241
|
+
.optional()
|
|
242
|
+
.describe("The substring or regex pattern to search for. When provided, filters results to matching paragraphs."),
|
|
243
|
+
search_regex: z
|
|
244
|
+
.boolean()
|
|
245
|
+
.default(false)
|
|
246
|
+
.describe("Set to true to interpret search_query as a regular expression."),
|
|
247
|
+
search_case_sensitive: z
|
|
248
|
+
.boolean()
|
|
249
|
+
.default(true)
|
|
250
|
+
.describe("Set to false to perform case-insensitive matching."),
|
|
238
251
|
}),
|
|
239
252
|
_meta: { ui: { resourceUri: MARKDOWN_UI_URI } },
|
|
240
253
|
},
|
|
@@ -245,6 +258,9 @@ registerAppTool(
|
|
|
245
258
|
page,
|
|
246
259
|
outline_max_level,
|
|
247
260
|
outline_verbose,
|
|
261
|
+
search_query,
|
|
262
|
+
search_regex,
|
|
263
|
+
search_case_sensitive,
|
|
248
264
|
}) => {
|
|
249
265
|
try {
|
|
250
266
|
const buf = readFileBytesOrThrow(file_path);
|
|
@@ -267,11 +283,15 @@ registerAppTool(
|
|
|
267
283
|
}
|
|
268
284
|
|
|
269
285
|
const text = await extractTextFromBuffer(buf, clean_view);
|
|
286
|
+
if (search_query !== undefined && search_query !== null) {
|
|
287
|
+
const res = build_search_response(text, search_query, search_regex, search_case_sensitive, page, file_path);
|
|
288
|
+
return res as any;
|
|
289
|
+
}
|
|
270
290
|
if (mode === "appendix") {
|
|
271
|
-
const res = build_appendix_response(text, page, file_path);
|
|
291
|
+
const res = build_appendix_response(text, typeof page === "number" ? page : parseInt(page, 10) || 1, file_path);
|
|
272
292
|
return res as any;
|
|
273
293
|
}
|
|
274
|
-
const res = build_paginated_response(text, page, file_path);
|
|
294
|
+
const res = build_paginated_response(text, typeof page === "number" ? page : parseInt(page, 10) || 1, file_path);
|
|
275
295
|
return res as any;
|
|
276
296
|
} catch (e: any) {
|
|
277
297
|
return {
|
|
@@ -728,8 +748,11 @@ export function formatBatchResult(
|
|
|
728
748
|
} else {
|
|
729
749
|
res = `Batch complete. Saved to: ${outPath}\n`;
|
|
730
750
|
}
|
|
751
|
+
const total_occurrences = stats.edits ? stats.edits.reduce((acc: number, e: any) => acc + (e.status === "applied" ? (e.occurrences_modified || 1) : 0), 0) : 0;
|
|
752
|
+
const occ_text = total_occurrences > stats.edits_applied ? ` (${total_occurrences} occurrences)` : "";
|
|
753
|
+
|
|
731
754
|
res += `Actions: ${stats.actions_applied} applied, ${stats.actions_skipped} skipped.\n`;
|
|
732
|
-
res += `Edits: ${stats.edits_applied} applied, ${stats.edits_skipped} skipped.\n`;
|
|
755
|
+
res += `Edits: ${stats.edits_applied} applied${occ_text}, ${stats.edits_skipped} skipped.\n`;
|
|
733
756
|
|
|
734
757
|
if (stats.edits && stats.edits.length > 0) {
|
|
735
758
|
res += "\nDetailed Edit Reports:\n";
|
|
@@ -737,28 +760,43 @@ export function formatBatchResult(
|
|
|
737
760
|
const report = stats.edits[i];
|
|
738
761
|
const status_indicator =
|
|
739
762
|
report.status === "applied" ? "✅ [applied]" : "❌ [failed]";
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
763
|
+
|
|
764
|
+
const pagesStr = report.pages && report.pages.length > 0
|
|
765
|
+
? ` (p${report.pages.join(", p")})`
|
|
766
|
+
: "";
|
|
767
|
+
|
|
768
|
+
res += `### Edit ${i + 1} ${status_indicator}${pagesStr}\n`;
|
|
769
|
+
|
|
770
|
+
if (report.heading_path) {
|
|
771
|
+
res += `**Path:** \`${report.heading_path}\`\n`;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (report.match_mode) {
|
|
775
|
+
const occ = report.occurrences_modified || (report.status === "applied" ? 1 : 0);
|
|
776
|
+
res += `**Mode:** \`${report.match_mode}\` (${occ} occurrence${occ !== 1 ? 's' : ''} modified)\n`;
|
|
745
777
|
}
|
|
778
|
+
|
|
746
779
|
if (report.error) {
|
|
747
|
-
res +=
|
|
780
|
+
res += `*Error:* ${report.error}\n`;
|
|
781
|
+
}
|
|
782
|
+
if (report.warning) {
|
|
783
|
+
res += `*Warning:* ${report.warning}\n`;
|
|
748
784
|
}
|
|
785
|
+
|
|
749
786
|
if (report.critic_markup) {
|
|
750
|
-
res +=
|
|
787
|
+
res += `*Preview (CriticMarkup):*\n> ${report.critic_markup.split('\\n').join('\\n> ')}\n`;
|
|
751
788
|
}
|
|
752
789
|
if (report.clean_text) {
|
|
753
|
-
res +=
|
|
790
|
+
res += `*Preview (Clean):*\n> ${report.clean_text.split('\\n').join('\\n> ')}\n`;
|
|
754
791
|
}
|
|
792
|
+
res += "\n";
|
|
755
793
|
}
|
|
756
794
|
}
|
|
757
795
|
|
|
758
796
|
if (stats.skipped_details && stats.skipped_details.length > 0) {
|
|
759
|
-
res +=
|
|
797
|
+
res += `Skipped Details:\n${stats.skipped_details.join("\n")}`;
|
|
760
798
|
}
|
|
761
|
-
return res;
|
|
799
|
+
return res.trim();
|
|
762
800
|
}
|
|
763
801
|
|
|
764
802
|
// --- Startup ---
|
package/src/response-builders.ts
CHANGED
|
@@ -208,3 +208,154 @@ export function build_appendix_response(
|
|
|
208
208
|
},
|
|
209
209
|
};
|
|
210
210
|
}
|
|
211
|
+
|
|
212
|
+
export function build_search_response(
|
|
213
|
+
text: string,
|
|
214
|
+
search_query: string,
|
|
215
|
+
search_regex: boolean,
|
|
216
|
+
search_case_sensitive: boolean,
|
|
217
|
+
page: number | string,
|
|
218
|
+
file_path: string,
|
|
219
|
+
): ToolResult {
|
|
220
|
+
const [body] = split_structural_appendix(text);
|
|
221
|
+
const escapeRegExp = (s: string) =>
|
|
222
|
+
s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
223
|
+
const flags = search_case_sensitive ? "g" : "gi";
|
|
224
|
+
const patternStr = search_regex ? search_query : escapeRegExp(search_query);
|
|
225
|
+
|
|
226
|
+
let regex: RegExp;
|
|
227
|
+
try {
|
|
228
|
+
regex = new RegExp(patternStr, flags);
|
|
229
|
+
} catch (e: any) {
|
|
230
|
+
throw new Error(`Invalid regex pattern: ${e.message}`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const matches = Array.from(body.matchAll(regex));
|
|
234
|
+
|
|
235
|
+
if (matches.length === 0) {
|
|
236
|
+
const ui_markdown = `> **Search Results** — No matches found for query \`${search_query}\` in \`${basename(file_path)}\`.\n\nVerify your search spelling, or try setting \`search_case_sensitive\` to false or enabling \`search_regex\` if you used pattern wildcards.`;
|
|
237
|
+
const llm_content = `> **File Path:** \`${resolve(file_path)}\`\n\n${ui_markdown}`;
|
|
238
|
+
return {
|
|
239
|
+
content: [{ type: "text", text: llm_content }],
|
|
240
|
+
structuredContent: {
|
|
241
|
+
markdown: ui_markdown,
|
|
242
|
+
title: `Search: ${basename(file_path)}`,
|
|
243
|
+
file_path: resolve(file_path),
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const pag_res = paginate(body, "");
|
|
249
|
+
const page_offsets = pag_res.body_page_offsets;
|
|
250
|
+
const total_matches = matches.length;
|
|
251
|
+
const total_pages = Math.ceil(total_matches / 10);
|
|
252
|
+
|
|
253
|
+
let start_idx = 0;
|
|
254
|
+
let end_idx = total_matches;
|
|
255
|
+
let page_text = "all";
|
|
256
|
+
|
|
257
|
+
const pageStr = String(page).toLowerCase();
|
|
258
|
+
if (pageStr !== "all") {
|
|
259
|
+
const page_num = parseInt(pageStr, 10);
|
|
260
|
+
if (isNaN(page_num) || page_num < 1 || page_num > total_pages) {
|
|
261
|
+
throw new Error(`Page ${page} out of range (search has ${total_pages} pages).`);
|
|
262
|
+
}
|
|
263
|
+
start_idx = (page_num - 1) * 10;
|
|
264
|
+
end_idx = Math.min(start_idx + 10, total_matches);
|
|
265
|
+
page_text = `${page_num} of ${total_pages}`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const page_matches = matches.slice(start_idx, end_idx);
|
|
269
|
+
|
|
270
|
+
const ui_parts: string[] = [
|
|
271
|
+
`> **Search Results** — Found ${total_matches} matches for query \`${search_query}\` in \`${basename(file_path)}\`.`,
|
|
272
|
+
];
|
|
273
|
+
|
|
274
|
+
if (total_pages > 1 && pageStr !== "all") {
|
|
275
|
+
const nextPage = parseInt(pageStr, 10) + 1;
|
|
276
|
+
ui_parts.push(`> Showing page ${page_text} (matches ${start_idx + 1}-${end_idx}). To see more matches, call \`read_docx\` with \`search_query='${search_query}'\`, \`search_regex=${search_regex ? "true" : "false"}\`, and \`page=${nextPage}\`.`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const occurrences_map: Record<string, number> = {};
|
|
280
|
+
for (const m of matches) {
|
|
281
|
+
const matched_str = m[0];
|
|
282
|
+
occurrences_map[matched_str] = (occurrences_map[matched_str] || 0) + 1;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function get_heading(idx: number, txt: string): string {
|
|
286
|
+
const txtBefore = txt.substring(0, idx);
|
|
287
|
+
const lines = txtBefore.split("\n");
|
|
288
|
+
const path: string[] = [];
|
|
289
|
+
let current_level = 999;
|
|
290
|
+
|
|
291
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
292
|
+
const line = lines[i];
|
|
293
|
+
const m = line.match(/^(#{1,6})\s+(.*)/);
|
|
294
|
+
if (m) {
|
|
295
|
+
const level = m[1].length;
|
|
296
|
+
if (level < current_level) {
|
|
297
|
+
let cleanHeading = m[2].replace(/\*\*|__|[*_]/g, "").replace(/\{#[^}]+\}/g, "").trim();
|
|
298
|
+
if (cleanHeading.length > 80) {
|
|
299
|
+
cleanHeading = cleanHeading.substring(0, 80) + "...";
|
|
300
|
+
}
|
|
301
|
+
path.unshift(cleanHeading);
|
|
302
|
+
current_level = level;
|
|
303
|
+
if (level === 1) break;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return path.join(" > ");
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
let i = start_idx + 1;
|
|
311
|
+
for (const m of page_matches) {
|
|
312
|
+
const matched_str = m[0];
|
|
313
|
+
const m_start = m.index!;
|
|
314
|
+
const m_end = m_start + matched_str.length;
|
|
315
|
+
|
|
316
|
+
let p_num = 1;
|
|
317
|
+
for (let j = 0; j < page_offsets.length; j++) {
|
|
318
|
+
if (m_start >= page_offsets[j]) {
|
|
319
|
+
p_num = j + 1;
|
|
320
|
+
} else {
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const snippet_start = Math.max(0, m_start - 100);
|
|
326
|
+
const snippet_end = Math.min(body.length, m_end + 100);
|
|
327
|
+
const snippet = body.substring(snippet_start, m_start) + `**${matched_str}**` + body.substring(m_end, snippet_end);
|
|
328
|
+
|
|
329
|
+
const snippet_lines = snippet
|
|
330
|
+
.split("\n")
|
|
331
|
+
.filter((line) => line.trim().length > 0)
|
|
332
|
+
.map((line) => `> ${line}`)
|
|
333
|
+
.join("\n");
|
|
334
|
+
|
|
335
|
+
ui_parts.push("---");
|
|
336
|
+
ui_parts.push(`### Match ${i} (p${p_num})`);
|
|
337
|
+
|
|
338
|
+
const h_path = get_heading(m_start, body);
|
|
339
|
+
if (h_path) {
|
|
340
|
+
ui_parts.push(`**Path:** \`${h_path}\``);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const count = occurrences_map[matched_str];
|
|
344
|
+
ui_parts.push(snippet_lines);
|
|
345
|
+
ui_parts.push(`*Occurrences:* This exact phrasing appears ${count} time${count !== 1 ? "s" : ""} in the document.`);
|
|
346
|
+
|
|
347
|
+
i++;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const ui_markdown = ui_parts.join("\n\n");
|
|
351
|
+
const llm_content = `> **File Path:** \`${resolve(file_path)}\`\n\n${ui_markdown}`;
|
|
352
|
+
|
|
353
|
+
return {
|
|
354
|
+
content: [{ type: "text", text: llm_content }],
|
|
355
|
+
structuredContent: {
|
|
356
|
+
markdown: ui_markdown,
|
|
357
|
+
title: `Search: ${basename(file_path)}`,
|
|
358
|
+
file_path: resolve(file_path),
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
}
|