@tiwater/office-mcp 0.21.38 → 0.21.40

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.
@@ -5,6 +5,7 @@ export const effectKindSchema = 'tiwater.provider-effect-kind/v1';
5
5
  export const documentRevisionRoleKey = 'x-tiwater-document-revision-role';
6
6
 
7
7
  const effectKinds = new Set([
8
+ 'document-create',
8
9
  'document-mutation',
9
10
  'source-conversion',
10
11
  'native-render',
@@ -46,10 +47,15 @@ export function assertEffectKindToolContract(tool, expectedKind = undefined) {
46
47
  if (expectedKind !== undefined && metadata.kind !== expectedKind) {
47
48
  throw new Error(`effect-kind-mismatch:${tool?.name || 'unnamed'}:${metadata.kind}`);
48
49
  }
49
- if (!bindings.some(binding => binding.role === 'read')) {
50
+ if (metadata.kind !== 'document-create' && !bindings.some(binding => binding.role === 'read')) {
50
51
  throw new Error(`effect-kind-source-binding-missing:${tool?.name || 'unnamed'}`);
51
52
  }
52
- if (metadata.kind === 'document-mutation') {
53
+ if (metadata.kind === 'document-create') {
54
+ if (bindings.some(binding => binding.role === 'read')
55
+ || currentDocumentBindings.length !== 0 || effectiveWrites.length !== 1) {
56
+ throw new Error(`document-create-bindings-invalid:${tool?.name || 'unnamed'}`);
57
+ }
58
+ } else if (metadata.kind === 'document-mutation') {
53
59
  if (currentDocumentBindings.length !== 1) {
54
60
  throw new Error(`document-mutation-current-binding-invalid:${tool?.name || 'unnamed'}`);
55
61
  }
@@ -70,6 +76,16 @@ export function assertEffectKindToolContract(tool, expectedKind = undefined) {
70
76
  return metadata.kind;
71
77
  }
72
78
 
79
+ export function documentCreateFileArguments(tool, args) {
80
+ assertEffectKindToolContract(tool, 'document-create');
81
+ const bindings = boundFileArguments(tool?.inputSchema, args);
82
+ const effectiveOutput = bindings.filter(binding => binding.role === 'write' && binding.effect);
83
+ if (bindings.some(binding => binding.role === 'read') || effectiveOutput.length !== 1) {
84
+ throw new Error(`document-create-file-arguments-invalid:${tool?.name || 'unnamed'}`);
85
+ }
86
+ return { effectiveOutput: effectiveOutput[0].value };
87
+ }
88
+
73
89
  export function documentMutationFileArguments(tool, args) {
74
90
  assertEffectKindToolContract(tool, 'document-mutation');
75
91
  const bindings = boundFileArguments(tool?.inputSchema, args);
@@ -20,7 +20,8 @@
20
20
  "type": "string",
21
21
  "minLength": 1,
22
22
  "description": "Absolute path for a new JSON file containing the complete result. Existing files are never overwritten.",
23
- "x-tiwater-file-role": "write"
23
+ "x-tiwater-file-role": "write",
24
+ "x-tiwater-file-effect": false
24
25
  }
25
26
  },
26
27
  "required": [
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "tiwater.office-mcp-input/docx_create/v1",
4
+ "type": "object",
5
+ "properties": {
6
+ "output": {
7
+ "type": "string",
8
+ "minLength": 1,
9
+ "description": "New DOCX path. Existing files are never overwritten.",
10
+ "x-tiwater-file-role": "write"
11
+ },
12
+ "receiptOutput": {
13
+ "type": "string",
14
+ "minLength": 1,
15
+ "description": "New JSON receipt path. Existing files are never overwritten.",
16
+ "x-tiwater-file-role": "write",
17
+ "x-tiwater-file-effect": false
18
+ }
19
+ },
20
+ "required": ["output", "receiptOutput"],
21
+ "additionalProperties": false
22
+ }
@@ -54,12 +54,6 @@
54
54
  "additionalProperties": false,
55
55
  "description": "Optional parent OpenXML address. Only nearest published children are returned."
56
56
  },
57
- "limit": {
58
- "default": 50,
59
- "type": "integer",
60
- "minimum": 1,
61
- "maximum": 2147483647
62
- },
63
57
  "offset": {
64
58
  "default": 0,
65
59
  "type": "integer",
@@ -70,7 +64,8 @@
70
64
  "type": "string",
71
65
  "minLength": 1,
72
66
  "description": "Optional path for retaining the complete requested page. May be combined with returnContent.",
73
- "x-tiwater-file-role": "write"
67
+ "x-tiwater-file-role": "write",
68
+ "x-tiwater-file-effect": false
74
69
  }
75
70
  },
76
71
  "required": [
@@ -57,7 +57,8 @@
57
57
  "type": "string",
58
58
  "minLength": 1,
59
59
  "description": "Optional path for retaining the complete selected observation. May be combined with returnContent.",
60
- "x-tiwater-file-role": "write"
60
+ "x-tiwater-file-role": "write",
61
+ "x-tiwater-file-effect": false
61
62
  }
62
63
  },
63
64
  "required": [
@@ -30,7 +30,8 @@
30
30
  "type": "string",
31
31
  "minLength": 1,
32
32
  "description": "Optional path for retaining the selected detailed row page. May be combined with returnContent.",
33
- "x-tiwater-file-role": "write"
33
+ "x-tiwater-file-role": "write",
34
+ "x-tiwater-file-effect": false
34
35
  }
35
36
  },
36
37
  "required": ["input", "table", "returnContent"],
@@ -17,7 +17,8 @@
17
17
  "type": "string",
18
18
  "minLength": 1,
19
19
  "description": "Optional new JSON artifact path for retaining the complete table index. May be combined with returnContent.",
20
- "x-tiwater-file-role": "write"
20
+ "x-tiwater-file-role": "write",
21
+ "x-tiwater-file-effect": false
21
22
  },
