mcp-proxy-adapter 4.1.1__py3-none-any.whl → 6.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_proxy_adapter/__main__.py +12 -0
- mcp_proxy_adapter/api/app.py +254 -33
- mcp_proxy_adapter/api/handlers.py +32 -6
- mcp_proxy_adapter/api/middleware/__init__.py +36 -30
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
- mcp_proxy_adapter/api/middleware/factory.py +243 -0
- mcp_proxy_adapter/api/middleware/logging.py +32 -6
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +135 -0
- mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
- mcp_proxy_adapter/api/middleware/unified_security.py +152 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
- mcp_proxy_adapter/commands/__init__.py +19 -4
- mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
- mcp_proxy_adapter/commands/base.py +66 -32
- mcp_proxy_adapter/commands/builtin_commands.py +95 -0
- mcp_proxy_adapter/commands/catalog_manager.py +838 -0
- mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
- mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
- mcp_proxy_adapter/commands/command_registry.py +711 -354
- mcp_proxy_adapter/commands/dependency_manager.py +245 -0
- mcp_proxy_adapter/commands/echo_command.py +81 -0
- mcp_proxy_adapter/commands/health_command.py +7 -0
- mcp_proxy_adapter/commands/help_command.py +21 -14
- mcp_proxy_adapter/commands/hooks.py +200 -167
- mcp_proxy_adapter/commands/key_management_command.py +506 -0
- mcp_proxy_adapter/commands/load_command.py +176 -0
- mcp_proxy_adapter/commands/plugins_command.py +235 -0
- mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
- mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
- mcp_proxy_adapter/commands/reload_command.py +48 -50
- mcp_proxy_adapter/commands/result.py +1 -0
- mcp_proxy_adapter/commands/role_test_command.py +141 -0
- mcp_proxy_adapter/commands/roles_management_command.py +697 -0
- mcp_proxy_adapter/commands/security_command.py +488 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +483 -0
- mcp_proxy_adapter/commands/token_management_command.py +529 -0
- mcp_proxy_adapter/commands/transport_management_command.py +144 -0
- mcp_proxy_adapter/commands/unload_command.py +158 -0
- mcp_proxy_adapter/config.py +159 -2
- mcp_proxy_adapter/core/app_factory.py +326 -0
- mcp_proxy_adapter/core/auth_validator.py +606 -0
- mcp_proxy_adapter/core/certificate_utils.py +827 -0
- mcp_proxy_adapter/core/client_security.py +384 -0
- mcp_proxy_adapter/core/config_converter.py +405 -0
- mcp_proxy_adapter/core/config_validator.py +218 -0
- mcp_proxy_adapter/core/logging.py +19 -3
- mcp_proxy_adapter/core/mtls_asgi.py +156 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/protocol_manager.py +235 -0
- mcp_proxy_adapter/core/proxy_client.py +602 -0
- mcp_proxy_adapter/core/proxy_registration.py +522 -0
- mcp_proxy_adapter/core/role_utils.py +426 -0
- mcp_proxy_adapter/core/security_adapter.py +370 -0
- mcp_proxy_adapter/core/security_factory.py +239 -0
- mcp_proxy_adapter/core/security_integration.py +277 -0
- mcp_proxy_adapter/core/server_adapter.py +345 -0
- mcp_proxy_adapter/core/server_engine.py +364 -0
- mcp_proxy_adapter/core/settings.py +1 -0
- mcp_proxy_adapter/core/ssl_utils.py +233 -0
- mcp_proxy_adapter/core/transport_manager.py +292 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
- mcp_proxy_adapter/custom_openapi.py +22 -11
- mcp_proxy_adapter/examples/README.md +230 -97
- mcp_proxy_adapter/examples/README_EN.md +258 -0
- mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
- mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
- mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
- mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
- mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +39 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +25 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
- mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
- mcp_proxy_adapter/examples/cert_config.json +9 -0
- mcp_proxy_adapter/examples/certs/admin.crt +32 -0
- mcp_proxy_adapter/examples/certs/admin.key +52 -0
- mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
- mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
- mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
- mcp_proxy_adapter/examples/certs/client.crt +32 -0
- mcp_proxy_adapter/examples/certs/client.key +52 -0
- mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
- mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
- mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
- mcp_proxy_adapter/examples/certs/client_user.key +52 -0
- mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
- mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
- mcp_proxy_adapter/examples/certs/readonly.key +52 -0
- mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/server.crt +32 -0
- mcp_proxy_adapter/examples/certs/server.key +52 -0
- mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
- mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
- mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
- mcp_proxy_adapter/examples/certs/user.crt +32 -0
- mcp_proxy_adapter/examples/certs/user.key +52 -0
- mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
- mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
- mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
- mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
- mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
- mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
- mcp_proxy_adapter/examples/commands/__init__.py +1 -0
- mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
- mcp_proxy_adapter/examples/debug_request_state.py +144 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
- mcp_proxy_adapter/examples/demo_client.py +341 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
- mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
- mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
- mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
- mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
- mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
- mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
- mcp_proxy_adapter/examples/full_application/main.py +138 -0
- mcp_proxy_adapter/examples/full_application/roles.json +21 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
- mcp_proxy_adapter/examples/generate_certificates.py +121 -0
- mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
- mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
- mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
- mcp_proxy_adapter/examples/roles.json +38 -0
- mcp_proxy_adapter/examples/run_example.py +81 -0
- mcp_proxy_adapter/examples/run_security_tests.py +326 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
- mcp_proxy_adapter/examples/security_test_client.py +743 -0
- mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
- mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
- mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
- mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
- mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
- mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
- mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
- mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
- mcp_proxy_adapter/examples/test_examples.py +344 -0
- mcp_proxy_adapter/examples/universal_client.py +628 -0
- mcp_proxy_adapter/main.py +186 -0
- mcp_proxy_adapter/utils/config_generator.py +639 -0
- mcp_proxy_adapter/version.py +2 -1
- mcp_proxy_adapter-6.1.0.dist-info/METADATA +205 -0
- mcp_proxy_adapter-6.1.0.dist-info/RECORD +193 -0
- mcp_proxy_adapter-6.1.0.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/licenses/LICENSE +2 -2
- mcp_proxy_adapter/api/middleware/auth.py +0 -146
- mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
- mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
- mcp_proxy_adapter/examples/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/README.md +0 -60
- mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
- mcp_proxy_adapter/examples/basic_server/config.json +0 -35
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
- mcp_proxy_adapter/examples/basic_server/server.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
- mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
- mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
- mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
- mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
- mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
- mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
- mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
- mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
- mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
- mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
- mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
- mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
- mcp_proxy_adapter/examples/deployment/README.md +0 -49
- mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
- mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
- mcp_proxy_adapter/examples/deployment/config.json +0 -29
- mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
- mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
- mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
- mcp_proxy_adapter/examples/deployment/run.sh +0 -43
- mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
- mcp_proxy_adapter/schemas/base_schema.json +0 -114
- mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
- mcp_proxy_adapter/tests/__init__.py +0 -0
- mcp_proxy_adapter/tests/api/__init__.py +0 -3
- mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
- mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
- mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
- mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
- mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
- mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
- mcp_proxy_adapter/tests/commands/__init__.py +0 -3
- mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
- mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
- mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
- mcp_proxy_adapter/tests/conftest.py +0 -131
- mcp_proxy_adapter/tests/functional/__init__.py +0 -3
- mcp_proxy_adapter/tests/functional/test_api.py +0 -253
- mcp_proxy_adapter/tests/integration/__init__.py +0 -3
- mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
- mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
- mcp_proxy_adapter/tests/performance/__init__.py +0 -3
- mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
- mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
- mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
- mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
- mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
- mcp_proxy_adapter/tests/test_base_command.py +0 -123
- mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
- mcp_proxy_adapter/tests/test_command_registry.py +0 -281
- mcp_proxy_adapter/tests/test_config.py +0 -127
- mcp_proxy_adapter/tests/test_utils.py +0 -65
- mcp_proxy_adapter/tests/unit/__init__.py +0 -3
- mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
- mcp_proxy_adapter/tests/unit/test_config.py +0 -217
- mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
- mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,506 @@
|
|
1
|
+
"""
|
2
|
+
Key Management Command
|
3
|
+
|
4
|
+
This module provides commands for key management including generation,
|
5
|
+
validation, rotation, backup, and restoration.
|
6
|
+
|
7
|
+
Author: MCP Proxy Adapter Team
|
8
|
+
Version: 1.0.0
|
9
|
+
"""
|
10
|
+
|
11
|
+
import logging
|
12
|
+
import os
|
13
|
+
import shutil
|
14
|
+
from typing import Dict, List, Optional, Any
|
15
|
+
from pathlib import Path
|
16
|
+
from datetime import datetime
|
17
|
+
|
18
|
+
from .base import Command
|
19
|
+
from .result import CommandResult, SuccessResult, ErrorResult
|
20
|
+
from ..core.certificate_utils import CertificateUtils
|
21
|
+
|
22
|
+
logger = logging.getLogger(__name__)
|
23
|
+
|
24
|
+
|
25
|
+
class KeyResult:
|
26
|
+
"""
|
27
|
+
Result class for key operations.
|
28
|
+
|
29
|
+
Contains key information and operation status.
|
30
|
+
"""
|
31
|
+
|
32
|
+
def __init__(self, key_path: str, key_type: str, key_size: int,
|
33
|
+
created_date: Optional[str] = None, expiry_date: Optional[str] = None,
|
34
|
+
status: str = "valid", error: Optional[str] = None):
|
35
|
+
"""
|
36
|
+
Initialize key result.
|
37
|
+
|
38
|
+
Args:
|
39
|
+
key_path: Path to key file
|
40
|
+
key_type: Type of key (RSA, ECDSA, etc.)
|
41
|
+
key_size: Key size in bits
|
42
|
+
created_date: Key creation date
|
43
|
+
expiry_date: Key expiry date
|
44
|
+
status: Key status (valid, expired, error)
|
45
|
+
error: Error message if any
|
46
|
+
"""
|
47
|
+
self.key_path = key_path
|
48
|
+
self.key_type = key_type
|
49
|
+
self.key_size = key_size
|
50
|
+
self.created_date = created_date
|
51
|
+
self.expiry_date = expiry_date
|
52
|
+
self.status = status
|
53
|
+
self.error = error
|
54
|
+
|
55
|
+
def to_dict(self) -> Dict[str, Any]:
|
56
|
+
"""
|
57
|
+
Convert to dictionary format.
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
Dictionary representation
|
61
|
+
"""
|
62
|
+
return {
|
63
|
+
"key_path": self.key_path,
|
64
|
+
"key_type": self.key_type,
|
65
|
+
"key_size": self.key_size,
|
66
|
+
"created_date": self.created_date,
|
67
|
+
"expiry_date": self.expiry_date,
|
68
|
+
"status": self.status,
|
69
|
+
"error": self.error
|
70
|
+
}
|
71
|
+
|
72
|
+
def get_schema(self) -> Dict[str, Any]:
|
73
|
+
"""
|
74
|
+
Get JSON schema for this result.
|
75
|
+
|
76
|
+
Returns:
|
77
|
+
JSON schema dictionary
|
78
|
+
"""
|
79
|
+
return {
|
80
|
+
"type": "object",
|
81
|
+
"properties": {
|
82
|
+
"key_path": {"type": "string", "description": "Path to key file"},
|
83
|
+
"key_type": {"type": "string", "description": "Type of key"},
|
84
|
+
"key_size": {"type": "integer", "description": "Key size in bits"},
|
85
|
+
"created_date": {"type": "string", "description": "Key creation date"},
|
86
|
+
"expiry_date": {"type": "string", "description": "Key expiry date"},
|
87
|
+
"status": {"type": "string", "enum": ["valid", "expired", "error"],
|
88
|
+
"description": "Key status"},
|
89
|
+
"error": {"type": "string", "description": "Error message if any"}
|
90
|
+
},
|
91
|
+
"required": ["key_path", "key_type", "key_size", "status"]
|
92
|
+
}
|
93
|
+
|
94
|
+
|
95
|
+
class KeyManagementCommand(Command):
|
96
|
+
"""
|
97
|
+
Command for key management.
|
98
|
+
|
99
|
+
Provides methods for generating, validating, rotating, backing up, and restoring keys.
|
100
|
+
"""
|
101
|
+
|
102
|
+
# Command metadata
|
103
|
+
name = "key_management"
|
104
|
+
version = "1.0.0"
|
105
|
+
descr = "Private key generation, validation, and management"
|
106
|
+
category = "security"
|
107
|
+
author = "MCP Proxy Adapter Team"
|
108
|
+
email = "team@mcp-proxy-adapter.com"
|
109
|
+
source_url = "https://github.com/mcp-proxy-adapter"
|
110
|
+
result_class = KeyResult
|
111
|
+
|
112
|
+
def __init__(self):
|
113
|
+
"""Initialize key management command."""
|
114
|
+
super().__init__()
|
115
|
+
self.certificate_utils = CertificateUtils()
|
116
|
+
|
117
|
+
async def execute(self, **kwargs) -> CommandResult:
|
118
|
+
"""
|
119
|
+
Execute key management command.
|
120
|
+
|
121
|
+
Args:
|
122
|
+
**kwargs: Command parameters including:
|
123
|
+
- action: Action to perform (key_generate, key_validate, key_rotate, key_backup, key_restore)
|
124
|
+
- key_type: Type of key to generate (RSA, ECDSA)
|
125
|
+
- key_size: Key size in bits for generation
|
126
|
+
- output_path: Output path for key generation
|
127
|
+
- password: Password for key encryption
|
128
|
+
- key_path: Key file path for validation, rotation, backup
|
129
|
+
- old_key_path: Old key path for rotation
|
130
|
+
- new_key_path: New key path for rotation
|
131
|
+
- cert_path: Certificate path for rotation
|
132
|
+
- backup_old: Whether to backup old key during rotation
|
133
|
+
- backup_path: Backup path for key backup/restore
|
134
|
+
- encrypt_backup: Whether to encrypt backup
|
135
|
+
|
136
|
+
Returns:
|
137
|
+
CommandResult with key operation status
|
138
|
+
"""
|
139
|
+
action = kwargs.get("action", "key_validate")
|
140
|
+
|
141
|
+
if action == "key_generate":
|
142
|
+
key_type = kwargs.get("key_type")
|
143
|
+
key_size = kwargs.get("key_size")
|
144
|
+
output_path = kwargs.get("output_path")
|
145
|
+
password = kwargs.get("password")
|
146
|
+
return await self.key_generate(key_type, key_size, output_path, password)
|
147
|
+
elif action == "key_validate":
|
148
|
+
key_path = kwargs.get("key_path")
|
149
|
+
password = kwargs.get("password")
|
150
|
+
return await self.key_validate(key_path, password)
|
151
|
+
elif action == "key_rotate":
|
152
|
+
old_key_path = kwargs.get("old_key_path")
|
153
|
+
new_key_path = kwargs.get("new_key_path")
|
154
|
+
cert_path = kwargs.get("cert_path")
|
155
|
+
backup_old = kwargs.get("backup_old", True)
|
156
|
+
return await self.key_rotate(old_key_path, new_key_path, cert_path, backup_old)
|
157
|
+
elif action == "key_backup":
|
158
|
+
key_path = kwargs.get("key_path")
|
159
|
+
backup_path = kwargs.get("backup_path")
|
160
|
+
encrypt_backup = kwargs.get("encrypt_backup", True)
|
161
|
+
password = kwargs.get("password")
|
162
|
+
return await self.key_backup(key_path, backup_path, encrypt_backup, password)
|
163
|
+
elif action == "key_restore":
|
164
|
+
backup_path = kwargs.get("backup_path")
|
165
|
+
key_path = kwargs.get("key_path")
|
166
|
+
password = kwargs.get("password")
|
167
|
+
return await self.key_restore(backup_path, key_path, password)
|
168
|
+
else:
|
169
|
+
return ErrorResult(
|
170
|
+
message=f"Unknown action: {action}. Supported actions: key_generate, key_validate, key_rotate, key_backup, key_restore"
|
171
|
+
)
|
172
|
+
|
173
|
+
async def key_generate(self, key_type: str, key_size: int, output_path: str,
|
174
|
+
password: Optional[str] = None) -> CommandResult:
|
175
|
+
"""
|
176
|
+
Generate a new private key.
|
177
|
+
|
178
|
+
Args:
|
179
|
+
key_type: Type of key to generate (RSA, ECDSA)
|
180
|
+
key_size: Key size in bits
|
181
|
+
output_path: Path to save the generated key
|
182
|
+
password: Optional password to encrypt the key
|
183
|
+
|
184
|
+
Returns:
|
185
|
+
CommandResult with key generation status
|
186
|
+
"""
|
187
|
+
try:
|
188
|
+
logger.info(f"Generating {key_type} key with size {key_size} bits")
|
189
|
+
|
190
|
+
# Validate parameters
|
191
|
+
if key_type not in ["RSA", "ECDSA"]:
|
192
|
+
return ErrorResult(
|
193
|
+
message="Key type must be RSA or ECDSA"
|
194
|
+
)
|
195
|
+
|
196
|
+
if key_type == "ECDSA":
|
197
|
+
if key_size not in [256, 384, 521]:
|
198
|
+
return ErrorResult(
|
199
|
+
message="ECDSA key size must be 256, 384, or 521 bits"
|
200
|
+
)
|
201
|
+
elif key_size < 1024:
|
202
|
+
return ErrorResult(
|
203
|
+
message="Key size must be at least 1024 bits"
|
204
|
+
)
|
205
|
+
|
206
|
+
# Create output directory if it doesn't exist
|
207
|
+
output_dir = os.path.dirname(output_path)
|
208
|
+
if output_dir:
|
209
|
+
Path(output_dir).mkdir(parents=True, exist_ok=True)
|
210
|
+
|
211
|
+
# Generate key
|
212
|
+
result = self.certificate_utils.generate_private_key(
|
213
|
+
key_type, key_size, output_path
|
214
|
+
)
|
215
|
+
|
216
|
+
if not result["success"]:
|
217
|
+
return ErrorResult(
|
218
|
+
message=f"Key generation failed: {result['error']}"
|
219
|
+
)
|
220
|
+
|
221
|
+
key_result = KeyResult(
|
222
|
+
key_path=output_path,
|
223
|
+
key_type=key_type,
|
224
|
+
key_size=key_size,
|
225
|
+
created_date=datetime.now().isoformat(),
|
226
|
+
status="valid"
|
227
|
+
)
|
228
|
+
|
229
|
+
logger.info(f"Key generated successfully: {output_path}")
|
230
|
+
return SuccessResult(
|
231
|
+
data={
|
232
|
+
"key": key_result.to_dict(),
|
233
|
+
"details": result
|
234
|
+
}
|
235
|
+
)
|
236
|
+
|
237
|
+
except Exception as e:
|
238
|
+
logger.error(f"Key generation failed: {e}")
|
239
|
+
return ErrorResult(
|
240
|
+
message=f"Key generation failed: {str(e)}"
|
241
|
+
)
|
242
|
+
|
243
|
+
async def key_validate(self, key_path: str, password: Optional[str] = None) -> CommandResult:
|
244
|
+
"""
|
245
|
+
Validate a private key.
|
246
|
+
|
247
|
+
Args:
|
248
|
+
key_path: Path to key file to validate
|
249
|
+
password: Optional password if key is encrypted
|
250
|
+
|
251
|
+
Returns:
|
252
|
+
CommandResult with key validation status
|
253
|
+
"""
|
254
|
+
try:
|
255
|
+
logger.info(f"Validating key: {key_path}")
|
256
|
+
|
257
|
+
# Validate parameters
|
258
|
+
if not key_path or not os.path.exists(key_path):
|
259
|
+
return ErrorResult(
|
260
|
+
message=f"Key file not found: {key_path}"
|
261
|
+
)
|
262
|
+
|
263
|
+
# Validate key
|
264
|
+
result = self.certificate_utils.validate_private_key(key_path)
|
265
|
+
|
266
|
+
if not result["success"]:
|
267
|
+
return ErrorResult(
|
268
|
+
message=f"Key validation failed: {result['error']}"
|
269
|
+
)
|
270
|
+
|
271
|
+
key_result = KeyResult(
|
272
|
+
key_path=key_path,
|
273
|
+
key_type=result.get("key_type", "unknown"),
|
274
|
+
key_size=result.get("key_size", 0),
|
275
|
+
created_date=result.get("created_date"),
|
276
|
+
status="valid"
|
277
|
+
)
|
278
|
+
|
279
|
+
logger.info(f"Key validation completed: {key_path}")
|
280
|
+
return SuccessResult(
|
281
|
+
data={
|
282
|
+
"key": key_result.to_dict()
|
283
|
+
}
|
284
|
+
)
|
285
|
+
|
286
|
+
except Exception as e:
|
287
|
+
logger.error(f"Key validation failed: {e}")
|
288
|
+
return ErrorResult(
|
289
|
+
message=f"Key validation failed: {str(e)}"
|
290
|
+
)
|
291
|
+
|
292
|
+
async def key_rotate(self, old_key_path: str, new_key_path: str,
|
293
|
+
cert_path: Optional[str] = None, backup_old: bool = True) -> CommandResult:
|
294
|
+
"""
|
295
|
+
Rotate a private key.
|
296
|
+
|
297
|
+
Args:
|
298
|
+
old_key_path: Path to old key file
|
299
|
+
new_key_path: Path to new key file
|
300
|
+
cert_path: Optional certificate path to update with new key
|
301
|
+
backup_old: Whether to backup the old key
|
302
|
+
|
303
|
+
Returns:
|
304
|
+
CommandResult with key rotation status
|
305
|
+
"""
|
306
|
+
try:
|
307
|
+
logger.info(f"Rotating key from {old_key_path} to {new_key_path}")
|
308
|
+
|
309
|
+
# Validate parameters
|
310
|
+
if not old_key_path or not os.path.exists(old_key_path):
|
311
|
+
return ErrorResult(
|
312
|
+
message=f"Old key file not found: {old_key_path}"
|
313
|
+
)
|
314
|
+
|
315
|
+
if not new_key_path or not os.path.exists(new_key_path):
|
316
|
+
return ErrorResult(
|
317
|
+
message=f"New key file not found: {new_key_path}"
|
318
|
+
)
|
319
|
+
|
320
|
+
# Validate both keys
|
321
|
+
old_key_validation = await self.key_validate(old_key_path)
|
322
|
+
new_key_validation = await self.key_validate(new_key_path)
|
323
|
+
|
324
|
+
if not old_key_validation.to_dict()["success"]:
|
325
|
+
return ErrorResult(
|
326
|
+
message=f"Old key validation failed: {old_key_validation.to_dict()['error']['message']}"
|
327
|
+
)
|
328
|
+
|
329
|
+
if not new_key_validation.to_dict()["success"]:
|
330
|
+
return ErrorResult(
|
331
|
+
message=f"New key validation failed: {new_key_validation.to_dict()['error']['message']}"
|
332
|
+
)
|
333
|
+
|
334
|
+
# Backup old key if requested
|
335
|
+
backup_path = None
|
336
|
+
if backup_old:
|
337
|
+
backup_path = f"{old_key_path}.backup.{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
338
|
+
shutil.copy2(old_key_path, backup_path)
|
339
|
+
logger.info(f"Old key backed up to: {backup_path}")
|
340
|
+
|
341
|
+
# Update certificate if provided
|
342
|
+
cert_updated = False
|
343
|
+
if cert_path and os.path.exists(cert_path):
|
344
|
+
try:
|
345
|
+
# This would require implementing certificate re-signing
|
346
|
+
# For now, we'll just note that it needs to be done
|
347
|
+
cert_updated = True
|
348
|
+
logger.info(f"Certificate {cert_path} needs to be re-signed with new key")
|
349
|
+
except Exception as e:
|
350
|
+
logger.warning(f"Could not update certificate {cert_path}: {e}")
|
351
|
+
|
352
|
+
# Replace old key with new key
|
353
|
+
shutil.copy2(new_key_path, old_key_path)
|
354
|
+
|
355
|
+
logger.info(f"Key rotation completed successfully")
|
356
|
+
return SuccessResult(
|
357
|
+
data={
|
358
|
+
"old_key_path": old_key_path,
|
359
|
+
"new_key_path": new_key_path,
|
360
|
+
"backup_path": backup_path,
|
361
|
+
"cert_updated": cert_updated,
|
362
|
+
"message": "Key rotation completed successfully"
|
363
|
+
}
|
364
|
+
)
|
365
|
+
|
366
|
+
except Exception as e:
|
367
|
+
logger.error(f"Key rotation failed: {e}")
|
368
|
+
return ErrorResult(
|
369
|
+
message=f"Key rotation failed: {str(e)}"
|
370
|
+
)
|
371
|
+
|
372
|
+
async def key_backup(self, key_path: str, backup_path: str,
|
373
|
+
encrypt_backup: bool = True, password: Optional[str] = None) -> CommandResult:
|
374
|
+
"""
|
375
|
+
Backup a private key.
|
376
|
+
|
377
|
+
Args:
|
378
|
+
key_path: Path to key file to backup
|
379
|
+
backup_path: Path to save the backup
|
380
|
+
encrypt_backup: Whether to encrypt the backup
|
381
|
+
password: Password for backup encryption
|
382
|
+
|
383
|
+
Returns:
|
384
|
+
CommandResult with backup status
|
385
|
+
"""
|
386
|
+
try:
|
387
|
+
logger.info(f"Backing up key: {key_path}")
|
388
|
+
|
389
|
+
# Validate parameters
|
390
|
+
if not key_path or not os.path.exists(key_path):
|
391
|
+
return ErrorResult(
|
392
|
+
message=f"Key file not found: {key_path}"
|
393
|
+
)
|
394
|
+
|
395
|
+
# Validate key before backup
|
396
|
+
key_validation = await self.key_validate(key_path)
|
397
|
+
if not key_validation.to_dict()["success"]:
|
398
|
+
return ErrorResult(
|
399
|
+
message=f"Key validation failed before backup: {key_validation.to_dict()['error']['message']}"
|
400
|
+
)
|
401
|
+
|
402
|
+
# Create backup directory if it doesn't exist
|
403
|
+
backup_dir = os.path.dirname(backup_path)
|
404
|
+
if backup_dir:
|
405
|
+
Path(backup_dir).mkdir(parents=True, exist_ok=True)
|
406
|
+
|
407
|
+
# Create backup
|
408
|
+
if encrypt_backup and password:
|
409
|
+
# Encrypted backup
|
410
|
+
result = self.certificate_utils.create_encrypted_backup(
|
411
|
+
key_path, backup_path, password
|
412
|
+
)
|
413
|
+
if not result["success"]:
|
414
|
+
return ErrorResult(
|
415
|
+
message=f"Encrypted backup failed: {result['error']}"
|
416
|
+
)
|
417
|
+
else:
|
418
|
+
# Simple file copy
|
419
|
+
shutil.copy2(key_path, backup_path)
|
420
|
+
|
421
|
+
# Verify backup
|
422
|
+
if not os.path.exists(backup_path):
|
423
|
+
return ErrorResult(
|
424
|
+
message="Backup file was not created"
|
425
|
+
)
|
426
|
+
|
427
|
+
logger.info(f"Key backup completed successfully: {backup_path}")
|
428
|
+
return SuccessResult(
|
429
|
+
data={
|
430
|
+
"key_path": key_path,
|
431
|
+
"backup_path": backup_path,
|
432
|
+
"encrypted": encrypt_backup,
|
433
|
+
"backup_size": os.path.getsize(backup_path),
|
434
|
+
"backup_date": datetime.now().isoformat()
|
435
|
+
}
|
436
|
+
)
|
437
|
+
|
438
|
+
except Exception as e:
|
439
|
+
logger.error(f"Key backup failed: {e}")
|
440
|
+
return ErrorResult(
|
441
|
+
message=f"Key backup failed: {str(e)}"
|
442
|
+
)
|
443
|
+
|
444
|
+
async def key_restore(self, backup_path: str, key_path: str,
|
445
|
+
password: Optional[str] = None) -> CommandResult:
|
446
|
+
"""
|
447
|
+
Restore a private key from backup.
|
448
|
+
|
449
|
+
Args:
|
450
|
+
backup_path: Path to backup file
|
451
|
+
key_path: Path to restore the key to
|
452
|
+
password: Password if backup is encrypted
|
453
|
+
|
454
|
+
Returns:
|
455
|
+
CommandResult with restore status
|
456
|
+
"""
|
457
|
+
try:
|
458
|
+
logger.info(f"Restoring key from backup: {backup_path}")
|
459
|
+
|
460
|
+
# Validate parameters
|
461
|
+
if not backup_path or not os.path.exists(backup_path):
|
462
|
+
return ErrorResult(
|
463
|
+
message=f"Backup file not found: {backup_path}"
|
464
|
+
)
|
465
|
+
|
466
|
+
# Create target directory if it doesn't exist
|
467
|
+
key_dir = os.path.dirname(key_path)
|
468
|
+
if key_dir:
|
469
|
+
Path(key_dir).mkdir(parents=True, exist_ok=True)
|
470
|
+
|
471
|
+
# Restore key
|
472
|
+
if password:
|
473
|
+
# Try encrypted restore first
|
474
|
+
result = self.certificate_utils.restore_encrypted_backup(
|
475
|
+
backup_path, key_path, password
|
476
|
+
)
|
477
|
+
if not result["success"]:
|
478
|
+
return ErrorResult(
|
479
|
+
message=f"Encrypted restore failed: {result['error']}"
|
480
|
+
)
|
481
|
+
else:
|
482
|
+
# Simple file copy
|
483
|
+
shutil.copy2(backup_path, key_path)
|
484
|
+
|
485
|
+
# Validate restored key
|
486
|
+
key_validation = await self.key_validate(key_path)
|
487
|
+
if not key_validation.to_dict()["success"]:
|
488
|
+
return ErrorResult(
|
489
|
+
message=f"Restored key validation failed: {key_validation.to_dict()['error']['message']}"
|
490
|
+
)
|
491
|
+
|
492
|
+
logger.info(f"Key restore completed successfully: {key_path}")
|
493
|
+
return SuccessResult(
|
494
|
+
data={
|
495
|
+
"backup_path": backup_path,
|
496
|
+
"key_path": key_path,
|
497
|
+
"restore_date": datetime.now().isoformat(),
|
498
|
+
"key_info": key_validation.to_dict().get("data", {}).get("key") if key_validation.to_dict().get("success") else None
|
499
|
+
}
|
500
|
+
)
|
501
|
+
|
502
|
+
except Exception as e:
|
503
|
+
logger.error(f"Key restore failed: {e}")
|
504
|
+
return ErrorResult(
|
505
|
+
message=f"Key restore failed: {str(e)}"
|
506
|
+
)
|
@@ -0,0 +1,176 @@
|
|
1
|
+
"""
|
2
|
+
Module with load command implementation.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Dict, Any, Optional, List
|
6
|
+
|
7
|
+
from mcp_proxy_adapter.commands.base import Command
|
8
|
+
from mcp_proxy_adapter.commands.result import CommandResult, SuccessResult
|
9
|
+
from mcp_proxy_adapter.commands.command_registry import registry
|
10
|
+
|
11
|
+
|
12
|
+
class LoadResult(SuccessResult):
|
13
|
+
"""
|
14
|
+
Result of the load command execution.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def __init__(self, success: bool, commands_loaded: int, loaded_commands: list, source: str, error: Optional[str] = None):
|
18
|
+
"""
|
19
|
+
Initialize load command result.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
success: Whether loading was successful
|
23
|
+
commands_loaded: Number of commands loaded
|
24
|
+
loaded_commands: List of loaded command names
|
25
|
+
source: Source path or URL
|
26
|
+
error: Error message if loading failed
|
27
|
+
"""
|
28
|
+
data = {
|
29
|
+
"success": success,
|
30
|
+
"commands_loaded": commands_loaded,
|
31
|
+
"loaded_commands": loaded_commands,
|
32
|
+
"source": source
|
33
|
+
}
|
34
|
+
if error:
|
35
|
+
data["error"] = error
|
36
|
+
|
37
|
+
message = f"Loaded {commands_loaded} commands from {source}"
|
38
|
+
if error:
|
39
|
+
message = f"Failed to load commands from {source}: {error}"
|
40
|
+
|
41
|
+
super().__init__(data=data, message=message)
|
42
|
+
|
43
|
+
@classmethod
|
44
|
+
def get_schema(cls) -> Dict[str, Any]:
|
45
|
+
"""
|
46
|
+
Get JSON schema for result validation.
|
47
|
+
|
48
|
+
Returns:
|
49
|
+
Dict[str, Any]: JSON schema
|
50
|
+
"""
|
51
|
+
return {
|
52
|
+
"type": "object",
|
53
|
+
"properties": {
|
54
|
+
"data": {
|
55
|
+
"type": "object",
|
56
|
+
"properties": {
|
57
|
+
"success": {"type": "boolean"},
|
58
|
+
"commands_loaded": {"type": "integer"},
|
59
|
+
"loaded_commands": {
|
60
|
+
"type": "array",
|
61
|
+
"items": {"type": "string"}
|
62
|
+
},
|
63
|
+
"source": {"type": "string"},
|
64
|
+
"error": {"type": "string"}
|
65
|
+
},
|
66
|
+
"required": ["success", "commands_loaded", "loaded_commands", "source"]
|
67
|
+
}
|
68
|
+
},
|
69
|
+
"required": ["data"]
|
70
|
+
}
|
71
|
+
|
72
|
+
|
73
|
+
class LoadCommand(Command):
|
74
|
+
"""
|
75
|
+
Command that loads commands from local path or URL.
|
76
|
+
|
77
|
+
This command allows dynamic loading of command modules from either local file system
|
78
|
+
or remote HTTP/HTTPS URLs. The command automatically detects whether the source
|
79
|
+
is a local path or URL and handles the loading accordingly.
|
80
|
+
|
81
|
+
For local paths, the command loads Python modules ending with '_command.py'.
|
82
|
+
For URLs, the command downloads the Python code and loads it as a temporary module.
|
83
|
+
|
84
|
+
The loaded commands are registered in the command registry and become immediately
|
85
|
+
available for execution. Only commands that inherit from the base Command class
|
86
|
+
and are properly structured will be loaded and registered.
|
87
|
+
|
88
|
+
Security considerations:
|
89
|
+
- Local paths are validated for existence and proper naming
|
90
|
+
- URLs are downloaded with timeout protection
|
91
|
+
- Temporary files are automatically cleaned up after loading
|
92
|
+
- Only files ending with '_command.py' are accepted
|
93
|
+
|
94
|
+
Examples:
|
95
|
+
- Load from local file: "./my_command.py"
|
96
|
+
- Load from URL: "https://example.com/remote_command.py"
|
97
|
+
"""
|
98
|
+
|
99
|
+
name = "load"
|
100
|
+
result_class = LoadResult
|
101
|
+
|
102
|
+
async def execute(self, source: str, **kwargs) -> LoadResult:
|
103
|
+
"""
|
104
|
+
Execute load command.
|
105
|
+
|
106
|
+
Args:
|
107
|
+
source: Source path or URL to load command from
|
108
|
+
**kwargs: Additional parameters
|
109
|
+
|
110
|
+
Returns:
|
111
|
+
LoadResult: Load command result
|
112
|
+
"""
|
113
|
+
# Load command from source
|
114
|
+
result = registry.load_command_from_source(source)
|
115
|
+
|
116
|
+
return LoadResult(
|
117
|
+
success=result.get("success", False),
|
118
|
+
commands_loaded=result.get("commands_loaded", 0),
|
119
|
+
loaded_commands=result.get("loaded_commands", []),
|
120
|
+
source=result.get("source", source),
|
121
|
+
error=result.get("error")
|
122
|
+
)
|
123
|
+
|
124
|
+
@classmethod
|
125
|
+
def get_schema(cls) -> Dict[str, Any]:
|
126
|
+
"""
|
127
|
+
Get JSON schema for command parameters.
|
128
|
+
|
129
|
+
Returns:
|
130
|
+
Dict[str, Any]: JSON schema
|
131
|
+
"""
|
132
|
+
return {
|
133
|
+
"type": "object",
|
134
|
+
"properties": {
|
135
|
+
"source": {
|
136
|
+
"type": "string",
|
137
|
+
"description": "Source path or URL to load command from (must end with '_command.py')",
|
138
|
+
"examples": [
|
139
|
+
"./my_command.py",
|
140
|
+
"https://example.com/remote_command.py"
|
141
|
+
]
|
142
|
+
}
|
143
|
+
},
|
144
|
+
"required": ["source"]
|
145
|
+
}
|
146
|
+
|
147
|
+
@classmethod
|
148
|
+
def _generate_examples(cls, params: Dict[str, Dict[str, Any]]) -> List[Dict[str, Any]]:
|
149
|
+
"""
|
150
|
+
Generate custom examples for load command.
|
151
|
+
|
152
|
+
Args:
|
153
|
+
params: Information about command parameters
|
154
|
+
|
155
|
+
Returns:
|
156
|
+
List of examples
|
157
|
+
"""
|
158
|
+
examples = [
|
159
|
+
{
|
160
|
+
"command": cls.name,
|
161
|
+
"params": {"source": "./custom_command.py"},
|
162
|
+
"description": "Load a command from local file system"
|
163
|
+
},
|
164
|
+
{
|
165
|
+
"command": cls.name,
|
166
|
+
"params": {"source": "https://raw.githubusercontent.com/user/repo/main/remote_command.py"},
|
167
|
+
"description": "Load a command from GitHub raw content"
|
168
|
+
},
|
169
|
+
{
|
170
|
+
"command": cls.name,
|
171
|
+
"params": {"source": "https://example.com/api/commands/test_command.py"},
|
172
|
+
"description": "Load a command from remote API endpoint"
|
173
|
+
}
|
174
|
+
]
|
175
|
+
|
176
|
+
return examples
|