@tiwater/office-mcp 0.10.0 → 0.11.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/office/README.md CHANGED
@@ -14,6 +14,7 @@ Shared stdio MCP server for Office document workflows.
14
14
  - `office_render_pdf`
15
15
  - `xlsx_inspect`
16
16
  - `xlsx_export_json`
17
+ - `xlsx_apply`
17
18
  - `xlsx_validate`
18
19
  - `pptx_inspect`
19
20
  - `pptx_export_json`
@@ -23,6 +24,15 @@ Shared stdio MCP server for Office document workflows.
23
24
  Install `@tiwater/office-mcp` together with the runtime versions required by
24
25
  the consumer, then run `tiwater-office-mcp` as a stdio MCP server.
25
26
 
27
+ Office MCP 0.11 requires these minimum published runtimes on `PATH`:
28
+
29
+ | Command | Package | Minimum version |
30
+ | --- | --- | --- |
31
+ | `tiwater-docx` | NuGet `tiwater.docx.cli` | 0.15.0 |
32
+ | `tiwater-xlsx` | NuGet `tiwater.xlsx.cli` | 0.2.55 |
33
+ | `tiwater-pptx` | NuGet `tiwater.pptx.cli` | 0.2.22 |
34
+ | `tiwater-convert` | NuGet `tiwater.convert.cli` | 0.9.22 |
35
+
26
36
  The server invokes published `tiwater-docx`, `tiwater-xlsx`,
27
37
  `tiwater-pptx`, and `tiwater-convert` commands from `PATH`. It does not require
28
38
  a source checkout or fall back to local projects.
@@ -32,6 +42,16 @@ tool arguments and structured results before they cross the protocol boundary.
32
42
  Large observations and exports are written to a caller-selected new JSON
33
43
  artifact. MCP returns only the artifact path, hash, and byte count.
34
44
 
45
+ ## Workbook editing
46
+
47
+ `xlsx_apply` executes one existing `tiwater.xlsx-edit/v1` artifact against a
48
+ current `.xlsx` workbook. The caller's deterministic builder owns all values,
49
+ coordinates, and operation selection. Office MCP binds the current input,
50
+ operations artifact, and created output by path and content hash and records the
51
+ complete runtime result in a new receipt artifact. It does not interpret
52
+ scenario knowledge or derive workbook edits. Callers independently inspect and
53
+ validate the resulting workbook before delivery.
54
+
35
55
  ## Template migration
36
56
 
37
57
  Template migration separates business choice from document mechanics:
package/office/index.mjs CHANGED
@@ -138,6 +138,39 @@ const artifact = z.object({
138
138
  bytes: z.number().int().nonnegative(),
139
139
  }).strict();
140
140
 