22
23
  "offset": {
23
24
  "type": "integer",
@@ -15,7 +15,8 @@
15
15
  "type": "string",
16
16
  "minLength": 1,
17
17
  "description": "Absolute path for a new JSON file containing the complete result. Existing files are never overwritten.",
18
- "x-tiwater-file-role": "write"
18
+ "x-tiwater-file-role": "write",
19
+ "x-tiwater-file-effect": false
19
20
  }
20
21
  },
21
22
  "required": [
@@ -48,7 +48,8 @@
48
48
  "type": "string",
49
49
  "minLength": 1,
50
50
  "description": "Absolute path for a new JSON file containing the complete result. Existing files are never overwritten.",
51
- "x-tiwater-file-role": "write"
51
+ "x-tiwater-file-role": "write",
52
+ "x-tiwater-file-effect": false
52
53
  }
53
54
  },
54
55
  "required": [
@@ -23,7 +23,8 @@
23
23
  "type": "string",
24
24
  "minLength": 1,
25
25
  "description": "Absolute path for a new JSON file containing the complete result. Existing files are never overwritten.",
26
- "x-tiwater-file-role": "write"
26
+ "x-tiwater-file-role": "write",
27
+ "x-tiwater-file-effect": false
27
28
  }
28
29
  },
29
30
  "required": [
@@ -0,0 +1,44 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "tiwater.office-mcp-input/pptx_read_shape/v1",
4
+ "type": "object",
5
+ "properties": {
6
+ "input": {
7
+ "type": "string",
8
+ "minLength": 1,
9
+ "description": "Current PPTX presentation.",
10
+ "x-tiwater-file-role": "read"
11
+ },
12
+ "slideNumber": {
13
+ "type": "integer",
14
+ "minimum": 1,
15
+ "maximum": 9007199254740991,
16
+ "description": "Exact one-based slide number returned by presentation observation."
17
+ },
18
+ "shapeId": {
19
+ "type": "integer",
20
+ "minimum": 1,
21
+ "maximum": 4294967295,
22
+ "description": "Exact native shape id returned by pptx_read_slide."
23
+ },
24
+ "offset": {
25
+ "type": "integer",
26
+ "minimum": 0,
27
+ "maximum": 9007199254740991,
28
+ "description": "Zero-based text-segment offset within the selected shape. Continue only when another segment is needed."
29
+ },
30
+ "returnContent": {
31
+ "type": "boolean",
32
+ "description": "Return this selected technical page directly when it fits the response limit. May be combined with output."
33
+ },
34
+ "output": {
35
+ "type": "string",
36
+ "minLength": 1,
37
+ "description": "Optional immutable JSON artifact path for this selected technical page. May be combined with returnContent.",
38
+ "x-tiwater-file-role": "write",
39
+ "x-tiwater-file-effect": false
40
+ }
41
+ },
42
+ "required": ["input", "slideNumber", "shapeId", "returnContent"],
43
+ "additionalProperties": false
44
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "tiwater.office-mcp-input/pptx_read_slide/v1",
4
+ "type": "object",
5
+ "properties": {
6
+ "input": {
7
+ "type": "string",
8
+ "minLength": 1,
9
+ "description": "Current PPTX presentation.",
10
+ "x-tiwater-file-role": "read"
11
+ },
12
+ "slideNumber": {
13
+ "type": "integer",
14
+ "minimum": 1,
15
+ "maximum": 9007199254740991,
16
+ "description": "Exact one-based slide number returned by presentation observation."
17
+ },
18
+ "offset": {
19
+ "type": "integer",
20
+ "minimum": 0,
21
+ "maximum": 9007199254740991,
22
+ "description": "Zero-based shape offset in native z-order on the selected slide. Continue only when another selected shape is needed."
23
+ },
24
+ "returnContent": {
25
+ "type": "boolean",
26
+ "description": "Return this selected technical page directly when it fits the response limit. May be combined with output."
27
+ },
28
+ "output": {
29
+ "type": "string",
30
+ "minLength": 1,
31
+ "description": "Optional immutable JSON artifact path for this selected technical page. May be combined with returnContent.",
32
+ "x-tiwater-file-role": "write",
33
+ "x-tiwater-file-effect": false
34
+ }
35
+ },
36
+ "required": ["input", "slideNumber", "returnContent"],
37
+ "additionalProperties": false
38
+ }
@@ -15,7 +15,8 @@
15
15
  "type": "string",
16
16
  "minLength": 1,
17
17
  "description": "Absolute path for a new JSON file containing the complete result. Existing files are never overwritten.",
18
- "x-tiwater-file-role": "write"
18
+ "x-tiwater-file-role": "write",
19
+ "x-tiwater-file-effect": false
19
20
  }
20
21
  },
