11import { getConfig } from '../../lib/config.js' ;
22import type { ToolOutputPolicy } from '../tooling/tool-descriptors.js' ;
3-
4- interface TruncationMeta {
5- truncated : boolean ;
6- maxItems : number ;
7- maxChars : number ;
8- originalSizeEstimate : number ;
9- }
10-
11- function isRecord ( value : unknown ) : value is Record < string , unknown > {
12- return typeof value === 'object' && value !== null && ! Array . isArray ( value ) ;
13- }
14-
15- function summarizeValue ( value : unknown ) : Record < string , unknown > {
16- if ( Array . isArray ( value ) ) {
17- return {
18- type : 'array' ,
19- length : value . length ,
20- preview : value . slice ( 0 , 3 ) ,
21- } ;
22- }
23-
24- if ( isRecord ( value ) ) {
25- const keys = Object . keys ( value ) ;
26- return {
27- type : 'object' ,
28- keys,
29- keyCount : keys . length ,
30- } ;
31- }
32-
33- return {
34- type : typeof value ,
35- value,
36- } ;
37- }
38-
39- function truncateArrays ( value : unknown , maxItems : number ) : { value : unknown ; truncated : boolean } {
40- if ( Array . isArray ( value ) ) {
41- const truncatedItems = value . slice ( 0 , maxItems ) . map ( item => truncateArrays ( item , maxItems ) ) ;
42- const wasTruncated = value . length > maxItems || truncatedItems . some ( item => item . truncated ) ;
43-
44- return {
45- value : truncatedItems . map ( item => item . value ) ,
46- truncated : wasTruncated ,
47- } ;
48- }
49-
50- if ( isRecord ( value ) ) {
51- let truncated = false ;
52- const next : Record < string , unknown > = { } ;
53-
54- for ( const [ key , itemValue ] of Object . entries ( value ) ) {
55- const child = truncateArrays ( itemValue , maxItems ) ;
56- if ( child . truncated ) {
57- truncated = true ;
58- }
59- next [ key ] = child . value ;
60- }
61-
62- return {
63- value : next ,
64- truncated,
65- } ;
66- }
67-
68- return {
69- value,
70- truncated : false ,
71- } ;
72- }
73-
74- function stripRawByConfig ( value : unknown , includeRaw : boolean ) : unknown {
75- if ( includeRaw ) {
76- return value ;
77- }
78-
79- if ( ! isRecord ( value ) ) {
80- return value ;
81- }
82-
83- const next = { ...value } ;
84- delete next . raw ;
85- return next ;
86- }
87-
88- function attachMeta ( value : unknown , meta : TruncationMeta ) : unknown {
89- if ( isRecord ( value ) ) {
90- return {
91- ...value ,
92- meta,
93- } ;
94- }
95-
96- return {
97- data : value ,
98- meta,
99- } ;
100- }
101-
102- function safeSerialize ( value : unknown ) : string {
103- try {
104- return JSON . stringify ( value , null , 2 ) ;
105- } catch {
106- return JSON . stringify ( { data : summarizeValue ( value ) } , null , 2 ) ;
107- }
108- }
109-
110- function shrinkForMaxChars ( value : unknown , meta : TruncationMeta ) : unknown {
111- if ( isRecord ( value ) ) {
112- return {
113- success : value . success ,
114- traceId : value . traceId ,
115- dataSummary : summarizeValue ( value . data ) ,
116- error : value . error ,
117- meta,
118- } ;
119- }
120-
121- return {
122- dataSummary : summarizeValue ( value ) ,
123- meta,
124- } ;
125- }
3+ import {
4+ applyOutputGovernance ,
5+ applySummaryPolicy as applySummaryPolicyFields ,
6+ } from '../tooling/mcp-output-governance.js' ;
1267
1278function applySummaryPolicy (
1289 value : unknown ,
@@ -133,60 +14,31 @@ function applySummaryPolicy(
13314 return { value, truncated : false } ;
13415 }
13516
136- if ( ! isRecord ( value ) || ! isRecord ( value . data ) ) {
137- return { value, truncated : false } ;
138- }
139-
140- const data = value . data as Record < string , unknown > ;
141- const nextData = { ...data } ;
142- let truncated = false ;
143-
144- for ( const key of [ 'list' , 'items' , 'blocks' , 'transactions' , 'logEvents' ] ) {
145- const item = nextData [ key ] ;
146- if ( Array . isArray ( item ) && item . length > maxItems ) {
147- nextData [ key ] = item . slice ( 0 , maxItems ) ;
148- truncated = true ;
149- }
150- }
151-
152- return {
153- value : {
154- ...value ,
155- data : nextData ,
156- } ,
157- truncated,
158- } ;
17+ return applySummaryPolicyFields ( value , maxItems ) ;
15918}
16019
16120export function asMcpResult ( data : unknown , outputPolicy : ToolOutputPolicy = 'normal' ) {
16221 const config = getConfig ( ) ;
16322 const maxItems = Math . max ( 1 , config . mcpMaxItems ) ;
16423 const maxChars = Math . max ( 1 , config . mcpMaxChars ) ;
16524
166- const stripped = stripRawByConfig ( data , config . mcpIncludeRaw ) ;
167- const summaryApplied = applySummaryPolicy ( stripped , outputPolicy , maxItems ) ;
168- const truncatedArrays = truncateArrays ( summaryApplied . value , maxItems ) ;
169-
170- const initialMeta : TruncationMeta = {
171- truncated : summaryApplied . truncated || truncatedArrays . truncated ,
25+ const summaryApplied = applySummaryPolicy ( data , outputPolicy , maxItems ) ;
26+ const governed = applyOutputGovernance ( summaryApplied . value , {
17227 maxItems,
17328 maxChars,
174- originalSizeEstimate : safeSerialize ( truncatedArrays . value ) . length ,
175- } ;
176-
177- const meta : TruncationMeta = initialMeta ;
178- let payload = attachMeta ( truncatedArrays . value , meta ) ;
179- let serialized = safeSerialize ( payload ) ;
180-
181- if ( serialized . length > maxChars ) {
182- const reducedMeta : TruncationMeta = {
183- ...meta ,
184- truncated : true ,
29+ includeRaw : config . mcpIncludeRaw ,
30+ } ) ;
31+ let payload = governed . payload as Record < string , unknown > ;
32+ if ( summaryApplied . truncated && payload ?. meta && typeof payload . meta === 'object' ) {
33+ payload = {
34+ ...payload ,
35+ meta : {
36+ ...( payload . meta as Record < string , unknown > ) ,
37+ truncated : true ,
38+ } ,
18539 } ;
186-
187- payload = shrinkForMaxChars ( truncatedArrays . value , reducedMeta ) ;
188- serialized = safeSerialize ( payload ) ;
18940 }
41+ const serialized = JSON . stringify ( payload , null , 2 ) ;
19042
19143 return {
19244 content : [
0 commit comments