141
+ const xlsxAppliedOperation = z.object({
142
+ type: z.string().min(1),
143
+ applied: z.boolean(),
144
+ detail: z.string(),
145
+ sheet: z.string().nullable().optional(),
146
+ changedRange: z.string().nullable().optional(),
147
+ warnings: z.array(z.string()).nullable().optional(),
148
+ }).strict();
149
+ const xlsxEditResult = z.object({
150
+ input: z.string().min(1),
151
+ output: z.string().min(1),
152
+ appliedOperations: z.array(xlsxAppliedOperation),
153
+ }).strict();
154
+ const xlsxApplyReceipt = z.object({
155
+ schema: z.literal('tiwater.office.xlsx-apply-receipt/v1'),
156
+ pass: z.boolean(),
157
+ input: artifact,
158
+ operations: artifact,
159
+ output: artifact.nullable(),
160
+ appliedOperations: z.array(xlsxAppliedOperation),
161
+ }).strict();
162
+ const xlsxApplyOutput = z.object({
163
+ tool: z.literal('xlsx_apply'),
164
+ runtime: runtimeIdentity,
165
+ receipt: artifact,
166
+ output: artifact.nullable(),
167
+ summary: z.object({
168
+ pass: z.boolean(),
169
+ operationCount: z.number().int().nonnegative(),
170
+ appliedCount: z.number().int().nonnegative(),
171
+ }).strict(),
172
+ }).strict();
173
+
141
174
  const renderFileIdentity = z.object({
142
175
  sha256: z.string().regex(/^[a-f0-9]{64}$/),
143
176
  size_bytes: z.number().int().positive(),
@@ -358,6 +391,18 @@ const tools = [
358
391
  outputSchema: artifactOutput('xlsx_export_json'),
359
392
  handler: xlsxExportJson,
360
393
  },
394
+ {
395
+ name: 'xlsx_apply',
396
+ description: 'Apply one deterministic XLSX operations artifact to a current workbook. This tool executes published workbook edits; it does not derive values, coordinates, or business decisions.',
397
+ inputSchema: z.object({
398
+ input: pathInput.describe('Path to the current XLSX workbook.'),
399
+ operations: pathInput.describe('Path to the deterministic tiwater.xlsx-edit/v1 operations artifact.'),
400
+ output: pathInput.describe('New XLSX output path. Existing files are never overwritten.'),
401
+ receiptOutput: pathInput.describe('New JSON receipt path. Existing files are never overwritten.'),
402
+ }).strict(),
403
+ outputSchema: xlsxApplyOutput,
404
+ handler: xlsxApply,
405
+ },
361
406
  {
362
407
  name: 'xlsx_validate',
363
408
  description: 'Validate an XLSX workbook package and return Open XML validation evidence.',
@@ -961,6 +1006,56 @@ async function xlsxExportJson(args) {
961
1006
  };
962
1007
  }
963
1008
 
1009
+ async function xlsxApply(args) {
1010
+ const input = path.resolve(requireString(args.input, 'input'));
1011
+ const operations = path.resolve(requireString(args.operations, 'operations'));
1012
+ const output = path.resolve(requireString(args.output, 'output'));
1013
+ const receiptOutput = path.resolve(requireString(args.receiptOutput, 'receiptOutput'));
1014
+ if (path.extname(input).toLowerCase() !== '.xlsx' || path.extname(output).toLowerCase() !== '.xlsx') {
1015
+ throw Object.assign(new Error('XLSX apply input and output must use the .xlsx extension'), { code: -32602 });
1016
+ }
1017
+ await requireNewFile(output, 'output');
1018
+ await requireNewFile(receiptOutput, 'receiptOutput');
1019
+ const inputArtifact = await fileArtifact(input);
1020
+ const operationsArtifact = await fileArtifact(operations);
1021
+ await mkdir(path.dirname(output), { recursive: true });
1022
+ try {
1023
+ const result = await runJsonCandidateChain(
1024
+ xlsxCandidates,
1025
+ ['edit', input, operations, output],
1026
+ { allowedExitCodes: [0, 1] });
1027
+ const edit = xlsxEditResult.parse(result.json);
1028
+ if (path.resolve(edit.input) !== input || path.resolve(edit.output) !== output) {
1029
+ throw new Error('XLSX edit receipt is not bound to the current input and output');
1030
+ }
1031
+ const pass = edit.appliedOperations.every(operation => operation.applied);
1032
+ const outputArtifact = pass ? await fileArtifact(output) : null;
1033
+ if (!pass) await rm(output, { force: true });
1034
+ const receipt = xlsxApplyReceipt.parse({
1035
+ schema: 'tiwater.office.xlsx-apply-receipt/v1',
1036
+ pass,
1037
+ input: inputArtifact,
1038
+ operations: operationsArtifact,
1039
+ output: outputArtifact,
1040
+ appliedOperations: edit.appliedOperations,
1041
+ });
1042
+ return {
1043
+ tool: 'xlsx_apply',
1044
+ runtime: commandRuntime(result),
1045
+ receipt: await writeJsonArtifact(receiptOutput, receipt),
1046
+ output: outputArtifact,
1047
+ summary: {
1048
+ pass,
1049
+ operationCount: edit.appliedOperations.length,
1050
+ appliedCount: edit.appliedOperations.filter(operation => operation.applied).length,
1051
+ },
1052
+ };
1053
+ } catch (error) {
1054
+ await rm(output, { force: true });
1055
+ throw error;
1056
+ }
1057
+ }
1058
+
964
1059
  async function xlsxValidate(args) {
965
1060
  const input = requireString(args.input, 'input');
966
1061
  const result = await runXlsxValidateCandidateChain(['validate', input]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiwater/office-mcp",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Published MCP server for Tiwater Office document capabilities",
5
5
  "type": "module",
6
6
  "license": "MIT",