@@ -207,82 +207,92 @@ function calculateImageSizeMB(base64Data: string): number {
207207 return sizeInBytes / ( 1024 * 1024 ) // Convert to MB
208208}
209209
210+ async function validateAndProcessImage ( item : any , maxImageSizeMB : number ) : Promise < string | null > {
211+ if ( ! item . mimeType || item . data === undefined || item . data === null ) {
212+ console . warn ( "Invalid MCP ImageContent: missing data or mimeType" )
213+ return null
214+ }
215+
216+ if ( ! SUPPORTED_IMAGE_TYPES . includes ( item . mimeType ) ) {
217+ console . warn ( `Unsupported image MIME type: ${ item . mimeType } ` )
218+ return null
219+ }
220+
221+ try {
222+ // Validate base64 data before constructing data URL
223+ if ( typeof item . data !== "string" || item . data . trim ( ) === "" ) {
224+ console . warn ( "Invalid MCP ImageContent: base64 data is not a valid string" )
225+ return null
226+ }
227+
228+ // Quick size check before full validation to prevent memory spikes
229+ const approximateSizeMB = ( item . data . length * 0.75 ) / ( 1024 * 1024 )
230+ if ( approximateSizeMB > maxImageSizeMB * 1.5 ) {
231+ console . warn (
232+ `MCP image likely exceeds size limit based on string length: ~${ approximateSizeMB . toFixed ( 2 ) } MB` ,
233+ )
234+ return null
235+ }
236+
237+ // Basic validation for base64 format
238+ if ( ! BASE64_REGEX . test ( item . data . replace ( / \s / g, "" ) ) ) {
239+ console . warn ( "Invalid MCP ImageContent: base64 data contains invalid characters" )
240+ return null
241+ }
242+
243+ // Check image size
244+ const imageSizeMB = calculateImageSizeMB ( item . data )
245+ if ( imageSizeMB > maxImageSizeMB ) {
246+ console . warn (
247+ `MCP image exceeds size limit: ${ imageSizeMB . toFixed ( 2 ) } MB > ${ maxImageSizeMB } MB. Image will be ignored.` ,
248+ )
249+ return null
250+ }
251+
252+ return `data:${ item . mimeType } ;base64,${ item . data } `
253+ } catch ( error ) {
254+ console . warn ( "Failed to process MCP image content:" , error )
255+ return null
256+ }
257+ }
258+
210259async function processToolContent ( toolResult : any , cline : Task ) : Promise < { text : string ; images : string [ ] } > {
211260 if ( ! toolResult ?. content || toolResult . content . length === 0 ) {
212261 return { text : "" , images : [ ] }
213262 }
214263
215264 const textParts : string [ ] = [ ]
216- const images : string [ ] = [ ]
217265
218266 // Get MCP settings from the extension's global state
219267 const state = await cline . providerRef . deref ( ) ?. getState ( )
220268 const maxImagesPerResponse = Math . max ( 1 , Math . min ( 100 , state ?. mcpMaxImagesPerResponse ?? 20 ) )
221269 const maxImageSizeMB = Math . max ( 0.1 , Math . min ( 50 , state ?. mcpMaxImageSizeMB ?? 10 ) )
222270
271+ // Separate content by type for efficient processing
272+ const imageItems = toolResult . content . filter ( ( item : any ) => item . type === "image" ) . slice ( 0 , maxImagesPerResponse ) // Limit images before processing
273+
274+ // Process images in parallel
275+ const validatedImages = await Promise . all (
276+ imageItems . map ( ( item : any ) => validateAndProcessImage ( item , maxImageSizeMB ) ) ,
277+ )
278+ const images = validatedImages . filter ( Boolean ) as string [ ]
279+
280+ // Process other content types
223281 toolResult . content . forEach ( ( item : any ) => {
224282 if ( item . type === "text" ) {
225283 textParts . push ( item . text )
226- } else if ( item . type === "image" ) {
227- // Check if we've exceeded the maximum number of images
228- if ( images . length >= maxImagesPerResponse ) {
229- console . warn (
230- `MCP response contains more than ${ maxImagesPerResponse } images. Additional images will be ignored to prevent performance issues.` ,
231- )
232- return // Skip processing additional images
233- }
234-
235- if ( item . mimeType && item . data !== undefined && item . data !== null ) {
236- if ( SUPPORTED_IMAGE_TYPES . includes ( item . mimeType ) ) {
237- try {
238- // Validate base64 data before constructing data URL
239- if ( typeof item . data !== "string" || item . data . trim ( ) === "" ) {
240- console . warn ( "Invalid MCP ImageContent: base64 data is not a valid string" )
241- return
242- }
243-
244- // Quick size check before full validation to prevent memory spikes
245- const approximateSizeMB = ( item . data . length * 0.75 ) / ( 1024 * 1024 )
246- if ( approximateSizeMB > maxImageSizeMB * 1.5 ) {
247- console . warn (
248- `MCP image likely exceeds size limit based on string length: ~${ approximateSizeMB . toFixed ( 2 ) } MB` ,
249- )
250- return
251- }
252-
253- // Basic validation for base64 format
254- if ( ! BASE64_REGEX . test ( item . data . replace ( / \s / g, "" ) ) ) {
255- console . warn ( "Invalid MCP ImageContent: base64 data contains invalid characters" )
256- return
257- }
258-
259- // Check image size
260- const imageSizeMB = calculateImageSizeMB ( item . data )
261- if ( imageSizeMB > maxImageSizeMB ) {
262- console . warn (
263- `MCP image exceeds size limit: ${ imageSizeMB . toFixed ( 2 ) } MB > ${ maxImageSizeMB } MB. Image will be ignored.` ,
264- )
265- return
266- }
267-
268- const dataUrl = `data:${ item . mimeType } ;base64,${ item . data } `
269- images . push ( dataUrl )
270- } catch ( error ) {
271- console . warn ( "Failed to process MCP image content:" , error )
272- // Continue processing other content instead of failing entirely
273- }
274- } else {
275- console . warn ( `Unsupported image MIME type: ${ item . mimeType } ` )
276- }
277- } else {
278- console . warn ( "Invalid MCP ImageContent: missing data or mimeType" )
279- }
280284 } else if ( item . type === "resource" ) {
281285 const { blob : _ , ...rest } = item . resource
282286 textParts . push ( JSON . stringify ( rest , null , 2 ) )
283287 }
284288 } )
285289
290+ if ( imageItems . length > maxImagesPerResponse ) {
291+ console . warn (
292+ `MCP response contains more than ${ maxImagesPerResponse } images. Additional images will be ignored to prevent performance issues.` ,
293+ )
294+ }
295+
286296 return {
287297 text : textParts . filter ( Boolean ) . join ( "\n\n" ) ,
288298 images,
0 commit comments