From 3f40b75860559b8cab42420f6e96e91e30a46bf1 Mon Sep 17 00:00:00 2001 From: angelplusultra Date: Thu, 26 Mar 2026 13:50:38 -0700 Subject: [PATCH] simplify --- .../ChatHistory/MCPImageContent/index.jsx | 24 +++--------- server/utils/MCP/index.js | 37 +++++-------------- 2 files changed, 15 insertions(+), 46 deletions(-) diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/MCPImageContent/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/MCPImageContent/index.jsx index 403c96bc4..c118f0606 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/MCPImageContent/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/MCPImageContent/index.jsx @@ -37,33 +37,19 @@ function MCPImageContent({ props }) { function MCPImage({ image, index }) { const [loading, setLoading] = useState(false); - const isDataUri = image.src.startsWith("data:"); const handleDownload = useCallback(async () => { setLoading(true); try { - const ext = (image.mimeType || "png").split("/").pop(); - if (isDataUri) { - const base64 = image.src.split(",")[1]; - const byteString = atob(base64); - const ab = new ArrayBuffer(byteString.length); - const ia = new Uint8Array(ab); - for (let i = 0; i < byteString.length; i++) { - ia[i] = byteString.charCodeAt(i); - } - const blob = new Blob([ab], { type: image.mimeType }); - saveAs(blob, `mcp-image-${index}.${ext}`); - } else { - // For remote URLs, fetch and save - const response = await fetch(image.src); - const blob = await response.blob(); - saveAs(blob, `mcp-image-${index}.${ext}`); - } + const ext = (image.mimeType || "image/png").split("/").pop(); + const response = await fetch(image.src); + const blob = await response.blob(); + saveAs(blob, `mcp-image-${index}.${ext}`); } catch (e) { console.error("Failed to download image:", e); } setLoading(false); - }, [image, index, isDataUri]); + }, [image, index]); return (
diff --git a/server/utils/MCP/index.js b/server/utils/MCP/index.js index 334f5b408..df3dd2f51 100644 --- a/server/utils/MCP/index.js +++ b/server/utils/MCP/index.js @@ -253,36 +253,22 @@ class MCPCompatibilityLayer extends MCPHypervisor { return { success: true, error: null }; } - /** - * Check if a MIME type is an image type. - * @param {string} mimeType - * @returns {boolean} - */ - static isImageMimeType(mimeType) { - return typeof mimeType === "string" && mimeType.startsWith("image/"); - } - /** * Extract image content items from an MCP tool result per MCP spec. - * Handles three MCP content types that can contain images: - * - `type: "image"` — inline base64-encoded image with data + mimeType - * - `type: "resource_link"` — URI to an externally hosted image - * - `type: "resource"` — embedded resource with a blob (base64) or URI - * + * Handles: type "image" (inline base64), "resource_link" (remote URL), "resource" (embedded blob/URI). * @param {Object} result - The MCP tool result * @returns {{images: {src: string, mimeType: string}[], textContent: string, llmText: string}|null} - * Returns null if no image content is found. Each image has a `src` that is either - * a data URI (for base64) or a remote URL (for resource links). */ static extractImageContent(result) { if (!result?.content || !Array.isArray(result.content)) return null; + const isImage = (mime) => + typeof mime === "string" && mime.startsWith("image/"); const images = []; const textParts = []; for (const item of result.content) { if (item.type === "image" && item.data && item.mimeType) { - // Inline base64 image images.push({ src: `data:${item.mimeType};base64,${item.data}`, mimeType: item.mimeType, @@ -290,16 +276,14 @@ class MCPCompatibilityLayer extends MCPHypervisor { } else if ( item.type === "resource_link" && item.uri && - this.isImageMimeType(item.mimeType) + isImage(item.mimeType) ) { - // Resource link pointing to a remote image images.push({ src: item.uri, mimeType: item.mimeType }); } else if ( item.type === "resource" && item.resource && - this.isImageMimeType(item.resource.mimeType) + isImage(item.resource.mimeType) ) { - // Embedded resource — can have a blob (base64) or just a URI if (item.resource.blob) { images.push({ src: `data:${item.resource.mimeType};base64,${item.resource.blob}`, @@ -319,13 +303,12 @@ class MCPCompatibilityLayer extends MCPHypervisor { if (images.length === 0) return null; const textContent = textParts.join("\n"); - const imagePlaceholders = images - .map( - (img) => - `[An image was returned by this tool and displayed to the user. Image type: ${img.mimeType}]` - ) + const llmText = [ + textContent, + `[${images.length} image(s) returned by this tool and displayed to the user]`, + ] + .filter(Boolean) .join("\n"); - const llmText = [textContent, imagePlaceholders].filter(Boolean).join("\n"); return { images, textContent, llmText }; }