mcp-proxy-adapter 4.1.1__py3-none-any.whl → 6.0.0__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.
Files changed (101) hide show
  1. mcp_proxy_adapter/__main__.py +12 -0
  2. mcp_proxy_adapter/api/app.py +138 -11
  3. mcp_proxy_adapter/api/handlers.py +16 -1
  4. mcp_proxy_adapter/api/middleware/__init__.py +30 -29
  5. mcp_proxy_adapter/api/middleware/auth_adapter.py +235 -0
  6. mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
  7. mcp_proxy_adapter/api/middleware/factory.py +219 -0
  8. mcp_proxy_adapter/api/middleware/logging.py +32 -6
  9. mcp_proxy_adapter/api/middleware/mtls_adapter.py +305 -0
  10. mcp_proxy_adapter/api/middleware/mtls_middleware.py +296 -0
  11. mcp_proxy_adapter/api/middleware/protocol_middleware.py +135 -0
  12. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +241 -0
  13. mcp_proxy_adapter/api/middleware/roles_adapter.py +365 -0
  14. mcp_proxy_adapter/api/middleware/roles_middleware.py +381 -0
  15. mcp_proxy_adapter/api/middleware/security.py +376 -0
  16. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +261 -0
  17. mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
  18. mcp_proxy_adapter/commands/__init__.py +13 -4
  19. mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
  20. mcp_proxy_adapter/commands/base.py +61 -30
  21. mcp_proxy_adapter/commands/builtin_commands.py +89 -0
  22. mcp_proxy_adapter/commands/catalog_manager.py +838 -0
  23. mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
  24. mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
  25. mcp_proxy_adapter/commands/command_registry.py +703 -354
  26. mcp_proxy_adapter/commands/dependency_manager.py +245 -0
  27. mcp_proxy_adapter/commands/health_command.py +7 -0
  28. mcp_proxy_adapter/commands/hooks.py +200 -167
  29. mcp_proxy_adapter/commands/key_management_command.py +506 -0
  30. mcp_proxy_adapter/commands/load_command.py +176 -0
  31. mcp_proxy_adapter/commands/plugins_command.py +235 -0
  32. mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
  33. mcp_proxy_adapter/commands/proxy_registration_command.py +268 -0
  34. mcp_proxy_adapter/commands/reload_command.py +48 -50
  35. mcp_proxy_adapter/commands/result.py +1 -0
  36. mcp_proxy_adapter/commands/roles_management_command.py +697 -0
  37. mcp_proxy_adapter/commands/ssl_setup_command.py +483 -0
  38. mcp_proxy_adapter/commands/token_management_command.py +529 -0
  39. mcp_proxy_adapter/commands/transport_management_command.py +144 -0
  40. mcp_proxy_adapter/commands/unload_command.py +158 -0
  41. mcp_proxy_adapter/config.py +99 -2
  42. mcp_proxy_adapter/core/auth_validator.py +606 -0
  43. mcp_proxy_adapter/core/certificate_utils.py +827 -0
  44. mcp_proxy_adapter/core/config_converter.py +405 -0
  45. mcp_proxy_adapter/core/config_validator.py +218 -0
  46. mcp_proxy_adapter/core/logging.py +11 -0
  47. mcp_proxy_adapter/core/protocol_manager.py +226 -0
  48. mcp_proxy_adapter/core/proxy_registration.py +270 -0
  49. mcp_proxy_adapter/core/role_utils.py +426 -0
  50. mcp_proxy_adapter/core/security_adapter.py +373 -0
  51. mcp_proxy_adapter/core/security_factory.py +239 -0
  52. mcp_proxy_adapter/core/settings.py +1 -0
  53. mcp_proxy_adapter/core/ssl_utils.py +233 -0
  54. mcp_proxy_adapter/core/transport_manager.py +292 -0
  55. mcp_proxy_adapter/custom_openapi.py +22 -11
  56. mcp_proxy_adapter/examples/basic_server/config.json +58 -23
  57. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +54 -0
  58. mcp_proxy_adapter/examples/basic_server/config_http.json +70 -0
  59. mcp_proxy_adapter/examples/basic_server/config_http_only.json +52 -0
  60. mcp_proxy_adapter/examples/basic_server/config_https.json +58 -0
  61. mcp_proxy_adapter/examples/basic_server/config_mtls.json +58 -0
  62. mcp_proxy_adapter/examples/basic_server/config_ssl.json +46 -0
  63. mcp_proxy_adapter/examples/basic_server/server.py +12 -1
  64. mcp_proxy_adapter/examples/custom_commands/__init__.py +1 -1
  65. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +339 -23
  66. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +105 -0
  67. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +129 -0
  68. mcp_proxy_adapter/examples/custom_commands/config.json +101 -18
  69. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +46 -0
  70. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +46 -0
  71. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +33 -0
  72. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +46 -0
  73. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +33 -0
  74. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +33 -0
  75. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +1 -0
  76. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +629 -0
  77. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +103 -0
  78. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +129 -0
  79. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +278 -0
  80. mcp_proxy_adapter/examples/custom_commands/server.py +92 -68
  81. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +75 -0
  82. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +299 -0
  83. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +278 -0
  84. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +27 -0
  85. mcp_proxy_adapter/examples/custom_commands/test_registry.py +23 -0
  86. mcp_proxy_adapter/examples/custom_commands/test_simple.py +19 -0
  87. mcp_proxy_adapter/examples/custom_project_example/README.md +103 -0
  88. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +103 -0
  89. mcp_proxy_adapter/examples/simple_custom_commands/README.md +149 -0
  90. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +149 -0
  91. mcp_proxy_adapter/main.py +175 -0
  92. mcp_proxy_adapter/schemas/roles_schema.json +162 -0
  93. mcp_proxy_adapter/tests/unit/test_config.py +53 -0
  94. mcp_proxy_adapter/version.py +1 -1
  95. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.0.dist-info}/METADATA +2 -1
  96. mcp_proxy_adapter-6.0.0.dist-info/RECORD +179 -0
  97. mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
  98. mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
  99. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.0.dist-info}/WHEEL +0 -0
  100. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.0.dist-info}/licenses/LICENSE +0 -0
  101. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,233 @@
