mcp-proxy-adapter 4.1.1__py3-none-any.whl → 6.1.0__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 (253) hide show
  1. mcp_proxy_adapter/__main__.py +12 -0
  2. mcp_proxy_adapter/api/app.py +254 -33
  3. mcp_proxy_adapter/api/handlers.py +32 -6
  4. mcp_proxy_adapter/api/middleware/__init__.py +36 -30
  5. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  6. mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
  7. mcp_proxy_adapter/api/middleware/factory.py +243 -0
  8. mcp_proxy_adapter/api/middleware/logging.py +32 -6
  9. mcp_proxy_adapter/api/middleware/protocol_middleware.py +135 -0
  10. mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
  11. mcp_proxy_adapter/api/middleware/unified_security.py +152 -0
  12. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
  13. mcp_proxy_adapter/commands/__init__.py +19 -4
  14. mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
  15. mcp_proxy_adapter/commands/base.py +66 -32
  16. mcp_proxy_adapter/commands/builtin_commands.py +95 -0
  17. mcp_proxy_adapter/commands/catalog_manager.py +838 -0
  18. mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
  19. mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
  20. mcp_proxy_adapter/commands/command_registry.py +711 -354
  21. mcp_proxy_adapter/commands/dependency_manager.py +245 -0
  22. mcp_proxy_adapter/commands/echo_command.py +81 -0
  23. mcp_proxy_adapter/commands/health_command.py +7 -0
  24. mcp_proxy_adapter/commands/help_command.py +21 -14
  25. mcp_proxy_adapter/commands/hooks.py +200 -167
  26. mcp_proxy_adapter/commands/key_management_command.py +506 -0
  27. mcp_proxy_adapter/commands/load_command.py +176 -0
  28. mcp_proxy_adapter/commands/plugins_command.py +235 -0
  29. mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
  30. mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
  31. mcp_proxy_adapter/commands/reload_command.py +48 -50
  32. mcp_proxy_adapter/commands/result.py +1 -0
  33. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  34. mcp_proxy_adapter/commands/roles_management_command.py +697 -0
  35. mcp_proxy_adapter/commands/security_command.py +488 -0
  36. mcp_proxy_adapter/commands/ssl_setup_command.py +483 -0
  37. mcp_proxy_adapter/commands/token_management_command.py +529 -0
  38. mcp_proxy_adapter/commands/transport_management_command.py +144 -0
  39. mcp_proxy_adapter/commands/unload_command.py +158 -0
  40. mcp_proxy_adapter/config.py +159 -2
  41. mcp_proxy_adapter/core/app_factory.py +326 -0
  42. mcp_proxy_adapter/core/auth_validator.py +606 -0
  43. mcp_proxy_adapter/core/certificate_utils.py +827 -0
  44. mcp_proxy_adapter/core/client_security.py +384 -0
  45. mcp_proxy_adapter/core/config_converter.py +405 -0
  46. mcp_proxy_adapter/core/config_validator.py +218 -0
  47. mcp_proxy_adapter/core/logging.py +19 -3
  48. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  49. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  50. mcp_proxy_adapter/core/protocol_manager.py +235 -0
  51. mcp_proxy_adapter/core/proxy_client.py +602 -0
  52. mcp_proxy_adapter/core/proxy_registration.py +522 -0
  53. mcp_proxy_adapter/core/role_utils.py +426 -0
  54. mcp_proxy_adapter/core/security_adapter.py +370 -0
  55. mcp_proxy_adapter/core/security_factory.py +239 -0
  56. mcp_proxy_adapter/core/security_integration.py +277 -0
  57. mcp_proxy_adapter/core/server_adapter.py +345 -0
  58. mcp_proxy_adapter/core/server_engine.py +364 -0
  59. mcp_proxy_adapter/core/settings.py +1 -0
  60. mcp_proxy_adapter/core/ssl_utils.py +233 -0
  61. mcp_proxy_adapter/core/transport_manager.py +292 -0
  62. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  63. mcp_proxy_adapter/custom_openapi.py +22 -11
  64. mcp_proxy_adapter/examples/README.md +230 -97
  65. mcp_proxy_adapter/examples/README_EN.md +258 -0
  66. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  67. mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
  68. mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
  69. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  70. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  71. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +39 -0
  72. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +25 -0
  73. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  74. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  75. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  76. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  77. mcp_proxy_adapter/examples/cert_config.json +9 -0
  78. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  79. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  80. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  81. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  82. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  83. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  84. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  85. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  86. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  87. mcp_proxy_adapter/examples/certs/client.key +52 -0
  88. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  89. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  90. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  91. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  92. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  93. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  94. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  95. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  96. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  97. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  98. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  99. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  100. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  101. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  102. mcp_proxy_adapter/examples/certs/server.key +52 -0
  103. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  104. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  105. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  106. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  107. mcp_proxy_adapter/examples/certs/user.key +52 -0
  108. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  109. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  110. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  111. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  112. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  113. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  114. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  115. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  116. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  117. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  118. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  119. mcp_proxy_adapter/examples/demo_client.py +341 -0
  120. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  121. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  122. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  123. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  124. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  125. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  126. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  127. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  128. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  129. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  130. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  131. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  132. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  133. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  134. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  135. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  136. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  137. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  138. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  139. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  140. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  141. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  142. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  143. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  144. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  145. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  146. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  147. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  148. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  149. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  150. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  151. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  152. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  153. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  154. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  155. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  156. mcp_proxy_adapter/examples/roles.json +38 -0
  157. mcp_proxy_adapter/examples/run_example.py +81 -0
  158. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  159. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  160. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  161. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  162. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  163. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  164. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  165. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  166. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  167. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  168. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  169. mcp_proxy_adapter/examples/test_examples.py +344 -0
  170. mcp_proxy_adapter/examples/universal_client.py +628 -0
  171. mcp_proxy_adapter/main.py +186 -0
  172. mcp_proxy_adapter/utils/config_generator.py +639 -0
  173. mcp_proxy_adapter/version.py +2 -1
  174. mcp_proxy_adapter-6.1.0.dist-info/METADATA +205 -0
  175. mcp_proxy_adapter-6.1.0.dist-info/RECORD +193 -0
  176. mcp_proxy_adapter-6.1.0.dist-info/entry_points.txt +2 -0
  177. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/licenses/LICENSE +2 -2
  178. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  179. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  180. mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
  181. mcp_proxy_adapter/examples/__init__.py +0 -7
  182. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  183. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  184. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  185. mcp_proxy_adapter/examples/basic_server/config.json +0 -35
  186. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  187. mcp_proxy_adapter/examples/basic_server/server.py +0 -103
  188. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  189. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  190. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
  191. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  192. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  193. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  194. mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
  195. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  196. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  197. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  198. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  199. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  200. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  201. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  202. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  203. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  204. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  205. mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
  206. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  207. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  208. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  209. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  210. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  211. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  212. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  213. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  214. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  215. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  216. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  217. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  218. mcp_proxy_adapter/tests/__init__.py +0 -0
  219. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  220. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  221. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  222. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  223. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  224. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  225. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  226. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  227. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  228. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  229. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  230. mcp_proxy_adapter/tests/conftest.py +0 -131
  231. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  232. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  233. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  234. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  235. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  236. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  237. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  238. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  239. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  240. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  241. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  242. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  243. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  244. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  245. mcp_proxy_adapter/tests/test_config.py +0 -127
  246. mcp_proxy_adapter/tests/test_utils.py +0 -65
  247. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  248. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  249. mcp_proxy_adapter/tests/unit/test_config.py +0 -217
  250. mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
  251. mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
  252. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/WHEEL +0 -0
  253. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,245 @@