21
22
  "required": [
@@ -2,7 +2,7 @@
2
2
  "schema": "tiwater.office-provider-contract-manifest/v1",
3
3
  "provider": {
4
4
  "id": "@tiwater/office-mcp",
5
- "version": "0.21.38"
5
+ "version": "0.21.40"
6
6
  },
7
7
  "tools": [
8
8
  {
@@ -31,11 +31,22 @@
31
31
  "name": "docx_compare",
32
32
  "providerContract": {
33
33
  "source": "packages/docx-cli/contracts/mcp-input/docx_compare.schema.json",
34
- "sha256": "a6d59ee785d8ab3994eb6f539b48411b272565ac136219de58d39a449b3112f3"
34
+ "sha256": "bcad394d99a118cee1b1b5d62fc08acba2b77de27d6c29c63535049150f2016a"
35
35
  },
36
36
  "inputContract": {
37
37
  "path": "office/contracts/docx_compare.schema.json",
38
- "sha256": "a6d59ee785d8ab3994eb6f539b48411b272565ac136219de58d39a449b3112f3"
38
+ "sha256": "bcad394d99a118cee1b1b5d62fc08acba2b77de27d6c29c63535049150f2016a"
39
+ }
40
+ },
41
+ {
42
+ "name": "docx_create",
43
+ "providerContract": {
44
+ "source": "packages/docx-cli/contracts/mcp-input/docx_create.schema.json",
45
+ "sha256": "1cf22a23fc5d8b27fff6f50db2672be623696eac79ea986053e703274eab5e3e"
46
+ },
47
+ "inputContract": {
48
+ "path": "office/contracts/docx_create.schema.json",
49
+ "sha256": "1cf22a23fc5d8b27fff6f50db2672be623696eac79ea986053e703274eab5e3e"
39
50
  }
40
51
  },
41
52
  {
@@ -108,11 +119,11 @@
108
119
  "name": "docx_list_objects",
109
120
  "providerContract": {
110
121
  "source": "packages/docx-cli/contracts/mcp-input/docx_list_objects.schema.json",
111
- "sha256": "f00b36f429ef4e6c68fcb39fd141024897a97ecac89378e3fc1e45cd6c04e906"
122
+ "sha256": "1f8d857c837d258e14959895260ac7f5c5041b381cdc936c2c4194e0c0b949ae"
112
123
  },
113
124
  "inputContract": {
114
125
  "path": "office/contracts/docx_list_objects.schema.json",
115
- "sha256": "f00b36f429ef4e6c68fcb39fd141024897a97ecac89378e3fc1e45cd6c04e906"
126
+ "sha256": "1f8d857c837d258e14959895260ac7f5c5041b381cdc936c2c4194e0c0b949ae"
116
127
  }
117
128
  },
118
129
  {
@@ -130,22 +141,22 @@
130
141
  "name": "docx_read_object",
131
142
  "providerContract": {
132
143
  "source": "packages/docx-cli/contracts/mcp-input/docx_read_object.schema.json",
133
- "sha256": "ab93256991eaf665dfc096cf7df9ba2b4ba26ad6c11813d885a3c5ff2c2fa45b"
144
+ "sha256": "bb75dc704a2da4dfbd69004a21697d6824011036e1153778b839357f642c7057"
134
145
  },
135
146
  "inputContract": {
136
147
  "path": "office/contracts/docx_read_object.schema.json",
137
- "sha256": "ab93256991eaf665dfc096cf7df9ba2b4ba26ad6c11813d885a3c5ff2c2fa45b"
148
+ "sha256": "bb75dc704a2da4dfbd69004a21697d6824011036e1153778b839357f642c7057"
138
149
  }
139
150
  },
140
151
  {
141
152
  "name": "docx_read_table",
142
153
  "providerContract": {
143
154
  "source": "packages/docx-cli/contracts/mcp-input/docx_read_table.schema.json",
144
- "sha256": "0b7a5669daeaa6f8e171ba01542b4bfd065cd282a32943e7b5b5854551b4afc7"
155
+ "sha256": "a0d13fb70cc43d7ee7f3be9730f98df9a5e988d33fb7454b44f5e455d068b950"
145
156
  },
146
157
  "inputContract": {
147
158
  "path": "office/contracts/docx_read_table.schema.json",
148
- "sha256": "0b7a5669daeaa6f8e171ba01542b4bfd065cd282a32943e7b5b5854551b4afc7"
159
+ "sha256": "a0d13fb70cc43d7ee7f3be9730f98df9a5e988d33fb7454b44f5e455d068b950"
149
160
  }
150
161
  },
151
162
  {
@@ -240,44 +251,44 @@
240
251
  "name": "docx_table_index",
241
252
  "providerContract": {
242
253
  "source": "packages/docx-cli/contracts/mcp-input/docx_table_index.schema.json",
243
- "sha256": "737052f653fe0d222bfc2b808e0fbc298376ab88cf052352888ff02e5b71ed52"
254
+ "sha256": "4dbc4079269d5c37ed8fd213f3987bea82843c2e5d4d17fae169cecd17d44964"
244
255
  },
245
256
  "inputContract": {
246
257
  "path": "office/contracts/docx_table_index.schema.json",
247
- "sha256": "737052f653fe0d222bfc2b808e0fbc298376ab88cf052352888ff02e5b71ed52"
258
+ "sha256": "4dbc4079269d5c37ed8fd213f3987bea82843c2e5d4d17fae169cecd17d44964"
248
259
  }
249
260
  },
250
261
  {
251
262
  "name": "docx_validate",
252
263
  "providerContract": {
253
264
  "source": "packages/docx-cli/contracts/mcp-input/docx_validate.schema.json",
254
- "sha256": "57c25f53aa319dc0e9aafc5cd3fa5f0ea12f28c50fe4a39955caa40e2dfc12d5"
265
+ "sha256": "06d5f29f7ed23ab53fdf7a5de09cbfb6a800e7a19ae076a3d6d80abf22c464be"
255
266
  },
256
267
  "inputContract": {
257
268
  "path": "office/contracts/docx_validate.schema.json",
258
- "sha256": "57c25f53aa319dc0e9aafc5cd3fa5f0ea12f28c50fe4a39955caa40e2dfc12d5"
269
+ "sha256": "06d5f29f7ed23ab53fdf7a5de09cbfb6a800e7a19ae076a3d6d80abf22c464be"
259
270
  }
260
271
  },
261
272
  {
262
273
  "name": "docx_validate_font_policy",
263
274
  "providerContract": {
264
275
  "source": "packages/docx-cli/contracts/mcp-input/docx_validate_font_policy.schema.json",
265
- "sha256": "1ef0ae13622931c92054b4cf492957c7d9d16f659b2d333c67c065f0bc4750d4"
276
+ "sha256": "75e98324c6a8387f0207d83ba1a80b351d487c15d386b542e2130956b1237904"
266
277
  },
267
278
  "inputContract": {
268
279
  "path": "office/contracts/docx_validate_font_policy.schema.json",
269
- "sha256": "1ef0ae13622931c92054b4cf492957c7d9d16f659b2d333c67c065f0bc4750d4"
280
+ "sha256": "75e98324c6a8387f0207d83ba1a80b351d487c15d386b542e2130956b1237904"
270
281
  }
271
282
  },
272
283
  {
273
284
  "name": "docx_validate_toc_style_policy",
274
285
  "providerContract": {
275
286
  "source": "packages/docx-cli/contracts/mcp-input/docx_validate_toc_style_policy.schema.json",
276
- "sha256": "e2317dfc690901a89ed3d8b0307674132f65977cee29cb6ace138e5c87d04603"
287
+ "sha256": "5a98a3073a8e149a591254ef4e93f27d3b8f5dbce3204199d7155365f00088ef"
277
288
  },
278
289
  "inputContract": {
279
290
  "path": "office/contracts/docx_validate_toc_style_policy.schema.json",
280
- "sha256": "e2317dfc690901a89ed3d8b0307674132f65977cee29cb6ace138e5c87d04603"
291
+ "sha256": "5a98a3073a8e149a591254ef4e93f27d3b8f5dbce3204199d7155365f00088ef"
281
292
  }
282
293
  },
283
294
  {
@@ -335,6 +346,28 @@
335
346
  "sha256": "9cde8fdda335f379d44e60819367b305a2c97c6c0c8fdf2141bf460335e5da84"
336
347
  }
337
348
  },
349
+ {
350
+ "name": "pptx_read_shape",
351
+ "providerContract": {
352
+ "source": "packages/pptx-cli/contracts/mcp-input/pptx_read_shape.schema.json",
353
+ "sha256": "0311db4e2675773d0dde0cb7751722394a4ae4b06ef828a09c0843a849299009"
354
+ },
355
+ "inputContract": {
356
+ "path": "office/contracts/pptx_read_shape.schema.json",
357
+ "sha256": "0311db4e2675773d0dde0cb7751722394a4ae4b06ef828a09c0843a849299009"
358
+ }
359
+ },
360
+ {
361
+ "name": "pptx_read_slide",
362
+ "providerContract": {
363
+ "source": "packages/pptx-cli/contracts/mcp-input/pptx_read_slide.schema.json",
364
+ "sha256": "831b4573bad0ed325682ac16275e122b44c515bceab21c4d0a82db9e90d54cf7"
365
+ },
366
+ "inputContract": {
367
+ "path": "office/contracts/pptx_read_slide.schema.json",
368
+ "sha256": "831b4573bad0ed325682ac16275e122b44c515bceab21c4d0a82db9e90d54cf7"
369
+ }
370
+ },
338
371
  {
339
372
  "name": "pptx_replace_picture_image",
340
373
  "providerContract": {
@@ -361,11 +394,11 @@
361
394
  "name": "pptx_validate",
362
395
  "providerContract": {
363
396
  "source": "packages/pptx-cli/contracts/mcp-input/pptx_validate.schema.json",
364
- "sha256": "57c25f53aa319dc0e9aafc5cd3fa5f0ea12f28c50fe4a39955caa40e2dfc12d5"
397
+ "sha256": "06d5f29f7ed23ab53fdf7a5de09cbfb6a800e7a19ae076a3d6d80abf22c464be"
365
398
  },
366
399
  "inputContract": {
367
400
  "path": "office/contracts/pptx_validate.schema.json",
368
- "sha256": "57c25f53aa319dc0e9aafc5cd3fa5f0ea12f28c50fe4a39955caa40e2dfc12d5"
401
+ "sha256": "06d5f29f7ed23ab53fdf7a5de09cbfb6a800e7a19ae076a3d6d80abf22c464be"
369
402
  }
370
403
  },
371
404
  {
@@ -449,11 +482,11 @@
449
482
  "name": "xlsx_read_range",
450
483
  "providerContract": {
451
484
  "source": "packages/xlsx-cli/contracts/mcp-input/xlsx_read_range.schema.json",
452
- "sha256": "04b0ce1b912047f774fbbbb6ba7cf55e5e53a9b74d6f027d9c21b4d6d7397225"
485
+ "sha256": "66a5ff24b1a207d70aa278b81a8efba5dc272af2f7a7c12331154e8aa8d8f3dd"
453
486
  },
454
487
  "inputContract": {
455
488
  "path": "office/contracts/xlsx_read_range.schema.json",
456
- "sha256": "04b0ce1b912047f774fbbbb6ba7cf55e5e53a9b74d6f027d9c21b4d6d7397225"
489
+ "sha256": "66a5ff24b1a207d70aa278b81a8efba5dc272af2f7a7c12331154e8aa8d8f3dd"
457
490
  }
458
491
  },
459
492
  {
@@ -548,11 +581,11 @@
548
581
  "name": "xlsx_validate",
549
582
  "providerContract": {
550
583
  "source": "packages/xlsx-cli/contracts/mcp-input/xlsx_validate.schema.json",
551
- "sha256": "57c25f53aa319dc0e9aafc5cd3fa5f0ea12f28c50fe4a39955caa40e2dfc12d5"
584
+ "sha256": "06d5f29f7ed23ab53fdf7a5de09cbfb6a800e7a19ae076a3d6d80abf22c464be"
552
585
  },
553
586
  "inputContract": {
554
587
  "path": "office/contracts/xlsx_validate.schema.json",
555
- "sha256": "57c25f53aa319dc0e9aafc5cd3fa5f0ea12f28c50fe4a39955caa40e2dfc12d5"
588
+ "sha256": "06d5f29f7ed23ab53fdf7a5de09cbfb6a800e7a19ae076a3d6d80abf22c464be"
556
589
  }
557
590
  }
558
591
  ]
@@ -25,12 +25,6 @@
25
25
  "maximum": 9007199254740991,
26
26
  "description": "Zero-based cell offset in row-major order within the selected range. Continue only when another selected cell is needed."
27
27
  },