1
+ """
2
+ SSL Utilities Module
3
+
4
+ This module provides utilities for SSL/TLS configuration and certificate validation.
5
+ Integrates with AuthValidator from Phase 0 for certificate validation.
6
+
7
+ Author: MCP Proxy Adapter Team
8
+ Version: 1.0.0
9
+ """
10
+
11
+ import ssl
12
+ import logging
13
+ from typing import List, Optional, Dict, Any
14
+ from pathlib import Path
15
+
16
+ from .auth_validator import AuthValidator
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class SSLUtils:
22
+ """
23
+ SSL utilities for creating SSL contexts and validating certificates.
24
+ """
25
+
26
+ # TLS version mapping
27
+ TLS_VERSIONS = {
28
+ "1.0": ssl.TLSVersion.TLSv1,
29
+ "1.1": ssl.TLSVersion.TLSv1_1,
30
+ "1.2": ssl.TLSVersion.TLSv1_2,
31
+ "1.3": ssl.TLSVersion.TLSv1_3
32
+ }
33
+
34
+ # Cipher suite mapping
35
+ CIPHER_SUITES = {
36
+ "TLS_AES_256_GCM_SHA384": "TLS_AES_256_GCM_SHA384",
37
+ "TLS_CHACHA20_POLY1305_SHA256": "TLS_CHACHA20_POLY1305_SHA256",
38
+ "TLS_AES_128_GCM_SHA256": "TLS_AES_128_GCM_SHA256",
39
+ "ECDHE-RSA-AES256-GCM-SHA384": "ECDHE-RSA-AES256-GCM-SHA384",
40
+ "ECDHE-RSA-AES128-GCM-SHA256": "ECDHE-RSA-AES128-GCM-SHA256",
41
+ "ECDHE-RSA-CHACHA20-POLY1305": "ECDHE-RSA-CHACHA20-POLY1305"
42
+ }
43
+
44
+ @staticmethod
45
+ def create_ssl_context(cert_file: str, key_file: str,
46
+ ca_cert: Optional[str] = None,
47
+ verify_client: bool = False,
48
+ cipher_suites: Optional[List[str]] = None,
49
+ min_tls_version: str = "1.2",
50
+ max_tls_version: str = "1.3") -> ssl.SSLContext:
51
+ """
52
+ Create SSL context with specified configuration.
53
+
54
+ Args:
55
+ cert_file: Path to certificate file
56
+ key_file: Path to private key file
57
+ ca_cert: Path to CA certificate file (optional)
58
+ verify_client: Whether to verify client certificates
59
+ cipher_suites: List of cipher suites to use
60
+ min_tls_version: Minimum TLS version
61
+ max_tls_version: Maximum TLS version
62
+
63
+ Returns:
64
+ Configured SSL context
65
+
66
+ Raises:
67
+ ValueError: If certificate validation fails
68
+ FileNotFoundError: If certificate or key files not found
69
+ """
70
+ # Validate certificate using AuthValidator
71
+ validator = AuthValidator()
72
+ result = validator.validate_certificate(cert_file)
73
+ if not result.is_valid:
74
+ raise ValueError(f"Invalid certificate: {result.error_message}")
75
+
76
+ # Check if files exist
77
+ if not Path(cert_file).exists():
78
+ raise FileNotFoundError(f"Certificate file not found: {cert_file}")
79
+ if not Path(key_file).exists():
80
+ raise FileNotFoundError(f"Key file not found: {key_file}")
81
+ if ca_cert and not Path(ca_cert).exists():
82
+ raise FileNotFoundError(f"CA certificate file not found: {ca_cert}")
83
+
84
+ # Create SSL context
85
+ if verify_client:
86
+ context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
87
+ else:
88
+ context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
89
+
90
+ # Load certificate and key
91
+ context.load_cert_chain(cert_file, key_file)
92
+
93
+ # Load CA certificate if provided
94
+ if ca_cert:
95
+ context.load_verify_locations(ca_cert)
96
+
97
+ # Configure client verification
98
+ if verify_client:
99
+ context.verify_mode = ssl.CERT_REQUIRED
100
+ context.check_hostname = False
101
+ else:
102
+ context.verify_mode = ssl.CERT_NONE
103
+
104
+ # Setup cipher suites
105
+ SSLUtils.setup_cipher_suites(context, cipher_suites or [])
106
+
107
+ # Setup TLS versions
108
+ SSLUtils.setup_tls_versions(context, min_tls_version, max_tls_version)
109
+
110
+ logger.info(f"SSL context created successfully with cert: {cert_file}")
111
+ return context
112
+
113
+ @staticmethod
114
+ def validate_certificate(cert_file: str) -> bool:
115
+ """
116
+ Validate certificate using AuthValidator.
117
+
118
+ Args:
119
+ cert_file: Path to certificate file
120
+
121
+ Returns:
122
+ True if certificate is valid, False otherwise
123
+ """
124
+ try:
125
+ validator = AuthValidator()
126
+ result = validator.validate_certificate(cert_file)
127
+ return result.is_valid
128
+ except Exception as e:
129
+ logger.error(f"Certificate validation failed: {e}")
130
+ return False
131
+
132
+ @staticmethod
133
+ def setup_cipher_suites(context: ssl.SSLContext, cipher_suites: List[str]) -> None:
134
+ """
135
+ Setup cipher suites for SSL context.
136
+
137
+ Args:
138
+ context: SSL context to configure
139
+ cipher_suites: List of cipher suite names
140
+ """
141
+ if not cipher_suites:
142
+ return
143
+
144
+ # Convert cipher suite names to actual cipher suite strings
145
+ actual_ciphers = []
146
+ for cipher_name in cipher_suites:
147
+ if cipher_name in SSLUtils.CIPHER_SUITES:
148
+ actual_ciphers.append(SSLUtils.CIPHER_SUITES[cipher_name])
149
+ else:
150
+ logger.warning(f"Unknown cipher suite: {cipher_name}")
151
+
152
+ if actual_ciphers:
153
+ try:
154
+ context.set_ciphers(":".join(actual_ciphers))
155
+ logger.info(f"Cipher suites configured: {actual_ciphers}")
156
+ except ssl.SSLError as e:
157
+ logger.error(f"Failed to set cipher suites: {e}")
158
+
159
+ @staticmethod
160
+ def setup_tls_versions(context: ssl.SSLContext, min_version: str, max_version: str) -> None:
161
+ """
162
+ Setup TLS version range for SSL context.
163
+
164
+ Args:
165
+ context: SSL context to configure
166
+ min_version: Minimum TLS version
167
+ max_version: Maximum TLS version
168
+ """
169
+ try:
170
+ min_tls = SSLUtils.TLS_VERSIONS.get(min_version)
171
+ max_tls = SSLUtils.TLS_VERSIONS.get(max_version)
172
+
173
+ if min_tls and max_tls:
174
+ context.minimum_version = min_tls
175
+ context.maximum_version = max_tls
176
+ logger.info(f"TLS versions configured: {min_version} - {max_version}")
177
+ else:
178
+ logger.warning(f"Invalid TLS version range: {min_version} - {max_version}")
179
+ except Exception as e:
180
+ logger.error(f"Failed to set TLS versions: {e}")
181
+
182
+ @staticmethod
183
+ def check_tls_version(min_version: str, max_version: str) -> bool:
184
+ """
185
+ Check if TLS version range is valid.
186
+
187
+ Args:
188
+ min_version: Minimum TLS version
189
+ max_version: Maximum TLS version
190
+
191
+ Returns:
192
+ True if version range is valid, False otherwise
193
+ """
194
+ min_tls = SSLUtils.TLS_VERSIONS.get(min_version)
195
+ max_tls = SSLUtils.TLS_VERSIONS.get(max_version)
196
+
197
+ if not min_tls or not max_tls:
198
+ return False
199
+
200
+ # Check if min version is less than or equal to max version
201
+ return min_tls <= max_tls
202
+
203
+ @staticmethod
204
+ def get_ssl_config_for_uvicorn(ssl_config: Dict[str, Any]) -> Dict[str, Any]:
205
+ """
206
+ Get SSL configuration for uvicorn from transport configuration.
207
+
208
+ Args:
209
+ ssl_config: SSL configuration from transport manager
210
+
211
+ Returns:
212
+ Configuration for uvicorn
213
+ """
214
+ uvicorn_ssl = {}
215
+
216
+ if not ssl_config:
217
+ return uvicorn_ssl
218
+
219
+ # Basic SSL parameters
220
+ if ssl_config.get("cert_file"):
221
+ uvicorn_ssl["ssl_certfile"] = ssl_config["cert_file"]
222
+
223
+ if ssl_config.get("key_file"):
224
+ uvicorn_ssl["ssl_keyfile"] = ssl_config["key_file"]
225
+
226
+ if ssl_config.get("ca_cert"):
227
+ uvicorn_ssl["ssl_ca_certs"] = ssl_config["ca_cert"]
228
+
229
+ # Note: uvicorn doesn't support ssl_verify_mode parameter
230
+ # Client verification is handled at the application level
231
+
232
+ logger.info(f"Generated uvicorn SSL config: {uvicorn_ssl}")
233
+ return uvicorn_ssl
@@ -0,0 +1,292 @@
1
+ """
2
+ Transport manager module.
3
+
4
+ This module provides transport management functionality for the MCP Proxy Adapter.
5
+ """
6
+
7
+ from typing import Dict, Any, Optional
8
+ from dataclasses import dataclass
9
+ from enum import Enum
10
+ from pathlib import Path
11
+
12
+ from mcp_proxy_adapter.core.logging import logger
13
+
14
+
15
+ class TransportType(Enum):
16
+ """Transport types enumeration."""
17
+ HTTP = "http"
18
+ HTTPS = "https"
19
+ MTLS = "mtls"
20
+
21
+
22
+ @dataclass
23
+ class TransportConfig:
24
+ """Transport configuration data class."""
25
+ type: TransportType
26
+ port: Optional[int]
27
+ ssl_enabled: bool
28
+ cert_file: Optional[str]
29
+ key_file: Optional[str]
30
+ ca_cert: Optional[str]
31
+ verify_client: bool
32
+ client_cert_required: bool
33
+
34
+
35
+ class TransportManager:
36
+ """
37
+ Transport manager for handling different transport types.
38
+
39
+ This class manages transport configuration and provides utilities
40
+ for determining ports and SSL settings based on transport type.
41
+ """
42
+
43
+ # Default ports for transport types
44
+ DEFAULT_PORTS = {
45
+ TransportType.HTTP: 8000,
46
+ TransportType.HTTPS: 8443,
47
+ TransportType.MTLS: 9443
48
+ }
49
+
50
+ def __init__(self):
51
+ """Initialize transport manager."""
52
+ self._config: Optional[TransportConfig] = None
53
+ self._current_transport: Optional[TransportType] = None
54
+
55
+ def load_config(self, config: Dict[str, Any]) -> bool:
56
+ """
57
+ Load transport configuration from config dict.
58
+
59
+ Args:
60
+ config: Configuration dictionary
61
+
62
+ Returns:
63
+ True if config loaded successfully, False otherwise
64
+ """
65
+ try:
66
+ transport_config = config.get("transport", {})
67
+
68
+ # Get transport type
69
+ transport_type_str = transport_config.get("type", "http").lower()
70
+ try:
71
+ transport_type = TransportType(transport_type_str)
72
+ except ValueError:
73
+ logger.error(f"Invalid transport type: {transport_type_str}")
74
+ return False
75
+
76
+ # Get port (use default if not specified)
77
+ port = transport_config.get("port")
78
+ if port is None:
79
+ port = self.DEFAULT_PORTS.get(transport_type, 8000)
80
+
81
+ # Get SSL configuration
82
+ ssl_config = transport_config.get("ssl", {})
83
+ ssl_enabled = ssl_config.get("enabled", False)
84
+
85
+ # Validate SSL requirements
86
+ if transport_type in [TransportType.HTTPS, TransportType.MTLS] and not ssl_enabled:
87
+ logger.error(f"SSL must be enabled for transport type: {transport_type.value}")
88
+ return False
89
+
90
+ if transport_type == TransportType.HTTP and ssl_enabled:
91
+ logger.warning("SSL enabled for HTTP transport - this may cause issues")
92
+
93
+ # Create transport config
94
+ self._config = TransportConfig(
95
+ type=transport_type,
96
+ port=port,
97
+ ssl_enabled=ssl_enabled,
98
+ cert_file=ssl_config.get("cert_file") if ssl_enabled else None,
99
+ key_file=ssl_config.get("key_file") if ssl_enabled else None,
100
+ ca_cert=ssl_config.get("ca_cert") if ssl_enabled else None,
101
+ verify_client=ssl_config.get("verify_client", False),
102
+ client_cert_required=ssl_config.get("client_cert_required", False)
103
+ )
104
+
105
+ self._current_transport = transport_type
106
+
107
+ logger.info(f"Transport config loaded: {transport_type.value} on port {port}")
108
+ return True
109
+
110
+ except Exception as e:
111
+ logger.error(f"Failed to load transport config: {e}")
112
+ return False
113
+
114
+ def get_transport_type(self) -> Optional[TransportType]:
115
+ """
116
+ Get current transport type.
117
+
118
+ Returns:
119
+ Current transport type or None if not configured
120
+ """
121
+ return self._current_transport
122
+
123
+ def get_port(self) -> Optional[int]:
124
+ """
125
+ Get configured port.
126
+
127
+ Returns:
128
+ Port number or None if not configured
129
+ """
130
+ return self._config.port if self._config else None
131
+
132
+ def is_ssl_enabled(self) -> bool:
133
+ """
134
+ Check if SSL is enabled.
135
+
136
+ Returns:
137
+ True if SSL is enabled, False otherwise
138
+ """
139
+ return self._config.ssl_enabled if self._config else False
140
+
141
+ def get_ssl_config(self) -> Optional[Dict[str, Any]]:
142
+ """
143
+ Get SSL configuration.
144
+
145
+ Returns:
146
+ SSL configuration dict or None if SSL not enabled
147
+ """
148
+ if not self._config or not self._config.ssl_enabled:
149
+ return None
150
+
151
+ return {
152
+ "cert_file": self._config.cert_file,
153
+ "key_file": self._config.key_file,
154
+ "ca_cert": self._config.ca_cert,
155
+ "verify_client": self._config.verify_client,
156
+ "client_cert_required": self._config.client_cert_required
157
+ }
158
+
159
+ def is_mtls(self) -> bool:
160
+ """
161
+ Check if current transport is MTLS.
162
+
163
+ Returns:
164
+ True if MTLS transport, False otherwise
165
+ """
166
+ return self._current_transport == TransportType.MTLS
167
+
168
+ def is_https(self) -> bool:
169
+ """
170
+ Check if current transport is HTTPS.
171
+
172
+ Returns:
173
+ True if HTTPS transport, False otherwise
174
+ """
175
+ return self._current_transport == TransportType.HTTPS
176
+
177
+ def is_http(self) -> bool:
178
+ """
179
+ Check if current transport is HTTP.
180
+
181
+ Returns:
182
+ True if HTTP transport, False otherwise
183
+ """
184
+ return self._current_transport == TransportType.HTTP
185
+
186
+ def get_transport_info(self) -> Dict[str, Any]:
187
+ """
188
+ Get transport information.
189
+
190
+ Returns:
191
+ Dictionary with transport information
192
+ """
193
+ if not self._config:
194
+ return {"error": "Transport not configured"}
195
+
196
+ return {
197
+ "type": self._config.type.value,
198
+ "port": self._config.port,
199
+ "ssl_enabled": self._config.ssl_enabled,
200
+ "is_mtls": self.is_mtls(),
201
+ "is_https": self.is_https(),
202
+ "is_http": self.is_http(),
203
+ "ssl_config": self.get_ssl_config()
204
+ }
205
+
206
+ def validate_config(self) -> bool:
207
+ """
208
+ Validate current transport configuration.
209
+
210
+ Returns:
211
+ True if configuration is valid, False otherwise
212
+ """
213
+ if not self._config:
214
+ logger.error("Transport not configured")
215
+ return False
216
+
217
+ # Validate SSL requirements
218
+ if self._config.type in [TransportType.HTTPS, TransportType.MTLS]:
219
+ if not self._config.ssl_enabled:
220
+ logger.error(f"SSL must be enabled for {self._config.type.value}")
221
+ return False
222
+
223
+ if not self._config.cert_file or not self._config.key_file:
224
+ logger.error(f"SSL certificate and key required for {self._config.type.value}")
225
+ return False
226
+
227
+ # Validate SSL files exist
228
+ if not self.validate_ssl_files():
229
+ return False
230
+
231
+ # Validate MTLS requirements
232
+ if self._config.type == TransportType.MTLS:
233
+ if not self._config.verify_client:
234
+ logger.warning("MTLS transport should have client verification enabled")
235
+
236
+ if not self._config.ca_cert:
237
+ logger.warning("CA certificate recommended for MTLS transport")
238
+
239
+ logger.info(f"Transport configuration validated: {self._config.type.value}")
240
+ return True
241
+
242
+ def validate_ssl_files(self) -> bool:
243
+ """
244
+ Check if SSL files exist.
245
+
246
+ Returns:
247
+ True if all SSL files exist, False otherwise
248
+ """
249
+ if not self._config or not self._config.ssl_enabled:
250
+ return True
251
+
252
+ files_to_check = []
253
+ if self._config.cert_file:
254
+ files_to_check.append(self._config.cert_file)
255
+ if self._config.key_file:
256
+ files_to_check.append(self._config.key_file)
257
+ if self._config.ca_cert:
258
+ files_to_check.append(self._config.ca_cert)
259
+
260
+ for file_path in files_to_check:
261
+ if not Path(file_path).exists():
262
+ logger.error(f"SSL file not found: {file_path}")
263
+ return False
264
+
265
+ logger.info(f"All SSL files validated successfully: {files_to_check}")
266
+ return True
267
+
268
+ def get_uvicorn_config(self) -> Dict[str, Any]:
269
+ """
270
+ Get configuration for uvicorn.
271
+
272
+ Returns:
273
+ Uvicorn configuration dictionary
274
+ """
275
+ config = {
276
+ "host": "0.0.0.0", # Can be moved to settings
277
+ "port": self.get_port(),
278
+ "log_level": "info"
279
+ }
280
+
281
+ if self.is_ssl_enabled():
282
+ ssl_config = self.get_ssl_config()
283
+ if ssl_config:
284
+ from mcp_proxy_adapter.core.ssl_utils import SSLUtils
285
+ uvicorn_ssl = SSLUtils.get_ssl_config_for_uvicorn(ssl_config)
286
+ config.update(uvicorn_ssl)
287
+
288
+ return config
289
+
290
+
291
+ # Global transport manager instance
292
+ transport_manager = TransportManager()
@@ -96,14 +96,25 @@ class CustomOpenAPIGenerator:
96
96
  Returns:
