Skip to main content
Glama

MaverickMCP

by wshobson
MIT License
165
  • Apple
test_agents_router_mcp.py23 kB
""" Tests for the new MCP tools in the agents router. Tests the orchestrated_analysis, deep_research_financial, and compare_multi_agent_analysis MCP tools for Claude Desktop integration. """ import uuid from unittest.mock import AsyncMock, MagicMock, patch import pytest from maverick_mcp.api.routers.agents import ( compare_multi_agent_analysis, deep_research_financial, get_or_create_agent, list_available_agents, orchestrated_analysis, ) @pytest.fixture def mock_supervisor_agent(): """Mock SupervisorAgent for testing.""" agent = MagicMock() agent.orchestrate_analysis = AsyncMock( return_value={ "status": "success", "synthesis": "Comprehensive analysis completed", "agents_used": ["market", "research"], "execution_time_ms": 4500, "synthesis_confidence": 0.87, "agent_results": { "market": {"summary": "Strong momentum", "confidence": 0.85}, "research": {"summary": "Solid fundamentals", "confidence": 0.88}, }, "key_recommendations": ["Focus on large-cap tech", "Monitor earnings"], "confidence": 0.87, } ) return agent @pytest.fixture def mock_research_agent(): """Mock DeepResearchAgent for testing.""" agent = MagicMock() agent.conduct_research = AsyncMock( return_value={ "status": "success", "research_findings": [ { "insight": "Strong revenue growth", "confidence": 0.9, "source": "earnings-report", }, { "insight": "Expanding market share", "confidence": 0.85, "source": "market-analysis", }, ], "sources_analyzed": 42, "research_confidence": 0.88, "validation_checks_passed": 35, "total_sources_processed": 50, "content_summaries": [ "Financial performance strong", "Market position improving", ], "citations": [ {"url": "https://example.com/report1", "title": "Q3 Earnings Analysis"}, {"url": "https://example.com/report2", "title": "Market Share Report"}, ], "execution_time_ms": 6500, } ) return agent @pytest.fixture def mock_market_agent(): """Mock MarketAnalysisAgent for testing.""" agent = MagicMock() agent.analyze_market = AsyncMock( return_value={ "status": "success", "summary": "Top momentum stocks identified", "screened_symbols": ["AAPL", "MSFT", "NVDA"], "confidence": 0.82, "results": { "screening_scores": {"AAPL": 0.92, "MSFT": 0.88, "NVDA": 0.95}, "sector_performance": {"Technology": 0.15, "Healthcare": 0.08}, }, "execution_time_ms": 2100, } ) return agent class TestOrchestratedAnalysis: """Test orchestrated_analysis MCP tool.""" @pytest.mark.asyncio async def test_orchestrated_analysis_success(self, mock_supervisor_agent): """Test successful orchestrated analysis.""" with patch( "maverick_mcp.api.routers.agents.get_or_create_agent", return_value=mock_supervisor_agent, ): result = await orchestrated_analysis( query="Analyze tech sector opportunities", persona="moderate", routing_strategy="llm_powered", max_agents=3, parallel_execution=True, ) assert result["status"] == "success" assert result["agent_type"] == "supervisor_orchestrated" assert result["persona"] == "moderate" assert result["routing_strategy"] == "llm_powered" assert "agents_used" in result assert "synthesis_confidence" in result assert "execution_time_ms" in result mock_supervisor_agent.orchestrate_analysis.assert_called_once() @pytest.mark.asyncio async def test_orchestrated_analysis_with_session_id(self, mock_supervisor_agent): """Test orchestrated analysis with provided session ID.""" session_id = "test-session-123" with patch( "maverick_mcp.api.routers.agents.get_or_create_agent", return_value=mock_supervisor_agent, ): result = await orchestrated_analysis( query="Market analysis", session_id=session_id ) assert result["session_id"] == session_id call_args = mock_supervisor_agent.orchestrate_analysis.call_args assert call_args[1]["session_id"] == session_id @pytest.mark.asyncio async def test_orchestrated_analysis_generates_session_id( self, mock_supervisor_agent ): """Test orchestrated analysis generates session ID when not provided.""" with patch( "maverick_mcp.api.routers.agents.get_or_create_agent", return_value=mock_supervisor_agent, ): result = await orchestrated_analysis(query="Market analysis") assert "session_id" in result # Should be a valid UUID format uuid.UUID(result["session_id"]) @pytest.mark.asyncio async def test_orchestrated_analysis_error_handling(self, mock_supervisor_agent): """Test orchestrated analysis error handling.""" mock_supervisor_agent.orchestrate_analysis.side_effect = Exception( "Orchestration failed" ) with patch( "maverick_mcp.api.routers.agents.get_or_create_agent", return_value=mock_supervisor_agent, ): result = await orchestrated_analysis(query="Test error handling") assert result["status"] == "error" assert result["agent_type"] == "supervisor_orchestrated" assert "error" in result assert "Orchestration failed" in result["error"] @pytest.mark.asyncio async def test_orchestrated_analysis_persona_variations( self, mock_supervisor_agent ): """Test orchestrated analysis with different personas.""" personas = ["conservative", "moderate", "aggressive", "day_trader"] with patch( "maverick_mcp.api.routers.agents.get_or_create_agent", return_value=mock_supervisor_agent, ): for persona in personas: result = await orchestrated_analysis( query="Test persona", persona=persona ) assert result["persona"] == persona # Check agent was created with correct persona call_args = mock_supervisor_agent.orchestrate_analysis.call_args assert call_args is not None class TestDeepResearchFinancial: """Test deep_research_financial MCP tool.""" @pytest.mark.asyncio async def test_deep_research_success(self, mock_research_agent): """Test successful deep research.""" with patch( "maverick_mcp.api.routers.agents.get_or_create_agent", return_value=mock_research_agent, ): result = await deep_research_financial( research_topic="AAPL competitive analysis", persona="moderate", research_depth="comprehensive", focus_areas=["fundamentals", "competitive_landscape"], timeframe="90d", ) assert result["status"] == "success" assert result["agent_type"] == "deep_research" assert result["research_topic"] == "AAPL competitive analysis" assert result["research_depth"] == "comprehensive" assert "fundamentals" in result["focus_areas"] assert "competitive_landscape" in result["focus_areas"] assert result["sources_analyzed"] == 42 assert result["research_confidence"] == 0.88 mock_research_agent.conduct_research.assert_called_once() @pytest.mark.asyncio async def test_deep_research_default_focus_areas(self, mock_research_agent): """Test deep research with default focus areas.""" with patch( "maverick_mcp.api.routers.agents.get_or_create_agent", return_value=mock_research_agent, ): result = await deep_research_financial( research_topic="Tesla analysis", focus_areas=None, # Should use defaults ) expected_defaults = [ "fundamentals", "market_sentiment", "competitive_landscape", ] assert result["focus_areas"] == expected_defaults call_args = mock_research_agent.conduct_research.call_args assert call_args[1]["focus_areas"] == expected_defaults @pytest.mark.asyncio async def test_deep_research_depth_variations(self, mock_research_agent): """Test deep research with different depth levels.""" depth_levels = ["basic", "standard", "comprehensive", "exhaustive"] with patch( "maverick_mcp.api.routers.agents.get_or_create_agent", return_value=mock_research_agent, ): for depth in depth_levels: result = await deep_research_financial( research_topic="Test research", research_depth=depth ) assert result["research_depth"] == depth @pytest.mark.asyncio async def test_deep_research_error_handling(self, mock_research_agent): """Test deep research error handling.""" mock_research_agent.conduct_research.side_effect = Exception( "Research API failed" ) with patch( "maverick_mcp.api.routers.agents.get_or_create_agent", return_value=mock_research_agent, ): result = await deep_research_financial(research_topic="Error test") assert result["status"] == "error" assert result["agent_type"] == "deep_research" assert "Research API failed" in result["error"] @pytest.mark.asyncio async def test_deep_research_timeframe_handling(self, mock_research_agent): """Test deep research with different timeframes.""" timeframes = ["7d", "30d", "90d", "1y"] with patch( "maverick_mcp.api.routers.agents.get_or_create_agent", return_value=mock_research_agent, ): for timeframe in timeframes: await deep_research_financial( research_topic="Timeframe test", timeframe=timeframe ) call_args = mock_research_agent.conduct_research.call_args assert call_args[1]["timeframe"] == timeframe class TestCompareMultiAgentAnalysis: """Test compare_multi_agent_analysis MCP tool.""" @pytest.mark.asyncio async def test_compare_agents_success( self, mock_market_agent, mock_supervisor_agent ): """Test successful multi-agent comparison.""" def get_agent_mock(agent_type, persona): if agent_type == "market": return mock_market_agent elif agent_type == "supervisor": return mock_supervisor_agent else: raise ValueError(f"Unknown agent type: {agent_type}") with patch( "maverick_mcp.api.routers.agents.get_or_create_agent", side_effect=get_agent_mock, ): result = await compare_multi_agent_analysis( query="Analyze NVDA stock potential", agent_types=["market", "supervisor"], persona="moderate", ) assert result["status"] == "success" assert result["persona"] == "moderate" assert "comparison" in result assert "market" in result["comparison"] assert "supervisor" in result["comparison"] assert "execution_times_ms" in result # Both agents should have been called mock_market_agent.analyze_market.assert_called_once() mock_supervisor_agent.orchestrate_analysis.assert_called_once() @pytest.mark.asyncio async def test_compare_agents_default_types( self, mock_market_agent, mock_supervisor_agent ): """Test comparison with default agent types.""" def get_agent_mock(agent_type, persona): return ( mock_market_agent if agent_type == "market" else mock_supervisor_agent ) with patch( "maverick_mcp.api.routers.agents.get_or_create_agent", side_effect=get_agent_mock, ): result = await compare_multi_agent_analysis( query="Default comparison test", agent_types=None, # Should use defaults ) # Should use default agent types ["market", "supervisor"] assert "market" in result["agents_compared"] assert "supervisor" in result["agents_compared"] @pytest.mark.asyncio async def test_compare_agents_with_failure( self, mock_market_agent, mock_supervisor_agent ): """Test comparison with one agent failing.""" mock_market_agent.analyze_market.side_effect = Exception("Market agent failed") def get_agent_mock(agent_type, persona): return ( mock_market_agent if agent_type == "market" else mock_supervisor_agent ) with patch( "maverick_mcp.api.routers.agents.get_or_create_agent", side_effect=get_agent_mock, ): result = await compare_multi_agent_analysis( query="Failure handling test", agent_types=["market", "supervisor"] ) assert result["status"] == "success" # Overall success assert "comparison" in result assert "error" in result["comparison"]["market"] assert result["comparison"]["market"]["status"] == "failed" # Supervisor should still succeed assert "summary" in result["comparison"]["supervisor"] @pytest.mark.asyncio async def test_compare_agents_session_id_handling( self, mock_market_agent, mock_supervisor_agent ): """Test session ID handling in agent comparison.""" session_id = "compare-test-456" def get_agent_mock(agent_type, persona): return ( mock_market_agent if agent_type == "market" else mock_supervisor_agent ) with patch( "maverick_mcp.api.routers.agents.get_or_create_agent", side_effect=get_agent_mock, ): await compare_multi_agent_analysis( query="Session ID test", session_id=session_id ) # Check session IDs were properly formatted for each agent market_call_args = mock_market_agent.analyze_market.call_args assert market_call_args[1]["session_id"] == f"{session_id}_market" supervisor_call_args = mock_supervisor_agent.orchestrate_analysis.call_args assert supervisor_call_args[1]["session_id"] == f"{session_id}_supervisor" class TestGetOrCreateAgent: """Test agent factory function.""" @patch.dict("os.environ", {"OPENAI_API_KEY": "test-key"}) def test_create_supervisor_agent(self): """Test creating supervisor agent.""" with patch("maverick_mcp.api.routers.agents.SupervisorAgent") as mock_class: mock_instance = MagicMock() mock_class.return_value = mock_instance agent = get_or_create_agent("supervisor", "moderate") assert agent == mock_instance mock_class.assert_called_once() @patch.dict( "os.environ", { "OPENAI_API_KEY": "test-key", "EXA_API_KEY": "exa-key", "TAVILY_API_KEY": "tavily-key", }, ) def test_create_deep_research_agent_with_api_keys(self): """Test creating deep research agent with API keys.""" with patch("maverick_mcp.api.routers.agents.DeepResearchAgent") as mock_class: mock_instance = MagicMock() mock_class.return_value = mock_instance get_or_create_agent("deep_research", "moderate") # Should pass API keys to constructor call_args = mock_class.call_args assert call_args[1]["exa_api_key"] == "exa-key" assert call_args[1]["tavily_api_key"] == "tavily-key" @patch.dict("os.environ", {"OPENAI_API_KEY": "test-key"}) def test_create_deep_research_agent_without_api_keys(self): """Test creating deep research agent without optional API keys.""" with patch("maverick_mcp.api.routers.agents.DeepResearchAgent") as mock_class: mock_instance = MagicMock() mock_class.return_value = mock_instance get_or_create_agent("deep_research", "moderate") # Should pass None for missing API keys call_args = mock_class.call_args assert call_args[1]["exa_api_key"] is None assert call_args[1]["tavily_api_key"] is None def test_agent_caching(self): """Test agent instance caching.""" with patch("maverick_mcp.api.routers.agents.MarketAnalysisAgent") as mock_class: mock_instance = MagicMock() mock_class.return_value = mock_instance # First call should create agent agent1 = get_or_create_agent("market", "moderate") # Second call should return cached agent agent2 = get_or_create_agent("market", "moderate") assert agent1 == agent2 == mock_instance # Constructor should only be called once due to caching mock_class.assert_called_once() def test_different_personas_create_different_agents(self): """Test different personas create separate cached agents.""" with patch("maverick_mcp.api.routers.agents.MarketAnalysisAgent") as mock_class: mock_class.return_value = MagicMock() agent_moderate = get_or_create_agent("market", "moderate") agent_aggressive = get_or_create_agent("market", "aggressive") # Should create separate instances for different personas assert agent_moderate != agent_aggressive assert mock_class.call_count == 2 def test_invalid_agent_type(self): """Test handling of invalid agent type.""" with pytest.raises(ValueError, match="Unknown agent type"): get_or_create_agent("invalid_agent_type", "moderate") class TestListAvailableAgents: """Test list_available_agents MCP tool.""" def test_list_available_agents_structure(self): """Test the structure of available agents list.""" result = list_available_agents() assert result["status"] == "success" assert "agents" in result assert "orchestrated_tools" in result assert "features" in result def test_active_agents_listed(self): """Test that active agents are properly listed.""" result = list_available_agents() agents = result["agents"] # Check new orchestrated agents assert "supervisor_orchestrated" in agents assert agents["supervisor_orchestrated"]["status"] == "active" assert ( "Multi-agent orchestration" in agents["supervisor_orchestrated"]["description"] ) assert "deep_research" in agents assert agents["deep_research"]["status"] == "active" assert ( "comprehensive financial research" in agents["deep_research"]["description"].lower() ) def test_orchestrated_tools_listed(self): """Test that orchestrated tools are listed.""" result = list_available_agents() tools = result["orchestrated_tools"] assert "orchestrated_analysis" in tools assert "deep_research_financial" in tools assert "compare_multi_agent_analysis" in tools def test_personas_supported(self): """Test that all personas are supported.""" result = list_available_agents() expected_personas = ["conservative", "moderate", "aggressive", "day_trader"] # Check supervisor agent supports all personas supervisor_personas = result["agents"]["supervisor_orchestrated"]["personas"] assert all(persona in supervisor_personas for persona in expected_personas) # Check research agent supports all personas research_personas = result["agents"]["deep_research"]["personas"] assert all(persona in research_personas for persona in expected_personas) def test_capabilities_documented(self): """Test that agent capabilities are documented.""" result = list_available_agents() agents = result["agents"] # Supervisor capabilities supervisor_caps = agents["supervisor_orchestrated"]["capabilities"] assert "Intelligent query routing" in supervisor_caps assert "Multi-agent coordination" in supervisor_caps # Research capabilities research_caps = agents["deep_research"]["capabilities"] assert "Multi-provider web search" in research_caps assert "AI-powered content analysis" in research_caps def test_new_features_documented(self): """Test that new orchestration features are documented.""" result = list_available_agents() features = result["features"] assert "multi_agent_orchestration" in features assert "web_search_research" in features assert "intelligent_routing" in features @pytest.mark.integration class TestAgentRouterIntegration: """Integration tests for agent router MCP tools.""" @pytest.mark.asyncio async def test_end_to_end_orchestrated_workflow(self): """Test complete orchestrated analysis workflow.""" # This would be a full integration test with real or more sophisticated mocks # Testing the complete flow: query -> classification -> agent execution -> synthesis pass @pytest.mark.asyncio async def test_research_agent_with_supervisor_integration(self): """Test research agent working with supervisor.""" # Test how research agent integrates with supervisor routing pass @pytest.mark.asyncio async def test_error_propagation_across_agents(self): """Test how errors propagate through the orchestration system.""" pass if __name__ == "__main__": # Run tests pytest.main([__file__, "-v", "--tb=short"])

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/wshobson/maverick-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server