mcp-proxy-adapter 6.3.11__py3-none-any.whl → 6.3.12__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.
@@ -0,0 +1,316 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Author: Vasiliy Zdanovskiy
4
+ email: vasilyvz@gmail.com
5
+ Script for setting up test environment for MCP Proxy Adapter.
6
+ Prepares the test environment with all necessary files and directories.
7
+ Uses mcp_security_framework for certificate generation.
8
+
9
+ This script accepts an output directory and copies required example files
10
+ and helper scripts into that directory, creating a ready-to-use workspace.
11
+ By default, the current working directory is used, so end-users can run
12
+ it in their project root after installing this framework in a virtual
13
+ environment.
14
+ """
15
+ import shutil
16
+ import sys
17
+ import argparse
18
+ from pathlib import Path
19
+
20
+ # Import mcp_security_framework
21
+ try:
22
+ from mcp_security_framework.core.cert_manager import CertificateManager
23
+ from mcp_security_framework.schemas.config import (
24
+ CertificateConfig,
25
+ CAConfig,
26
+ ServerCertConfig,
27
+ ClientCertConfig,
28
+ )
29
+
30
+ SECURITY_FRAMEWORK_AVAILABLE = True
31
+ except ImportError:
32
+ SECURITY_FRAMEWORK_AVAILABLE = False
33
+ print("Warning: mcp_security_framework not available")
34
+
35
+
36
+ def _get_package_paths() -> tuple[Path, Path]:
37
+ """
38
+ Resolve source paths for examples and utils relative to this file
39
+ to avoid importing the package during setup.
40
+ """
41
+ pkg_root = Path(__file__).resolve().parents[1]
42
+ return pkg_root / "examples", pkg_root / "utils"
43
+
44
+
45
+ def setup_test_environment(output_dir: Path) -> None:
46
+ """
47
+ Setup test environment under output_dir with required files
48
+ and directories.
49
+
50
+ All created directories and copied files are rooted at output_dir
51
+ so users can run scripts relative to that directory.
52
+ """
53
+ print("🔧 Setting up test environment...")
54
+ output_dir = output_dir.resolve()
55
+ output_dir.mkdir(parents=True, exist_ok=True)
56
+ # Create test environment directory structure
57
+ directories = [
58
+ "examples/basic_framework",
59
+ "examples/full_application",
60
+ "scripts",
61
+ "configs",
62
+ "certs",
63
+ "keys",
64
+ "tokens",
65
+ "logs",
66
+ ]
67
+ for directory in directories:
68
+ target_dir = output_dir / directory
69
+ target_dir.mkdir(parents=True, exist_ok=True)
70
+ print(f"✅ Created directory: {target_dir}")
71
+ # Resolve package paths
72
+ examples_src_root, utils_src_root = _get_package_paths()
73
+ # Copy example files
74
+ basic_framework_src = examples_src_root / "basic_framework"
75
+ if basic_framework_src.exists():
76
+ shutil.copytree(
77
+ basic_framework_src,
78
+ output_dir / "examples/basic_framework",
79
+ dirs_exist_ok=True,
80
+ )
81
+ print("✅ Copied basic_framework examples")
82
+ full_application_src = examples_src_root / "full_application"
83
+ if full_application_src.exists():
84
+ shutil.copytree(
85
+ full_application_src,
86
+ output_dir / "examples/full_application",
87
+ dirs_exist_ok=True,
88
+ )
89
+ print("✅ Copied full_application examples")
90
+ # Copy utility scripts
91
+ config_generator_src = utils_src_root / "config_generator.py"
92
+ if config_generator_src.exists():
93
+ shutil.copy2(config_generator_src, output_dir / "scripts/")
94
+ print("✅ Copied config_generator.py")
95
+ # Copy certificate generation scripts
96
+ create_certs_src = examples_src_root / "create_certificates_simple.py"
97
+ if create_certs_src.exists():
98
+ shutil.copy2(create_certs_src, output_dir / "scripts/")
99
+ print("✅ Copied create_certificates_simple.py")
100
+ cert_tokens_src = examples_src_root / "generate_certificates_and_tokens.py"
101
+ if cert_tokens_src.exists():
102
+ shutil.copy2(cert_tokens_src, output_dir / "scripts/")
103
+ print("✅ Copied generate_certificates_and_tokens.py")
104
+
105
+ # Copy roles.json to the root directory for compatibility
106
+ roles_src = examples_src_root / "roles.json"
107
+ if roles_src.exists():
108
+ shutil.copy2(roles_src, output_dir)
109
+ print("✅ Copied roles.json to root directory")
110
+
111
+ # Also copy from configs directory if it exists
112
+ roles_configs_src = output_dir / "configs" / "roles.json"
113
+ if roles_configs_src.exists():
114
+ shutil.copy2(roles_configs_src, output_dir / "roles.json")
115
+ print("✅ Updated roles.json from configs directory")
116
+ print("🎉 Test environment setup completed successfully at: {}".format(output_dir))
117
+
118
+
119
+ def generate_certificates_with_framework(output_dir: Path) -> bool:
120
+ """
121
+ Generate certificates using mcp_security_framework.
122
+ """
123
+ if not SECURITY_FRAMEWORK_AVAILABLE:
124
+ print("❌ mcp_security_framework not available for certificate " "generation")
125
+ return False
126
+ try:
127
+ print("🔐 Generating certificates using mcp_security_framework...")
128
+ # Configure certificate manager
129
+ cert_config = CertificateConfig(
130
+ cert_storage_path=str((output_dir / "certs").resolve()),
131
+ key_storage_path=str((output_dir / "keys").resolve()),
132
+ default_validity_days=365,
133
+ key_size=2048,
134
+ hash_algorithm="sha256",
135
+ )
136
+ cert_manager = CertificateManager(cert_config)
137
+ # Generate CA certificate
138
+ ca_config = CAConfig(
139
+ common_name="MCP Proxy Adapter Test CA",
140
+ organization="Test Organization",
141
+ organizational_unit="Certificate Authority",
142
+ country="US",
143
+ state="Test State",
144
+ locality="Test City",
145
+ validity_years=10, # Use validity_years instead of validity_days
146
+ key_size=2048,
147
+ hash_algorithm="sha256",
148
+ )
149
+ cert_pair = cert_manager.create_root_ca(ca_config)
150
+ if not cert_pair or not cert_pair.certificate_path:
151
+ print("❌ Failed to create CA certificate: Invalid certificate pair")
152
+ return False
153
+ print("✅ CA certificate created successfully")
154
+ # Find CA key file
155
+ ca_key_path = cert_pair.private_key_path
156
+ # Generate server certificate
157
+ server_config = ServerCertConfig(
158
+ common_name="localhost",
159
+ organization="Test Organization",
160
+ organizational_unit="Server",
161
+ country="US",
162
+ state="Test State",
163
+ locality="Test City",
164
+ validity_days=365,
165
+ key_size=2048,
166
+ hash_algorithm="sha256",
167
+ subject_alt_names=[
168
+ "localhost",
169
+ "127.0.0.1",
170
+ ],
171
+ ca_cert_path=cert_pair.certificate_path,
172
+ ca_key_path=ca_key_path,
173
+ )
174
+ cert_pair = cert_manager.create_server_certificate(server_config)
175
+ if not cert_pair or not cert_pair.certificate_path:
176
+ print("❌ Failed to create server certificate: Invalid certificate " "pair")
177
+ return False
178
+ print("✅ Server certificate created successfully")
179
+ # Generate client certificates
180
+ client_configs = [
181
+ (
182
+ "admin",
183
+ ["admin"],
184
+ [
185
+ "read",
186
+ "write",
187
+ "execute",
188
+ "delete",
189
+ "admin",
190
+ "register",
191
+ "unregister",
192
+ "heartbeat",
193
+ "discover",
194
+ ],
195
+ ),
196
+ (
197
+ "user",
198
+ ["user"],
199
+ [
200
+ "read",
201
+ "execute",
202
+ "register",
203
+ "unregister",
204
+ "heartbeat",
205
+ "discover",
206
+ ],
207
+ ),
208
+ ("readonly", ["readonly"], ["read", "discover"]),
209
+ ("guest", ["guest"], ["read", "discover"]),
210
+ (
211
+ "proxy",
212
+ ["proxy"],
213
+ ["register", "unregister", "heartbeat", "discover"],
214
+ ),
215
+ ]
216
+ for client_name, roles, permissions in client_configs:
217
+ client_config = ClientCertConfig(
218
+ common_name=f"{client_name}-client",
219
+ organization="Test Organization",
220
+ organizational_unit="Client",
221
+ country="US",
222
+ state="Test State",
223
+ locality="Test City",
224
+ validity_days=730,
225
+ key_size=2048,
226
+ hash_algorithm="sha256",
227
+ roles=roles,
228
+ permissions=permissions,
229
+ ca_cert_path=cert_pair.certificate_path,
230
+ ca_key_path=ca_key_path,
231
+ )
232
+ cert_pair = cert_manager.create_client_certificate(client_config)
233
+ if not cert_pair or not cert_pair.certificate_path:
234
+ print(
235
+ (
236
+ "❌ Failed to create client certificate {}: "
237
+ "Invalid certificate pair"
238
+ ).format(client_name)
239
+ )
240
+ return False
241
+ print("✅ Client certificate {} created successfully".format(client_name))
242
+ print(
243
+ "🎉 All certificates generated successfully using "
244
+ "mcp_security_framework!"
245
+ )
246
+ return True
247
+ except Exception as e:
248
+ print("❌ Error generating certificates with framework: {}".format(e))
249
+ print("\n🔧 TROUBLESHOOTING:")
250
+ print("1. Check if mcp_security_framework is installed:")
251
+ print(" pip install mcp_security_framework")
252
+ print("\n2. Verify write permissions in output directory")
253
+ print("\n3. Check if certs/ and keys/ directories exist")
254
+ return False
255
+
256
+
257
+ def main() -> int:
258
+ """Main function for command line execution."""
259
+ parser = argparse.ArgumentParser(
260
+ description="Setup test environment for MCP Proxy Adapter"
261
+ )
262
+ parser.add_argument(
263
+ "--output-dir",
264
+ "-o",
265
+ type=str,
266
+ default=".",
267
+ help=(
268
+ "Target directory to create the test environment "
269
+ "(default: current directory)"
270
+ ),
271
+ )
272
+ args = parser.parse_args()
273
+ try:
274
+ target_root = Path(args.output_dir)
275
+ setup_test_environment(target_root)
276
+ # Generate certificates if framework is available
277
+ if SECURITY_FRAMEWORK_AVAILABLE:
278
+ generate_certificates_with_framework(target_root)
279
+ else:
280
+ print(
281
+ "⚠️ Skipping certificate generation (mcp_security_framework "
282
+ "not available)"
283
+ )
284
+ except Exception as e:
285
+ print(
286
+ "❌ Error setting up test environment: {}".format(e),
287
+ file=sys.stderr,
288
+ )
289
+ print("\n🔧 TROUBLESHOOTING:")
290
+ print("1. Check if output directory is writable")
291
+ print("2. Verify mcp_security_framework installation")
292
+ print("3. Check available disk space")
293
+ return 1
294
+
295
+ print("\n" + "=" * 60)
296
+ print("✅ TEST ENVIRONMENT SETUP COMPLETED SUCCESSFULLY")
297
+ print("=" * 60)
298
+ print("\n📋 NEXT STEPS:")
299
+ print("1. Generate test configurations:")
300
+ print(
301
+ " python -m mcp_proxy_adapter.examples.generate_test_configs --output-dir configs"
302
+ )
303
+ print("\n2. Generate additional certificates (if needed):")
304
+ print(" python -m mcp_proxy_adapter.examples.generate_certificates")
305
+ print("\n3. Run security tests:")
306
+ print(" python -m mcp_proxy_adapter.examples.run_security_tests")
307
+ print("\n4. Start basic framework example:")
308
+ print(
309
+ " python -m mcp_proxy_adapter.examples.basic_framework.main --config configs/https_simple.json"
310
+ )
311
+ print("=" * 60)
312
+ return 0
313
+
314
+
315
+ if __name__ == "__main__":
316
+ exit(main())
@@ -2,4 +2,4 @@
2
2
  Version information for MCP Proxy Adapter.
3
3
  """
