@tiwater/office-mcp 0.21.31 → 0.21.32
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/_shared/effect-kind.mjs +72 -0
- package/office/contracts/tiwater-office-provider-contract-manifest-v1.json +1 -1
- package/office/index.mjs +30 -1
- package/package.json +2 -1
- package/pdf/contracts/tiwater-pdf-provider-contract-manifest-v1.json +1 -1
- package/text/contracts/tiwater-text-provider-contract-manifest-v1.json +1 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { evidenceRoleMetadataKey } from './evidence-role.mjs';
|
|
2
|
+
|
|
3
|
+
export const effectKindMetadataKey = 'x-tiwater-effect-kind';
|
|
4
|
+
export const effectKindSchema = 'tiwater.provider-effect-kind/v1';
|
|
5
|
+
|
|
6
|
+
const effectKinds = new Set([
|
|
7
|
+
'document-mutation',
|
|
8
|
+
'source-conversion',
|
|
9
|
+
'native-render',
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
export function effectKindMetadata(kind) {
|
|
13
|
+
if (!effectKinds.has(kind)) throw new Error(`unsupported-effect-kind:${kind}`);
|
|
14
|
+
return {
|
|
15
|
+
[effectKindMetadataKey]: {
|
|
16
|
+
schema: effectKindSchema,
|
|
17
|
+
kind,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function assertEffectKindToolContract(tool, expectedKind = undefined) {
|
|
23
|
+
const metadata = tool?._meta?.[effectKindMetadataKey];
|
|
24
|
+
const bindings = fileBindings(tool?.inputSchema);
|
|
25
|
+
const effectiveWrites = bindings.filter(binding => binding.role === 'write' && binding.effect);
|
|
26
|
+
const readOnly = tool?.annotations?.readOnlyHint === true;
|
|
27
|
+
|
|
28
|
+
if (readOnly || effectiveWrites.length === 0) {
|
|
29
|
+
if (metadata !== undefined) throw new Error(`effect-kind-unexpected:${tool?.name || 'unnamed'}`);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
if (!metadata || Object.keys(metadata).sort().join(',') !== 'kind,schema'
|
|
33
|
+
|| metadata.schema !== effectKindSchema || !effectKinds.has(metadata.kind)) {
|
|
34
|
+
throw new Error(`effect-kind-metadata-invalid:${tool?.name || 'unnamed'}`);
|
|
35
|
+
}
|
|
36
|
+
if (expectedKind !== undefined && metadata.kind !== expectedKind) {
|
|
37
|
+
throw new Error(`effect-kind-mismatch:${tool?.name || 'unnamed'}:${metadata.kind}`);
|
|
38
|
+
}
|
|
39
|
+
if (!bindings.some(binding => binding.role === 'read')) {
|
|
40
|
+
throw new Error(`effect-kind-source-binding-missing:${tool?.name || 'unnamed'}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const evidenceRole = tool?._meta?.[evidenceRoleMetadataKey]?.role;
|
|
44
|
+
if ((metadata.kind === 'native-render') !== (evidenceRole === 'native-render')) {
|
|
45
|
+
throw new Error(`native-render-effect-evidence-mismatch:${tool?.name || 'unnamed'}`);
|
|
46
|
+
}
|
|
47
|
+
if (metadata.kind !== 'native-render' && evidenceRole !== undefined) {
|
|
48
|
+
throw new Error(`effect-kind-evidence-role-conflict:${tool?.name || 'unnamed'}`);
|
|
49
|
+
}
|
|
50
|
+
return metadata.kind;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function fileBindings(schema) {
|
|
54
|
+
const bindings = [];
|
|
55
|
+
function visit(node) {
|
|
56
|
+
if (!node || typeof node !== 'object' || Array.isArray(node)) return;
|
|
57
|
+
if (node['x-tiwater-file-role'] === 'read' || node['x-tiwater-file-role'] === 'write') {
|
|
58
|
+
bindings.push({
|
|
59
|
+
role: node['x-tiwater-file-role'],
|
|
60
|
+
effect: node['x-tiwater-file-role'] === 'write'
|
|
61
|
+
&& node['x-tiwater-file-effect'] !== false,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
for (const child of Object.values(node.properties || {})) visit(child);
|
|
65
|
+
if (node.items) visit(node.items);
|
|
66
|
+
for (const keyword of ['allOf', 'anyOf', 'oneOf']) {
|
|
67
|
+
for (const child of node[keyword] || []) visit(child);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
visit(schema);
|
|
71
|
+
return bindings;
|
|
72
|
+
}
|
package/office/index.mjs
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from '../_shared/large-json-result.mjs';
|
|
25
25
|
import { withOutputWriteLock } from '../_shared/output-write-lock.mjs';
|
|
26
26
|
import { evidenceRoleMetadata } from '../_shared/evidence-role.mjs';
|
|
27
|
+
import { effectKindMetadata } from '../_shared/effect-kind.mjs';
|
|
27
28
|
import { compactDocxObjectIdentity } from './docx-object-identity.mjs';
|
|
28
29
|
|
|
29
30
|
const packageMetadata = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
|
|
@@ -153,6 +154,7 @@ const xlsxFixedTools = [
|
|
|
153
154
|
function fixedToolDefinitions(definitions) {
|
|
154
155
|
return definitions.map(definition => ({
|
|
155
156
|
name: definition.name,
|
|
157
|
+
effectKind: 'document-mutation',
|
|
156
158
|
description: definition.description,
|
|
157
159
|
inputSchema: inputContract(definition.name),
|
|
158
160
|
outputSchema: fixedEditOutput(definition.name),
|
|
@@ -466,6 +468,7 @@ const tools = [
|
|
|
466
468
|
},
|
|
467
469
|
{
|
|
468
470
|
name: 'docx_replace_content_from_source',
|
|
471
|
+
effectKind: 'document-mutation',
|
|
469
472
|
description: 'Replace existing target paragraph or table-cell content from explicitly selected native source cells, paragraphs, runs, text nodes, or exact text ranges while retaining target container formatting and table structure. For a source cell already returned by docx_read_table, select a Unicode-scalar range directly against its returned text; the provider retains every crossed native run, including superscript and subscript, without another descendant read. Consecutive run or text selections from one source paragraph form one target paragraph, and source paragraph boundaries remain paragraph boundaries. Use it after docx_fill_table_from_tables when the target needs selected source content instead of the whole source cell; pass returned addresses unchanged and never retype source-owned text. It does not copy source rows, cells, spans, or merges.',
|
|
470
473
|
inputSchema: inputContract('docx_replace_content_from_source'),
|
|
471
474
|
outputSchema: fixedEditOutput('docx_replace_content_from_source'),
|
|
@@ -473,6 +476,7 @@ const tools = [
|
|
|
473
476
|
},
|
|
474
477
|
{
|
|
475
478
|
name: 'docx_set_text',
|
|
479
|
+
effectKind: 'document-mutation',
|
|
476
480
|
description: 'Replace the whole text content of paragraph or cell objects observed from this exact input DOCX while retaining target formatting, bookmarks, spans, and vertical merges. For a vertically merged logical cell, write its visible text to the restart cell rather than a continue cell. Tabs and line breaks remain native document text controls; targets containing non-text objects are rejected. Use this only for newly derived text. Content copied or selected from a source DOCX uses docx_replace_content_from_source so native runs such as superscript and subscript are retained. This does not insert objects, change table structure, copy source formatting, or decide business wording.',
|
|
477
481
|
inputSchema: inputContract('docx_set_text'),
|
|
478
482
|
outputSchema: fixedEditOutput('docx_set_text'),
|
|
@@ -480,6 +484,7 @@ const tools = [
|
|
|
480
484
|
},
|
|
481
485
|
{
|
|
482
486
|
name: 'docx_set_paragraph_pagination',
|
|
487
|
+
effectKind: 'document-mutation',
|
|
483
488
|
description: 'Set native pagination properties on explicitly selected current DOCX paragraphs. Each change sets at least one pagination property. keepWithNext keeps a paragraph with the immediately following paragraph or table but does not guarantee that a table header remains with its first body row. keepLinesTogether keeps one paragraph on one page; pageBreakBefore starts it on a new page; preventWidowOrphanLines controls isolated first or last lines. Omitted properties remain unchanged. The caller chooses paragraphs from current native addresses; the provider does not decide document layout or business meaning.',
|
|
484
489
|
inputSchema: inputContract('docx_set_paragraph_pagination'),
|
|
485
490
|
outputSchema: fixedEditOutput('docx_set_paragraph_pagination'),
|
|
@@ -487,6 +492,7 @@ const tools = [
|
|
|
487
492
|
},
|
|
488
493
|
{
|
|
489
494
|
name: 'docx_set_table_body',
|
|
495
|
+
effectKind: 'document-mutation',
|
|
490
496
|
description: 'Atomically replace one exact current target-table row range while retaining the table, target styles, grid widths, and all rows and surrounding content outside the range. When retaining leading rows reported with repeatHeader=true, existingRows starts after all of them and never at a verticalMerge=continue row. Name every target grid column in native order and choose one current row inside existingRows as the style prototype for each final row. Horizontal spans use contiguous column IDs. Set rowSpan on one logical cell to occupy multiple rows and omit those columns from the covered rows; the provider writes native vertical merge cells. Set cantSplit on a final physical row when it must remain whole across page boundaries; omit it to preserve the prototype row property. Every other grid column remains explicit. Every explicit cell includes an already-derived value; source-owned content is not retyped here. An empty final row array removes the range when another table row remains. The provider commits once and returns structural readback. It does not read source tables, map source to target, derive text, choose business columns, identify headers, or accept non-text cell content; use docx_fill_table_from_tables and docx_replace_content_from_source for source-owned table content.',
|
|
491
497
|
inputSchema: inputContract('docx_set_table_body'),
|
|
492
498
|
outputSchema: fixedEditOutput('docx_set_table_body'),
|
|
@@ -494,6 +500,7 @@ const tools = [
|
|
|
494
500
|
},
|
|
495
501
|
{
|
|
496
502
|
name: 'docx_fill_table_from_tables',
|
|
503
|
+
effectKind: 'document-mutation',
|
|
497
504
|
description: 'Fill one current target-table body from one or more explicitly ordered current source-table row ranges. For each source, select an unmerged record column when every physical source row must remain an output row; mapped columns still retain their native vertical merges. Select a merged record column only when every mapping into one target cell has one equal scalar value throughout that merge group. Map source grid columns onto every target prototype cell. The provider concatenates source records in declared order, copies each mapped source cell in full, preserves horizontal spans, rebuilds vertical merges only within each source range, validates the complete target grid, commits once, and returns structural readback. It does not discover source tables, choose source or target business meaning, filter records, translate or rewrite text, or apply business-specific rules. A single source uses the same sources array with one item. If a target needs only selected source descendants, such as one language from a bilingual cell, immediately use the returned target-cell addresses with docx_replace_content_from_source before releasing that source or reading back the completed target.',
|
|
498
505
|
inputSchema: inputContract('docx_fill_table_from_tables'),
|
|
499
506
|
outputSchema: fixedEditOutput('docx_fill_table_from_tables'),
|
|
@@ -501,6 +508,7 @@ const tools = [
|
|
|
501
508
|
},
|
|
502
509
|
{
|
|
503
510
|
name: 'docx_insert_objects',
|
|
511
|
+
effectKind: 'document-mutation',
|
|
504
512
|
description: 'Insert selected current DOCX objects under an existing parent. Table rows are objects: expand a target table by copying one contiguous observed row range and use repeat for count; sourceInput may equal input. A row range beginning with vertical-merge continuations may be inserted only inside a target boundary with the same active grid spans, which extends those merges. Individual table cells are not raw insertion targets because that would bypass the table grid.',
|
|
505
513
|
inputSchema: inputContract('docx_insert_objects'),
|
|
506
514
|
outputSchema: fixedEditOutput('docx_insert_objects'),
|
|
@@ -508,6 +516,7 @@ const tools = [
|
|
|
508
516
|
},
|
|
509
517
|
{
|
|
510
518
|
name: 'docx_delete_object',
|
|
519
|
+
effectKind: 'document-mutation',
|
|
511
520
|
description: 'Delete selected current DOCX objects directly from the current target document. Selected table rows must close every vertical merge and cannot remove the whole table. Individual table cells are not raw deletion targets; use column or merge operations for table structure.',
|
|
512
521
|
inputSchema: inputContract('docx_delete_object'),
|
|
513
522
|
outputSchema: fixedEditOutput('docx_delete_object'),
|
|
@@ -515,6 +524,7 @@ const tools = [
|
|
|
515
524
|
},
|
|
516
525
|
{
|
|
517
526
|
name: 'docx_merge_cells',
|
|
527
|
+
effectKind: 'document-mutation',
|
|
518
528
|
description: 'Merge selected current DOCX cells when they form one closed rectangle. A one-column, multi-row rectangle creates a vertical merge whose first cell is the restart owner and whose later cells are continuations. All selected cell content moves into the top-left owner, so the selected content must already be correct for that one logical cell.',
|
|
519
529
|
inputSchema: inputContract('docx_merge_cells'),
|
|
520
530
|
outputSchema: fixedEditOutput('docx_merge_cells'),
|
|
@@ -522,6 +532,7 @@ const tools = [
|
|
|
522
532
|
},
|
|
523
533
|
{
|
|
524
534
|
name: 'docx_split_cells',
|
|
535
|
+
effectKind: 'document-mutation',
|
|
525
536
|
description: 'Split selected current DOCX merged cells.',
|
|
526
537
|
inputSchema: inputContract('docx_split_cells'),
|
|
527
538
|
outputSchema: fixedEditOutput('docx_split_cells'),
|
|
@@ -529,6 +540,7 @@ const tools = [
|
|
|
529
540
|
},
|
|
530
541
|
{
|
|
531
542
|
name: 'docx_insert_table_columns',
|
|
543
|
+
effectKind: 'document-mutation',
|
|
532
544
|
description: 'Insert empty template-shaped grid columns into one current main-document table. Select an observed source grid column for width and per-row cell formatting, and optionally a before grid-column address; cells spanning the insertion boundary expand instead of being split. It does not copy business values or decide column meaning.',
|
|
533
545
|
inputSchema: inputContract('docx_insert_table_columns'),
|
|
534
546
|
outputSchema: fixedEditOutput('docx_insert_table_columns'),
|
|
@@ -536,6 +548,7 @@ const tools = [
|
|
|
536
548
|
},
|
|
537
549
|
{
|
|
538
550
|
name: 'docx_delete_table_columns',
|
|
551
|
+
effectKind: 'document-mutation',
|
|
539
552
|
description: 'Delete selected observed grid columns from one current main-document table while shrinking spanning cells and preserving the remaining table grid. It cannot remove every column and does not decide whether a business column is unused.',
|
|
540
553
|
inputSchema: inputContract('docx_delete_table_columns'),
|
|
541
554
|
outputSchema: fixedEditOutput('docx_delete_table_columns'),
|
|
@@ -576,6 +589,7 @@ const tools = [
|
|
|
576
589
|
},
|
|
577
590
|
{
|
|
578
591
|
name: 'docx_apply_font_policy',
|
|
592
|
+
effectKind: 'document-mutation',
|
|
579
593
|
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.',
|
|
580
594
|
inputSchema: inputContract('docx_apply_font_policy'),
|
|
581
595
|
outputSchema: fixedEditOutput('docx_apply_font_policy'),
|
|
@@ -591,6 +605,7 @@ const tools = [
|
|
|
591
605
|
},
|
|
592
606
|
{
|
|
593
607
|
name: 'docx_apply_toc_style_policy',
|
|
608
|
+
effectKind: 'document-mutation',
|
|
594
609
|
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.',
|
|
595
610
|
inputSchema: inputContract('docx_apply_toc_style_policy'),
|
|
596
611
|
outputSchema: fixedEditOutput('docx_apply_toc_style_policy'),
|
|
@@ -598,6 +613,7 @@ const tools = [
|
|
|
598
613
|
},
|
|
599
614
|
{
|
|
600
615
|
name: 'docx_refresh_fields',
|
|
616
|
+
effectKind: 'document-mutation',
|
|
601
617
|
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.',
|
|
602
618
|
inputSchema: inputContract('docx_refresh_fields'),
|
|
603
619
|
outputSchema: docxFieldRefreshOutput,
|
|
@@ -605,12 +621,14 @@ const tools = [
|
|
|
605
621
|
},
|
|
606
622
|
{
|
|
607
623
|
name: 'docx_strip_direct_formatting',
|
|
624
|
+
effectKind: 'document-mutation',
|
|
608
625
|
description: 'Remove direct paragraph and run formatting while preserving styles.',
|
|
609
626
|
inputSchema: inputContract('docx_strip_direct_formatting'),
|
|
610
627
|
handler: docxStripDirectFormatting,
|
|
611
628
|
},
|
|
612
629
|
{
|
|
613
630
|
name: 'docx_replace_style_ids',
|
|
631
|
+
effectKind: 'document-mutation',
|
|
614
632
|
description: 'Replace current DOCX style IDs from an explicit style map.',
|
|
615
633
|
inputSchema: inputContract('docx_replace_style_ids'),
|
|
616
634
|
handler: docxReplaceStyleIds,
|
|
@@ -618,6 +636,7 @@ const tools = [
|
|
|
618
636
|
{
|
|
619
637
|
name: 'office_render_pdf',
|
|
620
638
|
evidenceRole: 'native-render',
|
|
639
|
+
effectKind: 'native-render',
|
|
621
640
|
description: 'Render a current Office document to PDF with its required native WPS backend and write the complete provider receipt as evidence. The input extension selects Writer, Spreadsheets, or Presentation; fallback rendering is rejected.',
|
|
622
641
|
inputSchema: inputContract('office_render_pdf'),
|
|
623
642
|
outputSchema: nativeRenderOutput,
|
|
@@ -626,6 +645,7 @@ const tools = [
|
|
|
626
645
|
},
|
|
627
646
|
{
|
|
628
647
|
name: 'xlsx_convert_legacy',
|
|
648
|
+
effectKind: 'source-conversion',
|
|
629
649
|
description: 'Convert a current legacy XLS workbook to XLSX using the published native ET backend.',
|
|
630
650
|
inputSchema: inputContract('xlsx_convert_legacy'),
|
|
631
651
|
handler: xlsxConvertLegacy,
|
|
@@ -685,6 +705,7 @@ const tools = [
|
|
|
685
705
|
},
|
|
686
706
|
{
|
|
687
707
|
name: 'pptx_apply_template',
|
|
708
|
+
effectKind: 'document-mutation',
|
|
688
709
|
description: 'Apply one deterministic PPTX template-application plan to a current presentation. This tool executes the published plan; it does not select a template or derive business content, slide mappings, geometry, or formatting decisions.',
|
|
689
710
|
inputSchema: inputContract('pptx_apply_template'),
|
|
690
711
|
outputSchema: fixedEditOutput('pptx_apply_template'),
|
|
@@ -692,6 +713,7 @@ const tools = [
|
|
|
692
713
|
},
|
|
693
714
|
{
|
|
694
715
|
name: 'pptx_apply_format',
|
|
716
|
+
effectKind: 'document-mutation',
|
|
695
717
|
description: 'Apply one deterministic PPTX formatting plan to a current presentation. This tool executes published formatting operations; it does not derive values, coordinates, or business decisions.',
|
|
696
718
|
inputSchema: inputContract('pptx_apply_format'),
|
|
697
719
|
outputSchema: fixedEditOutput('pptx_apply_format'),
|
|
@@ -699,6 +721,7 @@ const tools = [
|
|
|
699
721
|
},
|
|
700
722
|
{
|
|
701
723
|
name: 'pptx_set_shape_geometry',
|
|
724
|
+
effectKind: 'document-mutation',
|
|
702
725
|
description: 'Set exact native EMU bounds for uniquely identified current-slide PPTX objects. One call batches only this fixed geometry action and does not infer repair coordinates.',
|
|
703
726
|
inputSchema: inputContract('pptx_set_shape_geometry'),
|
|
704
727
|
outputSchema: fixedEditOutput('pptx_set_shape_geometry'),
|
|
@@ -706,6 +729,7 @@ const tools = [
|
|
|
706
729
|
},
|
|
707
730
|
{
|
|
708
731
|
name: 'pptx_replace_picture_image',
|
|
732
|
+
effectKind: 'document-mutation',
|
|
709
733
|
description: 'Replace embedded PNG or JPEG media for uniquely identified current-slide PPTX pictures while preserving the picture object, geometry, crop, and unrelated media. One call batches only this fixed replacement action.',
|
|
710
734
|
inputSchema: inputContract('pptx_replace_picture_image'),
|
|
711
735
|
outputSchema: fixedEditOutput('pptx_replace_picture_image'),
|
|
@@ -736,7 +760,12 @@ function buildServer() {
|
|
|
736
760
|
inputSchema: tool.inputSchema,
|
|
737
761
|
...(tool.outputSchema ? { outputSchema: tool.outputSchema } : {}),
|
|
738
762
|
...(tool.annotations ? { annotations: tool.annotations } : {}),
|
|
739
|
-
...(tool.evidenceRole
|
|
763
|
+
...((tool.evidenceRole || tool.effectKind) ? {
|
|
764
|
+
_meta: {
|
|
765
|
+
...(tool.evidenceRole ? evidenceRoleMetadata(tool.evidenceRole) : {}),
|
|
766
|
+
...(tool.effectKind ? effectKindMetadata(tool.effectKind) : {}),
|
|
767
|
+
},
|
|
768
|
+
} : {}),
|
|
740
769
|
},
|
|
741
770
|
async args => {
|
|
742
771
|
const payload = typeof args.output === 'string'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiwater/office-mcp",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.32",
|
|
4
4
|
"description": "Published MCP distribution for independent Tiwater Office, PDF, and Text document capabilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"_shared/output-write-lock.mjs",
|
|
24
24
|
"_shared/mcp-stdio.mjs",
|
|
25
25
|
"_shared/evidence-role.mjs",
|
|
26
|
+
"_shared/effect-kind.mjs",
|
|
26
27
|
"office/index.mjs",
|
|
27
28
|
"office/docx-object-identity.mjs",
|
|
28
29
|
"office/README.md",
|