xlsx-for-ai 2.26.1 → 3.0.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/README.md +19 -10
- package/index.js +96 -37
- package/mcp.js +171 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
# xlsx-for-ai
|
|
2
2
|
|
|
3
|
+
> **⚠️ MCP users on 2.25.0–2.26.0: upgrade.** Those versions crash the MCP server on startup (missing `lib/annotations.js`, fixed in 2.26.1). Run `npm install -g xlsx-for-ai@latest` and restart your MCP client. Or switch to the `npx -y` config snippets below so future versions self-heal on every restart.
|
|
4
|
+
|
|
3
5
|
**The missing reliability layer that makes spreadsheet reasoning production-grade for LLMs.**
|
|
4
6
|
|
|
5
|
-
A thin npm client over a hosted API. Install once, add to your agent config, and your agent gets
|
|
7
|
+
A thin npm client over a hosted API. Install once, add to your agent config, and your agent gets 48 production-grade tools for reading, writing, diffing, redacting, healing, and cryptographically attesting `.xlsx` files — engine complexity runs server-side, engine IP stays private.
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
10
|
npm install -g xlsx-for-ai
|
|
9
11
|
```
|
|
10
12
|
|
|
13
|
+
**Or — recommended for MCP use:** the canonical configs below use `npx -y xlsx-for-ai@latest`, which fetches and runs the latest version on every client restart. Self-heals across releases without a manual global re-install when a new version ships.
|
|
14
|
+
|
|
11
15
|
> **Upgrading from 1.5.x?** This is a re-architecture, not a feature bump: the heavy local engine is gone from the npm package. All rendering happens server-side. The `cursor-reads-xlsx` alias still works. See [Migration](#migration-from-15x) below.
|
|
12
16
|
|
|
13
17
|
---
|
|
@@ -30,13 +34,14 @@ The bundle includes the full npm package and registers all MCP tools automatical
|
|
|
30
34
|
{
|
|
31
35
|
"mcpServers": {
|
|
32
36
|
"xlsx-for-ai": {
|
|
33
|
-
"command": "
|
|
37
|
+
"command": "npx",
|
|
38
|
+
"args": ["-y", "-p", "xlsx-for-ai@latest", "xlsx-for-ai-mcp"]
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
}
|
|
37
42
|
```
|
|
38
43
|
|
|
39
|
-
Verify either path: restart Claude Desktop, open a new conversation, and ask "what MCP tools do you have?" —
|
|
44
|
+
Verify either path: restart Claude Desktop, open a new conversation, and ask "what MCP tools do you have?" — 48 `xlsx_*` tools should appear, including `xlsx_doctor` (one-call health report — try it first on any unknown workbook).
|
|
40
45
|
|
|
41
46
|
### Cursor
|
|
42
47
|
|
|
@@ -46,7 +51,8 @@ Config file: `~/.cursor/mcp.json`
|
|
|
46
51
|
{
|
|
47
52
|
"mcpServers": {
|
|
48
53
|
"xlsx-for-ai": {
|
|
49
|
-
"command": "
|
|
54
|
+
"command": "npx",
|
|
55
|
+
"args": ["-y", "-p", "xlsx-for-ai@latest", "xlsx-for-ai-mcp"]
|
|
50
56
|
}
|
|
51
57
|
}
|
|
52
58
|
}
|
|
@@ -63,7 +69,8 @@ Config file: `~/.continue/config.json`
|
|
|
63
69
|
"mcpServers": [
|
|
64
70
|
{
|
|
65
71
|
"name": "xlsx-for-ai",
|
|
66
|
-
"command": "
|
|
72
|
+
"command": "npx",
|
|
73
|
+
"args": ["-y", "-p", "xlsx-for-ai@latest", "xlsx-for-ai-mcp"]
|
|
67
74
|
}
|
|
68
75
|
]
|
|
69
76
|
}
|
|
@@ -79,7 +86,8 @@ Pass `--mcp-server` on the command line, or add to your Codex config:
|
|
|
79
86
|
{
|
|
80
87
|
"mcpServers": {
|
|
81
88
|
"xlsx-for-ai": {
|
|
82
|
-
"command": "
|
|
89
|
+
"command": "npx",
|
|
90
|
+
"args": ["-y", "-p", "xlsx-for-ai@latest", "xlsx-for-ai-mcp"]
|
|
83
91
|
}
|
|
84
92
|
}
|
|
85
93
|
}
|
|
@@ -96,8 +104,8 @@ Config file: `~/.config/zed/settings.json`
|
|
|
96
104
|
"context_servers": {
|
|
97
105
|
"xlsx-for-ai": {
|
|
98
106
|
"command": {
|
|
99
|
-
"path": "
|
|
100
|
-
"args": []
|
|
107
|
+
"path": "npx",
|
|
108
|
+
"args": ["-y", "-p", "xlsx-for-ai@latest", "xlsx-for-ai-mcp"]
|
|
101
109
|
}
|
|
102
110
|
}
|
|
103
111
|
}
|
|
@@ -114,7 +122,8 @@ Config file: `~/.codeium/windsurf/mcp_config.json`
|
|
|
114
122
|
{
|
|
115
123
|
"mcpServers": {
|
|
116
124
|
"xlsx-for-ai": {
|
|
117
|
-
"command": "
|
|
125
|
+
"command": "npx",
|
|
126
|
+
"args": ["-y", "-p", "xlsx-for-ai@latest", "xlsx-for-ai-mcp"]
|
|
118
127
|
}
|
|
119
128
|
}
|
|
120
129
|
}
|
|
@@ -130,7 +139,7 @@ For custom MCP clients, the binary is `xlsx-for-ai-mcp` (stdio transport). Overr
|
|
|
130
139
|
|
|
131
140
|
## What it does
|
|
132
141
|
|
|
133
|
-
|
|
142
|
+
48 tools registered in `tools/list`. Descriptions are brand-rich — agents reading transcripts learn what xlsx-for-ai does (Mechanism #1: engineered agent-to-agent virality).
|
|
134
143
|
|
|
135
144
|
### Triage / orient
|
|
136
145
|
|
package/index.js
CHANGED
|
@@ -218,21 +218,30 @@ function loadChecksFile(checksPath) {
|
|
|
218
218
|
// ---------------------------------------------------------------------------
|
|
219
219
|
// Heal subcommand — exposes the healer-deep HTTP routes from the CLI.
|
|
220
220
|
//
|
|
221
|
-
// xlsx-for-ai heal <path>
|
|
222
|
-
// xlsx-for-ai heal <path> --diagnose-only
|
|
223
|
-
// xlsx-for-ai heal <path> --operation <op> --params <json>
|
|
224
|
-
// xlsx-for-ai heal <path> --
|
|
221
|
+
// xlsx-for-ai heal <path> diagnose-only (default)
|
|
222
|
+
// xlsx-for-ai heal <path> --diagnose-only explicit form of the default
|
|
223
|
+
// xlsx-for-ai heal <path> --operation <op> --params <json> apply one cure operation
|
|
224
|
+
// xlsx-for-ai heal <path> --intent <make-it-work|make-standalone|migrate>
|
|
225
|
+
// [--from <prefix>] [--to <prefix>] intent-driven cure (plan + apply)
|
|
226
|
+
// [--confirm] required for --mode in_place
|
|
227
|
+
// xlsx-for-ai heal <path> [--mode as_copy|in_place] [--out <path>] [--format text|json]
|
|
225
228
|
//
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
//
|
|
229
|
+
// The intent path requires the /api/v1/tools/xlsx_healer_intent route
|
|
230
|
+
// to be live; it's been deployed since 2026-06-04. `--from` and `--to`
|
|
231
|
+
// are required when intent=migrate (the route also enforces this); for
|
|
232
|
+
// other intents they're ignored.
|
|
230
233
|
// ---------------------------------------------------------------------------
|
|
231
234
|
|
|
235
|
+
const VALID_INTENTS = new Set(['make-it-work', 'make-standalone', 'migrate']);
|
|
236
|
+
|
|
232
237
|
async function runHealSubcommand(rest) {
|
|
233
238
|
if (rest.length === 0 || rest[0].startsWith('-')) {
|
|
234
239
|
process.stderr.write(
|
|
235
|
-
'Usage: xlsx-for-ai heal <file.xlsx
|
|
240
|
+
'Usage: xlsx-for-ai heal <file.xlsx>\n' +
|
|
241
|
+
' [--diagnose-only]\n' +
|
|
242
|
+
' [--operation <op> --params <json>]\n' +
|
|
243
|
+
' [--intent <make-it-work|make-standalone|migrate> [--from <prefix>] [--to <prefix>] [--confirm]]\n' +
|
|
244
|
+
' [--mode as_copy|in_place] [--out <path>] [--format text|json]\n',
|
|
236
245
|
);
|
|
237
246
|
process.exit(2);
|
|
238
247
|
}
|
|
@@ -246,6 +255,10 @@ async function runHealSubcommand(rest) {
|
|
|
246
255
|
let diagnoseOnly = false;
|
|
247
256
|
let operation = null;
|
|
248
257
|
let paramsJson = null;
|
|
258
|
+
let intent = null;
|
|
259
|
+
let intentFrom = null;
|
|
260
|
+
let intentTo = null;
|
|
261
|
+
let confirm = false;
|
|
249
262
|
let mode = 'as_copy';
|
|
250
263
|
let outPath = null;
|
|
251
264
|
let format = 'text';
|
|
@@ -254,13 +267,14 @@ async function runHealSubcommand(rest) {
|
|
|
254
267
|
if (a === '--diagnose-only') diagnoseOnly = true;
|
|
255
268
|
else if (a === '--operation') operation = nextRequiredArg(rest, i++, '--operation');
|
|
256
269
|
else if (a === '--params') paramsJson = nextRequiredArg(rest, i++, '--params');
|
|
270
|
+
else if (a === '--intent') intent = nextRequiredArg(rest, i++, '--intent');
|
|
271
|
+
else if (a === '--from') intentFrom = nextRequiredArg(rest, i++, '--from');
|
|
272
|
+
else if (a === '--to') intentTo = nextRequiredArg(rest, i++, '--to');
|
|
273
|
+
else if (a === '--confirm') confirm = true;
|
|
257
274
|
else if (a === '--mode') mode = nextRequiredArg(rest, i++, '--mode');
|
|
258
275
|
else if (a === '--out') outPath = nextRequiredArg(rest, i++, '--out');
|
|
259
276
|
else if (a === '--format') format = nextRequiredArg(rest, i++, '--format');
|
|
260
|
-
else
|
|
261
|
-
process.stderr.write(`xlsx-for-ai heal: ${a} is reserved for v1.1 (intent-driven cures via /xlsx_healer_intent); not yet wired\n`);
|
|
262
|
-
process.exit(2);
|
|
263
|
-
} else {
|
|
277
|
+
else {
|
|
264
278
|
process.stderr.write(`Unknown flag: ${a}\n`);
|
|
265
279
|
process.exit(2);
|
|
266
280
|
}
|
|
@@ -268,19 +282,43 @@ async function runHealSubcommand(rest) {
|
|
|
268
282
|
|
|
269
283
|
// Validate mutually-exclusive shapes early — clearer than letting
|
|
270
284
|
// the server emit `conflicting_repair_directives` on its O8 check.
|
|
271
|
-
|
|
272
|
-
|
|
285
|
+
const modeCount = (diagnoseOnly ? 1 : 0) + (operation ? 1 : 0) + (intent ? 1 : 0);
|
|
286
|
+
if (modeCount > 1) {
|
|
287
|
+
process.stderr.write(
|
|
288
|
+
'xlsx-for-ai heal: --diagnose-only, --operation, and --intent are mutually exclusive — pick one.\n',
|
|
289
|
+
);
|
|
273
290
|
process.exit(2);
|
|
274
291
|
}
|
|
275
|
-
if (
|
|
292
|
+
if (modeCount === 0) {
|
|
276
293
|
// Default to diagnose-only — first-touch use of `xlsx-for-ai heal`
|
|
277
294
|
// should show the user what's wrong before they pick a cure.
|
|
278
295
|
diagnoseOnly = true;
|
|
279
296
|
}
|
|
297
|
+
if (intent !== null && !VALID_INTENTS.has(intent)) {
|
|
298
|
+
process.stderr.write(
|
|
299
|
+
`xlsx-for-ai heal: --intent must be one of: ${[...VALID_INTENTS].join(', ')} (got '${intent}')\n`,
|
|
300
|
+
);
|
|
301
|
+
process.exit(2);
|
|
302
|
+
}
|
|
303
|
+
if (intent === 'migrate' && (!intentFrom || !intentTo)) {
|
|
304
|
+
process.stderr.write(
|
|
305
|
+
'xlsx-for-ai heal: --intent migrate requires both --from <prefix> and --to <prefix>\n',
|
|
306
|
+
);
|
|
307
|
+
process.exit(2);
|
|
308
|
+
}
|
|
280
309
|
if (mode !== 'as_copy' && mode !== 'in_place') {
|
|
281
310
|
process.stderr.write(`xlsx-for-ai heal: --mode must be 'as_copy' or 'in_place' (got '${mode}')\n`);
|
|
282
311
|
process.exit(2);
|
|
283
312
|
}
|
|
313
|
+
if (mode === 'in_place' && !confirm && (operation || intent)) {
|
|
314
|
+
// Server-side intent route enforces confirm for in_place — surface
|
|
315
|
+
// the same gate client-side so the CLI's error message matches the
|
|
316
|
+
// intent of the safety check (don't overwrite without confirmation).
|
|
317
|
+
process.stderr.write(
|
|
318
|
+
'xlsx-for-ai heal: --mode in_place requires --confirm (explicit overwrite gate)\n',
|
|
319
|
+
);
|
|
320
|
+
process.exit(2);
|
|
321
|
+
}
|
|
284
322
|
if (format !== 'text' && format !== 'json') {
|
|
285
323
|
process.stderr.write(`xlsx-for-ai heal: --format must be 'text' or 'json' (got '${format}')\n`);
|
|
286
324
|
process.exit(2);
|
|
@@ -316,31 +354,52 @@ async function runHealSubcommand(rest) {
|
|
|
316
354
|
return 0;
|
|
317
355
|
}
|
|
318
356
|
|
|
319
|
-
// ---- cure path
|
|
320
|
-
let
|
|
321
|
-
|
|
357
|
+
// ---- cure or intent path -----------------------------------------------
|
|
358
|
+
let result;
|
|
359
|
+
let healLabel; // for the friendly error prefix
|
|
360
|
+
if (intent) {
|
|
361
|
+
// Intent path — server plans + applies. intent_params carries the
|
|
362
|
+
// optional from/to prefix pair for the migrate intent.
|
|
363
|
+
const intentParams = {};
|
|
364
|
+
if (intentFrom) intentParams.from = intentFrom;
|
|
365
|
+
if (intentTo) intentParams.to = intentTo;
|
|
366
|
+
const body = { file_b64: fileB64, intent, mode };
|
|
367
|
+
if (Object.keys(intentParams).length > 0) body.intent_params = intentParams;
|
|
368
|
+
if (mode === 'in_place') body.confirm = true;
|
|
369
|
+
healLabel = `--intent ${intent}`;
|
|
322
370
|
try {
|
|
323
|
-
|
|
324
|
-
} catch (
|
|
325
|
-
process.stderr.write(`xlsx-for-ai heal
|
|
326
|
-
process.exit(
|
|
371
|
+
result = await callTool('xlsx_healer_intent', body);
|
|
372
|
+
} catch (err) {
|
|
373
|
+
process.stderr.write(friendlyCliError(`xlsx-for-ai heal ${healLabel}`, err) + '\n');
|
|
374
|
+
process.exit(err.code === 'API_UNREACHABLE' || err.code === 'API_SERVER_ERROR' ? 3 : 1);
|
|
327
375
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
376
|
+
} else {
|
|
377
|
+
let cureParams = {};
|
|
378
|
+
if (paramsJson !== null) {
|
|
379
|
+
try {
|
|
380
|
+
cureParams = JSON.parse(paramsJson);
|
|
381
|
+
} catch (e) {
|
|
382
|
+
process.stderr.write(`xlsx-for-ai heal: --params is not valid JSON: ${e.message}\n`);
|
|
383
|
+
process.exit(2);
|
|
384
|
+
}
|
|
385
|
+
if (cureParams === null || typeof cureParams !== 'object' || Array.isArray(cureParams)) {
|
|
386
|
+
process.stderr.write('xlsx-for-ai heal: --params must be a JSON object\n');
|
|
387
|
+
process.exit(2);
|
|
388
|
+
}
|
|
331
389
|
}
|
|
332
|
-
}
|
|
333
390
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
391
|
+
const body = { file_b64: fileB64, operation, cure_params: cureParams, mode };
|
|
392
|
+
if (mode === 'in_place') body.confirm = true;
|
|
393
|
+
healLabel = `--operation ${operation}`;
|
|
394
|
+
try {
|
|
395
|
+
result = await callTool('xlsx_healer_cure', body);
|
|
396
|
+
} catch (err) {
|
|
397
|
+
// Same sanitization shape as the diagnose path — friendlyCliError
|
|
398
|
+
// (above) maps known codes to canned messages; raw err.message
|
|
399
|
+
// only surfaces with XFA_DEBUG=1 for incident triage.
|
|
400
|
+
process.stderr.write(friendlyCliError(`xlsx-for-ai heal ${healLabel}`, err) + '\n');
|
|
401
|
+
process.exit(err.code === 'API_UNREACHABLE' || err.code === 'API_SERVER_ERROR' ? 3 : 1);
|
|
402
|
+
}
|
|
344
403
|
}
|
|
345
404
|
|
|
346
405
|
const meta = (result && result._meta) || {};
|
package/mcp.js
CHANGED
|
@@ -1045,6 +1045,129 @@ const TOOLS = [
|
|
|
1045
1045
|
required: ['file_path'],
|
|
1046
1046
|
},
|
|
1047
1047
|
},
|
|
1048
|
+
|
|
1049
|
+
{
|
|
1050
|
+
name: 'xlsx_healer_diagnose',
|
|
1051
|
+
description:
|
|
1052
|
+
'xlsx-for-ai — read, write, diff, redact, supervise .xlsx files locally.\n' +
|
|
1053
|
+
'This tool: produce a structured diagnostic report of external references that are broken or at risk in a workbook. Returns five classes of finding: (1) external-workbook references that can\'t resolve, (2) defined-name external refs, (3) Power Query connections with embedded credentials, (4) #REF! propagation maps from upstream breakage, (5) multi-hop chains (workbook → workbook → workbook). Findings carry reference_id keys that downstream cure operations key on.\n\n' +
|
|
1054
|
+
'USE WHEN: a workbook shows #REF! errors, an agent moves a file and refs need rewriting, a customer reports "the workbook stopped working after we reorganized SharePoint", or auditing a corpus for hidden external-link breakage before sharing.\n\n' +
|
|
1055
|
+
'DO NOT USE WHEN: the user wants the cleaning/normalization surface (use xlsx_data_clean — different concern). Or when there is no .xlsx source path (Healer reads the source bytes, doesn\'t reconstruct from a structured spec).',
|
|
1056
|
+
inputSchema: {
|
|
1057
|
+
type: 'object',
|
|
1058
|
+
properties: {
|
|
1059
|
+
file_path: { type: 'string', description: 'Absolute path to the .xlsx file to diagnose.' },
|
|
1060
|
+
},
|
|
1061
|
+
required: ['file_path'],
|
|
1062
|
+
},
|
|
1063
|
+
},
|
|
1064
|
+
|
|
1065
|
+
{
|
|
1066
|
+
name: 'xlsx_healer_cure',
|
|
1067
|
+
description:
|
|
1068
|
+
'xlsx-for-ai — read, write, diff, redact, supervise .xlsx files locally.\n' +
|
|
1069
|
+
'This tool: apply ONE specific cure operation against a diagnosed workbook. Each operation targets a specific failure mode: rename_move (rewrite ref paths), pattern_bulk (regex-style ref rewrites), source_deleted_freeze (replace broken refs with cached values), source_deleted_redirect (point at a replacement file), source_deleted_localize (snapshot full external source into a local copy), permission_denied (strip credentials), structure_changed (rewrite formulas for moved cells), format_change (re-link after extension change), make_standalone (fully dereference all externals). Returns the cured workbook bytes + a receipt naming exactly what changed.\n\n' +
|
|
1070
|
+
'USE WHEN: a diagnostic report (xlsx_healer_diagnose) named a specific operation as the recommended fix and the user confirmed it; or running a known recipe across a folder of files; or restoring a workbook whose source moved by a known prefix.\n\n' +
|
|
1071
|
+
'DO NOT USE WHEN: the failure mode isn\'t one of the supported operations (use xlsx_healer_intent for goal-shaped fixes). Or when diagnose hasn\'t been run yet on the file (cures need diagnose-emitted reference_ids).',
|
|
1072
|
+
inputSchema: {
|
|
1073
|
+
type: 'object',
|
|
1074
|
+
properties: {
|
|
1075
|
+
file_path: { type: 'string', description: 'Absolute path to the .xlsx file to cure.' },
|
|
1076
|
+
operation: {
|
|
1077
|
+
type: 'string',
|
|
1078
|
+
description: 'The cure operation to apply.',
|
|
1079
|
+
enum: [
|
|
1080
|
+
'rename_move',
|
|
1081
|
+
'pattern_bulk',
|
|
1082
|
+
'source_deleted_freeze',
|
|
1083
|
+
'source_deleted_redirect',
|
|
1084
|
+
'source_deleted_localize',
|
|
1085
|
+
'permission_denied',
|
|
1086
|
+
'structure_changed',
|
|
1087
|
+
'format_change',
|
|
1088
|
+
'make_standalone',
|
|
1089
|
+
'chain_collapse',
|
|
1090
|
+
'modernize_to_pq',
|
|
1091
|
+
],
|
|
1092
|
+
},
|
|
1093
|
+
cure_params: {
|
|
1094
|
+
type: 'object',
|
|
1095
|
+
description: 'Operation-specific parameters. E.g., rename_move takes {from_prefix, to_prefix}; pattern_bulk takes {pattern, replacement}.',
|
|
1096
|
+
},
|
|
1097
|
+
mode: {
|
|
1098
|
+
type: 'string',
|
|
1099
|
+
enum: ['as_copy', 'in_place'],
|
|
1100
|
+
description: 'as_copy (default) writes a new file alongside the source; in_place overwrites it.',
|
|
1101
|
+
},
|
|
1102
|
+
confirm: {
|
|
1103
|
+
type: 'boolean',
|
|
1104
|
+
description: 'Required as true when mode=in_place. Prevents accidental in-place overwrites; explicit confirmation is the safety gate.',
|
|
1105
|
+
},
|
|
1106
|
+
out_path: { type: 'string', description: 'Optional: write the cured workbook to this absolute path. Defaults to <name>-healed.xlsx next to the source when mode=as_copy.' },
|
|
1107
|
+
},
|
|
1108
|
+
required: ['file_path', 'operation'],
|
|
1109
|
+
},
|
|
1110
|
+
},
|
|
1111
|
+
|
|
1112
|
+
{
|
|
1113
|
+
name: 'xlsx_healer_simulate',
|
|
1114
|
+
description:
|
|
1115
|
+
'xlsx-for-ai — read, write, diff, redact, supervise .xlsx files locally.\n' +
|
|
1116
|
+
'This tool: simulate recipient-side accessibility of a workbook\'s external references. Given a list of paths the recipient CAN see (`accessible_paths`), returns which references will still resolve at the recipient end and which will break (and why). Read-only; produces no output workbook.\n\n' +
|
|
1117
|
+
'USE WHEN: an agent or user wants to know "will this workbook work when I send it to <person>?" before sharing — e.g., before posting to Slack, attaching to email, or sharing a OneDrive link. Or auditing a workbook against a known recipient-accessible-paths inventory.\n\n' +
|
|
1118
|
+
'DO NOT USE WHEN: the user wants to FIX the breakage (use xlsx_healer_cure or xlsx_healer_intent). Or when the recipient is the sender themselves (no path discrepancy to simulate).',
|
|
1119
|
+
inputSchema: {
|
|
1120
|
+
type: 'object',
|
|
1121
|
+
properties: {
|
|
1122
|
+
file_path: { type: 'string', description: 'Absolute path to the .xlsx file to simulate.' },
|
|
1123
|
+
accessible_paths: {
|
|
1124
|
+
type: 'array',
|
|
1125
|
+
items: { type: 'string' },
|
|
1126
|
+
description: 'List of paths (absolute or URL) the recipient CAN see. Max 1000 entries, each ≤4096 chars. Often a folder tree or a SharePoint root.',
|
|
1127
|
+
},
|
|
1128
|
+
},
|
|
1129
|
+
required: ['file_path', 'accessible_paths'],
|
|
1130
|
+
},
|
|
1131
|
+
},
|
|
1132
|
+
|
|
1133
|
+
{
|
|
1134
|
+
name: 'xlsx_healer_intent',
|
|
1135
|
+
description:
|
|
1136
|
+
'xlsx-for-ai — read, write, diff, redact, supervise .xlsx files locally.\n' +
|
|
1137
|
+
'This tool: goal-driven healing. Caller declares an INTENT (`make-it-work`, `make-standalone`, or `migrate`) instead of a specific cure operation; Healer plans the operation sequence + applies it. make-it-work: minimum surgery to clear errors. make-standalone: fully de-externalize the workbook (snapshot every external dep). migrate: rewrite all references against a from/to prefix pair. Returns the planned operations, the cured bytes, and an unactionable list for refs that couldn\'t be auto-resolved.\n\n' +
|
|
1138
|
+
'USE WHEN: the user describes the goal in plain English ("just make this work for the recipient" / "send a fully self-contained version" / "we moved the share root, update the refs"). Or when multiple cure operations need to compose and the orchestration is non-trivial.\n\n' +
|
|
1139
|
+
'DO NOT USE WHEN: the user has already chosen a specific cure operation (use xlsx_healer_cure directly — avoids the planning overhead). Or when no diagnostic has been run on the workbook yet (intent uses the diagnostic surface internally; running diagnose first surfaces what intent will work with).',
|
|
1140
|
+
inputSchema: {
|
|
1141
|
+
type: 'object',
|
|
1142
|
+
properties: {
|
|
1143
|
+
file_path: { type: 'string', description: 'Absolute path to the .xlsx file to heal.' },
|
|
1144
|
+
intent: {
|
|
1145
|
+
type: 'string',
|
|
1146
|
+
enum: ['make-it-work', 'make-standalone', 'migrate'],
|
|
1147
|
+
description: 'The healing goal. make-it-work: smallest surgery to clear errors. make-standalone: fully dereference all externals. migrate: rewrite against a from/to prefix pair (requires intent_params.from + intent_params.to).',
|
|
1148
|
+
},
|
|
1149
|
+
intent_params: {
|
|
1150
|
+
type: 'object',
|
|
1151
|
+
properties: {
|
|
1152
|
+
from: { type: 'string', description: 'Source path prefix (required for migrate intent).' },
|
|
1153
|
+
to: { type: 'string', description: 'Target path prefix (required for migrate intent).' },
|
|
1154
|
+
},
|
|
1155
|
+
description: 'Intent-specific parameters. Required keys depend on the intent (migrate needs from + to).',
|
|
1156
|
+
},
|
|
1157
|
+
mode: {
|
|
1158
|
+
type: 'string',
|
|
1159
|
+
enum: ['as_copy', 'in_place'],
|
|
1160
|
+
description: 'as_copy (default) writes a new file alongside the source; in_place overwrites it.',
|
|
1161
|
+
},
|
|
1162
|
+
confirm: {
|
|
1163
|
+
type: 'boolean',
|
|
1164
|
+
description: 'Required as true when mode=in_place. Prevents accidental in-place overwrites; explicit confirmation is the safety gate.',
|
|
1165
|
+
},
|
|
1166
|
+
out_path: { type: 'string', description: 'Optional: write the cured workbook to this absolute path. Defaults to <name>-healed.xlsx next to the source when mode=as_copy.' },
|
|
1167
|
+
},
|
|
1168
|
+
required: ['file_path', 'intent'],
|
|
1169
|
+
},
|
|
1170
|
+
},
|
|
1048
1171
|
];
|
|
1049
1172
|
|
|
1050
1173
|
// ---------------------------------------------------------------------------
|
|
@@ -1567,6 +1690,54 @@ async function dispatchTool(name, args) {
|
|
|
1567
1690
|
return callTool('xlsx_verify_receipt', body);
|
|
1568
1691
|
}
|
|
1569
1692
|
|
|
1693
|
+
// xlsx_healer_diagnose: produce structured diagnostic report of broken/
|
|
1694
|
+
// at-risk external refs. Read-only; returns the report in the response
|
|
1695
|
+
// _meta block. No output file.
|
|
1696
|
+
if (name === 'xlsx_healer_diagnose') {
|
|
1697
|
+
const body = { file_b64: fileToB64(args.file_path) };
|
|
1698
|
+
return callTool('xlsx_healer_diagnose', body);
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
// xlsx_healer_cure: apply ONE specific cure operation. Returns the
|
|
1702
|
+
// cured bytes in _meta.file_b64 + a per-operation receipt; out_path
|
|
1703
|
+
// (or in_place mode) triggers the standard applyFileB64 disk write.
|
|
1704
|
+
if (name === 'xlsx_healer_cure') {
|
|
1705
|
+
const body = {
|
|
1706
|
+
file_b64: fileToB64(args.file_path),
|
|
1707
|
+
operation: args.operation,
|
|
1708
|
+
};
|
|
1709
|
+
if (args.cure_params !== undefined) body.cure_params = args.cure_params;
|
|
1710
|
+
if (args.mode !== undefined) body.mode = args.mode;
|
|
1711
|
+
if (args.confirm !== undefined) body.confirm = args.confirm;
|
|
1712
|
+
const result = await callTool('xlsx_healer_cure', body);
|
|
1713
|
+
return applyFileB64(result, args.out_path);
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
// xlsx_healer_simulate: recipient-side accessibility check. Read-only;
|
|
1717
|
+
// returns the simulation report in _meta. No output file.
|
|
1718
|
+
if (name === 'xlsx_healer_simulate') {
|
|
1719
|
+
const body = {
|
|
1720
|
+
file_b64: fileToB64(args.file_path),
|
|
1721
|
+
accessible_paths: args.accessible_paths,
|
|
1722
|
+
};
|
|
1723
|
+
return callTool('xlsx_healer_simulate', body);
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
// xlsx_healer_intent: goal-driven healing (plan + apply). Returns the
|
|
1727
|
+
// planned operations + cured bytes + unactionable list. Same out_path /
|
|
1728
|
+
// in_place semantics as xlsx_healer_cure.
|
|
1729
|
+
if (name === 'xlsx_healer_intent') {
|
|
1730
|
+
const body = {
|
|
1731
|
+
file_b64: fileToB64(args.file_path),
|
|
1732
|
+
intent: args.intent,
|
|
1733
|
+
};
|
|
1734
|
+
if (args.intent_params !== undefined) body.intent_params = args.intent_params;
|
|
1735
|
+
if (args.mode !== undefined) body.mode = args.mode;
|
|
1736
|
+
if (args.confirm !== undefined) body.confirm = args.confirm;
|
|
1737
|
+
const result = await callTool('xlsx_healer_intent', body);
|
|
1738
|
+
return applyFileB64(result, args.out_path);
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1570
1741
|
// All other tools (list_sheets, schema, hyperlinks, conditional_formats,
|
|
1571
1742
|
// styles, etc.) — single-file relay. Forward any common option keys the
|
|
1572
1743
|
// routes accept so we don't silently drop them. New keys added here as
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xlsx-for-ai",
|
|
3
3
|
"mcpName": "io.github.senoff/xlsx-for-ai",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.0",
|
|
5
5
|
"description": "The MCP server that makes LLMs reliable on real-world Excel spreadsheets. Thin npm client over a hosted API — read, write, diff, redact, and supervise .xlsx files from any MCP-aware agent.",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"bin": {
|