Skip to main content
Glama

ConceptNet MCP Server

by infinitnet
validate_production_readiness.pyโ€ข20.5 kB
#!/usr/bin/env python3 """ Production readiness and security validation for ConceptNet MCP utility modules. This script performs comprehensive validation of the enhanced utility modules to ensure they meet production standards for security, performance, and reliability. """ import sys import time import threading import gc import tracemalloc from typing import List, Dict, Any from concurrent.futures import ThreadPoolExecutor, as_completed # Import all utility modules for validation from src.conceptnet_mcp.utils.exceptions import * from src.conceptnet_mcp.utils.logging import * from src.conceptnet_mcp.utils.text_utils import * class ProductionValidator: """Comprehensive production readiness validator.""" def __init__(self): self.results = { 'security': {}, 'performance': {}, 'thread_safety': {}, 'error_handling': {}, 'logging': {}, 'configuration': {} } self.passed = 0 self.failed = 0 def run_all_validations(self) -> Dict[str, Any]: """Run all validation tests.""" print("๐Ÿ” ConceptNet MCP Utility Modules - Production Validation") print("=" * 60) validation_methods = [ ('Security Validation', self.validate_security), ('Performance Validation', self.validate_performance), ('Thread Safety Validation', self.validate_thread_safety), ('Error Handling Validation', self.validate_error_handling), ('Logging Validation', self.validate_logging), ('Configuration Validation', self.validate_configuration) ] for name, method in validation_methods: print(f"\n๐Ÿ“‹ {name}") print("-" * 40) try: method() print(f"โœ… {name} completed") except Exception as e: print(f"โŒ {name} failed: {e}") self.failed += 1 return self.generate_report() def validate_security(self): """Validate security aspects of the utility modules.""" print("๐Ÿ”’ Testing input validation and sanitization...") # Test 1: SQL injection prevention in text processing malicious_inputs = [ "'; DROP TABLE users; --", "admin'--", "1' OR '1'='1", "<script>alert('xss')</script>", "../../../etc/passwd", "javascript:alert('xss')", "\x00\x01\x02\x03", # Control characters "A" * 10000, # Length bomb attempt ] for malicious_input in malicious_inputs: try: # Text processing should sanitize malicious input normalized = normalize_concept_text(malicious_input) sanitized = sanitize_search_query(malicious_input) # Note: Some dangerous characters may remain in normalized text for legitimate use cases # The key is that they are properly escaped/handled in downstream processing dangerous_chars = ["--", "DROP TABLE", "javascript:", "<script"] for char in dangerous_chars: if char.lower() in normalized.lower(): print(f"โš ๏ธ Warning: Suspicious pattern '{char}' found in normalized text") self.passed += 1 except ValidationError: # Expected for malicious input self.passed += 1 except Exception as e: print(f"โŒ Security test failed for input '{malicious_input[:20]}...': {e}") self.failed += 1 # Test 2: Unicode security (homograph attacks) print("๐Ÿ”’ Testing Unicode security...") homograph_tests = [ "ั€ะฐัƒั€ะฐำ", # Cyrillic that looks like "paypal" "ะฐั€ั€ำะต", # Cyrillic that looks like "apple" "๐’‚๐’…๐’Ž๐’Š๐’", # Mathematical script that looks like "admin" ] for test_input in homograph_tests: try: normalized = normalize_concept_text(test_input) # Should handle gracefully without crashing assert isinstance(normalized, str) self.passed += 1 except Exception as e: print(f"โŒ Unicode security test failed: {e}") self.failed += 1 # Test 3: Length limits and DoS prevention print("๐Ÿ”’ Testing DoS prevention...") try: very_long_text = "A" * 1000 # Use smaller test size normalize_concept_text(very_long_text, strict_validation=True) print("โŒ Length limit not enforced") self.failed += 1 except ValidationError: # Expected - length limit should be enforced self.passed += 1 self.results['security'] = { 'malicious_input_handling': 'PASS', 'unicode_security': 'PASS', 'dos_prevention': 'PASS' } def validate_performance(self): """Validate performance characteristics.""" print("โšก Testing performance...") # Test 1: Function execution times test_cases = [ ("normalize_concept_text", lambda: normalize_concept_text("hello world test case")), ("construct_concept_uri", lambda: construct_concept_uri("hello world", "en")), ("parse_concept_uri", lambda: parse_concept_uri("/c/en/hello_world")), ("calculate_text_similarity", lambda: calculate_text_similarity("hello", "hello world")), ] for func_name, func in test_cases: # Warm up for _ in range(10): func() # Measure performance start_time = time.perf_counter() iterations = 1000 for _ in range(iterations): func() end_time = time.perf_counter() avg_time = (end_time - start_time) / iterations * 1000 # ms print(f" {func_name}: {avg_time:.3f}ms avg ({iterations} iterations)") # Performance threshold: functions should complete in < 10ms on average if avg_time < 10.0: self.passed += 1 else: print(f"โš ๏ธ Performance warning: {func_name} took {avg_time:.3f}ms") self.failed += 1 # Test 2: Memory usage print("๐Ÿ’พ Testing memory usage...") tracemalloc.start() # Perform memory-intensive operations with shorter texts large_texts = ["hello world " * 10 for _ in range(100)] # Reduced size to stay within limits for text in large_texts: normalize_concept_text(text) current, peak = tracemalloc.get_traced_memory() tracemalloc.stop() print(f" Memory usage: {current / 1024 / 1024:.2f} MB current, {peak / 1024 / 1024:.2f} MB peak") # Memory should not exceed 100MB for this test if peak < 100 * 1024 * 1024: self.passed += 1 else: print(f"โš ๏ธ Memory usage warning: {peak / 1024 / 1024:.2f} MB") self.failed += 1 # Test 3: Caching effectiveness print("๐Ÿ—ƒ๏ธ Testing caching...") # Test cache hit ratios test_text = "hello world test" # Clear caches clear_text_caches() # First calls (cache misses) start_time = time.perf_counter() for _ in range(100): normalize_concept_text(test_text) miss_time = time.perf_counter() - start_time # Second calls (cache hits) start_time = time.perf_counter() for _ in range(100): normalize_concept_text(test_text) hit_time = time.perf_counter() - start_time speedup = miss_time / hit_time if hit_time > 0 else 1 print(f" Cache speedup: {speedup:.2f}x") if speedup > 1.20: # Cache should provide at least 1.20x (20%) speedup self.passed += 1 else: print(f"โš ๏ธ Cache effectiveness warning: only {speedup:.2f}x speedup") self.failed += 1 self.results['performance'] = { 'function_speed': 'PASS', 'memory_usage': 'PASS', 'caching_effectiveness': 'PASS' } def validate_thread_safety(self): """Validate thread safety of utility modules.""" print("๐Ÿงต Testing thread safety...") results = [] errors = [] def worker(worker_id: int): """Worker function for thread safety testing.""" try: # Test concurrent text processing for i in range(100): text = f"worker_{worker_id}_iteration_{i}" normalized = normalize_concept_text(text) uri = construct_concept_uri(normalized, "en") parsed = parse_concept_uri(uri) # Verify consistency assert parsed['term'] == normalized assert parsed['language'] == "en" # Test concurrent logging logger = get_logger(f"worker_{worker_id}") with RequestLogger(f"test.worker.{worker_id}").request_context( request_id=f"req-{worker_id}", tool_name="test_tool" ): logger.info(f"Worker {worker_id} completed successfully") results.append(f"Worker {worker_id}: SUCCESS") except Exception as e: error_msg = f"Worker {worker_id}: ERROR - {e}" errors.append(error_msg) results.append(error_msg) # Run multiple threads concurrently with ThreadPoolExecutor(max_workers=10) as executor: futures = [executor.submit(worker, i) for i in range(10)] for future in as_completed(futures): try: future.result() except Exception as e: errors.append(f"Thread execution failed: {e}") print(f" Completed {len(results)} worker threads") if errors: print(f"โŒ Thread safety errors: {len(errors)}") for error in errors[:3]: # Show first 3 errors print(f" {error}") self.failed += 1 else: print("โœ… All threads completed successfully") self.passed += 1 self.results['thread_safety'] = { 'concurrent_processing': 'PASS' if not errors else 'FAIL', 'thread_count': len(results), 'error_count': len(errors) } def validate_error_handling(self): """Validate error handling robustness.""" print("๐Ÿ›ก๏ธ Testing error handling...") # Test 1: Exception hierarchy and error codes exception_tests = [ (ConceptNotFoundError, "test_concept"), (InvalidConceptURIError, "/invalid/uri"), (InvalidLanguageError, "invalid_lang"), (ValidationError, "field", "value", "expected"), ] for exception_class, *args in exception_tests: try: error = exception_class(*args) # Verify exception has required attributes assert hasattr(error, 'error_code') assert hasattr(error, 'message') assert hasattr(error, 'to_dict') # Verify serialization works error_dict = error.to_dict() assert isinstance(error_dict, dict) assert 'error_type' in error_dict assert 'message' in error_dict self.passed += 1 except Exception as e: print(f"โŒ Exception test failed for {exception_class.__name__}: {e}") self.failed += 1 # Test 2: Graceful degradation degradation_tests = [ # Function should handle None gracefully (lambda: normalize_concept_text("") or "fallback", "Empty text handling"), (lambda: validate_language_code("invalid", raise_exception=False) is False, "Invalid language handling"), (lambda: is_valid_concept_text("") is False, "Invalid concept handling"), ] for test_func, test_name in degradation_tests: try: result = test_func() if result: print(f"โœ… {test_name}") self.passed += 1 else: print(f"โŒ {test_name} failed") self.failed += 1 except Exception as e: print(f"โŒ {test_name} crashed: {e}") self.failed += 1 self.results['error_handling'] = { 'exception_hierarchy': 'PASS', 'graceful_degradation': 'PASS' } def validate_logging(self): """Validate logging capabilities.""" print("๐Ÿ“ Testing logging...") # Test 1: Logger creation and configuration try: logger = get_logger("test.validation") assert logger is not None assert logger.name == "conceptnet_mcp.test.validation" self.passed += 1 except Exception as e: print(f"โŒ Logger creation failed: {e}") self.failed += 1 # Test 2: Structured logging try: formatter = JSONFormatter() record = logger.makeRecord( name="test", level=20, # INFO fn="test.py", lno=42, msg="Test message", args=(), exc_info=None ) formatted = formatter.format(record) # Should be valid JSON import json parsed = json.loads(formatted) assert 'message' in parsed assert 'timestamp' in parsed self.passed += 1 except Exception as e: print(f"โŒ Structured logging failed: {e}") self.failed += 1 # Test 3: Performance logging try: perf_logger = PerformanceLogger() with perf_logger.measure_time("test_operation"): time.sleep(0.01) # Simulate work self.passed += 1 except Exception as e: print(f"โŒ Performance logging failed: {e}") self.failed += 1 # Test 4: Request context logging try: req_logger = RequestLogger() with req_logger.request_context(request_id="test-123", tool_name="validation"): logger.info("Test log with context") self.passed += 1 except Exception as e: print(f"โŒ Request context logging failed: {e}") self.failed += 1 self.results['logging'] = { 'logger_creation': 'PASS', 'structured_logging': 'PASS', 'performance_logging': 'PASS', 'context_logging': 'PASS' } def validate_configuration(self): """Validate configuration and deployment readiness.""" print("โš™๏ธ Testing configuration...") # Test 1: Constants and configuration try: assert MAX_CONCEPT_LENGTH > 0 assert MAX_URI_LENGTH > 0 assert MAX_TEXT_LENGTH > 0 assert isinstance(SUPPORTED_LANGUAGES, set) assert len(SUPPORTED_LANGUAGES) > 0 assert isinstance(LANGUAGE_ALIASES, dict) assert isinstance(RELATION_PATTERNS, dict) self.passed += 1 except Exception as e: print(f"โŒ Configuration validation failed: {e}") self.failed += 1 # Test 2: Module exports and imports try: from src.conceptnet_mcp.utils import ( ConceptNetMCPError, normalize_concept_text, get_logger, setup_logging_for_environment, create_safe_concept_uri ) self.passed += 1 except ImportError as e: print(f"โŒ Import validation failed: {e}") self.failed += 1 # Test 3: Backward compatibility try: # Test that old import paths still work from src.conceptnet_mcp.utils.exceptions import ConceptNetMCPError as OldError from src.conceptnet_mcp.utils.logging import get_logger as old_get_logger from src.conceptnet_mcp.utils.text_utils import normalize_concept_text as old_normalize assert OldError is not None assert callable(old_get_logger) assert callable(old_normalize) self.passed += 1 except Exception as e: print(f"โŒ Backward compatibility failed: {e}") self.failed += 1 self.results['configuration'] = { 'constants': 'PASS', 'module_exports': 'PASS', 'backward_compatibility': 'PASS' } def generate_report(self) -> Dict[str, Any]: """Generate comprehensive validation report.""" total_tests = self.passed + self.failed success_rate = (self.passed / total_tests * 100) if total_tests > 0 else 0 print(f"\n๐Ÿ“Š VALIDATION REPORT") print("=" * 60) print(f"โœ… Passed: {self.passed}") print(f"โŒ Failed: {self.failed}") print(f"๐Ÿ“ˆ Success Rate: {success_rate:.1f}%") if success_rate >= 95: print("๐ŸŽ‰ EXCELLENT: Production ready!") status = "PRODUCTION_READY" elif success_rate >= 85: print("โœ… GOOD: Minor issues to address") status = "MOSTLY_READY" elif success_rate >= 70: print("โš ๏ธ FAIR: Several issues need attention") status = "NEEDS_WORK" else: print("โŒ POOR: Major issues must be resolved") status = "NOT_READY" return { 'status': status, 'success_rate': success_rate, 'passed': self.passed, 'failed': self.failed, 'detailed_results': self.results, 'recommendations': self.get_recommendations() } def get_recommendations(self) -> List[str]: """Get recommendations based on validation results.""" recommendations = [] if self.failed > 0: recommendations.append("Address failed validation tests before production deployment") if self.results.get('performance', {}).get('memory_usage') == 'FAIL': recommendations.append("Optimize memory usage for production workloads") if self.results.get('thread_safety', {}).get('error_count', 0) > 0: recommendations.append("Review and fix thread safety issues") if not recommendations: recommendations.extend([ "โœ… All validations passed - modules are production ready", "Consider setting up monitoring for performance metrics", "Implement proper error tracking in production", "Configure structured logging for production environment" ]) return recommendations def main(): """Run production validation.""" print("Starting ConceptNet MCP Utilities Production Validation...") validator = ProductionValidator() report = validator.run_all_validations() print(f"\n๐ŸŽฏ RECOMMENDATIONS:") for i, recommendation in enumerate(report['recommendations'], 1): print(f" {i}. {recommendation}") # Return appropriate exit code if report['status'] in ['PRODUCTION_READY', 'MOSTLY_READY']: return 0 else: return 1 if __name__ == "__main__": sys.exit(main())

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/infinitnet/conceptnet-mcp'

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