mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. mcp_proxy_adapter/__init__.py +9 -5
  2. mcp_proxy_adapter/__main__.py +1 -1
  3. mcp_proxy_adapter/api/app.py +227 -176
  4. mcp_proxy_adapter/api/handlers.py +68 -60
  5. mcp_proxy_adapter/api/middleware/__init__.py +7 -5
  6. mcp_proxy_adapter/api/middleware/base.py +19 -16
  7. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +44 -34
  8. mcp_proxy_adapter/api/middleware/error_handling.py +57 -67
  9. mcp_proxy_adapter/api/middleware/factory.py +50 -52
  10. mcp_proxy_adapter/api/middleware/logging.py +46 -30
  11. mcp_proxy_adapter/api/middleware/performance.py +19 -16
  12. mcp_proxy_adapter/api/middleware/protocol_middleware.py +80 -50
  13. mcp_proxy_adapter/api/middleware/transport_middleware.py +26 -24
  14. mcp_proxy_adapter/api/middleware/unified_security.py +70 -51
  15. mcp_proxy_adapter/api/middleware/user_info_middleware.py +43 -34
  16. mcp_proxy_adapter/api/schemas.py +69 -43
  17. mcp_proxy_adapter/api/tool_integration.py +83 -63
  18. mcp_proxy_adapter/api/tools.py +60 -50
  19. mcp_proxy_adapter/commands/__init__.py +15 -6
  20. mcp_proxy_adapter/commands/auth_validation_command.py +107 -110
  21. mcp_proxy_adapter/commands/base.py +108 -112
  22. mcp_proxy_adapter/commands/builtin_commands.py +28 -18
  23. mcp_proxy_adapter/commands/catalog_manager.py +394 -265
  24. mcp_proxy_adapter/commands/cert_monitor_command.py +222 -204
  25. mcp_proxy_adapter/commands/certificate_management_command.py +210 -213
  26. mcp_proxy_adapter/commands/command_registry.py +275 -226
  27. mcp_proxy_adapter/commands/config_command.py +48 -33
  28. mcp_proxy_adapter/commands/dependency_container.py +22 -23
  29. mcp_proxy_adapter/commands/dependency_manager.py +65 -56
  30. mcp_proxy_adapter/commands/echo_command.py +15 -15
  31. mcp_proxy_adapter/commands/health_command.py +31 -29
  32. mcp_proxy_adapter/commands/help_command.py +97 -61
  33. mcp_proxy_adapter/commands/hooks.py +65 -49
  34. mcp_proxy_adapter/commands/key_management_command.py +148 -147
  35. mcp_proxy_adapter/commands/load_command.py +58 -40
  36. mcp_proxy_adapter/commands/plugins_command.py +80 -54
  37. mcp_proxy_adapter/commands/protocol_management_command.py +60 -48
  38. mcp_proxy_adapter/commands/proxy_registration_command.py +107 -115
  39. mcp_proxy_adapter/commands/reload_command.py +43 -37
  40. mcp_proxy_adapter/commands/result.py +26 -33
  41. mcp_proxy_adapter/commands/role_test_command.py +26 -26
  42. mcp_proxy_adapter/commands/roles_management_command.py +176 -173
  43. mcp_proxy_adapter/commands/security_command.py +134 -122
  44. mcp_proxy_adapter/commands/settings_command.py +47 -56
  45. mcp_proxy_adapter/commands/ssl_setup_command.py +109 -129
  46. mcp_proxy_adapter/commands/token_management_command.py +129 -158
  47. mcp_proxy_adapter/commands/transport_management_command.py +41 -36
  48. mcp_proxy_adapter/commands/unload_command.py +42 -37
  49. mcp_proxy_adapter/config.py +36 -35
  50. mcp_proxy_adapter/core/__init__.py +19 -21
  51. mcp_proxy_adapter/core/app_factory.py +30 -9
  52. mcp_proxy_adapter/core/app_runner.py +81 -64
  53. mcp_proxy_adapter/core/auth_validator.py +176 -182
  54. mcp_proxy_adapter/core/certificate_utils.py +469 -426
  55. mcp_proxy_adapter/core/client.py +155 -126
  56. mcp_proxy_adapter/core/client_manager.py +60 -54
  57. mcp_proxy_adapter/core/client_security.py +108 -88
  58. mcp_proxy_adapter/core/config_converter.py +176 -143
  59. mcp_proxy_adapter/core/config_validator.py +12 -4
  60. mcp_proxy_adapter/core/crl_utils.py +21 -7
  61. mcp_proxy_adapter/core/errors.py +64 -20
  62. mcp_proxy_adapter/core/logging.py +34 -29
  63. mcp_proxy_adapter/core/mtls_asgi.py +29 -25
  64. mcp_proxy_adapter/core/mtls_asgi_app.py +66 -54
  65. mcp_proxy_adapter/core/protocol_manager.py +154 -104
  66. mcp_proxy_adapter/core/proxy_client.py +202 -144
  67. mcp_proxy_adapter/core/proxy_registration.py +7 -3
  68. mcp_proxy_adapter/core/role_utils.py +139 -125
  69. mcp_proxy_adapter/core/security_adapter.py +88 -77
  70. mcp_proxy_adapter/core/security_factory.py +50 -44
  71. mcp_proxy_adapter/core/security_integration.py +72 -24
  72. mcp_proxy_adapter/core/server_adapter.py +68 -64
  73. mcp_proxy_adapter/core/server_engine.py +71 -53
  74. mcp_proxy_adapter/core/settings.py +68 -58
  75. mcp_proxy_adapter/core/ssl_utils.py +69 -56
  76. mcp_proxy_adapter/core/transport_manager.py +72 -60
  77. mcp_proxy_adapter/core/unified_config_adapter.py +201 -150
  78. mcp_proxy_adapter/core/utils.py +4 -2
  79. mcp_proxy_adapter/custom_openapi.py +107 -99
  80. mcp_proxy_adapter/examples/basic_framework/main.py +9 -2
  81. mcp_proxy_adapter/examples/commands/__init__.py +1 -1
  82. mcp_proxy_adapter/examples/create_certificates_simple.py +182 -71
  83. mcp_proxy_adapter/examples/debug_request_state.py +38 -19
  84. mcp_proxy_adapter/examples/debug_role_chain.py +53 -20
  85. mcp_proxy_adapter/examples/demo_client.py +48 -36
  86. mcp_proxy_adapter/examples/examples/basic_framework/main.py +9 -2
  87. mcp_proxy_adapter/examples/examples/full_application/__init__.py +1 -0
  88. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +22 -10
  89. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +24 -17
  90. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +16 -3
  91. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +13 -3
  92. mcp_proxy_adapter/examples/examples/full_application/main.py +27 -2
  93. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +48 -14
  94. mcp_proxy_adapter/examples/full_application/__init__.py +1 -0
  95. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +22 -10
  96. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +24 -17
  97. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +16 -3
  98. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +13 -3
  99. mcp_proxy_adapter/examples/full_application/main.py +27 -2
  100. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +48 -14
  101. mcp_proxy_adapter/examples/generate_all_certificates.py +198 -73
  102. mcp_proxy_adapter/examples/generate_certificates.py +31 -16
  103. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +220 -74
  104. mcp_proxy_adapter/examples/generate_test_configs.py +68 -91
  105. mcp_proxy_adapter/examples/proxy_registration_example.py +76 -75
  106. mcp_proxy_adapter/examples/run_example.py +23 -5
  107. mcp_proxy_adapter/examples/run_full_test_suite.py +109 -71
  108. mcp_proxy_adapter/examples/run_proxy_server.py +22 -9
  109. mcp_proxy_adapter/examples/run_security_tests.py +103 -41
  110. mcp_proxy_adapter/examples/run_security_tests_fixed.py +72 -36
  111. mcp_proxy_adapter/examples/scripts/config_generator.py +288 -187
  112. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +185 -72
  113. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +220 -74
  114. mcp_proxy_adapter/examples/security_test_client.py +196 -127
  115. mcp_proxy_adapter/examples/setup_test_environment.py +17 -29
  116. mcp_proxy_adapter/examples/test_config.py +19 -4
  117. mcp_proxy_adapter/examples/test_config_generator.py +23 -7
  118. mcp_proxy_adapter/examples/test_examples.py +84 -56
  119. mcp_proxy_adapter/examples/universal_client.py +119 -62
  120. mcp_proxy_adapter/openapi.py +108 -115
  121. mcp_proxy_adapter/utils/config_generator.py +429 -274
  122. mcp_proxy_adapter/version.py +1 -2
  123. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/METADATA +1 -1
  124. mcp_proxy_adapter-6.3.5.dist-info/RECORD +143 -0
  125. mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
  126. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/WHEEL +0 -0
  127. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/entry_points.txt +0 -0
  128. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/licenses/LICENSE +0 -0
  129. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/top_level.txt +0 -0