28
- "limit": {
29
- "type": "integer",
30
- "minimum": 1,
31
- "maximum": 256,
32
- "description": "Maximum selected cells in this bounded page."
33
- },
34
28
  "returnContent": {
35
29
  "type": "boolean",
36
30
  "description": "Return this selected technical page directly when it fits the response limit. May be combined with output."
@@ -39,9 +33,10 @@
39
33
  "type": "string",
40
34
  "minLength": 1,
41
35
  "description": "Optional immutable JSON artifact path for this selected technical page. May be combined with returnContent.",
42
- "x-tiwater-file-role": "write"
36
+ "x-tiwater-file-role": "write",
37
+ "x-tiwater-file-effect": false
43
38
  }
44
39
  },
45
- "required": ["input", "sheet", "range", "limit", "returnContent"],
40
+ "required": ["input", "sheet", "range", "returnContent"],
46
41
  "additionalProperties": false
47
42
  }
@@ -15,7 +15,8 @@
15
15
  "type": "string",
16
16
  "minLength": 1,
17
17
  "description": "Absolute path for a new JSON file containing the complete result. Existing files are never overwritten.",
18
- "x-tiwater-file-role": "write"
18
+ "x-tiwater-file-role": "write",
19
+ "x-tiwater-file-effect": false
19
20
  }