4
4
 
5
- __version__ = "6.3.11"
5
+ __version__ = "6.3.12"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-proxy-adapter
3
- Version: 6.3.11
3
+ Version: 6.3.12
4
4
  Summary: Powerful JSON-RPC microservices framework with built-in security, authentication, and proxy registration
5
5
  Home-page: https://github.com/maverikod/mcp-proxy-adapter
6
6
  Author: Vasiliy Zdanovskiy
@@ -1,10 +1,10 @@
1
1
  mcp_proxy_adapter/__init__.py,sha256=iH0EBBsRj_cfZJpAIsgN_8tTdfefhnl6uUKHjLHhWDQ,1037
2
- mcp_proxy_adapter/__main__.py,sha256=bh7XUZjW7Mo6cmA8yHrCbOoVfLSthZ_np9bBdiOF1Ko,578
2
+ mcp_proxy_adapter/__main__.py,sha256=sq3tANRuTd18euamt0Bmn1sJeAyzXENZ5VvsMwbrDFA,579
3
3
  mcp_proxy_adapter/config.py,sha256=-7iVS0mUWWKNeao7nqTAFlUD6FcMwRlDkchN7OwYsr0,21662
4
4
  mcp_proxy_adapter/custom_openapi.py,sha256=yLle4CntYK9wpivgn9NflZyJhy-YNrmWjJzt0ai5nP0,14672
