@adeu/mcp-server 1.6.8 → 1.7.1

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.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/response-builders.ts"],"sourcesContent":["import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { readFileSync } from 'node:fs';\nimport { basename, resolve, extname, dirname } from 'node:path';\nimport { \n identifyEngine, \n extractTextFromBuffer, \n DocumentObject, \n RedlineEngine, \n BatchValidationError,\n create_unified_diff,\n finalize_document\n} from '@adeu/core';\nimport { \n build_paginated_response, \n build_outline_response, \n build_appendix_response \n} from './response-builders.js';\n\n// --- Tool Description Constants (Parity with Python) ---\nconst READ_DOCX_COMMON_DESC = \"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 = \"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 = \"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 = \"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 = \"Compares two DOCX files and returns a unified diff of their text content. Useful for analyzing differences between versions before editing.\";\n\n// --- Server Setup ---\nconst server = new Server(\n {\n name: 'adeu-redlining-service',\n version: '1.0.0',\n },\n {\n capabilities: {\n tools: {},\n },\n }\n);\n\n// --- Tool Registration ---\nserver.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: [\n {\n name: 'read_docx',\n description: READ_DOCX_COMMON_DESC + READ_DOCX_TAIL,\n inputSchema: {\n type: 'object',\n properties: {\n file_path: { type: 'string', description: 'Absolute path to the DOCX file.' },\n clean_view: { type: 'boolean', description: \"If False (default), returns the 'Raw' text with inline CriticMarkup. If True, returns 'Accepted' text.\", default: false },\n mode: { type: 'string', enum: ['full', 'outline', 'appendix'], description: \"'full' returns body content. 'outline' returns a structural heading map. 'appendix' returns defined terms.\", default: 'full' },\n page: { type: 'number', description: 'Page number (1-indexed) for mode=\\'full\\'. Defaults to 1.', default: 1 },\n outline_max_level: { type: 'number', description: 'For mode=\\'outline\\' only: cap on heading depth.', default: 2 },\n outline_verbose: { type: 'boolean', description: 'For mode=\\'outline\\' only: includes metadata.', default: false }\n },\n required: ['file_path']\n }\n },\n {\n name: 'process_document_batch',\n description: PROCESS_BATCH_COMMON_DESC + PROCESS_BATCH_OPERATIONS_DESC,\n inputSchema: {\n type: 'object',\n properties: {\n original_docx_path: { type: 'string', description: 'Absolute path to the source file.' },\n author_name: { type: 'string', description: \"Name to appear in Track Changes (e.g., 'Reviewer AI').\" },\n changes: { \n type: 'array', \n description: \"List of changes to apply. Each change must specify 'type'.\",\n items: { type: 'object' } \n },\n output_path: { type: 'string', description: 'Optional output path.' }\n },\n required: ['original_docx_path', 'author_name', 'changes']\n }\n },\n {\n name: 'accept_all_changes',\n description: \"Accepts all tracked changes and removes all comments in a single operation, producing a finalized clean document. Use this when a document review is entirely complete and you want to clear all redlines.\",\n inputSchema: {\n type: 'object',\n properties: {\n docx_path: { type: 'string', description: 'Absolute path to the DOCX file.' },\n output_path: { type: 'string', description: 'Optional output path.' }\n },\n required: ['docx_path']\n }\n },\n {\n name: 'diff_docx_files',\n description: DIFF_DOCX_DESC,\n inputSchema: {\n type: 'object',\n properties: {\n original_path: { type: 'string', description: 'Absolute path to the baseline DOCX file.' },\n modified_path: { type: 'string', description: 'Absolute path to the modified DOCX file.' }\n },\n required: ['original_path', 'modified_path']\n }\n },\n {\n name: 'finalize_document',\n description: \"Prepares a document for external distribution or e-signature. This tool combines metadata sanitization, document locking (protection), and markup resolution into a single step. NOTE: PDF export and AES encryption are disabled in this environment.\",\n inputSchema: {\n type: 'object',\n properties: {\n file_path: { type: 'string', description: 'Absolute path to the DOCX file.' },\n output_path: { type: 'string', description: 'Optional output path.' },\n sanitize_mode: { type: 'string', enum: ['full', 'keep-markup'], description: 'full removes all markup, keep-markup redacts metadata but keeps comments/redlines.' },\n accept_all: { type: 'boolean', description: 'If true, auto-accepts all unresolved track changes before finalizing.' },\n protection_mode: { type: 'string', enum: ['read_only', 'encrypt'], description: 'Native OOXML document locking. encrypt falls back to read_only in this environment.' },\n password: { type: 'string', description: 'Ignored in this environment.' },\n author: { type: 'string', description: 'Replace all remaining markup authorship with this name.' },\n export_pdf: { type: 'boolean', description: 'Ignored in this environment.' }\n },\n required: ['file_path']\n }\n }\n ]\n };\n});\n\n// --- Tool Execution ---\nserver.setRequestHandler(CallToolRequestSchema, async (request): Promise<any> => {\n const { name, arguments: args } = request.params;\n\n try {\n if (name === 'read_docx') {\n const filePath = args?.file_path as string;\n const cleanView = args?.clean_view as boolean ?? false;\n const mode = args?.mode as string ?? 'full';\n const page = args?.page as number ?? 1;\n const outline_max_level = args?.outline_max_level as number ?? 2;\n const outline_verbose = args?.outline_verbose as boolean ?? false;\n \n const buf = readFileSync(filePath);\n const text = await extractTextFromBuffer(buf, cleanView);\n \n if (mode === 'outline') {\n const doc = await DocumentObject.load(buf);\n return build_outline_response(doc, text, filePath, outline_max_level, outline_verbose);\n }\n if (mode === 'appendix') {\n return build_appendix_response(text, page, filePath);\n }\n return build_paginated_response(text, page, filePath);\n }\n\n if (name === 'process_document_batch') {\n const origPath = args?.original_docx_path as string;\n const authorName = args?.author_name as string;\n const changes = args?.changes as any[];\n let outPath = args?.output_path as string;\n\n if (!outPath) {\n const ext = extname(origPath);\n const base = basename(origPath, ext);\n const dir = dirname(origPath);\n outPath = resolve(dir, `${base}_processed${ext}`);\n }\n\n const buf = readFileSync(origPath);\n const doc = await DocumentObject.load(buf);\n const engine = new RedlineEngine(doc, authorName);\n \n let stats;\n try {\n stats = engine.process_batch(changes);\n } catch (e) {\n if (e instanceof BatchValidationError) {\n return {\n content: [{ type: 'text', text: `Batch rejected. Some edits failed validation:\\n\\n${(e as BatchValidationError).errors.join('\\n\\n')}` }],\n isError: true\n };\n }\n throw e;\n }\n\n const outBuf = await doc.save();\n // Using dynamic import of fs/promises or just sync write\n const fs = await import('node:fs');\n fs.writeFileSync(outPath, outBuf);\n\n let res = `Batch complete. Saved to: ${outPath}\\nActions: ${stats.actions_applied} applied, ${stats.actions_skipped} skipped.\\nEdits: ${stats.edits_applied} applied, ${stats.edits_skipped} skipped.`;\n if (stats.skipped_details?.length > 0) {\n res += `\\n\\nSkipped Details:\\n${stats.skipped_details.join('\\n')}`;\n }\n\n return {\n content: [{ type: 'text', text: res }]\n };\n }\n\n if (name === 'accept_all_changes') {\n const docxPath = args?.docx_path as string;\n let outPath = args?.output_path as string;\n\n if (!outPath) {\n const ext = extname(docxPath);\n const base = basename(docxPath, ext);\n const dir = dirname(docxPath);\n outPath = resolve(dir, `${base}_clean${ext}`);\n }\n\n const buf = readFileSync(docxPath);\n const doc = await DocumentObject.load(buf);\n const engine = new RedlineEngine(doc);\n \n // We implement the public facing accept_all wrapper from python\n engine.accept_all_revisions();\n \n const outBuf = await doc.save();\n const fs = await import('node:fs');\n fs.writeFileSync(outPath, outBuf);\n\n return {\n content: [{ type: 'text', text: `Accepted all changes. Saved to: ${outPath}` }]\n };\n }\n\n if (name === 'diff_docx_files') {\n const origPath = args?.original_path as string;\n const modPath = args?.modified_path as string;\n\n const origBuf = readFileSync(origPath);\n const modBuf = readFileSync(modPath);\n\n const origText = await extractTextFromBuffer(origBuf, true);\n const modText = await extractTextFromBuffer(modBuf, true);\n\n const diff = create_unified_diff(origText, modText);\n \n return {\n content: [{ type: 'text', text: diff || \"No differences found.\" }]\n };\n }\n\n if (name === 'finalize_document') {\n const filePath = args?.file_path as string;\n let outPath = args?.output_path as string;\n \n if (!outPath) {\n const ext = extname(filePath);\n const base = basename(filePath, ext);\n const dir = dirname(filePath);\n outPath = resolve(dir, `${base}_final${ext}`);\n }\n\n const buf = readFileSync(filePath);\n const doc = await DocumentObject.load(buf);\n\n const result = await finalize_document(doc, {\n filename: basename(filePath),\n sanitize_mode: (args?.sanitize_mode as any) || 'full',\n accept_all: args?.accept_all as boolean,\n protection_mode: args?.protection_mode as any,\n author: args?.author as string,\n export_pdf: args?.export_pdf as boolean\n });\n\n const fs = await import('node:fs');\n fs.writeFileSync(outPath, result.outBuffer!);\n\n return {\n content: [{ type: 'text', text: `Saved to: ${outPath}\\n\\n${result.reportText}` }]\n };\n }\n\n throw new Error(`Unknown tool: ${name}`);\n\n } catch (error: any) {\n return {\n content: [{ type: 'text', text: `Error executing tool ${name}: ${error.message}` }],\n isError: true,\n };\n }\n});\n\n// --- Startup ---\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(`Adeu MCP Server (Node.js Engine: ${identifyEngine()}) running on stdio`);\n}\n\nmain().catch(console.error);","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 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(page: number, total: number, has_next: boolean): 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(nodes: OutlineNode[], max_level: number = 2, verbose: boolean = false): 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) 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(text: string, page: number, file_path: string): 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(`Page ${page} out of range (doc has ${result.total_pages} pages).`);\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(selected.page, selected.total_pages, selected.has_next);\n const appendix_pointer = _build_appendix_pointer(has_appendix);\n \n const ui_markdown = 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 };\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): 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 );\n\n const rendered = render_outline_tree(nodes, outline_max_level, outline_verbose);\n\n const visible_count = nodes.filter(n => n.level <= outline_max_level).length;\n const deeper_count = nodes.length - visible_count;\n const deeper_hint = deeper_count > 0 ? ` (${deeper_count} more at deeper levels, raise outline_max_level to see)` : '';\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 };\n}\n\nexport function build_appendix_response(text: string, page: number, file_path: string): ToolResult {\n const [, appendix] = split_structural_appendix(text);\n\n if (!appendix.trim()) {\n const ui_markdown = \"# 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 };\n }\n\n const result = paginate(appendix, '');\n\n if (page < 1 || page > result.total_pages) {\n throw new Error(`Appendix page ${page} out of range (appendix has ${result.total_pages} pages).`);\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> **Continues on appendix page ${selected.page + 1} of ${selected.total_pages}.**` : '';\n } else {\n banner = \"> **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 };\n}"],"mappings":"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","names":["basename","resolve","DocumentObject","ui_markdown","llm_content","DocumentObject","basename","resolve"]}
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 { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { readFileSync } from \"node:fs\";\nimport { basename, resolve, extname, dirname } from \"node:path\";\nimport {\n identifyEngine,\n extractTextFromBuffer,\n DocumentObject,\n RedlineEngine,\n BatchValidationError,\n create_word_patch_diff,\n finalize_document,\n} from \"@adeu/core\";\nimport {\n build_paginated_response,\n build_outline_response,\n build_appendix_response,\n} from \"./response-builders.js\";\nimport { login_to_adeu_cloud, logout_of_adeu_cloud } from \"./tools/auth.js\";\nimport { search_and_fetch_emails, create_email_draft } from \"./tools/email.js\";\nfunction readFileBytesOrThrow(filePath: string): Buffer {\n try {\n return readFileSync(filePath);\n } catch (err: any) {\n if (err.code === \"ENOENT\") {\n throw new Error(`File not found: ${filePath}`);\n }\n throw err;\n }\n}\n// --- Tool Description Constants (Parity with Python) ---\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\n// --- Server Setup ---\nconst server = new Server(\n {\n name: \"adeu-redlining-service\",\n version: \"1.0.0\",\n },\n {\n capabilities: {\n tools: {},\n },\n },\n);\n\n// --- Tool Registration ---\nserver.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: [\n {\n name: \"read_docx\",\n description: READ_DOCX_COMMON_DESC + READ_DOCX_TAIL,\n inputSchema: {\n type: \"object\",\n properties: {\n file_path: {\n type: \"string\",\n description: \"Absolute path to the DOCX file.\",\n },\n clean_view: {\n type: \"boolean\",\n description:\n \"If False (default), returns the 'Raw' text with inline CriticMarkup. If True, returns 'Accepted' text.\",\n default: false,\n },\n mode: {\n type: \"string\",\n enum: [\"full\", \"outline\", \"appendix\"],\n description:\n \"'full' returns body content. 'outline' returns a structural heading map. 'appendix' returns defined terms.\",\n default: \"full\",\n },\n page: {\n type: \"number\",\n description:\n \"Page number (1-indexed) for mode='full'. Defaults to 1.\",\n default: 1,\n },\n outline_max_level: {\n type: \"number\",\n description: \"For mode='outline' only: cap on heading depth.\",\n default: 2,\n },\n outline_verbose: {\n type: \"boolean\",\n description: \"For mode='outline' only: includes metadata.\",\n default: false,\n },\n },\n required: [\"file_path\"],\n },\n },\n {\n name: \"process_document_batch\",\n description: PROCESS_BATCH_COMMON_DESC + PROCESS_BATCH_OPERATIONS_DESC,\n inputSchema: {\n type: \"object\",\n properties: {\n original_docx_path: {\n type: \"string\",\n description: \"Absolute path to the source file.\",\n },\n author_name: {\n type: \"string\",\n description:\n \"Name to appear in Track Changes (e.g., 'Reviewer AI').\",\n },\n changes: {\n type: \"array\",\n description:\n \"List of changes to apply. Each change must specify 'type'.\",\n items: { type: \"object\" },\n },\n output_path: {\n type: \"string\",\n description: \"Optional output path.\",\n },\n },\n required: [\"original_docx_path\", \"author_name\", \"changes\"],\n },\n },\n {\n name: \"accept_all_changes\",\n description:\n \"Accepts all tracked changes and removes all comments in a single operation, producing a finalized clean document. Use this when a document review is entirely complete and you want to clear all redlines.\",\n inputSchema: {\n type: \"object\",\n properties: {\n docx_path: {\n type: \"string\",\n description: \"Absolute path to the DOCX file.\",\n },\n output_path: {\n type: \"string\",\n description: \"Optional output path.\",\n },\n },\n required: [\"docx_path\"],\n },\n },\n {\n name: \"diff_docx_files\",\n description: DIFF_DOCX_DESC,\n inputSchema: {\n type: \"object\",\n properties: {\n original_path: {\n type: \"string\",\n description: \"Absolute path to the baseline DOCX file.\",\n },\n modified_path: {\n type: \"string\",\n description: \"Absolute path to the modified DOCX file.\",\n },\n compare_clean: {\n type: \"boolean\",\n description:\n \"If True, compares 'Accepted' state. If False, compares raw text.\",\n default: true,\n },\n },\n required: [\"original_path\", \"modified_path\"],\n },\n },\n {\n name: \"finalize_document\",\n description:\n \"Prepares a document for external distribution or e-signature. This tool combines metadata sanitization, document locking (protection), and markup resolution into a single step. NOTE: PDF export and AES encryption are disabled in this environment.\",\n inputSchema: {\n type: \"object\",\n properties: {\n file_path: {\n type: \"string\",\n description: \"Absolute path to the DOCX file.\",\n },\n output_path: {\n type: \"string\",\n description: \"Optional output path.\",\n },\n sanitize_mode: {\n type: \"string\",\n enum: [\"full\", \"keep-markup\"],\n description:\n \"full removes all markup, keep-markup redacts metadata but keeps comments/redlines.\",\n },\n accept_all: {\n type: \"boolean\",\n description:\n \"If true, auto-accepts all unresolved track changes before finalizing.\",\n },\n protection_mode: {\n type: \"string\",\n enum: [\"read_only\", \"encrypt\"],\n description:\n \"Native OOXML document locking. encrypt falls back to read_only in this environment.\",\n },\n password: {\n type: \"string\",\n description: \"Ignored in this environment.\",\n },\n author: {\n type: \"string\",\n description:\n \"Replace all remaining markup authorship with this name.\",\n },\n export_pdf: {\n type: \"boolean\",\n description: \"Ignored in this environment.\",\n },\n },\n required: [\"file_path\"],\n },\n },\n {\n name: \"login_to_adeu_cloud\",\n description:\n \"Logs the user into the Adeu Cloud backend. Securely opens a browser window for authentication.\",\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"logout_of_adeu_cloud\",\n description:\n \"Logs out of the Adeu Cloud backend by clearing the local API key.\",\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"search_and_fetch_emails\",\n description:\n \"Searches the user's live email inbox. By default, searches only the Inbox folder. Returns a list of lightweight previews. Call again with `email_id` to fetch the full body and download attachments.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sender: { type: \"string\" },\n subject: { type: \"string\" },\n has_attachments: { type: \"boolean\" },\n attachment_name: { type: \"string\" },\n is_unread: { type: \"boolean\" },\n days_ago: { type: \"number\" },\n folder: { type: \"string\", enum: [\"inbox\", \"sent\", \"all\"] },\n limit: { type: \"number\", default: 10 },\n offset: { type: \"number\", default: 0 },\n email_id: { type: \"string\" },\n working_directory: { type: \"string\" },\n },\n },\n },\n {\n name: \"create_email_draft\",\n description:\n \"Creates an email draft in the user's native draft box. Provide `reply_to_email_id` to reply, or `subject` and `to_recipients` for a new email.\",\n inputSchema: {\n type: \"object\",\n properties: {\n body_markdown: { type: \"string\" },\n reply_to_email_id: { type: \"string\" },\n subject: { type: \"string\" },\n to_recipients: { type: \"array\", items: { type: \"string\" } },\n attachment_paths: { type: \"array\", items: { type: \"string\" } },\n },\n required: [\"body_markdown\"],\n },\n },\n ],\n };\n});\n\n// --- Tool Execution ---\nserver.setRequestHandler(\n CallToolRequestSchema,\n async (request): Promise<any> => {\n const { name, arguments: args } = request.params;\n\n try {\n if (name === \"read_docx\") {\n const filePath = args?.file_path as string;\n const cleanView = (args?.clean_view as boolean) ?? false;\n const mode = (args?.mode as string) ?? \"full\";\n const page = (args?.page as number) ?? 1;\n const outline_max_level = (args?.outline_max_level as number) ?? 2;\n const outline_verbose = (args?.outline_verbose as boolean) ?? false;\n\n const buf = readFileBytesOrThrow(filePath);\n const text = await extractTextFromBuffer(buf, cleanView);\n\n if (mode === \"outline\") {\n const doc = await DocumentObject.load(buf);\n return build_outline_response(\n doc,\n text,\n filePath,\n outline_max_level,\n outline_verbose,\n );\n }\n if (mode === \"appendix\") {\n return build_appendix_response(text, page, filePath);\n }\n return build_paginated_response(text, page, filePath);\n }\n if (name === \"process_document_batch\") {\n const origPath = args?.original_docx_path as string;\n const authorName = args?.author_name as string;\n const changes = args?.changes as any[];\n let outPath = args?.output_path as string;\n\n if (!authorName || !authorName.trim()) {\n return {\n content: [\n { type: \"text\", text: \"Error: author_name cannot be empty.\" },\n ],\n };\n }\n\n if (!changes || changes.length === 0) {\n return {\n content: [{ type: \"text\", text: \"Error: No changes provided.\" }],\n };\n }\n if (!outPath) {\n const ext = extname(origPath);\n const base = basename(origPath, ext);\n const dir = dirname(origPath);\n outPath = resolve(dir, `${base}_processed${ext}`);\n }\n\n const buf = readFileBytesOrThrow(origPath);\n const doc = await DocumentObject.load(buf);\n const engine = new RedlineEngine(doc, authorName);\n\n let stats;\n try {\n stats = engine.process_batch(changes);\n } catch (e) {\n if (e instanceof BatchValidationError) {\n return {\n content: [\n {\n type: \"text\",\n text: `Batch rejected. Some edits failed validation:\\n\\n${(e as BatchValidationError).errors.join(\"\\n\\n\")}`,\n },\n ],\n isError: true,\n };\n }\n throw e;\n }\n\n const outBuf = await doc.save();\n // Using dynamic import of fs/promises or just sync write\n const fs = await import(\"node:fs\");\n fs.writeFileSync(outPath, outBuf);\n\n let res = `Batch complete. Saved to: ${outPath}\\nActions: ${stats.actions_applied} applied, ${stats.actions_skipped} skipped.\\nEdits: ${stats.edits_applied} applied, ${stats.edits_skipped} skipped.`;\n if (stats.skipped_details?.length > 0) {\n res += `\\n\\nSkipped Details:\\n${stats.skipped_details.join(\"\\n\")}`;\n }\n\n return {\n content: [{ type: \"text\", text: res }],\n };\n }\n\n if (name === \"accept_all_changes\") {\n const docxPath = args?.docx_path as string;\n let outPath = args?.output_path as string;\n\n if (!outPath) {\n const ext = extname(docxPath);\n const base = basename(docxPath, ext);\n const dir = dirname(docxPath);\n outPath = resolve(dir, `${base}_clean${ext}`);\n }\n\n const buf = readFileBytesOrThrow(docxPath);\n const doc = await DocumentObject.load(buf);\n const engine = new RedlineEngine(doc);\n\n // We implement the public facing accept_all wrapper from python\n engine.accept_all_revisions();\n\n const outBuf = await doc.save();\n const fs = await import(\"node:fs\");\n fs.writeFileSync(outPath, outBuf);\n\n return {\n content: [\n {\n type: \"text\",\n text: `Accepted all changes. Saved to: ${outPath}`,\n },\n ],\n };\n }\n if (name === \"diff_docx_files\") {\n const origPath = args?.original_path as string;\n const modPath = args?.modified_path as string;\n const compareClean = (args?.compare_clean as boolean) ?? true;\n const origBuf = readFileBytesOrThrow(origPath);\n const modBuf = readFileBytesOrThrow(modPath);\n\n // Pass compareClean flag into extraction\n const origText = await extractTextFromBuffer(origBuf, compareClean);\n const modText = await extractTextFromBuffer(modBuf, compareClean);\n\n const diff = create_word_patch_diff(\n origText,\n modText,\n basename(origPath),\n basename(modPath),\n );\n\n return {\n content: [{ type: \"text\", text: diff || \"No differences found.\" }],\n };\n }\n\n if (name === \"finalize_document\") {\n const filePath = args?.file_path as string;\n let outPath = args?.output_path as string;\n\n if (!outPath) {\n const ext = extname(filePath);\n const base = basename(filePath, ext);\n const dir = dirname(filePath);\n outPath = resolve(dir, `${base}_final${ext}`);\n }\n\n const buf = readFileBytesOrThrow(filePath);\n const doc = await DocumentObject.load(buf);\n\n const result = await finalize_document(doc, {\n filename: basename(filePath),\n sanitize_mode: (args?.sanitize_mode as any) || \"full\",\n accept_all: args?.accept_all as boolean,\n protection_mode: args?.protection_mode as any,\n author: args?.author as string,\n export_pdf: args?.export_pdf as boolean,\n });\n\n const fs = await import(\"node:fs\");\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 }\n if (name === \"login_to_adeu_cloud\") {\n return await login_to_adeu_cloud();\n }\n if (name === \"logout_of_adeu_cloud\") {\n return await logout_of_adeu_cloud();\n }\n if (name === \"search_and_fetch_emails\") {\n return await search_and_fetch_emails(args || {});\n }\n if (name === \"create_email_draft\") {\n return await create_email_draft(args || {});\n }\n throw new Error(`Unknown tool: ${name}`);\n } catch (error: any) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error executing tool ${name}: ${error.message}`,\n },\n ],\n isError: true,\n };\n }\n },\n);\n\n// --- Startup ---\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(\n `Adeu MCP Server (Node.js Engine: ${identifyEngine()}) running on stdio`,\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 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(page: number, total: number, has_next: boolean): 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(nodes: OutlineNode[], max_level: number = 2, verbose: boolean = false): 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) 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(text: string, page: number, file_path: string): 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(`Page ${page} out of range (doc has ${result.total_pages} pages).`);\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(selected.page, selected.total_pages, selected.has_next);\n const appendix_pointer = _build_appendix_pointer(has_appendix);\n \n const ui_markdown = 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 };\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): 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 );\n\n const rendered = render_outline_tree(nodes, outline_max_level, outline_verbose);\n\n const visible_count = nodes.filter(n => n.level <= outline_max_level).length;\n const deeper_count = nodes.length - visible_count;\n const deeper_hint = deeper_count > 0 ? ` (${deeper_count} more at deeper levels, raise outline_max_level to see)` : '';\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 };\n}\n\nexport function build_appendix_response(text: string, page: number, file_path: string): ToolResult {\n const [, appendix] = split_structural_appendix(text);\n\n if (!appendix.trim()) {\n const ui_markdown = \"# 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 };\n }\n\n const result = paginate(appendix, '');\n\n if (page < 1 || page > result.total_pages) {\n throw new Error(`Appendix page ${page} out of range (appendix has ${result.total_pages} pages).`);\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> **Continues on appendix page ${selected.page + 1} of ${selected.total_pages}.**` : '';\n } else {\n banner = \"> **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 };\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 });\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 return {\n content: [\n {\n type: \"text\",\n text: `Login successful! Connected to Adeu Cloud as: ${data.email || \"Unknown Email\"}.`,\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","// FILE: node/packages/mcp-server/src/tools/email.ts\nimport { 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\";\n\nconst CACHE_FILE = join(homedir(), \".adeu\", \"mcp_id_cache.json\");\nconst MAX_CACHE_SIZE = 1000;\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\nfunction resolveEmailId(shortId: string): string {\n if (!shortId) return shortId;\n const cache = loadIdCache();\n return cache[shortId] || shortId;\n}\n\nfunction stripTags(html: string): string {\n if (!html) return \"\";\n let text = html.replace(/<(style|script|head)[^>]*>[\\s\\S]*?<\\/\\1>/gi, \"\");\n text = text.replace(\n /<\\/?(p|div|br|hr|tr|li|h[1-6]|blockquote)\\b[^>]*>/gi,\n \"\\n\",\n );\n text = text.replace(/<[^>]+>/g, \"\");\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 const patterns = [\n /_{10,}/m,\n /^From:\\s.*?\\n(?:.*\\n){0,5}?Sent:\\s/m,\n /-----Original Message-----/m,\n /On .{1,200}? wrote:/m,\n /^Original Message$/m,\n ];\n let earliestCut = text.length;\n for (const pattern of patterns) {\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 let filepath = join(saveDir, filename);\n let counter = 1;\n const parts = filename.split(\".\");\n const ext = parts.length > 1 ? `.${parts.pop()}` : \"\";\n const stem = parts.join(\".\");\n\n while (existsSync(filepath)) {\n filepath = join(saveDir, `${stem}_${counter}${ext}`);\n counter++;\n }\n return filepath;\n}\n\nexport async function search_and_fetch_emails(args: any): Promise<ToolResult> {\n const apiKey = await getCloudAuthToken();\n const realEmailId = args.email_id ? resolveEmailId(args.email_id) : undefined;\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 };\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 const 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 });\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) throw new Error(`Cloud search failed: ${await res.text()}`);\n\n const data: any = await res.json();\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 saveIdCache(cache);\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 );\n return { content: [{ type: \"text\", text: lines.join(\"\\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 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 async function processAttachments(msg: any): Promise<string[]> {\n const localFiles: string[] = [];\n for (const att of msg.attachments || []) {\n if (att.base64_data) {\n try {\n const filepath = getUniqueFilepath(\n saveDir,\n att.filename || \"unnamed_file\",\n );\n writeFileSync(filepath, Buffer.from(att.base64_data, \"base64\"));\n localFiles.push(filepath);\n delete att.base64_data; // Free memory\n } catch (e) {\n console.error(`Failed to save attachment ${att.filename}`, e);\n }\n }\n }\n return localFiles;\n }\n\n const targetFiles = await processAttachments(full);\n const lines = [\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 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 histFiles = 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 lines.push(\n `**Body**:\\n\\`\\`\\`\\n${removeNestedQuotes(stripTags(histMsg.body_html || \"\"))}\\n\\`\\`\\`\\n`,\n );\n }\n }\n return { content: [{ type: \"text\", text: lines.join(\"\\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 formData.append(\n \"reply_to_email_id\",\n resolveEmailId(args.reply_to_email_id),\n );\n }\n if (args.subject) formData.append(\"subject\", args.subject);\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 const res = await fetch(`${BACKEND_URL}/api/v1/emails/drafts/new`, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${apiKey}`, Accept: \"application/json\" },\n body: formData as any,\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(`Cloud draft creation failed: ${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}\n"],"mappings":"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","names":["Server","readFileSync","basename","resolve","DocumentObject","ui_markdown","llm_content","resolve","server","homedir","join","readFileSync","writeFileSync","mkdirSync","existsSync","join","homedir","existsSync","readFileSync","mkdirSync","writeFileSync","readFileSync","Server","DocumentObject","basename","resolve"]}
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@adeu/mcp-server",
3
- "version": "1.6.8",
3
+ "version": "1.7.1",
4
4
  "description": "",
