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
@@ -13,45 +13,59 @@ class ConfigResult(SuccessResult):
13
13
  """
14
14
  Config operation result.
15
15
  """
16
-
17
- def __init__(self, config: Dict[str, Any], operation: str, message: str = None):
16
+
17
+ def __init__(
18
+ self,
19
+ config: Dict[str, Any],
20
+ operation: str,
21
+ message: Optional[str] = None,
22
+ ):
18
23
  """
19
24
  Initialize config result.
20
-
25
+
21
26
  Args:
22
27
  config: Configuration values
23
28
  operation: Operation performed
24
29
  message: Optional message
25
30
  """
26
- super().__init__(data={
27
- "config": config,
28
- "operation": operation
29
- }, message=message)
31
+ super().__init__(
32
+ data={"config": config, "operation": operation}, message=message
33
+ )
30
34
 
31
35
 
32
36
  class ConfigCommand(Command):
33
37
  """
34
38
  Command for managing service configuration.
35
39
  """
40
+
36
41
  name = "config"
37
42
  description = "Get or set configuration values"
38
43
  result_class = ConfigResult
39
-
40
- async def execute(self, operation: str = "get", path: str = None, value: Any = None) -> ConfigResult:
44
+
45
+ async def execute(
46
+ self,
47
+ operation: str = "get",
48
+ path: Optional[str] = None,
49
+ value: Any = None,
50
+ context: Optional[Dict] = None,
51
+ **kwargs,
52
+ ) -> ConfigResult:
41
53
  """
42
54
  Execute the command.
43
-
55
+
44
56
  Args:
45
57
  operation: Operation to perform (get, set)
46
58
  path: Configuration path (dot notation)
47
59
  value: Value to set (for set operation)
48
-
60
+ context: Optional context parameter passed by framework
61
+ **kwargs: Additional parameters
62
+
49
63
  Returns:
50
64
  Config operation result
51
65
  """
52
66
  message = None
53
67
  result_config = {}
54
-
68
+
55
69
  if operation == "get":
56
70
  if path:
57
71
  # Get specific config value
@@ -59,8 +73,8 @@ class ConfigCommand(Command):
59
73
  else:
60
74
  # Get all config
61
75
  result_config = config_instance.get_all()
62
- message = f"Configuration retrieved successfully"
63
-
76
+ message = "Configuration retrieved successfully"
77
+
64
78
  elif operation == "set":
65
79
  if path and value is not None:
66
80
  # Set config value
@@ -68,26 +82,26 @@ class ConfigCommand(Command):
68
82
  # Save config
69
83
  config_instance.save()
70
84
  result_config = {path: value}
71
- message = f"Configuration updated successfully"
85
+ message = "Configuration updated successfully"
72
86
  else:
73
87
  # Error - missing required parameters
74
- raise ValueError("Both 'path' and 'value' are required for 'set' operation")
75
-
88
+ raise ValueError(
89
+ "Both 'path' and 'value' are required for 'set' operation"
90
+ )
91
+
76
92
  else:
77
93
  # Invalid operation
78
- raise ValueError(f"Invalid operation: {operation}. Valid operations: get, set")
79
-
80
- return ConfigResult(
81
- config=result_config,
82
- operation=operation,
83
- message=message
84
- )
85
-
94
+ raise ValueError(
95
+ f"Invalid operation: {operation}. Valid operations: get, set"
96
+ )
97
+
98
+ return ConfigResult(config=result_config, operation=operation, message=message)
99
+
86
100
  @classmethod
87
101
  def get_schema(cls) -> Dict[str, Any]:
88
102
  """
89
103
  Returns JSON schema for command parameters validation.
90
-
104
+
91
105
  Returns:
92
106
  Dictionary with JSON schema.
93
107
  """
@@ -95,19 +109,20 @@ class ConfigCommand(Command):
95
109
  "type": "object",
