mcp-proxy-adapter 6.9.2__py3-none-any.whl → 6.9.4__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.
@@ -314,7 +314,7 @@ class ConfigValidator:
314
314
  # Check conditional sections based on feature flags
315
315
  for feature_name, feature_config in self.feature_flags.items():
316
316
  enabled_key = feature_config["enabled_key"]
317
- is_enabled = self._get_nested_value(enabled_key, False)
317
+ is_enabled = self._get_nested_value_safe(enabled_key, False)
318
318
 
319
319
  if is_enabled and feature_name in self.required_sections:
320
320
  section_name = feature_name
@@ -362,7 +362,7 @@ class ConfigValidator:
362
362
  """Validate feature flags and their dependencies."""
363
363
  for feature_name, feature_config in self.feature_flags.items():
364
364
  enabled_key = feature_config["enabled_key"]
365
- is_enabled = self._get_nested_value(enabled_key, False)
365
+ is_enabled = self._get_nested_value_safe(enabled_key, False)
366
366
 
367
367
  if is_enabled:
368
368
  # Check dependencies
@@ -400,7 +400,7 @@ class ConfigValidator:
400
400
 
401
401
  def _validate_protocol_requirements(self) -> None:
402
402
  """Validate protocol-specific requirements."""
403
- protocol = self._get_nested_value("server.protocol", "http")
403
+ protocol = self._get_nested_value_safe("server.protocol", "http")
404
404
 
405
405
  if protocol not in self.protocol_requirements:
406
406
  self.validation_results.append(ValidationResult(
@@ -415,7 +415,7 @@ class ConfigValidator:
415
415
 
416
416
  # Check SSL requirements
417
417
  if requirements["ssl_enabled"]:
418
- ssl_enabled = self._get_nested_value("ssl.enabled", False)
418
+ ssl_enabled = self._get_nested_value_safe("ssl.enabled", False)
419
419
  if not ssl_enabled:
420
420
  self.validation_results.append(ValidationResult(
421
421
  level="error",
@@ -444,7 +444,7 @@ class ConfigValidator:
444
444
 
445
445
  # Check client verification requirements
446
446
  if requirements["client_verification"]:
447
- verify_client = self._get_nested_value("transport.ssl.verify_client", False)
447
+ verify_client = self._get_nested_value_safe("transport.ssl.verify_client", False)
448
448
  if not verify_client:
449
449
  self.validation_results.append(ValidationResult(
450
450
  level="error",
@@ -489,13 +489,13 @@ class ConfigValidator:
489
489
 
490
490
  def _validate_security_consistency(self) -> None:
491
491
  """Validate security configuration consistency."""
492
- security_enabled = self._get_nested_value("security.enabled", False)
492
+ security_enabled = self._get_nested_value_safe("security.enabled", False)
493
493
 
494
494
  if security_enabled:
495
495
  # Check if authentication is properly configured
496
- tokens = self._get_nested_value("security.tokens", {})
497
- roles = self._get_nested_value("security.roles", {})
498
- roles_file = self._get_nested_value("security.roles_file")
496
+ tokens = self._get_nested_value_safe("security.tokens", {})
497
+ roles = self._get_nested_value_safe("security.roles", {})
498
+ roles_file = self._get_nested_value_safe("security.roles_file")
499
499
 
500
500
  has_tokens = bool(tokens and any(tokens.values()))
501
501
  has_roles = bool(roles and any(roles.values()))
@@ -520,7 +520,7 @@ class ConfigValidator:
520
520
 
521
521
  def _validate_proxy_registration(self) -> None:
522
522
  """Validate proxy registration configuration."""
523
- registration_enabled = self._get_nested_value("proxy_registration.enabled", False)
523
+ registration_enabled = self._get_nested_value_safe("proxy_registration.enabled", False)
524
524
 
525
525
  if registration_enabled:
526
526
  proxy_url = self._get_nested_value("proxy_registration.proxy_url")
@@ -533,19 +533,19 @@ class ConfigValidator:
533
533
  ))
534
534
 
535
535
  # Check authentication method consistency
536
- auth_method = self._get_nested_value("proxy_registration.auth_method", "none")
536
+ auth_method = self._get_nested_value_safe("proxy_registration.auth_method", "none")
537
537
  if auth_method != "none":
538
538
  if auth_method == "certificate":
539
539
  cert_file = self._get_nested_value("proxy_registration.certificate.cert_file")
540
540
  key_file = self._get_nested_value("proxy_registration.certificate.key_file")
541
- if not cert_file or not key_file:
542
- self.validation_results.append(ValidationResult(
543
- level="error",
544
- message="Certificate authentication is enabled but certificate files are not specified",
545
- section="proxy_registration",
546
- key="certificate"
547
- ))
548
- elif auth_method == "token":
541
+ if not cert_file or not key_file:
542
+ self.validation_results.append(ValidationResult(
543
+ level="error",
544
+ message="Certificate authentication is enabled but certificate files are not specified",
545
+ section="proxy_registration",
546
+ key="certificate"
547
+ ))
548
+ elif auth_method == "token":
549
549
  token = self._get_nested_value("proxy_registration.token.token")
550
550
  if not token:
551
551
  self.validation_results.append(ValidationResult(
@@ -557,7 +557,7 @@ class ConfigValidator:
557
557
 
558
558
  def _validate_ssl_configuration(self) -> None:
559
559
  """Validate SSL configuration with detailed certificate validation."""
560
- ssl_enabled = self._get_nested_value("ssl.enabled", False)
560
+ ssl_enabled = self._get_nested_value_safe("ssl.enabled", False)
561
561
 
562
562
  if ssl_enabled:
563
563
  cert_file = self._get_nested_value("ssl.cert_file")
@@ -621,7 +621,7 @@ class ConfigValidator:
621
621
 
622
622
  def _validate_roles_configuration(self) -> None:
623
623
  """Validate roles configuration."""
624
- roles_enabled = self._get_nested_value("roles.enabled", False)
624
+ roles_enabled = self._get_nested_value_safe("roles.enabled", False)
625
625
 
626
626
  if roles_enabled:
627
627
  config_file = self._get_nested_value("roles.config_file")
@@ -640,8 +640,8 @@ class ConfigValidator:
640
640
  key="config_file"
641
641
  ))
642
642
 
643
- def _get_nested_value(self, key: str, default: Any = None) -> Any:
644
- """Get value from nested dictionary using dot notation."""
643
+ def _get_nested_value(self, key: str) -> Any:
644
+ """Get value from nested dictionary using dot notation. Raises exception if key not found."""
645
645
  keys = key.split(".")
646
646
  value = self.config_data
647
647
 
@@ -649,7 +649,8 @@ class ConfigValidator:
649
649
  if isinstance(value, dict) and k in value:
650
650
  value = value[k]
651
651
  else:
652
- return default
652
+ from .errors import MissingConfigKeyError
653
+ raise MissingConfigKeyError(k, ".".join(keys[:keys.index(k)]))
653
654
 
654
655
  return value
655
656
 
@@ -666,11 +667,18 @@ class ConfigValidator:
666
667
 
667
668
  return True
668
669
 
670
+ def _get_nested_value_safe(self, key: str, default: Any = None) -> Any:
671
+ """Get value from nested dictionary using dot notation with fallback."""
672
+ try:
673
+ return self._get_nested_value(key)
674
+ except MissingConfigKeyError:
675
+ return default
676
+
669
677
  def _is_file_required_for_enabled_features(self, file_key: str) -> bool:
670
678
  """Check if file is required based on enabled features."""
671
679
  for feature_name, feature_config in self.feature_flags.items():
672
680
  enabled_key = feature_config["enabled_key"]
673
- is_enabled = self._get_nested_value(enabled_key, False)
681
+ is_enabled = self._get_nested_value_safe(enabled_key, False)
674
682
 
675
683
  if is_enabled and file_key in feature_config["required_files"]:
676
684
  return True
@@ -257,3 +257,55 @@ class ConfigError(MicroserviceError):
257
257
  error_messages.append(error_msg)
258
258
 
259
259
  return "\n".join(error_messages)
260
+
261
+
262
+ class MissingConfigKeyError(ConfigError):
263
+ """Missing required configuration key."""
264
+
265
+ def __init__(self, key: str, section: str = None):
266
+ location = f"{section}.{key}" if section else key
267
+ message = f"Required configuration key '{location}' is missing"
268
+ super().__init__(message)
269
+ self.key = key
270
+ self.section = section
271
+
272
+
273
+ class InvalidConfigValueError(ConfigError):
274
+ """Invalid configuration value."""
275
+
276
+ def __init__(self, key: str, value: Any, expected_type: str, section: str = None):
277
+ location = f"{section}.{key}" if section else key
278
+ message = f"Invalid value for '{location}': got {type(value).__name__}, expected {expected_type}"
279
+ super().__init__(message)
280
+ self.key = key
281
+ self.section = section
282
+ self.value = value
283
+ self.expected_type = expected_type
284
+
285
+
286
+ class MissingConfigSectionError(ConfigError):
287
+ """Missing required configuration section."""
288
+
289
+ def __init__(self, section: str):
290
+ message = f"Required configuration section '{section}' is missing"
291
+ super().__init__(message)
292
+ self.section = section
293
+
294
+
295
+ class MissingConfigFileError(ConfigError):
296
+ """Missing configuration file."""
297
+
298
+ def __init__(self, file_path: str):
299
+ message = f"Configuration file '{file_path}' does not exist"
300
+ super().__init__(message)
301
+ self.file_path = file_path
302
+
303
+
304
+ class InvalidConfigFileError(ConfigError):
305
+ """Invalid configuration file format."""
306
+
307
+ def __init__(self, file_path: str, reason: str):
308
+ message = f"Invalid configuration file '{file_path}': {reason}"
309
+ super().__init__(message)
310
+ self.file_path = file_path
311
+ self.reason = reason
@@ -2,4 +2,4 @@
2
2
  Version information for MCP Proxy Adapter.
3
3
  """
4
4
 
5
- __version__ = "6.9.2"
5
+ __version__ = "6.9.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-proxy-adapter
3
- Version: 6.9.2
3
+ Version: 6.9.4
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
@@ -4,7 +4,7 @@ mcp_proxy_adapter/config.py,sha256=1Ngxri2IPGoytYdCF5pXzbLUXsWcf6qYENkaDkAppg0,2
4
4
  mcp_proxy_adapter/custom_openapi.py,sha256=XRviX-C-ZkSKdBhORhDTdeN_1FWyEfXZADiASft3t9I,28149
5
5
  mcp_proxy_adapter/main.py,sha256=NFcSW7WaEnimRWe5zj28D0CH9otHlRZ92d2Um6XiGjE,10399
6
6
  mcp_proxy_adapter/openapi.py,sha256=2UZOI09ZDRJuBYBjKbMyb2U4uASszoCMD5o_4ktRpvg,13480
7
- mcp_proxy_adapter/version.py,sha256=vuzTrRHSwgcLMySpJ4TEb05VMX9YheQ9F8Ky7MCpH4g,74
7
+ mcp_proxy_adapter/version.py,sha256=cwNmCGdmZYel3p6nea6_Te_09LWudM3jQBjHXIwgjgY,74
8
8
  mcp_proxy_adapter/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  mcp_proxy_adapter/api/app.py,sha256=PQ1Ch5ydJIHp3Z6gcMCzKkTsXPQAuZ9weHtQ-EXnNGY,37134
10
10
  mcp_proxy_adapter/api/handlers.py,sha256=X-hcMNVeTAu4yVkKJEChEsj2bFptUS6sLNN-Wysjkow,10011
@@ -62,9 +62,9 @@ mcp_proxy_adapter/core/client.py,sha256=qIbPl8prEwK2U65kl-vGJW2_imo1E4i6HxG_VpPe
62
62
  mcp_proxy_adapter/core/client_manager.py,sha256=yD8HZJlOwmDbVU49YfzSbh1XZ-Vib8qfcLVAaH03Jdg,8832
63
63
  mcp_proxy_adapter/core/client_security.py,sha256=siUaYorcDbpZsEIKgLfg-jBKkp7z_Er8wsO63mDD3Is,13127
64
64
  mcp_proxy_adapter/core/config_converter.py,sha256=Wnnsrbw7DxtgDfLG-IyyzK-hkKu0_1yp7-7dW87tu_4,17422
65
- mcp_proxy_adapter/core/config_validator.py,sha256=HdSsXNfieqhBkaQdpRU1Dx2-I7NmBtaoozvR46yTUWg,47937
65
+ mcp_proxy_adapter/core/config_validator.py,sha256=O0aX68oKWSzQam53kb-yeb95zGzW_2fW9VrYIatV83E,48390
66
66
  mcp_proxy_adapter/core/crl_utils.py,sha256=Jnwq2UN52IoCDZCwByRP3XNMOJexftb-mVaH6zes6Fc,11706
67
- mcp_proxy_adapter/core/errors.py,sha256=uXm0zslc1iyvuu-aEfX5euEHPWaQn8LZUwgQGKVWe8U,6825
67
+ mcp_proxy_adapter/core/errors.py,sha256=CyhQgvMt0ooQjONa65XRBJ44y-l-E5_ES4KOuRvIpBk,8557
68
68
  mcp_proxy_adapter/core/logging.py,sha256=VIpiC6QTGLukRjiJoVpq3VEoLKhUeLNl8IdfljpW6ZU,9654
69
69
  mcp_proxy_adapter/core/mtls_asgi.py,sha256=tvk0P9024s18dcCHY9AaQIecT4ojOTv21EuQWXwooU0,5200
70
70
  mcp_proxy_adapter/core/mtls_asgi_app.py,sha256=DT_fTUH1RkvBa3ThbyCyNb-XUHyCb4DqaKA1gcZC6z4,6538
@@ -135,8 +135,8 @@ mcp_proxy_adapter/schemas/base_schema.json,sha256=v9G9cGMd4dRhCZsOQ_FMqOi5VFyVbI
135
135
  mcp_proxy_adapter/schemas/openapi_schema.json,sha256=C3yLkwmDsvnLW9B5gnKKdBGl4zxkeU-rEmjTrNVsQU0,8405
136
136
  mcp_proxy_adapter/schemas/roles.json,sha256=pgf_ZyqKyXbfGUxvobpiLiSJz9zzxrMuoVWEkEpz3N8,764
137
137
  mcp_proxy_adapter/schemas/roles_schema.json,sha256=deHgI7L6GwfBXacOlNtDgDJelDThppClC3Ti4Eh8rJY,5659
138
- mcp_proxy_adapter-6.9.2.dist-info/METADATA,sha256=VcF7hcY89nQtaROg25aeYmkPC_PAAd98ZhYFevRUBtk,8510
139
- mcp_proxy_adapter-6.9.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
140
- mcp_proxy_adapter-6.9.2.dist-info/entry_points.txt,sha256=Bf-O5Aq80n22Ayu9fI9BgidzWqwzIVaqextAddTuHZw,563
141
- mcp_proxy_adapter-6.9.2.dist-info/top_level.txt,sha256=JZT7vPLBYrtroX-ij68JBhJYbjDdghcV-DFySRy-Nnw,18
142
- mcp_proxy_adapter-6.9.2.dist-info/RECORD,,
138
+ mcp_proxy_adapter-6.9.4.dist-info/METADATA,sha256=gcp96fDHwLCDr0aOI5ThEyFlSxLpMUajrYCdEzZX_30,8510
139
+ mcp_proxy_adapter-6.9.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
140
+ mcp_proxy_adapter-6.9.4.dist-info/entry_points.txt,sha256=Bf-O5Aq80n22Ayu9fI9BgidzWqwzIVaqextAddTuHZw,563
141
+ mcp_proxy_adapter-6.9.4.dist-info/top_level.txt,sha256=JZT7vPLBYrtroX-ij68JBhJYbjDdghcV-DFySRy-Nnw,18
142
+ mcp_proxy_adapter-6.9.4.dist-info/RECORD,,