mcp-proxy-adapter 2.0.1__py3-none-any.whl → 6.9.50__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.
- mcp_proxy_adapter/__init__.py +47 -0
- mcp_proxy_adapter/__main__.py +13 -0
- mcp_proxy_adapter/api/__init__.py +0 -0
- mcp_proxy_adapter/api/app.py +66 -0
- mcp_proxy_adapter/api/core/__init__.py +18 -0
- mcp_proxy_adapter/api/core/app_factory.py +400 -0
- mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
- mcp_proxy_adapter/api/core/registration_context.py +356 -0
- mcp_proxy_adapter/api/core/registration_manager.py +307 -0
- mcp_proxy_adapter/api/core/registration_tasks.py +84 -0
- mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
- mcp_proxy_adapter/api/handlers.py +181 -0
- mcp_proxy_adapter/api/middleware/__init__.py +21 -0
- mcp_proxy_adapter/api/middleware/base.py +54 -0
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +73 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +76 -0
- mcp_proxy_adapter/api/middleware/factory.py +147 -0
- mcp_proxy_adapter/api/middleware/logging.py +31 -0
- mcp_proxy_adapter/api/middleware/performance.py +51 -0
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +140 -0
- mcp_proxy_adapter/api/middleware/transport_middleware.py +87 -0
- mcp_proxy_adapter/api/middleware/unified_security.py +223 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +132 -0
- mcp_proxy_adapter/api/openapi/__init__.py +21 -0
- mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
- mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
- mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
- mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
- mcp_proxy_adapter/api/schemas.py +270 -0
- mcp_proxy_adapter/api/tool_integration.py +131 -0
- mcp_proxy_adapter/api/tools.py +163 -0
- mcp_proxy_adapter/cli/__init__.py +12 -0
- mcp_proxy_adapter/cli/commands/__init__.py +15 -0
- mcp_proxy_adapter/cli/commands/client.py +100 -0
- mcp_proxy_adapter/cli/commands/config_generate.py +105 -0
- mcp_proxy_adapter/cli/commands/config_validate.py +94 -0
- mcp_proxy_adapter/cli/commands/generate.py +259 -0
- mcp_proxy_adapter/cli/commands/server.py +174 -0
- mcp_proxy_adapter/cli/commands/sets.py +132 -0
- mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
- mcp_proxy_adapter/cli/examples/__init__.py +8 -0
- mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
- mcp_proxy_adapter/cli/examples/https_token.py +96 -0
- mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
- mcp_proxy_adapter/cli/main.py +63 -0
- mcp_proxy_adapter/cli/parser.py +338 -0
- mcp_proxy_adapter/cli/validators.py +231 -0
- mcp_proxy_adapter/client/jsonrpc_client/__init__.py +9 -0
- mcp_proxy_adapter/client/jsonrpc_client/client.py +42 -0
- mcp_proxy_adapter/client/jsonrpc_client/command_api.py +45 -0
- mcp_proxy_adapter/client/jsonrpc_client/proxy_api.py +224 -0
- mcp_proxy_adapter/client/jsonrpc_client/queue_api.py +60 -0
- mcp_proxy_adapter/client/jsonrpc_client/transport.py +108 -0
- mcp_proxy_adapter/client/proxy.py +123 -0
- mcp_proxy_adapter/commands/__init__.py +66 -0
- mcp_proxy_adapter/commands/auth_validation_command.py +69 -0
- mcp_proxy_adapter/commands/base.py +389 -0
- mcp_proxy_adapter/commands/builtin_commands.py +30 -0
- mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
- mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
- mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
- mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
- mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
- mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
- mcp_proxy_adapter/commands/catalog_manager.py +97 -0
- mcp_proxy_adapter/commands/cert_monitor_command.py +552 -0
- mcp_proxy_adapter/commands/certificate_management_command.py +562 -0
- mcp_proxy_adapter/commands/command_registry.py +298 -0
- mcp_proxy_adapter/commands/config_command.py +102 -0
- mcp_proxy_adapter/commands/dependency_container.py +40 -0
- mcp_proxy_adapter/commands/dependency_manager.py +143 -0
- mcp_proxy_adapter/commands/echo_command.py +48 -0
- mcp_proxy_adapter/commands/health_command.py +142 -0
- mcp_proxy_adapter/commands/help_command.py +175 -0
- mcp_proxy_adapter/commands/hooks.py +172 -0
- mcp_proxy_adapter/commands/key_management_command.py +484 -0
- mcp_proxy_adapter/commands/load_command.py +123 -0
- mcp_proxy_adapter/commands/plugins_command.py +246 -0
- mcp_proxy_adapter/commands/protocol_management_command.py +216 -0
- mcp_proxy_adapter/commands/proxy_registration_command.py +319 -0
- mcp_proxy_adapter/commands/queue_commands.py +750 -0
- mcp_proxy_adapter/commands/registration_status_command.py +76 -0
- mcp_proxy_adapter/commands/registry/__init__.py +18 -0
- mcp_proxy_adapter/commands/registry/command_info.py +103 -0
- mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
- mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
- mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
- mcp_proxy_adapter/commands/reload_command.py +136 -0
- mcp_proxy_adapter/commands/result.py +157 -0
- mcp_proxy_adapter/commands/role_test_command.py +99 -0
- mcp_proxy_adapter/commands/roles_management_command.py +502 -0
- mcp_proxy_adapter/commands/security_command.py +472 -0
- mcp_proxy_adapter/commands/settings_command.py +113 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +306 -0
- mcp_proxy_adapter/commands/token_management_command.py +500 -0
- mcp_proxy_adapter/commands/transport_management_command.py +129 -0
- mcp_proxy_adapter/commands/unload_command.py +92 -0
- mcp_proxy_adapter/config.py +32 -0
- mcp_proxy_adapter/core/__init__.py +8 -0
- mcp_proxy_adapter/core/app_factory.py +560 -0
- mcp_proxy_adapter/core/app_runner.py +318 -0
- mcp_proxy_adapter/core/auth_validator.py +508 -0
- mcp_proxy_adapter/core/certificate/__init__.py +20 -0
- mcp_proxy_adapter/core/certificate/certificate_creator.py +372 -0
- mcp_proxy_adapter/core/certificate/certificate_extractor.py +185 -0
- mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
- mcp_proxy_adapter/core/certificate/certificate_validator.py +481 -0
- mcp_proxy_adapter/core/certificate/ssl_context_manager.py +65 -0
- mcp_proxy_adapter/core/certificate_utils.py +249 -0
- mcp_proxy_adapter/core/client.py +608 -0
- mcp_proxy_adapter/core/client_manager.py +271 -0
- mcp_proxy_adapter/core/client_security.py +411 -0
- mcp_proxy_adapter/core/config/__init__.py +18 -0
- mcp_proxy_adapter/core/config/config.py +237 -0
- mcp_proxy_adapter/core/config/config_factory.py +22 -0
- mcp_proxy_adapter/core/config/config_loader.py +66 -0
- mcp_proxy_adapter/core/config/feature_manager.py +31 -0
- mcp_proxy_adapter/core/config/simple_config.py +204 -0
- mcp_proxy_adapter/core/config/simple_config_generator.py +131 -0
- mcp_proxy_adapter/core/config/simple_config_validator.py +476 -0
- mcp_proxy_adapter/core/config_converter.py +252 -0
- mcp_proxy_adapter/core/config_validator.py +211 -0
- mcp_proxy_adapter/core/crl_utils.py +362 -0
- mcp_proxy_adapter/core/errors.py +276 -0
- mcp_proxy_adapter/core/job_manager.py +54 -0
- mcp_proxy_adapter/core/logging.py +250 -0
- mcp_proxy_adapter/core/mtls_asgi.py +140 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/mtls_proxy.py +229 -0
- mcp_proxy_adapter/core/mtls_server.py +154 -0
- mcp_proxy_adapter/core/protocol_manager.py +232 -0
- mcp_proxy_adapter/core/proxy/__init__.py +19 -0
- mcp_proxy_adapter/core/proxy/auth_manager.py +26 -0
- mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +160 -0
- mcp_proxy_adapter/core/proxy/registration_client.py +186 -0
- mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
- mcp_proxy_adapter/core/proxy_client.py +184 -0
- mcp_proxy_adapter/core/proxy_registration.py +80 -0
- mcp_proxy_adapter/core/role_utils.py +103 -0
- mcp_proxy_adapter/core/security_adapter.py +343 -0
- mcp_proxy_adapter/core/security_factory.py +96 -0
- mcp_proxy_adapter/core/security_integration.py +342 -0
- mcp_proxy_adapter/core/server_adapter.py +251 -0
- mcp_proxy_adapter/core/server_engine.py +217 -0
- mcp_proxy_adapter/core/settings.py +260 -0
- mcp_proxy_adapter/core/signal_handler.py +107 -0
- mcp_proxy_adapter/core/ssl_utils.py +161 -0
- mcp_proxy_adapter/core/transport_manager.py +153 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +471 -0
- mcp_proxy_adapter/core/utils.py +101 -0
- mcp_proxy_adapter/core/validation/__init__.py +21 -0
- mcp_proxy_adapter/core/validation/config_validator.py +219 -0
- mcp_proxy_adapter/core/validation/file_validator.py +131 -0
- mcp_proxy_adapter/core/validation/protocol_validator.py +205 -0
- mcp_proxy_adapter/core/validation/security_validator.py +140 -0
- mcp_proxy_adapter/core/validation/validation_result.py +27 -0
- mcp_proxy_adapter/custom_openapi.py +58 -0
- mcp_proxy_adapter/examples/__init__.py +16 -0
- mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +52 -0
- mcp_proxy_adapter/examples/bugfix_certificate_config.py +261 -0
- mcp_proxy_adapter/examples/cert_manager_bugfix.py +203 -0
- mcp_proxy_adapter/examples/check_config.py +413 -0
- mcp_proxy_adapter/examples/client_usage_example.py +164 -0
- mcp_proxy_adapter/examples/commands/__init__.py +5 -0
- mcp_proxy_adapter/examples/config_builder.py +234 -0
- mcp_proxy_adapter/examples/config_cli.py +282 -0
- mcp_proxy_adapter/examples/create_test_configs.py +174 -0
- mcp_proxy_adapter/examples/debug_request_state.py +130 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +191 -0
- mcp_proxy_adapter/examples/demo_client.py +287 -0
- mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +8 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +45 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +52 -0
- mcp_proxy_adapter/examples/full_application/commands/echo_command.py +32 -0
- mcp_proxy_adapter/examples/full_application/commands/help_command.py +54 -0
- mcp_proxy_adapter/examples/full_application/commands/list_command.py +57 -0
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +5 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +29 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +27 -0
- mcp_proxy_adapter/examples/full_application/main.py +311 -0
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +161 -0
- mcp_proxy_adapter/examples/full_application/run_mtls.py +252 -0
- mcp_proxy_adapter/examples/full_application/run_simple.py +152 -0
- mcp_proxy_adapter/examples/full_application/test_minimal_server.py +45 -0
- mcp_proxy_adapter/examples/full_application/test_server.py +163 -0
- mcp_proxy_adapter/examples/full_application/test_simple_server.py +62 -0
- mcp_proxy_adapter/examples/generate_config.py +502 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +335 -0
- mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
- mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
- mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
- mcp_proxy_adapter/examples/queue_server_example.py +85 -0
- mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
- mcp_proxy_adapter/examples/required_certificates.py +208 -0
- mcp_proxy_adapter/examples/run_example.py +77 -0
- mcp_proxy_adapter/examples/run_full_test_suite.py +619 -0
- mcp_proxy_adapter/examples/run_proxy_server.py +153 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +435 -0
- mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
- mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
- mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
- mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
- mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
- mcp_proxy_adapter/examples/security_test_client.py +72 -0
- mcp_proxy_adapter/examples/setup/__init__.py +24 -0
- mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
- mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
- mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
- mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
- mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
- mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
- mcp_proxy_adapter/examples/setup_test_environment.py +235 -0
- mcp_proxy_adapter/examples/simple_protocol_test.py +125 -0
- mcp_proxy_adapter/examples/test_chk_hostname_automated.py +211 -0
- mcp_proxy_adapter/examples/test_config.py +205 -0
- mcp_proxy_adapter/examples/test_config_builder.py +110 -0
- mcp_proxy_adapter/examples/test_examples.py +308 -0
- mcp_proxy_adapter/examples/test_framework_complete.py +267 -0
- mcp_proxy_adapter/examples/test_mcp_server.py +187 -0
- mcp_proxy_adapter/examples/test_protocol_examples.py +337 -0
- mcp_proxy_adapter/examples/universal_client.py +674 -0
- mcp_proxy_adapter/examples/update_config_certificates.py +135 -0
- mcp_proxy_adapter/examples/validate_generator_compatibility.py +385 -0
- mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +61 -0
- mcp_proxy_adapter/integrations/__init__.py +25 -0
- mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
- mcp_proxy_adapter/main.py +311 -0
- mcp_proxy_adapter/openapi.py +375 -0
- mcp_proxy_adapter/schemas/base_schema.json +114 -0
- mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
- mcp_proxy_adapter/schemas/roles.json +37 -0
- mcp_proxy_adapter/schemas/roles_schema.json +162 -0
- mcp_proxy_adapter/version.py +5 -0
- mcp_proxy_adapter-6.9.50.dist-info/METADATA +1088 -0
- mcp_proxy_adapter-6.9.50.dist-info/RECORD +242 -0
- {mcp_proxy_adapter-2.0.1.dist-info → mcp_proxy_adapter-6.9.50.dist-info}/WHEEL +1 -1
- mcp_proxy_adapter-6.9.50.dist-info/entry_points.txt +14 -0
- mcp_proxy_adapter-6.9.50.dist-info/top_level.txt +1 -0
- adapters/__init__.py +0 -16
- analyzers/__init__.py +0 -14
- analyzers/docstring_analyzer.py +0 -199
- analyzers/type_analyzer.py +0 -151
- cli/__init__.py +0 -12
- cli/__main__.py +0 -79
- cli/command_runner.py +0 -233
- dispatchers/__init__.py +0 -14
- dispatchers/base_dispatcher.py +0 -85
- dispatchers/json_rpc_dispatcher.py +0 -198
- generators/__init__.py +0 -14
- generators/endpoint_generator.py +0 -172
- generators/openapi_generator.py +0 -254
- generators/rest_api_generator.py +0 -207
- mcp_proxy_adapter-2.0.1.dist-info/METADATA +0 -272
- mcp_proxy_adapter-2.0.1.dist-info/RECORD +0 -28
- mcp_proxy_adapter-2.0.1.dist-info/licenses/LICENSE +0 -21
- mcp_proxy_adapter-2.0.1.dist-info/top_level.txt +0 -7
- openapi_schema/__init__.py +0 -38
- openapi_schema/command_registry.py +0 -312
- openapi_schema/rest_schema.py +0 -510
- openapi_schema/rpc_generator.py +0 -307
- openapi_schema/rpc_schema.py +0 -416
- validators/__init__.py +0 -14
- validators/base_validator.py +0 -23
- validators/docstring_validator.py +0 -75
- validators/metadata_validator.py +0 -76
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Main command registry for MCP Proxy Adapter.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
from typing import Dict, List, Type, Union, Any, Optional
|
|
10
|
+
|
|
11
|
+
from mcp_proxy_adapter.commands.base import Command
|
|
12
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
13
|
+
# from .command_loader import CommandLoader
|
|
14
|
+
# from .command_manager import CommandManager
|
|
15
|
+
# from .command_info import CommandInfo
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CommandRegistry:
|
|
19
|
+
"""
|
|
20
|
+
Registry for registering and finding commands.
|
|
21
|
+
|
|
22
|
+
Supports three types of commands:
|
|
23
|
+
- Builtin: Core commands that come with the framework
|
|
24
|
+
- Custom: User-defined commands
|
|
25
|
+
- Loaded: Commands loaded from external sources
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
"""Initialize command registry."""
|
|
30
|
+
self.logger = get_global_logger()
|
|
31
|
+
|
|
32
|
+
# Command storage
|
|
33
|
+
self._commands: Dict[str, Type[Command]] = {}
|
|
34
|
+
self._instances: Dict[str, Command] = {}
|
|
35
|
+
self._command_types: Dict[str, str] = {} # "builtin", "custom", "loaded"
|
|
36
|
+
|
|
37
|
+
# Initialize components
|
|
38
|
+
# self._loader = CommandLoader()
|
|
39
|
+
self._loader = None
|
|
40
|
+
# self._manager = CommandManager()
|
|
41
|
+
# self._info = CommandInfo()
|
|
42
|
+
self._manager = None
|
|
43
|
+
self._info = None
|
|
44
|
+
|
|
45
|
+
# Register built-in echo command
|
|
46
|
+
self._register_echo_command()
|
|
47
|
+
self._register_long_task_commands()
|
|
48
|
+
|
|
49
|
+
def _register_echo_command(self) -> None:
|
|
50
|
+
"""Register built-in echo command."""
|
|
51
|
+
from mcp_proxy_adapter.commands.base import Command, CommandResult
|
|
52
|
+
|
|
53
|
+
class EchoCommand(Command):
|
|
54
|
+
name = "echo"
|
|
55
|
+
descr = "Echo command for testing"
|
|
56
|
+
|
|
57
|
+
async def execute(self, message: str = "Hello", **kwargs) -> CommandResult:
|
|
58
|
+
return CommandResult(success=True, data={"message": message})
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def get_schema(cls) -> Dict[str, Any]:
|
|
62
|
+
return {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"properties": {
|
|
65
|
+
"message": {"type": "string", "default": "Hello"}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
self._commands["echo"] = EchoCommand
|
|
70
|
+
self._command_types["echo"] = "builtin"
|
|
71
|
+
|
|
72
|
+
def _register_long_task_commands(self) -> None:
|
|
73
|
+
"""Register demo long-running task commands (enqueue/status)."""
|
|
74
|
+
from mcp_proxy_adapter.commands.base import Command, CommandResult
|
|
75
|
+
from mcp_proxy_adapter.core.job_manager import enqueue_coroutine, get_job_status
|
|
76
|
+
import asyncio
|
|
77
|
+
|
|
78
|
+
class LongTaskCommand(Command):
|
|
79
|
+
name = "long_task"
|
|
80
|
+
descr = "Enqueue a long-running task that sleeps for given seconds"
|
|
81
|
+
|
|
82
|
+
async def execute(self, seconds: float = 5.0, **kwargs) -> CommandResult:
|
|
83
|
+
async def _work():
|
|
84
|
+
await asyncio.sleep(max(0.0, float(seconds)))
|
|
85
|
+
return {"slept": float(seconds)}
|
|
86
|
+
|
|
87
|
+
job_id = enqueue_coroutine(_work())
|
|
88
|
+
return CommandResult(success=True, data={"job_id": job_id, "status": "queued"})
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
def get_schema(cls) -> Dict[str, Any]:
|
|
92
|
+
return {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"properties": {"seconds": {"type": "number", "default": 5.0}},
|
|
95
|
+
"description": "Start a demo long-running job"
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
class JobStatusCommand(Command):
|
|
99
|
+
name = "job_status"
|
|
100
|
+
descr = "Get status of a previously enqueued job"
|
|
101
|
+
|
|
102
|
+
async def execute(self, job_id: str, **kwargs) -> CommandResult:
|
|
103
|
+
status = get_job_status(job_id)
|
|
104
|
+
return CommandResult(success=True, data=status)
|
|
105
|
+
|
|
106
|
+
@classmethod
|
|
107
|
+
def get_schema(cls) -> Dict[str, Any]:
|
|
108
|
+
return {
|
|
109
|
+
"type": "object",
|
|
110
|
+
"properties": {"job_id": {"type": "string"}},
|
|
111
|
+
"required": ["job_id"],
|
|
112
|
+
"description": "Check job status"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
self._commands["long_task"] = LongTaskCommand
|
|
116
|
+
self._command_types["long_task"] = "builtin"
|
|
117
|
+
self._commands["job_status"] = JobStatusCommand
|
|
118
|
+
self._command_types["job_status"] = "builtin"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def register_loaded(self, command: Union[Type[Command], Command]) -> None:
|
|
122
|
+
"""
|
|
123
|
+
Register a loaded command.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
command: Command class or instance to register
|
|
127
|
+
"""
|
|
128
|
+
self._register_command(command, "loaded")
|
|
129
|
+
|
|
130
|
+
def _register_command(self, command: Union[Type[Command], Command], cmd_type: str) -> None:
|
|
131
|
+
"""
|
|
132
|
+
Register a command.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
command: Command class or instance to register
|
|
136
|
+
cmd_type: Type of command ("builtin", "custom", "loaded")
|
|
137
|
+
"""
|
|
138
|
+
if isinstance(command, Command):
|
|
139
|
+
# Register instance
|
|
140
|
+
command_name = self._manager._get_command_name(command.__class__)
|
|
141
|
+
self._instances[command_name] = command
|
|
142
|
+
self._commands[command_name] = command.__class__
|
|
143
|
+
else:
|
|
144
|
+
# Register class
|
|
145
|
+
command_name = self._manager._get_command_name(command)
|
|
146
|
+
self._commands[command_name] = command
|
|
147
|
+
|
|
148
|
+
self._command_types[command_name] = cmd_type
|
|
149
|
+
self.logger.info(f"Registered {cmd_type} command: {command_name}")
|
|
150
|
+
|
|
151
|
+
def load_command_from_source(self, source: str) -> Dict[str, Any]:
|
|
152
|
+
"""
|
|
153
|
+
Load command from source.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
source: Source string - local path, URL, or command name from registry
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Dictionary with loading result information
|
|
160
|
+
"""
|
|
161
|
+
result = self._loader.load_command_from_source(source)
|
|
162
|
+
|
|
163
|
+
if result["success"]:
|
|
164
|
+
# Register loaded commands
|
|
165
|
+
for command_class in result["commands"]:
|
|
166
|
+
self.register_loaded(command_class)
|
|
167
|
+
|
|
168
|
+
return result
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def command_exists(self, command_name: str) -> bool:
|
|
172
|
+
"""
|
|
173
|
+
Check if command exists.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
command_name: Name of the command
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
True if command exists, False otherwise
|
|
180
|
+
"""
|
|
181
|
+
return self._manager.command_exists(command_name, self._commands)
|
|
182
|
+
|
|
183
|
+
def get_command(self, command_name: str) -> Type[Command]:
|
|
184
|
+
"""
|
|
185
|
+
Get command class by name.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
command_name: Name of the command
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Command class
|
|
192
|
+
|
|
193
|
+
Raises:
|
|
194
|
+
NotFoundError: If command not found
|
|
195
|
+
"""
|
|
196
|
+
if command_name not in self._commands:
|
|
197
|
+
raise KeyError(f"Command '{command_name}' not found")
|
|
198
|
+
return self._commands[command_name]
|
|
199
|
+
|
|
200
|
+
def get_command_instance(self, command_name: str) -> Command:
|
|
201
|
+
"""
|
|
202
|
+
Get command instance by name.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
command_name: Name of the command
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
Command instance
|
|
209
|
+
|
|
210
|
+
Raises:
|
|
211
|
+
NotFoundError: If command not found
|
|
212
|
+
"""
|
|
213
|
+
return self._manager.get_command_instance(command_name, self._commands, self._instances)
|
|
214
|
+
|
|
215
|
+
def has_instance(self, command_name: str) -> bool:
|
|
216
|
+
"""
|
|
217
|
+
Check if command has instance.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
command_name: Name of the command
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
True if command has instance, False otherwise
|
|
224
|
+
"""
|
|
225
|
+
return self._manager.has_instance(command_name, self._instances)
|
|
226
|
+
|
|
227
|
+
def get_all_commands(self) -> Dict[str, Type[Command]]:
|
|
228
|
+
"""
|
|
229
|
+
Get all registered commands.
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
Dictionary of all commands
|
|
233
|
+
"""
|
|
234
|
+
return self._commands
|
|
235
|
+
|
|
236
|
+
def get_commands_by_type(self) -> Dict[str, Dict[str, Type[Command]]]:
|
|
237
|
+
"""
|
|
238
|
+
Get commands grouped by type.
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
Dictionary of commands grouped by type
|
|
242
|
+
"""
|
|
243
|
+
return self._manager.get_commands_by_type(self._commands, self._command_types)
|
|
244
|
+
|
|
245
|
+
def get_all_metadata(self) -> Dict[str, Dict[str, Any]]:
|
|
246
|
+
"""
|
|
247
|
+
Get metadata for all commands.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
Dictionary of command metadata
|
|
251
|
+
"""
|
|
252
|
+
return self._manager.get_all_metadata(self._commands, self._command_types)
|
|
253
|
+
|
|
254
|
+
def clear(self) -> None:
|
|
255
|
+
"""Clear all commands and instances."""
|
|
256
|
+
self._manager.clear(self._commands, self._instances, self._command_types)
|
|
257
|
+
self.logger.info("Cleared all commands")
|
|
258
|
+
|
|
259
|
+
def get_all_commands_info(self) -> Dict[str, Any]:
|
|
260
|
+
"""
|
|
261
|
+
Get comprehensive information about all commands.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Dictionary with command information
|
|
265
|
+
"""
|
|
266
|
+
return self._info.get_all_commands_info(self._commands, self._command_types)
|
|
267
|
+
|
|
268
|
+
def get_command_info(self, command_name: str) -> Optional[Dict[str, Any]]:
|
|
269
|
+
"""
|
|
270
|
+
Get detailed information about a specific command.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
command_name: Name of the command
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
Dictionary with command information or None if not found
|
|
277
|
+
"""
|
|
278
|
+
if command_name not in self._commands:
|
|
279
|
+
return None
|
|
280
|
+
|
|
281
|
+
return self._info.get_command_info(
|
|
282
|
+
command_name,
|
|
283
|
+
self._commands[command_name],
|
|
284
|
+
self._command_types
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
def _load_all_commands(self) -> Dict[str, int]:
|
|
288
|
+
"""
|
|
289
|
+
Load all commands from configured directories.
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
Dictionary with loading statistics
|
|
293
|
+
"""
|
|
294
|
+
return self._manager._load_all_commands(self._commands, self._command_types)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
# Global registry instance
|
|
298
|
+
registry = CommandRegistry()
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Config command implementation for managing service configuration.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, Any, Optional
|
|
6
|
+
|
|
7
|
+
from mcp_proxy_adapter.commands.base import Command
|
|
8
|
+
from mcp_proxy_adapter.commands.result import SuccessResult
|
|
9
|
+
from mcp_proxy_adapter.config import get_config
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ConfigResult(SuccessResult):
|
|
13
|
+
"""
|
|
14
|
+
Config operation result.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
config: Dict[str, Any],
|
|
20
|
+
operation: str,
|
|
21
|
+
message: Optional[str] = None,
|
|
22
|
+
):
|
|
23
|
+
"""
|
|
24
|
+
Initialize config result.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
config: Configuration values
|
|
28
|
+
operation: Operation performed
|
|
29
|
+
message: Optional message
|
|
30
|
+
"""
|
|
31
|
+
super().__init__(
|
|
32
|
+
data={"config": config, "operation": operation}, message=message
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ConfigCommand(Command):
|
|
37
|
+
"""
|
|
38
|
+
Command for managing service configuration.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
name = "config"
|
|
42
|
+
description = "Get or set configuration values"
|
|
43
|
+
result_class = 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:
|
|
53
|
+
"""
|
|
54
|
+
Execute the command.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
operation: Operation to perform (get, set)
|
|
58
|
+
path: Configuration path (dot notation)
|
|
59
|
+
value: Value to set (for set operation)
|
|
60
|
+
context: Optional context parameter passed by framework
|
|
61
|
+
**kwargs: Additional parameters
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Config operation result
|
|
65
|
+
"""
|
|
66
|
+
message = None
|
|
67
|
+
result_config = {}
|
|
68
|
+
|
|
69
|
+
if operation == "get":
|
|
70
|
+
config_instance = get_config()
|
|
71
|
+
if path:
|
|
72
|
+
# Get specific config value
|
|
73
|
+
result_config = {path: config_instance.get(path)}
|
|
74
|
+
else:
|
|
75
|
+
# Get all config
|
|
76
|
+
result_config = config_instance.get_all()
|
|
77
|
+
message = "Configuration retrieved successfully"
|
|
78
|
+
|
|
79
|
+
elif operation == "set":
|
|
80
|
+
if path and value is not None:
|
|
81
|
+
# Set config value
|
|
82
|
+
config_instance = get_config()
|
|
83
|
+
config_instance.set(path, value)
|
|
84
|
+
# Save config
|
|
85
|
+
config_instance.save()
|
|
86
|
+
result_config = {path: value}
|
|
87
|
+
message = "Configuration updated successfully"
|
|
88
|
+
else:
|
|
89
|
+
# Error - missing required parameters
|
|
90
|
+
raise ValueError(
|
|
91
|
+
"Both 'path' and 'value' are required for 'set' operation"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
else:
|
|
95
|
+
# Invalid operation
|
|
96
|
+
raise ValueError(
|
|
97
|
+
f"Invalid operation: {operation}. Valid operations: get, set"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
return ConfigResult(config=result_config, operation=operation, message=message)
|
|
101
|
+
|
|
102
|
+
@classmethod
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for dependency injection container implementation.
|
|
3
|
+
|
|
4
|
+
This module provides a container for registering and resolving dependencies
|
|
5
|
+
for command instances in the microservice.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import TypeVar, Dict, Any, Callable, Optional
|
|
9
|
+
|
|
10
|
+
T = TypeVar("T")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DependencyContainer:
|
|
14
|
+
"""
|
|
15
|
+
Container for managing dependencies.
|
|
16
|
+
|
|
17
|
+
This class provides functionality to register, resolve, and manage
|
|
18
|
+
dependencies that can be injected into command instances.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self):
|
|
22
|
+
"""Initialize dependency container."""
|
|
23
|
+
self._dependencies: Dict[str, Any] = {}
|
|
24
|
+
self._factories: Dict[str, callable] = {}
|
|
25
|
+
self._singletons: Dict[str, Any] = {}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def clear(self) -> None:
|
|
32
|
+
"""Clear all registered dependencies."""
|
|
33
|
+
self._dependencies.clear()
|
|
34
|
+
self._factories.clear()
|
|
35
|
+
self._singletons.clear()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Global dependency container instance
|
|
40
|
+
container = DependencyContainer()
|
|
@@ -0,0 +1,143 @@
|
|
|
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
|
+
Author: Vasiliy Zdanovskiy
|
|
8
|
+
email: vasilyvz@gmail.com
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
import importlib
|
|
14
|
+
from typing import List, Dict, Any, Tuple
|
|
15
|
+
|
|
16
|
+
try: # Python 3.8+
|
|
17
|
+
from importlib import metadata as importlib_metadata # type: ignore
|
|
18
|
+
except Exception: # pragma: no cover - very old Python fallback
|
|
19
|
+
import importlib_metadata # type: ignore
|
|
20
|
+
|
|
21
|
+
from packaging.requirements import Requirement
|
|
22
|
+
from packaging.version import Version, InvalidVersion
|
|
23
|
+
|
|
24
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class DependencyManager:
|
|
28
|
+
"""
|
|
29
|
+
Manages plugin dependencies installation and verification.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self):
|
|
33
|
+
"""Initialize dependency manager."""
|
|
34
|
+
self._installed_packages: Dict[str, str] = {}
|
|
35
|
+
self._load_installed_packages()
|
|
36
|
+
|
|
37
|
+
def _load_installed_packages(self) -> None:
|
|
38
|
+
"""Load list of currently installed packages."""
|
|
39
|
+
try:
|
|
40
|
+
self._installed_packages.clear()
|
|
41
|
+
for dist in importlib_metadata.distributions():
|
|
42
|
+
try:
|
|
43
|
+
name = dist.metadata.get("Name") or dist.metadata.get("name")
|
|
44
|
+
version = dist.version
|
|
45
|
+
if name and version:
|
|
46
|
+
self._installed_packages[name.lower()] = version
|
|
47
|
+
except Exception:
|
|
48
|
+
continue
|
|
49
|
+
except Exception as e:
|
|
50
|
+
get_global_logger().warning(f"Failed to load installed packages: {e}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _is_dependency_satisfied(self, dependency: str) -> bool:
|
|
54
|
+
"""
|
|
55
|
+
Check if a single dependency is satisfied.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
dependency: Dependency name or spec
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
True if dependency is satisfied, False otherwise
|
|
62
|
+
"""
|
|
63
|
+
# Parse requirement (handles version specifiers)
|
|
64
|
+
try:
|
|
65
|
+
req = Requirement(dependency)
|
|
66
|
+
except Exception:
|
|
67
|
+
# Fallback: treat as importable module name
|
|
68
|
+
try:
|
|
69
|
+
importlib.import_module(dependency)
|
|
70
|
+
return True
|
|
71
|
+
except ImportError:
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
# Check installation by distribution name
|
|
75
|
+
try:
|
|
76
|
+
installed_version = importlib_metadata.version(req.name)
|
|
77
|
+
except importlib_metadata.PackageNotFoundError:
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
# If no specifier, any installed version satisfies
|
|
81
|
+
if not req.specifier:
|
|
82
|
+
return True
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
return Version(installed_version) in req.specifier
|
|
86
|
+
except InvalidVersion:
|
|
87
|
+
# If version parsing fails, fallback to string comparison via specifier
|
|
88
|
+
return req.specifier.contains(installed_version, prereleases=True)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _install_single_dependency(
|
|
92
|
+
self, dependency: str, user_install: bool = False
|
|
93
|
+
) -> bool:
|
|
94
|
+
"""
|
|
95
|
+
Install a single dependency using pip.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
dependency: Dependency name or spec
|
|
99
|
+
user_install: Whether to install for current user only
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
True if installation successful, False otherwise
|
|
103
|
+
"""
|
|
104
|
+
try:
|
|
105
|
+
# Build pip command
|
|
106
|
+
cmd = [sys.executable, "-m", "pip", "install"]
|
|
107
|
+
|
|
108
|
+
if user_install:
|
|
109
|
+
cmd.append("--user")
|
|
110
|
+
|
|
111
|
+
# Add quiet flag to reduce output
|
|
112
|
+
cmd.append("--quiet")
|
|
113
|
+
|
|
114
|
+
# Add dependency
|
|
115
|
+
cmd.append(dependency)
|
|
116
|
+
|
|
117
|
+
get_global_logger().debug(f"Installing dependency: {' '.join(cmd)}")
|
|
118
|
+
|
|
119
|
+
# Run pip install
|
|
120
|
+
result = subprocess.run(
|
|
121
|
+
cmd, capture_output=True, text=True, timeout=300 # 5 minutes timeout
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
if result.returncode == 0:
|
|
125
|
+
get_global_logger().debug(f"Successfully installed {dependency}")
|
|
126
|
+
return True
|
|
127
|
+
else:
|
|
128
|
+
get_global_logger().error(f"Failed to install {dependency}: {result.stderr}")
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
except subprocess.TimeoutExpired:
|
|
132
|
+
get_global_logger().error(f"Timeout while installing {dependency}")
|
|
133
|
+
return False
|
|
134
|
+
except Exception as e:
|
|
135
|
+
get_global_logger().error(f"Error installing {dependency}: {e}")
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# Global instance
|
|
143
|
+
dependency_manager = DependencyManager()
|
|
@@ -0,0 +1,48 @@
|
|
|
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
|
+
|
|
26
|
+
|
|
27
|
+
class EchoCommand(Command):
|
|
28
|
+
"""Echo command for testing purposes."""
|
|
29
|
+
|
|
30
|
+
name = "echo"
|
|
31
|
+
version = "1.0.0"
|
|
32
|
+
descr = "Echo command for testing"
|
|
33
|
+
category = "testing"
|
|
34
|
+
author = "Vasiliy Zdanovskiy"
|
|
35
|
+
email = "vasilyvz@gmail.com"
|
|
36
|
+
result_class = EchoCommandResult
|
|
37
|
+
|
|
38
|
+
async def execute(self, **kwargs) -> EchoCommandResult:
|
|
39
|
+
"""Execute echo command."""
|
|
40
|
+
message = kwargs.get("message", "Hello, World!")
|
|
41
|
+
timestamp = kwargs.get("timestamp")
|
|
42
|
+
|
|
43
|
+
# Simulate some processing time
|
|
44
|
+
await asyncio.sleep(0.001)
|
|
45
|
+
|
|
46
|
+
return EchoCommandResult(message=message, timestamp=timestamp)
|
|
47
|
+
|
|
48
|
+
@classmethod
|