@@ -24,10 +24,26 @@ from requests.exceptions import RequestException
24
24
 
25
25
  # Import security framework components
26
26
  try:
27
- from mcp_security_framework import SecurityManager, AuthManager, CertificateManager, PermissionManager
28
- from mcp_security_framework.utils import generate_api_key, create_jwt_token, validate_jwt_token
29
- from mcp_security_framework.utils import extract_roles_from_cert, validate_certificate_chain
30
- from mcp_security_framework.utils import create_ssl_context, validate_server_certificate
27
+ from mcp_security_framework import (
28
+ SecurityManager,
29
+ AuthManager,
30
+ CertificateManager,
31
+ PermissionManager,
32
+ )
33
+ from mcp_security_framework.utils import (
34
+ generate_api_key,
35
+ create_jwt_token,
36
+ validate_jwt_token,
37
+ )
38
+ from mcp_security_framework.utils import (
39
+ extract_roles_from_cert,
40
+ validate_certificate_chain,
41
+ )
42
+ from mcp_security_framework.utils import (
43
+ create_ssl_context,
44
+ validate_server_certificate,
45
+ )
46
+
31
47
  SECURITY_FRAMEWORK_AVAILABLE = True
32
48
  except ImportError:
33
49
  SECURITY_FRAMEWORK_AVAILABLE = False
@@ -36,7 +52,7 @@ except ImportError:
36
52
  class UniversalClient:
37
53
  """
38
54
  Universal client that demonstrates all possible secure connection methods.
39
-
55
+
40
56
  Supports:
41
57
  - HTTP/HTTPS connections
42
58
  - API Key authentication
@@ -46,11 +62,11 @@ class UniversalClient:
46
62
  - Role-based access control
47
63
  - Rate limiting awareness
48
64
  """
49
-
65
+
50
66
  def __init__(self, config: Dict[str, Any]):
51
67
  """
52
68
  Initialize universal client with configuration.
53
-
69
+
54
70
  Args:
55
71
  config: Client configuration with security settings
56
72
  """
@@ -59,72 +75,74 @@ class UniversalClient:
59
75
  self.timeout = config.get("timeout", 30)
60
76
  self.retry_attempts = config.get("retry_attempts", 3)
61
77
  self.retry_delay = config.get("retry_delay", 1)
62
-
78
+
63
79
  # Security configuration
64
80
  self.security_config = config.get("security", {})
65
81
  self.auth_method = self.security_config.get("auth_method", "none")
66
-
82
+
67
83
  # Initialize security managers if framework is available
68
84
  self.security_manager = None
69
85
  self.auth_manager = None
70
86
  self.cert_manager = None
71
-
87
+
72
88
  if SECURITY_FRAMEWORK_AVAILABLE:
73
89
  self._initialize_security_managers()
74
-
90
+
75
91
  # Session management
76
92
  self.session: Optional[aiohttp.ClientSession] = None
77
93
  self.current_token: Optional[str] = None
78
94
  self.token_expiry: Optional[float] = None
79
-
95
+
80
96
  print(f"Universal client initialized with auth method: {self.auth_method}")
81
-
97
+
82
98
  def _initialize_security_managers(self) -> None:
83
99
  """Initialize security framework managers."""
84
100
  try:
85
101
  # Initialize security manager
86
102
  self.security_manager = SecurityManager(self.security_config)
87
-
103
+
88
104
  # Initialize permission manager first
89
105
  permissions_config = self.security_config.get("permissions", {})
90
106
  self.permission_manager = PermissionManager(permissions_config)
91
-
107
+
92
108
  # Initialize auth manager with permission_manager
93
109
  auth_config = self.security_config.get("auth", {})
94
110
  self.auth_manager = AuthManager(auth_config, self.permission_manager)
95
-
111
+
96
112
  # Initialize certificate manager
97
113
  cert_config = self.security_config.get("certificates", {})
98
114
  self.cert_manager = CertificateManager(cert_config)
99
-
115
+
100
116
  print("Security framework managers initialized successfully")
101
117
  except Exception as e:
102
118
  print(f"Warning: Failed to initialize security managers: {e}")
103
-
119
+
104
120
  async def __aenter__(self):
105
121
  """Async context manager entry."""
106
122
  await self.connect()
107
123
  return self
108
-
124
+
109
125
  async def __aexit__(self, exc_type, exc_val, exc_tb):
110
126
  """Async context manager exit."""
111
127
  await self.disconnect()
112
-
128
+
113
129
  async def connect(self) -> None:
114
130
  """Establish connection with authentication."""
115
- print(f"Connecting to {self.base_url} with {self.auth_method} authentication...")
116
-
131
+ print(
132
+ f"Connecting to {self.base_url} with {self.auth_method} authentication..."
133
+ )
134
+
117
135
  # Create SSL context
118
136
  ssl_context = self._create_ssl_context()
119
-
137
+
120
138
  # Create connector with SSL context
121
139
  connector = None
122
140
  if ssl_context:
123
141
  connector = aiohttp.TCPConnector(ssl=ssl_context)
124
-
142
+
125
143
  # Create session
126
144
  self.session = aiohttp.ClientSession(connector=connector)
127
-
145
+
128
146
  # Perform authentication based on method
129
147
  if self.auth_method == "api_key":
130
148
  await self._authenticate_api_key()
@@ -136,110 +154,110 @@ class UniversalClient:
136
154
  await self._authenticate_basic()
137
155
  else:
138
156
  print("No authentication required")
139
-
157
+
140
158
  print("Connection established successfully")
141
-
159
+
142
160
  async def disconnect(self) -> None:
143
161
  """Close connection and cleanup."""
144
162
  if self.session:
145
163
  await self.session.close()
146
164
  self.session = None
147
165
  print("Connection closed")
148
-
166
+
149
167
  async def _authenticate_api_key(self) -> None:
150
168
  """Authenticate using API key."""
151
169
  api_key_config = self.security_config.get("api_key", {})
152
170
  api_key = api_key_config.get("key")
153
-
171
+
154
172
  if not api_key:
155
173
  raise ValueError("API key not provided in configuration")
156
-
174
+
157
175
  # Store API key for requests
158
176
  self.current_token = api_key
159
177
  print(f"Authenticated with API key: {api_key[:8]}...")
160
-
178
+
161
179
  async def _authenticate_jwt(self) -> None:
162
180
  """Authenticate using JWT token."""
163
181
  jwt_config = self.security_config.get("jwt", {})
164
-
182
+
165
183
  # Check if we have a stored token that's still valid
166
184
  if self.current_token and self.token_expiry and time.time() < self.token_expiry:
167
185
  print("Using existing JWT token")
168
186
  return
