Repository navigation
Decode WebMCP output Chromium leaves as Latin-1 JSON text - #490
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
andrewleesteele
marked this pull request as ready for review
October 26, 2026 22:34
rgarcia
approved these changes
Oct 26, 2026
rgarcia
left a comment
Contributor
There was a problem hiding this comment.
Test makes sense: it covers the buggy shape (Latin-1-only JSON text, both object and JSON-string results) plus the no-op cases (plain Latin-1 text, ASCII JSON text, text with a char above U+00FF, already-parsed objects). Verified TestToolResponseDecodesLatin1JSONOutput fails on main (the latin1 json case comes back as the raw string) and the package passes with -race on this branch. Gating looks right: a string tool result is JSON.stringify'd to a quoted string, so decoding once matches what fixed Chromium returns.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
WebMCP.invokeToolreturnsoutputas a JSON string instead of an object when the tool's result contains a character in U+0080–U+00FF (é,×,ñ) and none above U+00FF. Results with an em dash or an emoji come back as objects.The cause is in Chromium's
InspectorWebMCPAgent::ParseJSON(third_party/blink/renderer/core/inspector/inspector_web_mcp_agent.cc). Blink stores such strings as 8-bit Latin-1 and passes those bytes tocrdtp::json::ConvertJSONToCBOR(span<uint8_t>), which expects UTF-8. The parse fails and the agent falls back toStringValue(result), sotoolRespondedcarries the raw JSON text. Strings with a character above U+00FF are 16-bit and parse correctly. Upstream fixed this withStringUtf8Adaptor. The fix is in 158 (currently Dev); 152 (our headless pin), 156 Stable, and 157 Beta still have the bug.This change parses string output in
WebMCP.toolRespondedwhen it is Latin-1-only, has at least one non-ASCII character, and is valid JSON. That matches what fixed Chromium produces:JSON.stringify, and Chromium turns any result that parses as JSON into a value. A string output that meets these conditions can only come from the failed parse.Once the browser images are on Chromium 158 or later, this is a no-op and can be removed.
Testing
go test -race ./lib/webmcpclient/: passes. The newTestToolResponseDecodesLatin1JSONOutputfails without the fix.🤖 Generated with Claude Code
Note
Low Risk
Narrow, backward-compatible normalization on tool response output only; gated conditions and tests limit blast radius until browser images upgrade past Chromium 158.
Overview
WebMCP tool results that include Latin-1 characters (e.g.
é,×) but nothing above U+00FF were arriving as raw JSON strings instead of parsed values on older Chromium builds. This PR normalizes that atWebMCP.toolRespondedhandling so callers see the same structuredoutputas on Chromium 158+.On each completed tool response,
decodeLatin1Outputruns onoutputbefore the invocation is stored. It only acts whenoutputis a string whose runes are all ≤ U+00FF with at least one in U+0080–U+00FF; it thenjson.Unmarshals the string and replacesoutputwith the decoded value when parsing succeeds. ASCII-only strings, plain Latin-1 text that is not JSON, strings containing wider Unicode, and already-parsed objects are left unchanged.TestToolResponseDecodesLatin1JSONOutputcovers the Latin-1 JSON object/string cases and confirms no-op behavior for the other shapes.Reviewed by Cursor Bugbot for commit adc5d27. Bugbot is set up for automated code reviews on this repo. Configure here.