96
110
  "properties": {
97
111
  "operation": {
98
- "type": "string",
112
+ "type": "string",
99
113
  "enum": ["get", "set"],
100
114
  "default": "get",
101
- "description": "Operation to perform (get or set)"
115
+ "description": "Operation to perform (get or set)",
102
116
  },
103
117
  "path": {
104
118
  "type": "string",
105
- "description": "Configuration path in dot notation (e.g. 'server.host')"
119
+ "description": "Configuration path in dot notation "
120
+ "(e.g. 'server.host')",
106
121
  },
107
122
  "value": {
108
- "description": "Value to set (required for 'set' operation)"
109
- }
123
+ "description": "Value to set (required for " "'set' operation)"
124
+ },
110
125
  },
111
126
  "required": ["operation"],
112
- "additionalProperties": False
113
- }
127
+ "additionalProperties": False,
128
+ }
@@ -7,47 +7,47 @@ for command instances in the microservice.
7
7
 
8
8
  from typing import Any, Dict, Optional, Type, TypeVar, cast
9
9
 
10
- T = TypeVar('T')
10
+ T = TypeVar("T")
11
11
 
12
12
 
13
13
  class DependencyContainer:
14
14
  """
15
15
  Container for managing dependencies.
16
-
16
+
17
17
  This class provides functionality to register, resolve, and manage
18
18
  dependencies that can be injected into command instances.
19
19
  """
20
-
20
+
21
21
  def __init__(self):
22
22
  """Initialize dependency container."""
23
23
  self._dependencies: Dict[str, Any] = {}
24
24
  self._factories: Dict[str, callable] = {}
25
25
  self._singletons: Dict[str, Any] = {}
26
-
26
+
27
27
  def register(self, name: str, instance: Any) -> None:
28
28
  """
29
29
  Register a dependency instance with a given name.
30
-
30
+
31
31
  Args:
32
32
  name: The name to register the dependency under
33
33
  instance: The dependency instance
34
34
  """
35
35
  self._dependencies[name] = instance
36
-
36
+
37
37
  def register_factory(self, name: str, factory: callable) -> None:
38
38
  """
39
39
  Register a factory function that will be called to create the dependency.
40
-
40
+
41
41
  Args:
42
42
  name: The name to register the factory under
43
43
  factory: A callable that creates the dependency
44
44
  """
45
45
  self._factories[name] = factory
46
-
46
+
47
47
  def register_singleton(self, name: str, factory: callable) -> None:
48
48
  """
49
49
  Register a singleton factory that will create the instance only once.
50
-
50
+
51
51
  Args:
52
52
  name: The name to register the singleton under
53
53
  factory: A callable that creates the singleton instance
@@ -55,56 +55,55 @@ class DependencyContainer:
55
55
  self._factories[name] = factory
56
56
  # Mark as singleton but don't create until requested
57
57
  self._singletons[name] = None
58
-
58
+
59
59
  def get(self, name: str) -> Any:
60
60
  """
61
61
  Get a dependency by name.
62
-
62
+
63
63
  Args:
64
64
  name: The name of the dependency to get
65
-
65
+
66
66
  Returns:
67
67
  The dependency instance
68
-
68
+
69
69
  Raises:
70
70
  KeyError: If the dependency is not registered
71
71
  """
72
72
  # Check for direct instance
73
73
  if name in self._dependencies:
74
74
  return self._dependencies[name]
75
-
75
+
76
76
  # Check for singleton
77
77
  if name in self._singletons:
78
78
  # Create singleton if doesn't exist
79
79
  if self._singletons[name] is None:
80
80
  self._singletons[name] = self._factories[name]()
81
81
  return self._singletons[name]
82
-
82
+
83
83
  # Check for factory
84
84
  if name in self._factories:
85
85
  return self._factories[name]()
86
-
86
+
87
87
  raise KeyError(f"Dependency '{name}' not registered")
88
-
88
+
89
89
  def clear(self) -> None:
90
90
  """Clear all registered dependencies."""
91
91
  self._dependencies.clear()
92
92
  self._factories.clear()
93
93
  self._singletons.clear()
94
-
94
+
95
95
  def has(self, name: str) -> bool:
96
96
  """
97
97
  Check if a dependency is registered.
98
-
98
+
99
99
  Args:
100
100
  name: The name of the dependency
101
-
101
+
102
102
  Returns:
103
103
  True if the dependency is registered, False otherwise
104
104
  """
105
- return (name in self._dependencies or
106
- name in self._factories)
105
+ return name in self._dependencies or name in self._factories
107
106
 
108
107
 
109
108
  # Global dependency container instance
110
- container = DependencyContainer()
109
+ container = DependencyContainer()
@@ -19,12 +19,12 @@ class DependencyManager:
19
19
  """
20
20
  Manages plugin dependencies installation and verification.
21
21
  """
22
-
22
+
23
23
  def __init__(self):
24
24
  """Initialize dependency manager."""
25
25
  self._installed_packages: Dict[str, str] = {}
26
26
  self._load_installed_packages()
27
-
27
+
28
28
  def _load_installed_packages(self) -> None:
29
29
  """Load list of currently installed packages."""
30
30
  try:
@@ -33,39 +33,41 @@ class DependencyManager:
33
33
  self._installed_packages[dist.project_name.lower()] = dist.version
34
34
  except Exception as e:
35
35
  logger.warning(f"Failed to load installed packages: {e}")
36
-
37
- def check_dependencies(self, dependencies: List[str]) -> Tuple[bool, List[str], List[str]]:
36
+
37
+ def check_dependencies(
38
+ self, dependencies: List[str]
39
+ ) -> Tuple[bool, List[str], List[str]]:
38
40
  """
39
41
  Check if dependencies are satisfied.
40
-
42
+
41
43
  Args:
42
44
  dependencies: List of dependency names
43
-
45
+
44
46
  Returns:
45
47
  Tuple of (all_satisfied, missing_deps, installed_deps)
46
48
  """
47
49
  if not dependencies:
48
50
  return True, [], []
49
-
51
+
50
52
  missing_deps = []
51
53
  installed_deps = []
52
-
54
+
53
55
  for dep in dependencies:
54
56
  if self._is_dependency_satisfied(dep):
55
57
  installed_deps.append(dep)
56
58
  else:
57
59
  missing_deps.append(dep)
58
-
60
+
59
61
  all_satisfied = len(missing_deps) == 0
60
62
  return all_satisfied, missing_deps, installed_deps
61
-
63
+
62
64
  def _is_dependency_satisfied(self, dependency: str) -> bool:
63
65
  """
64
66
  Check if a single dependency is satisfied.
65
-
67
+
66
68
  Args:
67
69
  dependency: Dependency name or spec
68
-
70
+
69
71
  Returns:
70
72
  True if dependency is satisfied, False otherwise
71
73
  """
@@ -75,38 +77,46 @@ class DependencyManager:
75
77
  return True
76
78
  except ImportError:
77
79
  pass
78
-
80
+
79
81
  # Check if it's installed as a package
80
82
  try:
81
83
  pkg_resources.require(dependency)
82
84
  return True
83
85
  except (pkg_resources.DistributionNotFound, pkg_resources.VersionConflict):
84
86
  pass
85
-
87
+
86
88
  # Check installed packages cache
87
- dep_name = dependency.split('==')[0].split('>=')[0].split('<=')[0].split('!=')[0].split('~=')[0]
89
+ dep_name = (
90
+ dependency.split("==")[0]
91
+ .split(">=")[0]
92
+ .split("<=")[0]
93
+ .split("!=")[0]
94
+ .split("~=")[0]
95
+ )
88
96
  if dep_name.lower() in self._installed_packages:
89
97
  return True
90
-
98
+
91
99
  return False
92
-
93
- def install_dependencies(self, dependencies: List[str], user_install: bool = False) -> Tuple[bool, List[str], List[str]]:
100
+
101
+ def install_dependencies(
102
+ self, dependencies: List[str], user_install: bool = False
103
+ ) -> Tuple[bool, List[str], List[str]]:
94
104
  """
95
105
  Install dependencies using pip.
96
-
106
+
97
107
  Args:
98
108
  dependencies: List of dependency names to install
99
109
  user_install: Whether to install for current user only
100
-
110
+
101
111
  Returns:
102
112
  Tuple of (success, installed_deps, failed_deps)
103
113
  """
104
114
  if not dependencies:
105
115
  return True, [], []
106
-
116
+
107
117
  installed_deps = []
108
118
  failed_deps = []
109
-
119
+
110
120
  for dep in dependencies:
111
121
  try:
112
122
  if self._install_single_dependency(dep, user_install):
@@ -118,92 +128,91 @@ class DependencyManager:
118
128
  except Exception as e:
119
129
  failed_deps.append(dep)
120
130
  logger.error(f"Error installing dependency {dep}: {e}")
121
-
131
+
122
132
  success = len(failed_deps) == 0
123
-
133
+
124
134
  # Reload installed packages cache
125
135
  if success:
126
136
  self._load_installed_packages()
127
-
137
+
128
138
  return success, installed_deps, failed_deps
129
-
130
- def _install_single_dependency(self, dependency: str, user_install: bool = False) -> bool:
139
+
140
+ def _install_single_dependency(
141
+ self, dependency: str, user_install: bool = False
142
+ ) -> bool:
131
143
  """
132
144
  Install a single dependency using pip.
133
-
145
+
134
146
  Args:
135
147
  dependency: Dependency name or spec
136
148
  user_install: Whether to install for current user only
137
-
149
+
138
150
  Returns:
139
151
  True if installation successful, False otherwise
140
152
  """
141
153
  try:
142
154
  # Build pip command
143
155
  cmd = [sys.executable, "-m", "pip", "install"]
144
-
156
+
145
157
  if user_install:
146
158
  cmd.append("--user")
147
-
159
+
148
160
  # Add quiet flag to reduce output
149
161
  cmd.append("--quiet")
150
-
162
+
151
163
  # Add dependency
152
164
  cmd.append(dependency)
153
-
165
+
154
166
  logger.debug(f"Installing dependency: {' '.join(cmd)}")
155
-
167
+
156
168
  # Run pip install
157
169
  result = subprocess.run(
158
- cmd,
159
- capture_output=True,
160
- text=True,
161
- timeout=300 # 5 minutes timeout
170
+ cmd, capture_output=True, text=True, timeout=300 # 5 minutes timeout
162
171
  )
163
-
172
+
164
173
  if result.returncode == 0:
165
174
  logger.debug(f"Successfully installed {dependency}")
166
175
  return True
167
176
  else:
168
177
  logger.error(f"Failed to install {dependency}: {result.stderr}")
169
178
  return False
170
-
179
+
171
180
  except subprocess.TimeoutExpired:
172
181
  logger.error(f"Timeout while installing {dependency}")
173
182
  return False
174
183
  except Exception as e:
175
184
  logger.error(f"Error installing {dependency}: {e}")
176
185
  return False
177
-
186
+
178
187
  def verify_installation(self, dependencies: List[str]) -> Tuple[bool, List[str]]:
179
188
  """
180
189
  Verify that dependencies are properly installed.
181
-
190
+
182
191
  Args:
183
192
  dependencies: List of dependencies to verify
184
-
193
+
185
194
  Returns:
186
195
  Tuple of (all_verified, failed_verifications)
187
196
  """
188
197
  if not dependencies:
189
198
  return True, []
190
-
199
+
191
200
  failed_verifications = []
192
-
201
+
193
202
  for dep in dependencies:
194
203
  if not self._is_dependency_satisfied(dep):
195
204
  failed_verifications.append(dep)
196
-
205
+
197
206
  all_verified = len(failed_verifications) == 0
198
207
  return all_verified, failed_verifications
199
-
208
+
200
209
  def get_dependency_info(self, dependency: str) -> Dict[str, Any]:
201
210
  """
202
211
  Get information about a dependency.
203
-
212
+
204
213
  Args:
205
214
  dependency: Dependency name
206
-
215
+
207
216
  Returns:
208
217
  Dictionary with dependency information