5
5
  mcp_proxy_adapter/main.py,sha256=qDkQTXnCvf8u0I3b8PRrguOoVdjd8YRr90ZooOqeOto,3401
6
6
  mcp_proxy_adapter/openapi.py,sha256=2UZOI09ZDRJuBYBjKbMyb2U4uASszoCMD5o_4ktRpvg,13480
7
- mcp_proxy_adapter/version.py,sha256=ovkQ_8lraVLkXaw9XpLn7O48DiegkkpXjYxmTsjH7p4,75
7
+ mcp_proxy_adapter/version.py,sha256=MMZ_TWZADBWTAiuZRAx6apN1G6g8QweStOITjQVux4U,75
8
8
  mcp_proxy_adapter/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  mcp_proxy_adapter/api/app.py,sha256=22NIDGo6pGkOZnvBWeKv_-RIRye4YaYkIskVGIsCCMs,28600
10
10
  mcp_proxy_adapter/api/handlers.py,sha256=iyFGoEuUS1wxbV1ELA0zmaxIyQR7j4zw-4MrD-uIO6E,8294
@@ -59,7 +59,7 @@ mcp_proxy_adapter/core/auth_validator.py,sha256=q8TNkdolvP-gM6Bvecc6nrVG9al5J31p
59
59
  mcp_proxy_adapter/core/certificate_utils.py,sha256=yeDwi-j42CxK_g-r5_ragGFY_HdSgDfTWHVUjDHF6nI,38480
60
60
  mcp_proxy_adapter/core/client.py,sha256=qIbPl8prEwK2U65kl-vGJW2_imo1E4i6HxG_VpPeWpQ,21168
61
61
  mcp_proxy_adapter/core/client_manager.py,sha256=yD8HZJlOwmDbVU49YfzSbh1XZ-Vib8qfcLVAaH03Jdg,8832
62
- mcp_proxy_adapter/core/client_security.py,sha256=guhlf8fJ2diUdNPa8slsiWXb6Hnkpifp4-X_ctMn0dg,13135
62
+ mcp_proxy_adapter/core/client_security.py,sha256=siUaYorcDbpZsEIKgLfg-jBKkp7z_Er8wsO63mDD3Is,13127
63
63
  mcp_proxy_adapter/core/config_converter.py,sha256=Wnnsrbw7DxtgDfLG-IyyzK-hkKu0_1yp7-7dW87tu_4,17422
64
64
  mcp_proxy_adapter/core/config_validator.py,sha256=M_iQqskKk5VfziNkXTJwjU97vEBLNSsXIqtpTQKHAcA,9644
65
65
  mcp_proxy_adapter/core/crl_utils.py,sha256=Jnwq2UN52IoCDZCwByRP3XNMOJexftb-mVaH6zes6Fc,11706
@@ -69,7 +69,7 @@ mcp_proxy_adapter/core/mtls_asgi.py,sha256=tvk0P9024s18dcCHY9AaQIecT4ojOTv21EuQW
69
69
  mcp_proxy_adapter/core/mtls_asgi_app.py,sha256=DT_fTUH1RkvBa3ThbyCyNb-XUHyCb4DqaKA1gcZC6z4,6538
70
70
  mcp_proxy_adapter/core/protocol_manager.py,sha256=FM01lBDxUU-W7P8sQR72Jel1aB2nbp1PvO4TRoZ5c_Y,14881
71
71
  mcp_proxy_adapter/core/proxy_client.py,sha256=XweRXBNbntJ5UYdSxUDcOYmJY0jbsKbfdWC0Jw72wdQ,23039
72
- mcp_proxy_adapter/core/proxy_registration.py,sha256=4XEg77rQOSA_fsH5eYq2NmtjgznWcLlOOi29LI2G3iM,22335
72
+ mcp_proxy_adapter/core/proxy_registration.py,sha256=HzcYay7V0lPgHdmHa980e9HiRGNvFpwPaure1fULiGk,25610
73
73
  mcp_proxy_adapter/core/role_utils.py,sha256=YwRenGoXI5YrHVbFjKFAH2DJs2miyqhcr9LWF7mxieg,12284
74
74
  mcp_proxy_adapter/core/security_adapter.py,sha256=MAtNthsp7Qj4-oLFzSi7Pr3vWQbWS_uelqa5LGgrXIE,12957
75
75
  mcp_proxy_adapter/core/security_factory.py,sha256=M-1McwUOmuV7Eo-m_P2undtJVNK_KIjDx8o_uRY8rLo,8005
@@ -97,7 +97,8 @@ mcp_proxy_adapter/examples/run_proxy_server.py,sha256=SBLSSY2F_VEBQD3MsCE_Pa9xFE
97
97
  mcp_proxy_adapter/examples/run_security_tests.py,sha256=0vjaUdWC-rLyviQuNxM3PtfiU9TzSRuxGxWMehrFA_w,23311
98
98
  mcp_proxy_adapter/examples/run_security_tests_fixed.py,sha256=2BKMT0_-FhmcZA73hdQOt2XR7Cgb9Sq8qBI88BkwAAA,10934
99
99
  mcp_proxy_adapter/examples/security_test_client.py,sha256=K5gEVat1SJS2pBVxqLl5c9-uiiG12k8UT3ULQDXZ2Uc,35713
100
- mcp_proxy_adapter/examples/setup_test_environment.py,sha256=qV7IT8lMObcaPeNrFXI7CkuvJnbJsLIVaRn7fTFXjJ0,11631
100
+ mcp_proxy_adapter/examples/setup_test_environment.py,sha256=u34_wRAzrFzfK6yt2jyCr9XeAgO2z2maJDmCpPlMAPE,37142
101
+ mcp_proxy_adapter/examples/setup_test_environment_old.py,sha256=qV7IT8lMObcaPeNrFXI7CkuvJnbJsLIVaRn7fTFXjJ0,11631
101
102
  mcp_proxy_adapter/examples/test_config.py,sha256=ekEoUZe9q484vU_0IxOVhQdNMVJXG3IpmQpP--VmuDI,6491
102
103
  mcp_proxy_adapter/examples/test_config_generator.py,sha256=PBXk1V_awJ-iBlbE66Pme5sQwu6CJDxkmqgm8uPtM58,4091
103
104
  mcp_proxy_adapter/examples/test_examples.py,sha256=CYlVatdHUVC_rwv4NsvxFG3GXiKIyxPDUH43BOJHjrU,12330
@@ -135,10 +136,10 @@ mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py,sha256=hU
135
136
  mcp_proxy_adapter/schemas/base_schema.json,sha256=v9G9cGMd4dRhCZsOQ_FMqOi5VFyVbI6Cf3fyIvOT9dc,2881
136
137
  mcp_proxy_adapter/schemas/openapi_schema.json,sha256=C3yLkwmDsvnLW9B5gnKKdBGl4zxkeU-rEmjTrNVsQU0,8405
137
138
  mcp_proxy_adapter/utils/config_generator.py,sha256=UXxuxxAyKTesAS3DOofQ26e20v771inA7EfBV8PZD1c,47543
138
- mcp_proxy_adapter-6.3.11.dist-info/licenses/LICENSE,sha256=6KdtUcTwmTRbJrAmYjVn7e6S-V42ubeDJ-AiVEzZ510,1075
139
- mcp_proxy_adapter_issue_package/demonstrate_issue.py,sha256=NDyiWSjQHly4ji1sB4ZqdmOFyZlPQ4Id80dTrRwcmoE,7736
140
- mcp_proxy_adapter-6.3.11.dist-info/METADATA,sha256=LU-DEQubtviXhl43eDn0ub5-qRkA3zQKuIYKaSxKpeA,22348
141
- mcp_proxy_adapter-6.3.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
142
- mcp_proxy_adapter-6.3.11.dist-info/entry_points.txt,sha256=J3eV6ID0lt_VSp4lIdIgBFTqLCThgObNNxRCbyfiMHw,70
143
- mcp_proxy_adapter-6.3.11.dist-info/top_level.txt,sha256=CHk-Mc-AxjO-tRheegA2qLiQnU4vZRnxuTF81So6SAc,50
144
- mcp_proxy_adapter-6.3.11.dist-info/RECORD,,
139
+ mcp_proxy_adapter-6.3.12.dist-info/licenses/LICENSE,sha256=6KdtUcTwmTRbJrAmYjVn7e6S-V42ubeDJ-AiVEzZ510,1075
140
+ mcp_proxy_adapter_issue_package/demonstrate_issue.py,sha256=O54fwWQvUAjEGiHhQGm1JLnARkhVCwAqjBk_89HyRbY,7894
141
+ mcp_proxy_adapter-6.3.12.dist-info/METADATA,sha256=jbTTNk7eD1EY_tf8GXmYTOlvornEW-WZSi-XfCRlVR8,22348
142
+ mcp_proxy_adapter-6.3.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
143
+ mcp_proxy_adapter-6.3.12.dist-info/entry_points.txt,sha256=J3eV6ID0lt_VSp4lIdIgBFTqLCThgObNNxRCbyfiMHw,70
144
+ mcp_proxy_adapter-6.3.12.dist-info/top_level.txt,sha256=CHk-Mc-AxjO-tRheegA2qLiQnU4vZRnxuTF81So6SAc,50
145
+ mcp_proxy_adapter-6.3.12.dist-info/RECORD,,
@@ -13,18 +13,22 @@ import logging
13
13
  from pathlib import Path
14
14
 
15
15
  # Setup logging
16
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
16
+ logging.basicConfig(
17
+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
18
+ )
17
19
  logger = logging.getLogger(__name__)
18
20
 
21
+
19
22
  class MockSSLConfig:
20
23
  """Mock SSL configuration to demonstrate the issue."""
21
-
24
+
22
25
  def __init__(self, verify_server=True):
23
26
  self.verify_server = verify_server
24
27
  self.ca_cert_file = "certs/mtls/truststore.pem"
25
28
  self.client_cert_file = "certs/mtls/client/embedding-service.pem"
26
29
  self.client_key_file = "certs/mtls/client/embedding-service.key"
27
30
 
31
+
28
32
  def create_client_ssl_context_broken(ssl_config: MockSSLConfig) -> ssl.SSLContext:
29
33
  """
30
34
  BROKEN: This is how mcp_proxy_adapter currently works.
@@ -32,34 +36,40 @@ def create_client_ssl_context_broken(ssl_config: MockSSLConfig) -> ssl.SSLContex
32
36
  """
33
37
  logger.info("🔴 Creating SSL context (BROKEN - current mcp_proxy_adapter behavior)")
34
38
  logger.info(f" Configuration: verify_server={ssl_config.verify_server}")
35
-
39
+
36
40
  try:
37
41
  ssl_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
38
-
42
+
39
43
  # Load CA certificate if provided
40
44
  if ssl_config.ca_cert_file and Path(ssl_config.ca_cert_file).exists():
41
45
  ssl_context.load_verify_locations(ssl_config.ca_cert_file)
42
46
  logger.info(f" ✅ Loaded CA certificate: {ssl_config.ca_cert_file}")
43
-
47
+
44
48
  # Load client certificate if provided
45
- if (ssl_config.client_cert_file and ssl_config.client_key_file and
46
- Path(ssl_config.client_cert_file).exists() and Path(ssl_config.client_key_file).exists()):
49
+ if (
50
+ ssl_config.client_cert_file
51
+ and ssl_config.client_key_file
52
+ and Path(ssl_config.client_cert_file).exists()
53
+ and Path(ssl_config.client_key_file).exists()
54
+ ):
47
55
  ssl_context.load_cert_chain(
48
- ssl_config.client_cert_file,
49
- ssl_config.client_key_file
56
+ ssl_config.client_cert_file, ssl_config.client_key_file
50
57
  )
51
- logger.info(f" ✅ Loaded client certificate: {ssl_config.client_cert_file}")
52
-
58
+ logger.info(
59
+ f" ✅ Loaded client certificate: {ssl_config.client_cert_file}"
60
+ )
61
+
53
62
  # PROBLEM: This line hardcodes CERT_REQUIRED, ignoring configuration
54
63
  ssl_context.verify_mode = ssl.CERT_REQUIRED
55
64
  logger.info(" ❌ HARDCODED: ssl_context.verify_mode = ssl.CERT_REQUIRED")
56
65
  logger.info(" ❌ IGNORES: verify_server=False configuration!")
57
-
66
+
58
67
  return ssl_context
59
68
  except Exception as e:
60
69
  logger.error(f" ❌ Failed to create SSL context: {e}")
61
70
  return ssl.create_default_context()
62
71
 
72
+
63
73
  def create_client_ssl_context_fixed(ssl_config: MockSSLConfig) -> ssl.SSLContext:
64
74
  """
65
75
  FIXED: This is how mcp_proxy_adapter should work.
@@ -67,48 +77,60 @@ def create_client_ssl_context_fixed(ssl_config: MockSSLConfig) -> ssl.SSLContext
67
77
  """
68
78
  logger.info("🟢 Creating SSL context (FIXED - proposed solution)")
69
79
  logger.info(f" Configuration: verify_server={ssl_config.verify_server}")
70
-
80
+
71
81
  try:
72
82
  ssl_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
73
-
83
+
74
84
  # Load CA certificate if provided
75
85
  if ssl_config.ca_cert_file and Path(ssl_config.ca_cert_file).exists():
76
86
  ssl_context.load_verify_locations(ssl_config.ca_cert_file)
77
87
  logger.info(f" ✅ Loaded CA certificate: {ssl_config.ca_cert_file}")
78
-
88
+
79
89
  # Load client certificate if provided
80
- if (ssl_config.client_cert_file and ssl_config.client_key_file and
81
- Path(ssl_config.client_cert_file).exists() and Path(ssl_config.client_key_file).exists()):
90
+ if (
91
+ ssl_config.client_cert_file
92
+ and ssl_config.client_key_file
93
+ and Path(ssl_config.client_cert_file).exists()
94
+ and Path(ssl_config.client_key_file).exists()
95
+ ):
82
96
  ssl_context.load_cert_chain(
83
- ssl_config.client_cert_file,
84
- ssl_config.client_key_file
97
+ ssl_config.client_cert_file, ssl_config.client_key_file
98
+ )
99
+ logger.info(
100
+ f" ✅ Loaded client certificate: {ssl_config.client_cert_file}"
85
101
  )
86
- logger.info(f" ✅ Loaded client certificate: {ssl_config.client_cert_file}")
87
-
102
+
88
103
  # FIX: Respect the verify_server configuration
89
104
  if not ssl_config.verify_server:
90
105
  ssl_context.check_hostname = False # Must be set BEFORE verify_mode
91
106
  ssl_context.verify_mode = ssl.CERT_NONE
92
107
  logger.info(" ✅ RESPECTS: ssl_context.check_hostname = False")
93
- logger.info(" ✅ RESPECTS: ssl_context.verify_mode = ssl.CERT_NONE (verify_server=False)")
108
+ logger.info(
109
+ " ✅ RESPECTS: ssl_context.verify_mode = ssl.CERT_NONE (verify_server=False)"
110
+ )
94
111
  else:
95
112
  ssl_context.verify_mode = ssl.CERT_REQUIRED
96
- logger.info(" ✅ RESPECTS: ssl_context.verify_mode = ssl.CERT_REQUIRED (verify_server=True)")
97
-
113
+ logger.info(
114
+ " ✅ RESPECTS: ssl_context.verify_mode = ssl.CERT_REQUIRED (verify_server=True)"
115
+ )
116
+
98
117
  return ssl_context
99
118
  except Exception as e:
100
119
  logger.error(f" ❌ Failed to create SSL context: {e}")
101
120
  return ssl.create_default_context()
102
121
 
122
+
103
123
  async def test_connection(ssl_context: ssl.SSLContext, url: str, description: str):
104
124
  """Test connection with the given SSL context."""
105
125
  logger.info(f"\n🧪 Testing connection: {description}")
106
126
  logger.info(f" URL: {url}")
107
-
127
+
108
128
  try:
109
129
  connector = aiohttp.TCPConnector(ssl=ssl_context)
110
130
  async with aiohttp.ClientSession(connector=connector) as session:
111
- async with session.get(url, timeout=aiohttp.ClientTimeout(total=5)) as response:
131
+ async with session.get(
132
+ url, timeout=aiohttp.ClientTimeout(total=5)
133
+ ) as response:
112
134
  logger.info(f" ✅ SUCCESS: HTTP {response.status}")
113
135
  return True
114
136
  except ssl.SSLCertVerificationError as e:
@@ -118,46 +140,53 @@ async def test_connection(ssl_context: ssl.SSLContext, url: str, description: st
118
140
  logger.error(f" ❌ CONNECTION ERROR: {e}")
119
141
  return False
120
142
 
143
+
121
144
  async def main():
122
145
  """Main demonstration function."""
123
146
  logger.info("🚀 mcp_proxy_adapter SSL Verification Issue Demonstration")
124
147
  logger.info("=" * 70)
125
-
148
+
126
149
  # Test URL with self-signed certificate
127
150
  test_url = "https://127.0.0.1:3004/health"
128
-
151
+
129
152
  # Configuration with verify_server=False (should be respected)
130
153
  config = MockSSLConfig(verify_server=False)
131
-
154
+
132
155
  logger.info(f"\n📋 Test Configuration:")
133
156
  logger.info(f" URL: {test_url}")
134
157
  logger.info(f" verify_server: {config.verify_server}")
135
158
  logger.info(f" CA cert: {config.ca_cert_file}")
136
159
  logger.info(f" Client cert: {config.client_cert_file}")
137
-
160
+
138
161
  # Test 1: Current broken behavior
139
- logger.info(f"\n" + "="*50)
162
+ logger.info(f"\n" + "=" * 50)
140
163
  logger.info("TEST 1: Current mcp_proxy_adapter behavior (BROKEN)")
141
- logger.info("="*50)
142
-
164
+ logger.info("=" * 50)
165
+
143
166
  broken_ssl_context = create_client_ssl_context_broken(config)
144
- broken_result = await test_connection(broken_ssl_context, test_url, "BROKEN - ignores verify_server=False")
145
-
167
+ broken_result = await test_connection(
168
+ broken_ssl_context, test_url, "BROKEN - ignores verify_server=False"
169
+ )
170
+
146
171
  # Test 2: Proposed fixed behavior
147
- logger.info(f"\n" + "="*50)
172
+ logger.info(f"\n" + "=" * 50)
148
173
  logger.info("TEST 2: Proposed fixed behavior")
149
- logger.info("="*50)
150
-
174
+ logger.info("=" * 50)
175
+
151
176
  fixed_ssl_context = create_client_ssl_context_fixed(config)
152
- fixed_result = await test_connection(fixed_ssl_context, test_url, "FIXED - respects verify_server=False")
153
-
177
+ fixed_result = await test_connection(
178
+ fixed_ssl_context, test_url, "FIXED - respects verify_server=False"
179
+ )
180
+
154
181
  # Summary
155
- logger.info(f"\n" + "="*50)
182
+ logger.info(f"\n" + "=" * 50)
156
183
  logger.info("SUMMARY")
157
- logger.info("="*50)
158
- logger.info(f"🔴 Current behavior (broken): {'FAILED' if not broken_result else 'SUCCESS'}")
184
+ logger.info("=" * 50)
185
+ logger.info(
186
+ f"🔴 Current behavior (broken): {'FAILED' if not broken_result else 'SUCCESS'}"
187
+ )
159
188
  logger.info(f"🟢 Proposed fix: {'SUCCESS' if fixed_result else 'FAILED'}")
160
-
189
+
161
190
  if not broken_result and fixed_result:
162
191
  logger.info("\n✅ DEMONSTRATION SUCCESSFUL!")
163
192
  logger.info(" The issue is confirmed: verify_server=False is ignored")
@@ -169,6 +198,7 @@ async def main():
169
198
  logger.info("\n❓ INCONCLUSIVE")
170
199
  logger.info(" Results don't clearly demonstrate the issue")
171
200
 
201
+
172
202
  if __name__ == "__main__":
173
203
  try:
174
204
  asyncio.run(main())