mcp-proxy-adapter 6.9.28__py3-none-any.whl → 6.9.29__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mcp-proxy-adapter might be problematic. Click here for more details.

Files changed (212) hide show
  1. mcp_proxy_adapter/__init__.py +10 -0
  2. mcp_proxy_adapter/__main__.py +8 -21
  3. mcp_proxy_adapter/api/app.py +10 -913
  4. mcp_proxy_adapter/api/core/__init__.py +18 -0
  5. mcp_proxy_adapter/api/core/app_factory.py +243 -0
  6. mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
  7. mcp_proxy_adapter/api/core/registration_manager.py +166 -0
  8. mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
  9. mcp_proxy_adapter/api/handlers.py +78 -199
  10. mcp_proxy_adapter/api/middleware/__init__.py +1 -44
  11. mcp_proxy_adapter/api/middleware/base.py +0 -42
  12. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +0 -85
  13. mcp_proxy_adapter/api/middleware/error_handling.py +1 -127
  14. mcp_proxy_adapter/api/middleware/factory.py +0 -94
  15. mcp_proxy_adapter/api/middleware/logging.py +0 -112
  16. mcp_proxy_adapter/api/middleware/performance.py +0 -35
  17. mcp_proxy_adapter/api/middleware/protocol_middleware.py +2 -98
  18. mcp_proxy_adapter/api/middleware/transport_middleware.py +0 -37
  19. mcp_proxy_adapter/api/middleware/unified_security.py +10 -10
  20. mcp_proxy_adapter/api/middleware/user_info_middleware.py +0 -118
  21. mcp_proxy_adapter/api/openapi/__init__.py +21 -0
  22. mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
  23. mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
  24. mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
  25. mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
  26. mcp_proxy_adapter/api/schemas.py +0 -61
  27. mcp_proxy_adapter/api/tool_integration.py +0 -117
  28. mcp_proxy_adapter/api/tools.py +0 -46
  29. mcp_proxy_adapter/cli/__init__.py +12 -0
  30. mcp_proxy_adapter/cli/commands/__init__.py +15 -0
  31. mcp_proxy_adapter/cli/commands/client.py +100 -0
  32. mcp_proxy_adapter/cli/commands/config_generate.py +21 -0
  33. mcp_proxy_adapter/cli/commands/config_validate.py +36 -0
  34. mcp_proxy_adapter/cli/commands/generate.py +259 -0
  35. mcp_proxy_adapter/cli/commands/server.py +174 -0
  36. mcp_proxy_adapter/cli/commands/sets.py +128 -0
  37. mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
  38. mcp_proxy_adapter/cli/examples/__init__.py +8 -0
  39. mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
  40. mcp_proxy_adapter/cli/examples/https_token.py +96 -0
  41. mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
  42. mcp_proxy_adapter/cli/main.py +63 -0
  43. mcp_proxy_adapter/cli/parser.py +324 -0
  44. mcp_proxy_adapter/cli/validators.py +231 -0
  45. mcp_proxy_adapter/client/jsonrpc_client.py +406 -0
  46. mcp_proxy_adapter/client/proxy.py +45 -0
  47. mcp_proxy_adapter/commands/__init__.py +44 -28
  48. mcp_proxy_adapter/commands/auth_validation_command.py +7 -344
  49. mcp_proxy_adapter/commands/base.py +19 -43
  50. mcp_proxy_adapter/commands/builtin_commands.py +0 -75
  51. mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
  52. mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
  53. mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
  54. mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
  55. mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
  56. mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
  57. mcp_proxy_adapter/commands/catalog_manager.py +58 -928
  58. mcp_proxy_adapter/commands/cert_monitor_command.py +0 -88
  59. mcp_proxy_adapter/commands/certificate_management_command.py +0 -45
  60. mcp_proxy_adapter/commands/command_registry.py +172 -904
  61. mcp_proxy_adapter/commands/config_command.py +0 -28
  62. mcp_proxy_adapter/commands/dependency_container.py +1 -70
  63. mcp_proxy_adapter/commands/dependency_manager.py +0 -128
  64. mcp_proxy_adapter/commands/echo_command.py +0 -34
  65. mcp_proxy_adapter/commands/health_command.py +0 -3
  66. mcp_proxy_adapter/commands/help_command.py +0 -159
  67. mcp_proxy_adapter/commands/hooks.py +0 -137
  68. mcp_proxy_adapter/commands/key_management_command.py +0 -25
  69. mcp_proxy_adapter/commands/load_command.py +7 -78
  70. mcp_proxy_adapter/commands/plugins_command.py +0 -16
  71. mcp_proxy_adapter/commands/protocol_management_command.py +0 -28
  72. mcp_proxy_adapter/commands/proxy_registration_command.py +0 -88
  73. mcp_proxy_adapter/commands/queue_commands.py +750 -0
  74. mcp_proxy_adapter/commands/registration_status_command.py +0 -43
  75. mcp_proxy_adapter/commands/registry/__init__.py +18 -0
  76. mcp_proxy_adapter/commands/registry/command_info.py +103 -0
  77. mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
  78. mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
  79. mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
  80. mcp_proxy_adapter/commands/reload_command.py +0 -80
  81. mcp_proxy_adapter/commands/result.py +25 -77
  82. mcp_proxy_adapter/commands/role_test_command.py +0 -44
  83. mcp_proxy_adapter/commands/roles_management_command.py +0 -199
  84. mcp_proxy_adapter/commands/security_command.py +0 -30
  85. mcp_proxy_adapter/commands/settings_command.py +0 -68
  86. mcp_proxy_adapter/commands/ssl_setup_command.py +0 -42
  87. mcp_proxy_adapter/commands/token_management_command.py +0 -1
  88. mcp_proxy_adapter/commands/transport_management_command.py +0 -20
  89. mcp_proxy_adapter/commands/unload_command.py +0 -71
  90. mcp_proxy_adapter/config.py +15 -626
  91. mcp_proxy_adapter/core/__init__.py +5 -39
  92. mcp_proxy_adapter/core/app_factory.py +14 -36
  93. mcp_proxy_adapter/core/app_runner.py +0 -27
  94. mcp_proxy_adapter/core/auth_validator.py +1 -93
  95. mcp_proxy_adapter/core/certificate/__init__.py +20 -0
  96. mcp_proxy_adapter/core/certificate/certificate_creator.py +371 -0
  97. mcp_proxy_adapter/core/certificate/certificate_extractor.py +183 -0
  98. mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
  99. mcp_proxy_adapter/core/certificate/certificate_validator.py +110 -0
  100. mcp_proxy_adapter/core/certificate/ssl_context_manager.py +70 -0
  101. mcp_proxy_adapter/core/certificate_utils.py +64 -903
  102. mcp_proxy_adapter/core/client.py +0 -6
  103. mcp_proxy_adapter/core/client_manager.py +0 -19
  104. mcp_proxy_adapter/core/client_security.py +0 -2
  105. mcp_proxy_adapter/core/config/__init__.py +18 -0
  106. mcp_proxy_adapter/core/config/config.py +195 -0
  107. mcp_proxy_adapter/core/config/config_factory.py +22 -0
  108. mcp_proxy_adapter/core/config/config_loader.py +66 -0
  109. mcp_proxy_adapter/core/config/feature_manager.py +31 -0
  110. mcp_proxy_adapter/core/config/simple_config.py +112 -0
  111. mcp_proxy_adapter/core/config/simple_config_generator.py +50 -0
  112. mcp_proxy_adapter/core/config/simple_config_validator.py +96 -0
  113. mcp_proxy_adapter/core/config_converter.py +0 -186
  114. mcp_proxy_adapter/core/config_validator.py +96 -1238
  115. mcp_proxy_adapter/core/errors.py +7 -42
  116. mcp_proxy_adapter/core/job_manager.py +54 -0
  117. mcp_proxy_adapter/core/logging.py +2 -22
  118. mcp_proxy_adapter/core/mtls_asgi.py +0 -20
  119. mcp_proxy_adapter/core/mtls_asgi_app.py +0 -12
  120. mcp_proxy_adapter/core/mtls_proxy.py +0 -80
  121. mcp_proxy_adapter/core/mtls_server.py +3 -173
  122. mcp_proxy_adapter/core/protocol_manager.py +1 -191
  123. mcp_proxy_adapter/core/proxy/__init__.py +22 -0
  124. mcp_proxy_adapter/core/proxy/auth_manager.py +27 -0
  125. mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +137 -0
  126. mcp_proxy_adapter/core/proxy/registration_client.py +60 -0
  127. mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
  128. mcp_proxy_adapter/core/proxy_client.py +0 -1
  129. mcp_proxy_adapter/core/proxy_registration.py +36 -913
  130. mcp_proxy_adapter/core/role_utils.py +0 -308
  131. mcp_proxy_adapter/core/security_adapter.py +1 -36
  132. mcp_proxy_adapter/core/security_factory.py +1 -150
  133. mcp_proxy_adapter/core/security_integration.py +0 -33
  134. mcp_proxy_adapter/core/server_adapter.py +1 -40
  135. mcp_proxy_adapter/core/server_engine.py +2 -173
  136. mcp_proxy_adapter/core/settings.py +0 -127
  137. mcp_proxy_adapter/core/signal_handler.py +0 -65
  138. mcp_proxy_adapter/core/ssl_utils.py +19 -137
  139. mcp_proxy_adapter/core/transport_manager.py +0 -151
  140. mcp_proxy_adapter/core/unified_config_adapter.py +1 -193
  141. mcp_proxy_adapter/core/utils.py +1 -182
  142. mcp_proxy_adapter/core/validation/__init__.py +21 -0
  143. mcp_proxy_adapter/core/validation/config_validator.py +211 -0
  144. mcp_proxy_adapter/core/validation/file_validator.py +73 -0
  145. mcp_proxy_adapter/core/validation/protocol_validator.py +191 -0
  146. mcp_proxy_adapter/core/validation/security_validator.py +58 -0
  147. mcp_proxy_adapter/core/validation/validation_result.py +27 -0
  148. mcp_proxy_adapter/custom_openapi.py +33 -652
  149. mcp_proxy_adapter/examples/bugfix_certificate_config.py +0 -23
  150. mcp_proxy_adapter/examples/check_config.py +0 -2
  151. mcp_proxy_adapter/examples/client_usage_example.py +164 -0
  152. mcp_proxy_adapter/examples/config_builder.py +13 -2
  153. mcp_proxy_adapter/examples/config_cli.py +0 -1
  154. mcp_proxy_adapter/examples/create_test_configs.py +0 -46
  155. mcp_proxy_adapter/examples/debug_request_state.py +0 -1
  156. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -47
  157. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -45
  158. mcp_proxy_adapter/examples/full_application/commands/echo_command.py +0 -12
  159. mcp_proxy_adapter/examples/full_application/commands/help_command.py +0 -12
  160. mcp_proxy_adapter/examples/full_application/commands/list_command.py +0 -7
  161. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +0 -2
  162. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -59
  163. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -54
  164. mcp_proxy_adapter/examples/full_application/main.py +186 -150
  165. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +0 -107
  166. mcp_proxy_adapter/examples/full_application/test_minimal_server.py +0 -24
  167. mcp_proxy_adapter/examples/full_application/test_server.py +0 -58
  168. mcp_proxy_adapter/examples/generate_config.py +65 -11
  169. mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
  170. mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
  171. mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
  172. mcp_proxy_adapter/examples/queue_server_example.py +85 -0
  173. mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
  174. mcp_proxy_adapter/examples/required_certificates.py +0 -2
  175. mcp_proxy_adapter/examples/run_full_test_suite.py +0 -29
  176. mcp_proxy_adapter/examples/run_proxy_server.py +31 -71
  177. mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -27
  178. mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
  179. mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
  180. mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
  181. mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
  182. mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
  183. mcp_proxy_adapter/examples/security_test_client.py +24 -1075
  184. mcp_proxy_adapter/examples/setup/__init__.py +24 -0
  185. mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
  186. mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
  187. mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
  188. mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
  189. mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
  190. mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
  191. mcp_proxy_adapter/examples/setup_test_environment.py +133 -1425
  192. mcp_proxy_adapter/examples/test_config.py +0 -3
  193. mcp_proxy_adapter/examples/test_config_builder.py +25 -405
  194. mcp_proxy_adapter/examples/test_examples.py +0 -1
  195. mcp_proxy_adapter/examples/test_framework_complete.py +0 -2
  196. mcp_proxy_adapter/examples/test_mcp_server.py +0 -1
  197. mcp_proxy_adapter/examples/test_protocol_examples.py +0 -1
  198. mcp_proxy_adapter/examples/universal_client.py +0 -6
  199. mcp_proxy_adapter/examples/update_config_certificates.py +0 -1
  200. mcp_proxy_adapter/examples/validate_generator_compatibility.py +0 -1
  201. mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +0 -187
  202. mcp_proxy_adapter/integrations/__init__.py +25 -0
  203. mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
  204. mcp_proxy_adapter/main.py +70 -62
  205. mcp_proxy_adapter/openapi.py +0 -22
  206. mcp_proxy_adapter/version.py +1 -1
  207. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/METADATA +2 -1
  208. mcp_proxy_adapter-6.9.29.dist-info/RECORD +235 -0
  209. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/entry_points.txt +1 -1
  210. mcp_proxy_adapter-6.9.28.dist-info/RECORD +0 -149
  211. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/WHEEL +0 -0
  212. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,211 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Main configuration validator for MCP Proxy Adapter.
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ from pathlib import Path
11
+ from typing import Dict, List, Any, Optional
12
+
13
+ from .file_validator import FileValidator
14
+ from .security_validator import SecurityValidator
15
+ from .protocol_validator import ProtocolValidator
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class ConfigValidator:
21
+ """
22
+ Comprehensive configuration validator for MCP Proxy Adapter.
23
+
24
+ Validates:
25
+ - Required sections and keys
26
+ - File existence for referenced files
27
+ - Feature flag dependencies
28
+ - Protocol-specific requirements
29
+ - Security configuration consistency
30
+ """
31
+
32
+ def __init__(self, config_path: Optional[str] = None):
33
+ """
34
+ Initialize configuration validator.
35
+
36
+ Args:
37
+ config_path: Path to configuration file (optional)
38
+ """
39
+ self.config_path = config_path
40
+ self.config_data: Dict[str, Any] = {}
41
+ self.validation_results: List[ValidationResult] = []
42
+
43
+ def load_config(self, config_path: Optional[str] = None) -> None:
44
+ """
45
+ Load configuration from file.
46
+
47
+ Args:
48
+ config_path: Path to configuration file
49
+ """
50
+ if config_path:
51
+ self.config_path = config_path
52
+
53
+ if not self.config_path:
54
+ raise ValueError("No configuration path provided")
55
+
56
+ try:
57
+ with open(self.config_path, 'r', encoding='utf-8') as f:
58
+ self.config_data = json.load(f)
59
+ logger.info(f"Configuration loaded from {self.config_path}")
60
+ except FileNotFoundError:
61
+ raise FileNotFoundError(f"Configuration file not found: {self.config_path}")
62
+ except json.JSONDecodeError as e:
63
+ raise ValueError(f"Invalid JSON in configuration file: {e}")
64
+ except Exception as e:
65
+ raise RuntimeError(f"Error loading configuration: {e}")
66
+
67
+ def validate_config(self, config_data: Optional[Dict[str, Any]] = None) -> List[ValidationResult]:
68
+ """
69
+ Validate configuration data.
70
+
71
+ Args:
72
+ config_data: Configuration data to validate (optional)
73
+
74
+ Returns:
75
+ List of validation results
76
+ """
77
+ if config_data is not None:
78
+ self.config_data = config_data
79
+
80
+ if not self.config_data:
81
+ raise ValueError("No configuration data to validate")
82
+
83
+ self.validation_results = []
84
+
85
+ # Initialize validators
86
+ file_validator = FileValidator(self.config_data)
87
+ security_validator = SecurityValidator(self.config_data)
88
+ protocol_validator = ProtocolValidator(self.config_data)
89
+
90
+ # Run all validations
91
+ self.validation_results.extend(protocol_validator.validate_required_sections())
92
+ self.validation_results.extend(protocol_validator.validate_protocol_requirements())
93
+ self.validation_results.extend(file_validator.validate_file_existence())
94
+ self.validation_results.extend(security_validator.validate_security_consistency())
95
+ self.validation_results.extend(security_validator.validate_ssl_configuration())
96
+ self.validation_results.extend(security_validator.validate_roles_configuration())
97
+ self.validation_results.extend(security_validator.validate_proxy_registration())
98
+
99
+ # Additional validations
100
+ self._validate_unknown_fields()
101
+ self._validate_uuid_format()
102
+
103
+ return self.validation_results
104
+
105
+ def validate_all(self, config_data: Optional[Dict[str, Any]] = None) -> List[ValidationResult]:
106
+ """
107
+ Validate all aspects of the configuration.
108
+
109
+ Args:
110
+ config_data: Configuration data to validate (optional)
111
+
112
+ Returns:
113
+ List of validation results
114
+ """
115
+ return self.validate_config(config_data)
116
+
117
+ def _validate_unknown_fields(self) -> None:
118
+ """Validate for unknown configuration fields."""
119
+ known_sections = {
120
+ "server", "protocols", "security", "ssl", "auth", "roles",
121
+ "logging", "commands", "proxy_registration", "transport"
122
+ }
123
+
124
+ for section in self.config_data.keys():
125
+ if section not in known_sections:
126
+ self.validation_results.append(ValidationResult(
127
+ level="warning",
128
+ message=f"Unknown configuration section: {section}",
129
+ section=section,
130
+ suggestion="Check if this section is needed or if it's a typo"
131
+ ))
132
+
133
+ def _validate_uuid_format(self) -> None:
134
+ """Validate UUID format in configuration."""
135
+ uuid_fields = ["server.server_id", "proxy_registration.server_id"]
136
+
137
+ for field in uuid_fields:
138
+ value = self._get_nested_value_safe(field)
139
+ if value and not self._is_valid_uuid4(str(value)):
140
+ self.validation_results.append(ValidationResult(
141
+ level="warning",
142
+ message=f"Invalid UUID format in {field}: {value}",
143
+ section=field.split(".")[0],
144
+ key=field.split(".")[1],
145
+ suggestion="Use a valid UUID4 format"
146
+ ))
147
+
148
+ def _is_valid_uuid4(self, uuid_str: str) -> bool:
149
+ """Check if string is a valid UUID4."""
150
+ import re
151
+ uuid_pattern = r'^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
152
+ return bool(re.match(uuid_pattern, uuid_str, re.IGNORECASE))
153
+
154
+ def _get_nested_value_safe(self, key: str, default: Any = None) -> Any:
155
+ """Safely get a nested value from configuration."""
156
+ keys = key.split('.')
157
+ value = self.config_data
158
+
159
+ for k in keys:
160
+ if isinstance(value, dict) and k in value:
161
+ value = value[k]
162
+ else:
163
+ return default
164
+
165
+ return value
166
+
167
+ def get_validation_summary(self) -> Dict[str, Any]:
168
+ """
169
+ Get a summary of validation results.
170
+
171
+ Returns:
172
+ Dictionary with validation summary
173
+ """
174
+ error_count = sum(1 for r in self.validation_results if r.level == "error")
175
+ warning_count = sum(1 for r in self.validation_results if r.level == "warning")
176
+ info_count = sum(1 for r in self.validation_results if r.level == "info")
177
+
178
+ return {
179
+ "total_issues": len(self.validation_results),
180
+ "errors": error_count,
181
+ "warnings": warning_count,
182
+ "info": info_count,
183
+ "is_valid": error_count == 0
184
+ }
185
+
186
+ def print_validation_report(self) -> None:
187
+ """Print a formatted validation report."""
188
+ summary = self.get_validation_summary()
189
+
190
+ print(f"\\n📋 Configuration Validation Report")
191
+ print(f"{'=' * 40}")
192
+ print(f"Total issues: {summary['total_issues']}")
193
+ print(f"Errors: {summary['errors']}")
194
+ print(f"Warnings: {summary['warnings']}")
195
+ print(f"Info: {summary['info']}")
196
+ print(f"Valid: {'✅ Yes' if summary['is_valid'] else '❌ No'}")
197
+
198
+ if self.validation_results:
199
+ print(f"\\n📝 Issues:")
200
+ for i, result in enumerate(self.validation_results, 1):
201
+ level_icon = {"error": "❌", "warning": "⚠️", "info": "ℹ️"}[result.level]
202
+ print(f"{i:2d}. {level_icon} {result.message}")
203
+ if result.section:
204
+ print(f" Section: {result.section}")
205
+ if result.key:
206
+ print(f" Key: {result.key}")
207
+ if result.suggestion:
208
+ print(f" Suggestion: {result.suggestion}")
209
+ print()
210
+
211
+
@@ -0,0 +1,73 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ File validation utilities for MCP Proxy Adapter configuration validation.
6
+ """
7
+
8
+ import os
9
+ import ssl
10
+
11
+ from .validation_result import ValidationResult
12
+
13
+
14
+ class FileValidator:
15
+ """Validator for file-related configuration settings."""
16
+
17
+ def __init__(self, config_data: Dict[str, Any]):
18
+ self.config_data = config_data
19
+ self.validation_results: List[ValidationResult] = []
20
+
21
+
22
+
23
+
24
+
25
+ def _get_nested_value_safe(self, key: str, default: Any = None) -> Any:
26
+ """Safely get a nested value from configuration."""
27
+ keys = key.split('.')
28
+ value = self.config_data
29
+
30
+ for k in keys:
31
+ if isinstance(value, dict) and k in value:
32
+ value = value[k]
33
+ else:
34
+ return default
35
+
36
+ return value
37
+
38
+ def _has_nested_key(self, key: str) -> bool:
39
+ """Check if a nested key exists in configuration."""
40
+ keys = key.split('.')
41
+ value = self.config_data
42
+
43
+ for k in keys:
44
+ if isinstance(value, dict) and k in value:
45
+ value = value[k]
46
+ else:
47
+ return False
48
+
49
+ return True
50
+
51
+ def _is_file_required_for_enabled_features(self, file_key: str) -> bool:
52
+ """Check if a file is required based on enabled features."""
53
+ # SSL files are required if SSL is enabled
54
+ if file_key.startswith("ssl.") or file_key.startswith("transport.ssl."):
55
+ return self._get_nested_value_safe("ssl.enabled", False)
56
+
57
+ # Proxy registration files are required if proxy registration is enabled
58
+ if file_key.startswith("proxy_registration."):
59
+ return self._get_nested_value_safe("proxy_registration.enabled", False)
60
+
61
+ # Log directory is required if logging is enabled
62
+ if file_key == "logging.log_dir":
63
+ return self._get_nested_value_safe("logging.enabled", True)
64
+
65
+ # Command directories are required if commands are enabled
66
+ if file_key.startswith("commands."):
67
+ return self._get_nested_value_safe("commands.enabled", True)
68
+
69
+ # Security files are required if security is enabled
70
+ if file_key.startswith("security."):
71
+ return self._get_nested_value_safe("security.enabled", False)
72
+
73
+ return False
@@ -0,0 +1,191 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Protocol validation utilities for MCP Proxy Adapter configuration validation.
6
+ """
7
+
8
+ import re
9
+ from typing import Dict, List, Any
10
+
11
+ from .validation_result import ValidationResult
12
+
13
+
14
+ class ProtocolValidator:
15
+ """Validator for protocol-related configuration settings."""
16
+
17
+ def __init__(self, config_data: Dict[str, Any]):
18
+ self.config_data = config_data
19
+ self.validation_results: List[ValidationResult] = []
20
+
21
+
22
+
23
+ def _validate_https_requirements(self) -> None:
24
+ """Validate HTTPS-specific requirements."""
25
+ ssl_config = self._get_nested_value_safe("ssl", {})
26
+
27
+ if not ssl_config.get("enabled", False):
28
+ self.validation_results.append(ValidationResult(
29
+ level="error",
30
+ message="HTTPS protocol requires SSL to be enabled",
31
+ section="ssl",
32
+ suggestion="Set ssl.enabled to true"
33
+ ))
34
+
35
+ # Check for required SSL files
36
+ if not ssl_config.get("cert_file"):
37
+ self.validation_results.append(ValidationResult(
38
+ level="error",
39
+ message="HTTPS protocol requires SSL certificate file",
40
+ section="ssl",
41
+ key="cert_file",
42
+ suggestion="Specify ssl.cert_file"
43
+ ))
44
+
45
+ if not ssl_config.get("key_file"):
46
+ self.validation_results.append(ValidationResult(
47
+ level="error",
48
+ message="HTTPS protocol requires SSL key file",
49
+ section="ssl",
50
+ key="key_file",
51
+ suggestion="Specify ssl.key_file"
52
+ ))
53
+
54
+ def _validate_mtls_requirements(self) -> None:
55
+ """Validate mTLS-specific requirements."""
56
+ # mTLS requires HTTPS
57
+ self._validate_https_requirements()
58
+
59
+ ssl_config = self._get_nested_value_safe("ssl", {})
60
+
61
+ # Check for client certificate requirements
62
+ if not ssl_config.get("client_cert"):
63
+ self.validation_results.append(ValidationResult(
64
+ level="error",
65
+ message="mTLS protocol requires client certificate file",
66
+ section="ssl",
67
+ key="client_cert",
68
+ suggestion="Specify ssl.client_cert"
69
+ ))
70
+
71
+ if not ssl_config.get("client_key"):
72
+ self.validation_results.append(ValidationResult(
73
+ level="error",
74
+ message="mTLS protocol requires client key file",
75
+ section="ssl",
76
+ key="client_key",
77
+ suggestion="Specify ssl.client_key"
78
+ ))
79
+
80
+ # Check for client verification
81
+ if not ssl_config.get("verify_client", False):
82
+ self.validation_results.append(ValidationResult(
83
+ level="warning",
84
+ message="mTLS protocol should have client verification enabled",
85
+ section="ssl",
86
+ key="verify_client",
87
+ suggestion="Set ssl.verify_client to true"
88
+ ))
89
+
90
+ def _validate_feature_flags(self) -> None:
91
+ """Validate feature flags based on protocol."""
92
+ protocol = self._get_nested_value_safe("server.protocol", "http")
93
+
94
+ # Check if features are compatible with protocol
95
+ if protocol == "http":
96
+ # HTTP doesn't support SSL features
97
+ if self._get_nested_value_safe("ssl.enabled", False):
98
+ self.validation_results.append(ValidationResult(
99
+ level="warning",
100
+ message="SSL is enabled but protocol is HTTP. Consider using HTTPS",
101
+ section="ssl",
102
+ suggestion="Change protocol to https or disable SSL"
103
+ ))
104
+
105
+ # Check transport configuration
106
+ transport_config = self._get_nested_value_safe("transport", {})
107
+ if transport_config:
108
+ transport_ssl = transport_config.get("ssl", {})
109
+ if transport_ssl.get("enabled", False) and protocol == "http":
110
+ self.validation_results.append(ValidationResult(
111
+ level="warning",
112
+ message="Transport SSL is enabled but protocol is HTTP",
113
+ section="transport.ssl",
114
+ suggestion="Change protocol to https or disable transport SSL"
115
+ ))
116
+
117
+ def _validate_server_section(self) -> None:
118
+ """Validate server section requirements."""
119
+ server_config = self.config_data.get("server", {})
120
+
121
+ # Check required fields
122
+ if "host" not in server_config:
123
+ self.validation_results.append(ValidationResult(
124
+ level="error",
125
+ message="Server host is required",
126
+ section="server",
127
+ key="host",
128
+ suggestion="Add host field to server section"
129
+ ))
130
+
131
+ if "port" not in server_config:
132
+ self.validation_results.append(ValidationResult(
133
+ level="error",
134
+ message="Server port is required",
135
+ section="server",
136
+ key="port",
137
+ suggestion="Add port field to server section"
138
+ ))
139
+
140
+ # Validate port number
141
+ port = server_config.get("port")
142
+ if port is not None:
143
+ if not isinstance(port, int) or not (1 <= port <= 65535):
144
+ self.validation_results.append(ValidationResult(
145
+ level="error",
146
+ message=f"Invalid port number: {port}. Must be between 1 and 65535",
147
+ section="server",
148
+ key="port"
149
+ ))
150
+
151
+ # Validate host format
152
+ host = server_config.get("host")
153
+ if host is not None:
154
+ if not self._is_valid_host(host):
155
+ self.validation_results.append(ValidationResult(
156
+ level="error",
157
+ message=f"Invalid host format: {host}",
158
+ section="server",
159
+ key="host",
160
+ suggestion="Use a valid hostname or IP address"
161
+ ))
162
+
163
+ def _is_valid_host(self, host: str) -> bool:
164
+ """Check if host has valid format."""
165
+ # Check for localhost
166
+ if host in ["localhost", "127.0.0.1", "::1", "0.0.0.0"]:
167
+ return True
168
+
169
+ # Check for IP address
170
+ ip_pattern = r'^(\d{1,3}\.){3}\d{1,3}$'
171
+ if re.match(ip_pattern, host):
172
+ # Validate IP address ranges
173
+ parts = host.split('.')
174
+ return all(0 <= int(part) <= 255 for part in parts)
175
+
176
+ # Check for hostname (basic validation)
177
+ hostname_pattern = r'^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$'
178
+ return bool(re.match(hostname_pattern, host))
179
+
180
+ def _get_nested_value_safe(self, key: str, default: Any = None) -> Any:
181
+ """Safely get a nested value from configuration."""
182
+ keys = key.split('.')
183
+ value = self.config_data
184
+
185
+ for k in keys:
186
+ if isinstance(value, dict) and k in value:
187
+ value = value[k]
188
+ else:
189
+ return default
190
+
191
+ return value
@@ -0,0 +1,58 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Security validation utilities for MCP Proxy Adapter configuration validation.
6
+ """
7
+
8
+ import os
9
+ from typing import Dict, List, Any
10
+
11
+ from .validation_result import ValidationResult
12
+
13
+
14
+ class SecurityValidator:
15
+ """Validator for security-related configuration settings."""
16
+
17
+ def __init__(self, config_data: Dict[str, Any]):
18
+ self.config_data = config_data
19
+ self.validation_results: List[ValidationResult] = []
20
+
21
+
22
+
23
+
24
+
25
+ def _get_nested_value_safe(self, key: str, default: Any = None) -> Any:
26
+ """Safely get a nested value from configuration."""
27
+ keys = key.split('.')
28
+ value = self.config_data
29
+
30
+ for k in keys:
31
+ if isinstance(value, dict) and k in value:
32
+ value = value[k]
33
+ else:
34
+ return default
35
+
36
+ return value
37
+
38
+ def _has_nested_key(self, key: str) -> bool:
39
+ """Check if a nested key exists in configuration."""
40
+ keys = key.split('.')
41
+ value = self.config_data
42
+
43
+ for k in keys:
44
+ if isinstance(value, dict) and k in value:
45
+ value = value[k]
46
+ else:
47
+ return False
48
+
49
+ return True
50
+
51
+ def _is_valid_url(self, url: str) -> bool:
52
+ """Check if URL has valid format."""
53
+ try:
54
+ from urllib.parse import urlparse
55
+ parsed = urlparse(url)
56
+ return bool(parsed.scheme and parsed.netloc)
57
+ except Exception:
58
+ return False
@@ -0,0 +1,27 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Validation result classes for MCP Proxy Adapter configuration validation.
6
+ """
7
+
8
+ from enum import Enum
9
+ from dataclasses import dataclass
10
+ from typing import Optional
11
+
12
+
13
+ class ValidationLevel(Enum):
14
+ """Validation severity levels."""
15
+ ERROR = "error"
16
+ WARNING = "warning"
17
+ INFO = "info"
18
+
19
+
20
+ @dataclass
21
+ class ValidationResult:
22
+ """Result of configuration validation."""
23
+ level: str # "error", "warning", "info"
24
+ message: str
25
+ section: Optional[str] = None
26
+ key: Optional[str] = None
27
+ suggestion: Optional[str] = None