209
218
  """
@@ -211,9 +220,9 @@ class DependencyManager:
211
220
  "name": dependency,
212
221
  "installed": False,
213
222
  "version": None,
214
- "importable": False
223
+ "importable": False,
215
224
  }
216
-
225
+
217
226
  # Check if it's installed
218
227
  try:
219
228
  dist = pkg_resources.get_distribution(dependency)
@@ -221,20 +230,20 @@ class DependencyManager:
221
230
  info["version"] = dist.version
222
231
  except pkg_resources.DistributionNotFound:
223
232
  pass
224
-
233
+
225
234
  # Check if it's importable
226
235
  try:
227
236
  importlib.import_module(dependency)
228
237
  info["importable"] = True
229
238
  except ImportError:
230
239
  pass
231
-
240
+
232
241
  return info
233
-
242
+
234
243
  def list_installed_dependencies(self) -> Dict[str, str]:
235
244
  """
236
245
  Get list of all installed packages.
237
-
246
+
238
247
  Returns:
239
248
  Dictionary mapping package names to versions
240
249
  """
@@ -242,4 +251,4 @@ class DependencyManager:
242
251
 
243
252
 
244
253
  # Global instance
245
- dependency_manager = DependencyManager()
254
+ dependency_manager = DependencyManager()
@@ -14,13 +14,13 @@ from mcp_proxy_adapter.commands.result import SuccessResult
14
14
 
15
15
  class EchoCommandResult(SuccessResult):
16
16
  """Result for echo command."""
17
-
17
+
18
18
  def __init__(self, message: str, timestamp: Optional[str] = None):
19
19
  data = {"message": message}
20
20
  if timestamp:
21
21
  data["timestamp"] = timestamp
22
22
  super().__init__(data=data, message=message)
23
-
23
+
24
24
  @classmethod
25
25
  def get_schema(cls) -> Dict[str, Any]:
26
26
  """Get JSON schema for result."""
@@ -32,18 +32,18 @@ class EchoCommandResult(SuccessResult):
32
32
  "type": "object",
33
33
  "properties": {
34
34
  "message": {"type": "string"},
35
- "timestamp": {"type": "string", "nullable": True}
36
- }
35
+ "timestamp": {"type": "string", "nullable": True},
36
+ },
37
37
  },
38
- "message": {"type": "string"}
38
+ "message": {"type": "string"},
39
39
  },
40
- "required": ["success", "data"]
40
+ "required": ["success", "data"],
41
41
  }
42
42
 
43
43
 
44
44
  class EchoCommand(Command):
45
45
  """Echo command for testing purposes."""
46
-
46
+
47
47
  name = "echo"
48
48
  version = "1.0.0"
49
49
  descr = "Echo command for testing"
@@ -51,17 +51,17 @@ class EchoCommand(Command):
51
51
  author = "Vasiliy Zdanovskiy"
52
52
  email = "vasilyvz@gmail.com"
53
53
  result_class = EchoCommandResult
54
-
54
+
55
55
  async def execute(self, **kwargs) -> EchoCommandResult:
56
56
  """Execute echo command."""
57
57
  message = kwargs.get("message", "Hello, World!")
58
58
  timestamp = kwargs.get("timestamp")
59
-
59
+
60
60
  # Simulate some processing time
61
61
  await asyncio.sleep(0.001)
62
-
62
+
63
63
  return EchoCommandResult(message=message, timestamp=timestamp)
64
-
64
+
65
65
  @classmethod
66
66
  def get_schema(cls) -> Dict[str, Any]:
67
67
  """Get command schema."""
@@ -71,12 +71,12 @@ class EchoCommand(Command):
71
71
  "message": {
72
72
  "type": "string",
73
73
  "description": "Message to echo",
74
- "default": "Hello, World!"
74
+ "default": "Hello, World!",
75
75
  },
76
76
  "timestamp": {
77
77
  "type": "string",
78
78
  "description": "Optional timestamp",
79
- "nullable": True
80
- }
81
- }
79
+ "nullable": True,
80
+ },
81
+ },
82
82
  }