MCPcopy Create free account
hub / github.com/IBM/mcp-cli / _on_tool_result

Method _on_tool_result

src/mcp_cli/chat/tool_processor.py:459–608  ·  view source on GitHub ↗

Callback when a tool completes. ENHANCED: Now includes value binding system for dataflow tracking. - Binds numeric results to $vN identifiers - Tracks per-tool call counts for anti-thrash - Caches results for state tracking

(self, result: CTPToolResult)

Source from the content-addressed store, hash-verified

457 await self.ui_manager.start_tool_execution(display_name, arguments)
458
459 async def _on_tool_result(self, result: CTPToolResult) -> None:
460 """Callback when a tool completes.
461
462 ENHANCED: Now includes value binding system for dataflow tracking.
463 - Binds numeric results to $vN identifiers
464 - Tracks per-tool call counts for anti-thrash
465 - Caches results for state tracking
466 """
467 meta = self._call_metadata.get(result.id)
468 llm_tool_name = meta.llm_tool_name if meta else result.tool
469 execution_tool_name = meta.execution_tool_name if meta else result.tool
470 arguments = meta.arguments if meta else {}
471
472 # For dynamic tools, extract the actual tool name for better logging/caching
473 actual_tool_name = execution_tool_name
474 actual_arguments = arguments
475 if execution_tool_name == DYNAMIC_TOOL_PROXY_NAME and "tool_name" in arguments:
476 actual_tool_name = arguments["tool_name"]
477 actual_arguments = {k: v for k, v in arguments.items() if k != "tool_name"}
478
479 success = result.is_success
480 logger.info(
481 f"Tool result ({actual_tool_name}): success={success}, error='{result.error}'"
482 )
483
484 tool_state = get_agent_tool_state(getattr(self.context, "agent_id", "default"))
485 value_binding = None
486
487 # Cache successful results and create value bindings
488 if success and result.result is not None:
489 # Extract the actual value from MCP response structure
490 actual_result = self._extract_result_value(result.result)
491
492 # Cache result for dedup
493 tool_state.cache_result(actual_tool_name, actual_arguments, actual_result)
494 logger.debug(f"Cached result for {actual_tool_name}: {actual_result}")
495
496 # Create value binding ($v1, $v2, etc.) for dataflow tracking
497 # Only bind "execution" tool results (not discovery tools)
498 if not tool_state.is_discovery_tool(execution_tool_name):
499 value_binding = tool_state.bind_value(
500 actual_tool_name, actual_arguments, actual_result
501 )
502 logger.info(
503 f"Bound value ${value_binding.id} = {actual_result} from {actual_tool_name}"
504 )
505
506 # Record numeric results for runaway detection
507 if isinstance(actual_result, (int, float)):
508 tool_state.record_numeric_result(float(actual_result))
509
510 # Increment tool call counter for budget tracking (with tool name for split budgets)
511 tool_state.increment_tool_call(execution_tool_name)
512
513 # Record tool use for session-aware search boosting
514 # Successful tools get boosted in future search results
515 search_engine = get_search_engine()
516 search_engine.record_tool_use(actual_tool_name, success=success)

Calls 13

_extract_result_valueMethod · 0.95
_format_tool_responseMethod · 0.95
_check_and_launch_appMethod · 0.95
get_agent_tool_stateFunction · 0.90
ToolCallResultClass · 0.90
display_tool_call_resultFunction · 0.90
on_tool_resultMethod · 0.80
getMethod · 0.45