20
21
  },
21
22
  "required": [
package/office/index.mjs CHANGED
@@ -25,6 +25,7 @@ import {
25
25
  import { withOutputWriteLock } from '../_shared/output-write-lock.mjs';
26
26
  import { evidenceRoleMetadata } from '../_shared/evidence-role.mjs';
27
27
  import {
28
+ documentCreateFileArguments,
28
29
  documentMutationFileArguments,
29
30
  effectKindMetadata,
30
31
  } from '../_shared/effect-kind.mjs';
@@ -178,6 +179,13 @@ function fixedEditOutput(tool) {
178
179
  }).strict();
179
180
  }
180
181
 
182
+ function fixedCreateOutput(tool) {
183
+ return z.object({
184
+ tool: z.literal(tool), runtime: runtimeIdentity, receipt: artifact, output: artifact,
185
+ summary: z.object({ pass: z.literal(true), operationCount: z.literal(1), appliedCount: z.literal(1) }).strict(),
186
+ }).strict();
187
+ }
188
+
181
189
  function largeResultOutput(tool) {
182
190
  return z.object({
183
191
  tool: z.literal(tool),
@@ -288,6 +296,102 @@ const pptxInspectionSummary = z.object({
288
296
  }).strict()).max(6),
289
297
  }).strict();
290
298
 
