@tiwater/office-mcp 0.21.38 → 0.21.39

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);
@@ -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
+ }
@@ -0,0 +1,49 @@
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
+ "limit": {
31
+ "type": "integer",
32
+ "minimum": 1,
33
+ "maximum": 4,
34
+ "description": "Maximum text segments in this bounded page."
35
+ },
36
+ "returnContent": {
37
+ "type": "boolean",
38
+ "description": "Return this selected technical page directly when it fits the response limit. May be combined with output."
39
+ },
40
+ "output": {
41
+ "type": "string",
42
+ "minLength": 1,
43
+ "description": "Optional immutable JSON artifact path for this selected technical page. May be combined with returnContent.",
44
+ "x-tiwater-file-role": "write"
45
+ }
46
+ },
47
+ "required": ["input", "slideNumber", "shapeId", "limit", "returnContent"],
48
+ "additionalProperties": false
49
+ }
@@ -0,0 +1,43 @@
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
+ "limit": {
25
+ "type": "integer",
26
+ "minimum": 1,
27
+ "maximum": 8,
28
+ "description": "Maximum shapes in this bounded page."
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
+ }
40
+ },
41
+ "required": ["input", "slideNumber", "limit", "returnContent"],
42
+ "additionalProperties": false
43
+ }
@@ -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.39"
6
6
  },
7
7
  "tools": [
8
8
  {
@@ -38,6 +38,17 @@
38
38
  "sha256": "a6d59ee785d8ab3994eb6f539b48411b272565ac136219de58d39a449b3112f3"
39
39
  }
40
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"
50
+ }
51
+ },
41
52
  {
42
53
  "name": "docx_delete_object",
43
54
  "providerContract": {
@@ -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": "63bf1819f95514dad3d6f43a58795aa8b57bf9d1f7075edcfb11ddd3253b83e4"
354
+ },
355
+ "inputContract": {
356
+ "path": "office/contracts/pptx_read_shape.schema.json",
357
+ "sha256": "63bf1819f95514dad3d6f43a58795aa8b57bf9d1f7075edcfb11ddd3253b83e4"
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": "3a2aaca3fd11da490863e1a2d60bf2f120e4e24991b457e316de04b5c9cc889a"
365
+ },
366
+ "inputContract": {
367
+ "path": "office/contracts/pptx_read_slide.schema.json",
368
+ "sha256": "3a2aaca3fd11da490863e1a2d60bf2f120e4e24991b457e316de04b5c9cc889a"
369
+ }
370
+ },
338
371
  {
339
372
  "name": "pptx_replace_picture_image",
340
373
  "providerContract": {
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',
@@ -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. 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. 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.39",
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.39"
6
6
  },
7
7
  "runtime": {
8
8
  "command": "tiwater-pdf"
@@ -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.39"
6
6
  },
7
7
  "tools": [
8
8
  {