@@ -63,6 +63,21 @@ def _resolve_headers(headers: dict[str, str | Secret] | None) -> dict[str, str]
6363 return resolved_headers
6464
6565
66+ def extract_first_text (result : str ) -> str :
67+ # Per MCP spec, content[] may contain TextContent, ImageContent, AudioContent, etc.
68+ # Parse only first TextContent block (ToolInvoker requires dict, not list).
69+ parsed : dict = json .loads (result )
70+ content : list = parsed .get ("content" , [])
71+ for block in content :
72+ if isinstance (block , dict ) and block .get ("type" ) == "text" :
73+ text = block .get ("text" , "" )
74+ try :
75+ return json .loads (text )
76+ except (json .JSONDecodeError , TypeError ):
77+ # No TextContent found, return full parsed response as fallback
78+ return text
79+
80+
6681class AsyncExecutor :
6782 """Thread-safe event loop executor for running async code from sync contexts."""
6883
@@ -1088,21 +1103,7 @@ async def invoke() -> Any:
10881103 # Parse JSON to dict only when outputs_to_state is configured.
10891104 # ToolInvoker requires dict for _merge_tool_outputs(); ToolCallResult.result expects str otherwise.
10901105 if self .outputs_to_state :
1091- parsed = json .loads (result )
1092-
1093- # Per MCP spec, content[] may contain TextContent, ImageContent, AudioContent, etc.
1094- # Parse only first TextContent block (ToolInvoker requires dict, not list).
1095- content = parsed .get ("content" , [])
1096- for block in content :
1097- if isinstance (block , dict ) and block .get ("type" ) == "text" :
1098- text = block .get ("text" , "" )
1099- try :
1100- return json .loads (text )
1101- except (json .JSONDecodeError , TypeError ):
1102- return text
1103-
1104- # No TextContent found, return full parsed response as fallback
1105- return parsed
1106+ return self .extract_first_text (result )
11061107
11071108 return result
11081109 except (MCPError , TimeoutError ) as e :
@@ -1133,21 +1134,7 @@ async def ainvoke(self, **kwargs: Any) -> str | dict[str, Any]:
11331134 # Parse JSON to dict only when outputs_to_state is configured.
11341135 # ToolInvoker requires dict for _merge_tool_outputs(); ToolCallResult.result expects str otherwise.
11351136 if self .outputs_to_state :
1136- parsed = json .loads (result )
1137-
1138- # Per MCP spec, content[] may contain TextContent, ImageContent, AudioContent, etc.
1139- # Parse only first TextContent block (ToolInvoker requires dict, not list).
1140- content = parsed .get ("content" , [])
1141- for block in content :
1142- if isinstance (block , dict ) and block .get ("type" ) == "text" :
1143- text = block .get ("text" , "" )
1144- try :
1145- return json .loads (text )
1146- except (json .JSONDecodeError , TypeError ):
1147- return text
1148-
1149- # No TextContent found, return full parsed response as fallback
1150- return parsed
1137+ return extract_first_text (result )
11511138
11521139 return result
11531140 except asyncio .TimeoutError as e :
0 commit comments