mcp-proxy-adapter 6.9.28__py3-none-any.whl → 6.9.30__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.

Potentially problematic release.


This version of mcp-proxy-adapter might be problematic. Click here for more details.

Files changed (212) hide show
  1. mcp_proxy_adapter/__init__.py +10 -0
  2. mcp_proxy_adapter/__main__.py +8 -21
  3. mcp_proxy_adapter/api/app.py +10 -913
  4. mcp_proxy_adapter/api/core/__init__.py +18 -0
  5. mcp_proxy_adapter/api/core/app_factory.py +243 -0
  6. mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
  7. mcp_proxy_adapter/api/core/registration_manager.py +166 -0
  8. mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
  9. mcp_proxy_adapter/api/handlers.py +78 -199
  10. mcp_proxy_adapter/api/middleware/__init__.py +1 -44
  11. mcp_proxy_adapter/api/middleware/base.py +0 -42
  12. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +0 -85
  13. mcp_proxy_adapter/api/middleware/error_handling.py +1 -127
  14. mcp_proxy_adapter/api/middleware/factory.py +0 -94
  15. mcp_proxy_adapter/api/middleware/logging.py +0 -112
  16. mcp_proxy_adapter/api/middleware/performance.py +0 -35
  17. mcp_proxy_adapter/api/middleware/protocol_middleware.py +2 -98
  18. mcp_proxy_adapter/api/middleware/transport_middleware.py +0 -37
  19. mcp_proxy_adapter/api/middleware/unified_security.py +10 -10
  20. mcp_proxy_adapter/api/middleware/user_info_middleware.py +0 -118
  21. mcp_proxy_adapter/api/openapi/__init__.py +21 -0
  22. mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
  23. mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
  24. mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
  25. mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
  26. mcp_proxy_adapter/api/schemas.py +0 -61
  27. mcp_proxy_adapter/api/tool_integration.py +0 -117
  28. mcp_proxy_adapter/api/tools.py +0 -46
  29. mcp_proxy_adapter/cli/__init__.py +12 -0
  30. mcp_proxy_adapter/cli/commands/__init__.py +15 -0
  31. mcp_proxy_adapter/cli/commands/client.py +100 -0
  32. mcp_proxy_adapter/cli/commands/config_generate.py +21 -0
  33. mcp_proxy_adapter/cli/commands/config_validate.py +36 -0
  34. mcp_proxy_adapter/cli/commands/generate.py +259 -0
  35. mcp_proxy_adapter/cli/commands/server.py +174 -0
  36. mcp_proxy_adapter/cli/commands/sets.py +128 -0
  37. mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
  38. mcp_proxy_adapter/cli/examples/__init__.py +8 -0
  39. mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
  40. mcp_proxy_adapter/cli/examples/https_token.py +96 -0
  41. mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
  42. mcp_proxy_adapter/cli/main.py +63 -0
  43. mcp_proxy_adapter/cli/parser.py +324 -0
  44. mcp_proxy_adapter/cli/validators.py +231 -0
  45. mcp_proxy_adapter/client/jsonrpc_client.py +406 -0
  46. mcp_proxy_adapter/client/proxy.py +45 -0
  47. mcp_proxy_adapter/commands/__init__.py +44 -28
  48. mcp_proxy_adapter/commands/auth_validation_command.py +7 -344
  49. mcp_proxy_adapter/commands/base.py +19 -43
  50. mcp_proxy_adapter/commands/builtin_commands.py +0 -75
  51. mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
  52. mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
  53. mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
  54. mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
  55. mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
  56. mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
  57. mcp_proxy_adapter/commands/catalog_manager.py +58 -928
  58. mcp_proxy_adapter/commands/cert_monitor_command.py +0 -88
  59. mcp_proxy_adapter/commands/certificate_management_command.py +0 -45
  60. mcp_proxy_adapter/commands/command_registry.py +172 -904
  61. mcp_proxy_adapter/commands/config_command.py +0 -28
  62. mcp_proxy_adapter/commands/dependency_container.py +1 -70
  63. mcp_proxy_adapter/commands/dependency_manager.py +0 -128
  64. mcp_proxy_adapter/commands/echo_command.py +0 -34
  65. mcp_proxy_adapter/commands/health_command.py +0 -3
  66. mcp_proxy_adapter/commands/help_command.py +0 -159
  67. mcp_proxy_adapter/commands/hooks.py +0 -137
  68. mcp_proxy_adapter/commands/key_management_command.py +0 -25
  69. mcp_proxy_adapter/commands/load_command.py +7 -78
  70. mcp_proxy_adapter/commands/plugins_command.py +0 -16
  71. mcp_proxy_adapter/commands/protocol_management_command.py +0 -28
  72. mcp_proxy_adapter/commands/proxy_registration_command.py +0 -88
  73. mcp_proxy_adapter/commands/queue_commands.py +750 -0
  74. mcp_proxy_adapter/commands/registration_status_command.py +0 -43
  75. mcp_proxy_adapter/commands/registry/__init__.py +18 -0
  76. mcp_proxy_adapter/commands/registry/command_info.py +103 -0
  77. mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
  78. mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
  79. mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
  80. mcp_proxy_adapter/commands/reload_command.py +0 -80
  81. mcp_proxy_adapter/commands/result.py +25 -77
  82. mcp_proxy_adapter/commands/role_test_command.py +0 -44
  83. mcp_proxy_adapter/commands/roles_management_command.py +0 -199
  84. mcp_proxy_adapter/commands/security_command.py +0 -30
  85. mcp_proxy_adapter/commands/settings_command.py +0 -68
  86. mcp_proxy_adapter/commands/ssl_setup_command.py +0 -42
  87. mcp_proxy_adapter/commands/token_management_command.py +0 -1
  88. mcp_proxy_adapter/commands/transport_management_command.py +0 -20
  89. mcp_proxy_adapter/commands/unload_command.py +0 -71
  90. mcp_proxy_adapter/config.py +15 -626
  91. mcp_proxy_adapter/core/__init__.py +5 -39
  92. mcp_proxy_adapter/core/app_factory.py +14 -36
  93. mcp_proxy_adapter/core/app_runner.py +0 -27
  94. mcp_proxy_adapter/core/auth_validator.py +1 -93
  95. mcp_proxy_adapter/core/certificate/__init__.py +20 -0
  96. mcp_proxy_adapter/core/certificate/certificate_creator.py +371 -0
  97. mcp_proxy_adapter/core/certificate/certificate_extractor.py +183 -0
  98. mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
  99. mcp_proxy_adapter/core/certificate/certificate_validator.py +110 -0
  100. mcp_proxy_adapter/core/certificate/ssl_context_manager.py +70 -0
  101. mcp_proxy_adapter/core/certificate_utils.py +64 -903
  102. mcp_proxy_adapter/core/client.py +10 -9
  103. mcp_proxy_adapter/core/client_manager.py +0 -19
  104. mcp_proxy_adapter/core/client_security.py +0 -2
  105. mcp_proxy_adapter/core/config/__init__.py +18 -0
  106. mcp_proxy_adapter/core/config/config.py +195 -0
  107. mcp_proxy_adapter/core/config/config_factory.py +22 -0
  108. mcp_proxy_adapter/core/config/config_loader.py +66 -0
  109. mcp_proxy_adapter/core/config/feature_manager.py +31 -0
  110. mcp_proxy_adapter/core/config/simple_config.py +112 -0
  111. mcp_proxy_adapter/core/config/simple_config_generator.py +50 -0
  112. mcp_proxy_adapter/core/config/simple_config_validator.py +96 -0
  113. mcp_proxy_adapter/core/config_converter.py +0 -186
  114. mcp_proxy_adapter/core/config_validator.py +96 -1238
  115. mcp_proxy_adapter/core/errors.py +7 -42
  116. mcp_proxy_adapter/core/job_manager.py +54 -0
  117. mcp_proxy_adapter/core/logging.py +2 -22
  118. mcp_proxy_adapter/core/mtls_asgi.py +0 -20
  119. mcp_proxy_adapter/core/mtls_asgi_app.py +0 -12
  120. mcp_proxy_adapter/core/mtls_proxy.py +0 -80
  121. mcp_proxy_adapter/core/mtls_server.py +3 -173
  122. mcp_proxy_adapter/core/protocol_manager.py +1 -191
  123. mcp_proxy_adapter/core/proxy/__init__.py +22 -0
  124. mcp_proxy_adapter/core/proxy/auth_manager.py +27 -0
  125. mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +137 -0
  126. mcp_proxy_adapter/core/proxy/registration_client.py +60 -0
  127. mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
  128. mcp_proxy_adapter/core/proxy_client.py +0 -1
  129. mcp_proxy_adapter/core/proxy_registration.py +36 -913
  130. mcp_proxy_adapter/core/role_utils.py +0 -308
  131. mcp_proxy_adapter/core/security_adapter.py +1 -36
  132. mcp_proxy_adapter/core/security_factory.py +1 -150
  133. mcp_proxy_adapter/core/security_integration.py +0 -33
  134. mcp_proxy_adapter/core/server_adapter.py +1 -40
  135. mcp_proxy_adapter/core/server_engine.py +2 -173
  136. mcp_proxy_adapter/core/settings.py +0 -127
  137. mcp_proxy_adapter/core/signal_handler.py +0 -65
  138. mcp_proxy_adapter/core/ssl_utils.py +19 -137
  139. mcp_proxy_adapter/core/transport_manager.py +0 -151
  140. mcp_proxy_adapter/core/unified_config_adapter.py +1 -193
  141. mcp_proxy_adapter/core/utils.py +1 -182
  142. mcp_proxy_adapter/core/validation/__init__.py +21 -0
  143. mcp_proxy_adapter/core/validation/config_validator.py +211 -0
  144. mcp_proxy_adapter/core/validation/file_validator.py +73 -0
  145. mcp_proxy_adapter/core/validation/protocol_validator.py +191 -0
  146. mcp_proxy_adapter/core/validation/security_validator.py +58 -0
  147. mcp_proxy_adapter/core/validation/validation_result.py +27 -0
  148. mcp_proxy_adapter/custom_openapi.py +33 -652
  149. mcp_proxy_adapter/examples/bugfix_certificate_config.py +0 -23
  150. mcp_proxy_adapter/examples/check_config.py +0 -2
  151. mcp_proxy_adapter/examples/client_usage_example.py +164 -0
  152. mcp_proxy_adapter/examples/config_builder.py +13 -2
  153. mcp_proxy_adapter/examples/config_cli.py +0 -1
  154. mcp_proxy_adapter/examples/create_test_configs.py +0 -46
  155. mcp_proxy_adapter/examples/debug_request_state.py +0 -1
  156. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -47
  157. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -45
  158. mcp_proxy_adapter/examples/full_application/commands/echo_command.py +0 -12
  159. mcp_proxy_adapter/examples/full_application/commands/help_command.py +0 -12
  160. mcp_proxy_adapter/examples/full_application/commands/list_command.py +0 -7
  161. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +0 -2
  162. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -59
  163. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -54
  164. mcp_proxy_adapter/examples/full_application/main.py +186 -150
  165. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +0 -107
  166. mcp_proxy_adapter/examples/full_application/test_minimal_server.py +0 -24
  167. mcp_proxy_adapter/examples/full_application/test_server.py +0 -58
  168. mcp_proxy_adapter/examples/generate_config.py +65 -11
  169. mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
  170. mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
  171. mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
  172. mcp_proxy_adapter/examples/queue_server_example.py +85 -0
  173. mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
  174. mcp_proxy_adapter/examples/required_certificates.py +0 -2
  175. mcp_proxy_adapter/examples/run_full_test_suite.py +0 -29
  176. mcp_proxy_adapter/examples/run_proxy_server.py +31 -71
  177. mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -27
  178. mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
  179. mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
  180. mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
  181. mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
  182. mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
  183. mcp_proxy_adapter/examples/security_test_client.py +24 -1075
  184. mcp_proxy_adapter/examples/setup/__init__.py +24 -0
  185. mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
  186. mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
  187. mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
  188. mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
  189. mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
  190. mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
  191. mcp_proxy_adapter/examples/setup_test_environment.py +133 -1425
  192. mcp_proxy_adapter/examples/test_config.py +0 -3
  193. mcp_proxy_adapter/examples/test_config_builder.py +25 -405
  194. mcp_proxy_adapter/examples/test_examples.py +0 -1
  195. mcp_proxy_adapter/examples/test_framework_complete.py +0 -2
  196. mcp_proxy_adapter/examples/test_mcp_server.py +0 -1
  197. mcp_proxy_adapter/examples/test_protocol_examples.py +0 -1
  198. mcp_proxy_adapter/examples/universal_client.py +0 -6
  199. mcp_proxy_adapter/examples/update_config_certificates.py +0 -1
  200. mcp_proxy_adapter/examples/validate_generator_compatibility.py +0 -1
  201. mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +0 -187
  202. mcp_proxy_adapter/integrations/__init__.py +25 -0
  203. mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
  204. mcp_proxy_adapter/main.py +70 -62
  205. mcp_proxy_adapter/openapi.py +0 -22
  206. mcp_proxy_adapter/version.py +1 -1
  207. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/METADATA +2 -1
  208. mcp_proxy_adapter-6.9.30.dist-info/RECORD +235 -0
  209. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/entry_points.txt +1 -1
  210. mcp_proxy_adapter-6.9.28.dist-info/RECORD +0 -149
  211. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/WHEEL +0 -0
  212. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/top_level.txt +0 -0
