@tiwater/office-mcp 0.21.35 → 0.21.37

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.
@@ -21,17 +21,10 @@
21
21
  "required": ["part", "path"],
22
22
  "additionalProperties": false
23
23
  },
24
- "limit": {
25
- "type": "integer",
26
- "minimum": 1,
27
- "maximum": 9007199254740991,
28
- "description": "Maximum number of table rows requested in this response page. The provider may return fewer to keep the machine response bounded."
29
- },
30
- "offset": {
31
- "type": "integer",
32
- "minimum": 0,
33
- "maximum": 9007199254740991,
34
- "description": "Zero-based row offset. Continue only when an unreturned row is needed for the current operation; unused blank template rows need not be read."
24
+ "continuation": {
25
+ "type": "string",
26
+ "minLength": 1,
27
+ "description": "Opaque continuation returned by the preceding page. Pass it back unchanged; omit it for the first page."
35
28
  },
36
29
  "output": {
37
30
  "type": "string",
@@ -6,12 +6,13 @@
6
6
  "type": "string",
7
7
  "minLength": 1,
8
8
  "description": "Current target DOCX containing every target paragraph or table cell.",
9
- "x-tiwater-file-role": "read", "x-tiwater-document-revision-role": "current"
9
+ "x-tiwater-file-role": "read",
10
+ "x-tiwater-document-revision-role": "current"
10
11
  },
11
12
  "changes": {
12
13
  "minItems": 1,
13
14
  "type": "array",
14
- "description": "Atomic source-to-target content replacements. Every source selection is resolved and validated before the target document is changed.",
15
+ "description": "Replace one or more existing target paragraphs or cells with explicit native source selections in one atomic commit. This preserves selected native runs but does not change rows, spans, or merges.",
15
16
  "items": {
16
17
  "type": "object",
17
18
  "properties": {
@@ -44,7 +45,7 @@
44
45
  "sourceSelections": {
45
46
  "minItems": 1,
46
47
  "type": "array",
47
- "description": "Ordered native source cells, paragraphs, runs, or text nodes. Consecutive run or text selections from one source paragraph form one target paragraph; a range retains the native runs it crosses. A source paragraph boundary remains a target paragraph boundary. Selected content becomes the target content without Agent transcription.",
48
+ "description": "Ordered native source cells, paragraphs, runs, or text nodes. The target becomes exactly these selections; omitted source content is omitted. When excluding parallel-language text, explicitly include every required language-neutral formula, number, unit, and symbol in source order. Consecutive selections from one source paragraph form one target paragraph; a range retains the native runs it crosses. A source paragraph boundary remains a target paragraph boundary. Selected content becomes the target content without Agent transcription.",
48
49
  "items": {
49
50
  "type": "object",
50
51
  "properties": {
@@ -0,0 +1,100 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "tiwater.office-mcp-input/docx_set_table",
4
+ "type": "object",
5
+ "properties": {
6
+ "input": { "type": "string", "minLength": 1, "x-tiwater-file-role": "read", "x-tiwater-document-revision-role": "current" },
7
+ "table": {
8
+ "type": "object",
9
+ "description": "Current target table address returned by docx_read_table or docx_table_index.",
10
+ "properties": {
11
+ "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
12
+ "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
13
+ },
14
+ "required": ["part", "path"], "additionalProperties": false
15
+ },
16
+ "existingRows": {
17
+ "type": "object",
18
+ "description": "Exact contiguous current row range to replace. Rows outside this range stay unchanged, and the range must enclose every vertical merge it intersects.",
19
+ "properties": {
20
+ "first": {
21
+ "type": "object", "properties": {
22
+ "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
23
+ "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
24
+ }, "required": ["part", "path"], "additionalProperties": false
25
+ },
26
+ "last": {
27
+ "type": "object", "properties": {
28
+ "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
29
+ "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
30
+ }, "required": ["part", "path"], "additionalProperties": false
31
+ }
32
+ }, "required": ["first", "last"], "additionalProperties": false
33
+ },
34
+ "columns": {
35
+ "type": "array", "minItems": 1,
36
+ "description": "Complete target table grid in native order. IDs are request-local names used only by rows below.",
37
+ "items": {
38
+ "type": "object", "properties": {
39
+ "id": { "type": "string", "minLength": 1, "maxLength": 80 },
40
+ "gridColumn": {
41
+ "type": "object", "properties": {
42
+ "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
43
+ "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
44
+ }, "required": ["part", "path"], "additionalProperties": false
45
+ }
46
+ }, "required": ["id", "gridColumn"], "additionalProperties": false
47
+ }
48
+ },
49
+ "rows": {
50
+ "type": "array", "minItems": 0,
51
+ "description": "Final logical rows. Explicit cells plus active row spans cover the target grid exactly once.",
52
+ "items": {
53
+ "type": "object", "properties": {
54
+ "prototypeRow": {
55
+ "type": "object", "properties": {
56
+ "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
57
+ "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
58
+ }, "required": ["part", "path"], "additionalProperties": false
59
+ },
60
+ "cells": {
61
+ "type": "array", "minItems": 1,
62
+ "items": {
63
+ "type": "object", "properties": {
64
+ "columns": { "type": "array", "minItems": 1, "description": "One or more contiguous request-local column IDs occupied by this cell.", "items": { "type": "string", "minLength": 1, "maxLength": 80 } },
65
+ "rowSpan": { "type": "integer", "minimum": 1, "maximum": 2147483647 },
66
+ "text": { "anyOf": [{ "type": "string" }, { "type": "null" }], "description": "Final derived plain text, or null when sourceInput and sourceSelections are used." },
67
+ "sourceInput": { "type": "string", "minLength": 1, "description": "Current source DOCX containing sourceSelections. Omit when text is used.", "x-tiwater-file-role": "read" },
68
+ "sourceSelections": {
69
+ "type": "array", "minItems": 1,
70
+ "description": "Exact ordered native source selections. Omit when text is used.",
71
+ "items": {
72
+ "type": "object", "properties": {
73
+ "address": {
74
+ "type": "object", "properties": {
75
+ "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
76
+ "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
77
+ }, "required": ["part", "path"], "additionalProperties": false
78
+ },
79
+ "range": {
80
+ "type": "object", "properties": {
81
+ "start": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
82
+ "length": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 }
83
+ }, "required": ["start", "length"], "additionalProperties": false
84
+ }
85
+ }, "required": ["address"], "additionalProperties": false
86
+ }
87
+ }
88
+ }, "required": ["columns", "text"], "additionalProperties": false
89
+ }
90
+ },
91
+ "cantSplit": { "type": "boolean" }
92
+ }, "required": ["prototypeRow", "cells"], "additionalProperties": false
93
+ }
94
+ },
95
+ "output": { "type": "string", "minLength": 1, "description": "Output DOCX path; may equal input for one atomic in-place update.", "x-tiwater-file-role": "write" },
96
+ "receiptOutput": { "type": "string", "minLength": 1, "x-tiwater-file-role": "write", "x-tiwater-file-effect": false }
97
+ },
98
+ "required": ["input", "table", "existingRows", "columns", "rows", "output", "receiptOutput"],
99
+ "additionalProperties": false
100
+ }
@@ -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.35"
5
+ "version": "0.21.37"
6
6
  },
7
7
  "tools": [
8
8
  {
@@ -71,17 +71,6 @@
71
71
  "sha256": "995eb1a5a8992e2a2186f2d7621b5eea7875eecd78ce153e77964d2942db2211"
72
72
  }
73
73
  },
74
- {
75
- "name": "docx_fill_table_from_tables",
76
- "providerContract": {
77
- "source": "packages/docx-cli/contracts/mcp-input/docx_fill_table_from_tables.schema.json",
78
- "sha256": "81d335ffaf20b896ae043e96a5a83f1beb3debbaf7404a99c8b4f06a62989c7d"
79
- },
80
- "inputContract": {
81
- "path": "office/contracts/docx_fill_table_from_tables.schema.json",
82
- "sha256": "81d335ffaf20b896ae043e96a5a83f1beb3debbaf7404a99c8b4f06a62989c7d"
83
- }
84
- },
85
74
  {
86
75
  "name": "docx_insert_objects",
87
76
  "providerContract": {
@@ -152,11 +141,11 @@
152
141
  "name": "docx_read_table",
153
142
  "providerContract": {
154
143
  "source": "packages/docx-cli/contracts/mcp-input/docx_read_table.schema.json",
155
- "sha256": "6f69cef86035d3905cb84b2b9c4a6af3ac66179be4122f82543fe7acacef288f"
144
+ "sha256": "0b7a5669daeaa6f8e171ba01542b4bfd065cd282a32943e7b5b5854551b4afc7"
156
145
  },
157
146
  "inputContract": {
158
147
  "path": "office/contracts/docx_read_table.schema.json",
159
- "sha256": "6f69cef86035d3905cb84b2b9c4a6af3ac66179be4122f82543fe7acacef288f"
148
+ "sha256": "0b7a5669daeaa6f8e171ba01542b4bfd065cd282a32943e7b5b5854551b4afc7"
160
149
  }
161
150
  },
162
151
  {
@@ -174,11 +163,11 @@
174
163
  "name": "docx_replace_content_from_source",
175
164
  "providerContract": {
176
165
  "source": "packages/docx-cli/contracts/mcp-input/docx_replace_content_from_source.schema.json",
177
- "sha256": "beca665f61a19911e8c4cb17dc5bad27051c5561e8b31dd839f8825bee7e6385"
166
+ "sha256": "04740981ad30190be4625ce3a2640b48abec02a9bc0eccc3e0e7d4fe40dbc6e0"
178
167
  },
179
168
  "inputContract": {
180
169
  "path": "office/contracts/docx_replace_content_from_source.schema.json",
181
- "sha256": "beca665f61a19911e8c4cb17dc5bad27051c5561e8b31dd839f8825bee7e6385"
170
+ "sha256": "04740981ad30190be4625ce3a2640b48abec02a9bc0eccc3e0e7d4fe40dbc6e0"
182
171
  }
183
172
  },
184
173
  {
@@ -204,14 +193,14 @@
204
193
  }
205
194
  },
206
195
  {
207
- "name": "docx_set_table_body",
196
+ "name": "docx_set_table",
208
197
  "providerContract": {
209
- "source": "packages/docx-cli/contracts/mcp-input/docx_set_table_body.schema.json",
210
- "sha256": "a22d7d38cd3bad30372d45e289e3dc4700a3e52715fec990ac4644888e92ac5e"
198
+ "source": "packages/docx-cli/contracts/mcp-input/docx_set_table.schema.json",
199
+ "sha256": "17c0f3c64fe36bbb3658ee44ce2b505b25241f37d0c08957a8706bd610f8e160"
211
200
  },
212
201
  "inputContract": {
213
- "path": "office/contracts/docx_set_table_body.schema.json",
214
- "sha256": "a22d7d38cd3bad30372d45e289e3dc4700a3e52715fec990ac4644888e92ac5e"
202
+ "path": "office/contracts/docx_set_table.schema.json",
203
+ "sha256": "17c0f3c64fe36bbb3658ee44ce2b505b25241f37d0c08957a8706bd610f8e160"
215
204
  }
216
205
  },
217
206
  {
package/office/index.mjs CHANGED
@@ -381,9 +381,10 @@ const docxTableReadOutput = docxObservationOutput('docx_read_table').extend({
381
381
  receipt: z.object({
382
382
  schema: z.literal('tiwater.docx-table-page-receipt/v1'),
383
383
  totalRowCount: z.number().int().nonnegative(),
384
+ retainedRowCount: z.number().int().nonnegative(),
384
385
  returnedRowCount: z.number().int().nonnegative(),
385
386
  remaining: z.number().int().nonnegative(),
386
- nextOffset: z.number().int().nonnegative().optional(),
387
+ nextContinuation: z.string().min(1).optional(),
387
388
  detailPageRetained: z.boolean(),
388
389
  narrowingRequired: z.boolean(),
389
390
  }).strict(),
@@ -469,7 +470,7 @@ const tools = [
469
470
  },
470
471
  {
471
472
  name: 'docx_read_table',
472
- description: 'Read an explicit row range from exactly one table selected by native OpenXML address; it never builds another whole-table data object. Set returnContent true to return a byte-bounded compact row page. Provide output to store full paragraph and text-node detail, including native verticalTextAlignment, for the selected row page and return its artifact receipt. These channels are independent and may be used together; at least one is required. Request only rows needed for the current decision; receipt.remaining is navigation information, not an obligation to read unused rows, and blank template rows need not be paged through. receipt.nextOffset is present only when another row page exists. Each returned row and cell keeps its reusable native address, zero-based logical gridColumnStart, gridSpan, vertical-merge owner, physical text, and logical text. Match columns across rows by gridColumnStart; a tc[n] path or array position is only that row\'s physical cell ordinal and is not a column identity when gridSpan or gridBefore is present. In a vertical merge, restart begins one logical cell and a continue cell is not an independent row value: logicalText resolves the restart cell value while text remains the physical cell value. Use docx_read_object when one exact object needs a narrower descendant view. The provider reports physical structure only; the Agent decides template and business meaning.',
473
+ description: 'Read one explicit native DOCX table. Provide output to retain every remaining row with full paragraph and text-node detail. Set returnContent true to also receive the largest compact inline page within the response limit; these channels are independent and at least one is required. retainedRowCount counts artifact rows and returnedRowCount counts inline rows. When remaining is nonzero, continue only by passing receipt.nextContinuation unchanged in the next call; a continuation page depends on the preceding receipt and cannot be predicted or read in parallel. Match columns by gridColumnStart, not tc position. A vertical-merge restart owns one logical value; a continue cell points to verticalMergeOwner and does not repeat that value inline.',
473
474
  inputSchema: inputContract('docx_read_table'),
474
475
  outputSchema: docxTableReadOutput,
475
476
  annotations: { readOnlyHint: true, idempotentHint: true },
@@ -478,7 +479,7 @@ const tools = [
478
479
  {
479
480
  name: 'docx_replace_content_from_source',
480
481
  effectKind: 'document-mutation',
481
- 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.',
482
+ description: 'Atomically replace one or more existing target paragraphs or cells with exactly the selected native source content. Select source cells, paragraphs, runs, text nodes, or Unicode-scalar text ranges; omitted content is omitted, while selected runs retain superscript, subscript, formulas, numbers, units, and symbols. This tool does not change table rows, spans, or merges.',
482
483
  inputSchema: inputContract('docx_replace_content_from_source'),
483
484
  outputSchema: fixedEditOutput('docx_replace_content_from_source'),
484
485
  handler: (args, tool) => fixedEdit(tool, args, docxCandidates),
@@ -500,19 +501,11 @@ const tools = [
500
501
  handler: (args, tool) => fixedEdit(tool, args, docxCandidates),
501
502
  },
502
503
  {
503
- name: 'docx_set_table_body',
504
+ name: 'docx_set_table',
504
505
  effectKind: 'document-mutation',
505
- 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.',
506
- inputSchema: inputContract('docx_set_table_body'),
507
- outputSchema: fixedEditOutput('docx_set_table_body'),
508
- handler: (args, tool) => fixedEdit(tool, args, docxCandidates),
509
- },
510
- {
511
- name: 'docx_fill_table_from_tables',
512
- effectKind: 'document-mutation',
513
- 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.',
514
- inputSchema: inputContract('docx_fill_table_from_tables'),
515
- outputSchema: fixedEditOutput('docx_fill_table_from_tables'),
506
+ description: 'Atomically replace one exact current target-table row range with a fully specified table body. Name every target grid column in native order. Each explicit cell occupies contiguous columns and may span logical rows; covered columns are omitted from following rows. Set each cell from either derived text or sourceInput plus exact native sourceSelections, never both. Native source selections retain run formatting such as superscript and subscript. The provider retains the target table, target cell styles, grid widths, and all content outside the replaced range, and exposes no intermediate document. It does not select source rows, map business columns, infer target shape, derive wording, or copy a source table wholesale.',
507
+ inputSchema: inputContract('docx_set_table'),
508
+ outputSchema: fixedEditOutput('docx_set_table'),
516
509
  handler: (args, tool) => fixedEdit(tool, args, docxCandidates),
517
510
  },
518
511
  {
@@ -862,6 +855,34 @@ async function docxInspect(args) {
862
855
  };
863
856
  }
864
857
 
858
+ function encodeTableContinuation(input, table, offset) {
859
+ return Buffer.from(JSON.stringify({
860
+ schema: 'tiwater.docx-table-continuation/v1',
861
+ input,
862
+ table,
863
+ offset,
864
+ })).toString('base64url');
865
+ }
866
+
867
+ function decodeTableContinuation(continuation, input, table) {
868
+ if (continuation === undefined) return 0;
869
+ let decoded;
870
+ try {
871
+ decoded = JSON.parse(Buffer.from(continuation, 'base64url').toString('utf8'));
872
+ } catch {
873
+ throw new Error('invalid-docx-table-continuation');
874
+ }
875
+ if (decoded?.schema !== 'tiwater.docx-table-continuation/v1'
876
+ || decoded.input !== input
877
+ || decoded.table?.part !== table?.part
878
+ || decoded.table?.path !== table?.path
879
+ || !Number.isSafeInteger(decoded.offset)
880
+ || decoded.offset < 0) {
881
+ throw new Error('invalid-docx-table-continuation');
882
+ }
883
+ return decoded.offset;
884
+ }
885
+
865
886
  async function docxObservation(tool, args) {
866
887
  const input = path.resolve(requireString(args.input, 'input'));
867
888
  const delivery = resultChannels(args);
@@ -956,18 +977,20 @@ async function docxObservation(tool, args) {
956
977
  }
957
978
  if (tool === 'docx_read_table') {
958
979
  const totalRowCount = payload.rows.length;
959
- const offset = Math.min(args.offset ?? 0, totalRowCount);
960
- const requestedLimit = args.limit ?? totalRowCount;
961
- const selectedRows = payload.rows.slice(offset, offset + requestedLimit);
980
+ const offset = Math.min(decodeTableContinuation(args.continuation, input, args.table), totalRowCount);
981
+ const selectedRows = payload.rows.slice(offset);
962
982
  const selectedNextOffset = offset + selectedRows.length < totalRowCount
963
983
  ? offset + selectedRows.length
964
984
  : null;
965
985
  const selectedPageReceipt = {
966
986
  schema: 'tiwater.docx-table-page-receipt/v1',
967
987
  totalRowCount,
988
+ retainedRowCount: selectedRows.length,
968
989
  returnedRowCount: selectedRows.length,
969
990
  remaining: totalRowCount - offset - selectedRows.length,
970
- ...(selectedNextOffset === null ? {} : { nextOffset: selectedNextOffset }),
991
+ ...(selectedNextOffset === null ? {} : {
992
+ nextContinuation: encodeTableContinuation(input, args.table, selectedNextOffset),
993
+ }),
971
994
  };
972
995
  const detailPage = {
973
996
  schema: 'tiwater.docx-table-detail-page/v1',
@@ -996,6 +1019,7 @@ async function docxObservation(tool, args) {
996
1019
  followingParagraph: payload.followingParagraph,
997
1020
  receipt: {
998
1021
  ...selectedPageReceipt,
1022
+ returnedRowCount: 0,
999
1023
  detailPageRetained: true,
1000
1024
  narrowingRequired: false,
1001
1025
  },
@@ -1017,7 +1041,7 @@ async function docxObservation(tool, args) {
1017
1041
  verticalMerge: cell.verticalMerge,
1018
1042
  verticalMergeOwner: cell.verticalMergeOwner,
1019
1043
  text: cell.paragraphs.map(paragraph => paragraph.text).join('\n'),
1020
- logicalText: cell.logicalText,
1044
+ logicalText: cell.verticalMerge === 'continue' ? '' : cell.logicalText,
1021
1045
  })),
1022
1046
  };
1023
1047
  const candidate = [...rows, row];
@@ -1027,15 +1051,19 @@ async function docxObservation(tool, args) {
1027
1051
  }
1028
1052
  rows.push(row);
1029
1053
  }
1030
- const nextOffset = !narrowingRequired && offset + rows.length < totalRowCount
1054
+ const retainedRowCount = delivery.output === null ? rows.length : selectedRows.length;
1055
+ const inlineNextOffset = offset + rows.length < totalRowCount
1031
1056
  ? offset + rows.length
1032
1057
  : null;
1033
1058
  const pageReceipt = {
1034
1059
  schema: 'tiwater.docx-table-page-receipt/v1',
1035
1060
  totalRowCount,
1061
+ retainedRowCount,
1036
1062
  returnedRowCount: rows.length,
1037
1063
  remaining: totalRowCount - offset - rows.length,
1038
- ...(nextOffset === null ? {} : { nextOffset }),
1064
+ ...(inlineNextOffset === null ? {} : {
1065
+ nextContinuation: encodeTableContinuation(input, args.table, inlineNextOffset),
1066
+ }),
1039
1067
  };
1040
1068
  return {
1041
1069
  ...withArtifact,
@@ -1049,7 +1077,7 @@ async function docxObservation(tool, args) {
1049
1077
  receipt: {
1050
1078
  ...pageReceipt,
1051
1079
  detailPageRetained: delivery.output !== null,
1052
- narrowingRequired,
1080
+ narrowingRequired: delivery.output === null && narrowingRequired,
1053
1081
  },
1054
1082
  rows,
1055
1083
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiwater/office-mcp",
3
- "version": "0.21.35",
3
+ "version": "0.21.37",
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.35"
5
+ "version": "0.21.37"
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.35"
5
+ "version": "0.21.37"
6
6
  },
7
7
  "tools": [
8
8
  {
@@ -1,162 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "tiwater.office-mcp-input/docx_fill_table_from_tables",
4
- "description": "Project source-table rows into one target template table atomically. Use this instead of manually rebuilding rows when target cells derive from source tables. Target formatting is reused, and native vertical-merge owners are preserved within each source range.",
5
- "type": "object",
6
- "properties": {
7
- "input": { "type": "string", "minLength": 1, "description": "Current target DOCX.", "x-tiwater-file-role": "read", "x-tiwater-document-revision-role": "current" },
8
- "table": {
9
- "type": "object",
10
- "description": "Current target table address returned by docx_read_table or docx_table_index.",
11
- "properties": {
12
- "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
13
- "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
14
- },
15
- "required": ["part", "path"],
16
- "additionalProperties": false
17
- },
18
- "existingRows": {
19
- "type": "object",
20
- "description": "Exact contiguous current target row range to replace; it must enclose every vertical merge it intersects.",
21
- "properties": {
22
- "first": {
23
- "type": "object",
24
- "properties": {
25
- "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
26
- "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
27
- },
28
- "required": ["part", "path"],
29
- "additionalProperties": false
30
- },
31
- "last": {
32
- "type": "object",
33
- "properties": {
34
- "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
35
- "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
36
- },
37
- "required": ["part", "path"],
38
- "additionalProperties": false
39
- }
40
- },
41
- "required": ["first", "last"],
42
- "additionalProperties": false
43
- },
44
- "prototypeRow": {
45
- "type": "object",
46
- "description": "One current row inside existingRows whose cell formatting is reused for every generated body row.",
47
- "properties": {
48
- "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
49
- "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
50
- },
51
- "required": ["part", "path"],
52
- "additionalProperties": false
53
- },
54
- "sources": {
55
- "type": "array",
56
- "minItems": 1,
57
- "description": "Ordered source-table ranges whose logical records are concatenated into the target body. Source boundaries never create a vertical merge with one another.",
58
- "items": {
59
- "type": "object",
60
- "properties": {
61
- "input": { "type": "string", "minLength": 1, "description": "Current source DOCX.", "x-tiwater-file-role": "read" },
62
- "table": {
63
- "type": "object",
64
- "description": "Current source table address.",
65
- "properties": {
66
- "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
67
- "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
68
- },
69
- "required": ["part", "path"],
70
- "additionalProperties": false
71
- },
72
- "rows": {
73
- "type": "object",
74
- "description": "Exact contiguous source data-row range. Intersecting native vertical merges are clipped to this range, retain their owner content, and restart at the first selected row.",
75
- "properties": {
76
- "first": {
77
- "type": "object",
78
- "properties": {
79
- "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
80
- "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
81
- },
82
- "required": ["part", "path"],
83
- "additionalProperties": false
84
- },
85
- "last": {
86
- "type": "object",
87
- "properties": {
88
- "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
89
- "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
90
- },
91
- "required": ["part", "path"],
92
- "additionalProperties": false
93
- }
94
- },
95
- "required": ["first", "last"],
96
- "additionalProperties": false
97
- },
98
- "recordColumn": {
99
- "type": "object",
100
- "description": "Source grid-column address that defines output-row records. Choose an unmerged column when every physical source row must remain an output row; vertical merges in other mapped columns are still preserved. Choose a merged column only when every mapping into one target cell has one equal scalar value throughout that merge group.",
101
- "properties": {
102
- "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
103
- "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
104
- },
105
- "required": ["part", "path"],
106
- "additionalProperties": false
107
- },
108
- "columnMappings": {
109
- "type": "array",
110
- "minItems": 1,
111
- "description": "Complete projection from this source grid onto all target prototype cells. Multiple adjacent source columns may compose one target cell; one source column may spread its logical values across several target cells.",
112
- "items": {
113
- "type": "object",
114
- "properties": {
115
- "sourceColumns": {
116
- "type": "array",
117
- "minItems": 1,
118
- "description": "One or more contiguous source grid columns in native order. Multiple columns compose one target cell; a source cell spanning several selected columns contributes once.",
119
- "items": {
120
- "type": "object",
121
- "properties": {
122
- "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
123
- "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
124
- },
125
- "required": ["part", "path"],
126
- "additionalProperties": false
127
- }
128
- },
129
- "targetColumns": {
130
- "type": "array",
131
- "minItems": 1,
132
- "description": "One or more contiguous target grid columns covering complete prototype cells.",
133
- "items": {
134
- "type": "object",
135
- "properties": {
136
- "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
137
- "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
138
- },
139
- "required": ["part", "path"],
140
- "additionalProperties": false
141
- }
142
- },
143
- "joinWith": {
144
- "type": "string",
145
- "description": "Separator used when sourceColumns compose one target cell. Defaults to a newline."
146
- }
147
- },
148
- "required": ["sourceColumns", "targetColumns"],
149
- "additionalProperties": false
150
- }
151
- }
152
- },
153
- "required": ["input", "table", "rows", "recordColumn", "columnMappings"],
154
- "additionalProperties": false
155
- }
156
- },
157
- "output": { "type": "string", "minLength": 1, "description": "Output DOCX path; may equal input for one atomic in-place update.", "x-tiwater-file-role": "write" },
158
- "receiptOutput": { "type": "string", "minLength": 1, "x-tiwater-file-role": "write", "x-tiwater-file-effect": false }
159
- },
160
- "required": ["input", "table", "existingRows", "prototypeRow", "sources", "output", "receiptOutput"],
161
- "additionalProperties": false
162
- }
@@ -1,136 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "tiwater.office-mcp-input/docx_set_table_body",
4
- "type": "object",
5
- "properties": {
6
- "input": {
7
- "type": "string",
8
- "minLength": 1,
9
- "x-tiwater-file-role": "read", "x-tiwater-document-revision-role": "current"
10
- },
11
- "table": {
12
- "type": "object",
13
- "properties": {
14
- "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
15
- "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
16
- },
17
- "required": ["part", "path"],
18
- "additionalProperties": false,
19
- "description": "Current target table address returned by docx_read_table or docx_table_index."
20
- },
21
- "existingRows": {
22
- "type": "object",
23
- "description": "Exact contiguous current row range to replace. Rows outside this range stay unchanged, and the range must enclose every vertical merge it intersects.",
24
- "properties": {
25
- "first": {
26
- "type": "object",
27
- "properties": {
28
- "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
29
- "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
30
- },
31
- "required": ["part", "path"],
32
- "additionalProperties": false
33
- },
34
- "last": {
35
- "type": "object",
36
- "properties": {
37
- "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
38
- "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
39
- },
40
- "required": ["part", "path"],
41
- "additionalProperties": false
42
- }
43
- },
44
- "required": ["first", "last"],
45
- "additionalProperties": false
46
- },
47
- "columns": {
48
- "type": "array",
49
- "minItems": 1,
50
- "description": "Complete target table grid in the native order returned by docx_read_table. IDs are request-local names used only by rows below.",
51
- "items": {
52
- "type": "object",
53
- "properties": {
54
- "id": { "type": "string", "minLength": 1, "maxLength": 80 },
55
- "gridColumn": {
56
- "type": "object",
57
- "properties": {
58
- "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
59
- "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
60
- },
61
- "required": ["part", "path"],
62
- "additionalProperties": false
63
- }
64
- },
65
- "required": ["id", "gridColumn"],
66
- "additionalProperties": false
67
- }
68
- },
69
- "rows": {
70
- "type": "array",
71
- "minItems": 0,
72
- "description": "Final logical rows; an empty array removes the range when another row remains. Cells plus active row spans cover the complete target grid exactly once.",
73
- "items": {
74
- "type": "object",
75
- "properties": {
76
- "prototypeRow": {
77
- "type": "object",
78
- "properties": {
79
- "part": { "type": "string", "minLength": 1, "pattern": "^\\/" },
80
- "path": { "type": "string", "pattern": "^\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\](?:\\/[A-Za-z_][A-Za-z0-9_.-]*:[A-Za-z_][A-Za-z0-9_.-]*\\[[1-9][0-9]*\\])*$" }
81
- },
82
- "required": ["part", "path"],
83
- "additionalProperties": false
84
- },
85
- "cells": {
86
- "type": "array",
87
- "minItems": 1,
88
- "items": {
89
- "type": "object",
90
- "properties": {
91
- "columns": {
92
- "type": "array",
93
- "minItems": 1,
94
- "description": "One or more contiguous request-local column IDs occupied by this cell.",
95
- "items": { "type": "string", "minLength": 1, "maxLength": 80 }
96
- },
97
- "text": {
98
- "type": "string",
99
- "description": "Final plain text for this cell. If the current cell at the same row and grid span already has exactly this text, its native paragraphs and run formatting are retained while row and merge properties are updated."
100
- },
101
- "rowSpan": {
102
- "type": "integer",
103
- "minimum": 1,
104
- "maximum": 2147483647,
105
- "description": "Logical row count occupied by this cell; omitted means one. Later rows omit these columns."
106
- }
107
- },
108
- "required": ["columns", "text"],
109
- "additionalProperties": false
110
- }
111
- },
112
- "cantSplit": {
113
- "type": "boolean",
114
- "description": "Whether Word may split this physical row across pages. Omit to preserve the prototype row property."
115
- }
116
- },
117
- "required": ["prototypeRow", "cells"],
118
- "additionalProperties": false
119
- }
120
- },
121
- "output": {
122
- "type": "string",
123
- "minLength": 1,
124
- "description": "Output DOCX path; may equal input for one atomic in-place update.",
125
- "x-tiwater-file-role": "write"
126
- },
127
- "receiptOutput": {
128
- "type": "string",
129
- "minLength": 1,
130
- "x-tiwater-file-role": "write",
131
- "x-tiwater-file-effect": false
132
- }
133
- },
134
- "required": ["input", "table", "existingRows", "columns", "rows", "output", "receiptOutput"],
135
- "additionalProperties": false
136
- }