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,389 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base command classes for MCP Microservice.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import inspect
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
from typing import TypeVar, Type, ClassVar, Dict, Any, List
|
|
8
|
+
|
|
9
|
+
from docstring_parser import parse
|
|
10
|
+
|
|
11
|
+
from mcp_proxy_adapter.core.errors import (
|
|
12
|
+
CommandError,
|
|
13
|
+
InternalError,
|
|
14
|
+
InvalidParamsError,
|
|
15
|
+
NotFoundError,
|
|
16
|
+
ValidationError,
|
|
17
|
+
)
|
|
18
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CommandResult:
|
|
22
|
+
"""Base class for command results."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, success: bool = True, data: dict = None, error: str = None):
|
|
25
|
+
self.success = success
|
|
26
|
+
self.data = data or {}
|
|
27
|
+
self.error = error
|
|
28
|
+
|
|
29
|
+
def to_dict(self) -> dict:
|
|
30
|
+
"""Convert to dictionary."""
|
|
31
|
+
result = {"success": self.success}
|
|
32
|
+
if self.data:
|
|
33
|
+
result["data"] = self.data
|
|
34
|
+
if self.error:
|
|
35
|
+
result["error"] = self.error
|
|
36
|
+
return result
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
T = TypeVar("T", bound=CommandResult)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Command(ABC):
|
|
43
|
+
"""
|
|
44
|
+
Base abstract class for all commands.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
# Command name for registration
|
|
48
|
+
name: ClassVar[str]
|
|
49
|
+
# Command version (default: 0.1)
|
|
50
|
+
version: ClassVar[str] = "0.1"
|
|
51
|
+
# Plugin filename
|
|
52
|
+
plugin: ClassVar[str] = ""
|
|
53
|
+
# Command description
|
|
54
|
+
descr: ClassVar[str] = ""
|
|
55
|
+
# Command category
|
|
56
|
+
category: ClassVar[str] = ""
|
|
57
|
+
# Command author
|
|
58
|
+
author: ClassVar[str] = ""
|
|
59
|
+
# Author email
|
|
60
|
+
email: ClassVar[str] = ""
|
|
61
|
+
# Source URL
|
|
62
|
+
source_url: ClassVar[str] = ""
|
|
63
|
+
# Result class
|
|
64
|
+
result_class: ClassVar[Type[CommandResult]]
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
async def execute(self, **kwargs) -> CommandResult:
|
|
68
|
+
"""
|
|
69
|
+
Execute command with the specified parameters.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
**kwargs: Command parameters including optional 'context' parameter.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Command result.
|
|
76
|
+
"""
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def get_schema(cls) -> Dict[str, Any]:
|
|
81
|
+
"""
|
|
82
|
+
Get JSON schema for command parameters.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
JSON schema.
|
|
86
|
+
"""
|
|
87
|
+
return {"type": "object", "properties": {}, "additionalProperties": False}
|
|
88
|
+
|
|
89
|
+
@classmethod
|
|
90
|
+
def get_result_schema(cls) -> Dict[str, Any]:
|
|
91
|
+
"""
|
|
92
|
+
Get JSON schema for command result.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
JSON schema.
|
|
96
|
+
"""
|
|
97
|
+
if hasattr(cls, "result_class") and cls.result_class:
|
|
98
|
+
return cls.result_class.get_schema()
|
|
99
|
+
return {}
|
|
100
|
+
|
|
101
|
+
def validate_params(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
102
|
+
"""
|
|
103
|
+
Validate command parameters.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
params: Parameters to validate.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Validated parameters.
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
ValidationError: If parameters are invalid.
|
|
113
|
+
"""
|
|
114
|
+
# Ensure params is a dictionary, even if None was passed
|
|
115
|
+
if params is None:
|
|
116
|
+
params = {}
|
|
117
|
+
|
|
118
|
+
# Create a copy to avoid modifying the input dictionary during iteration
|
|
119
|
+
validated_params = params.copy()
|
|
120
|
+
|
|
121
|
+
# Handle None values and empty strings in parameters
|
|
122
|
+
for key, value in list(validated_params.items()):
|
|
123
|
+
# Process None values or empty strings - this helps with JavaScript null/undefined conversions
|
|
124
|
+
if value is None or (
|
|
125
|
+
isinstance(value, str) and value.lower() in ["null", "none", ""]
|
|
126
|
+
):
|
|
127
|
+
# For commands that specifically handle None values, keep the parameter
|
|
128
|
+
# (like help), keep the parameter but ensure it's a proper Python None
|
|
129
|
+
if key in [
|
|
130
|
+
"cmdname"
|
|
131
|
+
]: # список параметров, для которых None является допустимым значением
|
|
132
|
+
validated_params[key] = None
|
|
133
|
+
else:
|
|
134
|
+
# For most parameters, remove None values to avoid issues
|
|
135
|
+
del validated_params[key]
|
|
136
|
+
|
|
137
|
+
# Get command schema to validate parameters
|
|
138
|
+
schema = self.get_schema()
|
|
139
|
+
if schema and "properties" in schema:
|
|
140
|
+
allowed_properties = schema["properties"].keys()
|
|
141
|
+
|
|
142
|
+
# Filter out parameters that are not in the schema
|
|
143
|
+
invalid_params = []
|
|
144
|
+
for param_name in list(validated_params.keys()):
|
|
145
|
+
if param_name not in allowed_properties:
|
|
146
|
+
invalid_params.append(param_name)
|
|
147
|
+
del validated_params[param_name]
|
|
148
|
+
|
|
149
|
+
# Log warning about invalid parameters
|
|
150
|
+
if invalid_params:
|
|
151
|
+
get_global_logger().warning(
|
|
152
|
+
f"Command {self.__class__.__name__} received invalid parameters: {invalid_params}. "
|
|
153
|
+
f"Allowed parameters: {list(allowed_properties)}"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Validate required parameters based on command schema
|
|
157
|
+
if schema and "required" in schema:
|
|
158
|
+
required_params = schema["required"]
|
|
159
|
+
missing_params = []
|
|
160
|
+
|
|
161
|
+
for param in required_params:
|
|
162
|
+
if param not in validated_params:
|
|
163
|
+
missing_params.append(param)
|
|
164
|
+
|
|
165
|
+
if missing_params:
|
|
166
|
+
raise ValidationError(
|
|
167
|
+
f"Missing required parameters: {', '.join(missing_params)}",
|
|
168
|
+
data={"missing_parameters": missing_params},
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
return validated_params
|
|
172
|
+
|
|
173
|
+
@classmethod
|
|
174
|
+
async def run(cls, **kwargs) -> CommandResult:
|
|
175
|
+
"""
|
|
176
|
+
Runs the command with the specified arguments.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
**kwargs: Command arguments including optional 'context' parameter.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Command result.
|
|
183
|
+
"""
|
|
184
|
+
# Extract context from kwargs
|
|
185
|
+
context = kwargs.pop("context", {}) if "context" in kwargs else {}
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
get_global_logger().debug(f"Running command {cls.__name__} with params: {kwargs}")
|
|
189
|
+
|
|
190
|
+
# Import registry here to avoid circular imports
|
|
191
|
+
from mcp_proxy_adapter.commands.command_registry import registry
|
|
192
|
+
|
|
193
|
+
# Get command name
|
|
194
|
+
if not hasattr(cls, "name") or not cls.name:
|
|
195
|
+
command_name = cls.__name__.lower()
|
|
196
|
+
if command_name.endswith("command"):
|
|
197
|
+
command_name = command_name[:-7]
|
|
198
|
+
else:
|
|
199
|
+
command_name = cls.name
|
|
200
|
+
|
|
201
|
+
# Ensure kwargs is never None
|
|
202
|
+
if kwargs is None:
|
|
203
|
+
kwargs = {}
|
|
204
|
+
|
|
205
|
+
# Get command with priority (custom commands first, then built-in)
|
|
206
|
+
command_class = registry.get_command(command_name)
|
|
207
|
+
if command_class is None:
|
|
208
|
+
raise NotFoundError(f"Command '{command_name}' not found")
|
|
209
|
+
|
|
210
|
+
# Create new instance and validate parameters
|
|
211
|
+
command = command_class()
|
|
212
|
+
validated_params = command.validate_params(kwargs)
|
|
213
|
+
|
|
214
|
+
# Execute command with validated parameters and context
|
|
215
|
+
result = await command.execute(**validated_params, context=context)
|
|
216
|
+
|
|
217
|
+
get_global_logger().debug(f"Command {cls.__name__} executed successfully")
|
|
218
|
+
return result
|
|
219
|
+
except ValidationError as e:
|
|
220
|
+
# Ошибка валидации параметров
|
|
221
|
+
get_global_logger().error(f"Validation error in command {cls.__name__}: {e}")
|
|
222
|
+
return ErrorResult(message=str(e), code=e.code, details=e.data)
|
|
223
|
+
except InvalidParamsError as e:
|
|
224
|
+
# Ошибка в параметрах команды
|
|
225
|
+
get_global_logger().error(f"Invalid parameters error in command {cls.__name__}: {e}")
|
|
226
|
+
return ErrorResult(message=str(e), code=e.code, details=e.data)
|
|
227
|
+
except NotFoundError as e:
|
|
228
|
+
# Ресурс не найден
|
|
229
|
+
get_global_logger().error(f"Resource not found error in command {cls.__name__}: {e}")
|
|
230
|
+
return ErrorResult(message=str(e), code=e.code, details=e.data)
|
|
231
|
+
except TimeoutError as e:
|
|
232
|
+
# Превышено время ожидания
|
|
233
|
+
get_global_logger().error(f"Timeout error in command {cls.__name__}: {e}")
|
|
234
|
+
return ErrorResult(message=str(e), code=e.code, details=e.data)
|
|
235
|
+
except CommandError as e:
|
|
236
|
+
# Ошибка выполнения команды
|
|
237
|
+
get_global_logger().error(f"Command error in {cls.__name__}: {e}")
|
|
238
|
+
return ErrorResult(message=str(e), code=e.code, details=e.data)
|
|
239
|
+
except Exception as e:
|
|
240
|
+
# Непредвиденная ошибка
|
|
241
|
+
get_global_logger().exception(f"Unexpected error executing command {cls.__name__}: {e}")
|
|
242
|
+
internal_error = InternalError(f"Command execution error: {str(e)}")
|
|
243
|
+
return ErrorResult(
|
|
244
|
+
message=internal_error.message,
|
|
245
|
+
code=internal_error.code,
|
|
246
|
+
details={"original_error": str(e)},
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
@classmethod
|
|
250
|
+
def get_param_info(cls) -> Dict[str, Dict[str, Any]]:
|
|
251
|
+
"""
|
|
252
|
+
Gets information about execute method parameters.
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
Dictionary with parameters information.
|
|
256
|
+
"""
|
|
257
|
+
signature = inspect.signature(cls.execute)
|
|
258
|
+
params = {}
|
|
259
|
+
|
|
260
|
+
for name, param in signature.parameters.items():
|
|
261
|
+
if name == "self":
|
|
262
|
+
continue
|
|
263
|
+
|
|
264
|
+
param_info = {
|
|
265
|
+
"name": name,
|
|
266
|
+
"required": param.default == inspect.Parameter.empty,
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if param.annotation != inspect.Parameter.empty:
|
|
270
|
+
param_info["type"] = str(param.annotation)
|
|
271
|
+
|
|
272
|
+
if param.default != inspect.Parameter.empty:
|
|
273
|
+
param_info["default"] = param.default
|
|
274
|
+
|
|
275
|
+
params[name] = param_info
|
|
276
|
+
|
|
277
|
+
return params
|
|
278
|
+
|
|
279
|
+
@classmethod
|
|
280
|
+
|
|
281
|
+
@classmethod
|
|
282
|
+
def _generate_examples(
|
|
283
|
+
cls, params: Dict[str, Dict[str, Any]]
|
|
284
|
+
) -> List[Dict[str, Any]]:
|
|
285
|
+
"""
|
|
286
|
+
Generates usage examples of the command based on its parameters.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
params: Information about command parameters
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
List of examples
|
|
293
|
+
"""
|
|
294
|
+
examples = []
|
|
295
|
+
|
|
296
|
+
# Simple example without parameters, if all parameters are optional
|
|
297
|
+
if not any(param.get("required", False) for param in params.values()):
|
|
298
|
+
examples.append(
|
|
299
|
+
{
|
|
300
|
+
"command": cls.name,
|
|
301
|
+
"description": f"Call {cls.name} command without parameters",
|
|
302
|
+
}
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# Example with all required parameters
|
|
306
|
+
required_params = {k: v for k, v in params.items() if v.get("required", False)}
|
|
307
|
+
if required_params:
|
|
308
|
+
sample_params = {}
|
|
309
|
+
for param_name, param_info in required_params.items():
|
|
310
|
+
# Try to generate sample value based on type
|
|
311
|
+
param_type = param_info.get("type", "")
|
|
312
|
+
|
|
313
|
+
if "str" in param_type:
|
|
314
|
+
sample_params[param_name] = f"sample_{param_name}"
|
|
315
|
+
elif "int" in param_type:
|
|
316
|
+
sample_params[param_name] = 1
|
|
317
|
+
elif "float" in param_type:
|
|
318
|
+
sample_params[param_name] = 1.0
|
|
319
|
+
elif "bool" in param_type:
|
|
320
|
+
sample_params[param_name] = True
|
|
321
|
+
elif "list" in param_type or "List" in param_type:
|
|
322
|
+
sample_params[param_name] = []
|
|
323
|
+
elif "dict" in param_type or "Dict" in param_type:
|
|
324
|
+
sample_params[param_name] = {}
|
|
325
|
+
else:
|
|
326
|
+
sample_params[param_name] = "..."
|
|
327
|
+
|
|
328
|
+
examples.append(
|
|
329
|
+
{
|
|
330
|
+
"command": cls.name,
|
|
331
|
+
"params": sample_params,
|
|
332
|
+
"description": f"Call {cls.name} command with required parameters",
|
|
333
|
+
}
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
# Example with all parameters (including optional ones)
|
|
337
|
+
if len(params) > len(required_params):
|
|
338
|
+
all_params = {}
|
|
339
|
+
for param_name, param_info in params.items():
|
|
340
|
+
# For required parameters, use the same values as above
|
|
341
|
+
if param_info.get("required", False):
|
|
342
|
+
# Try to generate sample value based on type
|
|
343
|
+
param_type = param_info.get("type", "")
|
|
344
|
+
|
|
345
|
+
if "str" in param_type:
|
|
346
|
+
all_params[param_name] = f"sample_{param_name}"
|
|
347
|
+
elif "int" in param_type:
|
|
348
|
+
all_params[param_name] = 1
|
|
349
|
+
elif "float" in param_type:
|
|
350
|
+
all_params[param_name] = 1.0
|
|
351
|
+
elif "bool" in param_type:
|
|
352
|
+
all_params[param_name] = True
|
|
353
|
+
elif "list" in param_type or "List" in param_type:
|
|
354
|
+
all_params[param_name] = []
|
|
355
|
+
elif "dict" in param_type or "Dict" in param_type:
|
|
356
|
+
all_params[param_name] = {}
|
|
357
|
+
else:
|
|
358
|
+
all_params[param_name] = "..."
|
|
359
|
+
# For optional parameters, use their default values or a sample value
|
|
360
|
+
else:
|
|
361
|
+
if "default" in param_info:
|
|
362
|
+
all_params[param_name] = param_info["default"]
|
|
363
|
+
else:
|
|
364
|
+
param_type = param_info.get("type", "")
|
|
365
|
+
|
|
366
|
+
if "str" in param_type:
|
|
367
|
+
all_params[param_name] = f"optional_{param_name}"
|
|
368
|
+
elif "int" in param_type:
|
|
369
|
+
all_params[param_name] = 0
|
|
370
|
+
elif "float" in param_type:
|
|
371
|
+
all_params[param_name] = 0.0
|
|
372
|
+
elif "bool" in param_type:
|
|
373
|
+
all_params[param_name] = False
|
|
374
|
+
elif "list" in param_type or "List" in param_type:
|
|
375
|
+
all_params[param_name] = []
|
|
376
|
+
elif "dict" in param_type or "Dict" in param_type:
|
|
377
|
+
all_params[param_name] = {}
|
|
378
|
+
else:
|
|
379
|
+
all_params[param_name] = None
|
|
380
|
+
|
|
381
|
+
examples.append(
|
|
382
|
+
{
|
|
383
|
+
"command": cls.name,
|
|
384
|
+
"params": all_params,
|
|
385
|
+
"description": f"Call {cls.name} command with all parameters",
|
|
386
|
+
}
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
return examples
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for registering built-in framework commands.
|
|
3
|
+
|
|
4
|
+
This module contains the procedure for adding predefined commands
|
|
5
|
+
that are part of the framework.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import List
|
|
9
|
+
from mcp_proxy_adapter.commands.command_registry import registry
|
|
10
|
+
from mcp_proxy_adapter.commands.help_command import HelpCommand
|
|
11
|
+
from mcp_proxy_adapter.commands.health_command import HealthCommand
|
|
12
|
+
from mcp_proxy_adapter.commands.config_command import ConfigCommand
|
|
13
|
+
from mcp_proxy_adapter.commands.reload_command import ReloadCommand
|
|
14
|
+
from mcp_proxy_adapter.commands.settings_command import SettingsCommand
|
|
15
|
+
from mcp_proxy_adapter.commands.load_command import LoadCommand
|
|
16
|
+
from mcp_proxy_adapter.commands.unload_command import UnloadCommand
|
|
17
|
+
from mcp_proxy_adapter.commands.plugins_command import PluginsCommand
|
|
18
|
+
from mcp_proxy_adapter.commands.transport_management_command import (
|
|
19
|
+
TransportManagementCommand,
|
|
20
|
+
)
|
|
21
|
+
from mcp_proxy_adapter.commands.proxy_registration_command import (
|
|
22
|
+
ProxyRegistrationCommand,
|
|
23
|
+
)
|
|
24
|
+
from mcp_proxy_adapter.commands.echo_command import EchoCommand
|
|
25
|
+
from mcp_proxy_adapter.commands.role_test_command import RoleTestCommand
|
|
26
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Command catalog management package for MCP Proxy Adapter.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .command_catalog import CommandCatalog
|
|
9
|
+
from .catalog_manager import CatalogManager
|
|
10
|
+
from .catalog_loader import CatalogLoader
|
|
11
|
+
from .catalog_syncer import CatalogSyncer
|
|
12
|
+
from .dependency_manager import DependencyManager
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"CommandCatalog",
|
|
16
|
+
"CatalogManager",
|
|
17
|
+
"CatalogLoader",
|
|
18
|
+
"CatalogSyncer",
|
|
19
|
+
"DependencyManager",
|
|
20
|
+
]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Catalog loading utilities for MCP Proxy Adapter.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
12
|
+
from .command_catalog import CommandCatalog
|
|
13
|
+
|
|
14
|
+
# Try to import requests, but don't fail if not available
|
|
15
|
+
try:
|
|
16
|
+
import requests
|
|
17
|
+
REQUESTS_AVAILABLE = True
|
|
18
|
+
except ImportError:
|
|
19
|
+
REQUESTS_AVAILABLE = False
|
|
20
|
+
get_global_logger().warning(
|
|
21
|
+
"requests library not available, HTTP/HTTPS functionality will be limited"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CatalogLoader:
|
|
26
|
+
"""Loader for command catalogs from various sources."""
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
"""Initialize catalog loader."""
|
|
30
|
+
self.logger = get_global_logger()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Main catalog manager for MCP Proxy Adapter.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Dict, List, Optional, Any
|
|
11
|
+
from packaging import version as pkg_version
|
|
12
|
+
|
|
13
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
14
|
+
from mcp_proxy_adapter.commands.dependency_manager import dependency_manager
|
|
15
|
+
from mcp_proxy_adapter.config import config
|
|
16
|
+
from .command_catalog import CommandCatalog
|
|
17
|
+
from .catalog_loader import CatalogLoader
|
|
18
|
+
from .catalog_syncer import CatalogSyncer
|
|
19
|
+
from .dependency_manager import DependencyManager
|
|
20
|
+
|
|
21
|
+
# Try to import requests, but don't fail if not available
|
|
22
|
+
try:
|
|
23
|
+
import requests
|
|
24
|
+
REQUESTS_AVAILABLE = True
|
|
25
|
+
except ImportError:
|
|
26
|
+
REQUESTS_AVAILABLE = False
|
|
27
|
+
get_global_logger().warning(
|
|
28
|
+
"requests library not available, HTTP/HTTPS functionality will be limited"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CatalogManager:
|
|
33
|
+
"""
|
|
34
|
+
Manager for command catalog operations.
|
|
35
|
+
|
|
36
|
+
Handles loading, syncing, and managing command catalogs from various sources.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, catalog_dir: str):
|
|
40
|
+
"""
|
|
41
|
+
Initialize catalog manager.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
catalog_dir: Directory for catalog storage
|
|
45
|
+
"""
|
|
46
|
+
self.catalog_dir = Path(catalog_dir)
|
|
47
|
+
self.logger = get_global_logger()
|
|
48
|
+
|
|
49
|
+
# Initialize components
|
|
50
|
+
self.loader = CatalogLoader()
|
|
51
|
+
self.syncer = CatalogSyncer(catalog_dir)
|
|
52
|
+
self.dependency_manager = DependencyManager()
|
|
53
|
+
|
|
54
|
+
# Load existing catalog
|
|
55
|
+
self.catalog: Dict[str, CommandCatalog] = {}
|
|
56
|
+
self._load_catalog()
|
|
57
|
+
|
|
58
|
+
def _load_catalog(self) -> None:
|
|
59
|
+
"""Load catalog from local storage."""
|
|
60
|
+
catalog_file = self.catalog_dir / "catalog.json"
|
|
61
|
+
self.catalog = self.loader.load_catalog_from_file(catalog_file)
|
|
62
|
+
|
|
63
|
+
def _save_catalog(self) -> None:
|
|
64
|
+
"""Save catalog to local storage."""
|
|
65
|
+
catalog_file = self.catalog_dir / "catalog.json"
|
|
66
|
+
self.loader.save_catalog_to_file(self.catalog, catalog_file)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def sync_with_servers(self, server_urls: List[str]) -> Dict[str, Any]:
|
|
70
|
+
"""
|
|
71
|
+
Synchronize catalog with remote servers.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
server_urls: List of server URLs to sync with
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Dictionary with sync results
|
|
78
|
+
"""
|
|
79
|
+
return self.syncer.sync_with_servers(server_urls)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _extract_metadata_from_file(self, file_path: str) -> Dict[str, Any]:
|
|
86
|
+
"""
|
|
87
|
+
Extract metadata from command file.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
file_path: Path to command file
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Dictionary of extracted metadata
|
|
94
|
+
"""
|
|
95
|
+
metadata = {}
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
99
|
+
content = f.read()
|
|
100
|
+
|
|
101
|
+
# Extract basic metadata
|
|
102
|
+
for line in content.split('\n'):
|
|
103
|
+
line = line.strip()
|
|
104
|
+
if line.startswith('__version__'):
|
|
105
|
+
metadata['version'] = line.split('=')[1].strip().strip('"\'')
|
|
106
|
+
elif line.startswith('__author__'):
|
|
107
|
+
metadata['author'] = line.split('=')[1].strip().strip('"\'')
|
|
108
|
+
elif line.startswith('__description__'):
|
|
109
|
+
metadata['description'] = line.split('=')[1].strip().strip('"\'')
|
|
110
|
+
elif line.startswith('__category__'):
|
|
111
|
+
metadata['category'] = line.split('=')[1].strip().strip('"\'')
|
|
112
|
+
elif line.startswith('__email__'):
|
|
113
|
+
metadata['email'] = line.split('=')[1].strip().strip('"\'')
|
|
114
|
+
elif line.startswith('__depends__'):
|
|
115
|
+
deps_str = line.split('=')[1].strip().strip('[]"\'')
|
|
116
|
+
if deps_str:
|
|
117
|
+
metadata['depends'] = [dep.strip().strip('"\'') for dep in deps_str.split(',')]
|
|
118
|
+
|
|
119
|
+
except Exception as e:
|
|
120
|
+
self.logger.error(f"Failed to extract metadata from {file_path}: {e}")
|
|
121
|
+
|
|
122
|
+
return metadata
|