169
-
187
+
170
188
  # Get credentials for JWT
171
189
  username = jwt_config.get("username")
172
190
  password = jwt_config.get("password")
173
191
  secret = jwt_config.get("secret")
174
-
192
+
175
193
  if not all([username, password, secret]):
176
194
  raise ValueError("JWT credentials not provided in configuration")
177
-
195
+
178
196
  # Create JWT token
179
197
  if SECURITY_FRAMEWORK_AVAILABLE:
180
198
  self.current_token = create_jwt_token(
181
- username,
182
- secret,
183
- expiry_hours=jwt_config.get("expiry_hours", 24)
199
+ username, secret, expiry_hours=jwt_config.get("expiry_hours", 24)
184
200
  )
185
201
  else:
186
202
  # Simple JWT creation (for demonstration)
187
203
  import jwt
204
+
188
205
  payload = {
189
206
  "username": username,
190
- "exp": time.time() + (jwt_config.get("expiry_hours", 24) * 3600)
207
+ "exp": time.time() + (jwt_config.get("expiry_hours", 24) * 3600),
191
208
  }
192
209
  self.current_token = jwt.encode(payload, secret, algorithm="HS256")
193
-
210
+
194
211
  self.token_expiry = time.time() + (jwt_config.get("expiry_hours", 24) * 3600)
195
212
  print(f"Authenticated with JWT token: {self.current_token[:20]}...")
196
-
213
+
197
214
  async def _authenticate_certificate(self) -> None:
198
215
  """Authenticate using client certificate."""
199
216
  cert_config = self.security_config.get("certificate", {})
200
-
217
+
201
218
  cert_file = cert_config.get("cert_file")
202
219
  key_file = cert_config.get("key_file")
203
-
220
+
204
221
  if not cert_file or not key_file:
205
222
  raise ValueError("Certificate files not provided in configuration")
206
-
223
+
207
224
  # Validate certificate
208
225
  if SECURITY_FRAMEWORK_AVAILABLE and self.cert_manager:
209
226
  try:
210
227
  cert_info = self.cert_manager.validate_certificate(cert_file, key_file)
211
228
  print(f"Certificate validated: {cert_info.get('subject', 'Unknown')}")
212
-
229
+
213
230
  # Extract roles from certificate
214
231
  roles = extract_roles_from_cert(cert_file)
215
232
  if roles:
216
233
  print(f"Certificate roles: {roles}")
217
234
  except Exception as e:
218
235
  print(f"Warning: Certificate validation failed: {e}")
219
-
236
+
220
237
  print("Certificate authentication prepared")
221
-
238
+
222
239
  async def _authenticate_basic(self) -> None:
223
240
  """Authenticate using basic authentication."""
224
241
  basic_config = self.security_config.get("basic", {})
225
242
  username = basic_config.get("username")
226
243
  password = basic_config.get("password")
227
-
244
+
228
245
  if not username or not password:
229
246
  raise ValueError("Basic auth credentials not provided in configuration")
230
-
247
+
231
248
  import base64
249
+
232
250
  credentials = base64.b64encode(f"{username}:{password}".encode()).decode()
233
251
  self.current_token = f"Basic {credentials}"
234
252
  print(f"Authenticated with basic auth: {username}")
235
-
253
+
236
254
  def _get_auth_headers(self) -> Dict[str, str]:
237
255
  """Get authentication headers for requests."""
238
256
  headers = {"Content-Type": "application/json"}
239
-
257
+
240
258
  if not self.current_token:
241
259
  return headers
242
-
260
+
243
261
  if self.auth_method == "api_key":
244
262
  api_key_config = self.security_config.get("api_key", {})
245
263
  header_name = api_key_config.get("header", "X-API-Key")
@@ -248,9 +266,9 @@ class UniversalClient:
248
266
  headers["Authorization"] = f"Bearer {self.current_token}"
249
267
  elif self.auth_method == "basic":
250
268
  headers["Authorization"] = self.current_token