299
+ const pptxSlidePageSummary = z.object({
300
+ schema: z.literal('tiwater.pptx-slide-page-receipt/v1'),
301
+ slideNumber: z.number().int().positive(),
302
+ slidePath: z.string().min(1),
303
+ masterPath: z.string().min(1).nullable(),
304
+ layoutPath: z.string().min(1).nullable(),
305
+ totalShapeCount: z.number().int().nonnegative(),
306
+ returnedShapeCount: z.number().int().nonnegative(),
307
+ remaining: z.number().int().nonnegative(),
308
+ nextOffset: z.number().int().nonnegative().nullable(),
309
+ }).strict();
310
+
311
+ const pptxShapeTextPageSummary = z.object({
312
+ schema: z.literal('tiwater.pptx-shape-text-page-receipt/v1'),
313
+ slideNumber: z.number().int().positive(),
314
+ shapeId: z.number().int().positive(),
315
+ totalSegmentCount: z.number().int().nonnegative(),
316
+ returnedSegmentCount: z.number().int().nonnegative(),
317
+ remaining: z.number().int().nonnegative(),
318
+ nextOffset: z.number().int().nonnegative().nullable(),
319
+ }).strict();
320
+
321
+ const pptxTransform = z.object({
322
+ x: z.number().int(),
323
+ y: z.number().int(),
324
+ cx: z.number().int(),
325
+ cy: z.number().int(),
326
+ }).strict();
327
+
328
+ const pptxSlideShapeIdentity = z.object({
329
+ shapeId: z.number().int().positive(),
330
+ name: z.string().max(120),
331
+ kind: z.string().min(1).max(40),
332
+ zOrder: z.number().int().nonnegative(),
333
+ placeholderType: z.string().max(120).nullable(),
334
+ placeholderPresent: z.boolean(),
335
+ placeholderIndex: z.number().int().nonnegative().nullable(),
336
+ mediaPartPath: z.string().max(240).nullable(),
337
+ mediaSha256: z.string().regex(/^[a-f0-9]{64}$/).nullable(),
338
+ textPreview: z.string().max(240),
339
+ textLength: z.number().int().nonnegative(),
340
+ transform: pptxTransform.nullable(),
341
+ paragraphCount: z.number().int().nonnegative(),
342
+ runCount: z.number().int().nonnegative(),
343
+ hasTable: z.boolean(),
344
+ }).strict();
345
+
346
+ const pptxSlidePage = z.object({
347
+ schema: z.literal('tiwater.pptx-slide-page/v1'),
348
+ file: z.string(),
349
+ inputSha256: z.string().regex(/^[a-f0-9]{64}$/),
350
+ slideCount: z.number().int().nonnegative(),
351
+ slideSize: z.object({ cx: z.number().int().nonnegative(), cy: z.number().int().nonnegative() }).strict(),
352
+ slide: z.object({
353
+ slideNumber: z.number().int().positive(),
354
+ path: z.string().min(1),
355
+ masterPath: z.string().min(1).nullable(),
356
+ layoutPath: z.string().min(1).nullable(),
357
+ shapes: z.array(pptxSlideShapeIdentity).max(8),
358
+ }).strict(),
359
+ receipt: pptxSlidePageSummary,
360
+ }).strict();
361
+
362
+ const boundedPptxString = z.string().max(120).nullable();
363
+ const pptxShapeTextSegment = z.object({
364
+ segmentIndex: z.number().int().nonnegative(),
365
+ runIndex: z.number().int().nonnegative(),
366
+ paragraphIndex: z.number().int().nonnegative(),
367
+ text: z.string().max(160),
368
+ textOffset: z.number().int().nonnegative(),
369
+ runTextLength: z.number().int().nonnegative(),
370
+ textContinues: z.boolean(),
371
+ paragraphAlignment: boundedPptxString,
372
+ fontFamily: boundedPptxString,
373
+ fontSize: z.number().positive().nullable(),
374
+ color: boundedPptxString,
375
+ bold: z.boolean().nullable(),
376
+ directFontFamily: boundedPptxString,
377
+ directFontSize: z.number().positive().nullable(),
378
+ directColor: boundedPptxString,
379
+ directBold: z.boolean().nullable(),
380
+ fontFamilySource: boundedPptxString,
381
+ fontSizeSource: boundedPptxString,
382
+ colorSource: boundedPptxString,
383
+ boldSource: boundedPptxString,
384
+ }).strict();
385
+
386
+ const pptxShapeTextPage = z.object({
387
+ schema: z.literal('tiwater.pptx-shape-text-page/v1'),
388
+ file: z.string(),
389
+ inputSha256: z.string().regex(/^[a-f0-9]{64}$/),
390
+ shape: pptxSlideShapeIdentity,
391
+ receipt: pptxShapeTextPageSummary,
392
+ segments: z.array(pptxShapeTextSegment).max(4),
393
+ }).strict();
394
+
291
395
  function docxObservationOutput(tool) {
292
396
  return z.object({
293
397
  tool: z.literal(tool),
@@ -435,6 +539,14 @@ const docxReadObjectOutput = docxObservationOutput('docx_read_object').extend({
435
539
  }).strict();
436
540
 
437
541
  const tools = [
542
+ {
543
+ name: 'docx_create',
544
+ effectKind: 'document-create',
545
+ description: 'Create one new minimal standards-valid DOCX containing one empty paragraph. The result is a current native document that can be populated incrementally with the ordinary DOCX object operations. The provider chooses no business wording, template, layout mapping, or target structure and never overwrites an existing path.',
546
+ inputSchema: inputContract('docx_create'),
547
+ outputSchema: fixedCreateOutput('docx_create'),
548
+ handler: (args, tool) => fixedCreate(tool, args, docxCandidates),
549
+ },
438
550
  {
439
551
  name: 'docx_inspect',
440
552
  evidenceRole: 'document-observation',
@@ -446,7 +558,7 @@ const tools = [
446
558
  },
447
559
  {
448
560
  name: 'docx_list_objects',
449
- description: 'Page through mixed nearest-child OpenXML objects only when document order or paragraph relationships are required. Set returnContent true to return a byte-bounded object page. Provide output to store the complete requested page and return its artifact receipt. These channels are independent and may be used together; at least one is required. Continue from receipt.nextOffset only when an unreturned object is needed for the current decision. Do not use this tool to locate tables or read a whole document: use docx_table_index to locate tables, then a narrow table or object read.',
561
+ description: 'Page through mixed nearest-child OpenXML objects only when document order or paragraph relationships are required. The provider chooses the bounded page size. Set returnContent true to return the selected object page. Provide output to store the complete requested page and return its artifact receipt. These channels are independent and may be used together; at least one is required. Continue from receipt.nextOffset only when an unreturned object is needed for the current decision. Do not use this tool to locate tables or read a whole document: use docx_table_index to locate tables, then a narrow table or object read.',
450
562
  inputSchema: inputContract('docx_list_objects'),
451
563
  outputSchema: docxListObjectsOutput,
452
564
  annotations: { readOnlyHint: true, idempotentHint: true },
@@ -672,7 +784,7 @@ const tools = [
672
784
  },
673
785
  {
674
786
  name: 'xlsx_read_range',
675
- description: 'Read one explicit native A1 cell or rectangular range from one current XLSX worksheet. Pages use a row-major cell offset and return physical presence, raw and formatted values, normalized value type, formula metadata, style, rich text, and merged-range ownership. The receipt always reports remaining cells and the next offset. Continue only when another selected cell is needed. Set returnContent true to return the selected page when it fits the response limit. Provide output to store the same complete selected page as an immutable artifact. These channels are independent and may be used together; at least one is required. This tool does not infer regions, headers, records, field meanings, or business mappings; convert legacy XLS before reading Open XML cells.',
787
+ description: 'Read one explicit native A1 cell or rectangular range from one current XLSX worksheet. The provider chooses the bounded page size. Pages use a row-major cell offset and return physical presence, raw and formatted values, normalized value type, formula metadata, style, rich text, and merged-range ownership. The receipt always reports remaining cells and the next offset. Continue only when another selected cell is needed. Set returnContent true to return the selected page when it fits the response limit. Provide output to store the same complete selected page as an immutable artifact. These channels are independent and may be used together; at least one is required. This tool does not infer regions, headers, records, field meanings, or business mappings; convert legacy XLS before reading Open XML cells.',
676
788
  inputSchema: inputContract('xlsx_read_range'),
677
789
  outputSchema: xlsxRangeReadOutput,
678
790
  annotations: { readOnlyHint: true, idempotentHint: true },
@@ -705,6 +817,28 @@ const tools = [
705
817
  annotations: { readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: false },
706
818
  handler: pptxExportJson,
707
819
  },
820
+ {
821
+ name: 'pptx_read_slide',
822
+ description: 'List one selected PPTX slide as bounded pages of compact native shape identities in z-order. The provider chooses the bounded page size. Each identity includes its shape id, kind, geometry, text preview, object counts, placeholder facts, media identity, and whether it contains a table. The receipt reports the selected slide, its layout and master paths, remaining shapes, and the next offset. Continue only when another shape on this slide is needed; use pptx_read_shape for bounded text and effective formatting of one selected shape. Set returnContent true to return the selected page when it fits the response limit. Provide output to store the same complete selected page as an immutable artifact. These channels are independent and may be used together; at least one is required. This tool does not select templates, assign business roles, infer repairs, or inspect another slide.',
823
+ inputSchema: inputContract('pptx_read_slide'),
824
+ outputSchema: largeResultOutput('pptx_read_slide').extend({
825
+ summary: pptxSlidePageSummary,
826
+ content: pptxSlidePage.optional(),
827
+ }).strict(),
828
+ annotations: { readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: false },
829
+ handler: pptxReadSlide,
830
+ },
831
+ {
832
+ name: 'pptx_read_shape',
833
+ description: 'Read one native PPTX shape selected from pptx_read_slide as bounded text segments. The provider chooses the bounded page size. Each segment retains its run and paragraph identity, text offset, paragraph alignment, effective text formatting, direct formatting, and formatting source. Long run text is split without changing its native run identity. The receipt reports remaining segments and the next offset. Continue only when another segment of this shape is needed. Set returnContent true to return the selected page when it fits the response limit. Provide output to store the same complete selected page as an immutable artifact. These channels are independent and may be used together; at least one is required. This tool does not choose formatting, derive repairs, or inspect another shape.',
834
+ inputSchema: inputContract('pptx_read_shape'),
835
+ outputSchema: largeResultOutput('pptx_read_shape').extend({
836
+ summary: pptxShapeTextPageSummary,
837
+ content: pptxShapeTextPage.optional(),
838
+ }).strict(),
839
+ annotations: { readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: false },
840
+ handler: pptxReadShape,
841
+ },
708
842
  {
709
843
  name: 'pptx_apply_template',
710
844
  effectKind: 'document-mutation',
@@ -1238,6 +1372,36 @@ async function fixedEdit(tool, args, candidates) {
1238
1372
  });
1239
1373
  }
1240
1374
 
1375
+ async function fixedCreate(tool, args, candidates) {
1376
+ const publishedContract = {
1377
+ name: tool.name,
1378
+ inputSchema: inputContractSchema(tool.name),
1379
+ annotations: tool.annotations,
1380
+ _meta: effectKindMetadata(tool.effectKind),
1381
+ };
1382
+ const bindings = documentCreateFileArguments(publishedContract, args);
1383
+ const output = path.resolve(bindings.effectiveOutput);
1384
+ const receiptOutput = path.resolve(requireString(args.receiptOutput, 'receiptOutput'));
1385
+ await requireNewFile(output, 'output');
1386
+ await requireNewFile(receiptOutput, 'receiptOutput');
1387
+ return withTempJsonFile(args, async requestPath => {
1388
+ const result = await runJsonCandidateChain(candidates, [tool.name, requestPath], { allowedExitCodes: [0, 1] });
1389
+ if (result.code !== 0) {
1390
+ const detail = result.stderr.trim() || result.stdout.trim()
1391
+ || `${tool.name} failed with exit code ${result.code}`;
1392
+ throw new Error(detail);
1393
+ }
1394
+ if (result.json?.tool !== tool.name) throw new Error(`${tool.name} returned a mismatched tool identity`);
1395
+ await requireReturnedArtifact(result.json.receipt, receiptOutput, 'receipt');
1396
+ await requireReturnedArtifact(result.json.output, output, 'output');
1397
+ if (result.json.summary?.pass !== true
1398
+ || result.json.summary?.operationCount !== 1 || result.json.summary?.appliedCount !== 1) {
1399
+ throw new Error(`${tool.name} returned invalid creation evidence`);
1400
+ }
1401
+ return { ...result.json, runtime: commandRuntime(result) };
1402
+ });
1403
+ }
1404
+
1241
1405
  async function docxCompare(args) {
1242
1406
  const baseline = path.resolve(requireString(args.baseline, 'baseline'));
1243
1407
  const updated = path.resolve(requireString(args.updated, 'updated'));
@@ -1396,6 +1560,36 @@ async function pptxExportJson(args) {
1396
1560
  return deliverLargeJsonResult({ tool: 'pptx_export_json', args, runtime: commandRuntime(result), payload: result.json, sourcePaths: [input] });
1397
1561
  }
1398
1562
 
1563
+ async function pptxReadSlide(args) {
1564
+ const input = path.resolve(requireString(args.input, 'input'));
1565
+ return withTempJsonFile(args, async requestPath => {
1566
+ const result = await runJsonCandidateChain(pptxCandidates, ['pptx_read_slide', requestPath]);
1567
+ return deliverLargeJsonResult({
1568
+ tool: 'pptx_read_slide',
1569
+ args,
1570
+ runtime: commandRuntime(result),
1571
+ payload: result.json,
1572
+ sourcePaths: [input],
1573
+ summary: result.json.receipt,
1574
+ });
1575
+ });
1576
+ }
1577
+
1578
+ async function pptxReadShape(args) {
1579
+ const input = path.resolve(requireString(args.input, 'input'));
1580
+ return withTempJsonFile(args, async requestPath => {
1581
+ const result = await runJsonCandidateChain(pptxCandidates, ['pptx_read_shape', requestPath]);
1582
+ return deliverLargeJsonResult({
1583
+ tool: 'pptx_read_shape',
1584
+ args,
1585
+ runtime: commandRuntime(result),
1586
+ payload: result.json,
1587
+ sourcePaths: [input],
1588
+ summary: result.json.receipt,
1589
+ });
1590
+ });
1591
+ }
1592
+
1399
1593
  async function pptxValidate(args) {
1400
1594
  const input = path.resolve(requireString(args.input, 'input'));
1401
1595
  const result = await runJsonCandidateChain(pptxCandidates, ['validate', input], { allowedExitCodes: [0, 1] });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiwater/office-mcp",
3
- "version": "0.21.38",
3
+ "version": "0.21.40",
4
4
  "description": "Published MCP distribution for independent Tiwater Office, PDF, and Text document capabilities",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -2,7 +2,7 @@
2
2
  "schema": "tiwater.pdf-provider-contract-manifest/v1",
3
3
  "provider": {
4
4
  "id": "@tiwater/office-mcp",
5
- "version": "0.21.38"
5
+ "version": "0.21.40"
6
6
  },
7
7
  "runtime": {
8
8
  "command": "tiwater-pdf"
@@ -14,12 +14,6 @@
14
14
  "maximum": 9007199254740991,
15
15
  "description": "Zero-based line offset in the exact current source revision."
16
16
  },
17
- "limit": {
18
- "type": "integer",
19
- "minimum": 1,
20
- "maximum": 200,
21
- "description": "Maximum number of lines in this bounded page."
22
- },
23
17
  "returnContent": {
24
18
  "type": "boolean",
25
19
  "description": "Return this selected line page directly when it fits the response limit. May be combined with output."
@@ -28,9 +22,10 @@
28
22
  "type": "string",
29
23
  "minLength": 1,
30
24
  "description": "Optional immutable JSON artifact path for this selected line page. May be combined with returnContent.",
31
- "x-tiwater-file-role": "write"
25
+ "x-tiwater-file-role": "write",
26
+ "x-tiwater-file-effect": false
32
27
  }
33
28
  },
34
- "required": ["input", "offset", "limit", "returnContent"],
29
+ "required": ["input", "offset", "returnContent"],
35
30
  "additionalProperties": false
36
31
  }
@@ -2,7 +2,7 @@
2
2
  "schema": "tiwater.text-provider-contract-manifest/v1",
3
3
  "provider": {
4
4
  "id": "@tiwater/office-mcp",
5
- "version": "0.21.38"
5
+ "version": "0.21.40"
6
6
  },
7
7
  "tools": [
8
8
  {
@@ -20,11 +20,11 @@
20
20
  "name": "text_read_lines",
21
21
  "providerContract": {
22
22
  "source": "servers/text/provider-contracts/text_read_lines.schema.json",
23
- "sha256": "0f9202f0b45bbd35b3ba4bebec7776da41f2c8668c7cdece068c44102f518bb6"
23
+ "sha256": "cf855e8131f5f2cfbb65185986d0f85ab87fb0efa5000c31491e10cc7ead3c3a"
24
24
  },
25
25
  "inputContract": {
26
26
  "path": "text/contracts/text_read_lines.schema.json",
27
- "sha256": "0f9202f0b45bbd35b3ba4bebec7776da41f2c8668c7cdece068c44102f518bb6"
27
+ "sha256": "cf855e8131f5f2cfbb65185986d0f85ab87fb0efa5000c31491e10cc7ead3c3a"
28
28
  }
29
29
  }
30
30
  ]
package/text/index.mjs CHANGED
@@ -113,7 +113,7 @@ const definitions = [
113
113
  },
114
114
  {
115
115
  name: 'text_read_lines',
116
- description: 'Read one explicit zero-based line page from one exact supported plain-text revision. The receipt reports remaining lines and nextOffset; continue only when another line is needed. Set returnContent true to return the selected page when it fits the response limit. Provide output to retain the same complete page. These channels are independent and may be used together; at least one is required. Lines retain their exact decoded text and terminator; the provider does not interpret fields, records, key-value pairs, sections, Markdown, or business meaning.',
116
+ description: 'Read one explicit zero-based line page from one exact supported plain-text revision. The provider chooses the bounded page size. The receipt reports remaining lines and nextOffset; continue only when another line is needed. Set returnContent true to return the selected page when it fits the response limit. Provide output to retain the same complete page. These channels are independent and may be used together; at least one is required. Lines retain their exact decoded text and terminator; the provider does not interpret fields, records, key-value pairs, sections, Markdown, or business meaning.',
117
117
  outputSchema: largeResultOutput(linePage).extend({ summary: linePageReceipt }).strict(),
118
118
  handler: textReadLines,
119
119
  },
@@ -165,7 +165,7 @@ async function textInspect(args) {
165
165
  }
166
166
 
167
167
  async function textReadLines(args) {
168
- const observation = await readTextLines(args.input, args.offset, args.limit);
168
+ const observation = await readTextLines(args.input, args.offset);
169
169
  const delivered = await deliverLargeJsonResult({
170
170
  tool: 'text_read_lines',
171
171
  args,
@@ -6,6 +6,7 @@ import { fileArtifact } from '../_shared/large-json-result.mjs';
6
6
  const supportedExtensions = new Set(['.txt', '.text', '.log', '.csv', '.tsv', '.md', '.markdown']);
7
7
  const openingLineLimit = 8;
8
8
  const openingTextLimit = 160;
9
+ const linePageLimit = 200;
9
10
 
10
11
  export async function inspectText(inputValue) {
11
12
  const observation = await observeText(inputValue);
@@ -30,17 +31,14 @@ export async function inspectText(inputValue) {
30
31
  };
31
32
  }
32
33
 
33
- export async function readTextLines(inputValue, requestedOffset, requestedLimit) {
34
+ export async function readTextLines(inputValue, requestedOffset) {
34
35
  if (!Number.isSafeInteger(requestedOffset) || requestedOffset < 0) {
35
36
  throw Object.assign(new Error('offset must be a non-negative safe integer'), { code: -32602 });
36
37
  }
37
- if (!Number.isSafeInteger(requestedLimit) || requestedLimit < 1 || requestedLimit > 200) {
38
- throw Object.assign(new Error('limit must be a safe integer from 1 through 200'), { code: -32602 });
39
- }
40
38
  const observation = await observeText(inputValue);
41
39
  const source = await fileArtifact(observation.input);
42
40
  const offset = Math.min(requestedOffset, observation.lines.length);
43
- const selected = observation.lines.slice(offset, offset + requestedLimit);
41
+ const selected = observation.lines.slice(offset, offset + linePageLimit);
44
42
  const nextOffset = offset + selected.length < observation.lines.length
45
43
  ? offset + selected.length
46
44
  : null;