@tiwater/office-mcp 0.16.20 → 0.17.1

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
@@ -17,8 +17,9 @@ OpenXML-internal identifiers, not filesystem arguments.
17
17
 
18
18
  ## Capability families
19
19
 
20
- - DOCX: inspect document/tables, export, compare, validate OpenXML, font, and table-of-contents styles
21
- policy, transform styles, and batch one fixed edit action.
20
+ - DOCX: inspect document/tables, export, compare, validate OpenXML, apply or validate font and
21
+ table-of-contents style policies, refresh document fields, replace object text, transform styles,
22
+ and batch one fixed edit action.
22
23
  - XLS/XLSX: convert legacy XLS with ET, inspect/export, validate, and batch
23
24
  one fixed workbook edit action.
24
25
  - PPTX: inspect/export, bind selected masters/layouts, apply text formatting,
@@ -0,0 +1,31 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "tiwater.office-mcp-input/docx_apply_font_policy",
4
+ "type": "object",
5
+ "properties": {
6
+ "targetDocument": {
7
+ "type": "object",
8
+ "properties": {
9
+ "input": { "type": "string", "minLength": 1, "x-tiwater-file-role": "read" },
10
+ "revision": { "type": "string", "pattern": "^docx-rev-v1-[0-9a-f]{64}$" }
11
+ },
12
+ "required": ["input", "revision"],
13
+ "additionalProperties": false
14
+ },
15
+ "policy": {
16
+ "type": "string",
17
+ "minLength": 1,
18
+ "x-tiwater-file-role": "read",
19
+ "description": "Path to the current caller-authorized tiwater.docx-font-policy/v1 JSON asset."
20
+ },
21
+ "output": { "type": "string", "minLength": 1, "x-tiwater-file-role": "write" },
22
+ "receiptOutput": {
23
+ "type": "string",
24
+ "minLength": 1,
25
+ "x-tiwater-file-role": "write",
26
+ "x-tiwater-file-effect": false
27
+ }
28
+ },
29
+ "required": ["targetDocument", "policy", "output", "receiptOutput"],
30
+ "additionalProperties": false
31
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "tiwater.office-mcp-input/docx_apply_toc_style_policy",
4
+ "type": "object",
5
+ "properties": {
6
+ "targetDocument": {
7
+ "type": "object",
8
+ "properties": {
9
+ "input": { "type": "string", "minLength": 1, "x-tiwater-file-role": "read" },
10
+ "revision": { "type": "string", "pattern": "^docx-rev-v1-[0-9a-f]{64}$" }
11
+ },
12
+ "required": ["input", "revision"],
13
+ "additionalProperties": false
14
+ },
15
+ "italic": { "type": "boolean" },
16
+ "indentCharactersPerLevel": { "type": "integer", "minimum": 0, "maximum": 21474836 },
17
+ "output": { "type": "string", "minLength": 1, "x-tiwater-file-role": "write" },
18
+ "receiptOutput": {
19
+ "type": "string",
20
+ "minLength": 1,
21
+ "x-tiwater-file-role": "write",
22
+ "x-tiwater-file-effect": false
23
+ }
24
+ },
25
+ "required": ["targetDocument", "italic", "indentCharactersPerLevel", "output", "receiptOutput"],
26
+ "additionalProperties": false
27
+ }
@@ -21,7 +21,7 @@
21
21
  "targetRef": {
22
22
  "type": "string",
23
23
  "pattern": "^dox1_[0-9a-f]{64}$",
24
- "description": "Target cell ref whose content is replaced while its cell formatting is retained."
24
+ "description": "Target plain-text paragraph or cell ref whose content is replaced while its container formatting is retained. Paragraph bookmarks are preserved; targets containing fields, drawings, nested tables, controls, links, or other non-text content are rejected."
25
25
  },
