#!/usr/bin/env python3 """ Browser Action Debugging Utility This utility helps debug browser automation issues by: 1. Testing MCP server connectivity 2. Validating browser state 3. Testing selector discovery and execution 4. Providing detailed logging for troubleshooting """ import asyncio import logging import json import sys from typing import Dict, Any, List from mcp_chrome_client import MCPChromeClient # Configure logging logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.StreamHandler(sys.stdout), logging.FileHandler('browser_debug.log') ] ) logger = logging.getLogger(__name__) class BrowserActionDebugger: """Debug utility for browser automation issues""" def __init__(self, config: Dict[str, Any]): self.config = config self.client = MCPChromeClient(config) self.logger = logging.getLogger(__name__) async def run_full_diagnostic(self) -> Dict[str, Any]: """Run a comprehensive diagnostic of browser automation""" results = { "connectivity": None, "browser_state": None, "page_content": None, "interactive_elements": None, "selector_tests": [], "action_tests": [] } try: # Test 1: MCP Server Connectivity self.logger.info("šŸ” TEST 1: Testing MCP server connectivity...") results["connectivity"] = await self._test_connectivity() # Test 2: Browser State self.logger.info("šŸ” TEST 2: Checking browser state...") results["browser_state"] = await self._test_browser_state() # Test 3: Page Content self.logger.info("šŸ” TEST 3: Getting page content...") results["page_content"] = await self._test_page_content() # Test 4: Interactive Elements self.logger.info("šŸ” TEST 4: Finding interactive elements...") results["interactive_elements"] = await self._test_interactive_elements() # Test 5: Selector Generation self.logger.info("šŸ” TEST 5: Testing selector generation...") results["selector_tests"] = await self._test_selector_generation() # Test 6: Action Execution self.logger.info("šŸ” TEST 6: Testing action execution...") results["action_tests"] = await self._test_action_execution() except Exception as e: self.logger.error(f"šŸ’„ Diagnostic failed: {e}") results["error"] = str(e) return results async def _test_connectivity(self) -> Dict[str, Any]: """Test MCP server connectivity""" try: await self.client.connect() return { "status": "success", "server_type": self.client.server_type, "server_url": self.client.server_url, "connected": self.client.session is not None } except Exception as e: return { "status": "failed", "error": str(e) } async def _test_browser_state(self) -> Dict[str, Any]: """Test browser state and availability""" try: # Try to get current URL result = await self.client._call_mcp_tool("chrome_get_web_content", { "format": "text", "selector": "title" }) return { "status": "success", "browser_available": True, "page_title": result.get("content", [{}])[0].get("text", "Unknown") if result.get("content") else "Unknown" } except Exception as e: return { "status": "failed", "browser_available": False, "error": str(e) } async def _test_page_content(self) -> Dict[str, Any]: """Test page content retrieval""" try: result = await self.client._call_mcp_tool("chrome_get_web_content", { "format": "text" }) content = result.get("content", []) if content and len(content) > 0: text_content = content[0].get("text", "") return { "status": "success", "content_length": len(text_content), "has_content": len(text_content) > 0, "preview": text_content[:200] + "..." if len(text_content) > 200 else text_content } else: return { "status": "success", "content_length": 0, "has_content": False, "preview": "" } except Exception as e: return { "status": "failed", "error": str(e) } async def _test_interactive_elements(self) -> Dict[str, Any]: """Test interactive element discovery""" try: result = await self.client._call_mcp_tool("chrome_get_interactive_elements", { "types": ["button", "a", "input", "select", "textarea"] }) elements = result.get("elements", []) # Analyze elements element_summary = {} for element in elements: tag = element.get("tagName", "unknown").lower() element_summary[tag] = element_summary.get(tag, 0) + 1 return { "status": "success", "total_elements": len(elements), "element_types": element_summary, "sample_elements": elements[:5] if elements else [] } except Exception as e: return { "status": "failed", "error": str(e) } async def _test_selector_generation(self) -> List[Dict[str, Any]]: """Test selector generation for various elements""" tests = [] try: # Get interactive elements first result = await self.client._call_mcp_tool("chrome_get_interactive_elements", { "types": ["button", "a", "input"] }) elements = result.get("elements", [])[:5] # Test first 5 elements for i, element in enumerate(elements): test_result = { "element_index": i, "element_tag": element.get("tagName", "unknown"), "element_text": element.get("textContent", "")[:50], "element_attributes": element.get("attributes", {}), "generated_selector": None, "selector_valid": False } try: # Generate selector selector = self.client._extract_best_selector(element) test_result["generated_selector"] = selector # Test if selector is valid by trying to use it validation_result = await self.client._call_mcp_tool("chrome_get_web_content", { "selector": selector, "textOnly": False }) test_result["selector_valid"] = validation_result.get("content") is not None except Exception as e: test_result["error"] = str(e) tests.append(test_result) except Exception as e: tests.append({ "error": f"Failed to get elements for selector testing: {e}" }) return tests async def _test_action_execution(self) -> List[Dict[str, Any]]: """Test action execution with safe, non-destructive actions""" tests = [] # Test 1: Try to get page title (safe action) test_result = { "action": "get_page_title", "description": "Safe action to get page title", "status": None, "error": None } try: result = await self.client._call_mcp_tool("chrome_get_web_content", { "selector": "title", "textOnly": True }) test_result["status"] = "success" test_result["result"] = result except Exception as e: test_result["status"] = "failed" test_result["error"] = str(e) tests.append(test_result) # Test 2: Try keyboard action (safe - just Escape key) test_result = { "action": "keyboard_escape", "description": "Safe keyboard action (Escape key)", "status": None, "error": None } try: result = await self.client._call_mcp_tool("chrome_keyboard", { "keys": "Escape" }) test_result["status"] = "success" test_result["result"] = result except Exception as e: test_result["status"] = "failed" test_result["error"] = str(e) tests.append(test_result) return tests async def test_specific_selector(self, selector: str) -> Dict[str, Any]: """Test a specific selector""" self.logger.info(f"šŸ” Testing specific selector: {selector}") result = { "selector": selector, "validation": None, "click_test": None } try: # Test 1: Validate selector exists validation = await self.client._call_mcp_tool("chrome_get_web_content", { "selector": selector, "textOnly": False }) result["validation"] = { "status": "success" if validation.get("content") else "not_found", "content": validation.get("content") } # Test 2: Try clicking (only if element was found) if validation.get("content"): try: click_result = await self.client._call_mcp_tool("chrome_click_element", { "selector": selector }) result["click_test"] = { "status": "success", "result": click_result } except Exception as click_error: result["click_test"] = { "status": "failed", "error": str(click_error) } else: result["click_test"] = { "status": "skipped", "reason": "Element not found" } except Exception as e: result["validation"] = { "status": "failed", "error": str(e) } return result async def cleanup(self): """Cleanup resources""" try: await self.client.disconnect() except Exception as e: self.logger.warning(f"Cleanup warning: {e}") async def main(): """Main function for running diagnostics""" # Default configuration - adjust as needed config = { 'mcp_server_type': 'http', 'mcp_server_url': 'http://localhost:3000/mcp', 'mcp_server_command': '', 'mcp_server_args': [] } debugger = BrowserActionDebugger(config) try: print("šŸš€ Starting Browser Action Diagnostics...") results = await debugger.run_full_diagnostic() print("\n" + "="*60) print("šŸ“Š DIAGNOSTIC RESULTS") print("="*60) for test_name, test_result in results.items(): print(f"\n{test_name.upper()}:") print(json.dumps(test_result, indent=2, default=str)) # Save results to file with open('browser_diagnostic_results.json', 'w') as f: json.dump(results, f, indent=2, default=str) print(f"\nāœ… Diagnostics complete! Results saved to browser_diagnostic_results.json") except Exception as e: print(f"šŸ’„ Diagnostic failed: {e}") finally: await debugger.cleanup() if __name__ == "__main__": asyncio.run(main())