@tiwater/office-mcp 0.18.2 → 0.18.3

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.
@@ -10,7 +10,7 @@
10
10
  "x-tiwater-file-role": "read",
11
11
  "description": "Path to the current caller-authorized tiwater.docx-font-policy/v1 JSON asset."
12
12
  },
13
- "output": { "type": "string", "minLength": 1, "x-tiwater-file-role": "write" },
13
+ "output": { "type": "string", "minLength": 1, "description": "Output DOCX path; may equal input for an atomic in-place update.", "x-tiwater-file-role": "write" },
14
14
  "receiptOutput": {
15
15
  "type": "string",
16
16
  "minLength": 1,
@@ -6,7 +6,7 @@
6
6
  "input": { "type": "string", "minLength": 1, "x-tiwater-file-role": "read" },
7
7
  "italic": { "type": "boolean" },
8
8
  "indentCharactersPerLevel": { "type": "integer", "minimum": 0, "maximum": 21474836 },
9
- "output": { "type": "string", "minLength": 1, "x-tiwater-file-role": "write" },
9
+ "output": { "type": "string", "minLength": 1, "description": "Output DOCX path; may equal input for an atomic in-place update.", "x-tiwater-file-role": "write" },
10
10
  "receiptOutput": {
11
11
  "type": "string",
12
12
  "minLength": 1,
@@ -99,6 +99,7 @@
99
99
  "output": {
100
100
  "type": "string",
101
101
  "minLength": 1,
102
+ "description": "Output DOCX path; may equal input for an atomic in-place update.",
102
103
  "x-tiwater-file-role": "write"
103
104
  },
104
105
  "receiptOutput": {
@@ -93,6 +93,7 @@
93
93
  "output": {
94
94
  "type": "string",
95
95
  "minLength": 1,
96
+ "description": "Output DOCX path; may equal input for an atomic in-place update.",
96
97
  "x-tiwater-file-role": "write"
97
98
  },
98
99
  "receiptOutput": {
@@ -45,6 +45,7 @@
45
45
  "output": {
46
46
  "type": "string",
47
47
  "minLength": 1,
48
+ "description": "Output DOCX path; may equal input for an atomic in-place update.",
48
49
  "x-tiwater-file-role": "write"
49
50
  },
50
51
  "receiptOutput": {
@@ -45,6 +45,7 @@
45
45
  "output": {
46
46
  "type": "string",
47
47
  "minLength": 1,
48
+ "description": "Output DOCX path; may equal input for an atomic in-place update.",
48
49
  "x-tiwater-file-role": "write"
49
50
  },
50
51
  "receiptOutput": {
@@ -11,7 +11,7 @@
11
11
  "output": {
12
12
  "type": "string",
13
13
  "minLength": 1,
14
- "description": "New DOCX output path. Existing files are never overwritten.",
14
+ "description": "Output DOCX path; may equal input for an atomic in-place update.",
15
15
  "x-tiwater-file-role": "write"
16
16
  },
17
17
  "receiptOutput": {
@@ -10,6 +10,7 @@
10
10
  "output": {
11
11
  "type": "string",
12
12
  "minLength": 1,
13
+ "description": "Output DOCX path; may equal input for an atomic in-place update.",
13
14
  "x-tiwater-file-role": "write"
14
15
  },
15
16
  "styleMap": {
@@ -292,6 +292,7 @@
292
292
  "output": {
293
293
  "type": "string",
294
294
  "minLength": 1,
295
+ "description": "Output DOCX path; may equal input for an atomic in-place update.",
295
296
  "x-tiwater-file-role": "write"
296
297
  },
297
298
  "receiptOutput": {
@@ -45,6 +45,7 @@
45
45
  "output": {
46
46
  "type": "string",
47
47
  "minLength": 1,
48
+ "description": "Output DOCX path; may equal input for an atomic in-place update.",
48
49
  "x-tiwater-file-role": "write"
49
50
  },
50
51
  "receiptOutput": {
@@ -45,6 +45,7 @@
45
45
  "output": {
46
46
  "type": "string",
47
47
  "minLength": 1,
48
+ "description": "Output DOCX path; may equal input for an atomic in-place update.",
48
49
  "x-tiwater-file-role": "write"
49
50
  },
50
51
  "receiptOutput": {
@@ -10,6 +10,7 @@
10
10
  "output": {
11
11
  "type": "string",
12
12
  "minLength": 1,
13
+ "description": "Output DOCX path; may equal input for an atomic in-place update.",
13
14
  "x-tiwater-file-role": "write"
14
15
  }
15
16
  },
@@ -2,29 +2,29 @@
2
2
  "schema": "tiwater.office-provider-contract-manifest/v1",
3
3
  "provider": {
4
4
  "id": "@tiwater/office-mcp",
5
- "version": "0.18.2"
5
+ "version": "0.18.3"
6
6
  },
7
7
  "tools": [
8
8
  {
9
9
  "name": "docx_apply_font_policy",
10
10
  "providerContract": {
11
11
  "source": "packages/docx-cli/contracts/mcp-input/docx_apply_font_policy.schema.json",
12
- "sha256": "d7c0006cc1ee6a001f8a82f26a9ed260c4ff48babcc7a93b785c019cfa91a3a0"
12
+ "sha256": "0df423bca745d196923086d3ff9e5b20be59303460dbb54d5a367cfd701e03d3"
13
13
  },
14
14
  "inputContract": {
15
15
  "path": "office/contracts/docx_apply_font_policy.schema.json",
16
- "sha256": "d7c0006cc1ee6a001f8a82f26a9ed260c4ff48babcc7a93b785c019cfa91a3a0"
16
+ "sha256": "0df423bca745d196923086d3ff9e5b20be59303460dbb54d5a367cfd701e03d3"
17
17
  }
18
18
  },
19
19
  {
20
20
  "name": "docx_apply_toc_style_policy",
21
21
  "providerContract": {
22
22
  "source": "packages/docx-cli/contracts/mcp-input/docx_apply_toc_style_policy.schema.json",
23
- "sha256": "20e97023efe9a06354f22c93fff1576e0102a5d8b16058e9684af9478cb36286"
23
+ "sha256": "bd912b9c2442ad2528ae5411a4399bb02ea5a44d5d64ed4a87e212d6d7b57dc2"
24
24
  },
25
25
  "inputContract": {
26
26
  "path": "office/contracts/docx_apply_toc_style_policy.schema.json",
27
- "sha256": "20e97023efe9a06354f22c93fff1576e0102a5d8b16058e9684af9478cb36286"
27
+ "sha256": "bd912b9c2442ad2528ae5411a4399bb02ea5a44d5d64ed4a87e212d6d7b57dc2"
28
28
  }
29
29
  },
30
30
  {
@@ -42,33 +42,33 @@
42
42
  "name": "docx_copy_content",
43
43
  "providerContract": {
44
44
  "source": "packages/docx-cli/contracts/mcp-input/docx_copy_content.schema.json",
45
- "sha256": "50e1633e97d0aaab32c58935b92aa73128b566ae3ed742bc3ae1a62d0b1ba617"
45
+ "sha256": "a7b4998f8c9f6423c6d6ca5953d4b8ca5572f38dfdc23bcc06c20c7c488af727"
46
46
  },
47
47
  "inputContract": {
48
48
  "path": "office/contracts/docx_copy_content.schema.json",
49
- "sha256": "50e1633e97d0aaab32c58935b92aa73128b566ae3ed742bc3ae1a62d0b1ba617"
49
+ "sha256": "a7b4998f8c9f6423c6d6ca5953d4b8ca5572f38dfdc23bcc06c20c7c488af727"
50
50
  }
51
51
  },
52
52
  {
53
53
  "name": "docx_copy_object",
54
54
  "providerContract": {
55
55
  "source": "packages/docx-cli/contracts/mcp-input/docx_copy_object.schema.json",
56
- "sha256": "5c76b96e7a56f10a2e2c14e5d40dfe40e44dc6b21c2265bbf54c06953703ac12"
56
+ "sha256": "f5ebb6c59a2bcb82e0504ab517505fe7525e905ff71bd9dea817967e79cee7a3"
57
57
  },
58
58
  "inputContract": {
59
59
  "path": "office/contracts/docx_copy_object.schema.json",
60
- "sha256": "5c76b96e7a56f10a2e2c14e5d40dfe40e44dc6b21c2265bbf54c06953703ac12"
60
+ "sha256": "f5ebb6c59a2bcb82e0504ab517505fe7525e905ff71bd9dea817967e79cee7a3"
61
61
  }
62
62
  },
63
63
  {
64
64
  "name": "docx_delete_object",
65
65
  "providerContract": {
66
66
  "source": "packages/docx-cli/contracts/mcp-input/docx_delete_object.schema.json",
67
- "sha256": "efbb9017fe4b5dd0a66ee7d1229560d006fbca09beffbc6a600bda225b4f2d05"
67
+ "sha256": "4497eb24df0e5631601232a8b35a9eddf914d02146992dd22f1cfba93ef7780f"
68
68
  },
69
69
  "inputContract": {
70
70
  "path": "office/contracts/docx_delete_object.schema.json",
71
- "sha256": "efbb9017fe4b5dd0a66ee7d1229560d006fbca09beffbc6a600bda225b4f2d05"
71
+ "sha256": "4497eb24df0e5631601232a8b35a9eddf914d02146992dd22f1cfba93ef7780f"
72
72
  }
73
73
  },
74
74
  {
@@ -108,11 +108,11 @@
108
108
  "name": "docx_merge_cells",
109
109
  "providerContract": {
110
110
  "source": "packages/docx-cli/contracts/mcp-input/docx_merge_cells.schema.json",
111
- "sha256": "417def05619b271471c76883eea52a2f5772ac399111215df5a7259b8a41c3d1"
111
+ "sha256": "2106f2b2a39f47fb766e43bcb9a6e586827b3f31a079d875b6cc5c647d253dd4"
112
112
  },
113
113
  "inputContract": {
114
114
  "path": "office/contracts/docx_merge_cells.schema.json",
115
- "sha256": "417def05619b271471c76883eea52a2f5772ac399111215df5a7259b8a41c3d1"
115
+ "sha256": "2106f2b2a39f47fb766e43bcb9a6e586827b3f31a079d875b6cc5c647d253dd4"
116
116
  }
117
117
  },
118
118
  {
@@ -130,66 +130,66 @@
130
130
  "name": "docx_refresh_fields",
131
131
  "providerContract": {
132
132
  "source": "packages/convert-cli/schemas/mcp-input/docx_refresh_fields.schema.json",
133
- "sha256": "5f2a4f928796752adf7a44098919bca94c6faba8dbc9e5ad36b43e7f33035c7c"
133
+ "sha256": "8f2f8694b6d533d7f48b6e1e0c82be3a9c5aac55c50dba00daadd0ea0a76a8a7"
134
134
  },
135
135
  "inputContract": {
136
136
  "path": "office/contracts/docx_refresh_fields.schema.json",
137
- "sha256": "5f2a4f928796752adf7a44098919bca94c6faba8dbc9e5ad36b43e7f33035c7c"
137
+ "sha256": "8f2f8694b6d533d7f48b6e1e0c82be3a9c5aac55c50dba00daadd0ea0a76a8a7"
138
138
  }
139
139
  },
140
140
  {
141
141
  "name": "docx_replace_style_ids",
142
142
  "providerContract": {
143
143
  "source": "packages/docx-cli/contracts/mcp-input/docx_replace_style_ids.schema.json",
144
- "sha256": "aa8fc12a350a391fadf32546847f29a816319efcab01866a1f406dc8313ea1ed"
144
+ "sha256": "25f4d4e7c69fa5a2c4a82621aa58ea8eda21245636f84f2243ca4e9092616b68"
145
145
  },
146
146
  "inputContract": {
147
147
  "path": "office/contracts/docx_replace_style_ids.schema.json",
148
- "sha256": "aa8fc12a350a391fadf32546847f29a816319efcab01866a1f406dc8313ea1ed"
148
+ "sha256": "25f4d4e7c69fa5a2c4a82621aa58ea8eda21245636f84f2243ca4e9092616b68"
149
149
  }
150
150
  },
151
151
  {
152
152
  "name": "docx_replace_table_rows",
153
153
  "providerContract": {
154
154
  "source": "packages/docx-cli/contracts/mcp-input/docx_replace_table_rows.schema.json",
155
- "sha256": "8dfc2cb6f366610281eb6f14118d3855e1e27a34f27b5e2a425690b581ada5a6"
155
+ "sha256": "02ce941414496ddffa7d531ca6fa982598f607b516d990d598ace521d725cdf6"
156
156
  },
157
157
  "inputContract": {
158
158
  "path": "office/contracts/docx_replace_table_rows.schema.json",
159
- "sha256": "8dfc2cb6f366610281eb6f14118d3855e1e27a34f27b5e2a425690b581ada5a6"
159
+ "sha256": "02ce941414496ddffa7d531ca6fa982598f607b516d990d598ace521d725cdf6"
160
160
  }
161
161
  },
162
162
  {
163
163
  "name": "docx_set_text",
164
164
  "providerContract": {
165
165
  "source": "packages/docx-cli/contracts/mcp-input/docx_set_text.schema.json",
166
- "sha256": "0e0ccfb2ac910660c6aea6b4dca0e023bd30931c691913647576999b096f43e9"
166
+ "sha256": "9feca848acff5ff7c0f2e08f618e183494959b5a74e9e37e06086d7a627810ca"
167
167
  },
168
168
  "inputContract": {
169
169
  "path": "office/contracts/docx_set_text.schema.json",
170
- "sha256": "0e0ccfb2ac910660c6aea6b4dca0e023bd30931c691913647576999b096f43e9"
170
+ "sha256": "9feca848acff5ff7c0f2e08f618e183494959b5a74e9e37e06086d7a627810ca"
171
171
  }
172
172
  },
173
173
  {
174
174
  "name": "docx_split_cells",
175
175
  "providerContract": {
176
176
  "source": "packages/docx-cli/contracts/mcp-input/docx_split_cells.schema.json",
177
- "sha256": "9d53d0234d98c6cc5cb5b523e1457b777d015c5aaf49cbd745e6598c1793d6bb"
177
+ "sha256": "085a5b8e924c68958752eeb2de9644a3084c249408e9f135ee936f14e9ce5f9b"
178
178
  },
179
179
  "inputContract": {
180
180
  "path": "office/contracts/docx_split_cells.schema.json",
181
- "sha256": "9d53d0234d98c6cc5cb5b523e1457b777d015c5aaf49cbd745e6598c1793d6bb"
181
+ "sha256": "085a5b8e924c68958752eeb2de9644a3084c249408e9f135ee936f14e9ce5f9b"
182
182
  }
183
183
  },
184
184
  {
185
185
  "name": "docx_strip_direct_formatting",
186
186
  "providerContract": {
187
187
  "source": "packages/docx-cli/contracts/mcp-input/docx_strip_direct_formatting.schema.json",
188
- "sha256": "49a6682f7c8aecce6e30e5a4bfc08111151d9c755d38e759de6b7efb66979cac"
188
+ "sha256": "4931d4432c574154eb7a344ee4dc485d99d8840c07d2e46f647e14490cbeedae"
189
189
  },
190
190
  "inputContract": {
191
191
  "path": "office/contracts/docx_strip_direct_formatting.schema.json",
192
- "sha256": "49a6682f7c8aecce6e30e5a4bfc08111151d9c755d38e759de6b7efb66979cac"
192
+ "sha256": "4931d4432c574154eb7a344ee4dc485d99d8840c07d2e46f647e14490cbeedae"
193
193
  }
194
194
  },
195
195
  {
package/office/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { createHash } from 'node:crypto';
2
+ import { createHash, randomUUID } from 'node:crypto';
3
3
  import { createReadStream } from 'node:fs';
4
- import { mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
4
+ import { mkdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
5
5
  import path from 'node:path';
6
6
  import { isDeepStrictEqual } from 'node:util';
7
7
  import { McpServer } from '@modelcontextprotocol/server';
@@ -610,34 +610,39 @@ async function docxRefreshFields(args) {
610
610
  const input = path.resolve(requireString(args.input, 'input'));
611
611
  const output = path.resolve(requireString(args.output, 'output'));
612
612
  const receiptOutput = path.resolve(requireString(args.receiptOutput, 'receiptOutput'));
613
+ const inPlace = input === output;
614
+ const invocationOutput = inPlace ? temporarySibling(output) : output;
613
615
  if (path.extname(input).toLowerCase() !== '.docx' || path.extname(output).toLowerCase() !== '.docx') {
614
616
  throw Object.assign(new Error('DOCX field refresh requires .docx input and output'), { code: -32602 });
615
617
  }
616
- await requireNewFile(output, 'output');
618
+ if (!inPlace) await requireNewFile(output, 'output');
617
619
  await requireNewFile(receiptOutput, 'receiptOutput');
618
620
  const inputArtifact = await fileArtifact(input);
619
621
  try {
620
- const result = await runJsonCandidateChain(convertCandidates, ['refresh-docx-fields', input, output]);
622
+ const result = await runJsonCandidateChain(convertCandidates, ['refresh-docx-fields', input, invocationOutput]);
621
623
  const providerReceipt = docxFieldRefreshReceipt.parse(result.json);
622
- const outputArtifact = await fileArtifact(output);
624
+ const invocationArtifact = await fileArtifact(invocationOutput);
623
625
  if (path.resolve(providerReceipt.input) !== input
624
- || path.resolve(providerReceipt.output) !== output
626
+ || path.resolve(providerReceipt.output) !== invocationOutput
625
627
  || providerReceipt.input_sha256 !== inputArtifact.sha256
626
- || providerReceipt.output_sha256 !== outputArtifact.sha256) {
628
+ || providerReceipt.output_sha256 !== invocationArtifact.sha256) {
627
629
  throw new Error('DOCX field refresh receipt is not bound to the current input and output');
628
630
  }
631
+ if (inPlace) await rename(invocationOutput, output);
632
+ const outputArtifact = await fileArtifact(output);
633
+ const acceptedReceipt = { ...providerReceipt, output, output_sha256: outputArtifact.sha256 };
629
634
  return {
630
635
  tool: 'docx_refresh_fields',
631
636
  runtime: commandRuntime(result),
632
637
  output: outputArtifact,
633
- receipt: await writeJsonArtifact(receiptOutput, providerReceipt),
638
+ receipt: await writeJsonArtifact(receiptOutput, acceptedReceipt),
634
639
  summary: {
635
640
  backend: providerReceipt.backend,
636
641
  refreshScope: providerReceipt.refresh_scope,
637
642
  },
638
643
  };
639
644
  } catch (error) {
640
- await rm(output, { force: true });
645
+ await rm(invocationOutput, { force: true });
641
646
  throw error;
642
647
  }
643
648
  }
@@ -645,15 +650,24 @@ async function docxRefreshFields(args) {
645
650
  async function copyTransform(tool, candidates, command, args, suffix = []) {
646
651
  const input = path.resolve(requireString(args.input, 'input'));
647
652
  const output = path.resolve(requireString(args.output, 'output'));
648
- await requireNewFile(output, 'output');
649
- const result = await runCandidateChain(candidates, [...command, input, output, ...suffix]);
650
- return { tool, runtime: commandRuntime(result), output: await fileArtifact(output) };
653
+ const inPlace = input === output;
654
+ const invocationOutput = inPlace ? temporarySibling(output) : output;
655
+ if (!inPlace) await requireNewFile(output, 'output');
656
+ try {
657
+ const result = await runCandidateChain(candidates, [...command, input, invocationOutput, ...suffix]);
658
+ if (inPlace) await rename(invocationOutput, output);
659
+ return { tool, runtime: commandRuntime(result), output: await fileArtifact(output) };
660
+ } catch (error) {
661
+ await rm(invocationOutput, { force: true });
662
+ throw error;
663
+ }
651
664
  }
652
665
 
653
666
  async function fixedEdit(tool, args) {
667
+ const input = path.resolve(requireString(args.input, 'input'));
654
668
  const output = path.resolve(requireString(args.output, 'output'));
655
669
  const receiptOutput = path.resolve(requireString(args.receiptOutput, 'receiptOutput'));
656
- await requireNewFile(output, 'output');
670
+ if (!(tool.startsWith('docx_') && input === output)) await requireNewFile(output, 'output');
657
671
  await requireNewFile(receiptOutput, 'receiptOutput');
658
672
  const candidates = tool.startsWith('docx_') ? docxCandidates
659
673
  : tool.startsWith('xlsx_') ? xlsxCandidates
@@ -880,6 +894,12 @@ async function requireNewFile(filePath, label) {
880
894
  throw Object.assign(new Error(`${label} already exists: ${filePath}`), { code: -32602 });
881
895
  }
882
896
 
897
+ function temporarySibling(filePath) {
898
+ const extension = path.extname(filePath);
899
+ const stem = path.basename(filePath, extension);
900
+ return path.join(path.dirname(filePath), `.${stem}.${randomUUID()}.tmp${extension}`);
901
+ }
902
+
883
903
  function commandRuntime(result) {
884
904
  return {
885
905
  command: result.command,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiwater/office-mcp",
3
- "version": "0.18.2",
3
+ "version": "0.18.3",
4
4
  "description": "Published MCP server for Tiwater Office document capabilities",
5
5
  "type": "module",
6
6
  "license": "MIT",