Skip to main content
Glama
test_mcp_resources.py•29.4 kB
""" Step 5 Tests: MCP Resources Implementation Comprehensive test suite for MCP resource functionality: - All 10 resource endpoints with proper URI handling - Resource listing and metadata validation - Pagination support and parameter validation - Error scenarios and MCP protocol compliance - Integration with memory manager and live data """ import pytest from unittest.mock import Mock, AsyncMock, patch from src.resource_handlers import ResourceHandlers from src.memory_manager import QdrantMemoryManager from src.mcp_server import MemoryMCPServer class TestMCPResources: """Test Step 5: MCP Resources Implementation""" @pytest.fixture(autouse=True) def setup(self) -> None: """Set up test environment""" # Mock memory manager self.mock_memory_manager = Mock(spec=QdrantMemoryManager) # Configure mock methods for resource testing self.mock_memory_manager.list_agents = AsyncMock() self.mock_memory_manager.get_agent = AsyncMock() self.mock_memory_manager.query_memory = AsyncMock() # Create resource handlers with mocked dependency self.resource_handlers = ResourceHandlers(self.mock_memory_manager) def teardown_method(self) -> None: """Clean up test environment""" pass # Test 1: Resource Listing def test_list_resources(self) -> None: """Test resource listing returns all 10 expected resources""" resources = self.resource_handlers.list_resources() expected_resources = [ 'agent_registry', 'memory_access_matrix', 'global_memory_catalog', 'learned_memory_insights', 'agent_memory_summary', 'memory_statistics', 'recent_agent_actions', 'memory_health_status', 'system_configuration', 'policy_catalog' ] assert len(resources) == 10 resource_names = [r['name'] for r in resources] for expected_name in expected_resources: assert expected_name in resource_names # Verify resource structure for resource in resources: assert 'name' in resource assert 'description' in resource assert 'mimeType' in resource assert 'uri' in resource assert resource['mimeType'] == 'application/json' assert resource['uri'].startswith('memory://') # Test 2: Agent Registry Resource async def test_agent_registry_resource(self) -> None: """Test agent_registry resource returns proper agent data""" # Mock agent data mock_agents = [ { 'agent_id': 'test-agent-1', 'role': 'developer', 'memory_layers': ['global', 'learned'], 'permissions': { 'can_read': ['global'], 'can_write': [], 'can_admin': [] }, 'active': True }, { 'agent_id': 'test-agent-2', 'role': 'analyst', 'memory_layers': ['global'], 'permissions': { 'can_read': ['global'], 'can_write': [], 'can_admin': [] }, 'active': False } ] self.mock_memory_manager.list_agents.return_value = mock_agents result = await self.resource_handlers.read_resource( 'memory://agent_registry' ) assert result['status'] == 'success' assert result['resource'] == 'agent_registry' assert 'data' in result assert 'agents' in result['data'] assert len(result['data']['agents']) == 2 assert result['data']['metadata']['total_agents'] == 2 assert result['data']['metadata']['active_agents'] == 1 # Verify pagination info assert 'pagination' in result['data'] assert result['data']['pagination']['total_count'] == 2 assert result['data']['pagination']['offset'] == 0 assert result['data']['pagination']['limit'] == 100 async def test_agent_registry_with_pagination(self) -> None: """Test agent_registry resource with pagination parameters""" # Mock large agent list mock_agents = [ {'agent_id': f'agent-{i}', 'role': 'test', 'active': True} for i in range(150) ] self.mock_memory_manager.list_agents.return_value = mock_agents result = await self.resource_handlers.read_resource( 'memory://agent_registry', limit=50, offset=25 ) assert result['status'] == 'success' assert len(result['data']['agents']) == 50 assert result['data']['pagination']['offset'] == 25 assert result['data']['pagination']['limit'] == 50 assert result['data']['pagination']['has_more'] is True assert result['data']['pagination']['total_count'] == 150 # Test 3: Memory Access Matrix Resource async def test_memory_access_matrix_resource(self) -> None: """Test memory_access_matrix resource returns permission mappings""" mock_agents = [ { 'agent_id': 'admin-agent', 'role': 'admin', 'memory_layers': ['global', 'learned', 'agent'], 'permissions': { 'can_read': ['global', 'learned', 'agent'], 'can_write': ['learned', 'agent'], 'can_admin': ['agent'] } }, { 'agent_id': 'read-only-agent', 'role': 'viewer', 'memory_layers': ['global'], 'permissions': { 'can_read': ['global'], 'can_write': [], 'can_admin': [] } } ] self.mock_memory_manager.list_agents.return_value = mock_agents result = await self.resource_handlers.read_resource( 'memory://memory_access_matrix' ) assert result['status'] == 'success' assert result['resource'] == 'memory_access_matrix' assert 'access_matrix' in result['data'] matrix = result['data']['access_matrix'] assert 'admin-agent' in matrix assert 'read-only-agent' in matrix # Verify admin agent permissions admin_data = matrix['admin-agent'] assert admin_data['role'] == 'admin' assert len(admin_data['access_summary']['can_read']) == 3 assert len(admin_data['access_summary']['can_write']) == 2 assert len(admin_data['access_summary']['can_admin']) == 1 # Verify read-only agent permissions readonly_data = matrix['read-only-agent'] assert readonly_data['role'] == 'viewer' assert len(readonly_data['access_summary']['can_read']) == 1 assert len(readonly_data['access_summary']['can_write']) == 0 # Test 4: Global Memory Catalog Resource async def test_global_memory_catalog_resource(self) -> None: """Test global_memory_catalog resource returns memory entries""" mock_memory_results = { 'success': True, 'results': [ { 'id': 'mem-1', 'content': 'Global documentation content', 'score': 0.95, 'metadata': {'source': 'docs.md', 'type': 'documentation'}, 'memory_type': 'global', 'created_at': '2024-12-19T10:00:00Z', 'tags': ['documentation', 'reference'] }, { 'id': 'mem-2', 'content': 'API reference information', 'score': 0.87, 'metadata': {'source': 'api.md', 'type': 'reference'}, 'memory_type': 'global', 'created_at': '2024-12-19T09:00:00Z', 'tags': ['api', 'reference'] } ] } self.mock_memory_manager.query_memory.return_value = ( mock_memory_results ) result = await self.resource_handlers.read_resource( 'memory://global_memory_catalog', limit=10 ) assert result['status'] == 'success' assert result['resource'] == 'global_memory_catalog' assert 'catalog' in result['data'] assert len(result['data']['catalog']) == 2 # Verify first entry structure first_entry = result['data']['catalog'][0] assert first_entry['id'] == 'mem-1' assert first_entry['content'] == 'Global documentation content' assert first_entry['memory_type'] == 'global' assert 'documentation' in first_entry['tags'] # Test 5: Learned Memory Insights Resource async def test_learned_memory_insights_resource(self) -> None: """Test learned_memory_insights resource categorizes content""" mock_learned_results = { 'success': True, 'results': [ { 'id': 'insight-1', 'content': 'Found a common pattern in error handling', 'score': 0.92, 'metadata': {'category': 'patterns'}, 'created_at': '2024-12-19T10:00:00Z', 'tags': ['patterns', 'errors'] }, { 'id': 'insight-2', 'content': ( 'Best practice: always validate input parameters' ), 'score': 0.89, 'metadata': {'category': 'best_practices'}, 'created_at': '2024-12-19T09:30:00Z', 'tags': ['validation', 'best-practices'] }, { 'id': 'insight-3', 'content': ( 'Lesson learned: debugging async issues requires ' 'careful timing' ), 'score': 0.85, 'metadata': {'category': 'lessons'}, 'created_at': '2024-12-19T09:00:00Z', 'tags': ['debugging', 'async'] } ] } self.mock_memory_manager.query_memory.return_value = ( mock_learned_results ) result = await self.resource_handlers.read_resource( 'memory://learned_memory_insights' ) assert result['status'] == 'success' assert result['resource'] == 'learned_memory_insights' assert 'insights' in result['data'] insights = result['data']['insights'] expected_categories = [ 'patterns', 'lessons_learned', 'best_practices', 'troubleshooting', 'other' ] for category in expected_categories: assert category in insights assert isinstance(insights[category], list) # Verify categorization worked (should have at least one item # in patterns, lessons, and best_practices) assert len(insights['patterns']) >= 1 # "pattern" keyword assert len(insights['best_practices']) >= 1 # "best" keyword assert len(insights['lessons_learned']) >= 1 # "lesson" keyword # Test 6: Agent Memory Summary Resource async def test_agent_memory_summary_resource(self) -> None: """Test agent_memory_summary resource for specific agent""" # Mock agent exists mock_agent_result = { 'success': True, 'agent': { 'agent_id': 'test-agent', 'role': 'developer', 'memory_layers': ['global', 'learned', 'agent'], 'permissions': { 'can_read': ['global', 'learned'], 'can_write': ['learned'], 'can_admin': [] } } } # Mock agent-specific memory mock_agent_memory = { 'success': True, 'results': [ { 'id': 'agent-mem-1', 'content': 'Agent specific task', 'created_at': '2024-12-19T10:00:00Z' } ] } # Mock recent actions mock_recent_actions = { 'success': True, 'results': [ { 'id': 'action-1', 'content': 'query: search documentation', 'created_at': '2024-12-19T09:30:00Z' } ] } self.mock_memory_manager.get_agent.return_value = mock_agent_result self.mock_memory_manager.query_memory.side_effect = [ mock_agent_memory, mock_recent_actions ] result = await self.resource_handlers.read_resource( 'memory://agent_memory_summary/test-agent' ) assert result['status'] == 'success' assert result['resource'] == 'agent_memory_summary' assert result['data']['agent_id'] == 'test-agent' assert 'summary' in result['data'] summary = result['data']['summary'] assert 'agent_info' in summary assert 'memory_statistics' in summary assert 'recent_memories' in summary assert 'recent_actions' in summary assert 'activity_summary' in summary # Verify statistics stats = summary['memory_statistics'] assert stats['total_memories'] == 1 assert stats['recent_actions'] == 1 assert 'global' in stats['memory_types_accessible'] async def test_agent_memory_summary_nonexistent_agent(self) -> None: """Test agent_memory_summary resource for nonexistent agent""" # Mock agent not found mock_agent_result = { 'success': False, 'error': 'Agent not found' } self.mock_memory_manager.get_agent.return_value = mock_agent_result result = await self.resource_handlers.read_resource( 'memory://agent_memory_summary/nonexistent-agent' ) assert result['status'] == 'error' assert 'Agent not found' in result['error'] # Test 7: Memory Statistics Resource async def test_memory_statistics_resource(self) -> None: """Test memory_statistics resource returns system-wide stats""" # Mock memory queries for each type mock_global_results = { 'success': True, 'results': [ { 'id': f'global-{i}', 'score': 0.9, 'created_at': '2024-12-19', 'metadata': {} } for i in range(50) ] } mock_learned_results = { 'success': True, 'results': [ { 'id': f'learned-{i}', 'score': 0.8, 'created_at': '2024-12-19', 'metadata': {} } for i in range(30) ] } mock_agent_results = { 'success': True, 'results': [ { 'id': f'agent-{i}', 'score': 0.7, 'created_at': '2024-12-19', 'metadata': {} } for i in range(20) ] } self.mock_memory_manager.query_memory.side_effect = [ mock_global_results, mock_learned_results, mock_agent_results ] # Mock agents for agent statistics mock_agents = [ {'agent_id': 'agent-1', 'role': 'developer', 'active': True}, {'agent_id': 'agent-2', 'role': 'developer', 'active': True}, {'agent_id': 'agent-3', 'role': 'analyst', 'active': False} ] self.mock_memory_manager.list_agents.return_value = mock_agents result = await self.resource_handlers.read_resource( 'memory://memory_statistics' ) assert result['status'] == 'success' assert result['resource'] == 'memory_statistics' assert 'memory_collections' in result['data'] assert 'agent_statistics' in result['data'] assert 'system_overview' in result['data'] # Verify memory collection stats collections = result['data']['memory_collections'] assert 'global' in collections assert 'learned' in collections assert 'agent' in collections assert collections['global']['total_entries'] == 50 assert collections['learned']['total_entries'] == 30 assert collections['agent']['total_entries'] == 20 # Verify agent statistics agent_stats = result['data']['agent_statistics'] assert agent_stats['total_agents'] == 3 assert agent_stats['active_agents'] == 2 assert agent_stats['agents_by_role']['developer'] == 2 assert agent_stats['agents_by_role']['analyst'] == 1 # Verify system overview overview = result['data']['system_overview'] assert overview['total_memories'] == 100 # 50 + 30 + 20 # Test 8: Recent Agent Actions Resource async def test_recent_agent_actions_resource(self) -> None: """Test recent_agent_actions resource returns action logs""" mock_actions_results = { 'success': True, 'results': [ { 'id': 'action-1', 'content': 'agent performed memory query action', 'score': 0.9, 'created_at': '2024-12-19T10:00:00Z', 'metadata': { 'agent_id': 'test-agent-1', 'action_type': 'query' } }, { 'id': 'action-2', 'content': 'agent stored new memory action', 'score': 0.85, 'created_at': '2024-12-19T09:30:00Z', 'metadata': { 'agent_id': 'test-agent-2', 'action_type': 'store' } } ] } self.mock_memory_manager.query_memory.return_value = ( mock_actions_results ) result = await self.resource_handlers.read_resource( 'memory://recent_agent_actions', limit=25 ) assert result['status'] == 'success' assert result['resource'] == 'recent_agent_actions' assert 'actions' in result['data'] assert len(result['data']['actions']) == 2 # Verify action structure first_action = result['data']['actions'][0] assert first_action['id'] == 'action-1' assert first_action['agent_id'] == 'test-agent-1' assert first_action['action_type'] == 'query' assert first_action['timestamp'] == '2024-12-19T10:00:00Z' # Verify pagination assert result['data']['pagination']['limit'] == 25 assert result['data']['pagination']['returned_count'] == 2 # Test 9: Memory Health Status Resource async def test_memory_health_status_resource(self) -> None: """Test memory_health_status resource returns system health""" # Mock successful queries for all memory types mock_successful_query = {'success': True, 'results': []} self.mock_memory_manager.query_memory.return_value = ( mock_successful_query ) # Mock successful agent list self.mock_memory_manager.list_agents.return_value = [ {'agent_id': 'test-agent', 'role': 'test'} ] result = await self.resource_handlers.read_resource( 'memory://memory_health_status' ) assert result['status'] == 'success' assert result['resource'] == 'memory_health_status' assert 'health' in result['data'] health = result['data']['health'] assert health['overall_status'] == 'healthy' assert len(health['issues']) == 0 assert 'collections' in health # Verify all collections are checked collections = health['collections'] expected_collections = ['global', 'learned', 'agent', 'agent_registry'] for collection in expected_collections: assert collection in collections assert collections[collection]['status'] == 'healthy' assert collections[collection]['accessible'] is True async def test_memory_health_status_with_failures(self) -> None: """Test memory_health_status resource with collection failures""" # Mock failures for some collections def mock_query_side_effect(*args, **kwargs): memory_type = kwargs.get('memory_type', 'unknown') if memory_type == 'global': return {'success': False, 'error': 'Connection timeout'} return {'success': True, 'results': []} self.mock_memory_manager.query_memory.side_effect = ( mock_query_side_effect ) self.mock_memory_manager.list_agents.return_value = [] result = await self.resource_handlers.read_resource( 'memory://memory_health_status' ) assert result['status'] == 'success' health = result['data']['health'] assert health['overall_status'] in ['degraded', 'unhealthy'] assert len(health['issues']) > 0 # Verify failed collection is marked as unhealthy assert health['collections']['global']['status'] == 'unhealthy' assert health['collections']['global']['accessible'] is False # Test 10: System Configuration Resource async def test_system_configuration_resource(self) -> None: """Test system_configuration resource returns config data""" with patch('src.resource_handlers.os.getcwd', return_value='/test/dir'), \ patch('src.resource_handlers.os.sys.version', '3.12.0'): result = await self.resource_handlers.read_resource( 'memory://system_configuration' ) assert result['status'] == 'success' assert result['resource'] == 'system_configuration' assert 'configuration' in result['data'] config = result['data']['configuration'] assert 'memory_settings' in config assert 'collections' in config assert 'system_info' in config # Verify memory settings are present memory_settings = config['memory_settings'] assert 'default_memory_type' in memory_settings assert 'chunk_size' in memory_settings assert 'chunk_overlap' in memory_settings assert 'similarity_threshold' in memory_settings # Verify collections configuration collections = config['collections'] assert 'global_memory' in collections assert 'learned_memory' in collections assert 'agent_memory' in collections assert 'agent_registry' in collections # Test 11: Policy Catalog Resource async def test_policy_catalog_resource(self) -> None: """Test policy_catalog resource returns policy information""" result = await self.resource_handlers.read_resource( 'memory://policy_catalog' ) assert result['status'] == 'success' assert result['resource'] == 'policy_catalog' assert 'policy_catalog' in result['data'] # This is currently a placeholder, so verify placeholder structure policy_data = result['data']['policy_catalog'] assert 'policies' in policy_data assert 'metadata' in policy_data assert policy_data['metadata']['policy_system'] == 'placeholder' # Test 12: URI Validation and Error Handling async def test_invalid_uri_scheme(self) -> None: """Test handling of invalid URI schemes""" result = await self.resource_handlers.read_resource( 'invalid://some_resource' ) assert result['status'] == 'error' assert 'Invalid URI scheme' in result['error'] async def test_unknown_resource(self) -> None: """Test handling of unknown resource paths""" result = await self.resource_handlers.read_resource( 'memory://nonexistent_resource' ) assert result['status'] == 'error' assert 'Unknown resource' in result['error'] async def test_resource_exception_handling(self) -> None: """Test exception handling in resource operations""" # Force an exception in list_agents self.mock_memory_manager.list_agents.side_effect = Exception( "Database error" ) result = await self.resource_handlers.read_resource( 'memory://agent_registry' ) assert result['status'] == 'error' assert 'Failed to get agent registry' in result['error'] # Test 13: MCP Server Integration async def test_mcp_server_resource_integration(self) -> None: """Test MCP server resource integration""" with patch('src.mcp_server.ensure_qdrant_running', return_value=True): server = MemoryMCPServer() # Test get_available_resources resources = server.get_available_resources() assert len(resources) == 10 # Test handle_resource_read with patch.object( server.resource_handlers, 'read_resource' ) as mock_read: mock_read.return_value = { 'status': 'success', 'data': {'test': 'data'}, 'resource': 'test_resource' } result = await server.handle_resource_read( 'memory://test_resource', {'limit': 50} ) assert 'contents' in result assert len(result['contents']) == 1 assert result['contents'][0]['uri'] == 'memory://test_resource' assert result['contents'][0]['mimeType'] == 'application/json' assert 'test' in result['contents'][0]['text'] async def test_mcp_server_resource_error_handling(self) -> None: """Test MCP server resource error handling""" with patch('src.mcp_server.ensure_qdrant_running', return_value=True): server = MemoryMCPServer() # Test error response formatting with patch.object( server.resource_handlers, 'read_resource' ) as mock_read: mock_read.return_value = { 'status': 'error', 'error': 'Test error message' } result = await server.handle_resource_read( 'memory://test_resource', {} ) assert 'error' in result assert result['error']['code'] == -32603 assert 'Test error message' in result['error']['message'] # Test 14: Pagination Testing async def test_pagination_parameters(self) -> None: """Test pagination parameter handling across resources""" # Test with large mock dataset large_agent_list = [ {'agent_id': f'agent-{i}', 'role': 'test', 'active': True} for i in range(250) ] self.mock_memory_manager.list_agents.return_value = large_agent_list # Test different pagination scenarios test_cases = [ { 'limit': 50, 'offset': 0, 'expected_count': 50, 'has_more': True }, { 'limit': 50, 'offset': 200, 'expected_count': 50, 'has_more': False }, { 'limit': 100, 'offset': 150, 'expected_count': 100, 'has_more': False }, ] for case in test_cases: result = await self.resource_handlers.read_resource( 'memory://agent_registry', limit=case['limit'], offset=case['offset'] ) assert result['status'] == 'success' assert len(result['data']['agents']) == case['expected_count'] assert result['data']['pagination']['has_more'] == case['has_more'] assert result['data']['pagination']['offset'] == case['offset'] assert result['data']['pagination']['limit'] == case['limit'] if __name__ == '__main__': pytest.main([__file__, '-v'])

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/hannesnortje/MCP'

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