5
+ "mcpName": "ai.adeu/adeu",
5
6
  "main": "./dist/index.js",
6
7
  "types": "./dist/index.d.ts",
7
8
  "bin": {
@@ -0,0 +1,127 @@
1
+ // FILE: node/packages/mcp-server/src/desktop-auth.ts
2
+ import { createServer, Server } from "node:http";
3
+ import { exec } from "node:child_process";
4
+ import { homedir, platform } from "node:os";
5
+ import { join } from "node:path";
6
+ import {
7
+ writeFileSync,
8
+ readFileSync,
9
+ mkdirSync,
10
+ existsSync,
11
+ rmSync,
12
+ chmodSync,
13
+ } from "node:fs";
14
+ import { FRONTEND_URL } from "./shared.js";
15
+
16
+ const ADEU_DIR = join(homedir(), ".adeu");
17
+ const CRED_PATH = join(ADEU_DIR, "credentials.json");
18
+
19
+ function openBrowser(url: string) {
20
+ if (platform() === "darwin") exec(`open "${url}"`);
21
+ else if (platform() === "win32") exec(`start "" "${url}"`);
22
+ else exec(`xdg-open "${url}"`);
23
+ }
24
+
25
+ export class DesktopAuthManager {
26
+ static getApiKey(): string | null {
27
+ if (!existsSync(CRED_PATH)) return null;
28
+ try {
29
+ const data = JSON.parse(readFileSync(CRED_PATH, "utf-8"));
30
+ return data.api_key || null;
31
+ } catch {
32
+ return null;
33
+ }
34
+ }
35
+
36
+ static setApiKey(apiKey: string): void {
37
+ if (!existsSync(ADEU_DIR)) {
38
+ mkdirSync(ADEU_DIR, { recursive: true });
39
+ }
40
+ writeFileSync(CRED_PATH, JSON.stringify({ api_key: apiKey }));
41
+ // Restrict read/write to the current user only (equivalent to 0o600)
42
+ chmodSync(CRED_PATH, 0o600);
43
+ }
44
+
45
+ static clearApiKey(): void {
46
+ if (existsSync(CRED_PATH)) {
47
+ rmSync(CRED_PATH);
48
+ }
49
+ }
50
+
51
+ static async authenticateInteractive(): Promise<string> {
52
+ return new Promise((resolve, reject) => {
53
+ let server: Server;
54
+
55
+ const timeout = setTimeout(
56
+ () => {
57
+ if (server) server.close();
58
+ reject(new Error("Authentication timed out after 5 minutes."));
59
+ },
60
+ 5 * 60 * 1000,
61
+ );
62
+
63
+ server = createServer((req, res) => {
64
+ const url = new URL(req.url || "", `http://${req.headers.host}`);
65
+
66
+ if (url.pathname === "/callback") {
67
+ const apiKey = url.searchParams.get("api_key");
68
+
69
+ res.writeHead(apiKey ? 200 : 400, { "Content-Type": "text/html" });
70
+ const title = apiKey
71
+ ? "Authentication Successful!"
72
+ : "Authentication Failed";
73
+ const text = apiKey
74
+ ? "Your Adeu MCP server has been successfully authenticated. You can safely close this window and return to Claude."
75
+ : "No API key received. Please try again.";
76
+ const color = apiKey ? "#107c10" : "#d83b01";
77
+
78
+ res.end(`
79
+ <!DOCTYPE html><html><head><title>${title}</title>
80
+ <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>
81
+ </head><body><div class="container"><h1>${title}</h1><p>${text}</p>
82
+ <script>setTimeout(()=>window.close(), 3000);</script>
83
+ </div></body></html>
84
+ `);
85
+
86
+ clearTimeout(timeout);
87
+ // Allow response to send before closing server
88
+ setTimeout(() => server.close(), 100);
89
+
90
+ if (apiKey) {
91
+ this.setApiKey(apiKey);
92
+ resolve(apiKey);
93
+ } else {
94
+ reject(new Error("No API key received in callback."));
95
+ }
96
+ } else {
97
+ res.writeHead(404);
98
+ res.end();
99
+ }
100
+ });
101
+
102
+ server.listen(0, "127.0.0.1", () => {
103
+ const address = server.address();
104
+ if (address && typeof address !== "string") {
105
+ const authUrl = `${FRONTEND_URL}/login?desktop_port=${address.port}`;
106
+ openBrowser(authUrl);
107
+ }
108
+ });
109
+ });
110
+ }
111
+
112
+ static async ensureAuthenticated(): Promise<string> {
113
+ const key = this.getApiKey();
114
+ if (key) return key;
115
+ return this.authenticateInteractive();
116
+ }
117
+ }
118
+
119
+ export async function getCloudAuthToken(): Promise<string> {
120
+ const key = DesktopAuthManager.getApiKey();
121
+ if (!key) {
122
+ throw new Error(
123
+ "Authentication Required: You are not logged in. Please call the `login_to_adeu_cloud` tool first to authenticate, then try this task again.",
124
+ );
125
+ }
126
+ return key;
127
+ }