@adeu/mcp-server 1.10.0 → 1.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,4 +39,18 @@ Once connected, your AI agent will have access to the following tools:
39
39
  - `finalize_document`: Prepares a document for signature by applying native OOXML read-only locking and deep metadata sanitization.
40
40
 
41
41
  ## Documentation & Support
42
- For full architectural details, prompt recommendations, and the project constitution, please visit the [main Adeu repository](https://github.com/dealfluence/adeu) or our [website](https://adeu.ai).
42
+ For full architectural details, prompt recommendations, and the project constitution, please visit the [main Adeu repository](https://github.com/dealfluence/adeu) or our [website](https://adeu.ai).
43
+
44
+ ---
45
+
46
+ ## Development Runbook Note: Workspace Linkage Trap
47
+
48
+ > [!WARNING]
49
+ > **Node MCP: a committed source fix is NOT a running fix.**
50
+ > `dist/` is gitignored and the server runs the compiled bundle. After ANY change to `node/packages/core` or `mcp-server`:
51
+ >
52
+ > 1. **Relink dependencies:** `cd node && npm install` (relinks `@adeu/core` in the workspace; a copied — not symlinked — core is the #1 cause of "fix didn't take").
53
+ > 2. **Rebuild the workspace:** `npm run build` (core builds before mcp-server; confirm `core/dist` timestamp is updated).
54
+ > 3. **Verify:** Check that the build verification sentinel check succeeds (automatically runs postbuild, or can be run manually via `npm run build:verify` under `packages/mcp-server`).
55
+ > 4. **Restart the MCP server:** Restart the MCP server AND reconnect in the client to flush any cached process states.
56
+ > 5. **Confirm the build stamp:** Verify that the build stamp in the live server (via `server_info` or startup logs) matches your built git HEAD.
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
7
7
  import { basename as basename2, resolve as resolve2, extname, dirname, join as join3 } from "path";
8
8
  import { z } from "zod";
9
9
  import {
10
- registerAppTool,
10
+ registerAppTool as origRegisterAppTool,
11
11
  registerAppResource,
12
12
  RESOURCE_MIME_TYPE
13
13
  } from "@modelcontextprotocol/ext-apps/server";
@@ -15,6 +15,7 @@ import fs from "fs";
15
15
  import {
16
16
  identifyEngine,
17
17
  extractTextFromBuffer,
18
+ _extractTextFromDoc,
18
19
  DocumentObject as DocumentObject2,
19
20
  RedlineEngine,
20
21
  BatchValidationError,
@@ -109,14 +110,15 @@ ${ui_markdown}`;
109
110
  }
110
111
  };
111
112
  }
112
- function build_outline_response(doc, projected_text, file_path, outline_max_level = 2, outline_verbose = false) {
113
+ function build_outline_response(doc, projected_text, file_path, outline_max_level = 2, outline_verbose = false, paragraph_offsets = null) {
113
114
  const [body] = split_structural_appendix(projected_text);
114
115
  const pagination_result = paginate(body, "");
115
116
  const nodes = extract_outline(
116
117
  doc,
117
118
  body,
118
119
  pagination_result.body_pages,
119
- pagination_result.body_page_offsets
120
+ pagination_result.body_page_offsets,
121
+ paragraph_offsets
120
122
  );
121
123
  const rendered = render_outline_tree(
122
124
  nodes,
@@ -331,11 +333,14 @@ async function login_to_adeu_cloud() {
331
333
  }
332
334
  if (!res.ok) throw new Error(`HTTP Error: ${res.status}`);
333
335
  const data = await res.json();
336
+ const email = data.email || "Unknown Email";
334
337
  return {
335
338
  content: [
336
339
  {
337
340
  type: "text",
338
- text: `Login successful! Connected to Adeu Cloud as: ${data.email || "Unknown Email"}.`
341
+ text: `Login successful. You are now authenticated to Adeu Cloud as the user who owns the provider account \`${email}\` (the account used for SSO).
342
+
343
+ This single login grants access to ALL of this user's linked provider accounts and ALL of their mailboxes for the duration of this session \u2014 not just \`${email}\`. Call \`list_available_mailboxes\` to see every mailbox that can be queried or drafted from.`
339
344
  }
340
345
  ]
341
346
  };
@@ -980,7 +985,9 @@ function readFileBytesOrThrow(filePath) {
980
985
  return readFileSync3(filePath);
981
986
  } catch (err) {
982
987
  if (err.code === "ENOENT") {
983
- throw new Error(`File not found: ${filePath}`);
988
+ throw new Error(
989
+ `File not found: ${filePath}. Note: If you are running in a sandboxed/containerized environment, the host application or MCP server may not have access to your local workspace files. You can resolve this by installing Adeu directly inside your sandboxed environment using 'uv tool install adeu' and executing the commands via the CLI.`
990
+ );
984
991
  }
985
992
  throw err;
986
993
  }
@@ -998,10 +1005,30 @@ var READ_DOCX_TAIL = "Modes:\n- 'full' (default): paginated body content. Use pa
998
1005
  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";
999
1006
  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.";
1000
1007
  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.";
1008
+ var gitSha = "650c01c";
1009
+ var packageVersion = "1.11.2";
1010
+ var buildTag = ` [Adeu v${packageVersion}+${gitSha}]`;
1001
1011
  var server = new McpServer({
1002
1012
  name: "adeu-redlining-service",
1003
- version: "1.0.0"
1013
+ version: packageVersion
1004
1014
  });
1015
+ var originalRegisterTool = server.registerTool.bind(server);
1016
+ server.registerTool = (name, schema, handler) => {
1017
+ if (schema && typeof schema === "object") {
1018
+ if (schema.description) {
1019
+ schema.description = schema.description.trim() + buildTag;
1020
+ }
1021
+ }
1022
+ return originalRegisterTool(name, schema, handler);
1023
+ };
1024
+ var registerAppTool = (mcpServer, name, schema, handler) => {
1025
+ if (schema && typeof schema === "object") {
1026
+ if (schema.description) {
1027
+ schema.description = schema.description.trim() + buildTag;
1028
+ }
1029
+ }
1030
+ return origRegisterAppTool(mcpServer, name, schema, handler);
1031
+ };
1005
1032
  var UI_CSP = {
1006
1033
  connectDomains: ["https://fonts.googleapis.com", "https://fonts.gstatic.com"],
1007
1034
  resourceDomains: [
@@ -1094,21 +1121,26 @@ registerAppTool(
1094
1121
  }) => {
1095
1122
  try {
1096
1123
  const buf = readFileBytesOrThrow(file_path);
1097
- const text = await extractTextFromBuffer(buf, clean_view);
1098
1124
  if (mode === "outline") {
1099
1125
  const doc = await DocumentObject2.load(buf);
1100
- return build_outline_response(
1126
+ const extract_res = _extractTextFromDoc(doc, clean_view, true, true);
1127
+ const res2 = build_outline_response(
1101
1128
  doc,
1102
- text,
1129
+ extract_res.text,
1103
1130
  file_path,
1104
1131
  outline_max_level,
1105
- outline_verbose
1132
+ outline_verbose,
1133
+ extract_res.paragraph_offsets
1106
1134
  );
1135
+ return res2;
1107
1136
  }
1137
+ const text = await extractTextFromBuffer(buf, clean_view);
1108
1138
  if (mode === "appendix") {
1109
- return build_appendix_response(text, page, file_path);
1139
+ const res2 = build_appendix_response(text, page, file_path);
1140
+ return res2;
1110
1141
  }
1111
- return build_paginated_response(text, page, file_path);
1142
+ const res = build_paginated_response(text, page, file_path);
1143
+ return res;
1112
1144
  } catch (e) {
1113
1145
  return {
1114
1146
  isError: true,
@@ -1167,10 +1199,18 @@ server.registerTool(
1167
1199
  author_name: z.string().describe("Name to appear in Track Changes (e.g., 'Reviewer AI')."),
1168
1200
  changes: z.array(z.any()).describe("List of changes to apply. Each change must specify 'type'."),
1169
1201
  output_path: z.string().optional().describe("Optional output path."),
1170
- dry_run: z.boolean().optional().default(false).describe("If True, simulates the changes and returns a detailed preview report without modifying any files.")
1202
+ dry_run: z.boolean().optional().default(false).describe(
1203
+ "If True, simulates the changes and returns a detailed preview report without modifying any files."
1204
+ )
1171
1205
  }
1172
1206
  },
1173
- async ({ original_docx_path, author_name, changes, output_path, dry_run }) => {
1207
+ async ({
1208
+ original_docx_path,
1209
+ author_name,
1210
+ changes,
1211
+ output_path,
1212
+ dry_run
1213
+ }) => {
1174
1214
  try {
1175
1215
  if (!author_name || !author_name.trim())
1176
1216
  return {
@@ -1362,7 +1402,9 @@ ${result.reportText}`
1362
1402
  );
1363
1403
  server.registerTool(
1364
1404
  "login_to_adeu_cloud",
1365
- { description: "Logs the user into the Adeu Cloud backend." },
1405
+ {
1406
+ description: "Logs the user into Adeu Cloud. Opens a browser window for SSO authentication.\n\nIMPORTANT \u2014 login is user-level, not account-level:\n- An Adeu user can have multiple linked provider accounts (Microsoft, Google) and multiple mailboxes (personal + shared/delegated). One linked account is marked primary.\n- Signing in through ANY of the user's linked accounts authenticates the same Adeu user. Once logged in, the session can read from and draft in ALL of that user's linked accounts and ALL of their mailboxes \u2014 not just the one used to sign in.\n- The choice of which provider account to sign in through is purely an SSO mechanism; it does not select a 'current account' for the session.\n\nWhen the user asks which accounts or mailboxes are available, call `list_available_mailboxes` rather than naming a single account from the login response."
1407
+ },
1366
1408
  async () => {
1367
1409
  try {
1368
1410
  return await login_to_adeu_cloud();
@@ -1408,7 +1450,7 @@ server.registerTool(
1408
1450
  server.registerTool(
1409
1451
  "list_available_mailboxes",
1410
1452
  {
1411
- description: "Lists all personal and shared delegated mailboxes the authenticated user has access to. Returns each mailbox's `email_address`, `display_name`, auto-processing settings, and write-back preference.\n\nCall this FIRST when the user mentions a specific mailbox or shared inbox by name, 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.\n\nOmitting `mailbox_address` on those tools targets the user's primary personal mailbox.",
1453
+ description: "Lists all personal and shared/delegated mailboxes the authenticated Adeu user has access to, across ALL of their linked provider accounts. Returns each mailbox's `email_address`, `display_name`, auto-processing settings, and write-back preference.\n\nThis is the right tool to answer 'which accounts/mailboxes am I logged into?' \u2014 Adeu login is user-level, so a single MCP session can see every mailbox listed here regardless of which provider account was used for SSO.\n\nCall this FIRST when the user names a specific mailbox or shared inbox, to resolve the canonical `email_address`. Then pass that address as `mailbox_address` to `search_and_fetch_emails` or `create_email_draft` to scope the operation. Omitting `mailbox_address` on those tools targets the user's primary personal mailbox.",
1412
1454
  inputSchema: {}
1413
1455
  },
1414
1456
  async () => {
@@ -1472,8 +1514,10 @@ ${stats.skipped_details.join("\n")}`;
1472
1514
  async function main() {
1473
1515
  const transport = new StdioServerTransport();
1474
1516
  await server.connect(transport);
1517
+ const gitSha2 = "650c01c";
1518
+ const buildTs = "2026-06-17T20:38:12.518Z";
1475
1519
  console.error(
1476
- `Adeu MCP Server (Node.js Engine: ${identifyEngine()}) running on stdio`
1520
+ `Adeu MCP Server (Node.js Engine: ${identifyEngine()}) running on stdio build=${gitSha2}@${buildTs}`
1477
1521
  );
1478
1522
  }
1479
1523
  main().catch(console.error);