Extract the actual value from MCP response structures. MCP responses can be nested in various ways: 1. Direct value (number, string) 2. Dict with "content" containing MCP ToolResult with .content list 3. Dict with "success"/"result" wrapper 4. List of content
(self, result: Any)
| 1263 | return {} |
| 1264 | |
| 1265 | def _extract_result_value(self, result: Any) -> Any: |
| 1266 | """Extract the actual value from MCP response structures. |
| 1267 | |
| 1268 | MCP responses can be nested in various ways: |
| 1269 | 1. Direct value (number, string) |
| 1270 | 2. Dict with "content" containing MCP ToolResult with .content list |
| 1271 | 3. Dict with "success"/"result" wrapper |
| 1272 | 4. List of content blocks [{type: "text", text: "..."}] |
| 1273 | 5. Object with .content attribute (MCP CallToolResult) |
| 1274 | 6. String representation like "content=[{'type': 'text', 'text': '4.2426'}]" |
| 1275 | |
| 1276 | This normalizes all formats to extract the core value for binding. |
| 1277 | """ |
| 1278 | if result is None: |
| 1279 | return None |
| 1280 | |
| 1281 | # Handle string "None" (bug in some MCP responses) |
| 1282 | if result == "None" or result == "null": |
| 1283 | return None |
| 1284 | |
| 1285 | # Handle MCP CallToolResult object (has .content attribute) |
| 1286 | if hasattr(result, "content") and isinstance(result.content, list): |
| 1287 | return self._extract_from_content_list(result.content) |
| 1288 | |
| 1289 | # Handle dict structures |
| 1290 | if isinstance(result, dict): |
| 1291 | # Case: {"content": <MCP ToolResult object>} |
| 1292 | if "content" in result: |
| 1293 | content = result["content"] |
| 1294 | # MCP ToolResult has a .content attribute that's a list |
| 1295 | if hasattr(content, "content"): |
| 1296 | return self._extract_from_content_list(content.content) |
| 1297 | # Or it might be a direct list |
| 1298 | if isinstance(content, list): |
| 1299 | return self._extract_from_content_list(content) |
| 1300 | # Or a string |
| 1301 | if isinstance(content, str): |
| 1302 | return self._try_parse_number(content) |
| 1303 | |
| 1304 | # Case: {"success": true, "result": ...} |
| 1305 | if "success" in result and "result" in result: |
| 1306 | inner = result["result"] |
| 1307 | # Recurse if inner is not None/string "None" |
| 1308 | if inner is not None and inner != "None": |
| 1309 | return self._extract_result_value(inner) |
| 1310 | return None |
| 1311 | |
| 1312 | # Case: {"isError": false, "content": ...} (MCP response wrapper) |
| 1313 | if "isError" in result: |
| 1314 | if result.get("isError"): |
| 1315 | return result.get("error") or result.get("content") |
| 1316 | return self._extract_result_value(result.get("content")) |
| 1317 | |
| 1318 | # Case: {"text": "value"} direct |
| 1319 | if "text" in result and isinstance(result["text"], str): |
| 1320 | return self._try_parse_number(result["text"]) |
| 1321 | |
| 1322 | # Handle list of content blocks directly |