Skip to content

Decode WebMCP output Chromium leaves as Latin-1 JSON text - #490

Merged
andrewleesteele merged 1 commit into
mainfrom
hypeship/webmcp-latin1-output
Oct 27, 2026
Merged

andrewleesteele merged 1 commit into
mainfrom
hypeship/webmcp-latin1-output

Conversation

@andrewleesteele

@andrewleesteele andrewleesteele commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

WebMCP.invokeTool returns output as 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 to crdtp::json::ConvertJSONToCBOR(span<uint8_t>), which expects UTF-8. The parse fails and the agent falls back to StringValue(result), so toolResponded carries the raw JSON text. Strings with a character above U+00FF are 16-bit and parse correctly. Upstream fixed this with StringUtf8Adaptor. 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.toolResponded when it is Latin-1-only, has at least one non-ASCII character, and is valid JSON. That matches what fixed Chromium produces:

  • Blink passes object results through 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.
  • ASCII-only strings, strings with a character above U+00FF, plain non-JSON text, and objects are unchanged.

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 new TestToolResponseDecodesLatin1JSONOutput fails without the fix.
  • Not verified end to end against a live browser.

🤖 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 at WebMCP.toolResponded handling so callers see the same structured output as on Chromium 158+.

On each completed tool response, decodeLatin1Output runs on output before the invocation is stored. It only acts when output is a string whose runes are all ≤ U+00FF with at least one in U+0080–U+00FF; it then json.Unmarshals the string and replaces output with 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.

TestToolResponseDecodesLatin1JSONOutput covers 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.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@andrewleesteele
andrewleesteele marked this pull request as ready for review October 26, 2026 22:34

@rgarcia rgarcia left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

@andrewleesteele
andrewleesteele merged commit d169d23 into main Oct 27, 2026
12 checks passed
@andrewleesteele
andrewleesteele deleted the hypeship/webmcp-latin1-output branch October 27, 2026 02:24
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