mcp-proxy-adapter 6.9.43__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.
- 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 +355 -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 +266 -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 +35 -0
- mcp_proxy_adapter/cli/commands/config_validate.py +74 -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 +128 -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 +388 -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 +116 -0
- mcp_proxy_adapter/core/config/simple_config_generator.py +100 -0
- mcp_proxy_adapter/core/config/simple_config_validator.py +380 -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 +190 -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 +13 -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 +264 -0
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +81 -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 +313 -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.43.dist-info/METADATA +739 -0
- mcp_proxy_adapter-6.9.43.dist-info/RECORD +242 -0
- mcp_proxy_adapter-6.9.43.dist-info/WHEEL +5 -0
- mcp_proxy_adapter-6.9.43.dist-info/entry_points.txt +12 -0
- mcp_proxy_adapter-6.9.43.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dynamic Calculator Command
|
|
3
|
+
This module demonstrates a dynamically loaded command implementation for the full application example.
|
|
4
|
+
Author: Vasiliy Zdanovskiy
|
|
5
|
+
email: vasilyvz@gmail.com
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from mcp_proxy_adapter.commands.base import BaseCommand
|
|
9
|
+
from mcp_proxy_adapter.commands.result import CommandResult
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CalculatorResult(CommandResult):
|
|
13
|
+
"""Result class for calculator command."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, operation: str, result: float, expression: str):
|
|
16
|
+
self.operation = operation
|
|
17
|
+
self.result = result
|
|
18
|
+
self.expression = expression
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DynamicCalculatorCommand(BaseCommand):
|
|
24
|
+
"""Dynamic calculator command implementation."""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def execute(self, params: Dict[str, Any]) -> CalculatorResult:
|
|
30
|
+
"""Execute the calculator command."""
|
|
31
|
+
operation = params.get("operation")
|
|
32
|
+
a = params.get("a")
|
|
33
|
+
b = params.get("b")
|
|
34
|
+
if operation == "add":
|
|
35
|
+
result = a + b
|
|
36
|
+
expression = f"{a} + {b}"
|
|
37
|
+
elif operation == "subtract":
|
|
38
|
+
result = a - b
|
|
39
|
+
expression = f"{a} - {b}"
|
|
40
|
+
elif operation == "multiply":
|
|
41
|
+
result = a * b
|
|
42
|
+
expression = f"{a} * {b}"
|
|
43
|
+
elif operation == "divide":
|
|
44
|
+
if b == 0:
|
|
45
|
+
raise ValueError("Division by zero is not allowed")
|
|
46
|
+
result = a / b
|
|
47
|
+
expression = f"{a} / {b}"
|
|
48
|
+
else:
|
|
49
|
+
raise ValueError(f"Unknown operation: {operation}")
|
|
50
|
+
return CalculatorResult(
|
|
51
|
+
operation=operation, result=result, expression=expression
|
|
52
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Echo command implementation.
|
|
3
|
+
"""
|
|
4
|
+
from mcp_proxy_adapter.commands.base import BaseCommand
|
|
5
|
+
from mcp_proxy_adapter.core.errors import MicroserviceError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EchoCommand(BaseCommand):
|
|
9
|
+
"""Echo command that returns the input message."""
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
super().__init__()
|
|
13
|
+
self.name = "echo"
|
|
14
|
+
self.description = "Echo command that returns the input message"
|
|
15
|
+
self.version = "1.0.0"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def execute(self, params: dict) -> dict:
|
|
19
|
+
"""Execute echo command."""
|
|
20
|
+
try:
|
|
21
|
+
message = params.get("message", "")
|
|
22
|
+
return {
|
|
23
|
+
"echo": message,
|
|
24
|
+
"timestamp": self._get_timestamp()
|
|
25
|
+
}
|
|
26
|
+
except Exception as e:
|
|
27
|
+
raise MicroserviceError(f"Echo command failed: {str(e)}")
|
|
28
|
+
|
|
29
|
+
def _get_timestamp(self):
|
|
30
|
+
"""Get current timestamp."""
|
|
31
|
+
import time
|
|
32
|
+
return time.time()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Help command implementation.
|
|
3
|
+
"""
|
|
4
|
+
from mcp_proxy_adapter.commands.base import BaseCommand
|
|
5
|
+
from mcp_proxy_adapter.core.errors import MicroserviceError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class HelpCommand(BaseCommand):
|
|
9
|
+
"""Help command that provides usage information."""
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
super().__init__()
|
|
13
|
+
self.name = "help"
|
|
14
|
+
self.description = "Get help information"
|
|
15
|
+
self.version = "1.0.0"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def execute(self, params: dict) -> dict:
|
|
19
|
+
"""Execute help command."""
|
|
20
|
+
try:
|
|
21
|
+
command = params.get("command")
|
|
22
|
+
|
|
23
|
+
if command:
|
|
24
|
+
# Get help for specific command
|
|
25
|
+
help_info = self._get_command_help(command)
|
|
26
|
+
return {
|
|
27
|
+
"command": command,
|
|
28
|
+
"help": help_info,
|
|
29
|
+
"timestamp": self._get_timestamp()
|
|
30
|
+
}
|
|
31
|
+
else:
|
|
32
|
+
# Get general help
|
|
33
|
+
return {
|
|
34
|
+
"help": "MCP Proxy Adapter - Available commands: echo, list, health, help",
|
|
35
|
+
"usage": "Use 'help' with a command name to get specific help",
|
|
36
|
+
"timestamp": self._get_timestamp()
|
|
37
|
+
}
|
|
38
|
+
except Exception as e:
|
|
39
|
+
raise MicroserviceError(f"Help command failed: {str(e)}")
|
|
40
|
+
|
|
41
|
+
def _get_command_help(self, command: str) -> str:
|
|
42
|
+
"""Get help for specific command."""
|
|
43
|
+
help_map = {
|
|
44
|
+
"echo": "Echo command - returns the input message. Usage: {'message': 'your message'}",
|
|
45
|
+
"list": "List command - returns available commands. Usage: {}",
|
|
46
|
+
"health": "Health command - returns server health status. Usage: {}",
|
|
47
|
+
"help": "Help command - provides usage information. Usage: {'command': 'command_name'} (optional)"
|
|
48
|
+
}
|
|
49
|
+
return help_map.get(command, f"No help available for command '{command}'")
|
|
50
|
+
|
|
51
|
+
def _get_timestamp(self):
|
|
52
|
+
"""Get current timestamp."""
|
|
53
|
+
import time
|
|
54
|
+
return time.time()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""
|
|
2
|
+
List command implementation.
|
|
3
|
+
"""
|
|
4
|
+
from mcp_proxy_adapter.commands.base import BaseCommand
|
|
5
|
+
from mcp_proxy_adapter.core.errors import MicroserviceError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ListCommand(BaseCommand):
|
|
9
|
+
"""List command that returns available commands."""
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
super().__init__()
|
|
13
|
+
self.name = "list"
|
|
14
|
+
self.description = "List available commands"
|
|
15
|
+
self.version = "1.0.0"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def execute(self, params: dict) -> dict:
|
|
19
|
+
"""Execute list command."""
|
|
20
|
+
try:
|
|
21
|
+
# This is a simplified list - in a real implementation,
|
|
22
|
+
# this would query the command registry
|
|
23
|
+
commands = [
|
|
24
|
+
{
|
|
25
|
+
"name": "echo",
|
|
26
|
+
"description": "Echo command that returns the input message",
|
|
27
|
+
"version": "1.0.0"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "list",
|
|
31
|
+
"description": "List available commands",
|
|
32
|
+
"version": "1.0.0"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "health",
|
|
36
|
+
"description": "Health check command",
|
|
37
|
+
"version": "1.0.0"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "help",
|
|
41
|
+
"description": "Help command",
|
|
42
|
+
"version": "1.0.0"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
"commands": commands,
|
|
48
|
+
"count": len(commands),
|
|
49
|
+
"timestamp": self._get_timestamp()
|
|
50
|
+
}
|
|
51
|
+
except Exception as e:
|
|
52
|
+
raise MicroserviceError(f"List command failed: {str(e)}")
|
|
53
|
+
|
|
54
|
+
def _get_timestamp(self):
|
|
55
|
+
"""Get current timestamp."""
|
|
56
|
+
import time
|
|
57
|
+
return time.time()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Application Hooks
|
|
3
|
+
This module demonstrates application-level hooks in the full application example.
|
|
4
|
+
Author: Vasiliy Zdanovskiy
|
|
5
|
+
email: vasilyvz@gmail.com
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ApplicationHooks:
|
|
15
|
+
"""Application-level hooks."""
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Built-in Command Hooks
|
|
3
|
+
This module demonstrates hooks for built-in commands in the full application example.
|
|
4
|
+
Author: Vasiliy Zdanovskiy
|
|
5
|
+
email: vasilyvz@gmail.com
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BuiltinCommandHooks:
|
|
15
|
+
"""Hooks for built-in commands."""
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Full Application Example
|
|
4
|
+
This is a complete application that demonstrates all features of MCP Proxy Adapter framework:
|
|
5
|
+
- Built-in commands
|
|
6
|
+
- Custom commands
|
|
7
|
+
- Dynamically loaded commands
|
|
8
|
+
- Built-in command hooks
|
|
9
|
+
- Application hooks
|
|
10
|
+
Author: Vasiliy Zdanovskiy
|
|
11
|
+
email: vasilyvz@gmail.com
|
|
12
|
+
"""
|
|
13
|
+
import argparse
|
|
14
|
+
import asyncio
|
|
15
|
+
import json
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
from mcp_proxy_adapter.api.app import create_app
|
|
19
|
+
from mcp_proxy_adapter.core.server_engine import ServerEngineFactory
|
|
20
|
+
from mcp_proxy_adapter.core.server_adapter import ServerConfigAdapter
|
|
21
|
+
from mcp_proxy_adapter.client.proxy import ProxyClient
|
|
22
|
+
from mcp_proxy_adapter.commands.command_registry import registry
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def register_all_commands():
|
|
26
|
+
"""Register all available commands (built-in, load, queue)."""
|
|
27
|
+
from mcp_proxy_adapter.commands.load_command import LoadCommand
|
|
28
|
+
|
|
29
|
+
# Register load command
|
|
30
|
+
registry._commands["load"] = LoadCommand
|
|
31
|
+
registry._command_types["load"] = "builtin"
|
|
32
|
+
|
|
33
|
+
# Register queue commands (will fail gracefully if queuemgr not available)
|
|
34
|
+
try:
|
|
35
|
+
from mcp_proxy_adapter.commands.queue_commands import (
|
|
36
|
+
QueueAddJobCommand,
|
|
37
|
+
QueueStartJobCommand,
|
|
38
|
+
QueueStopJobCommand,
|
|
39
|
+
QueueDeleteJobCommand,
|
|
40
|
+
QueueGetJobStatusCommand,
|
|
41
|
+
QueueListJobsCommand,
|
|
42
|
+
QueueHealthCommand,
|
|
43
|
+
)
|
|
44
|
+
registry._commands["queue_add_job"] = QueueAddJobCommand
|
|
45
|
+
registry._command_types["queue_add_job"] = "builtin"
|
|
46
|
+
registry._commands["queue_start_job"] = QueueStartJobCommand
|
|
47
|
+
registry._command_types["queue_start_job"] = "builtin"
|
|
48
|
+
registry._commands["queue_stop_job"] = QueueStopJobCommand
|
|
49
|
+
registry._command_types["queue_stop_job"] = "builtin"
|
|
50
|
+
registry._commands["queue_delete_job"] = QueueDeleteJobCommand
|
|
51
|
+
registry._command_types["queue_delete_job"] = "builtin"
|
|
52
|
+
registry._commands["queue_get_job_status"] = QueueGetJobStatusCommand
|
|
53
|
+
registry._command_types["queue_get_job_status"] = "builtin"
|
|
54
|
+
registry._commands["queue_list_jobs"] = QueueListJobsCommand
|
|
55
|
+
registry._command_types["queue_list_jobs"] = "builtin"
|
|
56
|
+
registry._commands["queue_health"] = QueueHealthCommand
|
|
57
|
+
registry._command_types["queue_health"] = "builtin"
|
|
58
|
+
print("✅ Queue commands registered")
|
|
59
|
+
except Exception as e:
|
|
60
|
+
print(f"⚠️ Queue commands not available: {e}")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def main():
|
|
64
|
+
"""Minimal runnable entrypoint for full application example."""
|
|
65
|
+
parser = argparse.ArgumentParser(description="MCP Proxy Adapter Full Application")
|
|
66
|
+
parser.add_argument("--config", required=True, help="Path to configuration file")
|
|
67
|
+
parser.add_argument("--port", type=int, help="Port to run server on (override)")
|
|
68
|
+
parser.add_argument("--host", default="0.0.0.0", help="Host to bind to")
|
|
69
|
+
args = parser.parse_args()
|
|
70
|
+
|
|
71
|
+
cfg_path = Path(args.config)
|
|
72
|
+
if not cfg_path.exists():
|
|
73
|
+
print(f"❌ Configuration file not found: {cfg_path}")
|
|
74
|
+
raise SystemExit(1)
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
with cfg_path.open("r", encoding="utf-8") as f:
|
|
78
|
+
app_config = json.load(f)
|
|
79
|
+
except Exception as exc: # noqa: BLE001
|
|
80
|
+
print(f"❌ Failed to load configuration: {exc}")
|
|
81
|
+
raise SystemExit(1)
|
|
82
|
+
|
|
83
|
+
if args.port:
|
|
84
|
+
app_config.setdefault("server", {}).update({"port": args.port})
|
|
85
|
+
print(f"🔧 Overriding port to {args.port}")
|
|
86
|
+
if args.host:
|
|
87
|
+
app_config.setdefault("server", {}).update({"host": args.host})
|
|
88
|
+
print(f"🔧 Overriding host to {args.host}")
|
|
89
|
+
|
|
90
|
+
# Strict protocol checks: forbid any form of mTLS over HTTP
|
|
91
|
+
proto = str(app_config.get("server", {}).get("protocol", "http")).lower()
|
|
92
|
+
ssl_cfg = app_config.get("ssl", {}) or {}
|
|
93
|
+
transport = app_config.get("transport", {}) or {}
|
|
94
|
+
require_client_cert = bool(
|
|
95
|
+
ssl_cfg.get("require_client_cert") or transport.get("verify_client")
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# --- SimpleConfig compatibility bridge: synthesize ssl section when absent ---
|
|
99
|
+
if not app_config.get("ssl") and isinstance(app_config.get("server"), dict):
|
|
100
|
+
srv = app_config["server"]
|
|
101
|
+
cert_file = srv.get("cert_file")
|
|
102
|
+
key_file = srv.get("key_file")
|
|
103
|
+
ca_file = srv.get("ca_cert_file")
|
|
104
|
+
if cert_file and key_file:
|
|
105
|
+
app_config["ssl"] = {
|
|
106
|
+
"enabled": True,
|
|
107
|
+
"cert_file": cert_file,
|
|
108
|
+
"key_file": key_file,
|
|
109
|
+
}
|
|
110
|
+
if ca_file:
|
|
111
|
+
app_config["ssl"]["ca_cert"] = ca_file
|
|
112
|
+
# For mtls protocol, enforce client verification
|
|
113
|
+
if proto == "mtls":
|
|
114
|
+
app_config.setdefault("transport", {}).update({"verify_client": True})
|
|
115
|
+
# Refresh local vars after synthesis
|
|
116
|
+
ssl_cfg = app_config.get("ssl", {}) or {}
|
|
117
|
+
transport = app_config.get("transport", {}) or {}
|
|
118
|
+
require_client_cert = bool(
|
|
119
|
+
ssl_cfg.get("require_client_cert") or transport.get("verify_client")
|
|
120
|
+
)
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
if proto == "http":
|
|
124
|
+
if require_client_cert:
|
|
125
|
+
raise SystemExit(
|
|
126
|
+
"CRITICAL CONFIG ERROR: mTLS (client certificate verification) cannot be used with HTTP. "
|
|
127
|
+
"Switch protocol to 'mtls' (or 'https' without client verification), and configure SSL certificates."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
if proto == "mtls":
|
|
131
|
+
if not ssl_cfg.get("enabled"):
|
|
132
|
+
raise SystemExit(
|
|
133
|
+
"CRITICAL CONFIG ERROR: Protocol 'mtls' requires SSL to be enabled."
|
|
134
|
+
)
|
|
135
|
+
if not require_client_cert:
|
|
136
|
+
raise SystemExit(
|
|
137
|
+
"CRITICAL CONFIG ERROR: Protocol 'mtls' requires client certificate verification. "
|
|
138
|
+
"Set ssl.require_client_cert=true or transport.verify_client=true."
|
|
139
|
+
)
|
|
140
|
+
cert = ssl_cfg.get("certfile") or ssl_cfg.get("cert_file")
|
|
141
|
+
key = ssl_cfg.get("keyfile") or ssl_cfg.get("key_file")
|
|
142
|
+
ca = ssl_cfg.get("cafile") or ssl_cfg.get("ca_cert") or ssl_cfg.get("ca_cert_file")
|
|
143
|
+
if not (cert and key and ca):
|
|
144
|
+
raise SystemExit(
|
|
145
|
+
"CRITICAL CONFIG ERROR: 'mtls' requires ssl.certfile/keyfile (or cert_file/key_file) and CA certificate."
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
app = create_app(title="Full Application Example", description="Complete MCP Proxy Adapter with all features", version="1.0.0", app_config=app_config, config_path=str(cfg_path))
|
|
149
|
+
|
|
150
|
+
port = int(app_config.get("server", {}).get("port", 8080))
|
|
151
|
+
host = app_config.get("server", {}).get("host", args.host)
|
|
152
|
+
|
|
153
|
+
print("🚀 Starting Full Application Example")
|
|
154
|
+
print(f"📋 Configuration: {cfg_path}")
|
|
155
|
+
print("============================================================")
|
|
156
|
+
|
|
157
|
+
# Register all commands
|
|
158
|
+
register_all_commands()
|
|
159
|
+
print(f"📋 Registered commands: {', '.join(sorted(registry.get_all_commands().keys()))}")
|
|
160
|
+
|
|
161
|
+
# Prepare server configuration for ServerEngine
|
|
162
|
+
server_config = {
|
|
163
|
+
"host": host,
|
|
164
|
+
"port": port,
|
|
165
|
+
"log_level": "info",
|
|
166
|
+
"reload": False,
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# Add SSL configuration if enabled
|
|
170
|
+
ssl_cfg = app_config.get("ssl", {})
|
|
171
|
+
if ssl_cfg.get("enabled"):
|
|
172
|
+
cert = ssl_cfg.get("certfile") or ssl_cfg.get("cert_file")
|
|
173
|
+
key = ssl_cfg.get("keyfile") or ssl_cfg.get("key_file")
|
|
174
|
+
ca = ssl_cfg.get("cafile") or ssl_cfg.get("ca_cert") or ssl_cfg.get("ca_cert_file")
|
|
175
|
+
if cert and key:
|
|
176
|
+
server_config["certfile"] = cert
|
|
177
|
+
server_config["keyfile"] = key
|
|
178
|
+
if ca:
|
|
179
|
+
server_config["ca_certs"] = ca
|
|
180
|
+
if ssl_cfg.get("verify_client") or app_config.get("transport", {}).get("verify_client"):
|
|
181
|
+
server_config["verify_mode"] = 2 # ssl.CERT_REQUIRED
|
|
182
|
+
if "chk_hostname" in ssl_cfg:
|
|
183
|
+
server_config["check_hostname"] = ssl_cfg["chk_hostname"]
|
|
184
|
+
|
|
185
|
+
# Optional proxy registration
|
|
186
|
+
pr = (app_config.get("proxy_registration") or {}) if isinstance(app_config, dict) else {}
|
|
187
|
+
name = pr.get("server_id") or pr.get("server_name") or "mcp-adapter"
|
|
188
|
+
scheme = "https" if str(app_config.get("server", {}).get("protocol", "http")) in ("https", "mtls") else "http"
|
|
189
|
+
advertised_host = app_config.get("server", {}).get("advertised_host") or "mcp-adapter"
|
|
190
|
+
advertised_url = f"{scheme}://{advertised_host}:{port}"
|
|
191
|
+
|
|
192
|
+
async def _run():
|
|
193
|
+
"""Run server with proxy registration and heartbeat."""
|
|
194
|
+
heartbeat_task = None
|
|
195
|
+
try:
|
|
196
|
+
if pr.get("enabled") and pr.get("proxy_url"):
|
|
197
|
+
pc = ProxyClient(pr["proxy_url"])
|
|
198
|
+
try:
|
|
199
|
+
pc.register(name=name, url=advertised_url, capabilities=["jsonrpc"], metadata={})
|
|
200
|
+
print(f"✅ Registered on proxy as {name} -> {advertised_url}")
|
|
201
|
+
except Exception as exc: # noqa: BLE001
|
|
202
|
+
print(f"⚠️ Proxy registration failed: {exc}")
|
|
203
|
+
|
|
204
|
+
async def _hb():
|
|
205
|
+
interval = int((pr.get("heartbeat") or {}).get("interval", 15))
|
|
206
|
+
while True:
|
|
207
|
+
try:
|
|
208
|
+
pc.heartbeat(name=name, url=advertised_url)
|
|
209
|
+
except Exception:
|
|
210
|
+
pass
|
|
211
|
+
await asyncio.sleep(max(2, interval))
|
|
212
|
+
|
|
213
|
+
heartbeat_task = asyncio.create_task(_hb())
|
|
214
|
+
|
|
215
|
+
# Use ServerEngine with hypercorn (via ServerEngineFactory)
|
|
216
|
+
engine = ServerEngineFactory.get_engine("hypercorn")
|
|
217
|
+
if not engine:
|
|
218
|
+
raise RuntimeError("Hypercorn engine not available")
|
|
219
|
+
|
|
220
|
+
# Convert SSL config if needed
|
|
221
|
+
if "ssl" in app_config:
|
|
222
|
+
ssl_converted = ServerConfigAdapter.convert_ssl_config_for_engine(
|
|
223
|
+
app_config["ssl"], "hypercorn"
|
|
224
|
+
)
|
|
225
|
+
server_config.update(ssl_converted)
|
|
226
|
+
|
|
227
|
+
# Run server using hypercorn engine
|
|
228
|
+
# Use create_task instead of run_server to avoid nested event loop
|
|
229
|
+
from hypercorn.asyncio import serve
|
|
230
|
+
from hypercorn.config import Config as HypercornConfig
|
|
231
|
+
|
|
232
|
+
hypercorn_config = HypercornConfig()
|
|
233
|
+
hypercorn_config.bind = [f"{host}:{port}"]
|
|
234
|
+
hypercorn_config.loglevel = "info"
|
|
235
|
+
|
|
236
|
+
# Add SSL configuration if enabled
|
|
237
|
+
if ssl_cfg.get("enabled"):
|
|
238
|
+
cert = ssl_cfg.get("certfile") or ssl_cfg.get("cert_file")
|
|
239
|
+
key = ssl_cfg.get("keyfile") or ssl_cfg.get("key_file")
|
|
240
|
+
ca = ssl_cfg.get("cafile") or ssl_cfg.get("ca_cert") or ssl_cfg.get("ca_cert_file")
|
|
241
|
+
if cert and key:
|
|
242
|
+
hypercorn_config.certfile = cert
|
|
243
|
+
hypercorn_config.keyfile = key
|
|
244
|
+
if ca:
|
|
245
|
+
hypercorn_config.ca_certs = ca
|
|
246
|
+
if ssl_cfg.get("verify_client") or app_config.get("transport", {}).get("verify_client"):
|
|
247
|
+
hypercorn_config.verify_mode = 2 # ssl.CERT_REQUIRED
|
|
248
|
+
|
|
249
|
+
await serve(app, hypercorn_config)
|
|
250
|
+
finally:
|
|
251
|
+
if heartbeat_task:
|
|
252
|
+
heartbeat_task.cancel()
|
|
253
|
+
if pr.get("enabled") and pr.get("proxy_url"):
|
|
254
|
+
try:
|
|
255
|
+
ProxyClient(pr["proxy_url"]).unregister(name)
|
|
256
|
+
print(f"🛑 Unregistered from proxy: {name}")
|
|
257
|
+
except Exception:
|
|
258
|
+
pass
|
|
259
|
+
|
|
260
|
+
asyncio.run(_run())
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
if __name__ == "__main__":
|
|
264
|
+
main()
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Proxy Registration Endpoints
|
|
3
|
+
This module provides proxy registration endpoints for testing.
|
|
4
|
+
Author: Vasiliy Zdanovskiy
|
|
5
|
+
email: vasilyvz@gmail.com
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from fastapi import APIRouter, HTTPException
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
from typing import Dict, List, Optional
|
|
11
|
+
import time
|
|
12
|
+
import uuid
|
|
13
|
+
|
|
14
|
+
# In-memory registry for testing
|
|
15
|
+
_registry: Dict[str, Dict] = {}
|
|
16
|
+
router = APIRouter(prefix="/proxy", tags=["proxy"])
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ServerRegistration(BaseModel):
|
|
20
|
+
"""Server registration request model."""
|
|
21
|
+
|
|
22
|
+
server_id: str
|
|
23
|
+
server_url: str
|
|
24
|
+
server_name: str
|
|
25
|
+
description: Optional[str] = None
|
|
26
|
+
version: Optional[str] = "1.0.0"
|
|
27
|
+
capabilities: Optional[List[str]] = None
|
|
28
|
+
endpoints: Optional[Dict[str, str]] = None
|
|
29
|
+
auth_method: Optional[str] = "none"
|
|
30
|
+
security_enabled: Optional[bool] = False
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ServerUnregistration(BaseModel):
|
|
34
|
+
"""Server unregistration request model."""
|
|
35
|
+
|
|
36
|
+
server_key: str # Use server_key directly
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class HeartbeatData(BaseModel):
|
|
40
|
+
"""Heartbeat data model."""
|
|
41
|
+
|
|
42
|
+
server_id: str
|
|
43
|
+
server_key: str
|
|
44
|
+
timestamp: Optional[int] = None
|
|
45
|
+
status: Optional[str] = "healthy"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class RegistrationResponse(BaseModel):
|
|
49
|
+
"""Registration response model."""
|
|
50
|
+
|
|
51
|
+
success: bool
|
|
52
|
+
server_key: str
|
|
53
|
+
message: str
|
|
54
|
+
copy_number: int
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class DiscoveryResponse(BaseModel):
|
|
58
|
+
"""Discovery response model."""
|
|
59
|
+
|
|
60
|
+
success: bool
|
|
61
|
+
servers: List[Dict]
|
|
62
|
+
total: int
|
|
63
|
+
active: int
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@router.post("/register", response_model=RegistrationResponse)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@router.post("/unregister")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@router.post("/heartbeat")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@router.get("/discover", response_model=DiscoveryResponse)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@router.get("/status")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@router.delete("/clear")
|