97
97
  Dict containing the parameter schema.
98
98
  """
99
- # Get command schema
100
- cmd_schema = cmd_class.get_schema()
101
-
102
- # Add title and description
103
- cmd_schema["title"] = f"Parameters for {cmd_class.name}"
104
- cmd_schema["description"] = f"Parameters for the {cmd_class.name} command"
105
-
106
- return cmd_schema
99
+ try:
100
+ # Get command schema
101
+ cmd_schema = cmd_class.get_schema()
102
+
103
+ # Add title and description
104
+ cmd_schema["title"] = f"Parameters for {cmd_class.name}"
105
+ cmd_schema["description"] = f"Parameters for the {cmd_class.name} command"
106
+
107
+ return cmd_schema
108
+ except Exception as e:
109
+ # Return default schema if command schema generation fails
110
+ logger.warning(f"Failed to get schema for command {cmd_class.name}: {e}")
111
+ return {
112
+ "type": "object",
113
+ "title": f"Parameters for {cmd_class.name}",
114
+ "description": f"Parameters for the {cmd_class.name} command (schema generation failed)",
115
+ "properties": {},
116
+ "additionalProperties": True
117
+ }
107
118
 
108
119
  def generate(self, title: Optional[str] = None, description: Optional[str] = None, version: Optional[str] = None) -> Dict[str, Any]:
109
120
  """
