mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.6__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.
- mcp_proxy_adapter/__init__.py +9 -5
- mcp_proxy_adapter/__main__.py +1 -1
- mcp_proxy_adapter/api/app.py +227 -176
- mcp_proxy_adapter/api/handlers.py +68 -60
- mcp_proxy_adapter/api/middleware/__init__.py +7 -5
- mcp_proxy_adapter/api/middleware/base.py +19 -16
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +44 -34
- mcp_proxy_adapter/api/middleware/error_handling.py +57 -67
- mcp_proxy_adapter/api/middleware/factory.py +50 -52
- mcp_proxy_adapter/api/middleware/logging.py +46 -30
- mcp_proxy_adapter/api/middleware/performance.py +19 -16
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +80 -50
- mcp_proxy_adapter/api/middleware/transport_middleware.py +26 -24
- mcp_proxy_adapter/api/middleware/unified_security.py +70 -51
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +43 -34
- mcp_proxy_adapter/api/schemas.py +69 -43
- mcp_proxy_adapter/api/tool_integration.py +83 -63
- mcp_proxy_adapter/api/tools.py +60 -50
- mcp_proxy_adapter/commands/__init__.py +15 -6
- mcp_proxy_adapter/commands/auth_validation_command.py +107 -110
- mcp_proxy_adapter/commands/base.py +108 -112
- mcp_proxy_adapter/commands/builtin_commands.py +28 -18
- mcp_proxy_adapter/commands/catalog_manager.py +394 -265
- mcp_proxy_adapter/commands/cert_monitor_command.py +222 -204
- mcp_proxy_adapter/commands/certificate_management_command.py +210 -213
- mcp_proxy_adapter/commands/command_registry.py +275 -226
- mcp_proxy_adapter/commands/config_command.py +48 -33
- mcp_proxy_adapter/commands/dependency_container.py +22 -23
- mcp_proxy_adapter/commands/dependency_manager.py +65 -56
- mcp_proxy_adapter/commands/echo_command.py +15 -15
- mcp_proxy_adapter/commands/health_command.py +31 -29
- mcp_proxy_adapter/commands/help_command.py +97 -61
- mcp_proxy_adapter/commands/hooks.py +65 -49
- mcp_proxy_adapter/commands/key_management_command.py +148 -147
- mcp_proxy_adapter/commands/load_command.py +58 -40
- mcp_proxy_adapter/commands/plugins_command.py +80 -54
- mcp_proxy_adapter/commands/protocol_management_command.py +60 -48
- mcp_proxy_adapter/commands/proxy_registration_command.py +107 -115
- mcp_proxy_adapter/commands/reload_command.py +43 -37
- mcp_proxy_adapter/commands/result.py +26 -33
- mcp_proxy_adapter/commands/role_test_command.py +26 -26
- mcp_proxy_adapter/commands/roles_management_command.py +176 -173
- mcp_proxy_adapter/commands/security_command.py +134 -122
- mcp_proxy_adapter/commands/settings_command.py +47 -56
- mcp_proxy_adapter/commands/ssl_setup_command.py +109 -129
- mcp_proxy_adapter/commands/token_management_command.py +129 -158
- mcp_proxy_adapter/commands/transport_management_command.py +41 -36
- mcp_proxy_adapter/commands/unload_command.py +42 -37
- mcp_proxy_adapter/config.py +36 -35
- mcp_proxy_adapter/core/__init__.py +19 -21
- mcp_proxy_adapter/core/app_factory.py +30 -9
- mcp_proxy_adapter/core/app_runner.py +81 -64
- mcp_proxy_adapter/core/auth_validator.py +176 -182
- mcp_proxy_adapter/core/certificate_utils.py +469 -426
- mcp_proxy_adapter/core/client.py +155 -126
- mcp_proxy_adapter/core/client_manager.py +60 -54
- mcp_proxy_adapter/core/client_security.py +120 -91
- mcp_proxy_adapter/core/config_converter.py +176 -143
- mcp_proxy_adapter/core/config_validator.py +12 -4
- mcp_proxy_adapter/core/crl_utils.py +21 -7
- mcp_proxy_adapter/core/errors.py +64 -20
- mcp_proxy_adapter/core/logging.py +34 -29
- mcp_proxy_adapter/core/mtls_asgi.py +29 -25
- mcp_proxy_adapter/core/mtls_asgi_app.py +66 -54
- mcp_proxy_adapter/core/protocol_manager.py +154 -104
- mcp_proxy_adapter/core/proxy_client.py +202 -144
- mcp_proxy_adapter/core/proxy_registration.py +7 -3
- mcp_proxy_adapter/core/role_utils.py +139 -125
- mcp_proxy_adapter/core/security_adapter.py +88 -77
- mcp_proxy_adapter/core/security_factory.py +50 -44
- mcp_proxy_adapter/core/security_integration.py +72 -24
- mcp_proxy_adapter/core/server_adapter.py +68 -64
- mcp_proxy_adapter/core/server_engine.py +71 -53
- mcp_proxy_adapter/core/settings.py +68 -58
- mcp_proxy_adapter/core/ssl_utils.py +69 -56
- mcp_proxy_adapter/core/transport_manager.py +72 -60
- mcp_proxy_adapter/core/unified_config_adapter.py +201 -150
- mcp_proxy_adapter/core/utils.py +4 -2
- mcp_proxy_adapter/custom_openapi.py +107 -99
- mcp_proxy_adapter/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/commands/__init__.py +1 -1
- mcp_proxy_adapter/examples/create_certificates_simple.py +182 -71
- mcp_proxy_adapter/examples/debug_request_state.py +38 -19
- mcp_proxy_adapter/examples/debug_role_chain.py +53 -20
- mcp_proxy_adapter/examples/demo_client.py +48 -36
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/generate_all_certificates.py +198 -73
- mcp_proxy_adapter/examples/generate_certificates.py +31 -16
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/generate_test_configs.py +68 -91
- mcp_proxy_adapter/examples/proxy_registration_example.py +76 -75
- mcp_proxy_adapter/examples/run_example.py +23 -5
- mcp_proxy_adapter/examples/run_full_test_suite.py +109 -71
- mcp_proxy_adapter/examples/run_proxy_server.py +22 -9
- mcp_proxy_adapter/examples/run_security_tests.py +103 -41
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +72 -36
- mcp_proxy_adapter/examples/scripts/config_generator.py +288 -187
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +185 -72
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/security_test_client.py +196 -127
- mcp_proxy_adapter/examples/setup_test_environment.py +17 -29
- mcp_proxy_adapter/examples/test_config.py +19 -4
- mcp_proxy_adapter/examples/test_config_generator.py +23 -7
- mcp_proxy_adapter/examples/test_examples.py +84 -56
- mcp_proxy_adapter/examples/universal_client.py +119 -62
- mcp_proxy_adapter/openapi.py +108 -115
- mcp_proxy_adapter/utils/config_generator.py +429 -274
- mcp_proxy_adapter/version.py +1 -2
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-6.3.6.dist-info/RECORD +144 -0
- mcp_proxy_adapter-6.3.6.dist-info/top_level.txt +2 -0
- mcp_proxy_adapter_issue_package/demonstrate_issue.py +178 -0
- mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
- mcp_proxy_adapter-6.3.4.dist-info/top_level.txt +0 -1
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/licenses/LICENSE +0 -0
@@ -25,15 +25,15 @@ class SSLUtils:
|
|
25
25
|
"""
|
26
26
|
SSL utilities for creating SSL contexts and validating certificates.
|
27
27
|
"""
|
28
|
-
|
28
|
+
|
29
29
|
# TLS version mapping
|
30
30
|
TLS_VERSIONS = {
|
31
31
|
"1.0": ssl.TLSVersion.TLSv1,
|
32
32
|
"1.1": ssl.TLSVersion.TLSv1_1,
|
33
33
|
"1.2": ssl.TLSVersion.TLSv1_2,
|
34
|
-
"1.3": ssl.TLSVersion.TLSv1_3
|
34
|
+
"1.3": ssl.TLSVersion.TLSv1_3,
|
35
35
|
}
|
36
|
-
|
36
|
+
|
37
37
|
# Cipher suite mapping
|
38
38
|
CIPHER_SUITES = {
|
39
39
|
"TLS_AES_256_GCM_SHA384": "TLS_AES_256_GCM_SHA384",
|
@@ -41,20 +41,23 @@ class SSLUtils:
|
|
41
41
|
"TLS_AES_128_GCM_SHA256": "TLS_AES_128_GCM_SHA256",
|
42
42
|
"ECDHE-RSA-AES256-GCM-SHA384": "ECDHE-RSA-AES256-GCM-SHA384",
|
43
43
|
"ECDHE-RSA-AES128-GCM-SHA256": "ECDHE-RSA-AES128-GCM-SHA256",
|
44
|
-
"ECDHE-RSA-CHACHA20-POLY1305": "ECDHE-RSA-CHACHA20-POLY1305"
|
44
|
+
"ECDHE-RSA-CHACHA20-POLY1305": "ECDHE-RSA-CHACHA20-POLY1305",
|
45
45
|
}
|
46
|
-
|
46
|
+
|
47
47
|
@staticmethod
|
48
|
-
def create_ssl_context(
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
48
|
+
def create_ssl_context(
|
49
|
+
cert_file: str,
|
50
|
+
key_file: str,
|
51
|
+
ca_cert: Optional[str] = None,
|
52
|
+
verify_client: bool = False,
|
53
|
+
cipher_suites: Optional[List[str]] = None,
|
54
|
+
min_tls_version: str = "1.2",
|
55
|
+
max_tls_version: str = "1.3",
|
56
|
+
crl_config: Optional[Dict[str, Any]] = None,
|
57
|
+
) -> ssl.SSLContext:
|
55
58
|
"""
|
56
59
|
Create SSL context with specified configuration.
|
57
|
-
|
60
|
+
|
58
61
|
Args:
|
59
62
|
cert_file: Path to certificate file
|
60
63
|
key_file: Path to private key file
|
@@ -64,10 +67,10 @@ class SSLUtils:
|
|
64
67
|
min_tls_version: Minimum TLS version
|
65
68
|
max_tls_version: Maximum TLS version
|
66
69
|
crl_config: CRL configuration dictionary (optional)
|
67
|
-
|
70
|
+
|
68
71
|
Returns:
|
69
72
|
Configured SSL context
|
70
|
-
|
73
|
+
|
71
74
|
Raises:
|
72
75
|
ValueError: If certificate validation fails
|
73
76
|
FileNotFoundError: If certificate or key files not found
|
@@ -77,18 +80,20 @@ class SSLUtils:
|
|
77
80
|
result = validator.validate_certificate(cert_file)
|
78
81
|
if not result.is_valid:
|
79
82
|
raise ValueError(f"Invalid certificate: {result.error_message}")
|
80
|
-
|
83
|
+
|
81
84
|
# Check CRL if configured
|
82
85
|
if crl_config:
|
83
86
|
try:
|
84
87
|
crl_manager = CRLManager(crl_config)
|
85
88
|
if crl_manager.is_certificate_revoked(cert_file):
|
86
|
-
raise ValueError(
|
89
|
+
raise ValueError(
|
90
|
+
f"Certificate is revoked according to CRL: {cert_file}"
|
91
|
+
)
|
87
92
|
except Exception as e:
|
88
93
|
logger.error(f"CRL check failed: {e}")
|
89
94
|
# For security, fail if CRL check fails
|
90
95
|
raise ValueError(f"CRL validation failed: {e}")
|
91
|
-
|
96
|
+
|
92
97
|
# Check if files exist
|
93
98
|
if not Path(cert_file).exists():
|
94
99
|
raise FileNotFoundError(f"Certificate file not found: {cert_file}")
|
@@ -96,45 +101,47 @@ class SSLUtils:
|
|
96
101
|
raise FileNotFoundError(f"Key file not found: {key_file}")
|
97
102
|
if ca_cert and not Path(ca_cert).exists():
|
98
103
|
raise FileNotFoundError(f"CA certificate file not found: {ca_cert}")
|
99
|
-
|
104
|
+
|
100
105
|
# Create SSL context
|
101
106
|
if verify_client:
|
102
107
|
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
103
108
|
else:
|
104
109
|
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
|
105
|
-
|
110
|
+
|
106
111
|
# Load certificate and key
|
107
112
|
context.load_cert_chain(cert_file, key_file)
|
108
|
-
|
113
|
+
|
109
114
|
# Load CA certificate if provided
|
110
115
|
if ca_cert:
|
111
116
|
context.load_verify_locations(ca_cert)
|
112
|
-
|
117
|
+
|
113
118
|
# Configure client verification
|
114
119
|
if verify_client:
|
115
120
|
context.verify_mode = ssl.CERT_REQUIRED
|
116
121
|
context.check_hostname = False
|
117
122
|
else:
|
118
123
|
context.verify_mode = ssl.CERT_NONE
|
119
|
-
|
124
|
+
|
120
125
|
# Setup cipher suites
|
121
126
|
SSLUtils.setup_cipher_suites(context, cipher_suites or [])
|
122
|
-
|
127
|
+
|
123
128
|
# Setup TLS versions
|
124
129
|
SSLUtils.setup_tls_versions(context, min_tls_version, max_tls_version)
|
125
|
-
|
130
|
+
|
126
131
|
logger.info(f"SSL context created successfully with cert: {cert_file}")
|
127
132
|
return context
|
128
|
-
|
133
|
+
|
129
134
|
@staticmethod
|
130
|
-
def validate_certificate(
|
135
|
+
def validate_certificate(
|
136
|
+
cert_file: str, crl_config: Optional[Dict[str, Any]] = None
|
137
|
+
) -> bool:
|
131
138
|
"""
|
132
139
|
Validate certificate using AuthValidator and optional CRL check.
|
133
|
-
|
140
|
+
|
134
141
|
Args:
|
135
142
|
cert_file: Path to certificate file
|
136
143
|
crl_config: CRL configuration dictionary (optional)
|
137
|
-
|
144
|
+
|
138
145
|
Returns:
|
139
146
|
True if certificate is valid, False otherwise
|
140
147
|
"""
|
@@ -143,36 +150,38 @@ class SSLUtils:
|
|
143
150
|
result = validator.validate_certificate(cert_file)
|
144
151
|
if not result.is_valid:
|
145
152
|
return False
|
146
|
-
|
153
|
+
|
147
154
|
# Check CRL if configured
|
148
155
|
if crl_config:
|
149
156
|
try:
|
150
157
|
crl_manager = CRLManager(crl_config)
|
151
158
|
if crl_manager.is_certificate_revoked(cert_file):
|
152
|
-
logger.warning(
|
159
|
+
logger.warning(
|
160
|
+
f"Certificate is revoked according to CRL: {cert_file}"
|
161
|
+
)
|
153
162
|
return False
|
154
163
|
except Exception as e:
|
155
164
|
logger.error(f"CRL check failed: {e}")
|
156
165
|
# For security, consider certificate invalid if CRL check fails
|
157
166
|
return False
|
158
|
-
|
167
|
+
|
159
168
|
return True
|
160
169
|
except Exception as e:
|
161
170
|
logger.error(f"Certificate validation failed: {e}")
|
162
171
|
return False
|
163
|
-
|
172
|
+
|
164
173
|
@staticmethod
|
165
174
|
def setup_cipher_suites(context: ssl.SSLContext, cipher_suites: List[str]) -> None:
|
166
175
|
"""
|
167
176
|
Setup cipher suites for SSL context.
|
168
|
-
|
177
|
+
|
169
178
|
Args:
|
170
179
|
context: SSL context to configure
|
171
180
|
cipher_suites: List of cipher suite names
|
172
181
|
"""
|
173
182
|
if not cipher_suites:
|
174
183
|
return
|
175
|
-
|
184
|
+
|
176
185
|
# Convert cipher suite names to actual cipher suite strings
|
177
186
|
actual_ciphers = []
|
178
187
|
for cipher_name in cipher_suites:
|
@@ -180,19 +189,21 @@ class SSLUtils:
|
|
180
189
|
actual_ciphers.append(SSLUtils.CIPHER_SUITES[cipher_name])
|
181
190
|
else:
|
182
191
|
logger.warning(f"Unknown cipher suite: {cipher_name}")
|
183
|
-
|
192
|
+
|
184
193
|
if actual_ciphers:
|
185
194
|
try:
|
186
195
|
context.set_ciphers(":".join(actual_ciphers))
|
187
196
|
logger.info(f"Cipher suites configured: {actual_ciphers}")
|
188
197
|
except ssl.SSLError as e:
|
189
198
|
logger.error(f"Failed to set cipher suites: {e}")
|
190
|
-
|
199
|
+
|
191
200
|
@staticmethod
|
192
|
-
def setup_tls_versions(
|
201
|
+
def setup_tls_versions(
|
202
|
+
context: ssl.SSLContext, min_version: str, max_version: str
|
203
|
+
) -> None:
|
193
204
|
"""
|
194
205
|
Setup TLS version range for SSL context.
|
195
|
-
|
206
|
+
|
196
207
|
Args:
|
197
208
|
context: SSL context to configure
|
198
209
|
min_version: Minimum TLS version
|
@@ -201,66 +212,68 @@ class SSLUtils:
|
|
201
212
|
try:
|
202
213
|
min_tls = SSLUtils.TLS_VERSIONS.get(min_version)
|
203
214
|
max_tls = SSLUtils.TLS_VERSIONS.get(max_version)
|
204
|
-
|
215
|
+
|
205
216
|
if min_tls and max_tls:
|
206
217
|
context.minimum_version = min_tls
|
207
218
|
context.maximum_version = max_tls
|
208
219
|
logger.info(f"TLS versions configured: {min_version} - {max_version}")
|
209
220
|
else:
|
210
|
-
logger.warning(
|
221
|
+
logger.warning(
|
222
|
+
f"Invalid TLS version range: {min_version} - {max_version}"
|
223
|
+
)
|
211
224
|
except Exception as e:
|
212
225
|
logger.error(f"Failed to set TLS versions: {e}")
|
213
|
-
|
226
|
+
|
214
227
|
@staticmethod
|
215
228
|
def check_tls_version(min_version: str, max_version: str) -> bool:
|
216
229
|
"""
|
217
230
|
Check if TLS version range is valid.
|
218
|
-
|
231
|
+
|
219
232
|
Args:
|
220
233
|
min_version: Minimum TLS version
|
221
234
|
max_version: Maximum TLS version
|
222
|
-
|
235
|
+
|
223
236
|
Returns:
|
224
237
|
True if version range is valid, False otherwise
|
225
238
|
"""
|
226
239
|
min_tls = SSLUtils.TLS_VERSIONS.get(min_version)
|
227
240
|
max_tls = SSLUtils.TLS_VERSIONS.get(max_version)
|
228
|
-
|
241
|
+
|
229
242
|
if not min_tls or not max_tls:
|
230
243
|
return False
|
231
|
-
|
244
|
+
|
232
245
|
# Check if min version is less than or equal to max version
|
233
246
|
return min_tls <= max_tls
|
234
|
-
|
247
|
+
|
235
248
|
@staticmethod
|
236
249
|
def get_ssl_config_for_hypercorn(ssl_config: Dict[str, Any]) -> Dict[str, Any]:
|
237
250
|
"""
|
238
251
|
Get SSL configuration for hypercorn from transport configuration.
|
239
|
-
|
252
|
+
|
240
253
|
Args:
|
241
254
|
ssl_config: SSL configuration from transport manager
|
242
|
-
|
255
|
+
|
243
256
|
Returns:
|
244
257
|
Configuration for hypercorn
|
245
258
|
"""
|
246
259
|
hypercorn_ssl = {}
|
247
|
-
|
260
|
+
|
248
261
|
if not ssl_config:
|
249
262
|
return hypercorn_ssl
|
250
|
-
|
263
|
+
|
251
264
|
# Basic SSL parameters
|
252
265
|
if ssl_config.get("cert_file"):
|
253
266
|
hypercorn_ssl["certfile"] = ssl_config["cert_file"]
|
254
|
-
|
267
|
+
|
255
268
|
if ssl_config.get("key_file"):
|
256
269
|
hypercorn_ssl["keyfile"] = ssl_config["key_file"]
|
257
|
-
|
270
|
+
|
258
271
|
if ssl_config.get("ca_cert"):
|
259
272
|
hypercorn_ssl["ca_certs"] = ssl_config["ca_cert"]
|
260
|
-
|
273
|
+
|
261
274
|
# Client verification mode
|
262
275
|
if ssl_config.get("verify_client", False):
|
263
276
|
hypercorn_ssl["verify_mode"] = "CERT_REQUIRED"
|
264
|
-
|
277
|
+
|
265
278
|
logger.info(f"Generated hypercorn SSL config: {hypercorn_ssl}")
|
266
|
-
return hypercorn_ssl
|
279
|
+
return hypercorn_ssl
|
@@ -14,6 +14,7 @@ from mcp_proxy_adapter.core.logging import logger
|
|
14
14
|
|
15
15
|
class TransportType(Enum):
|
16
16
|
"""Transport types enumeration."""
|
17
|
+
|
17
18
|
HTTP = "http"
|
18
19
|
HTTPS = "https"
|
19
20
|
MTLS = "mtls"
|
@@ -22,6 +23,7 @@ class TransportType(Enum):
|
|
22
23
|
@dataclass
|
23
24
|
class TransportConfig:
|
24
25
|
"""Transport configuration data class."""
|
26
|
+
|
25
27
|
type: TransportType
|
26
28
|
port: Optional[int]
|
27
29
|
ssl_enabled: bool
|
@@ -35,36 +37,36 @@ class TransportConfig:
|
|
35
37
|
class TransportManager:
|
36
38
|
"""
|
37
39
|
Transport manager for handling different transport types.
|
38
|
-
|
40
|
+
|
39
41
|
This class manages transport configuration and provides utilities
|
40
42
|
for determining ports and SSL settings based on transport type.
|
41
43
|
"""
|
42
|
-
|
44
|
+
|
43
45
|
# Default ports for transport types
|
44
46
|
DEFAULT_PORTS = {
|
45
47
|
TransportType.HTTP: 8000,
|
46
48
|
TransportType.HTTPS: 8443,
|
47
|
-
TransportType.MTLS: 9443
|
49
|
+
TransportType.MTLS: 9443,
|
48
50
|
}
|
49
|
-
|
51
|
+
|
50
52
|
def __init__(self):
|
51
53
|
"""Initialize transport manager."""
|
52
54
|
self._config: Optional[TransportConfig] = None
|
53
55
|
self._current_transport: Optional[TransportType] = None
|
54
|
-
|
56
|
+
|
55
57
|
def load_config(self, config: Dict[str, Any]) -> bool:
|
56
58
|
"""
|
57
59
|
Load transport configuration from config dict.
|
58
|
-
|
60
|
+
|
59
61
|
Args:
|
60
62
|
config: Configuration dictionary
|
61
|
-
|
63
|
+
|
62
64
|
Returns:
|
63
65
|
True if config loaded successfully, False otherwise
|
64
66
|
"""
|
65
67
|
try:
|
66
68
|
transport_config = config.get("transport", {})
|
67
|
-
|
69
|
+
|
68
70
|
# Get transport type
|
69
71
|
transport_type_str = transport_config.get("type", "http").lower()
|
70
72
|
try:
|
@@ -72,24 +74,29 @@ class TransportManager:
|
|
72
74
|
except ValueError:
|
73
75
|
logger.error(f"Invalid transport type: {transport_type_str}")
|
74
76
|
return False
|
75
|
-
|
77
|
+
|
76
78
|
# Get port (use default if not specified)
|
77
79
|
port = transport_config.get("port")
|
78
80
|
if port is None:
|
79
81
|
port = self.DEFAULT_PORTS.get(transport_type, 8000)
|
80
|
-
|
82
|
+
|
81
83
|
# Get SSL configuration
|
82
84
|
ssl_config = transport_config.get("ssl", {})
|
83
85
|
ssl_enabled = ssl_config.get("enabled", False)
|
84
|
-
|
86
|
+
|
85
87
|
# Validate SSL requirements
|
86
|
-
if
|
87
|
-
|
88
|
+
if (
|
89
|
+
transport_type in [TransportType.HTTPS, TransportType.MTLS]
|
90
|
+
and not ssl_enabled
|
91
|
+
):
|
92
|
+
logger.error(
|
93
|
+
f"SSL must be enabled for transport type: {transport_type.value}"
|
94
|
+
)
|
88
95
|
return False
|
89
|
-
|
96
|
+
|
90
97
|
if transport_type == TransportType.HTTP and ssl_enabled:
|
91
98
|
logger.warning("SSL enabled for HTTP transport - this may cause issues")
|
92
|
-
|
99
|
+
|
93
100
|
# Create transport config
|
94
101
|
self._config = TransportConfig(
|
95
102
|
type=transport_type,
|
@@ -99,100 +106,102 @@ class TransportManager:
|
|
99
106
|
key_file=ssl_config.get("key_file") if ssl_enabled else None,
|
100
107
|
ca_cert=ssl_config.get("ca_cert") if ssl_enabled else None,
|
101
108
|
verify_client=ssl_config.get("verify_client", False),
|
102
|
-
client_cert_required=ssl_config.get("client_cert_required", False)
|
109
|
+
client_cert_required=ssl_config.get("client_cert_required", False),
|
103
110
|
)
|
104
|
-
|
111
|
+
|
105
112
|
self._current_transport = transport_type
|
106
|
-
|
107
|
-
logger.info(
|
113
|
+
|
114
|
+
logger.info(
|
115
|
+
f"Transport config loaded: {transport_type.value} on port {port}"
|
116
|
+
)
|
108
117
|
return True
|
109
|
-
|
118
|
+
|
110
119
|
except Exception as e:
|
111
120
|
logger.error(f"Failed to load transport config: {e}")
|
112
121
|
return False
|
113
|
-
|
122
|
+
|
114
123
|
def get_transport_type(self) -> Optional[TransportType]:
|
115
124
|
"""
|
116
125
|
Get current transport type.
|
117
|
-
|
126
|
+
|
118
127
|
Returns:
|
119
128
|
Current transport type or None if not configured
|
120
129
|
"""
|
121
130
|
return self._current_transport
|
122
|
-
|
131
|
+
|
123
132
|
def get_port(self) -> Optional[int]:
|
124
133
|
"""
|
125
134
|
Get configured port.
|
126
|
-
|
135
|
+
|
127
136
|
Returns:
|
128
137
|
Port number or None if not configured
|
129
138
|
"""
|
130
139
|
return self._config.port if self._config else None
|
131
|
-
|
140
|
+
|
132
141
|
def is_ssl_enabled(self) -> bool:
|
133
142
|
"""
|
134
143
|
Check if SSL is enabled.
|
135
|
-
|
144
|
+
|
136
145
|
Returns:
|
137
146
|
True if SSL is enabled, False otherwise
|
138
147
|
"""
|
139
148
|
return self._config.ssl_enabled if self._config else False
|
140
|
-
|
149
|
+
|
141
150
|
def get_ssl_config(self) -> Optional[Dict[str, Any]]:
|
142
151
|
"""
|
143
152
|
Get SSL configuration.
|
144
|
-
|
153
|
+
|
145
154
|
Returns:
|
146
155
|
SSL configuration dict or None if SSL not enabled
|
147
156
|
"""
|
148
157
|
if not self._config or not self._config.ssl_enabled:
|
149
158
|
return None
|
150
|
-
|
159
|
+
|
151
160
|
return {
|
152
161
|
"cert_file": self._config.cert_file,
|
153
162
|
"key_file": self._config.key_file,
|
154
163
|
"ca_cert": self._config.ca_cert,
|
155
164
|
"verify_client": self._config.verify_client,
|
156
|
-
"client_cert_required": self._config.client_cert_required
|
165
|
+
"client_cert_required": self._config.client_cert_required,
|
157
166
|
}
|
158
|
-
|
167
|
+
|
159
168
|
def is_mtls(self) -> bool:
|
160
169
|
"""
|
161
170
|
Check if current transport is MTLS.
|
162
|
-
|
171
|
+
|
163
172
|
Returns:
|
164
173
|
True if MTLS transport, False otherwise
|
165
174
|
"""
|
166
175
|
return self._current_transport == TransportType.MTLS
|
167
|
-
|
176
|
+
|
168
177
|
def is_https(self) -> bool:
|
169
178
|
"""
|
170
179
|
Check if current transport is HTTPS.
|
171
|
-
|
180
|
+
|
172
181
|
Returns:
|
173
182
|
True if HTTPS transport, False otherwise
|
174
183
|
"""
|
175
184
|
return self._current_transport == TransportType.HTTPS
|
176
|
-
|
185
|
+
|
177
186
|
def is_http(self) -> bool:
|
178
187
|
"""
|
179
188
|
Check if current transport is HTTP.
|
180
|
-
|
189
|
+
|
181
190
|
Returns:
|
182
191
|
True if HTTP transport, False otherwise
|
183
192
|
"""
|
184
193
|
return self._current_transport == TransportType.HTTP
|
185
|
-
|
194
|
+
|
186
195
|
def get_transport_info(self) -> Dict[str, Any]:
|
187
196
|
"""
|
188
197
|
Get transport information.
|
189
|
-
|
198
|
+
|
190
199
|
Returns:
|
191
200
|
Dictionary with transport information
|
192
201
|
"""
|
193
202
|
if not self._config:
|
194
203
|
return {"error": "Transport not configured"}
|
195
|
-
|
204
|
+
|
196
205
|
return {
|
197
206
|
"type": self._config.type.value,
|
198
207
|
"port": self._config.port,
|
@@ -200,55 +209,57 @@ class TransportManager:
|
|
200
209
|
"is_mtls": self.is_mtls(),
|
201
210
|
"is_https": self.is_https(),
|
202
211
|
"is_http": self.is_http(),
|
203
|
-
"ssl_config": self.get_ssl_config()
|
212
|
+
"ssl_config": self.get_ssl_config(),
|
204
213
|
}
|
205
|
-
|
214
|
+
|
206
215
|
def validate_config(self) -> bool:
|
207
216
|
"""
|
208
217
|
Validate current transport configuration.
|
209
|
-
|
218
|
+
|
210
219
|
Returns:
|
211
220
|
True if configuration is valid, False otherwise
|
212
221
|
"""
|
213
222
|
if not self._config:
|
214
223
|
logger.error("Transport not configured")
|
215
224
|
return False
|
216
|
-
|
225
|
+
|
217
226
|
# Validate SSL requirements
|
218
227
|
if self._config.type in [TransportType.HTTPS, TransportType.MTLS]:
|
219
228
|
if not self._config.ssl_enabled:
|
220
229
|
logger.error(f"SSL must be enabled for {self._config.type.value}")
|
221
230
|
return False
|
222
|
-
|
231
|
+
|
223
232
|
if not self._config.cert_file or not self._config.key_file:
|
224
|
-
logger.error(
|
233
|
+
logger.error(
|
234
|
+
f"SSL certificate and key required for {self._config.type.value}"
|
235
|
+
)
|
225
236
|
return False
|
226
|
-
|
237
|
+
|
227
238
|
# Validate SSL files exist
|
228
239
|
if not self.validate_ssl_files():
|
229
240
|
return False
|
230
|
-
|
241
|
+
|
231
242
|
# Validate MTLS requirements
|
232
243
|
if self._config.type == TransportType.MTLS:
|
233
244
|
if not self._config.verify_client:
|
234
245
|
logger.warning("MTLS transport should have client verification enabled")
|
235
|
-
|
246
|
+
|
236
247
|
if not self._config.ca_cert:
|
237
248
|
logger.warning("CA certificate recommended for MTLS transport")
|
238
|
-
|
249
|
+
|
239
250
|
logger.info(f"Transport configuration validated: {self._config.type.value}")
|
240
251
|
return True
|
241
|
-
|
252
|
+
|
242
253
|
def validate_ssl_files(self) -> bool:
|
243
254
|
"""
|
244
255
|
Check if SSL files exist.
|
245
|
-
|
256
|
+
|
246
257
|
Returns:
|
247
258
|
True if all SSL files exist, False otherwise
|
248
259
|
"""
|
249
260
|
if not self._config or not self._config.ssl_enabled:
|
250
261
|
return True
|
251
|
-
|
262
|
+
|
252
263
|
files_to_check = []
|
253
264
|
if self._config.cert_file:
|
254
265
|
files_to_check.append(self._config.cert_file)
|
@@ -256,37 +267,38 @@ class TransportManager:
|
|
256
267
|
files_to_check.append(self._config.key_file)
|
257
268
|
if self._config.ca_cert:
|
258
269
|
files_to_check.append(self._config.ca_cert)
|
259
|
-
|
270
|
+
|
260
271
|
for file_path in files_to_check:
|
261
272
|
if not Path(file_path).exists():
|
262
273
|
logger.error(f"SSL file not found: {file_path}")
|
263
274
|
return False
|
264
|
-
|
275
|
+
|
265
276
|
logger.info(f"All SSL files validated successfully: {files_to_check}")
|
266
277
|
return True
|
267
|
-
|
278
|
+
|
268
279
|
def get_hypercorn_config(self) -> Dict[str, Any]:
|
269
280
|
"""
|
270
281
|
Get configuration for hypercorn.
|
271
|
-
|
282
|
+
|
272
283
|
Returns:
|
273
284
|
Hypercorn configuration dictionary
|
274
285
|
"""
|
275
286
|
config = {
|
276
287
|
"host": "0.0.0.0", # Can be moved to settings
|
277
288
|
"port": self.get_port(),
|
278
|
-
"log_level": "info"
|
289
|
+
"log_level": "info",
|
279
290
|
}
|
280
|
-
|
291
|
+
|
281
292
|
if self.is_ssl_enabled():
|
282
293
|
ssl_config = self.get_ssl_config()
|
283
294
|
if ssl_config:
|
284
295
|
from mcp_proxy_adapter.core.ssl_utils import SSLUtils
|
296
|
+
|
285
297
|
hypercorn_ssl = SSLUtils.get_ssl_config_for_hypercorn(ssl_config)
|
286
298
|
config.update(hypercorn_ssl)
|
287
|
-
|
299
|
+
|
288
300
|
return config
|
289
301
|
|
290
302
|
|
291
303
|
# Global transport manager instance
|
292
|
-
transport_manager = TransportManager()
|
304
|
+
transport_manager = TransportManager()
|