@tiwater/office-mcp 0.21.30 → 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/_shared/evidence-role.mjs +123 -0
- package/office/contracts/docx_export_json.schema.json +2 -1
- package/office/contracts/docx_inspect.schema.json +2 -1
- package/office/contracts/pptx_export_json.schema.json +2 -1
- package/office/contracts/pptx_inspect.schema.json +2 -1
- package/office/contracts/tiwater-office-provider-contract-manifest-v1.json +13 -13
- package/office/contracts/xlsx_export_json.schema.json +2 -1
- package/office/contracts/xlsx_inspect.schema.json +2 -1
- package/office/index.mjs +45 -6
- package/package.json +3 -1
- package/pdf/contracts/tiwater-pdf-provider-contract-manifest-v1.json +1 -1
- package/pdf/index.mjs +3 -0
- package/text/contracts/text_inspect.schema.json +2 -1
- package/text/contracts/tiwater-text-provider-contract-manifest-v1.json +3 -3
- package/text/index.mjs +3 -0
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
export const evidenceRoleMetadataKey = 'x-tiwater-evidence-role';
|
|
2
|
+
export const evidenceRoleSchema = 'tiwater.provider-evidence-role/v1';
|
|
3
|
+
|
|
4
|
+
const evidenceRoles = new Set([
|
|
5
|
+
'document-observation',
|
|
6
|
+
'final-readback',
|
|
7
|
+
'native-render',
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
export function evidenceRoleMetadata(role) {
|
|
11
|
+
if (!evidenceRoles.has(role)) throw new Error(`unsupported-evidence-role:${role}`);
|
|
12
|
+
return {
|
|
13
|
+
[evidenceRoleMetadataKey]: {
|
|
14
|
+
schema: evidenceRoleSchema,
|
|
15
|
+
role,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function assertEvidenceToolContract(tool, expectedRole) {
|
|
21
|
+
const metadata = tool?._meta?.[evidenceRoleMetadataKey];
|
|
22
|
+
if (!metadata || Object.keys(metadata).sort().join(',') !== 'role,schema'
|
|
23
|
+
|| metadata.schema !== evidenceRoleSchema || metadata.role !== expectedRole) {
|
|
24
|
+
throw new Error(`evidence-role-metadata-invalid:${tool?.name || 'unnamed'}`);
|
|
25
|
+
}
|
|
26
|
+
const bindings = fileBindings(tool.inputSchema);
|
|
27
|
+
if (!bindings.some(binding => binding.role === 'read')) {
|
|
28
|
+
throw new Error(`evidence-role-source-binding-missing:${tool.name}`);
|
|
29
|
+
}
|
|
30
|
+
if (!tool.outputSchema || typeof tool.outputSchema !== 'object') {
|
|
31
|
+
throw new Error(`evidence-role-output-schema-missing:${tool.name}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (expectedRole === 'native-render') {
|
|
35
|
+
assertAnnotations(tool, false, false);
|
|
36
|
+
if (!bindings.some(binding => binding.role === 'write' && binding.effect)
|
|
37
|
+
|| !bindings.some(binding => binding.role === 'write' && !binding.effect)
|
|
38
|
+
|| !requiredArtifact(tool.outputSchema, 'receipt')) {
|
|
39
|
+
throw new Error(`native-render-evidence-contract-invalid:${tool.name}`);
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
assertAnnotations(tool, true, true);
|
|
45
|
+
const writes = bindings.filter(binding => binding.role === 'write');
|
|
46
|
+
if (writes.length < 1 || writes.some(binding => binding.effect)) {
|
|
47
|
+
throw new Error(`read-evidence-artifact-binding-invalid:${tool.name}`);
|
|
48
|
+
}
|
|
49
|
+
if (!sourceIdentity(tool.outputSchema) || !requiredArtifact(tool.outputSchema, 'artifact')) {
|
|
50
|
+
throw new Error(`read-evidence-output-binding-invalid:${tool.name}`);
|
|
51
|
+
}
|
|
52
|
+
if (expectedRole === 'document-observation') {
|
|
53
|
+
const identity = requiredObject(tool.outputSchema, 'identity') || requiredObject(tool.outputSchema, 'summary');
|
|
54
|
+
if (!identity || unboundedArrays(identity).length > 0) {
|
|
55
|
+
throw new Error(`document-observation-identity-unbounded:${tool.name}`);
|
|
56
|
+
}
|
|
57
|
+
} else if (!requiredObject(tool.outputSchema, 'receipt')) {
|
|
58
|
+
throw new Error(`final-readback-receipt-missing:${tool.name}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function assertAnnotations(tool, readOnlyHint, idempotentHint) {
|
|
63
|
+
const annotations = tool.annotations || {};
|
|
64
|
+
if (annotations.readOnlyHint !== readOnlyHint
|
|
65
|
+
|| annotations.idempotentHint !== idempotentHint
|
|
66
|
+
|| annotations.destructiveHint !== false
|
|
67
|
+
|| annotations.openWorldHint !== false) {
|
|
68
|
+
throw new Error(`evidence-role-annotations-invalid:${tool.name}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function fileBindings(schema) {
|
|
73
|
+
const bindings = [];
|
|
74
|
+
function visit(node) {
|
|
75
|
+
if (!node || typeof node !== 'object' || Array.isArray(node)) return;
|
|
76
|
+
if (node['x-tiwater-file-role'] === 'read' || node['x-tiwater-file-role'] === 'write') {
|
|
77
|
+
bindings.push({
|
|
78
|
+
role: node['x-tiwater-file-role'],
|
|
79
|
+
effect: node['x-tiwater-file-role'] === 'write' && node['x-tiwater-file-effect'] !== false,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
for (const child of Object.values(node.properties || {})) visit(child);
|
|
83
|
+
if (node.items) visit(node.items);
|
|
84
|
+
for (const keyword of ['allOf', 'anyOf', 'oneOf']) {
|
|
85
|
+
for (const child of node[keyword] || []) visit(child);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
visit(schema);
|
|
89
|
+
return bindings;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function requiredObject(schema, property) {
|
|
93
|
+
const value = schema?.properties?.[property];
|
|
94
|
+
return schema?.required?.includes(property) && value?.type === 'object' ? value : null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function artifactShape(schema) {
|
|
98
|
+
const candidate = schema?.anyOf?.find(value => value?.type === 'object') || schema;
|
|
99
|
+
return candidate?.type === 'object'
|
|
100
|
+
&& ['path', 'sha256', 'bytes'].every(property => candidate.required?.includes(property));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function requiredArtifact(schema, property) {
|
|
104
|
+
return schema?.required?.includes(property) && artifactShape(schema?.properties?.[property]);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function sourceIdentity(schema) {
|
|
108
|
+
if (requiredArtifact(schema, 'source')) return true;
|
|
109
|
+
const sources = schema?.properties?.sources;
|
|
110
|
+
return schema?.required?.includes('sources') && sources?.type === 'array'
|
|
111
|
+
&& Number.isInteger(sources.maxItems) && artifactShape(sources.items);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function unboundedArrays(schema, location = '$', found = []) {
|
|
115
|
+
if (Array.isArray(schema)) {
|
|
116
|
+
schema.forEach((child, index) => unboundedArrays(child, `${location}[${index}]`, found));
|
|
117
|
+
return found;
|
|
118
|
+
}
|
|
119
|
+
if (!schema || typeof schema !== 'object') return found;
|
|
120
|
+
if (schema.type === 'array' && !Number.isInteger(schema.maxItems)) found.push(location);
|
|
121
|
+
for (const [key, child] of Object.entries(schema)) unboundedArrays(child, `${location}.${key}`, found);
|
|
122
|
+
return found;
|
|
123
|
+
}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"type": "string",
|
|
16
16
|
"minLength": 1,
|
|
17
17
|
"description": "Absolute path for a new JSON artifact inside the caller-authorized output directory. 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": [
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"type": "string",
|
|
16
16
|
"minLength": 1,
|
|
17
17
|
"description": "Optional absolute path for retaining the complete observation inside the caller-authorized output directory; existing files are never overwritten. May be combined with returnContent.",
|
|
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.
|
|
5
|
+
"version": "0.21.32"
|
|
6
6
|
},
|
|
7
7
|
"tools": [
|
|
8
8
|
{
|
|
@@ -64,11 +64,11 @@
|
|
|
64
64
|
"name": "docx_export_json",
|
|
65
65
|
"providerContract": {
|
|
66
66
|
"source": "packages/docx-cli/contracts/mcp-input/docx_export_json.schema.json",
|
|
67
|
-
"sha256": "
|
|
67
|
+
"sha256": "995eb1a5a8992e2a2186f2d7621b5eea7875eecd78ce153e77964d2942db2211"
|
|
68
68
|
},
|
|
69
69
|
"inputContract": {
|
|
70
70
|
"path": "office/contracts/docx_export_json.schema.json",
|
|
71
|
-
"sha256": "
|
|
71
|
+
"sha256": "995eb1a5a8992e2a2186f2d7621b5eea7875eecd78ce153e77964d2942db2211"
|
|
72
72
|
}
|
|
73
73
|
},
|
|
74
74
|
{
|
|
@@ -108,11 +108,11 @@
|
|
|
108
108
|
"name": "docx_inspect",
|
|
109
109
|
"providerContract": {
|
|
110
110
|
"source": "packages/docx-cli/contracts/mcp-input/docx_inspect.schema.json",
|
|
111
|
-
"sha256": "
|
|
111
|
+
"sha256": "a7d00a4caa5983d5dbf210eade9ebcc345afaa9f6c72dcae37978447ad180259"
|
|
112
112
|
},
|
|
113
113
|
"inputContract": {
|
|
114
114
|
"path": "office/contracts/docx_inspect.schema.json",
|
|
115
|
-
"sha256": "
|
|
115
|
+
"sha256": "a7d00a4caa5983d5dbf210eade9ebcc345afaa9f6c72dcae37978447ad180259"
|
|
116
116
|
}
|
|
117
117
|
},
|
|
118
118
|
{
|
|
@@ -328,22 +328,22 @@
|
|
|
328
328
|
"name": "pptx_export_json",
|
|
329
329
|
"providerContract": {
|
|
330
330
|
"source": "packages/pptx-cli/contracts/mcp-input/pptx_export_json.schema.json",
|
|
331
|
-
"sha256": "
|
|
331
|
+
"sha256": "9cde8fdda335f379d44e60819367b305a2c97c6c0c8fdf2141bf460335e5da84"
|
|
332
332
|
},
|
|
333
333
|
"inputContract": {
|
|
334
334
|
"path": "office/contracts/pptx_export_json.schema.json",
|
|
335
|
-
"sha256": "
|
|
335
|
+
"sha256": "9cde8fdda335f379d44e60819367b305a2c97c6c0c8fdf2141bf460335e5da84"
|
|
336
336
|
}
|
|
337
337
|
},
|
|
338
338
|
{
|
|
339
339
|
"name": "pptx_inspect",
|
|
340
340
|
"providerContract": {
|
|
341
341
|
"source": "packages/pptx-cli/contracts/mcp-input/pptx_inspect.schema.json",
|
|
342
|
-
"sha256": "
|
|
342
|
+
"sha256": "9cde8fdda335f379d44e60819367b305a2c97c6c0c8fdf2141bf460335e5da84"
|
|
343
343
|
},
|
|
344
344
|
"inputContract": {
|
|
345
345
|
"path": "office/contracts/pptx_inspect.schema.json",
|
|
346
|
-
"sha256": "
|
|
346
|
+
"sha256": "9cde8fdda335f379d44e60819367b305a2c97c6c0c8fdf2141bf460335e5da84"
|
|
347
347
|
}
|
|
348
348
|
},
|
|
349
349
|
{
|
|
@@ -427,11 +427,11 @@
|
|
|
427
427
|
"name": "xlsx_export_json",
|
|
428
428
|
"providerContract": {
|
|
429
429
|
"source": "packages/xlsx-cli/contracts/mcp-input/xlsx_export_json.schema.json",
|
|
430
|
-
"sha256": "
|
|
430
|
+
"sha256": "3943fa01506e5b9ea1cb0ecfd13fc484c16ec96245af462c9a6a37377f2a7a82"
|
|
431
431
|
},
|
|
432
432
|
"inputContract": {
|
|
433
433
|
"path": "office/contracts/xlsx_export_json.schema.json",
|
|
434
|
-
"sha256": "
|
|
434
|
+
"sha256": "3943fa01506e5b9ea1cb0ecfd13fc484c16ec96245af462c9a6a37377f2a7a82"
|
|
435
435
|
}
|
|
436
436
|
},
|
|
437
437
|
{
|
|
@@ -449,11 +449,11 @@
|
|
|
449
449
|
"name": "xlsx_inspect",
|
|
450
450
|
"providerContract": {
|
|
451
451
|
"source": "packages/xlsx-cli/contracts/mcp-input/xlsx_inspect.schema.json",
|
|
452
|
-
"sha256": "
|
|
452
|
+
"sha256": "9cde8fdda335f379d44e60819367b305a2c97c6c0c8fdf2141bf460335e5da84"
|
|
453
453
|
},
|
|
454
454
|
"inputContract": {
|
|
455
455
|
"path": "office/contracts/xlsx_inspect.schema.json",
|
|
456
|
-
"sha256": "
|
|
456
|
+
"sha256": "9cde8fdda335f379d44e60819367b305a2c97c6c0c8fdf2141bf460335e5da84"
|
|
457
457
|
}
|
|
458
458
|
},
|
|
459
459
|
{
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"type": "string",
|
|
16
16
|
"minLength": 1,
|
|
17
17
|
"description": "New JSON artifact path. 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
|
"resolveMergedCells": {
|
|
21
22
|
"description": "Resolve merged cells to project values.",
|
package/office/index.mjs
CHANGED
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
writeJsonArtifact,
|
|
24
24
|
} from '../_shared/large-json-result.mjs';
|
|
25
25
|
import { withOutputWriteLock } from '../_shared/output-write-lock.mjs';
|
|
26
|
+
import { evidenceRoleMetadata } from '../_shared/evidence-role.mjs';
|
|
27
|
+
import { effectKindMetadata } from '../_shared/effect-kind.mjs';
|
|
26
28
|
import { compactDocxObjectIdentity } from './docx-object-identity.mjs';
|
|
27
29
|
|
|
28
30
|
const packageMetadata = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
|
|
@@ -152,6 +154,7 @@ const xlsxFixedTools = [
|
|
|
152
154
|
function fixedToolDefinitions(definitions) {
|
|
153
155
|
return definitions.map(definition => ({
|
|
154
156
|
name: definition.name,
|
|
157
|
+
effectKind: 'document-mutation',
|
|
155
158
|
description: definition.description,
|
|
156
159
|
inputSchema: inputContract(definition.name),
|
|
157
160
|
outputSchema: fixedEditOutput(definition.name),
|
|
@@ -424,10 +427,11 @@ const docxReadObjectOutput = docxObservationOutput('docx_read_object').extend({
|
|
|
424
427
|
const tools = [
|
|
425
428
|
{
|
|
426
429
|
name: 'docx_inspect',
|
|
430
|
+
evidenceRole: 'document-observation',
|
|
427
431
|
description: 'Inspect one current DOCX for identity and package overview. The response always includes a bounded identity summary. Set returnContent true when that summary is the requested direct result. Provide output to retain the complete machine observation and return its artifact receipt. These channels are independent and may be used together. At least one channel is required. Use list and read operations to traverse selected document objects in native structure order. This overview is not a complete final-document readback; use docx_export_json when a downstream consumer requires the complete body projection.',
|
|
428
432
|
inputSchema: inputContract('docx_inspect'),
|
|
429
433
|
outputSchema: docxInspectionOutput('docx_inspect'),
|
|
430
|
-
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
434
|
+
annotations: { readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: false },
|
|
431
435
|
handler: docxInspect,
|
|
432
436
|
},
|
|
433
437
|
{
|
|
@@ -464,6 +468,7 @@ const tools = [
|
|
|
464
468
|
},
|
|
465
469
|
{
|
|
466
470
|
name: 'docx_replace_content_from_source',
|
|
471
|
+
effectKind: 'document-mutation',
|
|
467
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.',
|
|
468
473
|
inputSchema: inputContract('docx_replace_content_from_source'),
|
|
469
474
|
outputSchema: fixedEditOutput('docx_replace_content_from_source'),
|
|
@@ -471,6 +476,7 @@ const tools = [
|
|
|
471
476
|
},
|
|
472
477
|
{
|
|
473
478
|
name: 'docx_set_text',
|
|
479
|
+
effectKind: 'document-mutation',
|
|
474
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.',
|
|
475
481
|
inputSchema: inputContract('docx_set_text'),
|
|
476
482
|
outputSchema: fixedEditOutput('docx_set_text'),
|
|
@@ -478,6 +484,7 @@ const tools = [
|
|
|
478
484
|
},
|
|
479
485
|
{
|
|
480
486
|
name: 'docx_set_paragraph_pagination',
|
|
487
|
+
effectKind: 'document-mutation',
|
|
481
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.',
|
|
482
489
|
inputSchema: inputContract('docx_set_paragraph_pagination'),
|
|
483
490
|
outputSchema: fixedEditOutput('docx_set_paragraph_pagination'),
|
|
@@ -485,6 +492,7 @@ const tools = [
|
|
|
485
492
|
},
|
|
486
493
|
{
|
|
487
494
|
name: 'docx_set_table_body',
|
|
495
|
+
effectKind: 'document-mutation',
|
|
488
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.',
|
|
489
497
|
inputSchema: inputContract('docx_set_table_body'),
|
|
490
498
|
outputSchema: fixedEditOutput('docx_set_table_body'),
|
|
@@ -492,6 +500,7 @@ const tools = [
|
|
|
492
500
|
},
|
|
493
501
|
{
|
|
494
502
|
name: 'docx_fill_table_from_tables',
|
|
503
|
+
effectKind: 'document-mutation',
|
|
495
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.',
|
|
496
505
|
inputSchema: inputContract('docx_fill_table_from_tables'),
|
|
497
506
|
outputSchema: fixedEditOutput('docx_fill_table_from_tables'),
|
|
@@ -499,6 +508,7 @@ const tools = [
|
|
|
499
508
|
},
|
|
500
509
|
{
|
|
501
510
|
name: 'docx_insert_objects',
|
|
511
|
+
effectKind: 'document-mutation',
|
|
502
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.',
|
|
503
513
|
inputSchema: inputContract('docx_insert_objects'),
|
|
504
514
|
outputSchema: fixedEditOutput('docx_insert_objects'),
|
|
@@ -506,6 +516,7 @@ const tools = [
|
|
|
506
516
|
},
|
|
507
517
|
{
|
|
508
518
|
name: 'docx_delete_object',
|
|
519
|
+
effectKind: 'document-mutation',
|
|
509
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.',
|
|
510
521
|
inputSchema: inputContract('docx_delete_object'),
|
|
511
522
|
outputSchema: fixedEditOutput('docx_delete_object'),
|
|
@@ -513,6 +524,7 @@ const tools = [
|
|
|
513
524
|
},
|
|
514
525
|
{
|
|
515
526
|
name: 'docx_merge_cells',
|
|
527
|
+
effectKind: 'document-mutation',
|
|
516
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.',
|
|
517
529
|
inputSchema: inputContract('docx_merge_cells'),
|
|
518
530
|
outputSchema: fixedEditOutput('docx_merge_cells'),
|
|
@@ -520,6 +532,7 @@ const tools = [
|
|
|
520
532
|
},
|
|
521
533
|
{
|
|
522
534
|
name: 'docx_split_cells',
|
|
535
|
+
effectKind: 'document-mutation',
|
|
523
536
|
description: 'Split selected current DOCX merged cells.',
|
|
524
537
|
inputSchema: inputContract('docx_split_cells'),
|
|
525
538
|
outputSchema: fixedEditOutput('docx_split_cells'),
|
|
@@ -527,6 +540,7 @@ const tools = [
|
|
|
527
540
|
},
|
|
528
541
|
{
|
|
529
542
|
name: 'docx_insert_table_columns',
|
|
543
|
+
effectKind: 'document-mutation',
|
|
530
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.',
|
|
531
545
|
inputSchema: inputContract('docx_insert_table_columns'),
|
|
532
546
|
outputSchema: fixedEditOutput('docx_insert_table_columns'),
|
|
@@ -534,6 +548,7 @@ const tools = [
|
|
|
534
548
|
},
|
|
535
549
|
{
|
|
536
550
|
name: 'docx_delete_table_columns',
|
|
551
|
+
effectKind: 'document-mutation',
|
|
537
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.',
|
|
538
553
|
inputSchema: inputContract('docx_delete_table_columns'),
|
|
539
554
|
outputSchema: fixedEditOutput('docx_delete_table_columns'),
|
|
@@ -549,10 +564,11 @@ const tools = [
|
|
|
549
564
|
},
|
|
550
565
|
{
|
|
551
566
|
name: 'docx_export_json',
|
|
567
|
+
evidenceRole: 'final-readback',
|
|
552
568
|
description: 'Produce the complete body-only DOCX JSON projection required for final-document readback or another downstream consumer of that format. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required. This does not replace bounded list and read operations during document processing.',
|
|
553
569
|
inputSchema: inputContract('docx_export_json'),
|
|
554
570
|
outputSchema: largeResultOutput('docx_export_json'),
|
|
555
|
-
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
571
|
+
annotations: { readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: false },
|
|
556
572
|
handler: docxExportJson,
|
|
557
573
|
},
|
|
558
574
|
{
|
|
@@ -573,6 +589,7 @@ const tools = [
|
|
|
573
589
|
},
|
|
574
590
|
{
|
|
575
591
|
name: 'docx_apply_font_policy',
|
|
592
|
+
effectKind: 'document-mutation',
|
|
576
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.',
|
|
577
594
|
inputSchema: inputContract('docx_apply_font_policy'),
|
|
578
595
|
outputSchema: fixedEditOutput('docx_apply_font_policy'),
|
|
@@ -588,6 +605,7 @@ const tools = [
|
|
|
588
605
|
},
|
|
589
606
|
{
|
|
590
607
|
name: 'docx_apply_toc_style_policy',
|
|
608
|
+
effectKind: 'document-mutation',
|
|
591
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.',
|
|
592
610
|
inputSchema: inputContract('docx_apply_toc_style_policy'),
|
|
593
611
|
outputSchema: fixedEditOutput('docx_apply_toc_style_policy'),
|
|
@@ -595,6 +613,7 @@ const tools = [
|
|
|
595
613
|
},
|
|
596
614
|
{
|
|
597
615
|
name: 'docx_refresh_fields',
|
|
616
|
+
effectKind: 'document-mutation',
|
|
598
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.',
|
|
599
618
|
inputSchema: inputContract('docx_refresh_fields'),
|
|
600
619
|
outputSchema: docxFieldRefreshOutput,
|
|
@@ -602,43 +621,51 @@ const tools = [
|
|
|
602
621
|
},
|
|
603
622
|
{
|
|
604
623
|
name: 'docx_strip_direct_formatting',
|
|
624
|
+
effectKind: 'document-mutation',
|
|
605
625
|
description: 'Remove direct paragraph and run formatting while preserving styles.',
|
|
606
626
|
inputSchema: inputContract('docx_strip_direct_formatting'),
|
|
607
627
|
handler: docxStripDirectFormatting,
|
|
608
628
|
},
|
|
609
629
|
{
|
|
610
630
|
name: 'docx_replace_style_ids',
|
|
631
|
+
effectKind: 'document-mutation',
|
|
611
632
|
description: 'Replace current DOCX style IDs from an explicit style map.',
|
|
612
633
|
inputSchema: inputContract('docx_replace_style_ids'),
|
|
613
634
|
handler: docxReplaceStyleIds,
|
|
614
635
|
},
|
|
615
636
|
{
|
|
616
637
|
name: 'office_render_pdf',
|
|
638
|
+
evidenceRole: 'native-render',
|
|
639
|
+
effectKind: 'native-render',
|
|
617
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.',
|
|
618
641
|
inputSchema: inputContract('office_render_pdf'),
|
|
619
642
|
outputSchema: nativeRenderOutput,
|
|
643
|
+
annotations: { readOnlyHint: false, idempotentHint: false, destructiveHint: false, openWorldHint: false },
|
|
620
644
|
handler: officeRenderPdf,
|
|
621
645
|
},
|
|
622
646
|
{
|
|
623
647
|
name: 'xlsx_convert_legacy',
|
|
648
|
+
effectKind: 'source-conversion',
|
|
624
649
|
description: 'Convert a current legacy XLS workbook to XLSX using the published native ET backend.',
|
|
625
650
|
inputSchema: inputContract('xlsx_convert_legacy'),
|
|
626
651
|
handler: xlsxConvertLegacy,
|
|
627
652
|
},
|
|
628
653
|
{
|
|
629
654
|
name: 'xlsx_inspect',
|
|
655
|
+
evidenceRole: 'document-observation',
|
|
630
656
|
description: 'Inspect a current XLSX workbook or legacy XLS workbook, including workbook structure, exported values, formulas, styles, merged ranges, and published legacy-format conversion evidence. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
631
657
|
inputSchema: inputContract('xlsx_inspect'),
|
|
632
658
|
outputSchema: largeInspectionOutput('xlsx_inspect', xlsxInspectionSummary),
|
|
633
|
-
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
659
|
+
annotations: { readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: false },
|
|
634
660
|
handler: xlsxInspect,
|
|
635
661
|
},
|
|
636
662
|
{
|
|
637
663
|
name: 'xlsx_export_json',
|
|
664
|
+
evidenceRole: 'final-readback',
|
|
638
665
|
description: 'Export workbook sheet data from XLSX as structured JSON. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
639
666
|
inputSchema: inputContract('xlsx_export_json'),
|
|
640
667
|
outputSchema: largeResultOutput('xlsx_export_json'),
|
|
641
|
-
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
668
|
+
annotations: { readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: false },
|
|
642
669
|
handler: xlsxExportJson,
|
|
643
670
|
},
|
|
644
671
|
{
|
|
@@ -660,22 +687,25 @@ const tools = [
|
|
|
660
687
|
},
|
|
661
688
|
{
|
|
662
689
|
name: 'pptx_inspect',
|
|
690
|
+
evidenceRole: 'document-observation',
|
|
663
691
|
description: 'Inspect a PPTX file, including slides, masters, layouts, shapes, transforms, paragraphs, runs, and placeholders. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
664
692
|
inputSchema: inputContract('pptx_inspect'),
|
|
665
693
|
outputSchema: largeInspectionOutput('pptx_inspect', pptxInspectionSummary),
|
|
666
|
-
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
694
|
+
annotations: { readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: false },
|
|
667
695
|
handler: pptxInspect,
|
|
668
696
|
},
|
|
669
697
|
{
|
|
670
698
|
name: 'pptx_export_json',
|
|
699
|
+
evidenceRole: 'final-readback',
|
|
671
700
|
description: 'Export PPTX slide text, notes, and placeholder hints as structured JSON. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
672
701
|
inputSchema: inputContract('pptx_export_json'),
|
|
673
702
|
outputSchema: largeResultOutput('pptx_export_json'),
|
|
674
|
-
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
703
|
+
annotations: { readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: false },
|
|
675
704
|
handler: pptxExportJson,
|
|
676
705
|
},
|
|
677
706
|
{
|
|
678
707
|
name: 'pptx_apply_template',
|
|
708
|
+
effectKind: 'document-mutation',
|
|
679
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.',
|
|
680
710
|
inputSchema: inputContract('pptx_apply_template'),
|
|
681
711
|
outputSchema: fixedEditOutput('pptx_apply_template'),
|
|
@@ -683,6 +713,7 @@ const tools = [
|
|
|
683
713
|
},
|
|
684
714
|
{
|
|
685
715
|
name: 'pptx_apply_format',
|
|
716
|
+
effectKind: 'document-mutation',
|
|
686
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.',
|
|
687
718
|
inputSchema: inputContract('pptx_apply_format'),
|
|
688
719
|
outputSchema: fixedEditOutput('pptx_apply_format'),
|
|
@@ -690,6 +721,7 @@ const tools = [
|
|
|
690
721
|
},
|
|
691
722
|
{
|
|
692
723
|
name: 'pptx_set_shape_geometry',
|
|
724
|
+
effectKind: 'document-mutation',
|
|
693
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.',
|
|
694
726
|
inputSchema: inputContract('pptx_set_shape_geometry'),
|
|
695
727
|
outputSchema: fixedEditOutput('pptx_set_shape_geometry'),
|
|
@@ -697,6 +729,7 @@ const tools = [
|
|
|
697
729
|
},
|
|
698
730
|
{
|
|
699
731
|
name: 'pptx_replace_picture_image',
|
|
732
|
+
effectKind: 'document-mutation',
|
|
700
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.',
|
|
701
734
|
inputSchema: inputContract('pptx_replace_picture_image'),
|
|
702
735
|
outputSchema: fixedEditOutput('pptx_replace_picture_image'),
|
|
@@ -727,6 +760,12 @@ function buildServer() {
|
|
|
727
760
|
inputSchema: tool.inputSchema,
|
|
728
761
|
...(tool.outputSchema ? { outputSchema: tool.outputSchema } : {}),
|
|
729
762
|
...(tool.annotations ? { annotations: tool.annotations } : {}),
|
|
763
|
+
...((tool.evidenceRole || tool.effectKind) ? {
|
|
764
|
+
_meta: {
|
|
765
|
+
...(tool.evidenceRole ? evidenceRoleMetadata(tool.evidenceRole) : {}),
|
|
766
|
+
...(tool.effectKind ? effectKindMetadata(tool.effectKind) : {}),
|
|
767
|
+
},
|
|
768
|
+
} : {}),
|
|
730
769
|
},
|
|
731
770
|
async args => {
|
|
732
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",
|
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
"_shared/large-json-result.mjs",
|
|
23
23
|
"_shared/output-write-lock.mjs",
|
|
24
24
|
"_shared/mcp-stdio.mjs",
|
|
25
|
+
"_shared/evidence-role.mjs",
|
|
26
|
+
"_shared/effect-kind.mjs",
|
|
25
27
|
"office/index.mjs",
|
|
26
28
|
"office/docx-object-identity.mjs",
|
|
27
29
|
"office/README.md",
|
package/pdf/index.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
7
7
|
|
|
8
8
|
import { deliverLargeJsonResult } from '../_shared/large-json-result.mjs';
|
|
9
9
|
import { McpStdioServer } from '../_shared/mcp-stdio.mjs';
|
|
10
|
+
import { evidenceRoleMetadata } from '../_shared/evidence-role.mjs';
|
|
10
11
|
import {
|
|
11
12
|
commandCandidate,
|
|
12
13
|
createToolResult,
|
|
@@ -110,6 +111,7 @@ const definitions = new Map([
|
|
|
110
111
|
['pdf_inspect', {
|
|
111
112
|
description: 'Inspect one current PDF revision. Always retain the complete observation at output and return a bounded identity containing page and document metadata without traversing document content.',
|
|
112
113
|
outputSchema: inspectOutputSchema,
|
|
114
|
+
evidenceRole: 'document-observation',
|
|
113
115
|
}],
|
|
114
116
|
['pdf_extract_tables', {
|
|
115
117
|
description: 'Extract tables from selected current PDF pages with deterministic published extraction. Set returnContent true to return complete bounded content; provide output to retain the complete immutable result. At least one result channel is required.',
|
|
@@ -142,6 +144,7 @@ const tools = manifest.tools.map((entry) => {
|
|
|
142
144
|
description: definition.description,
|
|
143
145
|
inputSchema: JSON.parse(bytes.toString('utf8')),
|
|
144
146
|
outputSchema: definition.outputSchema,
|
|
147
|
+
...(definition.evidenceRole ? { _meta: evidenceRoleMetadata(definition.evidenceRole) } : {}),
|
|
145
148
|
annotations: {
|
|
146
149
|
readOnlyHint: true,
|
|
147
150
|
idempotentHint: true,
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"type": "string",
|
|
17
17
|
"minLength": 1,
|
|
18
18
|
"description": "New immutable JSON artifact path for the complete inspection.",
|
|
19
|
-
"x-tiwater-file-role": "write"
|
|
19
|
+
"x-tiwater-file-role": "write",
|
|
20
|
+
"x-tiwater-file-effect": false
|
|
20
21
|
}
|
|
21
22
|
},
|
|
22
23
|
"required": ["input", "returnContent", "output"],
|
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
"schema": "tiwater.text-provider-contract-manifest/v1",
|
|
3
3
|
"provider": {
|
|
4
4
|
"id": "@tiwater/office-mcp",
|
|
5
|
-
"version": "0.21.
|
|
5
|
+
"version": "0.21.32"
|
|
6
6
|
},
|
|
7
7
|
"tools": [
|
|
8
8
|
{
|
|
9
9
|
"name": "text_inspect",
|
|
10
10
|
"providerContract": {
|
|
11
11
|
"source": "servers/text/provider-contracts/text_inspect.schema.json",
|
|
12
|
-
"sha256": "
|
|
12
|
+
"sha256": "7a4af90303d2a0abce66a2e77f83838dbb2ea925edf98b04a80723b92b7f5ec0"
|
|
13
13
|
},
|
|
14
14
|
"inputContract": {
|
|
15
15
|
"path": "text/contracts/text_inspect.schema.json",
|
|
16
|
-
"sha256": "
|
|
16
|
+
"sha256": "7a4af90303d2a0abce66a2e77f83838dbb2ea925edf98b04a80723b92b7f5ec0"
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
{
|
package/text/index.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { serveStdio } from '@modelcontextprotocol/server/stdio';
|
|
|
9
9
|
import { createToolResult } from '../_shared/tool-runtime.mjs';
|
|
10
10
|
import { deliverLargeJsonResult } from '../_shared/large-json-result.mjs';
|
|
11
11
|
import { withOutputWriteLock } from '../_shared/output-write-lock.mjs';
|
|
12
|
+
import { evidenceRoleMetadata } from '../_shared/evidence-role.mjs';
|
|
12
13
|
import { inspectText, readTextLines } from './observation.mjs';
|
|
13
14
|
|
|
14
15
|
const packageMetadata = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
|
|
@@ -105,6 +106,7 @@ function largeResultOutput(contentSchema) {
|
|
|
105
106
|
const definitions = [
|
|
106
107
|
{
|
|
107
108
|
name: 'text_inspect',
|
|
109
|
+
evidenceRole: 'document-observation',
|
|
108
110
|
description: 'Inspect one exact supported plain-text revision. Return its byte identity, lossless encoding and BOM facts, line count, and at most eight opening line identities while retaining the complete bounded inspection at output. It does not parse fields, records, key-value pairs, sections, or markup.',
|
|
109
111
|
outputSchema: largeResultOutput(inspectContent).extend({ identity: inspectIdentity }).strict(),
|
|
110
112
|
handler: textInspect,
|
|
@@ -137,6 +139,7 @@ function buildServer() {
|
|
|
137
139
|
destructiveHint: false,
|
|
138
140
|
openWorldHint: false,
|
|
139
141
|
},
|
|
142
|
+
...(definition.evidenceRole ? { _meta: evidenceRoleMetadata(definition.evidenceRole) } : {}),
|
|
140
143
|
},
|
|
141
144
|
async args => {
|
|
142
145
|
const payload = typeof args.output === 'string'
|