26
26
  "sourceDocument": {
27
27
  "type": "object",
@@ -35,7 +35,7 @@
35
35
  "sourceSelections": {
36
36
  "type": "array",
37
37
  "minItems": 1,
38
- "description": "Ordered source content copied into the target cell. Select a whole observed object by ref alone, or select an exact substring by adding range to an observed run or text ref. Multiple selections are copied in this array order.",
38
+ "description": "Ordered source content copied into the target paragraph or cell. Select a whole observed object by ref alone, or select an exact substring by adding range to an observed run or text ref. Multiple selections are copied in this array order.",
39
39
  "items": {
40
40
  "type": "object",
41
41
  "properties": {
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "input": {
6
+ "type": "string",
7
+ "minLength": 1,
8
+ "description": "Path to the current DOCX document.",
9
+ "x-tiwater-file-role": "read"
10
+ },
11
+ "output": {
12
+ "type": "string",
13
+ "minLength": 1,
14
+ "description": "New DOCX output path. Existing files are never overwritten.",
15
+ "x-tiwater-file-role": "write"
16
+ },
17
+ "receiptOutput": {
18
+ "type": "string",
19
+ "minLength": 1,
20
+ "description": "New JSON receipt path. Existing files are never overwritten.",
21
+ "x-tiwater-file-role": "write",
22
+ "x-tiwater-file-effect": false
23
+ }
24
+ },
25
+ "required": ["input", "output", "receiptOutput"],
26
+ "additionalProperties": false
27
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "tiwater.office-mcp-input/docx_set_text",
4
+ "type": "object",
5
+ "properties": {
6
+ "targetDocument": {
7
+ "type": "object",
8
+ "properties": {
9
+ "input": { "type": "string", "minLength": 1, "x-tiwater-file-role": "read" },
10
+ "revision": { "type": "string", "pattern": "^docx-rev-v1-[0-9a-f]{64}$" }
11
+ },
12
+ "required": ["input", "revision"],
13
+ "additionalProperties": false
14
+ },
15
+ "changes": {
16
+ "type": "array",
17
+ "minItems": 1,
18
+ "items": {
19
+ "type": "object",
20
+ "properties": {
21
+ "targetRef": {
22
+ "type": "string",
23
+ "pattern": "^dox1_[0-9a-f]{64}$",
24
+ "description": "Observed plain-text paragraph or cell ref whose whole text content is replaced. Paragraph bookmarks are preserved; targets containing fields, drawings, nested tables, controls, links, or other non-text content are rejected."
25
+ },
26
+ "text": { "type": "string" }
27
+ },
28
+ "required": ["targetRef", "text"],
29
+ "additionalProperties": false
30
+ }
31
+ },
32
+ "output": { "type": "string", "minLength": 1, "x-tiwater-file-role": "write" },
33
+ "receiptOutput": {
34
+ "type": "string",
35
+ "minLength": 1,
36
+ "x-tiwater-file-role": "write",
37
+ "x-tiwater-file-effect": false
38
+ }
39
+ },
40
+ "required": ["targetDocument", "changes", "output", "receiptOutput"],
41
+ "additionalProperties": false
42
+ }
@@ -2,9 +2,31 @@
2
2
  "schema": "tiwater.office-provider-contract-manifest/v1",
3
3
  "provider": {
4
4
  "id": "@tiwater/office-mcp",
5
- "version": "0.16.20"
5
+ "version": "0.17.1"
6
6
  },
7
7
  "tools": [
8
+ {
9
+ "name": "docx_apply_font_policy",
10
+ "providerContract": {
11
+ "source": "packages/docx-cli/contracts/mcp-input/docx_apply_font_policy.schema.json",
12
+ "sha256": "153e50e2f6577b5ea70fd1d3522f8ccf3a74b82b683f1f8fe724f6433a9c72e0"
13
+ },
14
+ "inputContract": {
15
+ "path": "office/contracts/docx_apply_font_policy.schema.json",
16
+ "sha256": "153e50e2f6577b5ea70fd1d3522f8ccf3a74b82b683f1f8fe724f6433a9c72e0"
17
+ }
18
+ },
19
+ {
20
+ "name": "docx_apply_toc_style_policy",
21
+ "providerContract": {
22
+ "source": "packages/docx-cli/contracts/mcp-input/docx_apply_toc_style_policy.schema.json",
23
+ "sha256": "2d4887ab4dc57e2c655f130e67c08886ff1630a889e7258b13519fb6e1529d25"
24
+ },
25
+ "inputContract": {
26
+ "path": "office/contracts/docx_apply_toc_style_policy.schema.json",
27
+ "sha256": "2d4887ab4dc57e2c655f130e67c08886ff1630a889e7258b13519fb6e1529d25"
28
+ }
29
+ },
8
30
  {
9
31
  "name": "docx_compare",
10
32
  "providerContract": {
@@ -20,11 +42,11 @@
20
42
  "name": "docx_copy_content",
21
43
  "providerContract": {
22
44
  "source": "packages/docx-cli/contracts/mcp-input/docx_copy_content.schema.json",
23
- "sha256": "27110c4590087bb38bd08f77ab2dc19a62407a836f58a603d222ebdb184c97b6"
45
+ "sha256": "a988f7c4284c5f99bd3cc28f31813c0a12b999c2486f697776bf6f873c9c49e7"
24
46
  },
25
47
  "inputContract": {
26
48
  "path": "office/contracts/docx_copy_content.schema.json",
27
- "sha256": "27110c4590087bb38bd08f77ab2dc19a62407a836f58a603d222ebdb184c97b6"
49
+ "sha256": "a988f7c4284c5f99bd3cc28f31813c0a12b999c2486f697776bf6f873c9c49e7"
28
50
  }
29
51
  },
30
52
  {
@@ -126,6 +148,17 @@
126
148
  "sha256": "29ba026050f1120219491f918e74c344551224795b1d15e49ec886d4e81e092a"
127
149
  }
128
150
  },
151
+ {
152
+ "name": "docx_refresh_fields",
153
+ "providerContract": {
154
+ "source": "packages/convert-cli/schemas/mcp-input/docx_refresh_fields.schema.json",
155
+ "sha256": "5f2a4f928796752adf7a44098919bca94c6faba8dbc9e5ad36b43e7f33035c7c"
156
+ },
157
+ "inputContract": {
158
+ "path": "office/contracts/docx_refresh_fields.schema.json",
159
+ "sha256": "5f2a4f928796752adf7a44098919bca94c6faba8dbc9e5ad36b43e7f33035c7c"
160
+ }
161
+ },
129
162
  {
130
163
  "name": "docx_replace_style_ids",
131
164
  "providerContract": {
@@ -137,6 +170,17 @@
137
170
  "sha256": "aa8fc12a350a391fadf32546847f29a816319efcab01866a1f406dc8313ea1ed"
138
171
  }
139
172
  },
173
+ {
174
+ "name": "docx_set_text",
175
+ "providerContract": {
176
+ "source": "packages/docx-cli/contracts/mcp-input/docx_set_text.schema.json",
177
+ "sha256": "945a5af0d9390c21f380cc9d255429353d6f743264b19a9e85111e75b3c0344b"
178
+ },
179
+ "inputContract": {
180
+ "path": "office/contracts/docx_set_text.schema.json",
181
+ "sha256": "945a5af0d9390c21f380cc9d255429353d6f743264b19a9e85111e75b3c0344b"
182
+ }
183
+ },
140
184
  {
141
185
  "name": "docx_split_cells",
142
186
  "providerContract": {
package/office/index.mjs CHANGED
@@ -100,6 +100,31 @@ const nativeRenderOutput = z.object({
100
100
  }).strict(),
101
101
  }).strict();
102
102
 
103
+ const docxFieldRefreshReceipt = z.object({
104
+ schema: z.literal('tiwater.convert-refresh-docx-fields/v1'),
105
+ status: z.literal('ok'),
106
+ input: z.string(),
107
+ input_sha256: z.string().regex(/^[a-f0-9]{64}$/),
108
+ output: z.string(),
109
+ output_sha256: z.string().regex(/^[a-f0-9]{64}$/),
110
+ source_format: z.literal('docx'),
111
+ target_format: z.literal('docx'),
112
+ version: z.string(),
113
+ backend: z.literal('wps'),
114
+ refresh_scope: z.array(z.enum(['table-of-contents', 'table-of-figures'])).min(1),
115
+ }).strict();
116
+
117
+ const docxFieldRefreshOutput = z.object({
118
+ tool: z.literal('docx_refresh_fields'),
119
+ runtime: runtimeIdentity,
120
+ output: artifact,
121
+ receipt: artifact,
122
+ summary: z.object({
123
+ backend: z.literal('wps'),
124
+ refreshScope: z.array(z.enum(['table-of-contents', 'table-of-figures'])).min(1),
125
+ }).strict(),
126
+ }).strict();
127
+
103
128
  const xlsxFixedTools = [
104
129
  {"name":"xlsx_set_cell_value","description":"Set current workbook cell values."},
105
130
  {"name":"xlsx_set_cell_number_format","description":"Set current workbook cell number formats."},
@@ -173,6 +198,7 @@ const docxObjectIdentity = z.object({
173
198
  const docxReadObjectOutput = artifactOutput('docx_read_object').extend({
174
199
  revision: docxRevision,
175
200
  object: docxObjectIdentity,
201
+ objects: z.array(docxObjectIdentity).min(1),
176
202
  }).strict();
177
203
 
178
204
  const tools = [
@@ -200,7 +226,7 @@ const tools = [
200
226
  },
201
227
  {
202
228
  name: 'docx_read_object',
203
- description: 'Read one selected native DOCX object as an ordered native hierarchy written to the requested output artifact. The tool response only identifies that artifact and the selected root object; read the artifact for descendant refs and content. For a table, request row and cell once. Add paragraph for whole-paragraph choices; add run and text for exact content selections.',
229
+ description: 'Read one selected native DOCX object. The response returns the selected root and requested descendants as ordered object identities for the next operation; the complete hierarchy remains in the evidence artifact. For a table, request row, cell, and paragraph once when row bounds, header columns, and paragraph choices are all needed.',
204
230
  inputSchema: inputContract('docx_read_object'),
205
231
  outputSchema: docxReadObjectOutput,
206
232
  annotations: { readOnlyHint: true, idempotentHint: true },
@@ -208,11 +234,18 @@ const tools = [
208
234
  },
209
235
  {
210
236
  name: 'docx_copy_content',
211
- description: 'Replace content in existing target cells while retaining target cell formatting and source inline meaning. Batch cells that need exact observed subsets after docx_copy_table_rows. A selection copies a whole object by ref, or an exact substring when range is attached to a run or text ref.',
237
+ description: 'Replace content in existing plain-text target paragraphs or cells while retaining target container formatting and source inline meaning. Targets containing non-text objects are rejected. Batch cells that need exact observed subsets after docx_copy_table_rows. A selection copies a whole object by ref, or an exact substring when range is attached to a run or text ref.',
212
238
  inputSchema: inputContract('docx_copy_content'),
213
239
  outputSchema: fixedEditOutput('docx_copy_content'),
214
240
  handler: args => fixedEdit('docx_copy_content', args),
215
241
  },
242
+ {
243
+ name: 'docx_set_text',
244
+ description: 'Replace the whole text content of observed plain-text paragraph or cell objects while retaining their target formatting. Targets containing non-text objects are rejected. This sets already-derived text; it does not insert objects, copy source formatting, or decide business wording.',
245
+ inputSchema: inputContract('docx_set_text'),
246
+ outputSchema: fixedEditOutput('docx_set_text'),
247
+ handler: args => fixedEdit('docx_set_text', args),
248
+ },
216
249
  {
217
250
  name: 'docx_copy_table_rows',
218
251
  description: 'Populate existing target tables by replacing selected data-row ranges with selected source rows. Explicit header-cell mappings preserve target presentation and source grouping. A column content mode applies to every copied cell in that column; when cells need different subsets, copy the row structure first and batch those cells with docx_copy_content.',
@@ -277,6 +310,13 @@ const tools = [
277
310
  annotations: { readOnlyHint: true, idempotentHint: true },
278
311
  handler: docxValidateFontPolicy,
279
312
  },
313
+ {
314
+ name: 'docx_apply_font_policy',
315
+ description: 'Apply one explicit font family and size policy to current main-document body and table text. It does not derive a policy or alter other run semantics.',
316
+ inputSchema: inputContract('docx_apply_font_policy'),
317
+ outputSchema: fixedEditOutput('docx_apply_font_policy'),
318
+ handler: args => fixedEdit('docx_apply_font_policy', args),
319
+ },
280
320
  {
281
321
  name: 'docx_validate_toc_style_policy',
282
322
  description: 'Validate current DOCX table-of-contents paragraph styles against an explicit policy.',
@@ -284,6 +324,20 @@ const tools = [
284
324
  annotations: { readOnlyHint: true, idempotentHint: true },
285
325
  handler: docxValidateTocStylePolicy,
286
326
  },
327
+ {
328
+ name: 'docx_apply_toc_style_policy',
329
+ description: 'Apply explicit italic and per-level indentation values to current built-in table-of-contents paragraph styles. It does not change heading text or refresh fields.',
330
+ inputSchema: inputContract('docx_apply_toc_style_policy'),
331
+ outputSchema: fixedEditOutput('docx_apply_toc_style_policy'),
332
+ handler: args => fixedEdit('docx_apply_toc_style_policy', args),
333
+ },
334
+ {
335
+ name: 'docx_refresh_fields',
336
+ description: 'Refresh table-of-contents and table-of-figures field results in a current DOCX through native WPS Writer. It does not change headings, captions, or field definitions.',
337
+ inputSchema: inputContract('docx_refresh_fields'),
338
+ outputSchema: docxFieldRefreshOutput,
339
+ handler: docxRefreshFields,
340
+ },
287
341
  {
288
342
  name: 'docx_strip_direct_formatting',
289
343
  description: 'Remove direct paragraph and run formatting while preserving styles.',
@@ -429,6 +483,17 @@ function compactDocxObjectIdentity(object) {
429
483
  };
430
484
  }
431
485
 
486
+ function compactDocxObservationObjects(observation) {
487
+ const objects = [];
488
+ function visit(node) {
489
+ if (!node?.object) return;
490
+ objects.push(compactDocxObjectIdentity(node.object));
491
+ for (const child of node.children ?? []) visit(child);
492
+ }
493
+ visit(observation);
494
+ return objects;
495
+ }
496
+
432
497
  async function docxInspect(args) {
433
498
  const input = path.resolve(requireString(args.input, 'input'));
434
499
  const result = await runJsonCandidateChain(docxCandidates, ['inspect', input, '--json']);
@@ -461,6 +526,7 @@ async function docxReadObject(args) {
461
526
  artifact: await writeJsonArtifact(output, result.json),
462
527
  revision: result.json.receipt.revision,
463
528
  object: compactDocxObjectIdentity(result.json.observation.object),
529
+ objects: compactDocxObservationObjects(result.json.observation),
464
530
  };
465
531
  });
466
532
  }
@@ -492,6 +558,42 @@ async function docxReplaceStyleIds(args) {
492
558
  return withTempJsonFile(args.styleMap, styleMapPath => copyTransform('docx_replace_style_ids', docxCandidates, ['replace-style-ids'], args, [styleMapPath]));
493
559
  }
494
560
 
561
+ async function docxRefreshFields(args) {
562
+ const input = path.resolve(requireString(args.input, 'input'));
563
+ const output = path.resolve(requireString(args.output, 'output'));
564
+ const receiptOutput = path.resolve(requireString(args.receiptOutput, 'receiptOutput'));
565
+ if (path.extname(input).toLowerCase() !== '.docx' || path.extname(output).toLowerCase() !== '.docx') {
566
+ throw Object.assign(new Error('DOCX field refresh requires .docx input and output'), { code: -32602 });
567
+ }
568
+ await requireNewFile(output, 'output');
569
+ await requireNewFile(receiptOutput, 'receiptOutput');
570
+ const inputArtifact = await fileArtifact(input);
571
+ try {
572
+ const result = await runJsonCandidateChain(convertCandidates, ['refresh-docx-fields', input, output]);
573
+ const providerReceipt = docxFieldRefreshReceipt.parse(result.json);
574
+ const outputArtifact = await fileArtifact(output);
575
+ if (path.resolve(providerReceipt.input) !== input
576
+ || path.resolve(providerReceipt.output) !== output
577
+ || providerReceipt.input_sha256 !== inputArtifact.sha256
578
+ || providerReceipt.output_sha256 !== outputArtifact.sha256) {
579
+ throw new Error('DOCX field refresh receipt is not bound to the current input and output');
580
+ }
581
+ return {
582
+ tool: 'docx_refresh_fields',
583
+ runtime: commandRuntime(result),
584
+ output: outputArtifact,
585
+ receipt: await writeJsonArtifact(receiptOutput, providerReceipt),
586
+ summary: {
587
+ backend: providerReceipt.backend,
588
+ refreshScope: providerReceipt.refresh_scope,
589
+ },
590
+ };
591
+ } catch (error) {
592
+ await rm(output, { force: true });
593
+ throw error;
594
+ }
595
+ }
596
+
495
597
  async function copyTransform(tool, candidates, command, args, suffix = []) {
496
598
  const input = path.resolve(requireString(args.input, 'input'));
497
599
  const output = path.resolve(requireString(args.output, 'output'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiwater/office-mcp",
3
- "version": "0.16.20",
3
+ "version": "0.17.1",
4
4
  "description": "Published MCP server for Tiwater Office document capabilities",
5
5
  "type": "module",
6
6
  "license": "MIT",