@tiwater/office-mcp 0.21.0 → 0.21.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.
|
@@ -16,7 +16,7 @@ export function resultChannels(args) {
|
|
|
16
16
|
return { returnContent, output };
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export async function deliverLargeJsonResult({ tool, args, runtime, payload, sourcePaths }) {
|
|
19
|
+
export async function deliverLargeJsonResult({ tool, args, runtime, payload, sourcePaths, summary }) {
|
|
20
20
|
const channels = resultChannels(args);
|
|
21
21
|
const contentBytes = Buffer.byteLength(JSON.stringify(payload), 'utf8');
|
|
22
22
|
const contentReturned = channels.returnContent && contentBytes <= returnedContentBudgetBytes;
|
|
@@ -37,6 +37,7 @@ export async function deliverLargeJsonResult({ tool, args, runtime, payload, sou
|
|
|
37
37
|
contentReturned,
|
|
38
38
|
contentWritten: channels.output !== null,
|
|
39
39
|
},
|
|
40
|
+
...(summary === undefined ? {} : { summary }),
|
|
40
41
|
...(contentReturned ? { content: payload } : {}),
|
|
41
42
|
};
|
|
42
43
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
const pendingWrites = new Map();
|
|
4
|
+
|
|
5
|
+
export async function withOutputWriteLock(output, write) {
|
|
6
|
+
const target = path.resolve(output);
|
|
7
|
+
const previous = pendingWrites.get(target) ?? Promise.resolve();
|
|
8
|
+
const current = previous.catch(() => undefined).then(write);
|
|
9
|
+
pendingWrites.set(target, current);
|
|
10
|
+
try {
|
|
11
|
+
return await current;
|
|
12
|
+
} finally {
|
|
13
|
+
if (pendingWrites.get(target) === current) pendingWrites.delete(target);
|
|
14
|
+
}
|
|
15
|
+
}
|
package/office/index.mjs
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
returnedContentBudgetBytes,
|
|
22
22
|
writeJsonArtifact,
|
|
23
23
|
} from '../_shared/large-json-result.mjs';
|
|
24
|
+
import { withOutputWriteLock } from '../_shared/output-write-lock.mjs';
|
|
24
25
|
|
|
25
26
|
const packageMetadata = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
|
|
26
27
|
const inputContractManifest = JSON.parse(await readFile(
|
|
@@ -179,6 +180,39 @@ function largeResultOutput(tool) {
|
|
|
179
180
|
}).strict();
|
|
180
181
|
}
|
|
181
182
|
|
|
183
|
+
function largeInspectionOutput(tool, summary) {
|
|
184
|
+
return largeResultOutput(tool).extend({ summary }).strict();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const xlsxInspectionSummary = z.object({
|
|
188
|
+
sheetCount: z.number().int().nonnegative(),
|
|
189
|
+
sheets: z.array(z.object({
|
|
190
|
+
name: z.string(),
|
|
191
|
+
rowCount: z.number().int().nonnegative(),
|
|
192
|
+
columnCount: z.number().int().nonnegative(),
|
|
193
|
+
usedRange: z.string().nullable(),
|
|
194
|
+
mergedRangeCount: z.number().int().nonnegative(),
|
|
195
|
+
formulaCellCount: z.number().int().nonnegative(),
|
|
196
|
+
openingText: z.array(z.object({
|
|
197
|
+
reference: z.string(),
|
|
198
|
+
textPreview: z.string(),
|
|
199
|
+
}).strict()).max(6),
|
|
200
|
+
}).strict()).max(6),
|
|
201
|
+
}).strict();
|
|
202
|
+
|
|
203
|
+
const pptxInspectionSummary = z.object({
|
|
204
|
+
slideCount: z.number().int().nonnegative(),
|
|
205
|
+
masterCount: z.number().int().nonnegative(),
|
|
206
|
+
slideSize: z.object({
|
|
207
|
+
cx: z.number().int().nonnegative(),
|
|
208
|
+
cy: z.number().int().nonnegative(),
|
|
209
|
+
}).strict().nullable(),
|
|
210
|
+
openingSlides: z.array(z.object({
|
|
211
|
+
slideNumber: z.number().int().positive(),
|
|
212
|
+
textPreview: z.string(),
|
|
213
|
+
}).strict()).max(6),
|
|
214
|
+
}).strict();
|
|
215
|
+
|
|
182
216
|
function docxObservationOutput(tool) {
|
|
183
217
|
return z.object({
|
|
184
218
|
tool: z.literal(tool),
|
|
@@ -519,7 +553,7 @@ const tools = [
|
|
|
519
553
|
name: 'xlsx_inspect',
|
|
520
554
|
description: 'Inspect a current XLSX workbook or legacy XLS workbook, including workbook structure, exported values, formulas, styles, merged ranges, and published legacy-format conversion evidence. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
521
555
|
inputSchema: inputContract('xlsx_inspect'),
|
|
522
|
-
outputSchema:
|
|
556
|
+
outputSchema: largeInspectionOutput('xlsx_inspect', xlsxInspectionSummary),
|
|
523
557
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
524
558
|
handler: xlsxInspect,
|
|
525
559
|
},
|
|
@@ -544,7 +578,7 @@ const tools = [
|
|
|
544
578
|
name: 'pptx_inspect',
|
|
545
579
|
description: 'Inspect a PPTX file, including slides, masters, layouts, shapes, transforms, paragraphs, runs, and placeholders. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
546
580
|
inputSchema: inputContract('pptx_inspect'),
|
|
547
|
-
outputSchema:
|
|
581
|
+
outputSchema: largeInspectionOutput('pptx_inspect', pptxInspectionSummary),
|
|
548
582
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
549
583
|
handler: pptxInspect,
|
|
550
584
|
},
|
|
@@ -611,7 +645,9 @@ function buildServer() {
|
|
|
611
645
|
...(tool.annotations ? { annotations: tool.annotations } : {}),
|
|
612
646
|
},
|
|
613
647
|
async args => {
|
|
614
|
-
const payload =
|
|
648
|
+
const payload = typeof args.output === 'string'
|
|
649
|
+
? await withOutputWriteLock(args.output, () => tool.handler(args))
|
|
650
|
+
: await tool.handler(args);
|
|
615
651
|
return createToolResult(payload, { isError: payload?.summary?.pass === false });
|
|
616
652
|
},
|
|
617
653
|
);
|
|
@@ -1146,7 +1182,14 @@ function nativeRenderBackend(sourceFormat) {
|
|
|
1146
1182
|
async function xlsxInspect(args) {
|
|
1147
1183
|
const input = path.resolve(requireString(args.input, 'input'));
|
|
1148
1184
|
const result = await runJsonCandidateChain(xlsxCandidates, ['inspect', input, '--json']);
|
|
1149
|
-
return deliverLargeJsonResult({
|
|
1185
|
+
return deliverLargeJsonResult({
|
|
1186
|
+
tool: 'xlsx_inspect',
|
|
1187
|
+
args,
|
|
1188
|
+
runtime: commandRuntime(result),
|
|
1189
|
+
payload: result.json,
|
|
1190
|
+
sourcePaths: [input],
|
|
1191
|
+
summary: compactXlsxInspection(result.json),
|
|
1192
|
+
});
|
|
1150
1193
|
}
|
|
1151
1194
|
|
|
1152
1195
|
async function xlsxExportJson(args) {
|
|
@@ -1168,7 +1211,14 @@ async function xlsxValidate(args) {
|
|
|
1168
1211
|
async function pptxInspect(args) {
|
|
1169
1212
|
const input = path.resolve(requireString(args.input, 'input'));
|
|
1170
1213
|
const result = await runJsonCandidateChain(pptxCandidates, ['inspect', input, '--json']);
|
|
1171
|
-
return deliverLargeJsonResult({
|
|
1214
|
+
return deliverLargeJsonResult({
|
|
1215
|
+
tool: 'pptx_inspect',
|
|
1216
|
+
args,
|
|
1217
|
+
runtime: commandRuntime(result),
|
|
1218
|
+
payload: result.json,
|
|
1219
|
+
sourcePaths: [input],
|
|
1220
|
+
summary: compactPptxInspection(result.json),
|
|
1221
|
+
});
|
|
1172
1222
|
}
|
|
1173
1223
|
|
|
1174
1224
|
async function pptxExportJson(args) {
|
|
@@ -1183,6 +1233,58 @@ async function pptxValidate(args) {
|
|
|
1183
1233
|
return deliverLargeJsonResult({ tool: 'pptx_validate', args, runtime: commandRuntime(result), payload: result.json, sourcePaths: [input] });
|
|
1184
1234
|
}
|
|
1185
1235
|
|
|
1236
|
+
function compactPreview(value, maximum) {
|
|
1237
|
+
return typeof value === 'string'
|
|
1238
|
+
? value.trim().replace(/\s+/gu, ' ').slice(0, maximum)
|
|
1239
|
+
: '';
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
function compactXlsxInspection(payload) {
|
|
1243
|
+
const workbook = payload?.workbook ?? {};
|
|
1244
|
+
const allSheets = Array.isArray(workbook.sheets) ? workbook.sheets : [];
|
|
1245
|
+
return {
|
|
1246
|
+
sheetCount: Number.isInteger(workbook.sheetCount) ? workbook.sheetCount : allSheets.length,
|
|
1247
|
+
sheets: allSheets.slice(0, 6).map(sheet => ({
|
|
1248
|
+
name: compactPreview(sheet?.name, 120),
|
|
1249
|
+
rowCount: Number.isInteger(sheet?.rowCount) ? sheet.rowCount : 0,
|
|
1250
|
+
columnCount: Number.isInteger(sheet?.columnCount) ? sheet.columnCount : 0,
|
|
1251
|
+
usedRange: typeof sheet?.usedRange === 'string' ? compactPreview(sheet.usedRange, 120) : null,
|
|
1252
|
+
mergedRangeCount: Array.isArray(sheet?.mergedRanges) ? sheet.mergedRanges.length : 0,
|
|
1253
|
+
formulaCellCount: Number.isInteger(sheet?.formulaCellCount) ? sheet.formulaCellCount : 0,
|
|
1254
|
+
openingText: (Array.isArray(sheet?.textCells) ? sheet.textCells : [])
|
|
1255
|
+
.filter(cell => typeof cell?.text === 'string' && cell.text.trim() !== '')
|
|
1256
|
+
.slice(0, 6)
|
|
1257
|
+
.map(cell => ({
|
|
1258
|
+
reference: compactPreview(cell.reference, 40),
|
|
1259
|
+
textPreview: compactPreview(cell.text, 160),
|
|
1260
|
+
})),
|
|
1261
|
+
})),
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
function compactPptxInspection(payload) {
|
|
1266
|
+
const slides = Array.isArray(payload?.slides) ? payload.slides : [];
|
|
1267
|
+
return {
|
|
1268
|
+
slideCount: Number.isInteger(payload?.slideCount) ? payload.slideCount : slides.length,
|
|
1269
|
+
masterCount: Array.isArray(payload?.masters) ? payload.masters.length : 0,
|
|
1270
|
+
slideSize: Number.isInteger(payload?.slideSize?.cx) && Number.isInteger(payload?.slideSize?.cy)
|
|
1271
|
+
? { cx: payload.slideSize.cx, cy: payload.slideSize.cy }
|
|
1272
|
+
: null,
|
|
1273
|
+
openingSlides: slides.slice(0, 6).map((slide, index) => ({
|
|
1274
|
+
slideNumber: Number.isInteger(slide?.slideNumber) && slide.slideNumber > 0
|
|
1275
|
+
? slide.slideNumber
|
|
1276
|
+
: index + 1,
|
|
1277
|
+
textPreview: compactPreview(
|
|
1278
|
+
(Array.isArray(slide?.shapes) ? slide.shapes : [])
|
|
1279
|
+
.map(shape => shape?.text)
|
|
1280
|
+
.filter(text => typeof text === 'string' && text.trim() !== '')
|
|
1281
|
+
.join(' '),
|
|
1282
|
+
240,
|
|
1283
|
+
),
|
|
1284
|
+
})),
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1186
1288
|
async function requireReturnedArtifact(returned, expectedPath, label) {
|
|
1187
1289
|
if (!returned || path.resolve(returned.path || '') !== expectedPath) {
|
|
1188
1290
|
throw new Error(`${label} artifact is not bound to the accepted provider call`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiwater/office-mcp",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.2",
|
|
4
4
|
"description": "Published MCP server for Tiwater Office document capabilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"files": [
|
|
19
19
|
"_shared/tool-runtime.mjs",
|
|
20
20
|
"_shared/large-json-result.mjs",
|
|
21
|
+
"_shared/output-write-lock.mjs",
|
|
21
22
|
"office/index.mjs",
|
|
22
23
|
"office/README.md",
|
|
23
24
|
"office/contracts/tiwater-office-provider-contract-manifest-v1.json",
|