Skip to content

Render image URLs in tool output details as inline images#2331

Open
gary149 wants to merge 2 commits into
mainfrom
claude/kind-planck-roskv7
Open

Render image URLs in tool output details as inline images#2331
gary149 wants to merge 2 commits into
mainfrom
claude/kind-planck-roskv7

Conversation

@gary149

@gary149 gary149 commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Context

Images stopped showing in the tool-call output details (e.g. gr1_qwen_image_fast_infer results showed only Image URL: https://...image.webp as raw text).

Investigation traced it end to end:

  • chat-ui renders tool images from MCP base64 {type: "image", data, mimeType} content blocks — that path is unchanged and works.
  • The Gradio space (multimodalart/Qwen-Image-Fast, pinned Gradio 5.39) still returns the base64 image block + the Image URL: text block (verified by calling its MCP endpoint directly).
  • The deployed hf.co/mcp (@huggingface/mcp-services v0.3.19) strips image content blocks for accounts whose MCP settings include the NO_GRADIO_IMAGE_CONTENT ("Skip Gradio Images") flag — GET /api/settings/mcp confirmed the flag is set on the affected account, and an authenticated A/B call confirmed only text blocks survive the proxy.

So the base64 block never reaches chat-ui in that configuration, and the only image reference left is the URL inside the text.

Change

ToolUpdate.svelte now falls back to extracting image URLs (png/jpg/jpeg/webp/gif/avif/svg, query strings allowed) from the output text and renders them inline — only when the output carries no base64 image blocks, so nothing is shown twice in the normal case.

Testing

  • Regex validated against the real Gradio output shape (Image URL: https://...hf.space/--replicas/.../image.webp + seed line), markdown-wrapped URLs, query strings, and non-image URLs (ignored).
  • npm run check: 0 errors (1 pre-existing warning in ChatMessage.svelte).
  • Prettier/ESLint clean; server (310 tests) and ssr workspaces pass. Client browser workspace is opt-in (VITEST_BROWSER=true) and was not run.

https://claude.ai/code/session_01EvvtbhEB86c56RNjf9sPZx


Generated by Claude Code

MCP tool results normally carry images as base64 image content blocks,
which ToolUpdate already renders. Some proxies strip those blocks and
leave only a text block containing the image URL — e.g. hf.co/mcp with
the account-level "Skip Gradio Images" flag reduces Gradio results to
"Image URL: https://...image.webp" — so the tool detail showed only raw
text where an image used to appear.

Fall back to extracting image URLs (png/jpg/webp/gif/avif/svg) from the
output text and rendering them inline when no base64 image blocks are
present in that output.

https://claude.ai/code/session_01EvvtbhEB86c56RNjf9sPZx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bda56587ae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

<img
alt={`Tool result image ${imageIndex + 1}`}
class="max-h-60 cursor-pointer rounded border border-gray-200 dark:border-gray-700"
src={imageUrl}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restrict auto-loaded images to trusted tool output

When a custom or otherwise untrusted MCP tool returns text containing an image-looking URL, expanding the tool details now makes the user's browser fetch that URL automatically. That turns previously inert tool text into a network request that can leak the client IP/referrer origin and can also hit localhost, intranet, or authenticated same-origin GET endpoints if such a URL is present; please gate this on the exact trusted Image URL: field or proxy/sanitize the URL instead of loading every regex match from arbitrary output text.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This doesn't add a new exposure class in this app's threat model: the chat markdown renderer already auto-renders remote <img> (and <video>/<audio>) from model-authored output (marked.ts image: renderer; sanitizeHref only blocks javascript:/data:text/html), with no user interaction. Model output is influenced by the same tool text — any URL a tool wants fetched can already be (and for image tools, routinely is) embedded in the answer as markdown and fetched on render. The tool-details pane is strictly narrower: it only loads after the user expands the block, and tool outputs come from admin-configured MCP_SERVERS, not arbitrary user-supplied servers. The localhost/intranet GET concern applies identically to markdown images today; <img> requests are opaque no-cors GETs either way.

Gating on the literal Image URL: label adds little: a malicious tool can emit the label trivially, so it only filters incidental URLs, while breaking the fallback for MCP servers that return bare image URLs. Proxying server-side would trade a client-side fetch for an SSRF surface plus caching/abuse concerns, which isn't warranted given the above.

Applied the free part: referrerpolicy="no-referrer" on the URL-based images (9fcb5f6), so the page origin isn't disclosed to image hosts — which makes these images leak less than markdown-rendered ones do today.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants