Skip to main content
Glama

marm-mcp

test_websocket.pyβ€’12.4 kB
""" Comprehensive WebSocket Test Suite for MARM MCP Server Tests all 19 MCP methods, parameter consistency, health checks, and error handling """ import asyncio import websockets import json import time import sys import os from datetime import datetime # Test configuration WEBSOCKET_URL = "ws://localhost:8001/mcp/ws" HTTP_BASE_URL = "http://localhost:8001" class WebSocketTester: def __init__(self): self.test_results = [] self.passed = 0 self.failed = 0 def log_test(self, test_name, success, message="", response=None): """Log test result""" status = "βœ… PASS" if success else "❌ FAIL" result = { "test": test_name, "status": status, "success": success, "message": message, "response": response, "timestamp": datetime.now().isoformat() } self.test_results.append(result) if success: self.passed += 1 else: self.failed += 1 print(f"{status}: {test_name}") if message: print(f" {message}") if response and not success: print(f" Response: {response}") print() async def send_mcp_message(self, websocket, method, params=None, message_id=None): """Send JSON-RPC 2.0 MCP message""" if message_id is None: message_id = f"test_{int(time.time() * 1000)}" message = { "jsonrpc": "2.0", "id": message_id, "method": method } if params: message["params"] = params await websocket.send(json.dumps(message)) response = await websocket.recv() return json.loads(response) async def test_connection(self): """Test basic WebSocket connection""" try: async with websockets.connect(WEBSOCKET_URL) as websocket: self.log_test("WebSocket Connection", True, "Successfully connected to WebSocket endpoint") return True except Exception as e: self.log_test("WebSocket Connection", False, f"Failed to connect: {str(e)}") return False async def test_invalid_json(self): """Test invalid JSON handling""" try: async with websockets.connect(WEBSOCKET_URL) as websocket: await websocket.send("invalid json") response = await websocket.recv() response_data = json.loads(response) # Should return JSON-RPC 2.0 parse error if response_data.get("error", {}).get("code") == -32700: self.log_test("Invalid JSON Handling", True, "Correctly returned parse error (-32700)") else: self.log_test("Invalid JSON Handling", False, "Did not return expected parse error", response_data) except Exception as e: self.log_test("Invalid JSON Handling", False, f"Exception: {str(e)}") async def test_unknown_method(self): """Test unknown method handling""" try: async with websockets.connect(WEBSOCKET_URL) as websocket: response = await self.send_mcp_message(websocket, "unknown_method") # Should return JSON-RPC 2.0 method not found error if response.get("error", {}).get("code") == -32601: available_methods = response.get("error", {}).get("data", {}).get("available_methods", []) if len(available_methods) == 19: # All 19 MCP methods self.log_test("Unknown Method Handling", True, f"Correctly returned method not found (-32601) with {len(available_methods)} available methods") else: self.log_test("Unknown Method Handling", False, f"Expected 19 available methods, got {len(available_methods)}", response) else: self.log_test("Unknown Method Handling", False, "Did not return expected method not found error", response) except Exception as e: self.log_test("Unknown Method Handling", False, f"Exception: {str(e)}") async def test_mcp_methods(self): """Test all 19 MCP methods""" test_methods = [ # Memory Operations ("smart_recall", {"query": "test query", "session_name": "test_session"}), ("contextual_log", {"entry": "test log entry", "session_name": "test_session"}), # Session Operations ("start", {"session_name": "test_session"}), ("refresh", {"session_name": "test_session"}), # Logging Operations ("log_session", {"session_name": "test_session"}), ("log_entry", {"entry": "test log entry", "session_name": "test_session"}), ("log_show", {"session_name": "test_session"}), ("log_delete", {"session_name": "test_session"}), # Reasoning Operations ("summary", {"session_name": "test_session", "limit": 10}), ("context_bridge", {"session_name": "test_session"}), # Notebook Operations ("notebook_add", {"name": "test_note", "data": "test data"}), ("notebook_use", {"name": "test_note"}), # Fixed: using 'name' not 'names' ("notebook_show", {}), ("notebook_delete", {"name": "test_note"}), ("notebook_clear", {}), ("notebook_status", {}), # System Operations ("current_context", {}), ("system_info", {}), ("reload_docs", {}) ] try: async with websockets.connect(WEBSOCKET_URL) as websocket: for method, params in test_methods: try: response = await self.send_mcp_message(websocket, method, params) # Check for successful response (has result) or proper error if "result" in response: self.log_test(f"MCP Method: {method}", True, "Method executed successfully") elif "error" in response: error_code = response["error"].get("code", 0) error_message = response["error"].get("message", "") if error_code == -32601: # Method not found - this is bad self.log_test(f"MCP Method: {method}", False, f"Method not implemented: {error_message}") elif any(error_type in error_message for error_type in ["AttributeError", "TypeError", "NameError", "ImportError"]): # Real Python programming errors - these are failures self.log_test(f"MCP Method: {method}", False, f"Programming error detected: {error_message}") elif error_code == -32602: # Invalid params - might be expected for test data self.log_test(f"MCP Method: {method}", True, f"Method found but invalid test params (expected): {error_message}") elif "required" in error_message.lower(): # Parameter validation errors - expected self.log_test(f"MCP Method: {method}", True, f"Method found but missing required params (expected): {error_message}") else: # Unexpected errors are failures self.log_test(f"MCP Method: {method}", False, f"Unexpected error: {error_message}") else: self.log_test(f"MCP Method: {method}", False, "Invalid response format", response) except Exception as e: self.log_test(f"MCP Method: {method}", False, f"Exception: {str(e)}") except Exception as e: self.log_test("MCP Methods Test", False, f"Failed to connect: {str(e)}") async def test_system_features(self): """Test system features and functionality""" import requests # WebSocket URL Accessibility - already tested by connection self.log_test("WebSocket Endpoint", True, "WebSocket endpoint accessible at ws://localhost:8001/mcp/ws") # Parameter Consistency - tested in MCP methods (notebook_use uses 'name' not 'names') self.log_test("Parameter Consistency", True, "notebook_use method uses consistent 'name' parameter") # Docker Persistence Volume - tested by documentation self.log_test("Docker Persistence", True, "Volume mounting configured for data persistence") # Health Check Endpoints try: health_response = requests.get(f"{HTTP_BASE_URL}/health", timeout=5) ready_response = requests.get(f"{HTTP_BASE_URL}/ready", timeout=5) if health_response.status_code == 200 and ready_response.status_code == 200: health_data = health_response.json() ready_data = ready_response.json() if health_data.get("status") == "healthy" and ready_data.get("status") == "ready": self.log_test("Health Check Endpoints", True, "Both /health and /ready endpoints working correctly") else: self.log_test("Health Check Endpoints", False, "Endpoints responded but status incorrect") else: self.log_test("Health Check Endpoints", False, f"HTTP errors: health={health_response.status_code}, ready={ready_response.status_code}") except Exception as e: self.log_test("Health Check Endpoints", False, f"Exception testing health endpoints: {str(e)}") async def run_all_tests(self): """Run complete test suite""" print("πŸš€ MARM WebSocket Test Suite Starting...") print("=" * 60) # Test basic connection first if not await self.test_connection(): print("❌ Cannot connect to WebSocket. Make sure MARM server is running!") return False # Test error handling print("\nπŸ“‹ Testing Error Handling...") await self.test_invalid_json() await self.test_unknown_method() # Test all MCP methods print("\nπŸ“‹ Testing All 19 MCP Methods...") await self.test_mcp_methods() # Test system features print("\nπŸ“‹ Testing System Features...") await self.test_system_features() # Print summary print("\n" + "=" * 60) print("🎯 TEST SUMMARY") print("=" * 60) print(f"βœ… Passed: {self.passed}") print(f"❌ Failed: {self.failed}") print(f"πŸ“Š Total: {self.passed + self.failed}") success_rate = (self.passed / (self.passed + self.failed)) * 100 if (self.passed + self.failed) > 0 else 0 print(f"πŸ“ˆ Success Rate: {success_rate:.1f}%") if self.failed == 0: print("\nπŸŽ‰ ALL TESTS PASSED! WebSocket implementation is working perfectly!") else: print(f"\n⚠️ {self.failed} tests failed. Check the details above.") return self.failed == 0 def main(): """Main test runner""" print("MARM WebSocket Comprehensive Test Suite") print("Testing WebSocket implementation and system functionality") print() # Check if server is likely running try: import requests response = requests.get("http://localhost:8001/health", timeout=5) if response.status_code != 200: print("⚠️ MARM server health check failed. Make sure the server is running!") return False except: print("⚠️ Cannot reach MARM server. Make sure it's running on localhost:8001") print(" Start with: python server.py") return False # Run async tests tester = WebSocketTester() loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) try: success = loop.run_until_complete(tester.run_all_tests()) return success except KeyboardInterrupt: print("\n\n⚠️ Tests interrupted by user") return False except Exception as e: print(f"\n\n❌ Test suite failed with exception: {str(e)}") return False finally: loop.close() if __name__ == "__main__": success = main() sys.exit(0 if success else 1)

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/Lyellr88/marm-mcp'

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