mcp-proxy-adapter 6.9.43__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. mcp_proxy_adapter/__init__.py +47 -0
  2. mcp_proxy_adapter/__main__.py +13 -0
  3. mcp_proxy_adapter/api/__init__.py +0 -0
  4. mcp_proxy_adapter/api/app.py +66 -0
  5. mcp_proxy_adapter/api/core/__init__.py +18 -0
  6. mcp_proxy_adapter/api/core/app_factory.py +355 -0
  7. mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
  8. mcp_proxy_adapter/api/core/registration_context.py +356 -0
  9. mcp_proxy_adapter/api/core/registration_manager.py +266 -0
  10. mcp_proxy_adapter/api/core/registration_tasks.py +84 -0
  11. mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
  12. mcp_proxy_adapter/api/handlers.py +181 -0
  13. mcp_proxy_adapter/api/middleware/__init__.py +21 -0
  14. mcp_proxy_adapter/api/middleware/base.py +54 -0
  15. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +73 -0
  16. mcp_proxy_adapter/api/middleware/error_handling.py +76 -0
  17. mcp_proxy_adapter/api/middleware/factory.py +147 -0
  18. mcp_proxy_adapter/api/middleware/logging.py +31 -0
  19. mcp_proxy_adapter/api/middleware/performance.py +51 -0
  20. mcp_proxy_adapter/api/middleware/protocol_middleware.py +140 -0
  21. mcp_proxy_adapter/api/middleware/transport_middleware.py +87 -0
  22. mcp_proxy_adapter/api/middleware/unified_security.py +223 -0
  23. mcp_proxy_adapter/api/middleware/user_info_middleware.py +132 -0
  24. mcp_proxy_adapter/api/openapi/__init__.py +21 -0
  25. mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
  26. mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
  27. mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
  28. mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
  29. mcp_proxy_adapter/api/schemas.py +270 -0
  30. mcp_proxy_adapter/api/tool_integration.py +131 -0
  31. mcp_proxy_adapter/api/tools.py +163 -0
  32. mcp_proxy_adapter/cli/__init__.py +12 -0
  33. mcp_proxy_adapter/cli/commands/__init__.py +15 -0
  34. mcp_proxy_adapter/cli/commands/client.py +100 -0
  35. mcp_proxy_adapter/cli/commands/config_generate.py +35 -0
  36. mcp_proxy_adapter/cli/commands/config_validate.py +74 -0
  37. mcp_proxy_adapter/cli/commands/generate.py +259 -0
  38. mcp_proxy_adapter/cli/commands/server.py +174 -0
  39. mcp_proxy_adapter/cli/commands/sets.py +128 -0
  40. mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
  41. mcp_proxy_adapter/cli/examples/__init__.py +8 -0
  42. mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
  43. mcp_proxy_adapter/cli/examples/https_token.py +96 -0
  44. mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
  45. mcp_proxy_adapter/cli/main.py +63 -0
  46. mcp_proxy_adapter/cli/parser.py +338 -0
  47. mcp_proxy_adapter/cli/validators.py +231 -0
  48. mcp_proxy_adapter/client/jsonrpc_client/__init__.py +9 -0
  49. mcp_proxy_adapter/client/jsonrpc_client/client.py +42 -0
  50. mcp_proxy_adapter/client/jsonrpc_client/command_api.py +45 -0
  51. mcp_proxy_adapter/client/jsonrpc_client/proxy_api.py +224 -0
  52. mcp_proxy_adapter/client/jsonrpc_client/queue_api.py +60 -0
  53. mcp_proxy_adapter/client/jsonrpc_client/transport.py +108 -0
  54. mcp_proxy_adapter/client/proxy.py +123 -0
  55. mcp_proxy_adapter/commands/__init__.py +66 -0
  56. mcp_proxy_adapter/commands/auth_validation_command.py +69 -0
  57. mcp_proxy_adapter/commands/base.py +389 -0
  58. mcp_proxy_adapter/commands/builtin_commands.py +30 -0
  59. mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
  60. mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
  61. mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
  62. mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
  63. mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
  64. mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
  65. mcp_proxy_adapter/commands/catalog_manager.py +97 -0
  66. mcp_proxy_adapter/commands/cert_monitor_command.py +552 -0
  67. mcp_proxy_adapter/commands/certificate_management_command.py +562 -0
  68. mcp_proxy_adapter/commands/command_registry.py +298 -0
  69. mcp_proxy_adapter/commands/config_command.py +102 -0
  70. mcp_proxy_adapter/commands/dependency_container.py +40 -0
  71. mcp_proxy_adapter/commands/dependency_manager.py +143 -0
  72. mcp_proxy_adapter/commands/echo_command.py +48 -0
  73. mcp_proxy_adapter/commands/health_command.py +142 -0
  74. mcp_proxy_adapter/commands/help_command.py +175 -0
  75. mcp_proxy_adapter/commands/hooks.py +172 -0
  76. mcp_proxy_adapter/commands/key_management_command.py +484 -0
  77. mcp_proxy_adapter/commands/load_command.py +123 -0
  78. mcp_proxy_adapter/commands/plugins_command.py +246 -0
  79. mcp_proxy_adapter/commands/protocol_management_command.py +216 -0
  80. mcp_proxy_adapter/commands/proxy_registration_command.py +319 -0
  81. mcp_proxy_adapter/commands/queue_commands.py +750 -0
  82. mcp_proxy_adapter/commands/registration_status_command.py +76 -0
  83. mcp_proxy_adapter/commands/registry/__init__.py +18 -0
  84. mcp_proxy_adapter/commands/registry/command_info.py +103 -0
  85. mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
  86. mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
  87. mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
  88. mcp_proxy_adapter/commands/reload_command.py +136 -0
  89. mcp_proxy_adapter/commands/result.py +157 -0
  90. mcp_proxy_adapter/commands/role_test_command.py +99 -0
  91. mcp_proxy_adapter/commands/roles_management_command.py +502 -0
  92. mcp_proxy_adapter/commands/security_command.py +472 -0
  93. mcp_proxy_adapter/commands/settings_command.py +113 -0
  94. mcp_proxy_adapter/commands/ssl_setup_command.py +306 -0
  95. mcp_proxy_adapter/commands/token_management_command.py +500 -0
  96. mcp_proxy_adapter/commands/transport_management_command.py +129 -0
  97. mcp_proxy_adapter/commands/unload_command.py +92 -0
  98. mcp_proxy_adapter/config.py +32 -0
  99. mcp_proxy_adapter/core/__init__.py +8 -0
  100. mcp_proxy_adapter/core/app_factory.py +560 -0
  101. mcp_proxy_adapter/core/app_runner.py +318 -0
  102. mcp_proxy_adapter/core/auth_validator.py +508 -0
  103. mcp_proxy_adapter/core/certificate/__init__.py +20 -0
  104. mcp_proxy_adapter/core/certificate/certificate_creator.py +372 -0
  105. mcp_proxy_adapter/core/certificate/certificate_extractor.py +185 -0
  106. mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
  107. mcp_proxy_adapter/core/certificate/certificate_validator.py +388 -0
  108. mcp_proxy_adapter/core/certificate/ssl_context_manager.py +65 -0
  109. mcp_proxy_adapter/core/certificate_utils.py +249 -0
  110. mcp_proxy_adapter/core/client.py +608 -0
  111. mcp_proxy_adapter/core/client_manager.py +271 -0
  112. mcp_proxy_adapter/core/client_security.py +411 -0
  113. mcp_proxy_adapter/core/config/__init__.py +18 -0
  114. mcp_proxy_adapter/core/config/config.py +237 -0
  115. mcp_proxy_adapter/core/config/config_factory.py +22 -0
  116. mcp_proxy_adapter/core/config/config_loader.py +66 -0
  117. mcp_proxy_adapter/core/config/feature_manager.py +31 -0
  118. mcp_proxy_adapter/core/config/simple_config.py +116 -0
  119. mcp_proxy_adapter/core/config/simple_config_generator.py +100 -0
  120. mcp_proxy_adapter/core/config/simple_config_validator.py +380 -0
  121. mcp_proxy_adapter/core/config_converter.py +252 -0
  122. mcp_proxy_adapter/core/config_validator.py +211 -0
  123. mcp_proxy_adapter/core/crl_utils.py +362 -0
  124. mcp_proxy_adapter/core/errors.py +276 -0
  125. mcp_proxy_adapter/core/job_manager.py +54 -0
  126. mcp_proxy_adapter/core/logging.py +250 -0
  127. mcp_proxy_adapter/core/mtls_asgi.py +140 -0
  128. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  129. mcp_proxy_adapter/core/mtls_proxy.py +229 -0
  130. mcp_proxy_adapter/core/mtls_server.py +154 -0
  131. mcp_proxy_adapter/core/protocol_manager.py +232 -0
  132. mcp_proxy_adapter/core/proxy/__init__.py +19 -0
  133. mcp_proxy_adapter/core/proxy/auth_manager.py +26 -0
  134. mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +160 -0
  135. mcp_proxy_adapter/core/proxy/registration_client.py +186 -0
  136. mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
  137. mcp_proxy_adapter/core/proxy_client.py +184 -0
  138. mcp_proxy_adapter/core/proxy_registration.py +80 -0
  139. mcp_proxy_adapter/core/role_utils.py +103 -0
  140. mcp_proxy_adapter/core/security_adapter.py +343 -0
  141. mcp_proxy_adapter/core/security_factory.py +96 -0
  142. mcp_proxy_adapter/core/security_integration.py +342 -0
  143. mcp_proxy_adapter/core/server_adapter.py +251 -0
  144. mcp_proxy_adapter/core/server_engine.py +217 -0
  145. mcp_proxy_adapter/core/settings.py +260 -0
  146. mcp_proxy_adapter/core/signal_handler.py +107 -0
  147. mcp_proxy_adapter/core/ssl_utils.py +161 -0
  148. mcp_proxy_adapter/core/transport_manager.py +153 -0
  149. mcp_proxy_adapter/core/unified_config_adapter.py +471 -0
  150. mcp_proxy_adapter/core/utils.py +101 -0
  151. mcp_proxy_adapter/core/validation/__init__.py +21 -0
  152. mcp_proxy_adapter/core/validation/config_validator.py +219 -0
  153. mcp_proxy_adapter/core/validation/file_validator.py +131 -0
  154. mcp_proxy_adapter/core/validation/protocol_validator.py +190 -0
  155. mcp_proxy_adapter/core/validation/security_validator.py +140 -0
  156. mcp_proxy_adapter/core/validation/validation_result.py +27 -0
  157. mcp_proxy_adapter/custom_openapi.py +58 -0
  158. mcp_proxy_adapter/examples/__init__.py +16 -0
  159. mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
  160. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  161. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  162. mcp_proxy_adapter/examples/basic_framework/main.py +52 -0
  163. mcp_proxy_adapter/examples/bugfix_certificate_config.py +261 -0
  164. mcp_proxy_adapter/examples/cert_manager_bugfix.py +203 -0
  165. mcp_proxy_adapter/examples/check_config.py +413 -0
  166. mcp_proxy_adapter/examples/client_usage_example.py +164 -0
  167. mcp_proxy_adapter/examples/commands/__init__.py +5 -0
  168. mcp_proxy_adapter/examples/config_builder.py +234 -0
  169. mcp_proxy_adapter/examples/config_cli.py +282 -0
  170. mcp_proxy_adapter/examples/create_test_configs.py +174 -0
  171. mcp_proxy_adapter/examples/debug_request_state.py +130 -0
  172. mcp_proxy_adapter/examples/debug_role_chain.py +191 -0
  173. mcp_proxy_adapter/examples/demo_client.py +287 -0
  174. mcp_proxy_adapter/examples/full_application/__init__.py +13 -0
  175. mcp_proxy_adapter/examples/full_application/commands/__init__.py +8 -0
  176. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +45 -0
  177. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +52 -0
  178. mcp_proxy_adapter/examples/full_application/commands/echo_command.py +32 -0
  179. mcp_proxy_adapter/examples/full_application/commands/help_command.py +54 -0
  180. mcp_proxy_adapter/examples/full_application/commands/list_command.py +57 -0
  181. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +5 -0
  182. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +29 -0
  183. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +27 -0
  184. mcp_proxy_adapter/examples/full_application/main.py +264 -0
  185. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +81 -0
  186. mcp_proxy_adapter/examples/full_application/run_mtls.py +252 -0
  187. mcp_proxy_adapter/examples/full_application/run_simple.py +152 -0
  188. mcp_proxy_adapter/examples/full_application/test_minimal_server.py +45 -0
  189. mcp_proxy_adapter/examples/full_application/test_server.py +163 -0
  190. mcp_proxy_adapter/examples/full_application/test_simple_server.py +62 -0
  191. mcp_proxy_adapter/examples/generate_config.py +502 -0
  192. mcp_proxy_adapter/examples/proxy_registration_example.py +335 -0
  193. mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
  194. mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
  195. mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
  196. mcp_proxy_adapter/examples/queue_server_example.py +85 -0
  197. mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
  198. mcp_proxy_adapter/examples/required_certificates.py +208 -0
  199. mcp_proxy_adapter/examples/run_example.py +77 -0
  200. mcp_proxy_adapter/examples/run_full_test_suite.py +619 -0
  201. mcp_proxy_adapter/examples/run_proxy_server.py +153 -0
  202. mcp_proxy_adapter/examples/run_security_tests_fixed.py +435 -0
  203. mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
  204. mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
  205. mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
  206. mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
  207. mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
  208. mcp_proxy_adapter/examples/security_test_client.py +72 -0
  209. mcp_proxy_adapter/examples/setup/__init__.py +24 -0
  210. mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
  211. mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
  212. mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
  213. mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
  214. mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
  215. mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
  216. mcp_proxy_adapter/examples/setup_test_environment.py +235 -0
  217. mcp_proxy_adapter/examples/simple_protocol_test.py +125 -0
  218. mcp_proxy_adapter/examples/test_chk_hostname_automated.py +211 -0
  219. mcp_proxy_adapter/examples/test_config.py +205 -0
  220. mcp_proxy_adapter/examples/test_config_builder.py +110 -0
  221. mcp_proxy_adapter/examples/test_examples.py +308 -0
  222. mcp_proxy_adapter/examples/test_framework_complete.py +267 -0
  223. mcp_proxy_adapter/examples/test_mcp_server.py +187 -0
  224. mcp_proxy_adapter/examples/test_protocol_examples.py +337 -0
  225. mcp_proxy_adapter/examples/universal_client.py +674 -0
  226. mcp_proxy_adapter/examples/update_config_certificates.py +135 -0
  227. mcp_proxy_adapter/examples/validate_generator_compatibility.py +385 -0
  228. mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +61 -0
  229. mcp_proxy_adapter/integrations/__init__.py +25 -0
  230. mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
  231. mcp_proxy_adapter/main.py +313 -0
  232. mcp_proxy_adapter/openapi.py +375 -0
  233. mcp_proxy_adapter/schemas/base_schema.json +114 -0
  234. mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
  235. mcp_proxy_adapter/schemas/roles.json +37 -0
  236. mcp_proxy_adapter/schemas/roles_schema.json +162 -0
  237. mcp_proxy_adapter/version.py +5 -0
  238. mcp_proxy_adapter-6.9.43.dist-info/METADATA +739 -0
  239. mcp_proxy_adapter-6.9.43.dist-info/RECORD +242 -0
  240. mcp_proxy_adapter-6.9.43.dist-info/WHEEL +5 -0
  241. mcp_proxy_adapter-6.9.43.dist-info/entry_points.txt +12 -0
  242. mcp_proxy_adapter-6.9.43.dist-info/top_level.txt +1 -0
@@ -0,0 +1,380 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Simple configuration validator ensuring required fields and files exist.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from dataclasses import dataclass
12
+ from typing import List
13
+
14
+ from .simple_config import SimpleConfigModel
15
+ from mcp_proxy_adapter.core.certificate.certificate_validator import (
16
+ CertificateValidator,
17
+ )
18
+
19
+
20
+ @dataclass
21
+ class ValidationError:
22
+ message: str
23
+
24
+
25
+ class SimpleConfigValidator:
26
+ """Validate SimpleConfigModel instances."""
27
+
28
+ def validate(self, model: SimpleConfigModel) -> List[ValidationError]:
29
+ errors: List[ValidationError] = []
30
+ errors.extend(self._validate_server(model))
31
+ errors.extend(self._validate_proxy_client(model))
32
+ errors.extend(self._validate_auth(model))
33
+ return errors
34
+
35
+ def _validate_server(self, model: SimpleConfigModel) -> List[ValidationError]:
36
+ e: List[ValidationError] = []
37
+ s = model.server
38
+ if not s.host:
39
+ e.append(ValidationError("server.host is required"))
40
+ if not isinstance(s.port, int): # type: ignore[unreachable]
41
+ e.append(ValidationError("server.port must be integer"))
42
+ if s.protocol not in ("http", "https", "mtls"):
43
+ e.append(
44
+ ValidationError("server.protocol must be one of: http, https, mtls")
45
+ )
46
+ # Protocol-specific certificate requirements
47
+ if s.protocol == "mtls":
48
+ # For mtls: cert_file and key_file are required
49
+ # CA is required if use_system_ca=False (default), optional if use_system_ca=True
50
+ if not s.cert_file:
51
+ e.append(
52
+ ValidationError("server.cert_file is required for mtls protocol")
53
+ )
54
+ if not s.key_file:
55
+ e.append(
56
+ ValidationError("server.key_file is required for mtls protocol")
57
+ )
58
+ # CA validation is handled below based on use_system_ca flag
59
+ elif s.protocol == "https":
60
+ # For https: certificates are optional, but if one is specified, both must be
61
+ # If key is specified but cert is not - this is an error
62
+ if s.key_file and not s.cert_file:
63
+ e.append(
64
+ ValidationError(
65
+ "server.key_file is specified but server.cert_file is missing for https protocol"
66
+ )
67
+ )
68
+ if s.cert_file and not s.key_file:
69
+ e.append(
70
+ ValidationError(
71
+ "server.cert_file is specified but server.key_file is missing for https protocol"
72
+ )
73
+ )
74
+ # Files existence (if provided)
75
+ for label, path in (
76
+ ("cert_file", s.cert_file),
77
+ ("key_file", s.key_file),
78
+ ("ca_cert_file", s.ca_cert_file),
79
+ ("crl_file", s.crl_file),
80
+ ):
81
+ if path and not os.path.exists(path):
82
+ e.append(ValidationError(f"server.{label} not found: {path}"))
83
+
84
+ # Validate certificate validity if files exist
85
+ if (
86
+ s.cert_file
87
+ and s.key_file
88
+ and os.path.exists(s.cert_file)
89
+ and os.path.exists(s.key_file)
90
+ ):
91
+ # Check certificate-key match
92
+ if not CertificateValidator.validate_certificate_key_match(
93
+ s.cert_file, s.key_file
94
+ ):
95
+ e.append(
96
+ ValidationError("server.cert_file does not match server.key_file")
97
+ )
98
+ # Check certificate expiry
99
+ if not CertificateValidator.validate_certificate_not_expired(s.cert_file):
100
+ e.append(ValidationError("server.cert_file is expired"))
101
+
102
+ # Check CRL if specified
103
+ if s.crl_file:
104
+ if not os.path.exists(s.crl_file):
105
+ e.append(
106
+ ValidationError(f"server.crl_file not found: {s.crl_file}")
107
+ )
108
+ elif not CertificateValidator.validate_certificate_not_revoked(
109
+ s.cert_file, s.crl_file
110
+ ):
111
+ e.append(
112
+ ValidationError(
113
+ "server.cert_file is revoked according to server.crl_file"
114
+ )
115
+ )
116
+
117
+ # Validate certificate chain - use CA cert if provided, otherwise check use_system_ca
118
+ if s.cert_file and os.path.exists(s.cert_file):
119
+ if s.ca_cert_file and os.path.exists(s.ca_cert_file):
120
+ # Use provided CA certificate (only CA from config is used by default)
121
+ if not CertificateValidator.validate_certificate_chain(
122
+ s.cert_file, s.ca_cert_file
123
+ ):
124
+ e.append(
125
+ ValidationError(
126
+ "server.cert_file is not signed by server.ca_cert_file"
127
+ )
128
+ )
129
+ else:
130
+ # CA not provided - check if system CA is allowed
131
+ if s.protocol == "mtls":
132
+ # For mTLS: CA is required if use_system_ca=False (default)
133
+ if s.use_system_ca:
134
+ # System CA is explicitly allowed
135
+ if not CertificateValidator.validate_certificate_with_system_store(
136
+ s.cert_file
137
+ ):
138
+ e.append(
139
+ ValidationError(
140
+ "server.cert_file is not valid according to system CA store"
141
+ )
142
+ )
143
+ else:
144
+ # System CA is not allowed (default) - CA must be provided for mTLS
145
+ e.append(
146
+ ValidationError(
147
+ "server.ca_cert_file is required for mtls protocol when use_system_ca is False"
148
+ )
149
+ )
150
+ elif s.protocol == "https":
151
+ # For HTTPS: CA is optional, but if not provided, use system CA if allowed
152
+ if s.use_system_ca:
153
+ # System CA is explicitly allowed
154
+ if not CertificateValidator.validate_certificate_with_system_store(
155
+ s.cert_file
156
+ ):
157
+ e.append(
158
+ ValidationError(
159
+ "server.cert_file is not valid according to system CA store"
160
+ )
161
+ )
162
+ # If use_system_ca=False and CA not provided for HTTPS, that's OK
163
+ # (HTTPS can work without explicit CA validation)
164
+
165
+ return e
166
+
167
+ def _validate_proxy_client(self, model: SimpleConfigModel) -> List[ValidationError]:
168
+ """
169
+ Validate proxy_client configuration.
170
+
171
+ Note: Validation is performed only when enabled=True, because if the client
172
+ is disabled, its configuration is not used. The registration.auto_on_startup
173
+ flag controls automatic registration behavior but doesn't affect whether
174
+ the client configuration needs to be valid.
175
+
176
+ IMPORTANT: proxy_client.protocol indicates the SERVER's protocol, not the proxy's protocol.
177
+ The proxy itself typically runs on HTTP (for test proxy) or may have its own protocol.
178
+ Client certificates in proxy_client are used for connecting TO the proxy (if proxy uses HTTPS/mTLS),
179
+ not for the server's own protocol. For test proxy (HTTP), these certificates are not used
180
+ but may be validated for consistency.
181
+ """
182
+ e: List[ValidationError] = []
183
+ pc = model.proxy_client
184
+ if pc.enabled:
185
+ if not pc.host:
186
+ e.append(ValidationError("proxy_client.host is required when enabled"))
187
+ if not isinstance(pc.port, int): # type: ignore[unreachable]
188
+ e.append(ValidationError("proxy_client.port must be integer"))
189
+ if pc.protocol not in ("http", "https", "mtls"):
190
+ e.append(
191
+ ValidationError(
192
+ "proxy_client.protocol must be one of: http, https, mtls"
193
+ )
194
+ )
195
+ # Validate server_id if provided (preferred field name)
196
+ if pc.server_id is not None and not isinstance(pc.server_id, str):
197
+ e.append(ValidationError("proxy_client.server_id must be a string"))
198
+ if pc.server_id is not None and not pc.server_id.strip():
199
+ e.append(ValidationError("proxy_client.server_id cannot be empty"))
200
+ # Warn about deprecated server_name (but allow it for backward compatibility)
201
+ if pc.server_name is not None and pc.server_id is None:
202
+ # server_name is deprecated but allowed for backward compatibility
203
+ # registration_context.py will use server_id if available, fallback to server_name
204
+ pass
205
+ # Protocol-specific certificate requirements
206
+ # NOTE: These certificates are for connecting TO the proxy (if proxy uses HTTPS/mTLS),
207
+ # not for the server's own protocol. For test proxy (HTTP), these are not used.
208
+ # NOTE: proxy_client.protocol indicates the SERVER's protocol, not the proxy's protocol
209
+ # Certificates here are for connecting TO the proxy if proxy itself uses HTTPS/mTLS
210
+ if pc.protocol == "mtls":
211
+ # For mtls: cert_file and key_file are required
212
+ # CA is required if use_system_ca=False (default), optional if use_system_ca=True
213
+ if not pc.cert_file:
214
+ e.append(
215
+ ValidationError(
216
+ "proxy_client.cert_file is required for mtls protocol"
217
+ )
218
+ )
219
+ if not pc.key_file:
220
+ e.append(
221
+ ValidationError(
222
+ "proxy_client.key_file is required for mtls protocol"
223
+ )
224
+ )
225
+ # CA validation is handled below based on use_system_ca flag
226
+ elif pc.protocol == "https":
227
+ # For https: certificates are optional, but if one is specified, both must be
228
+ # If key is specified but cert is not - this is an error
229
+ if pc.key_file and not pc.cert_file:
230
+ e.append(
231
+ ValidationError(
232
+ "proxy_client.key_file is specified but proxy_client.cert_file is missing for https protocol"
233
+ )
234
+ )
235
+ if pc.cert_file and not pc.key_file:
236
+ e.append(
237
+ ValidationError(
238
+ "proxy_client.cert_file is specified but proxy_client.key_file is missing for https protocol"
239
+ )
240
+ )
241
+ # Files existence (if provided)
242
+ for label, path in (
243
+ ("cert_file", pc.cert_file),
244
+ ("key_file", pc.key_file),
245
+ ("ca_cert_file", pc.ca_cert_file),
246
+ ("crl_file", pc.crl_file),
247
+ ):
248
+ if path and not os.path.exists(path):
249
+ e.append(ValidationError(f"proxy_client.{label} not found: {path}"))
250
+
251
+ # Validate certificate validity if files exist
252
+ if (
253
+ pc.cert_file
254
+ and pc.key_file
255
+ and os.path.exists(pc.cert_file)
256
+ and os.path.exists(pc.key_file)
257
+ ):
258
+ # Check certificate-key match
259
+ if not CertificateValidator.validate_certificate_key_match(
260
+ pc.cert_file, pc.key_file
261
+ ):
262
+ e.append(
263
+ ValidationError(
264
+ "proxy_client.cert_file does not match proxy_client.key_file"
265
+ )
266
+ )
267
+ # Check certificate expiry
268
+ if not CertificateValidator.validate_certificate_not_expired(
269
+ pc.cert_file
270
+ ):
271
+ e.append(ValidationError("proxy_client.cert_file is expired"))
272
+
273
+ # Check CRL if specified
274
+ if pc.crl_file:
275
+ if not os.path.exists(pc.crl_file):
276
+ e.append(
277
+ ValidationError(
278
+ f"proxy_client.crl_file not found: {pc.crl_file}"
279
+ )
280
+ )
281
+ elif not CertificateValidator.validate_certificate_not_revoked(
282
+ pc.cert_file, pc.crl_file
283
+ ):
284
+ e.append(
285
+ ValidationError(
286
+ "proxy_client.cert_file is revoked according to proxy_client.crl_file"
287
+ )
288
+ )
289
+
290
+ # Validate certificate chain - use CA cert if provided, otherwise check use_system_ca
291
+ # NOTE: These certificates are for connecting TO the proxy (if proxy uses HTTPS/mTLS),
292
+ # not for the server's own protocol. For test proxy (HTTP), these are not used.
293
+ if pc.cert_file and os.path.exists(pc.cert_file):
294
+ if pc.ca_cert_file and os.path.exists(pc.ca_cert_file):
295
+ # Use provided CA certificate (only CA from config is used by default)
296
+ if not CertificateValidator.validate_certificate_chain(
297
+ pc.cert_file, pc.ca_cert_file
298
+ ):
299
+ e.append(
300
+ ValidationError(
301
+ "proxy_client.cert_file is not signed by proxy_client.ca_cert_file"
302
+ )
303
+ )
304
+ else:
305
+ # CA not provided - check if system CA is allowed
306
+ # NOTE: This validation is for proxy connection certificates, not server protocol
307
+ # NOTE: proxy_client.protocol indicates the SERVER's protocol, not the proxy's protocol
308
+ # But we validate certificates for proxy connection here
309
+ if pc.protocol == "mtls":
310
+ # For mTLS: CA is required if use_system_ca=False (default)
311
+ if pc.use_system_ca:
312
+ # System CA is explicitly allowed
313
+ if not CertificateValidator.validate_certificate_with_system_store(
314
+ pc.cert_file
315
+ ):
316
+ e.append(
317
+ ValidationError(
318
+ "proxy_client.cert_file is not valid according to system CA store"
319
+ )
320
+ )
321
+ else:
322
+ # System CA is not allowed (default) - CA must be provided for mTLS
323
+ e.append(
324
+ ValidationError(
325
+ "proxy_client.ca_cert_file is required for mtls protocol when use_system_ca is False"
326
+ )
327
+ )
328
+ elif pc.protocol == "https":
329
+ # For HTTPS: CA is optional, but if not provided, use system CA if allowed
330
+ if pc.use_system_ca:
331
+ # System CA is explicitly allowed
332
+ if not CertificateValidator.validate_certificate_with_system_store(
333
+ pc.cert_file
334
+ ):
335
+ e.append(
336
+ ValidationError(
337
+ "proxy_client.cert_file is not valid according to system CA store"
338
+ )
339
+ )
340
+ # If use_system_ca=False and CA not provided for HTTPS, that's OK
341
+ # (HTTPS can work without explicit CA validation)
342
+
343
+ # Heartbeat
344
+ if not pc.heartbeat.endpoint:
345
+ e.append(ValidationError("proxy_client.heartbeat.endpoint is required"))
346
+ if not isinstance(pc.heartbeat.interval, int) or pc.heartbeat.interval <= 0:
347
+ e.append(
348
+ ValidationError(
349
+ "proxy_client.heartbeat.interval must be positive integer"
350
+ )
351
+ )
352
+ # Registration endpoints
353
+ if not pc.registration.register_endpoint:
354
+ e.append(
355
+ ValidationError(
356
+ "proxy_client.registration.register_endpoint is required"
357
+ )
358
+ )
359
+ if not pc.registration.unregister_endpoint:
360
+ e.append(
361
+ ValidationError(
362
+ "proxy_client.registration.unregister_endpoint is required"
363
+ )
364
+ )
365
+ return e
366
+
367
+ def _validate_auth(self, model: SimpleConfigModel) -> List[ValidationError]:
368
+ e: List[ValidationError] = []
369
+ a = model.auth
370
+ if a.use_roles and not a.use_token:
371
+ e.append(
372
+ ValidationError("auth.use_roles requires auth.use_token to be true")
373
+ )
374
+ if a.use_token and not a.tokens:
375
+ e.append(
376
+ ValidationError(
377
+ "auth.tokens must be provided when auth.use_token is true"
378
+ )
379
+ )
380
+ return e
@@ -0,0 +1,252 @@
1
+ """
2
+ Configuration Converter for security framework integration.
3
+
4
+ This module provides utilities to convert between mcp_proxy_adapter configuration
5
+ format and mcp_security_framework configuration format, ensuring backward compatibility.
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ from typing import Dict, Any, Optional
11
+ from pathlib import Path
12
+
13
+ from mcp_proxy_adapter.core.logging import get_global_logger
14
+
15
+
16
+ class ConfigConverter:
17
+ """
18
+ Converter for configuration formats.
19
+
20
+ Provides methods to convert between mcp_proxy_adapter configuration
21
+ and mcp_security_framework configuration formats.
22
+ """
23
+
24
+ @staticmethod
25
+ def to_security_framework_config(mcp_config: Dict[str, Any]) -> Dict[str, Any]:
26
+ """
27
+ Convert mcp_proxy_adapter configuration to SecurityConfig format.
28
+
29
+ Args:
30
+ mcp_config: mcp_proxy_adapter configuration dictionary
31
+
32
+ Returns:
33
+ SecurityConfig compatible dictionary
34
+ """
35
+ try:
36
+ # Start with default security framework config
37
+ security_config = {
38
+ "auth": {
39
+ "enabled": True,
40
+ "methods": ["api_key"],
41
+ "api_keys": {},
42
+ "jwt_secret": "",
43
+ "jwt_algorithm": "HS256",
44
+ },
45
+ "ssl": {
46
+ "enabled": False,
47
+ "cert_file": None,
48
+ "key_file": None,
49
+ "ca_cert": None,
50
+ "min_tls_version": "TLSv1.2",
51
+ "verify_client": False,
52
+ "client_cert_required": False,
53
+ },
54
+ "permissions": {
55
+ "enabled": True,
56
+ "roles_file": "roles.json",
57
+ "default_role": "user",
58
+ "deny_by_default": True,
59
+ },
60
+ "rate_limit": {
61
+ "enabled": True,
62
+ "requests_per_minute": 60,
63
+ "requests_per_hour": 1000,
64
+ "burst_limit": 10,
65
+ "by_ip": True,
66
+ "by_user": True,
67
+ },
68
+ }
69
+
70
+ # Convert from security section if exists
71
+ if "security" in mcp_config:
72
+ security_section = mcp_config["security"]
73
+
74
+ # Convert auth config
75
+ if "auth" in security_section:
76
+ auth_config = security_section["auth"]
77
+ security_config["auth"].update(
78
+ {
79
+ "enabled": auth_config.get("enabled", True),
80
+ "methods": auth_config.get("methods", ["api_key"]),
81
+ "api_keys": auth_config.get("api_keys", {}),
82
+ "jwt_secret": auth_config.get("jwt_secret", ""),
83
+ "jwt_algorithm": auth_config.get("jwt_algorithm", "HS256"),
84
+ }
85
+ )
86
+
87
+ # Convert SSL config
88
+ if "ssl" in security_section:
89
+ ssl_config = security_section["ssl"]
90
+ security_config["ssl"].update(
91
+ {
92
+ "enabled": ssl_config.get("enabled", False),
93
+ "cert_file": ssl_config.get("cert_file"),
94
+ "key_file": ssl_config.get("key_file"),
95
+ "ca_cert": ssl_config.get("ca_cert"),
96
+ "min_tls_version": ssl_config.get(
97
+ "min_tls_version", "TLSv1.2"
98
+ ),
99
+ "verify_client": ssl_config.get("verify_client", False),
100
+ "client_cert_required": ssl_config.get(
101
+ "client_cert_required", False
102
+ ),
103
+ }
104
+ )
105
+
106
+ # Convert permissions config
107
+ if "permissions" in security_section:
108
+ permissions_config = security_section["permissions"]
109
+ security_config["permissions"].update(
110
+ {
111
+ "enabled": permissions_config.get("enabled", True),
112
+ "roles_file": permissions_config.get(
113
+ "roles_file", "roles.json"
114
+ ),
115
+ "default_role": permissions_config.get(
116
+ "default_role", "user"
117
+ ),
118
+ "deny_by_default": permissions_config.get(
119
+ "deny_by_default", True
120
+ ),
121
+ }
122
+ )
123
+
124
+ # Convert rate limit config
125
+ if "rate_limit" in security_section:
126
+ rate_limit_config = security_section["rate_limit"]
127
+ security_config["rate_limit"].update(
128
+ {
129
+ "enabled": rate_limit_config.get("enabled", True),
130
+ "requests_per_minute": rate_limit_config.get(
131
+ "requests_per_minute", 60
132
+ ),
133
+ "requests_per_hour": rate_limit_config.get(
134
+ "requests_per_hour", 1000
135
+ ),
136
+ "burst_limit": rate_limit_config.get("burst_limit", 10),
137
+ "by_ip": rate_limit_config.get("by_ip", True),
138
+ "by_user": rate_limit_config.get("by_user", True),
139
+ }
140
+ )
141
+
142
+ # Convert from legacy SSL config if security section doesn't exist
143
+ elif "ssl" in mcp_config:
144
+ ssl_config = mcp_config["ssl"]
145
+ security_config["ssl"].update(
146
+ {
147
+ "enabled": ssl_config.get("enabled", False),
148
+ "cert_file": ssl_config.get("cert_file"),
149
+ "key_file": ssl_config.get("key_file"),
150
+ "ca_cert": ssl_config.get("ca_cert"),
151
+ "min_tls_version": ssl_config.get("min_tls_version", "TLSv1.2"),
152
+ "verify_client": ssl_config.get("verify_client", False),
153
+ "client_cert_required": ssl_config.get(
154
+ "client_cert_required", False
155
+ ),
156
+ }
157
+ )
158
+
159
+ # Extract API keys from legacy SSL config
160
+ if "api_keys" in ssl_config:
161
+ security_config["auth"]["api_keys"] = ssl_config["api_keys"]
162
+
163
+ # Convert from legacy roles config
164
+ if "roles" in mcp_config:
165
+ roles_config = mcp_config["roles"]
166
+ security_config["permissions"].update(
167
+ {
168
+ "enabled": roles_config.get("enabled", True),
169
+ "roles_file": roles_config.get("config_file", "roles.json"),
170
+ "default_role": "user",
171
+ "deny_by_default": roles_config.get("default_policy", {}).get(
172
+ "deny_by_default", True
173
+ ),
174
+ }
175
+ )
176
+
177
+ get_global_logger().info(
178
+ "Configuration converted to security framework format successfully"
179
+ )
180
+ return security_config
181
+
182
+ except Exception as e:
183
+ get_global_logger().error(
184
+ f"Failed to convert configuration to security framework format: {e}"
185
+ )
186
+ return ConfigConverter._get_default_security_config()
187
+
188
+ @staticmethod
189
+
190
+ @staticmethod
191
+
192
+ @staticmethod
193
+
194
+ @staticmethod
195
+ def _get_default_security_config() -> Dict[str, Any]:
196
+ """
197
+ Get default security framework configuration.
198
+
199
+ Returns:
200
+ Default security framework configuration
201
+ """
202
+ return {
203
+ "auth": {
204
+ "enabled": True,
205
+ "methods": ["api_key"],
206
+ "api_keys": {},
207
+ "jwt_secret": "",
208
+ "jwt_algorithm": "HS256",
209
+ },
210
+ "ssl": {
211
+ "enabled": False,
212
+ "cert_file": None,
213
+ "key_file": None,
214
+ "ca_cert": None,
215
+ "min_tls_version": "TLSv1.2",
216
+ "verify_client": False,
217
+ "client_cert_required": False,
218
+ },
219
+ "permissions": {
220
+ "enabled": True,
221
+ "roles_file": "roles.json",
222
+ "default_role": "user",
223
+ "deny_by_default": True,
224
+ },
225
+ "rate_limit": {
226
+ "enabled": True,
227
+ "requests_per_minute": 60,
228
+ "requests_per_hour": 1000,
229
+ "burst_limit": 10,
230
+ "by_ip": True,
231
+ "by_user": True,
232
+ },
233
+ }
234
+
235
+ @staticmethod
236
+ def _get_default_mcp_config() -> Dict[str, Any]:
237
+ """
238
+ Get default mcp_proxy_adapter configuration.
239
+
240
+ Returns:
241
+ Default mcp_proxy_adapter configuration
242
+ """
243
+ return {
244
+ "security": {
245
+ "framework": "mcp_security_framework",
246
+ "enabled": True,
247
+ "auth": {"enabled": True, "methods": ["api_key"], "api_keys": {}},
248
+ "ssl": {"enabled": False, "cert_file": None, "key_file": None},
249
+ "permissions": {"enabled": True, "roles_file": "roles.json"},
250
+ "rate_limit": {"enabled": True, "requests_per_minute": 60},
251
+ }
252
+ }