#!/usr/bin/env python3 """ Simple MCP Test App - Test MCP connection and user profile loading """ import asyncio import json import logging import sys from pathlib import Path # Import our modules from config import Config, load_config, create_sample_config from mcp_client import MCPClient from cache_manager import CacheManager # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) class MCPTestApp: """Simple test application for MCP functionality""" def __init__(self, config: Config): self.config = config self.mcp_client = MCPClient(config) self.cache_manager = CacheManager(default_ttl=300) async def initialize(self): """Initialize MCP client""" logger.info("Initializing MCP test app...") await self.mcp_client.initialize() async def cleanup(self): """Cleanup resources""" await self.mcp_client.cleanup() async def test_mcp_connection(self): """Test basic MCP connection and list tools""" print("\n" + "="*60) print("MCP CONNECTION TEST") print("="*60) if not self.mcp_client.is_available: print("āŒ MCP server not available") return False print("āœ… MCP server connected") # List available tools tools = await self.mcp_client.list_tools() print(f"šŸ“‹ Found {len(tools)} tools:") if tools: for i, tool in enumerate(tools, 1): print(f" {i}. {tool.name}") if hasattr(tool, 'description') and tool.description: print(f" {tool.description}") else: print(" No tools available") return len(tools) > 0 async def test_user_profile(self): """Test user profile loading and caching""" print("\n" + "="*60) print("USER PROFILE TEST") print("="*60) # Check if user_profile tool is available if not await self.mcp_client.has_tool("user_profile"): print("āŒ user_profile tool not available") return None print("āœ… user_profile tool found") try: # Call user_profile tool print("šŸ“ž Calling user_profile tool...") profile_data = await self.mcp_client.call_tool("user_profile", {}) # Cache the profile self.cache_manager.set("user_profile", profile_data, ttl=3600) print("šŸ’¾ User profile cached (TTL: 1 hour)") # Pretty print the profile print("\n" + "-"*40) print("USER PROFILE DATA:") print("-"*40) print(json.dumps(profile_data, indent=2, default=str)) print("-"*40) return profile_data except Exception as e: print(f"āŒ Error getting user profile: {e}") logger.error(f"User profile error: {e}", exc_info=True) return None async def test_cached_retrieval(self): """Test retrieving cached user profile""" print("\n" + "="*60) print("CACHE RETRIEVAL TEST") print("="*60) # Try to get cached profile cached_profile = self.cache_manager.get("user_profile") if cached_profile: print("āœ… User profile retrieved from cache") print(f"šŸ“Š Cache stats: {self.cache_manager.get_stats()}") return cached_profile else: print("āŒ No cached user profile found") return None async def test_activities_preview(self): """Test getting recent activities if available""" print("\n" + "="*60) print("ACTIVITIES PREVIEW TEST") print("="*60) if not await self.mcp_client.has_tool("get_activities"): print("āŒ get_activities tool not available") return None print("āœ… get_activities tool found") try: print("šŸ“ž Calling get_activities (limit=5)...") activities = await self.mcp_client.call_tool("get_activities", {"limit": 5}) if activities: print(f"šŸ“‹ Retrieved {len(activities)} activities") # Show basic info for each activity print("\nRecent Activities:") for i, activity in enumerate(activities[:3], 1): # Show first 3 activity_type = activity.get('activityType', {}).get('typeKey', 'Unknown') start_time = activity.get('startTimeLocal', 'Unknown time') duration = activity.get('duration', 0) print(f" {i}. {activity_type} - {start_time}") print(f" Duration: {duration // 60}m {duration % 60}s") # Cache activities self.cache_manager.set("recent_activities", activities, ttl=900) print("šŸ’¾ Activities cached (TTL: 15 minutes)") return activities else: print("šŸ“‹ No activities found") return [] except Exception as e: print(f"āŒ Error getting activities: {e}") logger.error(f"Activities error: {e}", exc_info=True) return None async def run_all_tests(self): """Run all tests in sequence""" print("šŸš€ Starting MCP Test Suite") results = {} # Test 1: MCP Connection results['mcp_connection'] = await self.test_mcp_connection() if not results['mcp_connection']: print("\nāŒ MCP connection failed - skipping remaining tests") return results # Test 2: User Profile results['user_profile'] = await self.test_user_profile() # Test 3: Cache Retrieval results['cache_retrieval'] = await self.test_cached_retrieval() # Test 4: Activities Preview (optional) results['activities'] = await self.test_activities_preview() # Summary print("\n" + "="*60) print("TEST SUMMARY") print("="*60) for test_name, result in results.items(): status = "āœ… PASS" if result else "āŒ FAIL" print(f"{test_name.replace('_', ' ').title()}: {status}") # Cache summary cache_stats = self.cache_manager.get_stats() print(f"\nCache Status: {cache_stats['total_entries']} entries") for key in cache_stats['keys']: print(f" - {key}") return results def validate_config(config: Config) -> bool: """Validate configuration for MCP testing""" issues = [] if not config.garth_token: issues.append("GARTH_TOKEN not set") if not config.garth_mcp_server_path: issues.append("garth_mcp_server_path not set") if issues: print("āŒ Configuration issues:") for issue in issues: print(f" - {issue}") print("\nTo fix:") print("1. Run 'uvx garth login' to get GARTH_TOKEN") print("2. Install garth-mcp-server: 'npm install -g garth-mcp-server'") print("3. Update config.yaml with your tokens") return False return True async def main(): """Main entry point""" print("MCP Test App - Simple MCP and User Profile Test") print("=" * 50) try: # Setup config create_sample_config() config = load_config() if not validate_config(config): return # Create and run test app app = MCPTestApp(config) try: await app.initialize() results = await app.run_all_tests() # Exit with appropriate code if all(results.values()): print("\nšŸŽ‰ All tests passed!") sys.exit(0) else: print("\nāš ļø Some tests failed") sys.exit(1) finally: await app.cleanup() except KeyboardInterrupt: print("\nšŸ‘‹ Test interrupted") except Exception as e: print(f"\nšŸ’„ Test error: {e}") logger.error(f"Main error: {e}", exc_info=True) sys.exit(1) if __name__ == "__main__": asyncio.run(main())