mcp-proxy-adapter 6.3.11__py3-none-any.whl → 6.3.13__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/__main__.py +1 -0
- mcp_proxy_adapter/core/app_factory.py +20 -4
- mcp_proxy_adapter/core/client_security.py +1 -1
- mcp_proxy_adapter/core/proxy_registration.py +68 -3
- mcp_proxy_adapter/examples/basic_framework/main.py +4 -3
- mcp_proxy_adapter/examples/full_application/main.py +1 -1
- mcp_proxy_adapter/examples/setup_test_environment.py +830 -20
- mcp_proxy_adapter/examples/setup_test_environment_old.py +316 -0
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.3.11.dist-info → mcp_proxy_adapter-6.3.13.dist-info}/METADATA +1 -1
- {mcp_proxy_adapter-6.3.11.dist-info → mcp_proxy_adapter-6.3.13.dist-info}/RECORD +16 -15
- mcp_proxy_adapter_issue_package/demonstrate_issue.py +74 -44
- {mcp_proxy_adapter-6.3.11.dist-info → mcp_proxy_adapter-6.3.13.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.3.11.dist-info → mcp_proxy_adapter-6.3.13.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.3.11.dist-info → mcp_proxy_adapter-6.3.13.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-6.3.11.dist-info → mcp_proxy_adapter-6.3.13.dist-info}/top_level.txt +0 -0
@@ -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())
|
mcp_proxy_adapter/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: mcp-proxy-adapter
|
3
|
-
Version: 6.3.
|
3
|
+
Version: 6.3.13
|
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=
|
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=
|
7
|
+
mcp_proxy_adapter/version.py,sha256=q3dJbLNT-eaAvhtKZxgxdTJ_cMx1xA68oga3nWFknUs,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
|
@@ -53,13 +53,13 @@ mcp_proxy_adapter/commands/token_management_command.py,sha256=tCVjhWqAQ3KhcwSsZU
|
|
53
53
|
mcp_proxy_adapter/commands/transport_management_command.py,sha256=HEnUyL4S014jheyBwO90u9gnzk0qxBlOJdC_0Sxhq9E,4585
|
54
54
|
mcp_proxy_adapter/commands/unload_command.py,sha256=6CUM9B9c-mNxw7uvt2vcvZSnxMySfoMT5UmDhzNXq38,4984
|
55
55
|
mcp_proxy_adapter/core/__init__.py,sha256=3yt0CFZdsIG8Ln4bg-r4ISYzipm3ZUAxTn0twYTs9FI,867
|
56
|
-
mcp_proxy_adapter/core/app_factory.py,sha256=
|
56
|
+
mcp_proxy_adapter/core/app_factory.py,sha256=NvJBTY4sh9tIxbEvykfNROkHN6XviZAG6U-WNl8tMpI,19964
|
57
57
|
mcp_proxy_adapter/core/app_runner.py,sha256=1t9p_UkWb1IvZDTD7FOCRMNSpOSgtNeHM3i7PP7x6xc,10605
|
58
58
|
mcp_proxy_adapter/core/auth_validator.py,sha256=q8TNkdolvP-gM6Bvecc6nrVG9al5J31pocdwhguhTBk,19742
|
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=
|
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=
|
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,13 +97,14 @@ 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=
|
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
|
104
105
|
mcp_proxy_adapter/examples/universal_client.py,sha256=n1-cBPOiCipA86Zcc_mI_jMywDMZS1p3u5JT3AqTsrQ,27577
|
105
106
|
mcp_proxy_adapter/examples/basic_framework/__init__.py,sha256=4aYD--R6hy9n9CUxj7Osb9HcdVUMJ6_cfpu4ujkbCwI,345
|
106
|
-
mcp_proxy_adapter/examples/basic_framework/main.py,sha256=
|
107
|
+
mcp_proxy_adapter/examples/basic_framework/main.py,sha256=AkGUXW05_AK8SEKwlS_0isJKKqjulKBDPp7t36t9QJk,1787
|
107
108
|
mcp_proxy_adapter/examples/basic_framework/commands/__init__.py,sha256=_VQNLUEdsxUG-4yt9BZI_vtOxHAdGG0OUSsP6Wj-Vz4,76
|
108
109
|
mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py,sha256=IE_EIXMnkdXuakZn7wLD9kBFyfDF5lYi56ejgiBeb-A,70
|
109
110
|
mcp_proxy_adapter/examples/commands/__init__.py,sha256=zvY_OpH_B1bVc_khrNIl6O8vqCw1FH6gGMAsJAkGWGY,170
|
@@ -121,7 +122,7 @@ mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py,sha256=OR
|
|
121
122
|
mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py,sha256=vcMHakKOt9pvJDZ6XfgvcYJfrrxg-RnIC8wX6LPqKvA,3500
|
122
123
|
mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py,sha256=P5KjODcVPier-nxjWWpG7yO7ppSjSx-6BJ9FxArD-ps,2988
|
123
124
|
mcp_proxy_adapter/examples/full_application/__init__.py,sha256=xGiPYhRAzs1Fh9wA8HoowV-Gg9QMLaMZn-OamExq1TI,320
|
124
|
-
mcp_proxy_adapter/examples/full_application/main.py,sha256=
|
125
|
+
mcp_proxy_adapter/examples/full_application/main.py,sha256=ogL3Bil_5puGnwvMh3YNOjrW76FIzzoggKEp-04HSfo,7855
|
125
126
|
mcp_proxy_adapter/examples/full_application/proxy_endpoints.py,sha256=Kt_WAsG61HLTMkKQ1mQqjvlX9I4TcfwYq0NaRR9HKvM,6179
|
126
127
|
mcp_proxy_adapter/examples/full_application/commands/__init__.py,sha256=yQHxVSFkAyFLUOdk42QOebUODPlQV9IbydPgF3UKsGM,217
|
127
128
|
mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py,sha256=H7FPJmVJNWT61rPWxep06-7hsYRt8XYBUSBiwqpBurU,3096
|
@@ -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.
|
139
|
-
mcp_proxy_adapter_issue_package/demonstrate_issue.py,sha256=
|
140
|
-
mcp_proxy_adapter-6.3.
|
141
|
-
mcp_proxy_adapter-6.3.
|
142
|
-
mcp_proxy_adapter-6.3.
|
143
|
-
mcp_proxy_adapter-6.3.
|
144
|
-
mcp_proxy_adapter-6.3.
|
139
|
+
mcp_proxy_adapter-6.3.13.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.13.dist-info/METADATA,sha256=Nx_rgEvCndANAGM_s78moYtZU1NwZ3LAdHOMHvwBa3Q,22348
|
142
|
+
mcp_proxy_adapter-6.3.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
143
|
+
mcp_proxy_adapter-6.3.13.dist-info/entry_points.txt,sha256=J3eV6ID0lt_VSp4lIdIgBFTqLCThgObNNxRCbyfiMHw,70
|
144
|
+
mcp_proxy_adapter-6.3.13.dist-info/top_level.txt,sha256=CHk-Mc-AxjO-tRheegA2qLiQnU4vZRnxuTF81So6SAc,50
|
145
|
+
mcp_proxy_adapter-6.3.13.dist-info/RECORD,,
|
@@ -13,18 +13,22 @@ import logging
|
|
13
13
|
from pathlib import Path
|
14
14
|
|
15
15
|
# Setup logging
|
16
|
-
logging.basicConfig(
|
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 (
|
46
|
-
|
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(
|
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 (
|
81
|
-
|
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
|
-
|
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
|
-
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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())
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|