@@ -73,6 +73,28 @@ export const McpExecution = ({
7373 }
7474 } , [ ] )
7575
76+ const tryUnescapeJsonBlob = useCallback ( ( value : string ) : string | undefined => {
77+ // Some MCP servers return JSON "blobs" with escaped quotes/backslashes but WITHOUT
78+ // wrapping the value in a JSON string literal (e.g. `[{\"id\":1}]`).
79+ //
80+ // Avoid manual `replaceAll("\\\\", "\\")` / `replaceAll('\\"', '"')` transformations,
81+ // which can accidentally double-unescape sequences. Instead, ask the JSON parser to
82+ // interpret escape sequences by wrapping the blob into a JSON string literal.
83+ try {
84+ // Intentionally do NOT escape backslashes, so sequences like `\"` and `\\` are
85+ // interpreted as escapes inside the JSON string literal.
86+ // We only escape raw newlines/tabs so the wrapper remains valid JSON.
87+ const jsonStringLiteral = `"${ value
88+ . replaceAll ( "\n" , "\\n" )
89+ . replaceAll ( "\r" , "\\r" )
90+ . replaceAll ( "\t" , "\\t" ) } "`
91+ const decoded = JSON . parse ( jsonStringLiteral )
92+ return typeof decoded === "string" ? decoded : undefined
93+ } catch {
94+ return undefined
95+ }
96+ } , [ ] )
97+
7698 // Try to parse JSON and return both the result and formatted text.
7799 // Handles:
78100 // - minified JSON
@@ -87,8 +109,10 @@ export const McpExecution = ({
87109
88110 // If initial parse fails, try un-escaping common "JSON encoded as a string blob" patterns.
89111 if ( parsed === undefined && ( trimmed . includes ( '\\"' ) || trimmed . includes ( "\\\\" ) ) ) {
90- const unescaped = trimmed . replaceAll ( "\\\\" , "\\" ) . replaceAll ( '\\"' , '"' )
91- parsed = tryParseJsonValue ( unescaped )
112+ const unescaped = tryUnescapeJsonBlob ( trimmed )
113+ if ( unescaped !== undefined ) {
114+ parsed = tryParseJsonValue ( unescaped )
115+ }
92116 }
93117
94118 // If we parsed a string that itself looks like JSON, try parsing again (double-encoded).
@@ -113,7 +137,7 @@ export const McpExecution = ({
113137 formatted : text ,
114138 }
115139 } ,
116- [ looksLikeJson , tryParseJsonValue ] ,
140+ [ looksLikeJson , tryParseJsonValue , tryUnescapeJsonBlob ] ,
117141 )
118142
119143 // Only parse response data when expanded AND complete to avoid parsing partial JSON
0 commit comments