mcp-proxy-adapter 4.1.1__py3-none-any.whl → 6.0.1__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 +32 -0
- mcp_proxy_adapter/api/app.py +290 -33
- mcp_proxy_adapter/api/handlers.py +32 -6
- mcp_proxy_adapter/api/middleware/__init__.py +38 -32
- 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 +201 -0
- mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
- mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -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 +8 -1
- 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 +366 -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 +394 -14
- mcp_proxy_adapter/core/app_factory.py +410 -0
- mcp_proxy_adapter/core/app_runner.py +272 -0
- mcp_proxy_adapter/core/auth_validator.py +606 -0
- mcp_proxy_adapter/core/certificate_utils.py +1045 -0
- mcp_proxy_adapter/core/client.py +574 -0
- mcp_proxy_adapter/core/client_manager.py +284 -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 +385 -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 +286 -0
- mcp_proxy_adapter/core/server_adapter.py +282 -0
- mcp_proxy_adapter/core/server_engine.py +270 -0
- mcp_proxy_adapter/core/settings.py +1 -0
- mcp_proxy_adapter/core/ssl_utils.py +234 -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/__init__.py +13 -4
- 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 +44 -0
- mcp_proxy_adapter/examples/commands/__init__.py +5 -0
- mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
- mcp_proxy_adapter/examples/debug_request_state.py +112 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
- mcp_proxy_adapter/examples/demo_client.py +275 -0
- mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
- mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
- mcp_proxy_adapter/examples/full_application/main.py +173 -0
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
- mcp_proxy_adapter/examples/generate_certificates.py +177 -0
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
- mcp_proxy_adapter/examples/run_example.py +59 -0
- mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
- mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
- mcp_proxy_adapter/examples/run_security_tests.py +544 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
- mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/security_test_client.py +782 -0
- mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
- mcp_proxy_adapter/examples/test_config.py +148 -0
- mcp_proxy_adapter/examples/test_config_generator.py +86 -0
- mcp_proxy_adapter/examples/test_examples.py +281 -0
- mcp_proxy_adapter/examples/universal_client.py +620 -0
- mcp_proxy_adapter/main.py +93 -0
- mcp_proxy_adapter/utils/config_generator.py +1008 -0
- mcp_proxy_adapter/version.py +5 -2
- mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
- mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
- mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.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/README.md +0 -124
- 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.0.1.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,158 @@
|
|
1
|
+
"""
|
2
|
+
Module with unload 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 UnloadResult(SuccessResult):
|
13
|
+
"""
|
14
|
+
Result of the unload command execution.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def __init__(self, success: bool, command_name: str, message: str, error: Optional[str] = None):
|
18
|
+
"""
|
19
|
+
Initialize unload command result.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
success: Whether unloading was successful
|
23
|
+
command_name: Name of the command that was unloaded
|
24
|
+
message: Result message
|
25
|
+
error: Error message if unloading failed
|
26
|
+
"""
|
27
|
+
data = {
|
28
|
+
"success": success,
|
29
|
+
"command_name": command_name
|
30
|
+
}
|
31
|
+
if error:
|
32
|
+
data["error"] = error
|
33
|
+
|
34
|
+
super().__init__(data=data, message=message)
|
35
|
+
|
36
|
+
@classmethod
|
37
|
+
def get_schema(cls) -> Dict[str, Any]:
|
38
|
+
"""
|
39
|
+
Get JSON schema for result validation.
|
40
|
+
|
41
|
+
Returns:
|
42
|
+
Dict[str, Any]: JSON schema
|
43
|
+
"""
|
44
|
+
return {
|
45
|
+
"type": "object",
|
46
|
+
"properties": {
|
47
|
+
"data": {
|
48
|
+
"type": "object",
|
49
|
+
"properties": {
|
50
|
+
"success": {"type": "boolean"},
|
51
|
+
"command_name": {"type": "string"},
|
52
|
+
"error": {"type": "string"}
|
53
|
+
},
|
54
|
+
"required": ["success", "command_name"]
|
55
|
+
}
|
56
|
+
},
|
57
|
+
"required": ["data"]
|
58
|
+
}
|
59
|
+
|
60
|
+
|
61
|
+
class UnloadCommand(Command):
|
62
|
+
"""
|
63
|
+
Command that unloads loaded commands from registry.
|
64
|
+
|
65
|
+
This command allows removal of dynamically loaded commands from the command registry.
|
66
|
+
Only commands that were loaded via the 'load' command or from the commands directory
|
67
|
+
can be unloaded. Built-in commands and custom commands registered with higher priority
|
68
|
+
cannot be unloaded using this command.
|
69
|
+
|
70
|
+
When a command is unloaded:
|
71
|
+
- The command class is removed from the loaded commands registry
|
72
|
+
- Any command instances are also removed
|
73
|
+
- The command becomes unavailable for execution
|
74
|
+
- Built-in and custom commands with the same name remain unaffected
|
75
|
+
|
76
|
+
This is useful for:
|
77
|
+
- Removing outdated or problematic commands
|
78
|
+
- Managing memory usage by unloading unused commands
|
79
|
+
- Testing different versions of commands
|
80
|
+
- Cleaning up temporary commands loaded for testing
|
81
|
+
|
82
|
+
Note: Unloading a command does not affect other commands and does not require
|
83
|
+
a system restart. The command can be reloaded later if needed.
|
84
|
+
"""
|
85
|
+
|
86
|
+
name = "unload"
|
87
|
+
result_class = UnloadResult
|
88
|
+
|
89
|
+
async def execute(self, command_name: str, **kwargs) -> UnloadResult:
|
90
|
+
"""
|
91
|
+
Execute unload command.
|
92
|
+
|
93
|
+
Args:
|
94
|
+
command_name: Name of the command to unload
|
95
|
+
**kwargs: Additional parameters
|
96
|
+
|
97
|
+
Returns:
|
98
|
+
UnloadResult: Unload command result
|
99
|
+
"""
|
100
|
+
# Unload command from registry
|
101
|
+
result = registry.unload_command(command_name)
|
102
|
+
|
103
|
+
return UnloadResult(
|
104
|
+
success=result.get("success", False),
|
105
|
+
command_name=result.get("command_name", command_name),
|
106
|
+
message=result.get("message", "Unknown result"),
|
107
|
+
error=result.get("error")
|
108
|
+
)
|
109
|
+
|
110
|
+
@classmethod
|
111
|
+
def get_schema(cls) -> Dict[str, Any]:
|
112
|
+
"""
|
113
|
+
Get JSON schema for command parameters.
|
114
|
+
|
115
|
+
Returns:
|
116
|
+
Dict[str, Any]: JSON schema
|
117
|
+
"""
|
118
|
+
return {
|
119
|
+
"type": "object",
|
120
|
+
"properties": {
|
121
|
+
"command_name": {
|
122
|
+
"type": "string",
|
123
|
+
"description": "Name of the command to unload (must be a loaded command)"
|
124
|
+
}
|
125
|
+
},
|
126
|
+
"required": ["command_name"]
|
127
|
+
}
|
128
|
+
|
129
|
+
@classmethod
|
130
|
+
def _generate_examples(cls, params: Dict[str, Dict[str, Any]]) -> List[Dict[str, Any]]:
|
131
|
+
"""
|
132
|
+
Generate custom examples for unload command.
|
133
|
+
|
134
|
+
Args:
|
135
|
+
params: Information about command parameters
|
136
|
+
|
137
|
+
Returns:
|
138
|
+
List of examples
|
139
|
+
"""
|
140
|
+
examples = [
|
141
|
+
{
|
142
|
+
"command": cls.name,
|
143
|
+
"params": {"command_name": "test_command"},
|
144
|
+
"description": "Unload a previously loaded test command"
|
145
|
+
},
|
146
|
+
{
|
147
|
+
"command": cls.name,
|
148
|
+
"params": {"command_name": "remote_command"},
|
149
|
+
"description": "Unload a command that was loaded from URL"
|
150
|
+
},
|
151
|
+
{
|
152
|
+
"command": cls.name,
|
153
|
+
"params": {"command_name": "custom_command"},
|
154
|
+
"description": "Unload a custom command loaded from local file"
|
155
|
+
}
|
156
|
+
]
|
157
|
+
|
158
|
+
return examples
|
mcp_proxy_adapter/config.py
CHANGED
@@ -1,17 +1,20 @@
|
|
1
1
|
"""
|
2
2
|
Module for microservice configuration management.
|
3
|
+
|
4
|
+
Author: Vasiliy Zdanovskiy
|
5
|
+
email: vasilyvz@gmail.com
|
3
6
|
"""
|
4
7
|
|
5
8
|
import json
|
6
9
|
import os
|
7
|
-
from
|
8
|
-
from typing import Any, Dict, Optional
|
10
|
+
from typing import Any, Dict, Optional, List
|
9
11
|
|
10
12
|
|
11
13
|
class Config:
|
12
14
|
"""
|
13
15
|
Configuration management class for the microservice.
|
14
16
|
Allows loading settings from configuration file and environment variables.
|
17
|
+
Supports optional features that can be enabled/disabled.
|
15
18
|
"""
|
16
19
|
|
17
20
|
def __init__(self, config_path: Optional[str] = None):
|
@@ -19,7 +22,8 @@ class Config:
|
|
19
22
|
Initialize configuration.
|
20
23
|
|
21
24
|
Args:
|
22
|
-
config_path: Path to configuration file. If not specified,
|
25
|
+
config_path: Path to configuration file. If not specified,
|
26
|
+
"./config.json" is used.
|
23
27
|
"""
|
24
28
|
self.config_path = config_path or "./config.json"
|
25
29
|
self.config_data: Dict[str, Any] = {}
|
@@ -53,11 +57,177 @@ class Config:
|
|
53
57
|
},
|
54
58
|
"commands": {
|
55
59
|
"auto_discovery": True,
|
56
|
-
"
|
57
|
-
"
|
60
|
+
"commands_directory": "./commands",
|
61
|
+
"catalog_directory": "./catalog",
|
62
|
+
"plugin_servers": [],
|
63
|
+
"auto_install_dependencies": True,
|
64
|
+
"enabled_commands": ["health", "echo", "list", "help"],
|
65
|
+
"disabled_commands": [],
|
66
|
+
"custom_commands_path": "./commands"
|
67
|
+
},
|
68
|
+
"ssl": {
|
69
|
+
"enabled": False,
|
70
|
+
"mode": "https_only",
|
71
|
+
"cert_file": None,
|
72
|
+
"key_file": None,
|
73
|
+
"ca_cert": None,
|
74
|
+
"verify_client": False,
|
75
|
+
"client_cert_required": False,
|
76
|
+
"cipher_suites": [
|
77
|
+
"TLS_AES_256_GCM_SHA384",
|
78
|
+
"TLS_CHACHA20_POLY1305_SHA256"
|
79
|
+
],
|
80
|
+
"min_tls_version": "TLSv1.2",
|
81
|
+
"max_tls_version": "1.3",
|
82
|
+
"token_auth": {
|
83
|
+
"enabled": False,
|
84
|
+
"header_name": "Authorization",
|
85
|
+
"token_prefix": "Bearer",
|
86
|
+
"tokens_file": "tokens.json",
|
87
|
+
"token_expiry": 3600,
|
88
|
+
"jwt_secret": "",
|
89
|
+
"jwt_algorithm": "HS256"
|
90
|
+
}
|
91
|
+
},
|
92
|
+
"roles": {
|
93
|
+
"enabled": False,
|
94
|
+
"config_file": None,
|
95
|
+
"default_policy": {
|
96
|
+
"deny_by_default": False,
|
97
|
+
"require_role_match": False,
|
98
|
+
"case_sensitive": False,
|
99
|
+
"allow_wildcard": False
|
100
|
+
},
|
101
|
+
"auto_load": False,
|
102
|
+
"validation_enabled": False
|
103
|
+
},
|
104
|
+
"transport": {
|
105
|
+
"type": "http",
|
106
|
+
"port": None,
|
107
|
+
"ssl": {
|
108
|
+
"enabled": False,
|
109
|
+
"cert_file": None,
|
110
|
+
"key_file": None,
|
111
|
+
"ca_cert": None,
|
112
|
+
"verify_client": False,
|
113
|
+
"client_cert_required": False
|
114
|
+
}
|
115
|
+
},
|
116
|
+
"proxy_registration": {
|
117
|
+
"enabled": False,
|
118
|
+
"proxy_url": "http://localhost:3004",
|
119
|
+
"server_id": "mcp_proxy_adapter",
|
120
|
+
"server_name": "MCP Proxy Adapter",
|
121
|
+
"description": "JSON-RPC API for interacting with MCP Proxy",
|
122
|
+
"registration_timeout": 30,
|
123
|
+
"retry_attempts": 3,
|
124
|
+
"retry_delay": 5,
|
125
|
+
"auto_register_on_startup": True,
|
126
|
+
"auto_unregister_on_shutdown": True
|
127
|
+
},
|
128
|
+
"debug": {
|
129
|
+
"enabled": False,
|
130
|
+
"level": "WARNING"
|
131
|
+
},
|
132
|
+
"security": {
|
133
|
+
"framework": "mcp_security_framework",
|
134
|
+
"enabled": False,
|
135
|
+
"debug": False,
|
136
|
+
"environment": "dev",
|
137
|
+
"version": "1.0.0",
|
138
|
+
"auth": {
|
139
|
+
"enabled": False,
|
140
|
+
"methods": ["api_key"],
|
141
|
+
"api_keys": {},
|
142
|
+
"user_roles": {},
|
143
|
+
"jwt_secret": "",
|
144
|
+
"jwt_algorithm": "HS256",
|
145
|
+
"jwt_expiry_hours": 24,
|
146
|
+
"certificate_auth": False,
|
147
|
+
"certificate_roles_oid": "1.3.6.1.4.1.99999.1.1",
|
148
|
+
"certificate_permissions_oid": "1.3.6.1.4.1.99999.1.2",
|
149
|
+
"basic_auth": False,
|
150
|
+
"oauth2_config": None,
|
151
|
+
"public_paths": ["/health", "/docs", "/openapi.json"],
|
152
|
+
"security_headers": None
|
153
|
+
},
|
154
|
+
"ssl": {
|
155
|
+
"enabled": False,
|
156
|
+
"cert_file": None,
|
157
|
+
"key_file": None,
|
158
|
+
"ca_cert_file": None,
|
159
|
+
"client_cert_file": None,
|
160
|
+
"client_key_file": None,
|
161
|
+
"verify_mode": "CERT_NONE",
|
162
|
+
"min_tls_version": "TLSv1.2",
|
163
|
+
"max_tls_version": None,
|
164
|
+
"cipher_suite": None,
|
165
|
+
"check_hostname": True,
|
166
|
+
"check_expiry": True,
|
167
|
+
"expiry_warning_days": 30
|
168
|
+
},
|
169
|
+
"certificates": {
|
170
|
+
"enabled": False,
|
171
|
+
"ca_cert_path": None,
|
172
|
+
"ca_key_path": None,
|
173
|
+
"cert_storage_path": "./certs",
|
174
|
+
"key_storage_path": "./keys",
|
175
|
+
"default_validity_days": 365,
|
176
|
+
"key_size": 2048,
|
177
|
+
"hash_algorithm": "sha256",
|
178
|
+
"crl_enabled": False,
|
179
|
+
"crl_path": None,
|
180
|
+
"crl_validity_days": 30,
|
181
|
+
"auto_renewal": False,
|
182
|
+
"renewal_threshold_days": 30
|
183
|
+
},
|
184
|
+
"permissions": {
|
185
|
+
"enabled": False,
|
186
|
+
"roles_file": None,
|
187
|
+
"default_role": "guest",
|
188
|
+
"admin_role": "admin",
|
189
|
+
"role_hierarchy": {},
|
190
|
+
"permission_cache_enabled": False,
|
191
|
+
"permission_cache_ttl": 300,
|
192
|
+
"wildcard_permissions": False,
|
193
|
+
"strict_mode": False,
|
194
|
+
"roles": None
|
195
|
+
},
|
196
|
+
"rate_limit": {
|
197
|
+
"enabled": False,
|
198
|
+
"default_requests_per_minute": 60,
|
199
|
+
"default_requests_per_hour": 1000,
|
200
|
+
"burst_limit": 2,
|
201
|
+
"window_size_seconds": 60,
|
202
|
+
"storage_backend": "memory",
|
203
|
+
"redis_config": None,
|
204
|
+
"cleanup_interval": 300,
|
205
|
+
"exempt_paths": ["/health", "/docs", "/openapi.json"],
|
206
|
+
"exempt_roles": ["admin"]
|
207
|
+
},
|
208
|
+
"logging": {
|
209
|
+
"enabled": True,
|
210
|
+
"level": "INFO",
|
211
|
+
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
212
|
+
"date_format": "%Y-%m-%d %H:%M:%S",
|
213
|
+
"file_path": None,
|
214
|
+
"max_file_size": 10,
|
215
|
+
"backup_count": 5,
|
216
|
+
"console_output": True,
|
217
|
+
"json_format": False,
|
218
|
+
"include_timestamp": True,
|
219
|
+
"include_level": True,
|
220
|
+
"include_module": True
|
221
|
+
}
|
222
|
+
},
|
223
|
+
"protocols": {
|
224
|
+
"enabled": True,
|
225
|
+
"allowed_protocols": ["http", "jsonrpc"],
|
226
|
+
"default_protocol": "http",
|
227
|
+
"auto_discovery": True
|
58
228
|
}
|
59
229
|
}
|
60
|
-
|
230
|
+
|
61
231
|
# Try to load configuration from file
|
62
232
|
if os.path.exists(self.config_path):
|
63
233
|
try:
|
@@ -66,7 +236,7 @@ class Config:
|
|
66
236
|
self._update_nested_dict(self.config_data, file_config)
|
67
237
|
except Exception as e:
|
68
238
|
print(f"Error loading config from {self.config_path}: {e}")
|
69
|
-
|
239
|
+
|
70
240
|
# Load configuration from environment variables
|
71
241
|
self._load_env_variables()
|
72
242
|
|
@@ -131,14 +301,14 @@ class Config:
|
|
131
301
|
Configuration value
|
132
302
|
"""
|
133
303
|
parts = key.split(".")
|
134
|
-
|
304
|
+
|
135
305
|
# Get value from config
|
136
306
|
value = self.config_data
|
137
307
|
for part in parts:
|
138
308
|
if not isinstance(value, dict) or part not in value:
|
139
309
|
return default
|
140
310
|
value = value[part]
|
141
|
-
|
311
|
+
|
142
312
|
return value
|
143
313
|
|
144
314
|
def get_all(self) -> Dict[str, Any]:
|
@@ -163,17 +333,17 @@ class Config:
|
|
163
333
|
self.config_data[key] = value
|
164
334
|
else:
|
165
335
|
section = parts[0]
|
166
|
-
|
167
|
-
|
336
|
+
param_key = ".".join(parts[1:])
|
337
|
+
|
168
338
|
if section not in self.config_data:
|
169
339
|
self.config_data[section] = {}
|
170
|
-
|
340
|
+
|
171
341
|
current = self.config_data[section]
|
172
342
|
for part in parts[1:-1]:
|
173
343
|
if part not in current:
|
174
344
|
current[part] = {}
|
175
345
|
current = current[part]
|
176
|
-
|
346
|
+
|
177
347
|
current[parts[-1]] = value
|
178
348
|
|
179
349
|
def save(self, path: Optional[str] = None) -> None:
|
@@ -181,7 +351,8 @@ class Config:
|
|
181
351
|
Save configuration to file.
|
182
352
|
|
183
353
|
Args:
|
184
|
-
path: Path to configuration file. If not specified,
|
354
|
+
path: Path to configuration file. If not specified,
|
355
|
+
self.config_path is used.
|
185
356
|
"""
|
186
357
|
save_path = path or self.config_path
|
187
358
|
with open(save_path, 'w', encoding='utf-8') as f:
|
@@ -205,6 +376,215 @@ class Config:
|
|
205
376
|
d[k] = v
|
206
377
|
return d
|
207
378
|
|
379
|
+
def enable_feature(self, feature: str) -> None:
|
380
|
+
"""
|
381
|
+
Enable a specific feature in the configuration.
|
382
|
+
|
383
|
+
Args:
|
384
|
+
feature: Feature to enable (ssl, auth, roles, proxy_registration,
|
385
|
+
security)
|
386
|
+
"""
|
387
|
+
if feature == "ssl":
|
388
|
+
self.set("ssl.enabled", True)
|
389
|
+
self.set("security.ssl.enabled", True)
|
390
|
+
elif feature == "auth":
|
391
|
+
self.set("security.auth.enabled", True)
|
392
|
+
elif feature == "roles":
|
393
|
+
self.set("security.permissions.enabled", True)
|
394
|
+
self.set("roles.enabled", True)
|
395
|
+
elif feature == "proxy_registration":
|
396
|
+
self.set("proxy_registration.enabled", True)
|
397
|
+
elif feature == "security":
|
398
|
+
self.set("security.enabled", True)
|
399
|
+
elif feature == "rate_limit":
|
400
|
+
self.set("security.rate_limit.enabled", True)
|
401
|
+
elif feature == "certificates":
|
402
|
+
self.set("security.certificates.enabled", True)
|
403
|
+
else:
|
404
|
+
raise ValueError(f"Unknown feature: {feature}")
|
405
|
+
|
406
|
+
def disable_feature(self, feature: str) -> None:
|
407
|
+
"""
|
408
|
+
Disable a specific feature in the configuration.
|
409
|
+
|
410
|
+
Args:
|
411
|
+
feature: Feature to disable (ssl, auth, roles, proxy_registration,
|
412
|
+
security)
|
413
|
+
"""
|
414
|
+
if feature == "ssl":
|
415
|
+
self.set("ssl.enabled", False)
|
416
|
+
self.set("security.ssl.enabled", False)
|
417
|
+
elif feature == "auth":
|
418
|
+
self.set("security.auth.enabled", False)
|
419
|
+
elif feature == "roles":
|
420
|
+
self.set("security.permissions.enabled", False)
|
421
|
+
self.set("roles.enabled", False)
|
422
|
+
elif feature == "proxy_registration":
|
423
|
+
self.set("proxy_registration.enabled", False)
|
424
|
+
elif feature == "security":
|
425
|
+
self.set("security.enabled", False)
|
426
|
+
elif feature == "rate_limit":
|
427
|
+
self.set("security.rate_limit.enabled", False)
|
428
|
+
elif feature == "certificates":
|
429
|
+
self.set("security.certificates.enabled", False)
|
430
|
+
else:
|
431
|
+
raise ValueError(f"Unknown feature: {feature}")
|
432
|
+
|
433
|
+
def is_feature_enabled(self, feature: str) -> bool:
|
434
|
+
"""
|
435
|
+
Check if a specific feature is enabled.
|
436
|
+
|
437
|
+
Args:
|
438
|
+
feature: Feature to check (ssl, auth, roles, proxy_registration,
|
439
|
+
security)
|
440
|
+
|
441
|
+
Returns:
|
442
|
+
True if feature is enabled, False otherwise
|
443
|
+
"""
|
444
|
+
if feature == "ssl":
|
445
|
+
return (self.get("ssl.enabled", False) or
|
446
|
+
self.get("security.ssl.enabled", False))
|
447
|
+
elif feature == "auth":
|
448
|
+
return self.get("security.auth.enabled", False)
|
449
|
+
elif feature == "roles":
|
450
|
+
return (self.get("security.permissions.enabled", False) or
|
451
|
+
self.get("roles.enabled", False))
|
452
|
+
elif feature == "proxy_registration":
|
453
|
+
return self.get("proxy_registration.enabled", False)
|
454
|
+
elif feature == "security":
|
455
|
+
return self.get("security.enabled", False)
|
456
|
+
elif feature == "rate_limit":
|
457
|
+
return self.get("security.rate_limit.enabled", False)
|
458
|
+
elif feature == "certificates":
|
459
|
+
return self.get("security.certificates.enabled", False)
|
460
|
+
else:
|
461
|
+
raise ValueError(f"Unknown feature: {feature}")
|
462
|
+
|
463
|
+
def get_enabled_features(self) -> List[str]:
|
464
|
+
"""
|
465
|
+
Get list of all enabled features.
|
466
|
+
|
467
|
+
Returns:
|
468
|
+
List of enabled feature names
|
469
|
+
"""
|
470
|
+
features = []
|
471
|
+
if self.is_feature_enabled("ssl"):
|
472
|
+
features.append("ssl")
|
473
|
+
if self.is_feature_enabled("auth"):
|
474
|
+
features.append("auth")
|
475
|
+
if self.is_feature_enabled("roles"):
|
476
|
+
features.append("roles")
|
477
|
+
if self.is_feature_enabled("proxy_registration"):
|
478
|
+
features.append("proxy_registration")
|
479
|
+
if self.is_feature_enabled("security"):
|
480
|
+
features.append("security")
|
481
|
+
if self.is_feature_enabled("rate_limit"):
|
482
|
+
features.append("rate_limit")
|
483
|
+
if self.is_feature_enabled("certificates"):
|
484
|
+
features.append("certificates")
|
485
|
+
return features
|
486
|
+
|
487
|
+
def configure_auth_mode(self, mode: str, **kwargs) -> None:
|
488
|
+
"""
|
489
|
+
Configure authentication mode.
|
490
|
+
|
491
|
+
Args:
|
492
|
+
mode: Authentication mode (api_key, jwt, certificate, basic, oauth2)
|
493
|
+
**kwargs: Additional configuration parameters
|
494
|
+
"""
|
495
|
+
if mode == "api_key":
|
496
|
+
self.set("security.auth.methods", ["api_key"])
|
497
|
+
if "api_keys" in kwargs:
|
498
|
+
self.set("security.auth.api_keys", kwargs["api_keys"])
|
499
|
+
elif mode == "jwt":
|
500
|
+
self.set("security.auth.methods", ["jwt"])
|
501
|
+
if "jwt_secret" in kwargs:
|
502
|
+
self.set("security.auth.jwt_secret", kwargs["jwt_secret"])
|
503
|
+
elif mode == "certificate":
|
504
|
+
self.set("security.auth.methods", ["certificate"])
|
505
|
+
self.set("security.auth.certificate_auth", True)
|
506
|
+
elif mode == "basic":
|
507
|
+
self.set("security.auth.methods", ["basic"])
|
508
|
+
self.set("security.auth.basic_auth", True)
|
509
|
+
elif mode == "oauth2":
|
510
|
+
self.set("security.auth.methods", ["oauth2"])
|
511
|
+
if "oauth2_config" in kwargs:
|
512
|
+
self.set("security.auth.oauth2_config", kwargs["oauth2_config"])
|
513
|
+
else:
|
514
|
+
raise ValueError(f"Unknown authentication mode: {mode}")
|
515
|
+
|
516
|
+
def configure_proxy_registration_mode(self, mode: str, **kwargs) -> None:
|
517
|
+
"""
|
518
|
+
Configure proxy registration mode.
|
519
|
+
|
520
|
+
Args:
|
521
|
+
mode: Registration mode (token, certificate, api_key, none)
|
522
|
+
**kwargs: Additional configuration parameters
|
523
|
+
"""
|
524
|
+
if mode == "none":
|
525
|
+
self.set("proxy_registration.enabled", False)
|
526
|
+
else:
|
527
|
+
self.set("proxy_registration.enabled", True)
|
528
|
+
|
529
|
+
if mode == "token":
|
530
|
+
self.set("proxy_registration.auth_method", "token")
|
531
|
+
if "token" in kwargs:
|
532
|
+
self.set("proxy_registration.token.token", kwargs["token"])
|
533
|
+
elif mode == "certificate":
|
534
|
+
self.set("proxy_registration.auth_method", "certificate")
|
535
|
+
if "cert_file" in kwargs:
|
536
|
+
self.set("proxy_registration.certificate.cert_file",
|
537
|
+
kwargs["cert_file"])
|
538
|
+
if "key_file" in kwargs:
|
539
|
+
self.set("proxy_registration.certificate.key_file",
|
540
|
+
kwargs["key_file"])
|
541
|
+
elif mode == "api_key":
|
542
|
+
self.set("proxy_registration.auth_method", "api_key")
|
543
|
+
if "key" in kwargs:
|
544
|
+
self.set("proxy_registration.api_key.key", kwargs["key"])
|
545
|
+
|
546
|
+
def create_minimal_config(self) -> Dict[str, Any]:
|
547
|
+
"""
|
548
|
+
Create minimal configuration with only essential features.
|
549
|
+
|
550
|
+
Returns:
|
551
|
+
Minimal configuration dictionary
|
552
|
+
"""
|
553
|
+
minimal_config = self.config_data.copy()
|
554
|
+
|
555
|
+
# Disable all optional features
|
556
|
+
minimal_config["ssl"]["enabled"] = False
|
557
|
+
minimal_config["security"]["enabled"] = False
|
558
|
+
minimal_config["security"]["auth"]["enabled"] = False
|
559
|
+
minimal_config["security"]["permissions"]["enabled"] = False
|
560
|
+
minimal_config["security"]["rate_limit"]["enabled"] = False
|
561
|
+
minimal_config["security"]["certificates"]["enabled"] = False
|
562
|
+
minimal_config["proxy_registration"]["enabled"] = False
|
563
|
+
minimal_config["roles"]["enabled"] = False
|
564
|
+
|
565
|
+
return minimal_config
|
566
|
+
|
567
|
+
def create_secure_config(self) -> Dict[str, Any]:
|
568
|
+
"""
|
569
|
+
Create secure configuration with all security features enabled.
|
570
|
+
|
571
|
+
Returns:
|
572
|
+
Secure configuration dictionary
|
573
|
+
"""
|
574
|
+
secure_config = self.config_data.copy()
|
575
|
+
|
576
|
+
# Enable all security features
|
577
|
+
secure_config["ssl"]["enabled"] = True
|
578
|
+
secure_config["security"]["enabled"] = True
|
579
|
+
secure_config["security"]["auth"]["enabled"] = True
|
580
|
+
secure_config["security"]["permissions"]["enabled"] = True
|
581
|
+
secure_config["security"]["rate_limit"]["enabled"] = True
|
582
|
+
secure_config["security"]["certificates"]["enabled"] = True
|
583
|
+
secure_config["proxy_registration"]["enabled"] = True
|
584
|
+
secure_config["roles"]["enabled"] = True
|
585
|
+
|
586
|
+
return secure_config
|
587
|
+
|
208
588
|
|
209
589
|
# Singleton instance
|
210
590
|
config = Config()
|