251
-
269
+
252
270
  return headers
253
-
271
+
254
272
  def _create_ssl_context(self) -> Optional[ssl.SSLContext]:
255
273
  """Create SSL context for secure connections."""
256
274
  ssl_config = self.security_config.get("ssl", {})
@@ -296,33 +314,33 @@ class UniversalClient:
296
314
  except Exception as e:
297
315
  print(f"Warning: Failed to create SSL context: {e}")
298
316
  return None
299
-
317
+
300
318
  async def request(
301
- self,
302
- method: str,
303
- endpoint: str,
319
+ self,
320
+ method: str,
321
+ endpoint: str,
304
322
  data: Optional[Dict[str, Any]] = None,
305
- headers: Optional[Dict[str, str]] = None
323
+ headers: Optional[Dict[str, str]] = None,
306
324
  ) -> Dict[str, Any]:
307
325
  """
308
326
  Make authenticated request to server.
309
-
327
+
310
328
  Args:
311
329
  method: HTTP method (GET, POST, etc.)
312
330
  endpoint: API endpoint
313
331
  data: Request data
314
332
  headers: Additional headers
315
-
333
+
316
334
  Returns:
317
335
  Response data
318
336
  """
319
337
  url = urljoin(self.base_url, endpoint)
320
-
338
+
321
339
  # Prepare headers
322
340
  request_headers = self._get_auth_headers()
323
341
  if headers:
324
342
  request_headers.update(headers)
325
-
343
+
326
344
  try:
327
345
  for attempt in range(self.retry_attempts):
328
346
  try:
@@ -331,20 +349,24 @@ class UniversalClient:
331
349
  url,
332
350
  json=data,
333
351
  headers=request_headers,
334
- timeout=aiohttp.ClientTimeout(total=self.timeout)
352
+ timeout=aiohttp.ClientTimeout(total=self.timeout),
335
353
  ) as response:
336
354
  result = await response.json()
337
-
355
+
338
356
  # Validate response if security framework available
339
357
  if SECURITY_FRAMEWORK_AVAILABLE and self.security_manager:
340
- self.security_manager.validate_server_response(dict(response.headers))
341
-
358
+ self.security_manager.validate_server_response(
359
+ dict(response.headers)
360
+ )
361
+
342
362
  if response.status >= 400:
343
- print(f"Request failed with status {response.status}: {result}")
363
+ print(
364
+ f"Request failed with status {response.status}: {result}"
365
+ )
344
366
  return {"error": result, "status": response.status}
345
-
367
+
346
368
  return result
347
-
369
+
348
370
  except Exception as e:
349
371
  print(f"Request attempt {attempt + 1} failed: {e}")
350
372
  if attempt < self.retry_attempts - 1:
@@ -354,23 +376,27 @@ class UniversalClient:
354
376
  except Exception as e:
355
377
  print(f"Request failed: {e}")
356
378
  raise
357
-
379
+
358
380
  async def get(self, endpoint: str, **kwargs) -> Dict[str, Any]:
359
381
  """Make GET request."""
360
382
  return await self.request("GET", endpoint, **kwargs)
361
-
362
- async def post(self, endpoint: str, data: Dict[str, Any], **kwargs) -> Dict[str, Any]:
383
+
384
+ async def post(
385
+ self, endpoint: str, data: Dict[str, Any], **kwargs
386
+ ) -> Dict[str, Any]:
363
387
  """Make POST request."""
364
388
  return await self.request("POST", endpoint, data=data, **kwargs)
365
-
366
- async def put(self, endpoint: str, data: Dict[str, Any], **kwargs) -> Dict[str, Any]:
389
+
390
+ async def put(
391
+ self, endpoint: str, data: Dict[str, Any], **kwargs
392
+ ) -> Dict[str, Any]:
367
393
  """Make PUT request."""
368
394
  return await self.request("PUT", endpoint, data=data, **kwargs)
369
-
395
+
370
396
  async def delete(self, endpoint: str, **kwargs) -> Dict[str, Any]:
371
397
  """Make DELETE request."""
372
398
  return await self.request("DELETE", endpoint, **kwargs)
373
-
399
+
374
400
  async def test_connection(self) -> bool:
375
401
  """Test connection to server."""
376
402
  try:
@@ -384,14 +410,14 @@ class UniversalClient:
384
410
  except Exception as e:
385
411
  print(f"❌ Connection test failed: {e}")
386
412
  return False
387
-
413
+
388
414
  async def test_security_features(self) -> Dict[str, bool]:
389
415
  """Test various security features."""
390
416
  results = {}
391
-
417
+
392
418
  # Test basic connectivity
393
419
  results["connectivity"] = await self.test_connection()
394
-
420
+
395
421
  # Test authentication
396
422
  if self.auth_method != "none":
397
423
  try:
@@ -399,55 +425,58 @@ class UniversalClient:
399
425
  results["authentication"] = "error" not in result
400
426
  except:
401
427
  results["authentication"] = False
402
-
428
+
403
429
  # Test SSL/TLS
404
430
  if self.base_url.startswith("https"):
405
431
  results["ssl_tls"] = True
406
432
  else:
407
433
  results["ssl_tls"] = False
408
-
434
+
409
435
  return results
410
-
436
+
411
437
  async def register_proxy(self, proxy_config: Dict[str, Any]) -> Dict[str, Any]:
412
438
  """
413
439
  Register with proxy server.
414
-
440
+
415
441
  Args:
416
442
  proxy_config: Proxy registration configuration
417
-
443
+
418
444
  Returns:
419
445
  Registration result
420
446
  """
421
447
  try:
422
- result = await self.post("/api/jsonrpc", {
423
- "jsonrpc": "2.0",
424
- "method": "proxy_registration",
425
- "params": proxy_config,
426
- "id": 1
427
- })
448
+ result = await self.post(
449
+ "/api/jsonrpc",
450
+ {
451
+ "jsonrpc": "2.0",
452
+ "method": "proxy_registration",
453
+ "params": proxy_config,
454
+ "id": 1,
455
+ },
456
+ )
428
457
  return result
429
458
  except Exception as e:
430
459
  print(f"Proxy registration failed: {e}")
431
460
  return {"error": str(e)}
432
-
433
- async def execute_command(self, command: str, params: Dict[str, Any] = None) -> Dict[str, Any]:
461
+
462
+ async def execute_command(
463
+ self, command: str, params: Dict[str, Any] = None
464
+ ) -> Dict[str, Any]:
434
465
  """
435
466
  Execute a command on the server.
436
-
467
+
437
468
  Args:
438
469
  command: Command name
439
470
  params: Command parameters
440
-
471
+
441
472
  Returns:
442
473
  Command result
443
474
  """
444
475
  try:
445
- result = await self.post("/api/jsonrpc", {
446
- "jsonrpc": "2.0",
447
- "method": command,
448
- "params": params or {},
449
- "id": 1
450
- })
476
+ result = await self.post(
477
+ "/api/jsonrpc",
478
+ {"jsonrpc": "2.0", "method": command, "params": params or {}, "id": 1},
479
+ )
451
480
  return result
452
481
  except Exception as e:
453
482
  print(f"Command execution failed: {e}")
@@ -457,55 +486,53 @@ class UniversalClient:
457
486
  def create_client_from_config(config_file: str) -> UniversalClient:
458
487
  """
459
488
  Create a UniversalClient instance from a configuration file.
460
-
489
+
461
490
  Args:
462
491
  config_file: Path to configuration file
463
-
492
+
464
493
  Returns:
465
494
  UniversalClient instance
466
495
  """
467
496
  try:
468
- with open(config_file, 'r') as f:
497
+ with open(config_file, "r") as f:
469
498
  config_data = json.load(f)
470
-
499
+
471
500
  # Extract server configuration
472
501
  server_config = config_data.get("server", {})
473
502
  host = server_config.get("host", "127.0.0.1")
474
503
  port = server_config.get("port", 8000)
475
-
504
+
476
505
  # Determine protocol
477
506
  ssl_config = config_data.get("ssl", {})
478
507
  ssl_enabled = ssl_config.get("enabled", False)
479
508
  protocol = "https" if ssl_enabled else "http"
480
-
509
+
481
510
  server_url = f"{protocol}://{host}:{port}"
482
-
511
+
483
512
  # Create client configuration
484
513
  client_config = {
485
514
  "server_url": server_url,
486
515
  "timeout": 30,
487
516
  "retry_attempts": 3,
488
517
  "retry_delay": 1,
489
- "security": {
490
- "auth_method": "none"
491
- }
518
+ "security": {"auth_method": "none"},
492
519
  }
493
-
520
+
494
521
  # Add SSL configuration if needed
495
522
  if ssl_enabled:
496
523
  client_config["security"]["ssl"] = {
497
524
  "enabled": True,
498
525
  "check_hostname": False,
499
- "verify": False
526
+ "verify": False,
500
527
  }
501
-
528
+
502
529
  # Add CA certificate if available
503
530
  ca_cert = ssl_config.get("ca_cert")
504
531
  if ca_cert and os.path.exists(ca_cert):
505
532
  client_config["security"]["ssl"]["ca_cert_file"] = ca_cert
506
-
533
+
507
534
  return UniversalClient(client_config)
508
-
535
+
509
536
  except Exception as e:
510
537
  raise ValueError(f"Failed to create client from config: {e}")
511
538
 
@@ -514,34 +541,36 @@ def create_client_from_config(config_file: str) -> UniversalClient:
514
541
  async def main():
515
542
  """Main function for CLI usage."""
516
543
  import argparse
517
-
518
- parser = argparse.ArgumentParser(description="Universal Client for MCP Proxy Adapter")
544
+
545
+ parser = argparse.ArgumentParser(
546
+ description="Universal Client for MCP Proxy Adapter"
547
+ )
519
548
  parser.add_argument("--config", help="Path to configuration file")
520
549
  parser.add_argument("--method", help="JSON-RPC method to call")
521
550
  parser.add_argument("--params", help="JSON-RPC parameters (JSON string)")
522
551
  parser.add_argument("--auth-method", help="Authentication method")
523
552
  parser.add_argument("--server-url", help="Server URL")
524
-
553
+
525
554
  args = parser.parse_args()
526
-
555
+
527
556
  if args.config:
528
557
  # Load configuration from file
529
558
  try:
530
559
  client = create_client_from_config(args.config)
531
-
560
+
532
561
  print(f"🚀 Testing --config connection")
533
562
  print("=" * 40)
534
563
  print(f"Universal client initialized with auth method: --config")
535
564
  print(f"Connecting to {client.base_url} with --config authentication...")
536
-
565
+
537
566
  async with client:
538
567
  # Test connection
539
568
  success = await client.test_connection()
540
-
569
+
541
570
  if success:
542
571
  print("No authentication required")
543
572
  print("Connection established successfully")
544
-
573
+
545
574
  if args.method:
546
575
  # Execute JSON-RPC method
547
576
  params = {}
@@ -551,7 +580,7 @@ async def main():
551
580
  except json.JSONDecodeError:
552
581
  print("❌ Invalid JSON parameters")
553
582
  return
554
-
583
+
555
584
  result = await client.execute_command(args.method, params)
556
585
  print(f"✅ Method '{args.method}' executed successfully:")
557
586
  print(json.dumps(result, indent=2))
@@ -563,7 +592,7 @@ async def main():
563
592
  else:
564
593
  print("❌ Connection failed")
565
594
  print("Connection closed")
566
-
595
+
567
596
  except FileNotFoundError:
568
597
  print(f"❌ Configuration file not found: {args.config}")
569
598
  except json.JSONDecodeError: