From f1f76b9a99d2fb8707f1f969c810a22f5deac4d3 Mon Sep 17 00:00:00 2001 From: Bill Date: Fri, 7 Nov 2025 14:24:48 -0500 Subject: [PATCH] fix: extract position dict from CallToolResult.structuredContent Fix negative cash bug where ContextInjector._current_position never updated. Root cause: MCP tools return mcp.types.CallToolResult objects, not plain dicts. The isinstance(result, dict) check always failed, preventing _current_position from accumulating trades within a session. This caused all trades to calculate from initial $10,000 position instead of previous trade's ending position, resulting in negative cash balances when total purchases exceeded $10,000. Solution: Extract position dict from CallToolResult.structuredContent field before validating. Maintains backward compatibility by handling both CallToolResult objects (production) and plain dicts (unit tests). Impact: - Fixes negative cash positions (e.g., -$8,768.68 after 11 trades) - Enables proper intra-day position tracking - Validates sufficient cash before each trade based on cumulative position - Trade tool responses now properly accumulate all holdings Testing: - All existing unit tests pass (handle plain dict results) - Production logs confirm structuredContent extraction works - Debug logging shows _current_position now updates after each trade --- agent/context_injector.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/agent/context_injector.py b/agent/context_injector.py index c01c021..42a07bb 100644 --- a/agent/context_injector.py +++ b/agent/context_injector.py @@ -91,15 +91,25 @@ class ContextInjector: # Debug: Log result type and structure print(f"[DEBUG ContextInjector] Trade result type: {type(result)}") print(f"[DEBUG ContextInjector] Trade result: {result}") - print(f"[DEBUG ContextInjector] isinstance(result, dict): {isinstance(result, dict)}") - # Check if result is a valid position dict (not an error) - if isinstance(result, dict) and "error" not in result and "CASH" in result: + # Extract position dict from MCP result + # MCP tools return CallToolResult objects with structuredContent field + position_dict = None + if hasattr(result, 'structuredContent') and result.structuredContent: + position_dict = result.structuredContent + print(f"[DEBUG ContextInjector] Extracted from structuredContent: {position_dict}") + elif isinstance(result, dict): + position_dict = result + print(f"[DEBUG ContextInjector] Using result as dict: {position_dict}") + + # Check if position dict is valid (not an error) and update state + if position_dict and "error" not in position_dict and "CASH" in position_dict: # Update our tracked position with the new state - self._current_position = result.copy() + self._current_position = position_dict.copy() print(f"[DEBUG ContextInjector] Updated _current_position: {self._current_position}") else: print(f"[DEBUG ContextInjector] Did NOT update _current_position - check failed") + print(f"[DEBUG ContextInjector] position_dict: {position_dict}") print(f"[DEBUG ContextInjector] _current_position remains: {self._current_position}") return result