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
@@ -2,20 +2,28 @@
|
|
2
2
|
"""
|
3
3
|
Author: Vasiliy Zdanovskiy
|
4
4
|
email: vasilyvz@gmail.com
|
5
|
-
|
6
|
-
Prepares the test environment with all necessary files and
|
7
|
-
|
5
|
+
Enhanced script for setting up test environment for MCP Proxy Adapter.
|
6
|
+
Prepares the test environment with all necessary files, directories, and configurations.
|
7
|
+
Includes comprehensive documentation and validation for configuration settings.
|
8
8
|
|
9
9
|
This script accepts an output directory and copies required example files
|
10
10
|
and helper scripts into that directory, creating a ready-to-use workspace.
|
11
11
|
By default, the current working directory is used, so end-users can run
|
12
12
|
it in their project root after installing this framework in a virtual
|
13
13
|
environment.
|
14
|
+
|
15
|
+
Features:
|
16
|
+
- Comprehensive configuration documentation
|
17
|
+
- Validation of mutually exclusive settings
|
18
|
+
- Protocol-aware configuration generation
|
19
|
+
- Enhanced error handling and troubleshooting
|
14
20
|
"""
|
15
21
|
import shutil
|
16
22
|
import sys
|
17
23
|
import argparse
|
24
|
+
import json
|
18
25
|
from pathlib import Path
|
26
|
+
from typing import Dict, List, Any, Optional, Tuple
|
19
27
|
|
20
28
|
# Import mcp_security_framework
|
21
29
|
try:
|
@@ -33,6 +41,145 @@ except ImportError:
|
|
33
41
|
print("Warning: mcp_security_framework not available")
|
34
42
|
|
35
43
|
|
44
|
+
class ConfigurationValidator:
|
45
|
+
"""
|
46
|
+
Validates MCP Proxy Adapter configurations for mutually exclusive settings
|
47
|
+
and protocol compatibility.
|
48
|
+
"""
|
49
|
+
|
50
|
+
def __init__(self):
|
51
|
+
self.errors: List[str] = []
|
52
|
+
self.warnings: List[str] = []
|
53
|
+
|
54
|
+
def validate_config(
|
55
|
+
self, config: Dict[str, Any], config_name: str
|
56
|
+
) -> Tuple[bool, List[str], List[str]]:
|
57
|
+
"""
|
58
|
+
Validate a configuration for mutually exclusive settings and protocol compatibility.
|
59
|
+
|
60
|
+
Args:
|
61
|
+
config: Configuration dictionary to validate
|
62
|
+
config_name: Name of the configuration for error reporting
|
63
|
+
|
64
|
+
Returns:
|
65
|
+
Tuple of (is_valid, errors, warnings)
|
66
|
+
"""
|
67
|
+
self.errors = []
|
68
|
+
self.warnings = []
|
69
|
+
|
70
|
+
# Validate protocol settings
|
71
|
+
self._validate_protocol_settings(config, config_name)
|
72
|
+
|
73
|
+
# Validate SSL/TLS settings
|
74
|
+
self._validate_ssl_settings(config, config_name)
|
75
|
+
|
76
|
+
# Validate mTLS settings
|
77
|
+
self._validate_mtls_settings(config, config_name)
|
78
|
+
|
79
|
+
# Validate authentication settings
|
80
|
+
self._validate_auth_settings(config, config_name)
|
81
|
+
|
82
|
+
return len(self.errors) == 0, self.errors, self.warnings
|
83
|
+
|
84
|
+
def _validate_protocol_settings(
|
85
|
+
self, config: Dict[str, Any], config_name: str
|
86
|
+
) -> None:
|
87
|
+
"""Validate protocol configuration settings."""
|
88
|
+
protocols = config.get("protocols", {})
|
89
|
+
|
90
|
+
if not protocols.get("enabled", False):
|
91
|
+
self.warnings.append(
|
92
|
+
f"⚠️ {config_name}: Protocol middleware is disabled - all protocols will be allowed"
|
93
|
+
)
|
94
|
+
return
|
95
|
+
|
96
|
+
allowed_protocols = protocols.get("allowed_protocols", [])
|
97
|
+
if not allowed_protocols:
|
98
|
+
self.errors.append(
|
99
|
+
f"❌ {config_name}: No allowed protocols specified when protocol middleware is enabled"
|
100
|
+
)
|
101
|
+
return
|
102
|
+
|
103
|
+
# Check for invalid protocol combinations
|
104
|
+
if "http" in allowed_protocols and "https" in allowed_protocols:
|
105
|
+
self.warnings.append(
|
106
|
+
f"⚠️ {config_name}: Both HTTP and HTTPS protocols are allowed - consider security implications"
|
107
|
+
)
|
108
|
+
|
109
|
+
if "mtls" in allowed_protocols and "http" in allowed_protocols:
|
110
|
+
self.errors.append(
|
111
|
+
f"❌ {config_name}: mTLS and HTTP protocols are mutually exclusive - mTLS requires HTTPS"
|
112
|
+
)
|
113
|
+
|
114
|
+
def _validate_ssl_settings(self, config: Dict[str, Any], config_name: str) -> None:
|
115
|
+
"""Validate SSL/TLS configuration settings."""
|
116
|
+
security = config.get("security", {})
|
117
|
+
ssl = security.get("ssl", {})
|
118
|
+
|
119
|
+
if not ssl.get("enabled", False):
|
120
|
+
return
|
121
|
+
|
122
|
+
# Check certificate file requirements
|
123
|
+
cert_file = ssl.get("server_cert_file")
|
124
|
+
key_file = ssl.get("server_key_file")
|
125
|
+
|
126
|
+
if not cert_file or not key_file:
|
127
|
+
self.errors.append(
|
128
|
+
f"❌ {config_name}: SSL enabled but server certificate or key file not specified"
|
129
|
+
)
|
130
|
+
|
131
|
+
# Check CA certificate requirements
|
132
|
+
ca_cert_file = ssl.get("ca_cert_file")
|
133
|
+
verify_server = ssl.get("verify_server", True)
|
134
|
+
|
135
|
+
if verify_server and not ca_cert_file:
|
136
|
+
self.warnings.append(
|
137
|
+
f"⚠️ {config_name}: Server verification enabled but no CA certificate specified"
|
138
|
+
)
|
139
|
+
|
140
|
+
def _validate_mtls_settings(self, config: Dict[str, Any], config_name: str) -> None:
|
141
|
+
"""Validate mTLS configuration settings."""
|
142
|
+
security = config.get("security", {})
|
143
|
+
ssl = security.get("ssl", {})
|
144
|
+
|
145
|
+
if not ssl.get("enabled", False):
|
146
|
+
return
|
147
|
+
|
148
|
+
# Check if mTLS is configured
|
149
|
+
client_cert_file = ssl.get("client_cert_file")
|
150
|
+
client_key_file = ssl.get("client_key_file")
|
151
|
+
verify_client = ssl.get("verify_client", False)
|
152
|
+
|
153
|
+
if verify_client and (not client_cert_file or not client_key_file):
|
154
|
+
self.errors.append(
|
155
|
+
f"❌ {config_name}: Client verification enabled but client certificate or key file not specified"
|
156
|
+
)
|
157
|
+
|
158
|
+
# Check protocol compatibility
|
159
|
+
protocols = config.get("protocols", {})
|
160
|
+
if protocols.get("enabled", False):
|
161
|
+
allowed_protocols = protocols.get("allowed_protocols", [])
|
162
|
+
if verify_client and "mtls" not in allowed_protocols:
|
163
|
+
self.warnings.append(
|
164
|
+
f"⚠️ {config_name}: Client verification enabled but 'mtls' not in allowed protocols"
|
165
|
+
)
|
166
|
+
|
167
|
+
def _validate_auth_settings(self, config: Dict[str, Any], config_name: str) -> None:
|
168
|
+
"""Validate authentication configuration settings."""
|
169
|
+
security = config.get("security", {})
|
170
|
+
auth = security.get("auth", {})
|
171
|
+
|
172
|
+
if not auth.get("enabled", False):
|
173
|
+
return
|
174
|
+
|
175
|
+
# Check token requirements
|
176
|
+
token_required = auth.get("token_required", False)
|
177
|
+
if token_required and not auth.get("token_secret"):
|
178
|
+
self.errors.append(
|
179
|
+
f"❌ {config_name}: Token authentication enabled but no token secret specified"
|
180
|
+
)
|
181
|
+
|
182
|
+
|
36
183
|
def _get_package_paths() -> tuple[Path, Path]:
|
37
184
|
"""
|
38
185
|
Resolve source paths for examples and utils relative to this file
|
@@ -42,6 +189,641 @@ def _get_package_paths() -> tuple[Path, Path]:
|
|
42
189
|
return pkg_root / "examples", pkg_root / "utils"
|
43
190
|
|
44
191
|
|
192
|
+
def create_configuration_documentation(output_dir: Path) -> None:
|
193
|
+
"""
|
194
|
+
Create comprehensive documentation for MCP Proxy Adapter configurations.
|
195
|
+
"""
|
196
|
+
docs_dir = output_dir / "docs"
|
197
|
+
docs_dir.mkdir(parents=True, exist_ok=True)
|
198
|
+
|
199
|
+
# Create main configuration guide
|
200
|
+
config_guide = docs_dir / "CONFIGURATION_GUIDE.md"
|
201
|
+
with open(config_guide, "w", encoding="utf-8") as f:
|
202
|
+
f.write(
|
203
|
+
"""# MCP Proxy Adapter Configuration Guide
|
204
|
+
|
205
|
+
## Overview
|
206
|
+
|
207
|
+
This guide explains how to configure MCP Proxy Adapter for different deployment scenarios,
|
208
|
+
including HTTP, HTTPS, and mTLS configurations.
|
209
|
+
|
210
|
+
## Configuration Structure
|
211
|
+
|
212
|
+
```json
|
213
|
+
{
|
214
|
+
"server": {
|
215
|
+
"host": "0.0.0.0",
|
216
|
+
"port": 8000
|
217
|
+
},
|
218
|
+
"protocols": {
|
219
|
+
"enabled": true,
|
220
|
+
"allowed_protocols": ["https", "mtls"]
|
221
|
+
},
|
222
|
+
"security": {
|
223
|
+
"ssl": {
|
224
|
+
"enabled": true,
|
225
|
+
"server_cert_file": "path/to/server.crt",
|
226
|
+
"server_key_file": "path/to/server.key",
|
227
|
+
"ca_cert_file": "path/to/ca.crt",
|
228
|
+
"client_cert_file": "path/to/client.crt",
|
229
|
+
"client_key_file": "path/to/client.key",
|
230
|
+
"verify_server": true,
|
231
|
+
"verify_client": false,
|
232
|
+
"min_tls_version": "TLSv1.2"
|
233
|
+
},
|
234
|
+
"auth": {
|
235
|
+
"enabled": false,
|
236
|
+
"token_required": false,
|
237
|
+
"token_secret": "your-secret-key"
|
238
|
+
}
|
239
|
+
}
|
240
|
+
}
|
241
|
+
```
|
242
|
+
|
243
|
+
## Protocol Configuration
|
244
|
+
|
245
|
+
### Protocol Middleware
|
246
|
+
|
247
|
+
The `protocols` section controls which protocols are allowed:
|
248
|
+
|
249
|
+
- `enabled: true` - Protocol validation is active
|
250
|
+
- `enabled: false` - All protocols are allowed (bypasses validation)
|
251
|
+
|
252
|
+
### Allowed Protocols
|
253
|
+
|
254
|
+
- `"http"` - Plain HTTP (insecure)
|
255
|
+
- `"https"` - HTTPS with server certificate
|
256
|
+
- `"mtls"` - Mutual TLS (client and server certificates)
|
257
|
+
|
258
|
+
### Protocol Combinations
|
259
|
+
|
260
|
+
**Valid combinations:**
|
261
|
+
- `["https"]` - HTTPS only
|
262
|
+
- `["https", "mtls"]` - HTTPS and mTLS
|
263
|
+
- `["http"]` - HTTP only (not recommended for production)
|
264
|
+
|
265
|
+
**Invalid combinations:**
|
266
|
+
- `["http", "mtls"]` - HTTP and mTLS are mutually exclusive
|
267
|
+
- `["http", "https"]` - Mixed HTTP/HTTPS (security risk)
|
268
|
+
|
269
|
+
## SSL/TLS Configuration
|
270
|
+
|
271
|
+
### Server Certificates
|
272
|
+
|
273
|
+
Required for HTTPS and mTLS:
|
274
|
+
- `server_cert_file` - Server certificate file
|
275
|
+
- `server_key_file` - Server private key file
|
276
|
+
|
277
|
+
### CA Certificates
|
278
|
+
|
279
|
+
- `ca_cert_file` - CA certificate for server verification
|
280
|
+
- `verify_server: true` - Verify server certificate against CA
|
281
|
+
- `verify_server: false` - Disable server certificate verification
|
282
|
+
|
283
|
+
### Client Certificates (mTLS)
|
284
|
+
|
285
|
+
- `client_cert_file` - Client certificate file
|
286
|
+
- `client_key_file` - Client private key file
|
287
|
+
- `verify_client: true` - Require client certificates
|
288
|
+
- `verify_client: false` - No client certificate required
|
289
|
+
|
290
|
+
## Authentication
|
291
|
+
|
292
|
+
### Token Authentication
|
293
|
+
|
294
|
+
- `auth.enabled: true` - Enable authentication
|
295
|
+
- `token_required: true` - Require authentication tokens
|
296
|
+
- `token_secret` - Secret key for token validation
|
297
|
+
|
298
|
+
## Common Configuration Patterns
|
299
|
+
|
300
|
+
### 1. Development (HTTP)
|
301
|
+
```json
|
302
|
+
{
|
303
|
+
"protocols": {"enabled": false},
|
304
|
+
"security": {"ssl": {"enabled": false}}
|
305
|
+
}
|
306
|
+
```
|
307
|
+
|
308
|
+
### 2. Production HTTPS
|
309
|
+
```json
|
310
|
+
{
|
311
|
+
"protocols": {
|
312
|
+
"enabled": true,
|
313
|
+
"allowed_protocols": ["https"]
|
314
|
+
},
|
315
|
+
"security": {
|
316
|
+
"ssl": {
|
317
|
+
"enabled": true,
|
318
|
+
"server_cert_file": "certs/server.crt",
|
319
|
+
"server_key_file": "keys/server.key",
|
320
|
+
"verify_server": true,
|
321
|
+
"ca_cert_file": "certs/ca.crt"
|
322
|
+
}
|
323
|
+
}
|
324
|
+
}
|
325
|
+
```
|
326
|
+
|
327
|
+
### 3. mTLS with Client Verification
|
328
|
+
```json
|
329
|
+
{
|
330
|
+
"protocols": {
|
331
|
+
"enabled": true,
|
332
|
+
"allowed_protocols": ["https", "mtls"]
|
333
|
+
},
|
334
|
+
"security": {
|
335
|
+
"ssl": {
|
336
|
+
"enabled": true,
|
337
|
+
"server_cert_file": "certs/server.crt",
|
338
|
+
"server_key_file": "keys/server.key",
|
339
|
+
"ca_cert_file": "certs/ca.crt",
|
340
|
+
"client_cert_file": "certs/client.crt",
|
341
|
+
"client_key_file": "keys/client.key",
|
342
|
+
"verify_server": true,
|
343
|
+
"verify_client": true
|
344
|
+
}
|
345
|
+
}
|
346
|
+
}
|
347
|
+
```
|
348
|
+
|
349
|
+
## Validation Rules
|
350
|
+
|
351
|
+
The configuration validator checks for:
|
352
|
+
|
353
|
+
1. **Mutually Exclusive Settings:**
|
354
|
+
- HTTP and mTLS protocols
|
355
|
+
- Client verification without client certificates
|
356
|
+
- Server verification without CA certificates
|
357
|
+
|
358
|
+
2. **Required Dependencies:**
|
359
|
+
- SSL enabled requires server certificates
|
360
|
+
- mTLS requires both server and client certificates
|
361
|
+
- Protocol middleware enabled requires allowed protocols
|
362
|
+
|
363
|
+
3. **Security Warnings:**
|
364
|
+
- HTTP and HTTPS in same configuration
|
365
|
+
- Server verification without CA certificate
|
366
|
+
- Client verification without mTLS protocol
|
367
|
+
|
368
|
+
## Troubleshooting
|
369
|
+
|
370
|
+
### Common Issues
|
371
|
+
|
372
|
+
1. **"Protocol not allowed" errors:**
|
373
|
+
- Check `protocols.allowed_protocols` includes required protocol
|
374
|
+
- Ensure `protocols.enabled: true` for validation
|
375
|
+
|
376
|
+
2. **SSL certificate errors:**
|
377
|
+
- Verify certificate file paths are correct
|
378
|
+
- Check certificate validity and format
|
379
|
+
- Ensure CA certificate matches server certificate
|
380
|
+
|
381
|
+
3. **mTLS connection failures:**
|
382
|
+
- Verify client certificates are valid
|
383
|
+
- Check `verify_client: true` is set
|
384
|
+
- Ensure "mtls" is in allowed protocols
|
385
|
+
|
386
|
+
### Debug Mode
|
387
|
+
|
388
|
+
Enable debug logging to troubleshoot issues:
|
389
|
+
|
390
|
+
```json
|
391
|
+
{
|
392
|
+
"logging": {
|
393
|
+
"level": "DEBUG",
|
394
|
+
"handlers": ["console", "file"]
|
395
|
+
}
|
396
|
+
}
|
397
|
+
```
|
398
|
+
"""
|
399
|
+
)
|
400
|
+
|
401
|
+
print(f"✅ Created configuration documentation: {config_guide}")
|
402
|
+
|
403
|
+
|
404
|
+
def create_test_files(output_dir: Path) -> None:
|
405
|
+
"""
|
406
|
+
Create additional test files for proxy registration testing.
|
407
|
+
"""
|
408
|
+
# Create test proxy server script
|
409
|
+
test_proxy_server = output_dir / "test_proxy_server.py"
|
410
|
+
with open(test_proxy_server, "w", encoding="utf-8") as f:
|
411
|
+
f.write(
|
412
|
+
'''#!/usr/bin/env python3
|
413
|
+
"""
|
414
|
+
Author: Vasiliy Zdanovskiy
|
415
|
+
email: vasilyvz@gmail.com
|
416
|
+
|
417
|
+
Simple mTLS proxy server for testing proxy registration SSL fix.
|
418
|
+
"""
|
419
|
+
import asyncio
|
420
|
+
import ssl
|
421
|
+
from fastapi import FastAPI, Request
|
422
|
+
from fastapi.responses import JSONResponse
|
423
|
+
import uvicorn
|
424
|
+
|
425
|
+
|
426
|
+
app = FastAPI(title="Test mTLS Proxy Server", version="1.0.0")
|
427
|
+
|
428
|
+
|
429
|
+
@app.post("/register")
|
430
|
+
async def register_server(request: Request):
|
431
|
+
"""Register server endpoint."""
|
432
|
+
try:
|
433
|
+
data = await request.json()
|
434
|
+
print(f"✅ Received registration request: {data}")
|
435
|
+
|
436
|
+
# Check if client certificate is present
|
437
|
+
client_cert = request.client
|
438
|
+
if client_cert:
|
439
|
+
print(f"✅ Client certificate verified: {client_cert}")
|
440
|
+
|
441
|
+
return JSONResponse(
|
442
|
+
status_code=200,
|
443
|
+
content={
|
444
|
+
"success": True,
|
445
|
+
"server_key": f"{data.get('server_id', 'unknown')}_1",
|
446
|
+
"message": "Server registered successfully"
|
447
|
+
}
|
448
|
+
)
|
449
|
+
except Exception as e:
|
450
|
+
print(f"❌ Registration error: {e}")
|
451
|
+
return JSONResponse(
|
452
|
+
status_code=500,
|
453
|
+
content={
|
454
|
+
"success": False,
|
455
|
+
"error": {
|
456
|
+
"message": str(e),
|
457
|
+
"code": "REGISTRATION_ERROR"
|
458
|
+
}
|
459
|
+
}
|
460
|
+
)
|
461
|
+
|
462
|
+
|
463
|
+
@app.get("/health")
|
464
|
+
async def health_check():
|
465
|
+
"""Health check endpoint."""
|
466
|
+
return JSONResponse(
|
467
|
+
status_code=200,
|
468
|
+
content={
|
469
|
+
"status": "healthy",
|
470
|
+
"message": "mTLS Proxy Server is running"
|
471
|
+
}
|
472
|
+
)
|
473
|
+
|
474
|
+
|
475
|
+
def main():
|
476
|
+
"""Run the mTLS proxy server."""
|
477
|
+
print("🚀 Starting Test mTLS Proxy Server...")
|
478
|
+
print("📡 Server URL: https://127.0.0.1:3004")
|
479
|
+
print("🔐 mTLS enabled with client certificate verification")
|
480
|
+
print("📋 Available endpoints:")
|
481
|
+
print(" POST /register - Register server")
|
482
|
+
print(" GET /health - Health check")
|
483
|
+
print("⚡ Press Ctrl+C to stop\\n")
|
484
|
+
|
485
|
+
# Run server with mTLS
|
486
|
+
uvicorn.run(
|
487
|
+
app,
|
488
|
+
host="127.0.0.1",
|
489
|
+
port=3004,
|
490
|
+
ssl_keyfile="mtls_certificates/server/mcp-proxy.key",
|
491
|
+
ssl_certfile="mtls_certificates/server/mcp-proxy.pem",
|
492
|
+
log_level="info"
|
493
|
+
)
|
494
|
+
|
495
|
+
|
496
|
+
if __name__ == "__main__":
|
497
|
+
main()
|
498
|
+
'''
|
499
|
+
)
|
500
|
+
|
501
|
+
# Make it executable
|
502
|
+
test_proxy_server.chmod(0o755)
|
503
|
+
print(f"✅ Created test proxy server: {test_proxy_server}")
|
504
|
+
|
505
|
+
# Create test script for proxy registration
|
506
|
+
test_script = output_dir / "test_proxy_registration.py"
|
507
|
+
with open(test_script, "w", encoding="utf-8") as f:
|
508
|
+
f.write(
|
509
|
+
'''#!/usr/bin/env python3
|
510
|
+
"""
|
511
|
+
Author: Vasiliy Zdanovskiy
|
512
|
+
email: vasilyvz@gmail.com
|
513
|
+
|
514
|
+
Test script to verify proxy registration SSL configuration fix.
|
515
|
+
"""
|
516
|
+
import sys
|
517
|
+
import subprocess
|
518
|
+
import time
|
519
|
+
import requests
|
520
|
+
from pathlib import Path
|
521
|
+
|
522
|
+
|
523
|
+
def test_proxy_registration():
|
524
|
+
"""Test proxy registration with SSL configuration."""
|
525
|
+
print("🧪 Testing Proxy Registration SSL Configuration Fix")
|
526
|
+
print("=" * 60)
|
527
|
+
|
528
|
+
# Start proxy server
|
529
|
+
print("🚀 Starting test proxy server...")
|
530
|
+
proxy_process = subprocess.Popen([
|
531
|
+
sys.executable, "test_proxy_server.py"
|
532
|
+
], cwd=Path(__file__).parent)
|
533
|
+
|
534
|
+
try:
|
535
|
+
# Wait for server to start
|
536
|
+
print("⏳ Waiting for proxy server to start...")
|
537
|
+
time.sleep(5)
|
538
|
+
|
539
|
+
# Test proxy server health
|
540
|
+
print("🔍 Testing proxy server health...")
|
541
|
+
try:
|
542
|
+
response = requests.get(
|
543
|
+
"https://127.0.0.1:3004/health",
|
544
|
+
verify=False,
|
545
|
+
timeout=10
|
546
|
+
)
|
547
|
+
if response.status_code == 200:
|
548
|
+
print("✅ Proxy server is running")
|
549
|
+
else:
|
550
|
+
print(f"❌ Proxy server health check failed: {response.status_code}")
|
551
|
+
return False
|
552
|
+
except Exception as e:
|
553
|
+
print(f"❌ Failed to connect to proxy server: {e}")
|
554
|
+
return False
|
555
|
+
|
556
|
+
# Test mTLS server with registration
|
557
|
+
print("🚀 Starting mTLS server with proxy registration...")
|
558
|
+
server_process = subprocess.Popen([
|
559
|
+
sys.executable, "-m", "mcp_proxy_adapter",
|
560
|
+
"--config", "configs/test_proxy_registration.json"
|
561
|
+
], cwd=Path(__file__).parent)
|
562
|
+
|
563
|
+
try:
|
564
|
+
# Wait for server to start and attempt registration
|
565
|
+
print("⏳ Waiting for server to start and register...")
|
566
|
+
time.sleep(10)
|
567
|
+
|
568
|
+
# Check if server is running
|
569
|
+
if server_process.poll() is None:
|
570
|
+
print("✅ mTLS server started successfully")
|
571
|
+
print("✅ Proxy registration SSL configuration fix is working!")
|
572
|
+
return True
|
573
|
+
else:
|
574
|
+
print("❌ mTLS server failed to start")
|
575
|
+
return False
|
576
|
+
|
577
|
+
finally:
|
578
|
+
# Clean up server process
|
579
|
+
if server_process.poll() is None:
|
580
|
+
server_process.terminate()
|
581
|
+
server_process.wait()
|
582
|
+
|
583
|
+
finally:
|
584
|
+
# Clean up proxy process
|
585
|
+
if proxy_process.poll() is None:
|
586
|
+
proxy_process.terminate()
|
587
|
+
proxy_process.wait()
|
588
|
+
|
589
|
+
|
590
|
+
def main():
|
591
|
+
"""Run the test."""
|
592
|
+
success = test_proxy_registration()
|
593
|
+
|
594
|
+
if success:
|
595
|
+
print("\\n🎉 All tests passed! Proxy registration SSL fix is working correctly.")
|
596
|
+
return 0
|
597
|
+
else:
|
598
|
+
print("\\n❌ Tests failed. Check the logs above for details.")
|
599
|
+
return 1
|
600
|
+
|
601
|
+
|
602
|
+
if __name__ == "__main__":
|
603
|
+
sys.exit(main())
|
604
|
+
'''
|
605
|
+
)
|
606
|
+
|
607
|
+
# Make it executable
|
608
|
+
test_script.chmod(0o755)
|
609
|
+
print(f"✅ Created test script: {test_script}")
|
610
|
+
|
611
|
+
|
612
|
+
def generate_enhanced_configurations(output_dir: Path) -> None:
|
613
|
+
"""
|
614
|
+
Generate enhanced configurations with proper protocol settings and validation.
|
615
|
+
"""
|
616
|
+
configs_dir = output_dir / "configs"
|
617
|
+
configs_dir.mkdir(parents=True, exist_ok=True)
|
618
|
+
|
619
|
+
validator = ConfigurationValidator()
|
620
|
+
|
621
|
+
# Configuration templates with proper protocol settings
|
622
|
+
configs = {
|
623
|
+
"development_http.json": {
|
624
|
+
"server": {"host": "0.0.0.0", "port": 8000},
|
625
|
+
"protocols": {"enabled": False},
|
626
|
+
"security": {"ssl": {"enabled": False}},
|
627
|
+
"logging": {"level": "DEBUG"},
|
628
|
+
},
|
629
|
+
"production_https.json": {
|
630
|
+
"server": {"host": "0.0.0.0", "port": 8443},
|
631
|
+
"protocols": {"enabled": True, "allowed_protocols": ["https"]},
|
632
|
+
"security": {
|
633
|
+
"ssl": {
|
634
|
+
"enabled": True,
|
635
|
+
"server_cert_file": "certs/server_cert.pem",
|
636
|
+
"server_key_file": "keys/server_key.pem",
|
637
|
+
"ca_cert_file": "certs/ca_cert.pem",
|
638
|
+
"verify_server": True,
|
639
|
+
"verify_client": False,
|
640
|
+
"min_tls_version": "TLSv1.2",
|
641
|
+
}
|
642
|
+
},
|
643
|
+
"logging": {"level": "INFO"},
|
644
|
+
},
|
645
|
+
"mtls_with_roles.json": {
|
646
|
+
"server": {"host": "0.0.0.0", "port": 8443},
|
647
|
+
"protocols": {"enabled": True, "allowed_protocols": ["https", "mtls"]},
|
648
|
+
"security": {
|
649
|
+
"ssl": {
|
650
|
+
"enabled": True,
|
651
|
+
"server_cert_file": "certs/server_cert.pem",
|
652
|
+
"server_key_file": "keys/server_key.pem",
|
653
|
+
"ca_cert_file": "certs/ca_cert.pem",
|
654
|
+
"client_cert_file": "certs/client_cert.pem",
|
655
|
+
"client_key_file": "keys/client_key.pem",
|
656
|
+
"verify_server": True,
|
657
|
+
"verify_client": True,
|
658
|
+
"min_tls_version": "TLSv1.2",
|
659
|
+
},
|
660
|
+
"auth": {
|
661
|
+
"enabled": True,
|
662
|
+
"token_required": False,
|
663
|
+
"token_secret": "your-secret-key",
|
664
|
+
},
|
665
|
+
},
|
666
|
+
"logging": {"level": "INFO"},
|
667
|
+
},
|
668
|
+
"mtls_no_roles.json": {
|
669
|
+
"server": {"host": "0.0.0.0", "port": 8443},
|
670
|
+
"protocols": {"enabled": True, "allowed_protocols": ["https", "mtls"]},
|
671
|
+
"security": {
|
672
|
+
"ssl": {
|
673
|
+
"enabled": True,
|
674
|
+
"server_cert_file": "certs/server_cert.pem",
|
675
|
+
"server_key_file": "keys/server_key.pem",
|
676
|
+
"ca_cert_file": "certs/ca_cert.pem",
|
677
|
+
"client_cert_file": "certs/client_cert.pem",
|
678
|
+
"client_key_file": "keys/client_key.pem",
|
679
|
+
"verify_server": True,
|
680
|
+
"verify_client": True,
|
681
|
+
"min_tls_version": "TLSv1.2",
|
682
|
+
}
|
683
|
+
},
|
684
|
+
"logging": {"level": "INFO"},
|
685
|
+
},
|
686
|
+
"https_simple.json": {
|
687
|
+
"server": {"host": "0.0.0.0", "port": 8443},
|
688
|
+
"protocols": {"enabled": True, "allowed_protocols": ["https"]},
|
689
|
+
"security": {
|
690
|
+
"ssl": {
|
691
|
+
"enabled": True,
|
692
|
+
"server_cert_file": "certs/server_cert.pem",
|
693
|
+
"server_key_file": "keys/server_key.pem",
|
694
|
+
"verify_server": False,
|
695
|
+
"verify_client": False,
|
696
|
+
"min_tls_version": "TLSv1.2",
|
697
|
+
}
|
698
|
+
},
|
699
|
+
"logging": {"level": "INFO"},
|
700
|
+
},
|
701
|
+
"https_with_auth.json": {
|
702
|
+
"server": {"host": "0.0.0.0", "port": 8443},
|
703
|
+
"protocols": {"enabled": True, "allowed_protocols": ["https"]},
|
704
|
+
"security": {
|
705
|
+
"ssl": {
|
706
|
+
"enabled": True,
|
707
|
+
"server_cert_file": "certs/server_cert.pem",
|
708
|
+
"server_key_file": "keys/server_key.pem",
|
709
|
+
"verify_server": True,
|
710
|
+
"ca_cert_file": "certs/ca_cert.pem",
|
711
|
+
"verify_client": False,
|
712
|
+
"min_tls_version": "TLSv1.2",
|
713
|
+
},
|
714
|
+
"auth": {
|
715
|
+
"enabled": True,
|
716
|
+
"token_required": True,
|
717
|
+
"token_secret": "your-secret-key",
|
718
|
+
},
|
719
|
+
},
|
720
|
+
"logging": {"level": "INFO"},
|
721
|
+
},
|
722
|
+
"test_proxy_registration.json": {
|
723
|
+
"server": {"host": "127.0.0.1", "port": 8010},
|
724
|
+
"protocols": {"enabled": True, "allowed_protocols": ["https", "mtls"]},
|
725
|
+
"security": {
|
726
|
+
"ssl": {
|
727
|
+
"enabled": True,
|
728
|
+
"server_cert_file": "certs/mtls/server/embedding-service.pem",
|
729
|
+
"server_key_file": "certs/mtls/server/embedding-service.key",
|
730
|
+
"ca_cert_file": "certs/mtls/truststore.pem",
|
731
|
+
"client_cert_file": "certs/mtls/client/embedding-service.pem",
|
732
|
+
"client_key_file": "certs/mtls/client/embedding-service.key",
|
733
|
+
"verify_server": True,
|
734
|
+
"verify_client": True,
|
735
|
+
"min_tls_version": "TLSv1.2",
|
736
|
+
}
|
737
|
+
},
|
738
|
+
"registration": {
|
739
|
+
"enabled": True,
|
740
|
+
"proxy_url": "https://127.0.0.1:3004",
|
741
|
+
"server_id": "test-proxy-registration",
|
742
|
+
"server_name": "Test Proxy Registration Service",
|
743
|
+
"description": "Test service for proxy registration SSL fix",
|
744
|
+
"auth_method": "certificate",
|
745
|
+
"certificate": {
|
746
|
+
"cert_file": "certs/mtls/client/embedding-service.pem",
|
747
|
+
"key_file": "certs/mtls/client/embedding-service.key",
|
748
|
+
},
|
749
|
+
"ssl": {
|
750
|
+
"verify_mode": "CERT_REQUIRED",
|
751
|
+
"ca_cert": "mtls_certificates/truststore.pem",
|
752
|
+
},
|
753
|
+
},
|
754
|
+
"logging": {"level": "DEBUG"},
|
755
|
+
},
|
756
|
+
}
|
757
|
+
|
758
|
+
# Generate and validate configurations
|
759
|
+
for config_name, config_data in configs.items():
|
760
|
+
config_path = configs_dir / config_name
|
761
|
+
|
762
|
+
# Validate configuration
|
763
|
+
is_valid, errors, warnings = validator.validate_config(config_data, config_name)
|
764
|
+
|
765
|
+
if not is_valid:
|
766
|
+
print(f"❌ Configuration {config_name} has errors:")
|
767
|
+
for error in errors:
|
768
|
+
print(f" {error}")
|
769
|
+
continue
|
770
|
+
|
771
|
+
if warnings:
|
772
|
+
print(f"⚠️ Configuration {config_name} has warnings:")
|
773
|
+
for warning in warnings:
|
774
|
+
print(f" {warning}")
|
775
|
+
|
776
|
+
# Write configuration file
|
777
|
+
with open(config_path, "w", encoding="utf-8") as f:
|
778
|
+
json.dump(config_data, f, indent=2)
|
779
|
+
|
780
|
+
print(f"✅ Generated configuration: {config_path}")
|
781
|
+
|
782
|
+
# Create configuration index
|
783
|
+
index_path = configs_dir / "README.md"
|
784
|
+
with open(index_path, "w", encoding="utf-8") as f:
|
785
|
+
f.write(
|
786
|
+
"""# Configuration Files
|
787
|
+
|
788
|
+
This directory contains pre-configured examples for different deployment scenarios.
|
789
|
+
|
790
|
+
## Available Configurations
|
791
|
+
|
792
|
+
### Development
|
793
|
+
- `development_http.json` - HTTP development server (no SSL)
|
794
|
+
|
795
|
+
### Production HTTPS
|
796
|
+
- `production_https.json` - HTTPS with server certificate verification
|
797
|
+
- `https_simple.json` - HTTPS without certificate verification
|
798
|
+
- `https_with_auth.json` - HTTPS with token authentication
|
799
|
+
|
800
|
+
### mTLS (Mutual TLS)
|
801
|
+
- `mtls_with_roles.json` - mTLS with role-based authentication
|
802
|
+
- `mtls_no_roles.json` - mTLS without roles
|
803
|
+
|
804
|
+
## Usage
|
805
|
+
|
806
|
+
Start the server with a specific configuration:
|
807
|
+
|
808
|
+
```bash
|
809
|
+
python -m mcp_proxy_adapter --config configs/production_https.json
|
810
|
+
```
|
811
|
+
|
812
|
+
## Validation
|
813
|
+
|
814
|
+
All configurations are validated for:
|
815
|
+
- Mutually exclusive settings
|
816
|
+
- Required dependencies
|
817
|
+
- Protocol compatibility
|
818
|
+
- Security best practices
|
819
|
+
|
820
|
+
See `docs/CONFIGURATION_GUIDE.md` for detailed documentation.
|
821
|
+
"""
|
822
|
+
)
|
823
|
+
|
824
|
+
print(f"✅ Created configuration index: {index_path}")
|
825
|
+
|
826
|
+
|
45
827
|
def setup_test_environment(output_dir: Path) -> None:
|
46
828
|
"""
|
47
829
|
Setup test environment under output_dir with required files
|
@@ -50,9 +832,10 @@ def setup_test_environment(output_dir: Path) -> None:
|
|
50
832
|
All created directories and copied files are rooted at output_dir
|
51
833
|
so users can run scripts relative to that directory.
|
52
834
|
"""
|
53
|
-
print("🔧 Setting up test environment...")
|
835
|
+
print("🔧 Setting up enhanced test environment...")
|
54
836
|
output_dir = output_dir.resolve()
|
55
837
|
output_dir.mkdir(parents=True, exist_ok=True)
|
838
|
+
|
56
839
|
# Create test environment directory structure
|
57
840
|
directories = [
|
58
841
|
"examples/basic_framework",
|
@@ -63,13 +846,16 @@ def setup_test_environment(output_dir: Path) -> None:
|
|
63
846
|
"keys",
|
64
847
|
"tokens",
|
65
848
|
"logs",
|
849
|
+
"docs",
|
66
850
|
]
|
67
851
|
for directory in directories:
|
68
852
|
target_dir = output_dir / directory
|
69
853
|
target_dir.mkdir(parents=True, exist_ok=True)
|
70
854
|
print(f"✅ Created directory: {target_dir}")
|
855
|
+
|
71
856
|
# Resolve package paths
|
72
857
|
examples_src_root, utils_src_root = _get_package_paths()
|
858
|
+
|
73
859
|
# Copy example files
|
74
860
|
basic_framework_src = examples_src_root / "basic_framework"
|
75
861
|
if basic_framework_src.exists():
|
@@ -79,6 +865,7 @@ def setup_test_environment(output_dir: Path) -> None:
|
|
79
865
|
dirs_exist_ok=True,
|
80
866
|
)
|
81
867
|
print("✅ Copied basic_framework examples")
|
868
|
+
|
82
869
|
full_application_src = examples_src_root / "full_application"
|
83
870
|
if full_application_src.exists():
|
84
871
|
shutil.copytree(
|
@@ -87,16 +874,19 @@ def setup_test_environment(output_dir: Path) -> None:
|
|
87
874
|
dirs_exist_ok=True,
|
88
875
|
)
|
89
876
|
print("✅ Copied full_application examples")
|
877
|
+
|
90
878
|
# Copy utility scripts
|
91
879
|
config_generator_src = utils_src_root / "config_generator.py"
|
92
880
|
if config_generator_src.exists():
|
93
881
|
shutil.copy2(config_generator_src, output_dir / "scripts/")
|
94
882
|
print("✅ Copied config_generator.py")
|
883
|
+
|
95
884
|
# Copy certificate generation scripts
|
96
885
|
create_certs_src = examples_src_root / "create_certificates_simple.py"
|
97
886
|
if create_certs_src.exists():
|
98
887
|
shutil.copy2(create_certs_src, output_dir / "scripts/")
|
99
888
|
print("✅ Copied create_certificates_simple.py")
|
889
|
+
|
100
890
|
cert_tokens_src = examples_src_root / "generate_certificates_and_tokens.py"
|
101
891
|
if cert_tokens_src.exists():
|
102
892
|
shutil.copy2(cert_tokens_src, output_dir / "scripts/")
|
@@ -113,7 +903,21 @@ def setup_test_environment(output_dir: Path) -> None:
|
|
113
903
|
if roles_configs_src.exists():
|
114
904
|
shutil.copy2(roles_configs_src, output_dir / "roles.json")
|
115
905
|
print("✅ Updated roles.json from configs directory")
|
116
|
-
|
906
|
+
|
907
|
+
# Create configuration documentation
|
908
|
+
create_configuration_documentation(output_dir)
|
909
|
+
|
910
|
+
# Generate enhanced configurations
|
911
|
+
generate_enhanced_configurations(output_dir)
|
912
|
+
|
913
|
+
# Create test files
|
914
|
+
create_test_files(output_dir)
|
915
|
+
|
916
|
+
print(
|
917
|
+
"🎉 Enhanced test environment setup completed successfully at: {}".format(
|
918
|
+
output_dir
|
919
|
+
)
|
920
|
+
)
|
117
921
|
|
118
922
|
|
119
923
|
def generate_certificates_with_framework(output_dir: Path) -> bool:
|
@@ -257,7 +1061,7 @@ def generate_certificates_with_framework(output_dir: Path) -> bool:
|
|
257
1061
|
def main() -> int:
|
258
1062
|
"""Main function for command line execution."""
|
259
1063
|
parser = argparse.ArgumentParser(
|
260
|
-
description="Setup test environment for MCP Proxy Adapter"
|
1064
|
+
description="Setup enhanced test environment for MCP Proxy Adapter"
|
261
1065
|
)
|
262
1066
|
parser.add_argument(
|
263
1067
|
"--output-dir",
|
@@ -269,13 +1073,19 @@ def main() -> int:
|
|
269
1073
|
"(default: current directory)"
|
270
1074
|
),
|
271
1075
|
)
|
1076
|
+
parser.add_argument(
|
1077
|
+
"--skip-certs", action="store_true", help="Skip certificate generation"
|
1078
|
+
)
|
272
1079
|
args = parser.parse_args()
|
273
1080
|
try:
|
274
1081
|
target_root = Path(args.output_dir)
|
275
1082
|
setup_test_environment(target_root)
|
276
|
-
|
277
|
-
if
|
1083
|
+
|
1084
|
+
# Generate certificates if framework is available and not skipped
|
1085
|
+
if not args.skip_certs and SECURITY_FRAMEWORK_AVAILABLE:
|
278
1086
|
generate_certificates_with_framework(target_root)
|
1087
|
+
elif args.skip_certs:
|
1088
|
+
print("⚠️ Skipping certificate generation (--skip-certs specified)")
|
279
1089
|
else:
|
280
1090
|
print(
|
281
1091
|
"⚠️ Skipping certificate generation (mcp_security_framework "
|
@@ -293,21 +1103,21 @@ def main() -> int:
|
|
293
1103
|
return 1
|
294
1104
|
|
295
1105
|
print("\n" + "=" * 60)
|
296
|
-
print("✅ TEST ENVIRONMENT SETUP COMPLETED SUCCESSFULLY")
|
1106
|
+
print("✅ ENHANCED TEST ENVIRONMENT SETUP COMPLETED SUCCESSFULLY")
|
297
1107
|
print("=" * 60)
|
298
1108
|
print("\n📋 NEXT STEPS:")
|
299
|
-
print("1.
|
300
|
-
print(
|
301
|
-
|
302
|
-
)
|
303
|
-
print("\
|
304
|
-
print(" python
|
305
|
-
print("\
|
1109
|
+
print("1. Review configuration documentation:")
|
1110
|
+
print(" cat docs/CONFIGURATION_GUIDE.md")
|
1111
|
+
print("\n2. Check available configurations:")
|
1112
|
+
print(" ls -la configs/")
|
1113
|
+
print("\n3. Test proxy registration SSL fix:")
|
1114
|
+
print(" python test_proxy_registration.py")
|
1115
|
+
print("\n4. Start server with a specific configuration:")
|
1116
|
+
print(" python -m mcp_proxy_adapter --config configs/production_https.json")
|
1117
|
+
print("\n5. Run security tests:")
|
306
1118
|
print(" python -m mcp_proxy_adapter.examples.run_security_tests")
|
307
|
-
print("\
|
308
|
-
print(
|
309
|
-
" python -m mcp_proxy_adapter.examples.basic_framework.main --config configs/https_simple.json"
|
310
|
-
)
|
1119
|
+
print("\n6. Generate additional certificates (if needed):")
|
1120
|
+
print(" python scripts/create_certificates_simple.py")
|
311
1121
|
print("=" * 60)
|
312
1122
|
return 0
|
313
1123
|
|