1
+ """
2
+ Dependency management system for remote plugins.
3
+
4
+ This module handles automatic installation and verification of plugin dependencies
5
+ using pip and other package management tools.
6
+ """
7
+
8
+ import subprocess
9
+ import sys
10
+ import importlib
11
+ import pkg_resources
12
+ from typing import List, Dict, Any, Optional, Tuple
13
+ from pathlib import Path
14
+
15
+ from mcp_proxy_adapter.core.logging import logger
16
+
17
+
18
+ class DependencyManager:
19
+ """
20
+ Manages plugin dependencies installation and verification.
21
+ """
22
+
23
+ def __init__(self):
24
+ """Initialize dependency manager."""
25
+ self._installed_packages: Dict[str, str] = {}
26
+ self._load_installed_packages()
27
+
28
+ def _load_installed_packages(self) -> None:
29
+ """Load list of currently installed packages."""
30
+ try:
31
+ installed = pkg_resources.working_set
32
+ for dist in installed:
33
+ self._installed_packages[dist.project_name.lower()] = dist.version
34
+ except Exception as e:
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]]:
38
+ """
39
+ Check if dependencies are satisfied.
40
+
41
+ Args:
42
+ dependencies: List of dependency names
43
+
44
+ Returns:
45
+ Tuple of (all_satisfied, missing_deps, installed_deps)
46
+ """
47
+ if not dependencies:
48
+ return True, [], []
49
+
50
+ missing_deps = []
51
+ installed_deps = []
52
+
53
+ for dep in dependencies:
54
+ if self._is_dependency_satisfied(dep):
55
+ installed_deps.append(dep)
56
+ else:
57
+ missing_deps.append(dep)
58
+
59
+ all_satisfied = len(missing_deps) == 0
60
+ return all_satisfied, missing_deps, installed_deps
61
+
62
+ def _is_dependency_satisfied(self, dependency: str) -> bool:
63
+ """
64
+ Check if a single dependency is satisfied.
65
+
66
+ Args:
67
+ dependency: Dependency name or spec
68
+
69
+ Returns:
70
+ True if dependency is satisfied, False otherwise
71
+ """
72
+ # Try to import the module
73
+ try:
74
+ importlib.import_module(dependency)
75
+ return True
76
+ except ImportError:
77
+ pass
78
+
79
+ # Check if it's installed as a package
80
+ try:
81
+ pkg_resources.require(dependency)
82
+ return True
83
+ except (pkg_resources.DistributionNotFound, pkg_resources.VersionConflict):
84
+ pass
85
+
86
+ # Check installed packages cache
87
+ dep_name = dependency.split('==')[0].split('>=')[0].split('<=')[0].split('!=')[0].split('~=')[0]
88
+ if dep_name.lower() in self._installed_packages:
89
+ return True
90
+
91
+ return False
92
+
93
+ def install_dependencies(self, dependencies: List[str], user_install: bool = False) -> Tuple[bool, List[str], List[str]]:
94
+ """
95
+ Install dependencies using pip.
96
+
97
+ Args:
98
+ dependencies: List of dependency names to install
99
+ user_install: Whether to install for current user only
100
+
101
+ Returns:
102
+ Tuple of (success, installed_deps, failed_deps)
103
+ """
104
+ if not dependencies:
105
+ return True, [], []
106
+
107
+ installed_deps = []
108
+ failed_deps = []
109
+
110
+ for dep in dependencies:
111
+ try:
112
+ if self._install_single_dependency(dep, user_install):
113
+ installed_deps.append(dep)
114
+ logger.info(f"Successfully installed dependency: {dep}")
115
+ else:
116
+ failed_deps.append(dep)
117
+ logger.error(f"Failed to install dependency: {dep}")
118
+ except Exception as e:
119
+ failed_deps.append(dep)
120
+ logger.error(f"Error installing dependency {dep}: {e}")
121
+
122
+ success = len(failed_deps) == 0
123
+
124
+ # Reload installed packages cache
125
+ if success:
126
+ self._load_installed_packages()
127
+
128
+ return success, installed_deps, failed_deps
129
+
130
+ def _install_single_dependency(self, dependency: str, user_install: bool = False) -> bool:
131
+ """
132
+ Install a single dependency using pip.
133
+
134
+ Args:
135
+ dependency: Dependency name or spec
136
+ user_install: Whether to install for current user only
137
+
138
+ Returns:
139
+ True if installation successful, False otherwise
140
+ """
141
+ try:
142
+ # Build pip command
143
+ cmd = [sys.executable, "-m", "pip", "install"]
144
+
145
+ if user_install:
146
+ cmd.append("--user")
147
+
148
+ # Add quiet flag to reduce output
149
+ cmd.append("--quiet")
150
+
151
+ # Add dependency
152
+ cmd.append(dependency)
153
+
154
+ logger.debug(f"Installing dependency: {' '.join(cmd)}")
155
+
156
+ # Run pip install
157
+ result = subprocess.run(
158
+ cmd,
159
+ capture_output=True,
160
+ text=True,
161
+ timeout=300 # 5 minutes timeout
162
+ )
163
+
164
+ if result.returncode == 0:
165
+ logger.debug(f"Successfully installed {dependency}")
166
+ return True
167
+ else:
168
+ logger.error(f"Failed to install {dependency}: {result.stderr}")
169
+ return False
170
+
171
+ except subprocess.TimeoutExpired:
172
+ logger.error(f"Timeout while installing {dependency}")
173
+ return False
174
+ except Exception as e:
175
+ logger.error(f"Error installing {dependency}: {e}")
176
+ return False
177
+
178
+ def verify_installation(self, dependencies: List[str]) -> Tuple[bool, List[str]]:
179
+ """
180
+ Verify that dependencies are properly installed.
181
+
182
+ Args:
183
+ dependencies: List of dependencies to verify
184
+
185
+ Returns:
186
+ Tuple of (all_verified, failed_verifications)
187
+ """
188
+ if not dependencies:
189
+ return True, []
190
+
191
+ failed_verifications = []
192
+
193
+ for dep in dependencies:
194
+ if not self._is_dependency_satisfied(dep):
195
+ failed_verifications.append(dep)
196
+
197
+ all_verified = len(failed_verifications) == 0
198
+ return all_verified, failed_verifications
199
+
200
+ def get_dependency_info(self, dependency: str) -> Dict[str, Any]:
201
+ """
202
+ Get information about a dependency.
203
+
204
+ Args:
205
+ dependency: Dependency name
206
+
207
+ Returns:
208
+ Dictionary with dependency information
209
+ """
210
+ info = {
211
+ "name": dependency,
212
+ "installed": False,
213
+ "version": None,
214
+ "importable": False
215
+ }
216
+
217
+ # Check if it's installed
218
+ try:
219
+ dist = pkg_resources.get_distribution(dependency)
220
+ info["installed"] = True
221
+ info["version"] = dist.version
222
+ except pkg_resources.DistributionNotFound:
223
+ pass
224
+
225
+ # Check if it's importable
226
+ try:
227
+ importlib.import_module(dependency)
228
+ info["importable"] = True
229
+ except ImportError:
230
+ pass
231
+
232
+ return info
233
+
234
+ def list_installed_dependencies(self) -> Dict[str, str]:
235
+ """
236
+ Get list of all installed packages.
237
+
238
+ Returns:
239
+ Dictionary mapping package names to versions
240
+ """
241
+ return self._installed_packages.copy()
242
+
243
+
244
+ # Global instance
245
+ dependency_manager = DependencyManager()
@@ -0,0 +1,81 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Echo command for testing purposes.
6
+ """
7
+
8
+ import asyncio
9
+ from typing import Any, Dict, Optional
10
+
11
+ from mcp_proxy_adapter.commands.base import Command
12
+ from mcp_proxy_adapter.commands.result import SuccessResult
13
+
14
+
15
+ class EchoCommandResult(SuccessResult):
16
+ """Result for echo command."""
17
+
18
+ def __init__(self, message: str, timestamp: Optional[str] = None):
19
+ data = {"message": message}
20
+ if timestamp:
21
+ data["timestamp"] = timestamp
22
+ super().__init__(data=data, message=message)
23
+
24
+ @classmethod
25
+ def get_schema(cls) -> Dict[str, Any]:
26
+ """Get JSON schema for result."""
27
+ return {
28
+ "type": "object",
29
+ "properties": {
30
+ "success": {"type": "boolean"},
31
+ "data": {
32
+ "type": "object",
33
+ "properties": {
34
+ "message": {"type": "string"},
35
+ "timestamp": {"type": "string", "nullable": True}
36
+ }
37
+ },
38
+ "message": {"type": "string"}
39
+ },
40
+ "required": ["success", "data"]
41
+ }
42
+
43
+
44
+ class EchoCommand(Command):
45
+ """Echo command for testing purposes."""
46
+
47
+ name = "echo"
48
+ version = "1.0.0"
49
+ descr = "Echo command for testing"
50
+ category = "testing"
51
+ author = "Vasiliy Zdanovskiy"
52
+ email = "vasilyvz@gmail.com"
53
+ result_class = EchoCommandResult
54
+
55
+ async def execute(self, **kwargs) -> EchoCommandResult:
56
+ """Execute echo command."""
57
+ message = kwargs.get("message", "Hello, World!")
58
+ timestamp = kwargs.get("timestamp")
59
+
60
+ # Simulate some processing time
61
+ await asyncio.sleep(0.001)
62
+
63
+ return EchoCommandResult(message=message, timestamp=timestamp)
64
+
65
+ def get_schema(self) -> Dict[str, Any]:
66
+ """Get command schema."""
67
+ return {
68
+ "type": "object",
69
+ "properties": {
70
+ "message": {
71
+ "type": "string",
72
+ "description": "Message to echo",
73
+ "default": "Hello, World!"
74
+ },
75
+ "timestamp": {
76
+ "type": "string",
77
+ "description": "Optional timestamp",
78
+ "nullable": True
79
+ }
80
+ }
81
+ }
@@ -12,6 +12,7 @@ from typing import Dict, Any, Optional
12
12
  from mcp_proxy_adapter.commands.base import Command
13
13
  from mcp_proxy_adapter.commands.result import CommandResult, SuccessResult
14
14
  from mcp_proxy_adapter.commands.command_registry import registry
15
+ from mcp_proxy_adapter.core.proxy_registration import get_proxy_registration_status
15
16
 
16
17
 
17
18
  class HealthResult(SuccessResult):
@@ -117,6 +118,12 @@ class HealthCommand(Command):
117
118
  },
118
119
  "commands": {
119
120
  "registered_count": len(registry.get_all_commands())
121
+ },
122
+ "proxy_registration": {
123
+ "enabled": get_proxy_registration_status().get("enabled", False),
124
+ "registered": get_proxy_registration_status().get("registered", False),
125
+ "server_key": get_proxy_registration_status().get("server_key"),
126
+ "proxy_url": get_proxy_registration_status().get("proxy_url")
120
127
  }
121
128
  }
122
129
  )
@@ -46,13 +46,15 @@ class HelpResult(CommandResult):
46
46
  if self.command_info is not None:
47
47
  logger.debug(f"HelpResult.to_dict: returning command_info for {self.command_info.get('name', 'unknown')}")
48
48
  # Делаем безопасное получение всех полей с дефолтными значениями
49
+ metadata = self.command_info.get("metadata", {})
50
+ schema = self.command_info.get("schema", {})
49
51
  return {
50
52
  "cmdname": self.command_info.get("name", "unknown"),
51
53
  "info": {
52
- "description": self.command_info.get("description", ""),
53
- "summary": self.command_info.get("summary", ""),
54
- "params": self.command_info.get("params", {}),
55
- "examples": self.command_info.get("examples", [])
54
+ "description": metadata.get("description", ""),
55
+ "summary": metadata.get("summary", ""),
56
+ "params": schema.get("properties", {}),
57
+ "examples": metadata.get("examples", [])
56
58
  }
57
59
  }
58
60
 
@@ -190,14 +192,16 @@ class HelpCommand(Command):
190
192
  if cmdname is not None and cmdname != "":
191
193
  logger.debug(f"Обработка запроса для конкретной команды: {cmdname}")
192
194
  try:
193
- # Get command metadata from registry
194
- command_metadata = registry.get_command_metadata(cmdname)
195
+ # Get command info from registry
196
+ command_info = registry.get_command_info(cmdname)
197
+ if command_info is None:
198
+ raise NotFoundError(f"Command '{cmdname}' not found")
195
199
  logger.debug(f"Получены метаданные для команды {cmdname}")
196
- return HelpResult(command_info=command_metadata)
200
+ return HelpResult(command_info=command_info)
197
201
  except NotFoundError:
198
202
  logger.warning(f"Команда '{cmdname}' не найдена")
199
203
  # Получаем список всех команд
200
- all_commands = list(registry.get_all_metadata().keys())
204
+ all_commands = list(registry.get_all_commands().keys())
201
205
  if all_commands:
202
206
  example_cmd = all_commands[0]
203
207
  example = {
@@ -218,9 +222,9 @@ class HelpCommand(Command):
218
222
  # Otherwise, return information about all available commands
219
223
  logger.debug("Обработка запроса для всех команд")
220
224
 
221
- # Get metadata for all commands
222
- all_metadata = registry.get_all_metadata()
223
- logger.debug(f"Получены метаданные для {len(all_metadata)} команд")
225
+ # Get info for all commands
226
+ all_commands_info = registry.get_all_commands_info()
227
+ logger.debug(f"Получены метаданные для {len(all_commands_info.get('commands', {}))} команд")
224
228
 
225
229
  # Prepare response format with tool metadata
226
230
  result = {
@@ -240,17 +244,20 @@ class HelpCommand(Command):
240
244
  }
241
245
 
242
246
  # Add brief information about commands
243
- for name, metadata in all_metadata.items():
247
+ commands_data = all_commands_info.get("commands", {})
248
+ for name, command_info in commands_data.items():
244
249
  try:
245
250
  logger.debug(f"Обработка метаданных команды {name}")
246
251
  # Безопасное получение параметров с проверкой на наличие ключей
252
+ metadata = command_info.get("metadata", {})
253
+ schema = command_info.get("schema", {})
247
254
  result["commands"][name] = {
248
255
  "summary": metadata.get("summary", ""),
249
- "params_count": len(metadata.get("params", {}))
256
+ "params_count": len(schema.get("properties", {}))
250
257
  }
251
258
  except Exception as e:
252
259
  logger.error(f"Ошибка при обработке метаданных команды {name}: {e}")
253
- logger.debug(f"Метаданные команды {name}: {metadata}")
260
+ logger.debug(f"Метаданные команды {name}: {command_info}")
254
261
  # Пропускаем проблемную команду
255
262
  continue
256
263