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.
Files changed (253) hide show
  1. mcp_proxy_adapter/__main__.py +12 -0
  2. mcp_proxy_adapter/api/app.py +254 -33
  3. mcp_proxy_adapter/api/handlers.py +32 -6
  4. mcp_proxy_adapter/api/middleware/__init__.py +36 -30
  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 +135 -0
  10. mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
  11. mcp_proxy_adapter/api/middleware/unified_security.py +152 -0
  12. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -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 +7 -0
  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 +483 -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 +159 -2
  41. mcp_proxy_adapter/core/app_factory.py +326 -0
  42. mcp_proxy_adapter/core/auth_validator.py +606 -0
  43. mcp_proxy_adapter/core/certificate_utils.py +827 -0
  44. mcp_proxy_adapter/core/client_security.py +384 -0
  45. mcp_proxy_adapter/core/config_converter.py +405 -0
  46. mcp_proxy_adapter/core/config_validator.py +218 -0
  47. mcp_proxy_adapter/core/logging.py +19 -3
  48. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  49. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  50. mcp_proxy_adapter/core/protocol_manager.py +235 -0
  51. mcp_proxy_adapter/core/proxy_client.py +602 -0
  52. mcp_proxy_adapter/core/proxy_registration.py +522 -0
  53. mcp_proxy_adapter/core/role_utils.py +426 -0
  54. mcp_proxy_adapter/core/security_adapter.py +370 -0
  55. mcp_proxy_adapter/core/security_factory.py +239 -0
  56. mcp_proxy_adapter/core/security_integration.py +277 -0
  57. mcp_proxy_adapter/core/server_adapter.py +345 -0
  58. mcp_proxy_adapter/core/server_engine.py +364 -0
  59. mcp_proxy_adapter/core/settings.py +1 -0
  60. mcp_proxy_adapter/core/ssl_utils.py +233 -0
  61. mcp_proxy_adapter/core/transport_manager.py +292 -0
  62. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  63. mcp_proxy_adapter/custom_openapi.py +22 -11
  64. mcp_proxy_adapter/examples/README.md +230 -97
  65. mcp_proxy_adapter/examples/README_EN.md +258 -0
  66. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  67. mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
  68. mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
  69. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  70. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  71. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +39 -0
  72. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +25 -0
  73. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  74. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  75. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  76. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  77. mcp_proxy_adapter/examples/cert_config.json +9 -0
  78. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  79. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  80. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  81. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  82. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  83. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  84. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  85. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  86. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  87. mcp_proxy_adapter/examples/certs/client.key +52 -0
  88. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  89. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  90. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  91. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  92. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  93. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  94. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  95. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  96. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  97. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  98. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  99. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  100. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  101. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  102. mcp_proxy_adapter/examples/certs/server.key +52 -0
  103. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  104. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  105. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  106. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  107. mcp_proxy_adapter/examples/certs/user.key +52 -0
  108. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  109. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  110. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  111. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  112. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  113. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  114. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  115. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  116. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  117. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  118. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  119. mcp_proxy_adapter/examples/demo_client.py +341 -0
  120. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  121. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  122. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  123. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  124. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  125. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  126. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  127. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  128. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  129. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  130. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  131. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  132. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  133. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  134. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  135. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  136. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  137. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  138. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  139. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  140. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  141. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  142. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  143. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  144. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  145. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  146. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  147. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  148. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  149. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  150. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  151. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  152. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  153. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  154. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  155. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  156. mcp_proxy_adapter/examples/roles.json +38 -0
  157. mcp_proxy_adapter/examples/run_example.py +81 -0
  158. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  159. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  160. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  161. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  162. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  163. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  164. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  165. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  166. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  167. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  168. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  169. mcp_proxy_adapter/examples/test_examples.py +344 -0
  170. mcp_proxy_adapter/examples/universal_client.py +628 -0
  171. mcp_proxy_adapter/main.py +186 -0
  172. mcp_proxy_adapter/utils/config_generator.py +639 -0
  173. mcp_proxy_adapter/version.py +2 -1
  174. mcp_proxy_adapter-6.1.0.dist-info/METADATA +205 -0
  175. mcp_proxy_adapter-6.1.0.dist-info/RECORD +193 -0
  176. mcp_proxy_adapter-6.1.0.dist-info/entry_points.txt +2 -0
  177. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/licenses/LICENSE +2 -2
  178. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  179. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  180. mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
  181. mcp_proxy_adapter/examples/__init__.py +0 -7
  182. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  183. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  184. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  185. mcp_proxy_adapter/examples/basic_server/config.json +0 -35
  186. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  187. mcp_proxy_adapter/examples/basic_server/server.py +0 -103
  188. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  189. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  190. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
  191. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  192. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  193. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  194. mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
  195. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  196. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  197. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  198. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  199. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  200. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  201. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  202. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  203. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  204. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  205. mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
  206. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  207. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  208. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  209. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  210. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  211. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  212. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  213. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  214. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  215. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  216. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  217. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  218. mcp_proxy_adapter/tests/__init__.py +0 -0
  219. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  220. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  221. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  222. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  223. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  224. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  225. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  226. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  227. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  228. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  229. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  230. mcp_proxy_adapter/tests/conftest.py +0 -131
  231. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  232. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  233. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  234. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  235. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  236. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  237. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  238. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  239. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  240. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  241. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  242. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  243. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  244. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  245. mcp_proxy_adapter/tests/test_config.py +0 -127
  246. mcp_proxy_adapter/tests/test_utils.py +0 -65
  247. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  248. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  249. mcp_proxy_adapter/tests/unit/test_config.py +0 -217
  250. mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
  251. mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
  252. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/WHEEL +0 -0
  253. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.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,5 +1,8 @@
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
@@ -53,8 +56,162 @@ class Config:
53
56
  },
54
57
  "commands": {
55
58
  "auto_discovery": True,
56
- "discovery_path": "mcp_proxy_adapter.commands", # Path to package with commands (e.g., "myproject.commands")
57
- "custom_commands_path": None
59
+ "commands_directory": "./commands", # Path to directory with command files
60
+ "catalog_directory": "./catalog", # Path to command catalog directory
61
+ "plugin_servers": [], # List of plugin server URLs
62
+ "auto_install_dependencies": True # Automatically install plugin dependencies
63
+ },
64
+ "ssl": {
65
+ "enabled": False,
66
+ "mode": "https_only",
67
+ "cert_file": None,
68
+ "key_file": None,
69
+ "ca_cert": None,
70
+ "verify_client": False,
71
+ "client_cert_required": False,
72
+ "cipher_suites": ["TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256"],
73
+ "min_tls_version": "TLSv1.2",
74
+ "max_tls_version": "1.3",
75
+ "token_auth": {
76
+ "enabled": False,
77
+ "header_name": "Authorization",
78
+ "token_prefix": "Bearer",
79
+ "tokens_file": "tokens.json",
80
+ "token_expiry": 3600,
81
+ "jwt_secret": "",
82
+ "jwt_algorithm": "HS256"
83
+ }
84
+ },
85
+ "roles": {
86
+ "enabled": False,
87
+ "config_file": None,
88
+ "default_policy": {
89
+ "deny_by_default": False,
90
+ "require_role_match": False,
91
+ "case_sensitive": False,
92
+ "allow_wildcard": False
93
+ },
94
+ "auto_load": False,
95
+ "validation_enabled": False
96
+ },
97
+ "transport": {
98
+ "type": "http",
99
+ "port": None,
100
+ "ssl": {
101
+ "enabled": False,
102
+ "cert_file": None,
103
+ "key_file": None,
104
+ "ca_cert": None,
105
+ "verify_client": False,
106
+ "client_cert_required": False
107
+ }
108
+ },
109
+ "proxy_registration": {
110
+ "enabled": False,
111
+ "proxy_url": "http://localhost:3004",
112
+ "server_id": "mcp_proxy_adapter",
113
+ "server_name": "MCP Proxy Adapter",
114
+ "description": "JSON-RPC API for interacting with MCP Proxy",
115
+ "registration_timeout": 30,
116
+ "retry_attempts": 3,
117
+ "retry_delay": 5,
118
+ "auto_register_on_startup": True,
119
+ "auto_unregister_on_shutdown": True
120
+ },
121
+ "debug": {
122
+ "enabled": False,
123
+ "level": "WARNING"
124
+ },
125
+ "security": {
126
+ "framework": "mcp_security_framework",
127
+ "enabled": True,
128
+ "debug": False,
129
+ "environment": "dev",
130
+ "version": "1.0.0",
131
+ "auth": {
132
+ "enabled": True,
133
+ "methods": ["api_key"],
134
+ "api_keys": {},
135
+ "user_roles": {},
136
+ "jwt_secret": "",
137
+ "jwt_algorithm": "HS256",
138
+ "jwt_expiry_hours": 24,
139
+ "certificate_auth": False,
140
+ "certificate_roles_oid": "1.3.6.1.4.1.99999.1.1",
141
+ "certificate_permissions_oid": "1.3.6.1.4.1.99999.1.2",
142
+ "basic_auth": False,
143
+ "oauth2_config": None,
144
+ "public_paths": ["/health", "/docs", "/openapi.json"],
145
+ "security_headers": None
146
+ },
147
+ "ssl": {
148
+ "enabled": False,
149
+ "cert_file": None,
150
+ "key_file": None,
151
+ "ca_cert_file": None,
152
+ "client_cert_file": None,
153
+ "client_key_file": None,
154
+ "verify_mode": "CERT_NONE",
155
+ "min_tls_version": "TLSv1.2",
156
+ "max_tls_version": None,
157
+ "cipher_suite": None,
158
+ "check_hostname": True,
159
+ "check_expiry": True,
160
+ "expiry_warning_days": 30
161
+ },
162
+ "certificates": {
163
+ "enabled": False,
164
+ "ca_cert_path": None,
165
+ "ca_key_path": None,
166
+ "cert_storage_path": "./certs",
167
+ "key_storage_path": "./keys",
168
+ "default_validity_days": 365,
169
+ "key_size": 2048,
170
+ "hash_algorithm": "sha256",
171
+ "crl_enabled": False,
172
+ "crl_path": None,
173
+ "crl_validity_days": 30,
174
+ "auto_renewal": False,
175
+ "renewal_threshold_days": 30
176
+ },
177
+ "permissions": {
178
+ "enabled": False,
179
+ "roles_file": None,
180
+ "default_role": "guest",
181
+ "admin_role": "admin",
182
+ "role_hierarchy": {},
183
+ "permission_cache_enabled": False,
184
+ "permission_cache_ttl": 300,
185
+ "wildcard_permissions": False,
186
+ "strict_mode": False,
187
+ "roles": None
188
+ },
189
+ "rate_limit": {
190
+ "enabled": True,
191
+ "default_requests_per_minute": 60,
192
+ "default_requests_per_hour": 1000,
193
+ "burst_limit": 2,
194
+ "window_size_seconds": 60,
195
+ "storage_backend": "memory",
196
+ "redis_config": None,
197
+ "cleanup_interval": 300,
198
+ "exempt_paths": ["/health", "/docs", "/openapi.json"],
199
+ "exempt_roles": ["admin"]
200
+ },
201
+ "logging": {
202
+ "enabled": True,
203
+ "level": "INFO",
204
+ "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
205
+ "date_format": "%Y-%m-%d %H:%M:%S",
206
+ "file_path": None,
207
+ "max_file_size": 10,
208
+ "backup_count": 5,
209
+ "console_output": True,
210
+ "json_format": False,
211
+ "include_timestamp": True,
212
+ "include_level": True,
213
+ "include_module": True
214
+ }
58
215
  }