@@ -23,44 +23,8 @@ class RegistrationStatusCommandResult(SuccessResult):
23
23
  status: Dict[str, Any]
24
24
  message: str = "Registration status retrieved successfully"
25
25
 
26
- def to_dict(self) -> Dict[str, Any]:
27
- """Convert result to dictionary."""
28
- return {
29
- "success": True,
30
- "status": self.status,
31
- "message": self.message,
32
- }
33
26
 
34
27
  @classmethod
35
- def get_schema(cls) -> Dict[str, Any]:
36
- """Get JSON schema for result."""
37
- return {
38
- "type": "object",
39
- "properties": {
40
- "success": {
41
- "type": "boolean",
42
- "description": "Whether the command was successful",
43
- },
44
- "status": {
45
- "type": "object",
46
- "description": "Registration status information",
47
- "properties": {
48
- "state": {"type": "string", "description": "Current registration state"},
49
- "server_key": {"type": "string", "description": "Server key if registered"},
50
- "last_attempt": {"type": "number", "description": "Timestamp of last registration attempt"},
51
- "last_success": {"type": "number", "description": "Timestamp of last successful registration"},
52
- "last_error": {"type": "string", "description": "Last error message"},
53
- "attempt_count": {"type": "integer", "description": "Total registration attempts"},
54
- "success_count": {"type": "integer", "description": "Total successful registrations"},
55
- "heartbeat_enabled": {"type": "boolean", "description": "Whether heartbeat is enabled"},
56
- "heartbeat_interval": {"type": "integer", "description": "Heartbeat interval in seconds"},
57
- "thread_alive": {"type": "boolean", "description": "Whether registration thread is alive"},
58
- },
59
- },
60
- "message": {"type": "string", "description": "Result message"},
61
- },
62
- "required": ["success", "status", "message"],
63
- }
64
28
 
65
29
 
66
30
  class RegistrationStatusCommand(Command):
@@ -110,10 +74,3 @@ class RegistrationStatusCommand(Command):
110
74
  )
111
75
 
112
76
  @classmethod
113
- def get_schema(cls) -> Dict[str, Any]:
114
- """Get JSON schema for command parameters."""
115
- return {
116
- "type": "object",
117
- "properties": {},
118
- "additionalProperties": False,
119
- }
@@ -0,0 +1,18 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Command registry package for MCP Proxy Adapter.
6
+ """
7
+
8
+ from .command_registry import CommandRegistry
9
+ from .command_loader import CommandLoader
10
+ from .command_manager import CommandManager
11
+ from .command_info import CommandInfo
12
+
13
+ __all__ = [
14
+ "CommandRegistry",
15
+ "CommandLoader",
16
+ "CommandManager",
17
+ "CommandInfo",
18
+ ]
@@ -0,0 +1,103 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Command information utilities for MCP Proxy Adapter.
6
+ """
7
+
8
+ from typing import Any, Dict, List, Optional, Type
9
+
10
+ from mcp_proxy_adapter.commands.base import Command
11
+ from mcp_proxy_adapter.core.logging import get_global_logger
12
+
13
+
14
+ class CommandInfo:
15
+ """Utilities for getting command information."""
16
+
17
+ def __init__(self):
18
+ """Initialize command info utilities."""
19
+ self.logger = get_global_logger()
20
+
21
+
22
+ def get_command_info(self, command_name: str, command_class: Type[Command], command_types: Dict[str, str]) -> Optional[Dict[str, Any]]:
23
+ """
24
+ Get detailed information about a specific command.
25
+
26
+ Args:
27
+ command_name: Name of the command
28
+ command_class: Command class
29
+ command_types: Dictionary mapping command names to types
30
+
31
+ Returns:
32
+ Dictionary with command information or None if not found
33
+ """
34
+ try:
35
+ cmd_type = command_types.get(command_name, "unknown")
36
+
37
+ info = {
38
+ "name": command_name,
39
+ "type": cmd_type,
40
+ "class": command_class.__name__,
41
+ "module": command_class.__module__,
42
+ "file": getattr(command_class, "__file__", None),
43
+ "description": getattr(command_class, "__doc__", None),
44
+ "methods": self._get_command_methods(command_class),
45
+ "attributes": self._get_command_attributes(command_class),
46
+ }
47
+
48
+ # Get command-specific information
49
+ if hasattr(command_class, "get_schema"):
50
+ try:
51
+ info["schema"] = command_class.get_schema()
52
+ except Exception as e:
53
+ self.logger.warning(f"Failed to get schema for {command_name}: {e}")
54
+
55
+ return info
56
+
57
+ except Exception as e:
58
+ self.logger.error(f"Failed to get info for command {command_name}: {e}")
59
+ return None
60
+
61
+ def _get_command_methods(self, command_class: Type[Command]) -> List[Dict[str, Any]]:
62
+ """
63
+ Get information about command methods.
64
+
65
+ Args:
66
+ command_class: Command class
67
+
68
+ Returns:
69
+ List of method information
70
+ """
71
+ methods = []
72
+
73
+ for name, method in command_class.__dict__.items():
74
+ if callable(method) and not name.startswith("_"):
75
+ methods.append({
76
+ "name": name,
77
+ "description": getattr(method, "__doc__", None),
78
+ "is_async": hasattr(method, "__code__") and method.__code__.co_flags & 0x80, # CO_ITERABLE_COROUTINE
79
+ })
80
+
81
+ return methods
82
+
83
+ def _get_command_attributes(self, command_class: Type[Command]) -> Dict[str, Any]:
84
+ """
85
+ Get information about command attributes.
86
+
87
+ Args:
88
+ command_class: Command class
89
+
90
+ Returns:
91
+ Dictionary of attribute information
92
+ """
93
+ attributes = {}
94
+
95
+ for name, value in command_class.__dict__.items():
96
+ if not callable(value) and not name.startswith("_"):
97
+ attributes[name] = {
98
+ "value": value,
99
+ "type": type(value).__name__,
100
+ }
101
+
102
+ return attributes
103
+
@@ -0,0 +1,207 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Command loading utilities for MCP Proxy Adapter.
6
+ """
7
+
8
+ import importlib
9
+ import importlib.util
10
+ import inspect
11
+ import os
12
+ import tempfile
13
+ import urllib.parse
14
+ from pathlib import Path
15
+
16
+ from mcp_proxy_adapter.commands.base import Command
17
+ from mcp_proxy_adapter.core.logging import get_global_logger
18
+
19
+ try:
20
+ import requests
21
+ REQUESTS_AVAILABLE = True
22
+ except ImportError:
23
+ REQUESTS_AVAILABLE = False
24
+ get_global_logger().warning("requests library not available, HTTP/HTTPS loading will not work")
25
+
26
+
27
+ class CommandLoader:
28
+ """Loader for commands from various sources."""
29
+
30
+ def __init__(self):
31
+ """Initialize command loader."""
32
+ self.logger = get_global_logger()
33
+
34
+
35
+ def _load_command_with_registry_check(self, source: str) -> Dict[str, Any]:
36
+ """
37
+ Load command with remote registry check.
38
+
39
+ Args:
40
+ source: Local path or command name
41
+
42
+ Returns:
43
+ Dictionary with loading result information
44
+ """
45
+ try:
46
+ from mcp_proxy_adapter.commands.catalog_manager import CatalogManager
47
+ from mcp_proxy_adapter.config import get_config
48
+
49
+ # Get configuration
50
+ config_obj = get_config()
51
+
52
+ # Get remote registry
53
+ plugin_servers = config_obj.get("commands.plugin_servers", [])
54
+ catalog_dir = "./catalog"
55
+
56
+ if plugin_servers:
57
+ # Initialize catalog manager
58
+ catalog_manager = CatalogManager(catalog_dir)
59
+
60
+ # Check if source is a command name in registry
61
+ if not os.path.exists(source) and not source.endswith("_command.py"):
62
+ # Try to find in remote registry
63
+ for server_url in plugin_servers:
64
+ try:
65
+ server_catalog = catalog_manager.get_catalog_from_server(
66
+ server_url
67
+ )
68
+ if source in server_catalog:
69
+ server_cmd = server_catalog[source]
70
+ # Download from registry
71
+ if catalog_manager._download_command(
72
+ source, server_cmd
73
+ ):
74
+ source = str(
75
+ catalog_manager.commands_dir
76
+ / f"{source}_command.py"
77
+ )
78
+ break
79
+ except Exception as e:
80
+ self.logger.warning(
81
+ f"Failed to check registry {server_url}: {e}"
82
+ )
83
+
84
+ # Load from local file
85
+ return self._load_command_from_file(source)
86
+
87
+ except Exception as e:
88
+ self.logger.error(f"Failed to load command with registry check: {e}")
89
+ return {"success": False, "commands_loaded": 0, "error": str(e)}
90
+
91
+ def _load_command_from_url(self, url: str) -> Dict[str, Any]:
92
+ """
93
+ Load command from HTTP/HTTPS URL.
94
+
95
+ Args:
96
+ url: URL to load command from
97
+
98
+ Returns:
99
+ Dictionary with loading result information
100
+ """
101
+ if not REQUESTS_AVAILABLE:
102
+ error_msg = "requests library not available, cannot load from URL"
103
+ self.logger.error(error_msg)
104
+ return {
105
+ "success": False,
106
+ "error": error_msg,
107
+ "commands_loaded": 0,
108
+ "source": url,
109
+ }
110
+
111
+ try:
112
+ # Download command file
113
+ response = requests.get(url, timeout=30)
114
+ response.raise_for_status()
115
+
116
+ # Create temporary file
117
+ with tempfile.NamedTemporaryFile(
118
+ mode="w", suffix="_command.py", delete=False
119
+ ) as temp_file:
120
+ temp_file.write(response.text)
121
+ temp_file_path = temp_file.name
122
+
123
+ try:
124
+ # Load from temporary file
125
+ result = self._load_command_from_file(temp_file_path)
126
+ result["source"] = url
127
+ return result
128
+ finally:
129
+ # Clean up temporary file
130
+ os.unlink(temp_file_path)
131
+
132
+ except Exception as e:
133
+ self.logger.error(f"Failed to load command from URL {url}: {e}")
134
+ return {
135
+ "success": False,
136
+ "error": str(e),
137
+ "commands_loaded": 0,
138
+ "source": url,
139
+ }
140
+
141
+ def _load_command_from_file(self, file_path: str) -> Dict[str, Any]:
142
+ """
143
+ Load command from local file.
144
+
145
+ Args:
146
+ file_path: Path to command file
147
+
148
+ Returns:
149
+ Dictionary with loading result information
150
+ """
151
+ try:
152
+ if not os.path.exists(file_path):
153
+ return {
154
+ "success": False,
155
+ "error": f"File not found: {file_path}",
156
+ "commands_loaded": 0,
157
+ "source": file_path,
158
+ }
159
+
160
+ # Load module from file
161
+ spec = importlib.util.spec_from_file_location(
162
+ f"command_{Path(file_path).stem}", file_path
163
+ )
164
+ if spec is None or spec.loader is None:
165
+ return {
166
+ "success": False,
167
+ "error": f"Could not load module from {file_path}",
168
+ "commands_loaded": 0,
169
+ "source": file_path,
170
+ }
171
+
172
+ module = importlib.util.module_from_spec(spec)
173
+ spec.loader.exec_module(module)
174
+
175
+ # Find command classes in module
176
+ commands = []
177
+ for name, obj in inspect.getmembers(module):
178
+ if (
179
+ inspect.isclass(obj)
180
+ and issubclass(obj, Command)
181
+ and obj != Command
182
+ ):
183
+ commands.append(obj)
184
+
185
+ if not commands:
186
+ return {
187
+ "success": False,
188
+ "error": f"No command classes found in {file_path}",
189
+ "commands_loaded": 0,
190
+ "source": file_path,
191
+ }
192
+
193
+ return {
194
+ "success": True,
195
+ "commands": commands,
196
+ "commands_loaded": len(commands),
197
+ "source": file_path,
198
+ }
199
+
200
+ except Exception as e:
201
+ self.logger.error(f"Failed to load command from file {file_path}: {e}")
202
+ return {
203
+ "success": False,
204
+ "error": str(e),
205
+ "commands_loaded": 0,
206
+ "source": file_path,
207
+ }
@@ -0,0 +1,119 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Command management utilities for MCP Proxy Adapter.
6
+ """
7
+
8
+ import os
9
+
10
+ from mcp_proxy_adapter.commands.base import Command
11
+ from mcp_proxy_adapter.core.logging import get_global_logger
12
+
13
+
14
+ class CommandManager:
15
+ """Manager for command operations."""
16
+
17
+ def __init__(self):
18
+ """Initialize command manager."""
19
+ self.logger = get_global_logger()
20
+
21
+ def _get_command_name(self, command_class: Type[Command]) -> str:
22
+ """
23
+ Get command name from command class.
24
+
25
+ Args:
26
+ command_class: Command class
27
+
28
+ Returns:
29
+ Command name
30
+ """
31
+ # Try to get name from class attribute
32
+ if hasattr(command_class, "name") and command_class.name:
33
+ return command_class.name
34
+
35
+ # Fallback to class name
36
+ class_name = command_class.__name__
37
+ if class_name.endswith("Command"):
38
+ return class_name[:-7].lower() # Remove "Command" suffix
39
+ return class_name.lower()
40
+
41
+
42
+ def get_command(self, command_name: str, commands: Dict[str, Type[Command]]) -> Type[Command]:
43
+ """
44
+ Get command class by name.
45
+
46
+ Args:
47
+ command_name: Name of the command
48
+ commands: Dictionary of registered commands
49
+
50
+ Returns:
51
+ Command class
52
+
53
+ Raises:
54
+ NotFoundError: If command not found
55
+ """
56
+ if command_name not in commands:
57
+ from mcp_proxy_adapter.core.errors import NotFoundError
58
+ raise NotFoundError(f"Command '{command_name}' not found")
59
+ return commands[command_name]
60
+
61
+
62
+
63
+
64
+
65
+
66
+ def clear(self, commands: Dict[str, Type[Command]], instances: Dict[str, Command], command_types: Dict[str, str]) -> None:
67
+ """
68
+ Clear all commands and instances.
69
+
70
+ Args:
71
+ commands: Dictionary of registered commands
72
+ instances: Dictionary of command instances
73
+ command_types: Dictionary mapping command names to types
74
+ """
75
+ commands.clear()
76
+ instances.clear()
77
+ command_types.clear()
78
+
79
+
80
+ def _load_commands_from_directory(self, directory: str, commands: Dict[str, Type[Command]], command_types: Dict[str, str], cmd_type: str) -> int:
81
+ """
82
+ Load commands from directory.
83
+
84
+ Args:
85
+ directory: Directory to load commands from
86
+ commands: Dictionary of registered commands
87
+ command_types: Dictionary mapping command names to types
88
+ cmd_type: Type of commands being loaded
89
+
90
+ Returns:
91
+ Number of commands loaded
92
+ """
93
+ loaded_count = 0
94
+
95
+ try:
96
+ for root, dirs, files in os.walk(directory):
97
+ for file in files:
98
+ if file.endswith("_command.py"):
99
+ file_path = os.path.join(root, file)
100
+ try:
101
+ # Load command from file
102
+ from .command_loader import CommandLoader
103
+ loader = CommandLoader()
104
+ result = loader._load_command_from_file(file_path)
105
+
106
+ if result["success"]:
107
+ for command_class in result["commands"]:
108
+ command_name = self._get_command_name(command_class)
109
+ commands[command_name] = command_class
110
+ command_types[command_name] = cmd_type
111
+ loaded_count += 1
112
+
113
+ except Exception as e:
114
+ self.logger.warning(f"Failed to load command from {file_path}: {e}")
115
+
116
+ except Exception as e:
117
+ self.logger.error(f"Failed to load commands from directory {directory}: {e}")
118
+
119
+ return loaded_count