From a2ea2b49c3344129a49b42e1a4e203f216b58891 Mon Sep 17 00:00:00 2001 From: bbbugg Date: Wed, 14 Jan 2026 18:16:23 +0800 Subject: [PATCH 1/2] fix: enhance tool response handling for array formats in FormatConverter --- src/core/FormatConverter.js | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/core/FormatConverter.js b/src/core/FormatConverter.js index 89599d7f..a31daf81 100644 --- a/src/core/FormatConverter.js +++ b/src/core/FormatConverter.js @@ -174,6 +174,46 @@ class FormatConverter { try { responseContent = typeof message.content === "string" ? JSON.parse(message.content) : message.content; + + // Handle array format (common in MCP, e.g., [{ type: "text", text: "..." }]) + // Gemini requires 'response' to be an object (Struct), not an array. + if (Array.isArray(responseContent)) { + // 1. Process ALL items (text, image, etc.) + const processedItems = responseContent.map(item => { + if (item.type === "text" && typeof item.text === "string") { + try { + return JSON.parse(item.text); + } catch { + return { content: item.text, type: "text" }; // Wrap raw text + } + } + return item; // Keep other types (e.g. image) as is + }); + + if (processedItems.length > 0) { + // 2. Determine structure + if ( + processedItems.length === 1 && + typeof processedItems[0] === "object" && + !Array.isArray(processedItems[0]) && + processedItems[0] !== null + ) { + // Single object: use it directly as the root response (Best for standard MCP) + responseContent = processedItems[0]; + } else { + // Multiple/Mixed items: Gemini currently rejects mixed/multiple content in Structs. + // Strategy (User Suggested): Stringify the entire array and wrap it in an object. + // This preserves all data (including images) without breaking the Struct format. + responseContent = { result: JSON.stringify(processedItems) }; + this.logger.info( + `[Adapter] Multiple tool response items found (${processedItems.length}). Wrapping in JSON string to preserve all data.` + ); + } + } else { + // Empty array or unforeseen structure, wrap original + responseContent = { result: responseContent }; + } + } } catch (e) { // If content is not valid JSON, wrap it responseContent = { result: message.content }; From 57ebfdb58f62541baea965991f68afc9962abfc2 Mon Sep 17 00:00:00 2001 From: bbbugg Date: Wed, 14 Jan 2026 18:42:04 +0800 Subject: [PATCH 2/2] fix: improve robustness of tool response handling in FormatConverter --- src/core/FormatConverter.js | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/core/FormatConverter.js b/src/core/FormatConverter.js index a31daf81..50a706bb 100644 --- a/src/core/FormatConverter.js +++ b/src/core/FormatConverter.js @@ -182,7 +182,14 @@ class FormatConverter { const processedItems = responseContent.map(item => { if (item.type === "text" && typeof item.text === "string") { try { - return JSON.parse(item.text); + const parsed = JSON.parse(item.text); + // Robustness Check: Only unwrap if it's a bare object (not null, not array, not primitive) + // This prevents "123" or "true" or "[]" from becoming inconsistent types in the list + if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) { + return parsed; + } + // If it's a primitive or array, keep it wrapped as text to avoid structure confusion + return { content: item.text, type: "text" }; } catch { return { content: item.text, type: "text" }; // Wrap raw text } @@ -201,17 +208,20 @@ class FormatConverter { // Single object: use it directly as the root response (Best for standard MCP) responseContent = processedItems[0]; } else { - // Multiple/Mixed items: Gemini currently rejects mixed/multiple content in Structs. - // Strategy (User Suggested): Stringify the entire array and wrap it in an object. - // This preserves all data (including images) without breaking the Struct format. + // Multiple/Mixed items configuration responseContent = { result: JSON.stringify(processedItems) }; this.logger.info( `[Adapter] Multiple tool response items found (${processedItems.length}). Wrapping in JSON string to preserve all data.` ); } } else { - // Empty array or unforeseen structure, wrap original - responseContent = { result: responseContent }; + // Empty array or unforeseen structure + // To keep behavior consistent with the multiple-items case, stringify the array + // (e.g. returns { result: "[]" }) + responseContent = { result: JSON.stringify(responseContent) }; + this.logger.info( + `[Adapter] Empty/Unforeseen tool response structure. Wrapping in JSON string: ${JSON.stringify(responseContent)}` + ); } } } catch (e) {