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.
Files changed (200) hide show
  1. mcp_proxy_adapter/__main__.py +32 -0
  2. mcp_proxy_adapter/api/app.py +290 -33
  3. mcp_proxy_adapter/api/handlers.py +32 -6
  4. mcp_proxy_adapter/api/middleware/__init__.py +38 -32
  5. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  6. mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
  7. mcp_proxy_adapter/api/middleware/factory.py +243 -0
  8. mcp_proxy_adapter/api/middleware/logging.py +32 -6
  9. mcp_proxy_adapter/api/middleware/protocol_middleware.py +201 -0
  10. mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
  11. mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
  12. mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
  13. mcp_proxy_adapter/commands/__init__.py +19 -4
  14. mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
  15. mcp_proxy_adapter/commands/base.py +66 -32
  16. mcp_proxy_adapter/commands/builtin_commands.py +95 -0
  17. mcp_proxy_adapter/commands/catalog_manager.py +838 -0
  18. mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
  19. mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
  20. mcp_proxy_adapter/commands/command_registry.py +711 -354
  21. mcp_proxy_adapter/commands/dependency_manager.py +245 -0
  22. mcp_proxy_adapter/commands/echo_command.py +81 -0
  23. mcp_proxy_adapter/commands/health_command.py +8 -1
  24. mcp_proxy_adapter/commands/help_command.py +21 -14
  25. mcp_proxy_adapter/commands/hooks.py +200 -167
  26. mcp_proxy_adapter/commands/key_management_command.py +506 -0
  27. mcp_proxy_adapter/commands/load_command.py +176 -0
  28. mcp_proxy_adapter/commands/plugins_command.py +235 -0
  29. mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
  30. mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
  31. mcp_proxy_adapter/commands/reload_command.py +48 -50
  32. mcp_proxy_adapter/commands/result.py +1 -0
  33. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  34. mcp_proxy_adapter/commands/roles_management_command.py +697 -0
  35. mcp_proxy_adapter/commands/security_command.py +488 -0
  36. mcp_proxy_adapter/commands/ssl_setup_command.py +366 -0
  37. mcp_proxy_adapter/commands/token_management_command.py +529 -0
  38. mcp_proxy_adapter/commands/transport_management_command.py +144 -0
  39. mcp_proxy_adapter/commands/unload_command.py +158 -0
  40. mcp_proxy_adapter/config.py +394 -14
  41. mcp_proxy_adapter/core/app_factory.py +410 -0
  42. mcp_proxy_adapter/core/app_runner.py +272 -0
  43. mcp_proxy_adapter/core/auth_validator.py +606 -0
  44. mcp_proxy_adapter/core/certificate_utils.py +1045 -0
  45. mcp_proxy_adapter/core/client.py +574 -0
  46. mcp_proxy_adapter/core/client_manager.py +284 -0
  47. mcp_proxy_adapter/core/client_security.py +384 -0
  48. mcp_proxy_adapter/core/config_converter.py +405 -0
  49. mcp_proxy_adapter/core/config_validator.py +218 -0
  50. mcp_proxy_adapter/core/logging.py +19 -3
  51. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  52. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  53. mcp_proxy_adapter/core/protocol_manager.py +385 -0
  54. mcp_proxy_adapter/core/proxy_client.py +602 -0
  55. mcp_proxy_adapter/core/proxy_registration.py +522 -0
  56. mcp_proxy_adapter/core/role_utils.py +426 -0
  57. mcp_proxy_adapter/core/security_adapter.py +370 -0
  58. mcp_proxy_adapter/core/security_factory.py +239 -0
  59. mcp_proxy_adapter/core/security_integration.py +286 -0
  60. mcp_proxy_adapter/core/server_adapter.py +282 -0
  61. mcp_proxy_adapter/core/server_engine.py +270 -0
  62. mcp_proxy_adapter/core/settings.py +1 -0
  63. mcp_proxy_adapter/core/ssl_utils.py +234 -0
  64. mcp_proxy_adapter/core/transport_manager.py +292 -0
  65. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  66. mcp_proxy_adapter/custom_openapi.py +22 -11
  67. mcp_proxy_adapter/examples/__init__.py +13 -4
  68. mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
  69. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  70. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  71. mcp_proxy_adapter/examples/basic_framework/main.py +44 -0
  72. mcp_proxy_adapter/examples/commands/__init__.py +5 -0
  73. mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
  74. mcp_proxy_adapter/examples/debug_request_state.py +112 -0
  75. mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
  76. mcp_proxy_adapter/examples/demo_client.py +275 -0
  77. mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
  78. mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
  79. mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
  80. mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
  81. mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
  82. mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
  83. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
  84. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  85. mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
  86. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
  87. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  88. mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
  89. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
  90. mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
  91. mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
  92. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
  93. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  94. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
  95. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
  96. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  97. mcp_proxy_adapter/examples/full_application/main.py +173 -0
  98. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
  99. mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
  100. mcp_proxy_adapter/examples/generate_certificates.py +177 -0
  101. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
  102. mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
  103. mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
  104. mcp_proxy_adapter/examples/run_example.py +59 -0
  105. mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
  106. mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
  107. mcp_proxy_adapter/examples/run_security_tests.py +544 -0
  108. mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
  109. mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
  110. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
  111. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
  112. mcp_proxy_adapter/examples/security_test_client.py +782 -0
  113. mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
  114. mcp_proxy_adapter/examples/test_config.py +148 -0
  115. mcp_proxy_adapter/examples/test_config_generator.py +86 -0
  116. mcp_proxy_adapter/examples/test_examples.py +281 -0
  117. mcp_proxy_adapter/examples/universal_client.py +620 -0
  118. mcp_proxy_adapter/main.py +93 -0
  119. mcp_proxy_adapter/utils/config_generator.py +1008 -0
  120. mcp_proxy_adapter/version.py +5 -2
  121. mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
  122. mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
  123. mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
  124. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
  125. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  126. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  127. mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
  128. mcp_proxy_adapter/examples/README.md +0 -124
  129. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  130. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  131. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  132. mcp_proxy_adapter/examples/basic_server/config.json +0 -35
  133. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  134. mcp_proxy_adapter/examples/basic_server/server.py +0 -103
  135. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  136. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  137. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
  138. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  139. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  140. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  141. mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
  142. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  143. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  144. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  145. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  146. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  147. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  148. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  149. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  150. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  151. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  152. mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
  153. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  154. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  155. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  156. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  157. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  158. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  159. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  160. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  161. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  162. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  163. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  164. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  165. mcp_proxy_adapter/tests/__init__.py +0 -0
  166. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  167. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  168. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  169. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  170. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  171. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  172. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  173. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  174. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  175. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  176. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  177. mcp_proxy_adapter/tests/conftest.py +0 -131
  178. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  179. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  180. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  181. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  182. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  183. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  184. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  185. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  186. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  187. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  188. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  189. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  190. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  191. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  192. mcp_proxy_adapter/tests/test_config.py +0 -127
  193. mcp_proxy_adapter/tests/test_utils.py +0 -65
  194. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  195. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  196. mcp_proxy_adapter/tests/unit/test_config.py +0 -217
  197. mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
  198. mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
  199. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
  200. {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
@@ -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 pathlib import Path
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, "./config.json" is used.
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
- "discovery_path": "mcp_proxy_adapter.commands", # Path to package with commands (e.g., "myproject.commands")
57
- "custom_commands_path": None
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
- param = ".".join(parts[1:])
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, self.config_path is used.
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()