59
216
  }
60
217
 
@@ -0,0 +1,326 @@
1
+ """
2
+ Application Factory for MCP Proxy Adapter
3
+
4
+ This module provides a factory function for creating and running MCP Proxy Adapter servers
5
+ with proper configuration validation and initialization.
6
+
7
+ Author: Vasiliy Zdanovskiy
8
+ email: vasilyvz@gmail.com
9
+ """
10
+
11
+ import logging
12
+ import sys
13
+ from pathlib import Path
14
+ from typing import Optional, Dict, Any
15
+
16
+ from mcp_proxy_adapter.api.app import create_app
17
+ from mcp_proxy_adapter.core.logging import setup_logging, get_logger
18
+ from mcp_proxy_adapter.core.server_adapter import UnifiedServerRunner
19
+ from mcp_proxy_adapter.config import config
20
+ from mcp_proxy_adapter.commands.builtin_commands import register_builtin_commands
21
+
22
+ logger = get_logger("app_factory")
23
+
24
+
25
+ def create_and_run_server(
26
+ config_path: Optional[str] = None,
27
+ log_config_path: Optional[str] = None,
28
+ title: str = "MCP Proxy Adapter Server",
29
+ description: str = "Model Context Protocol Proxy Adapter with Security Framework",
30
+ version: str = "1.0.0",
31
+ host: str = "0.0.0.0",
32
+ log_level: str = "info",
33
+ engine: Optional[str] = None
34
+ ) -> None:
35
+ """
36
+ Create and run MCP Proxy Adapter server with proper validation.
37
+
38
+ This factory function validates all configuration files, sets up logging,
39
+ initializes the application, and starts the server with optimal settings.
40
+
41
+ Args:
42
+ config_path: Path to configuration file (JSON)
43
+ log_config_path: Path to logging configuration file (optional)
44
+ title: Application title for OpenAPI schema
45
+ description: Application description for OpenAPI schema
46
+ version: Application version
47
+ host: Server host address
48
+ port: Server port
49
+ log_level: Logging level
50
+ engine: Specific server engine to use (optional)
51
+
52
+ Raises:
53
+ SystemExit: If configuration validation fails or server cannot start
54
+ """
55
+ print("šŸš€ MCP Proxy Adapter Server Factory")
56
+ print("=" * 60)
57
+ print(f"šŸ“‹ Title: {title}")
58
+ print(f"šŸ“ Description: {description}")
59
+ print(f"šŸ”¢ Version: {version}")
60
+ print(f"🌐 Host: {host}")
61
+ print(f"šŸ“Š Log Level: {log_level}")
62
+ print("=" * 60)
63
+ print()
64
+
65
+ # 1. Validate and load configuration file
66
+ app_config = None
67
+ if config_path:
68
+ config_file = Path(config_path)
69
+ if not config_file.exists():
70
+ print(f"āŒ Configuration file not found: {config_path}")
71
+ print(" Please provide a valid path to config.json")
72
+ sys.exit(1)
73
+
74
+ try:
75
+ config.load_from_file(str(config_file))
76
+ app_config = config.get_all()
77
+ print(f"āœ… Configuration loaded from: {config_path}")
78
+
79
+ # Debug: Check what config.get_all() actually returns
80
+ print(f"šŸ” Debug: config.get_all() keys: {list(app_config.keys())}")
81
+ if "security" in app_config:
82
+ security_ssl = app_config["security"].get("ssl", {})
83
+ print(f"šŸ” Debug: config.get_all() security.ssl: {security_ssl}")
84
+
85
+ # Debug: Check if root ssl section exists after loading
86
+ if "ssl" in app_config:
87
+ print(f"šŸ” Debug: Root SSL section after loading: enabled={app_config['ssl'].get('enabled', False)}")
88
+ print(f"šŸ” Debug: Root SSL section after loading: cert_file={app_config['ssl'].get('cert_file')}")
89
+ print(f"šŸ” Debug: Root SSL section after loading: key_file={app_config['ssl'].get('key_file')}")
90
+ else:
91
+ print(f"šŸ” Debug: No root SSL section after loading")
92
+
93
+ # Debug: Check app_config immediately after get_all()
94
+ if app_config and "security" in app_config:
95
+ ssl_config = app_config["security"].get("ssl", {})
96
+ print(f"šŸ” Debug: app_config after get_all(): SSL enabled={ssl_config.get('enabled', False)}")
97
+ print(f"šŸ” Debug: app_config after get_all(): SSL cert_file={ssl_config.get('cert_file')}")
98
+ print(f"šŸ” Debug: app_config after get_all(): SSL key_file={ssl_config.get('key_file')}")
99
+
100
+ # Debug: Check SSL configuration after loading
101
+ if app_config and "security" in app_config:
102
+ ssl_config = app_config["security"].get("ssl", {})
103
+ print(f"šŸ” Debug: SSL config after loading: enabled={ssl_config.get('enabled', False)}")
104
+ print(f"šŸ” Debug: SSL config after loading: cert_file={ssl_config.get('cert_file')}")
105
+ print(f"šŸ” Debug: SSL config after loading: key_file={ssl_config.get('key_file')}")
106
+
107
+ # Debug: Check if SSL config is correct
108
+ if ssl_config.get('enabled', False):
109
+ print(f"šŸ” Debug: SSL config is enabled and correct")
110
+ else:
111
+ print(f"šŸ” Debug: SSL config is disabled or incorrect")
112
+ # Try to get SSL config from root level
113
+ root_ssl = app_config.get("ssl", {})
114
+ print(f"šŸ” Debug: Root SSL config: enabled={root_ssl.get('enabled', False)}")
115
+ print(f"šŸ” Debug: Root SSL config: cert_file={root_ssl.get('cert_file')}")
116
+ print(f"šŸ” Debug: Root SSL config: key_file={root_ssl.get('key_file')}")
117
+
118
+ # Validate security framework configuration only if enabled
119
+ security_config = app_config.get("security", {})
120
+ if security_config.get("enabled", False):
121
+ framework = security_config.get("framework", "mcp_security_framework")
122
+ print(f"šŸ”’ Security framework: {framework}")
123
+
124
+ # Debug: Check SSL config before validation
125
+ ssl_config = security_config.get("ssl", {})
126
+ print(f"šŸ” Debug: SSL config before validation: enabled={ssl_config.get('enabled', False)}")
127
+
128
+ # Validate security configuration
129
+ from mcp_proxy_adapter.core.unified_config_adapter import UnifiedConfigAdapter
130
+ adapter = UnifiedConfigAdapter()
131
+ validation_result = adapter.validate_configuration(app_config)
132
+
133
+ # Debug: Check SSL config after validation
134
+ ssl_config = app_config.get("security", {}).get("ssl", {})
135
+ print(f"šŸ” Debug: SSL config after validation: enabled={ssl_config.get('enabled', False)}")
136
+
137
+ if not validation_result.is_valid:
138
+ print("āŒ Security configuration validation failed:")
139
+ for error in validation_result.errors:
140
+ print(f" - {error}")
141
+ sys.exit(1)
142
+
143
+ if validation_result.warnings:
144
+ print("āš ļø Security configuration warnings:")
145
+ for warning in validation_result.warnings:
146
+ print(f" - {warning}")
147
+
148
+ print("āœ… Security configuration validated successfully")
149
+ else:
150
+ print("šŸ”“ Security framework disabled")
151
+
152
+ except Exception as e:
153
+ print(f"āŒ Failed to load configuration from {config_path}: {e}")
154
+ sys.exit(1)
155
+ else:
156
+ print("āš ļø No configuration file provided, using defaults")
157
+ app_config = config.get_all()
158
+
159
+ # 2. Setup logging
160
+ try:
161
+ if log_config_path:
162
+ log_config_file = Path(log_config_path)
163
+ if not log_config_file.exists():
164
+ print(f"āŒ Log configuration file not found: {log_config_path}")
165
+ sys.exit(1)
166
+ setup_logging(log_config_path=str(log_config_file))
167
+ print(f"āœ… Logging configured from: {log_config_path}")
168
+ else:
169
+ setup_logging()
170
+ print("āœ… Logging configured with defaults")
171
+ except Exception as e:
172
+ print(f"āŒ Failed to setup logging: {e}")
173
+ sys.exit(1)
174
+
175
+ # 3. Register built-in commands
176
+ try:
177
+ builtin_count = register_builtin_commands()
178
+ print(f"āœ… Registered {builtin_count} built-in commands")
179
+ except Exception as e:
180
+ print(f"āŒ Failed to register built-in commands: {e}")
181
+ sys.exit(1)
182
+
183
+ # 4. Create FastAPI application with configuration
184
+ try:
185
+ # Debug: Check app_config before passing to create_app
186
+ if app_config and "security" in app_config:
187
+ ssl_config = app_config["security"].get("ssl", {})
188
+ print(f"šŸ” Debug: app_config before create_app: SSL enabled={ssl_config.get('enabled', False)}")
189
+ print(f"šŸ” Debug: app_config before create_app: SSL cert_file={ssl_config.get('cert_file')}")
190
+ print(f"šŸ” Debug: app_config before create_app: SSL key_file={ssl_config.get('key_file')}")
191
+
192
+ app = create_app(
193
+ title=title,
194
+ description=description,
195
+ version=version,
196
+ app_config=app_config, # Pass configuration to create_app
197
+ config_path=config_path # Pass config path to preserve SSL settings
198
+ )
199
+ print("āœ… FastAPI application created successfully")
200
+ except Exception as e:
201
+ print(f"āŒ Failed to create FastAPI application: {e}")
202
+ sys.exit(1)
203
+
204
+ # 5. Create server configuration
205
+ # Get port from config if available, otherwise use default
206
+ server_port = app_config.get("server", {}).get("port", 8000) if app_config else 8000
207
+ print(f"šŸ”Œ Port: {server_port}")
208
+
209
+ server_config = {
210
+ "host": host,
211
+ "port": server_port,
212
+ "log_level": log_level,
213
+ "reload": False
214
+ }
215
+
216
+ # Add SSL configuration if present
217
+ print(f"šŸ” Debug: app_config keys: {list(app_config.keys()) if app_config else 'None'}")
218
+
219
+ # Check for SSL config in root section first (higher priority)
220
+ if app_config and "ssl" in app_config:
221
+ print(f"šŸ” Debug: SSL config found in root: {app_config['ssl']}")
222
+ print(f"šŸ” Debug: SSL enabled: {app_config['ssl'].get('enabled', False)}")
223
+ if app_config["ssl"].get("enabled", False):
224
+ ssl_config = app_config["ssl"]
225
+ # Add SSL config directly to server_config for Hypercorn
226
+ server_config["certfile"] = ssl_config.get("cert_file")
227
+ server_config["keyfile"] = ssl_config.get("key_file")
228
+ server_config["ca_certs"] = ssl_config.get("ca_cert_file")
229
+ server_config["verify_mode"] = ssl_config.get("verify_mode")
230
+ print(f"šŸ”’ SSL enabled: {ssl_config.get('cert_file', 'N/A')}")
231
+ print(f"šŸ”’ SSL enabled: cert={ssl_config.get('cert_file')}, key={ssl_config.get('key_file')}")
232
+ print(f"šŸ”’ Server config SSL: certfile={server_config.get('certfile')}, keyfile={server_config.get('keyfile')}, ca_certs={server_config.get('ca_certs')}, verify_mode={server_config.get('verify_mode')}")
233
+
234
+ # Check for SSL config in security section (fallback)
235
+ if app_config and "security" in app_config:
236
+ security_config = app_config["security"]
237
+ print(f"šŸ” Debug: security_config keys: {list(security_config.keys())}")
238
+ if "ssl" in security_config:
239
+ print(f"šŸ” Debug: SSL config found in security: {security_config['ssl']}")
240
+ print(f"šŸ” Debug: SSL enabled: {security_config['ssl'].get('enabled', False)}")
241
+ if security_config["ssl"].get("enabled", False):
242
+ ssl_config = security_config["ssl"]
243
+ # Add SSL config directly to server_config for Hypercorn
244
+ server_config["certfile"] = ssl_config.get("cert_file")
245
+ server_config["keyfile"] = ssl_config.get("key_file")
246
+ server_config["ca_certs"] = ssl_config.get("ca_cert_file")
247
+ server_config["verify_mode"] = ssl_config.get("verify_mode")
248
+ print(f"šŸ”’ SSL enabled: {ssl_config.get('cert_file', 'N/A')}")
249
+ print(f"šŸ”’ SSL enabled: cert={ssl_config.get('cert_file')}, key={ssl_config.get('key_file')}")
250
+ print(f"šŸ”’ Server config SSL: certfile={server_config.get('certfile')}, keyfile={server_config.get('keyfile')}, ca_certs={server_config.get('ca_certs')}, verify_mode={server_config.get('verify_mode')}")
251
+ print(f"šŸ” Debug: SSL config found in root: {app_config['ssl']}")
252
+ print(f"šŸ” Debug: SSL enabled: {app_config['ssl'].get('enabled', False)}")
253
+ if app_config["ssl"].get("enabled", False):
254
+ ssl_config = app_config["ssl"]
255
+ # Add SSL config directly to server_config for Hypercorn
256
+ server_config["certfile"] = ssl_config.get("cert_file")
257
+ server_config["keyfile"] = ssl_config.get("key_file")
258
+ server_config["ca_certs"] = ssl_config.get("ca_cert_file")
259
+ server_config["verify_mode"] = ssl_config.get("verify_mode")
260
+ print(f"šŸ”’ SSL enabled: {ssl_config.get('cert_file', 'N/A')}")
261
+ print(f"šŸ”’ SSL enabled: cert={ssl_config.get('cert_file')}, key={ssl_config.get('key_file')}")
262
+ print(f"šŸ”’ Server config SSL: certfile={server_config.get('certfile')}, keyfile={server_config.get('keyfile')}, ca_certs={server_config.get('ca_certs')}, verify_mode={server_config.get('verify_mode')}")
263
+
264
+ # 6. Start server
265
+ try:
266
+ print("šŸš€ Starting server...")
267
+ print(" Use Ctrl+C to stop the server")
268
+ print("=" * 60)
269
+
270
+ server_runner = UnifiedServerRunner()
271
+ server_runner.run_server(app, server_config, "hypercorn")
272
+
273
+ except KeyboardInterrupt:
274
+ print("\nšŸ›‘ Server stopped by user")
275
+ except Exception as e:
276
+ print(f"\nāŒ Failed to start server: {e}")
277
+ import traceback
278
+ traceback.print_exc()
279
+ sys.exit(1)
280
+
281
+
282
+ def validate_config_file(config_path: str) -> bool:
283
+ """
284
+ Validate configuration file exists and is readable.
285
+
286
+ Args:
287
+ config_path: Path to configuration file
288
+
289
+ Returns:
290
+ True if valid, False otherwise
291
+ """
292
+ try:
293
+ config_file = Path(config_path)
294
+ if not config_file.exists():
295
+ print(f"āŒ Configuration file not found: {config_path}")
296
+ return False
297
+
298
+ # Try to load configuration to validate JSON format
299
+ config.load_from_file(str(config_file))
300
+ return True
301
+
302
+ except Exception as e:
303
+ print(f"āŒ Configuration file validation failed: {e}")
304
+ return False
305
+
306
+
307
+ def validate_log_config_file(log_config_path: str) -> bool:
308
+ """
309
+ Validate logging configuration file exists and is readable.
310
+
311
+ Args:
312
+ log_config_path: Path to logging configuration file
313
+
314
+ Returns:
315
+ True if valid, False otherwise
316
+ """
317
+ try:
318
+ log_config_file = Path(log_config_path)
319
+ if not log_config_file.exists():
320
+ print(f"āŒ Log configuration file not found: {log_config_path}")
321
+ return False
322
+ return True
323
+
324
+ except Exception as e:
325
+ print(f"āŒ Log configuration file validation failed: {e}")
326
+ return False