fix(agentic-tools): return binary HTTP responses as base64 envelope#36242
fix(agentic-tools): return binary HTTP responses as base64 envelope#36242fmontes wants to merge 5 commits into
Conversation
response.text() UTF-8-decoded every non-JSON body, corrupting binary
file assets (images, fonts, ico) into U+FFFD. Parse JSON via .json(),
textual content types as strings, and everything else as a base64
{ __dotcmsBinary, contentType, base64, byteLength } envelope so bytes
survive JSON.stringify across the sandbox boundary. Error responses are
read as text so server messages are preserved, with a 25MB cap on the
binary path. Adds a PNG round-trip regression test.
Fixes #36241
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @fmontes's task in 1m 55s —— View job Rollback Safety Analysis
Result: ✅ Safe To Rollback All 5 changed files are TypeScript frontend code in
The change is purely an in-process fix to the MCP adapter's response-parsing logic. Rolling back to N-1 simply reverts to the old Label applied: AI: Safe To Rollback |
🤖 Bedrock Review —
|
Tell the model that binary file-asset endpoints (e.g. /api/v2/assets,
/dA) return a { __dotcmsBinary, contentType, base64, byteLength }
envelope whose base64 is the raw bytes to decode — not text.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes binary HTTP response corruption in the agentic-tools MCP adapter by avoiding UTF-8 .text() decoding for non-textual payloads, returning a base64 “binary envelope” instead so bytes survive the sandbox JSON.stringify boundary.
Changes:
- Added content-type–based response decoding with a base64
BinaryResponseEnvelopepath for non-textual bodies (plusresponseType: 'base64'to force it). - Updated error handling to always read non-OK responses as text for readable server error messages.
- Added Jest regression coverage including a real PNG byte-exact round-trip assertion, and exported envelope helpers from the package entrypoint.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| core-web/libs/agentic-tools/src/lib/http-client.ts | Implements binary-safe response parsing via base64 envelope + type guard, with a binary-size cap. |
| core-web/libs/agentic-tools/src/lib/http-client.spec.ts | Adds regression and behavior tests for JSON/text/+json/forced-base64/error-as-text cases. |
| core-web/libs/agentic-tools/src/index.ts | Exposes BinaryResponseEnvelope and isBinaryResponseEnvelope to consumers. |
Address PR review: - isBinaryResponseEnvelope now also validates contentType (string) and byteLength (number), so a partial object can't pass the guard. - readBinaryResponse rejects early via Content-Length before buffering the body, so an oversized response can't OOM before the cap fires; the post-read check stays as the backstop for absent/lying headers. - Tests for both: oversized Content-Length rejection (asserts the body is never buffered) and malformed-envelope guard cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks for the review. Addressed in 92fca06:
Also hardened the binary size cap to reject early via |
The @nx/js:tsc build compiled src/**/*.spec.ts into the library because tsconfig.lib.json had no exclude — it lacked jest/test-runner types and broke `nx build agentic-tools`. This never surfaced before because the lib had no spec files until this PR added http-client.spec.ts. Exclude *.spec.ts / *.test.ts from the lib build (they still compile under tsconfig.spec.json for the test target). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Proposed Changes
Fixes the MCP
agentic-toolsadapter corrupting binary HTTP responses (libs/agentic-tools/src/lib/http-client.ts).response.text()UTF-8-decoded every non-JSON body, replacing invalid bytes withU+FFFD— so any binary file asset (images, fonts,.ico) pulled via MCPapi.requestcame back irreversibly corrupted. The dotCMS endpoints were already correct; the bug was entirely in the adapter.application/json→.json()text/*,application/xml,application/javascript,+json/+xml,application/x-www-form-urlencoded) →.text()response.arrayBuffer()returned as a base64 envelope{ __dotcmsBinary: true, contentType, base64, byteLength }, so the raw bytes surviveJSON.stringifyacross the sandbox boundary inexecute.ts.MAX_BINARY_RESPONSE_BYTES), matching the existingMAX_REMOTE_FILE_BYTESon the upload side.responseType?: 'auto' | 'base64'option onRequestOptionsto force the binary path regardless of declared content-type.BinaryResponseEnvelopetype +isBinaryResponseEnvelope()guard so consumers can detect and base64-decode the body.Checklist
http-client.spec.tswith a real 1×1 PNG fixture asserting the round-tripped bytes are byte-exact to the source (the regression guard), plus JSON / textual /+json/ forced-base64 / error-as-text cases. All 6 pass; lint clean.Additional Info
The fix mirrors the existing upload path (
Buffer.from(arrayBuffer).toString('base64')).execute.tswas not changed — itsJSON.stringifyboundary now serializes the envelope correctly; consumers can importisBinaryResponseEnvelopeto decode when ready.Fixes #36241
🤖 Generated with Claude Code