Skip to content

Conversation

@roomote
Copy link
Contributor

@roomote roomote bot commented Jan 21, 2026

Summary

This PR attempts to address Issue #10877. It builds on PR #10874 (MCP tool image support) to add:

Feature 1: Image Thumbnail Preview

  • Shows thumbnails of images returned from MCP tools in the chat interface
  • Click to open images in a new VSCode tab (using existing Thumbnails component)
  • Images are displayed below the MCP tool response section

Feature 2: save_image Tool for Agent

  • New tool that allows the agent to save base64-encoded images to any location
  • Agent can programmatically decide where to save images received from MCP tools
  • Supports PNG, JPG, JPEG, GIF, WEBP, and SVG formats
  • Parameters: path (required), data (required - base64 data URL)

Changes

Frontend (webview-ui):

  • McpExecution.tsx: Added images prop and Thumbnails rendering
  • ChatRow.tsx: Pass message.images to McpExecution component

Backend (src):

  • packages/types/src/tool.ts: Added save_image to toolNames
  • src/shared/tools.ts: Added tool params, native args, display name, and tool group
  • src/core/tools/SaveImageTool.ts: New tool implementation
  • src/core/prompts/tools/native-tools/save_image.ts: Native tool description
  • src/core/prompts/tools/native-tools/index.ts: Export save_image
  • src/core/assistant-message/presentAssistantMessage.ts: Register and execute save_image tool

Testing

  • TypeScript compilation passes
  • All existing useMcpToolTool tests pass (16/16)
  • Lint checks pass

Dependencies

This PR is based on PR #10874 and should be reviewed/merged after that PR.

Feedback and guidance are welcome!


Important

This PR adds image thumbnail previews in the chat interface and a new save_image tool for handling image saving, with updates to both frontend and backend components.

  • Features:
    • Adds image thumbnail previews in chat interface using Thumbnails component in McpExecution.tsx and ChatRow.tsx.
    • Introduces save_image tool in src/core/tools/SaveImageTool.ts for saving base64-encoded images, supporting multiple formats.
  • Backend:
    • Registers save_image tool in presentAssistantMessage.ts and native-tools/index.ts.
    • Updates UseMcpToolTool.ts to handle image content and save to temp storage.
  • Frontend:
    • Modifies McpExecution.tsx and ChatRow.tsx to pass and render image data.
  • Testing:
    • Adds tests for image handling in useMcpToolTool.spec.ts.
  • Internationalization:
    • Updates error messages in multiple language files for save_image tool.

This description was created by Ellipsis for c16ee01. You can customize this summary. It will automatically update as commits are pushed.

Fixes #10872

The processToolContent method in UseMcpToolTool.ts now handles image
content types from MCP protocol responses (e.g., Figma's get_screenshot).

Changes:
- Modified processToolContent to return both text and images
- Updated executeToolAndProcessResult to pass images to task.say() and
  pushToolResult()
- Added tests for image handling scenarios
- Add image thumbnails to McpExecution component (Feature 1)
  - Import Thumbnails component
  - Add images prop to McpExecutionProps interface
  - Render thumbnails when images are present (click to open in VSCode)
  - Pass message.images from ChatRow to McpExecution

- Add save_image tool for agent to save images (Feature 2)
  - Create SaveImageTool.ts with base64 data URL support
  - Add save_image to toolNames in types/tool.ts
  - Add tool definitions in shared/tools.ts
  - Create native tool description in prompts/tools/native-tools
  - Register tool in presentAssistantMessage.ts
  - Add to edit tool group for file write operations

Addresses Issue #10877
@roomote
Copy link
Contributor Author

roomote bot commented Jan 21, 2026

Rooviewer Clock   See task

Reviewed commit c16ee01. The image index misalignment issue is now fixed via originalIndex tracking. No new issues found.

  • Missing i18n translation key tools:saveImage.invalidDataFormat in SaveImageTool.ts
  • Security validation (.rooignore and write protection) uses original path before extension is added, but file is written to path with extension
  • savedImagePaths and images arrays can be different lengths, causing images[index] to pair wrong base64 data with file paths in UseMcpToolTool.ts
Previous reviews

Mention @roomote in a comment to request specific changes to this pull request or fix all unresolved issues.

// Validate the image data format
const base64Match = data.match(/^data:image\/(png|jpeg|jpg|gif|webp|svg\+xml);base64,(.+)$/)
if (!base64Match) {
await task.say("error", t("tools:saveImage.invalidDataFormat"))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The translation key tools:saveImage.invalidDataFormat is used here but doesn't exist in src/i18n/locales/en/tools.json. The i18n function will return the raw key string. You should add a saveImage section to the tools.json translation file with this key, similar to how generateImage translations are defined.

Fix it with Roo Code or mention @roomote and request a fix.

Comment on lines 39 to 44
// Validate access via .rooignore
const accessAllowed = task.rooIgnoreController?.validateAccess(relPath)
if (!accessAllowed) {
await task.say("rooignore_error", relPath)
pushToolResult(formatResponse.rooIgnoreError(relPath))
return
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .rooignore access check and write protection check (line 48) are performed using relPath before the image extension is potentially added. However, the file is written to finalPath which may have an extension appended (lines 70-74). If a .rooignore pattern targets specific extensions (e.g., *.png), passing a path without an extension would bypass the restriction. Consider moving these security checks after finalPath is computed, or re-validating with finalPath before writing.

Fix it with Roo Code or mention @roomote and request a fix.

@Sniper199999
Copy link

@roomote please fix the 2 issues that you have mentioned

@roomote
Copy link
Contributor Author

roomote bot commented Jan 22, 2026

Fixaroo Clock   See task on Roo Cloud

Fixed the reported issues. All local checks passed.

View commit | Revert commit

@Sniper199999
Copy link

@roomote Both the features are not working as expected...

Feature 1:
Expected: Thumbnails below [1 image(s) received] and inspecting images on click
Result: Only the text "[1 image(s) received]" is shown.
Here is the reference screenshot showing the UI:
image

Feature 2:
Expected: save image(s) using the save_image tool to any location given by the agent
Result: Tool is called but Image(s) are not getting saved. The chat shows "Checkpoint" UI for some time and then the Agent continues to say that it needs base64 image from Figma screenshot.
Here is the reference screenshot of the "Checkpoint" UI:
image

This is the defination of the Figma get_screenshot tool:
get_screenshot
Supported file types: Figma Design, FigJam
Allows the agent to take a screenshot of your selection. This helps preserve layout fidelity in the generated code. Recommended to keep on (only turn off if you're concerned about token limits).

Here is a Redacted task History of my chat, you might get an insight about the issue for feature 2:
roo_task_jan-22-2026_1-57-09-pm.md

@roomote
Copy link
Contributor Author

roomote bot commented Jan 22, 2026

Fixaroo Clock   See task on Roo Cloud

Fixed both reported issues. All local checks passed.

View commit | Revert commit

- Feature 1: Fix image thumbnails not displaying in MCP tool responses
  - Add mcpResponseImages prop to ChatRow to pass images from mcp_server_response
  - Find corresponding mcp_server_response message in ChatView and pass its images
  - McpExecution now receives images from the response message instead of ask message

- Feature 2: Fix save_image tool not receiving image data
  - Include base64 data URLs in MCP tool result text response
  - Agent now receives image data in a format usable with save_image tool
  - Images are wrapped in <image_N> tags for easy parsing

- Update tests to match new behavior
@Sniper199999
Copy link

@roomote Both features are not working still...

Feature1: In Chat It outputs:
[1 image(s) received - data URLs provided below for use with save_image tool]
<image_1>"Base 64 Image"</image_1>
Image Preview/Thumbnail is still missing.
image

Feature 2: Still having issues with saving image

[Tool (Error)]
{"status":"error","message":"The tool execution failed","error":"Invalid tool call for 'save_image': missing nativeArgs. This usually means the model streamed invalid or incomplete arguments and the call could not be finalized."}

The base64 data got from figma tool is valid and converts to an image when i converted the base64 data manually.

The data (base64) that is passed to the save_image tool is not same as that of the base64 data obtained from Figma MCP screenshot tool. There is something wrong here. The base64 data passing to save_image is visually repetitive and when I tried to manually convert to image, gives me empty image. Also base64 data changed every time the save_image tool is called.

Also after using the save_image tool, the cost incurred is too high.

image

I have also attached the task history for the chat, please refer to this to get more insights on Feature 2:
roo_task_jan-22-2026_3-49-24-pm.md

Proposed Solution: "Intermediate File Persistence" (Architecture Change)

To resolve the corruption and cost issues, we should move away from passing raw Base64 strings through the LLM context.

  1. Auto-Staging Workflow

    Step A: When an MCP tool (like Figma) returns image data, the system should immediately intercept the Base64 and write it to a local .tmp or .media folder within the workspace.

    Step B: The LLM should receive a File Path Reference (e.g., tmp/figma_screenshot_v1.png) instead of the raw Base64 string.

  2. Reference-Based Saving

    The save_image tool should be updated (or a new tool created) to accept a source_path and destination_path.

    Benefit: This eliminates the need for the LLM to process thousands of lines of Base64 text, reducing costs to near-zero and preventing data mutation.

@roomote
Copy link
Contributor Author

roomote bot commented Jan 22, 2026

Fixaroo Clock   See task on Roo Cloud

Implemented the "Intermediate File Persistence" architecture as suggested. MCP tool images are now saved to temp storage immediately upon receipt, and file paths are passed to the LLM instead of raw base64 data. This prevents data corruption and significantly reduces token costs.

View commit | Revert commit

- Save MCP tool images to temp storage instead of passing raw base64 to LLM
- Add source_path parameter to save_image tool for copying from temp storage
- This prevents data corruption and reduces token costs significantly
- Images are still stored as data URLs in message.images for UI thumbnails
- Update tests to match new behavior with file paths instead of base64 in text
Base automatically changed from fix/mcp-tool-image-support-10872 to main January 31, 2026 12:23
@Sniper199999
Copy link

@roomote The "Intermediate File Persistence" architecture is working as expected, can you remove support for "MCP image preview thumbnails" as this is still not resolved.
Make this PR only about "Intermediate File Persistence".

@roomote
Copy link
Contributor Author

roomote bot commented Feb 6, 2026

Fixaroo Clock   See task

Removed MCP image preview thumbnails (Feature 1) from the PR. The PR now only contains the Intermediate File Persistence feature. All checks passed.

View commit | Revert commit

@Sniper199999
Copy link

Fixaroo Clock See task on Roo Cloud

Implemented the "Intermediate File Persistence" architecture as suggested. MCP tool images are now saved to temp storage immediately upon receipt, and file paths are passed to the LLM instead of raw base64 data. This prevents data corruption and significantly reduces token costs.

View commit | Revert commit

@roomote pass file paths as well as the raw base64 data to the LLM. do not remove the base64 data.
This ensures the model can actually "see" and analyze the image in the current turn while still benefiting from the token savings of path-based persistence for future context.

@roomote
Copy link
Contributor Author

roomote bot commented Feb 6, 2026

Fixaroo Clock   See task

MCP image responses now include both file paths and raw base64 data URLs in the text passed to the LLM. All local checks passed.

View commit | Revert commit

Pass both source_path and raw base64 data URLs to the LLM in MCP tool
image responses. This ensures the model can analyze images in the current
turn (via base64 data) while still benefiting from path-based persistence
for future context (via source_path).
Comment on lines 333 to 337
const imagePathsSection = savedImagePaths
.map(
(imgPath, index) =>
`<image_${index + 1}>\n <source_path>${imgPath}</source_path>\n <data>${images[index]}</data>\n</image_${index + 1}>`,
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

images[index] assumes savedImagePaths and images are the same length and in the same order, but saveImagesToTempStorage skips images whose data URLs don't match the parseImageDataUrl regex (e.g., image/tiff, image/bmp, image/avif), while processToolContent pushes all image types into images. If any image is skipped during save, savedImagePaths becomes shorter than images and each <source_path> gets paired with the wrong <data> value. Consider tracking the original index alongside each saved path, or filtering images with the same regex before building the XML.

Fix it with Roo Code or mention @roomote and request a fix.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@roomote track original index alongside each saved path

Copy link
Contributor Author

@roomote roomote bot Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixaroo Clock   See task

Fixed the image/path misalignment by having saveImagesToTempStorage and saveImagesToFallbackLocation return {path, originalIndex}[] instead of string[], so skipped unsupported formats no longer shift the pairing. All checks passed.

View commit | Revert commit

…/path misalignment

When saveImagesToTempStorage skips unsupported image formats (e.g. tiff,
bmp, avif), savedImagePaths becomes shorter than images[]. Using a plain
index to pair paths with data URLs causes misalignment. Now both
saveImagesToTempStorage and saveImagesToFallbackLocation return
{path, originalIndex}[] so the caller can correctly look up the matching
data URL from the original images array.

Adds a test with mixed supported/unsupported formats to verify correct
pairing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

No open projects
Status: Triage

Development

Successfully merging this pull request may close these issues.

2 participants