@@ -65,6 +65,20 @@ export const McpExecution = ({
6565 return ( trimmed . startsWith ( "{" ) && trimmed . endsWith ( "}" ) ) || ( trimmed . startsWith ( "[" ) && trimmed . endsWith ( "]" ) )
6666 } , [ ] )
6767
68+ const looksLikeEscapedJsonBlob = useCallback (
69+ ( value : string ) : boolean => {
70+ // Gate the "unescape blob" fallback strictly to avoid mutating arbitrary strings
71+ // that merely contain backslashes.
72+ // Examples we want to handle:
73+ // - `{\"id\":1}`
74+ // - `[{\"id\":1}]`
75+ const trimmed = value . trim ( )
76+ if ( ! looksLikeJson ( trimmed ) ) return false
77+ return / ^ \{ \\ " / . test ( trimmed ) || / ^ \[ \s * \{ \\ " / . test ( trimmed )
78+ } ,
79+ [ looksLikeJson ] ,
80+ )
81+
6882 const tryParseJsonValue = useCallback ( ( value : string ) : unknown | undefined => {
6983 try {
7084 return JSON . parse ( value )
@@ -108,7 +122,7 @@ export const McpExecution = ({
108122 let parsed : unknown | undefined = tryParseJsonValue ( trimmed )
109123
110124 // If initial parse fails, try un-escaping common "JSON encoded as a string blob" patterns.
111- if ( parsed === undefined && ( trimmed . includes ( '\\"' ) || trimmed . includes ( "\\\\" ) ) ) {
125+ if ( parsed === undefined && looksLikeEscapedJsonBlob ( trimmed ) ) {
112126 const unescaped = tryUnescapeJsonBlob ( trimmed )
113127 if ( unescaped !== undefined ) {
114128 parsed = tryParseJsonValue ( unescaped )
@@ -137,7 +151,7 @@ export const McpExecution = ({
137151 formatted : text ,
138152 }
139153 } ,
140- [ looksLikeJson , tryParseJsonValue , tryUnescapeJsonBlob ] ,
154+ [ looksLikeEscapedJsonBlob , looksLikeJson , tryParseJsonValue , tryUnescapeJsonBlob ] ,
141155 )
142156
143157 // Only parse response data when expanded AND complete to avoid parsing partial JSON
0 commit comments