@@ -262,7 +273,7 @@ class CustomOpenAPIGenerator:
262
273
  # Add commands to schema
263
274
  self._add_commands_to_schema(schema)
264
275
 
265
- logger.info(f"Generated OpenAPI schema with {len(registry.get_all_commands())} commands")
276
+ logger.debug(f"Generated OpenAPI schema with {len(registry.get_all_commands())} commands")
266
277
 
267
278
  return schema
268
279
 
@@ -358,9 +369,9 @@ def custom_openapi_with_fallback(app: FastAPI) -> Dict[str, Any]:
358
369
  # Use the first registered generator
359
370
  generator_name = list(_openapi_generators.keys())[0]
360
371
  generator_func = _openapi_generators[generator_name]
361
- logger.info(f"Using custom OpenAPI generator: {generator_name}")
372
+ logger.debug(f"Using custom OpenAPI generator: {generator_name}")
362
373
  return generator_func(app)
363
374
 
364
375
  # Fall back to default generator
365
- logger.info("Using default OpenAPI generator")
376
+ logger.debug("Using default OpenAPI generator")
366
377
  return custom_openapi(app)
@@ -1,35 +1,70 @@
1
1
  {
2
2
  "server": {
3
- "host": "127.0.0.1",
4
- "port": 8000,
3
+ "host": "0.0.0.0",
4
+ "port": 9443,
5
5
  "debug": true,
6
- "log_level": "DEBUG"
6
+ "log_level": "INFO"
7
7
  },
8
8
  "logging": {
9
- "level": "DEBUG",
10
- "log_dir": "./logs/basic_server",
11
- "log_file": "basic_server.log",
12
- "error_log_file": "basic_server_error.log",
13
- "access_log_file": "basic_server_access.log",
14
- "max_file_size": "5MB",
15
- "backup_count": 3,
16
- "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
17
- "date_format": "%Y-%m-%d %H:%M:%S",
18
- "console_output": true,
19
- "file_output": true
9
+ "level": "INFO",
10
+ "log_dir": "./logs",
11
+ "console_output": true
20
12
  },
21
13
  "commands": {
22
14
  "auto_discovery": true,
23
- "discovery_path": "mcp_proxy_adapter.commands",
24
- "custom_commands_path": null
15
+ "discovery_path": "mcp_proxy_adapter.commands"
25
16
  },
26
- "custom": {
27
- "server_name": "Basic MCP Proxy Server",
28
- "description": "Simple example server with basic configuration",
29
- "features": {
30
- "hooks_enabled": false,
31
- "custom_commands_enabled": false,
32
- "advanced_logging": false
17
+ "ssl": {
18
+ "enabled": true,
19
+ "mode": "mtls_only",
20
+ "cert_file": "/home/vasilyvz/projects/mcp_proxy_adapter/test_env/server_test-server.crt",
21
+ "key_file": "/home/vasilyvz/projects/mcp_proxy_adapter/test_env/server_test-server.key",
22
+ "ca_cert": "/home/vasilyvz/projects/mcp_proxy_adapter/test_env/ca_test-ca.crt",
23
+ "verify_client": true,
24
+ "client_cert_required": true
25
+ },
26
+ "protocols": {
27
+ "enabled": false,
28
+ "allowed_protocols": ["https", "mtls"],
29
+ "http": {
30
+ "enabled": false,
31
+ "port": 8000
32
+ },
33
+ "https": {
34
+ "enabled": true,
35
+ "port": 9443
36
+ },
37
+ "mtls": {
38
+ "enabled": true,
39
+ "port": 9443
33
40
  }
41
+ },
42
+ "roles": {
43
+ "enabled": true,
44
+ "config_file": "schemas/roles_schema.json",
45
+ "default_policy": {
46
+ "deny_by_default": true,
47
+ "require_role_match": true,
48
+ "case_sensitive": false,
49
+ "allow_wildcard": true
50
+ },
51
+ "auto_load": true,
52
+ "validation_enabled": true
53
+ },
54
+ "proxy_registration": {
55
+ "enabled": true,
56
+ "proxy_url": "http://localhost:3004",
57
+ "server_id": "mcp_proxy_adapter_mtls",
58
+ "server_name": "MTLS MCP Proxy Adapter Server",
59
+ "description": "JSON-RPC API for interacting with MCP Proxy (MTLS mode)",
60
+ "registration_timeout": 30,
61
+ "retry_attempts": 3,
62
+ "retry_delay": 5,
63
+ "auto_register_on_startup": true,
64
+ "auto_unregister_on_shutdown": true
65
+ },
66
+ "custom": {
67
+ "description": "MTLS-only server example with mutual authentication",
68
+ "server_name": "MTLS MCP Proxy Adapter Server"
34
69
  }
35
70
  }