mcp-proxy-adapter 2.0.1__py3-none-any.whl → 6.9.50__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.

Potentially problematic release.


This version of mcp-proxy-adapter might be problematic. Click here for more details.

Files changed (269) 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 +400 -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 +307 -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 +105 -0
  36. mcp_proxy_adapter/cli/commands/config_validate.py +94 -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 +132 -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 +481 -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 +204 -0
  119. mcp_proxy_adapter/core/config/simple_config_generator.py +131 -0
  120. mcp_proxy_adapter/core/config/simple_config_validator.py +476 -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 +205 -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 +12 -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 +311 -0
  185. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +161 -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 +311 -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.50.dist-info/METADATA +1088 -0
  239. mcp_proxy_adapter-6.9.50.dist-info/RECORD +242 -0
  240. {mcp_proxy_adapter-2.0.1.dist-info → mcp_proxy_adapter-6.9.50.dist-info}/WHEEL +1 -1
  241. mcp_proxy_adapter-6.9.50.dist-info/entry_points.txt +14 -0
  242. mcp_proxy_adapter-6.9.50.dist-info/top_level.txt +1 -0
  243. adapters/__init__.py +0 -16
  244. analyzers/__init__.py +0 -14
  245. analyzers/docstring_analyzer.py +0 -199
  246. analyzers/type_analyzer.py +0 -151
  247. cli/__init__.py +0 -12
  248. cli/__main__.py +0 -79
  249. cli/command_runner.py +0 -233
  250. dispatchers/__init__.py +0 -14
  251. dispatchers/base_dispatcher.py +0 -85
  252. dispatchers/json_rpc_dispatcher.py +0 -198
  253. generators/__init__.py +0 -14
  254. generators/endpoint_generator.py +0 -172
  255. generators/openapi_generator.py +0 -254
  256. generators/rest_api_generator.py +0 -207
  257. mcp_proxy_adapter-2.0.1.dist-info/METADATA +0 -272
  258. mcp_proxy_adapter-2.0.1.dist-info/RECORD +0 -28
  259. mcp_proxy_adapter-2.0.1.dist-info/licenses/LICENSE +0 -21
  260. mcp_proxy_adapter-2.0.1.dist-info/top_level.txt +0 -7
  261. openapi_schema/__init__.py +0 -38
  262. openapi_schema/command_registry.py +0 -312
  263. openapi_schema/rest_schema.py +0 -510
  264. openapi_schema/rpc_generator.py +0 -307
  265. openapi_schema/rpc_schema.py +0 -416
  266. validators/__init__.py +0 -14
  267. validators/base_validator.py +0 -23
  268. validators/docstring_validator.py +0 -75
  269. validators/metadata_validator.py +0 -76
@@ -0,0 +1,372 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Certificate creation utilities for MCP Proxy Adapter.
6
+ """
7
+
8
+ import logging
9
+ from datetime import datetime, timedelta, timezone
10
+ from pathlib import Path
11
+ from typing import Dict, List, Optional
12
+
13
+ # Import mcp_security_framework
14
+ try:
15
+ from mcp_security_framework.core.cert_manager import (
16
+ CertificateManager,
17
+ CertificateConfig,
18
+ CAConfig,
19
+ ClientCertConfig,
20
+ ServerCertConfig,
21
+ )
22
+ SECURITY_FRAMEWORK_AVAILABLE = True
23
+ except ImportError:
24
+ SECURITY_FRAMEWORK_AVAILABLE = False
25
+ # Fallback to cryptography if mcp_security_framework is not available
26
+ from cryptography import x509
27
+ from cryptography.hazmat.primitives import hashes, serialization
28
+ from cryptography.hazmat.primitives.asymmetric import rsa
29
+ from cryptography.x509.oid import NameOID
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ class CertificateCreator:
35
+ """Creator for various types of certificates."""
36
+
37
+ # Default certificate validity period (1 year)
38
+ DEFAULT_VALIDITY_DAYS = 365
39
+
40
+ # Default key size
41
+ DEFAULT_KEY_SIZE = 2048
42
+
43
+ @staticmethod
44
+ def create_ca_certificate(
45
+ common_name: str,
46
+ output_dir: str,
47
+ validity_days: int = DEFAULT_VALIDITY_DAYS,
48
+ key_size: int = DEFAULT_KEY_SIZE,
49
+ ) -> Dict[str, str]:
50
+ """
51
+ Create a CA certificate and private key using mcp_security_framework.
52
+
53
+ Args:
54
+ common_name: Common name for the CA certificate
55
+ output_dir: Directory to save certificate and key files
56
+ validity_days: Certificate validity period in days
57
+ key_size: RSA key size in bits
58
+
59
+ Returns:
60
+ Dictionary with paths to created files
61
+
62
+ Raises:
63
+ ValueError: If parameters are invalid
64
+ OSError: If files cannot be created
65
+ """
66
+ if not SECURITY_FRAMEWORK_AVAILABLE:
67
+ logger.warning("mcp_security_framework not available, using fallback method")
68
+ return CertificateCreator._create_ca_certificate_fallback(
69
+ common_name, output_dir, validity_days, key_size
70
+ )
71
+
72
+ try:
73
+ # Validate parameters
74
+ if not common_name or not common_name.strip():
75
+ raise ValueError("Common name cannot be empty")
76
+
77
+ if validity_days <= 0:
78
+ raise ValueError("Validity days must be positive")
79
+
80
+ if key_size < 1024:
81
+ raise ValueError("Key size must be at least 1024 bits")
82
+
83
+ # Create output directory if it doesn't exist
84
+ Path(output_dir).mkdir(parents=True, exist_ok=True)
85
+
86
+ # Configure CA using mcp_security_framework
87
+ ca_config = CAConfig(
88
+ common_name=common_name,
89
+ organization="MCP Proxy Adapter CA",
90
+ organizational_unit="Certificate Authority",
91
+ country="US",
92
+ state="Default State",
93
+ locality="Default City",
94
+ validity_days=validity_days,
95
+ key_size=key_size,
96
+ key_type="RSA",
97
+ )
98
+
99
+ # Create certificate manager
100
+ cert_config = CertificateConfig(
101
+ output_dir=output_dir,
102
+ ca_cert_path=str(Path(output_dir) / f"{common_name}.crt"),
103
+ ca_key_path=str(Path(output_dir) / f"{common_name}.key"),
104
+ )
105
+
106
+ cert_manager = CertificateManager(cert_config)
107
+
108
+ # Generate CA certificate
109
+ ca_pair = cert_manager.create_ca_certificate(ca_config)
110
+
111
+ return {
112
+ "cert_path": str(ca_pair.cert_path),
113
+ "key_path": str(ca_pair.key_path),
114
+ }
115
+
116
+ except Exception as e:
117
+ logger.error(f"Failed to create CA certificate: {e}")
118
+ raise
119
+
120
+ @staticmethod
121
+ def _create_ca_certificate_fallback(
122
+ common_name: str, output_dir: str, validity_days: int, key_size: int
123
+ ) -> Dict[str, str]:
124
+ """Fallback CA certificate creation using cryptography."""
125
+ try:
126
+ # Create output directory if it doesn't exist
127
+ Path(output_dir).mkdir(parents=True, exist_ok=True)
128
+
129
+ # Generate private key
130
+ private_key = rsa.generate_private_key(
131
+ public_exponent=65537,
132
+ key_size=key_size,
133
+ )
134
+
135
+ # Create certificate
136
+ subject = issuer = x509.Name([
137
+ x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
138
+ x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Default State"),
139
+ x509.NameAttribute(NameOID.LOCALITY_NAME, "Default City"),
140
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, "MCP Proxy Adapter CA"),
141
+ x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Certificate Authority"),
142
+ x509.NameAttribute(NameOID.COMMON_NAME, common_name),
143
+ ])
144
+
145
+ cert = x509.CertificateBuilder().subject_name(
146
+ subject
147
+ ).issuer_name(
148
+ issuer
149
+ ).public_key(
150
+ private_key.public_key()
151
+ ).serial_number(
152
+ x509.random_serial_number()
153
+ ).not_valid_before(
154
+ datetime.now(timezone.utc)
155
+ ).not_valid_after(
156
+ datetime.now(timezone.utc) + timedelta(days=validity_days)
157
+ ).add_extension(
158
+ x509.BasicConstraints(ca=True, path_length=None),
159
+ critical=True,
160
+ ).add_extension(
161
+ x509.KeyUsage(
162
+ key_cert_sign=True,
163
+ crl_sign=True,
164
+ digital_signature=True,
165
+ key_encipherment=False,
166
+ data_encipherment=False,
167
+ key_agreement=False,
168
+ encipher_only=False,
169
+ decipher_only=False,
170
+ content_commitment=False,
171
+ ),
172
+ critical=True,
173
+ ).sign(private_key, hashes.SHA256())
174
+
175
+ # Save certificate and key
176
+ cert_path = Path(output_dir) / f"{common_name}.crt"
177
+ key_path = Path(output_dir) / f"{common_name}.key"
178
+
179
+ with open(cert_path, "wb") as f:
180
+ f.write(cert.public_bytes(serialization.Encoding.PEM))
181
+
182
+ with open(key_path, "wb") as f:
183
+ f.write(private_key.private_bytes(
184
+ encoding=serialization.Encoding.PEM,
185
+ format=serialization.PrivateFormat.PKCS8,
186
+ encryption_algorithm=serialization.NoEncryption()
187
+ ))
188
+
189
+ return {
190
+ "cert_path": str(cert_path),
191
+ "key_path": str(key_path),
192
+ }
193
+
194
+ except Exception as e:
195
+ logger.error(f"Failed to create CA certificate (fallback): {e}")
196
+ raise
197
+
198
+ @staticmethod
199
+ def create_server_certificate(
200
+ common_name: str,
201
+ output_dir: str,
202
+ ca_cert_path: str,
203
+ ca_key_path: str,
204
+ validity_days: int = DEFAULT_VALIDITY_DAYS,
205
+ key_size: int = DEFAULT_KEY_SIZE,
206
+ san_dns: Optional[List[str]] = None,
207
+ san_ip: Optional[List[str]] = None,
208
+ ) -> Dict[str, str]:
209
+ """
210
+ Create a server certificate signed by CA.
211
+
212
+ Args:
213
+ common_name: Common name for the server certificate
214
+ output_dir: Directory to save certificate and key files
215
+ ca_cert_path: Path to CA certificate
216
+ ca_key_path: Path to CA private key
217
+ validity_days: Certificate validity period in days
218
+ key_size: RSA key size in bits
219
+ san_dns: List of DNS names for SAN extension
220
+ san_ip: List of IP addresses for SAN extension
221
+
222
+ Returns:
223
+ Dictionary with paths to created files
224
+ """
225
+ if not SECURITY_FRAMEWORK_AVAILABLE:
226
+ logger.warning("mcp_security_framework not available, using fallback method")
227
+ return CertificateCreator._create_server_certificate_fallback(
228
+ common_name, output_dir, ca_cert_path, ca_key_path,
229
+ validity_days, key_size, san_dns, san_ip
230
+ )
231
+
232
+ try:
233
+ # Validate parameters
234
+ if not common_name or not common_name.strip():
235
+ raise ValueError("Common name cannot be empty")
236
+
237
+ if not Path(ca_cert_path).exists():
238
+ raise FileNotFoundError(f"CA certificate not found: {ca_cert_path}")
239
+
240
+ if not Path(ca_key_path).exists():
241
+ raise FileNotFoundError(f"CA key not found: {ca_key_path}")
242
+
243
+ # Create output directory if it doesn't exist
244
+ Path(output_dir).mkdir(parents=True, exist_ok=True)
245
+
246
+ # Configure server certificate using mcp_security_framework
247
+ server_config = ServerCertConfig(
248
+ common_name=common_name,
249
+ organization="MCP Proxy Adapter",
250
+ organizational_unit="Server",
251
+ country="US",
252
+ state="Default State",
253
+ locality="Default City",
254
+ validity_days=validity_days,
255
+ key_size=key_size,
256
+ key_type="RSA",
257
+ san_dns=san_dns or [],
258
+ san_ip=san_ip or [],
259
+ )
260
+
261
+ # Create certificate manager
262
+ cert_config = CertificateConfig(
263
+ output_dir=output_dir,
264
+ ca_cert_path=ca_cert_path,
265
+ ca_key_path=ca_key_path,
266
+ )
267
+
268
+ cert_manager = CertificateManager(cert_config)
269
+
270
+ # Generate server certificate
271
+ server_pair = cert_manager.create_server_certificate(server_config)
272
+
273
+ return {
274
+ "cert_path": str(server_pair.cert_path),
275
+ "key_path": str(server_pair.key_path),
276
+ }
277
+
278
+ except Exception as e:
279
+ logger.error(f"Failed to create server certificate: {e}")
280
+ raise
281
+
282
+ @staticmethod
283
+ def _create_server_certificate_fallback(
284
+ common_name: str,
285
+ output_dir: str,
286
+ ca_cert_path: str,
287
+ ca_key_path: str,
288
+ validity_days: int,
289
+ key_size: int,
290
+ san_dns: Optional[List[str]],
291
+ san_ip: Optional[List[str]],
292
+ ) -> Dict[str, str]:
293
+ """Fallback server certificate creation using cryptography."""
294
+ try:
295
+ # Create output directory if it doesn't exist
296
+ Path(output_dir).mkdir(parents=True, exist_ok=True)
297
+
298
+ # Load CA certificate and key
299
+ with open(ca_cert_path, "rb") as f:
300
+ ca_cert = x509.load_pem_x509_certificate(f.read())
301
+
302
+ with open(ca_key_path, "rb") as f:
303
+ ca_key = serialization.load_pem_private_key(f.read(), password=None)
304
+
305
+ # Generate server private key
306
+ private_key = rsa.generate_private_key(
307
+ public_exponent=65537,
308
+ key_size=key_size,
309
+ )
310
+
311
+ # Create certificate
312
+ subject = x509.Name([
313
+ x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
314
+ x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Default State"),
315
+ x509.NameAttribute(NameOID.LOCALITY_NAME, "Default City"),
316
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, "MCP Proxy Adapter"),
317
+ x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Server"),
318
+ x509.NameAttribute(NameOID.COMMON_NAME, common_name),
319
+ ])
320
+
321
+ # Build certificate
322
+ cert_builder = x509.CertificateBuilder().subject_name(
323
+ subject
324
+ ).issuer_name(
325
+ ca_cert.subject
326
+ ).public_key(
327
+ private_key.public_key()
328
+ ).serial_number(
329
+ x509.random_serial_number()
330
+ ).not_valid_before(
331
+ datetime.now(timezone.utc)
332
+ ).not_valid_after(
333
+ datetime.now(timezone.utc) + timedelta(days=validity_days)
334
+ )
335
+
336
+ # Add SAN extension if provided
337
+ if san_dns or san_ip:
338
+ san_list = []
339
+ if san_dns:
340
+ san_list.extend([x509.DNSName(name) for name in san_dns])
341
+ if san_ip:
342
+ san_list.extend([x509.IPAddress(ip) for ip in san_ip])
343
+
344
+ cert_builder = cert_builder.add_extension(
345
+ x509.SubjectAlternativeName(san_list),
346
+ critical=False,
347
+ )
348
+
349
+ cert = cert_builder.sign(ca_key, hashes.SHA256())
350
+
351
+ # Save certificate and key
352
+ cert_path = Path(output_dir) / f"{common_name}_server.crt"
353
+ key_path = Path(output_dir) / f"{common_name}_server.key"
354
+
355
+ with open(cert_path, "wb") as f:
356
+ f.write(cert.public_bytes(serialization.Encoding.PEM))
357
+
358
+ with open(key_path, "wb") as f:
359
+ f.write(private_key.private_bytes(
360
+ encoding=serialization.Encoding.PEM,
361
+ format=serialization.PrivateFormat.PKCS8,
362
+ encryption_algorithm=serialization.NoEncryption()
363
+ ))
364
+
365
+ return {
366
+ "cert_path": str(cert_path),
367
+ "key_path": str(key_path),
368
+ }
369
+
370
+ except Exception as e:
371
+ logger.error(f"Failed to create server certificate (fallback): {e}")
372
+ raise
@@ -0,0 +1,185 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Certificate information extraction utilities for MCP Proxy Adapter.
6
+ """
7
+
8
+ import logging
9
+ from typing import List
10
+
11
+ # Import mcp_security_framework
12
+ try:
13
+ from mcp_security_framework.utils.cert_utils import (
14
+ parse_certificate,
15
+ extract_roles_from_certificate,
16
+ extract_permissions_from_certificate,
17
+ )
18
+ SECURITY_FRAMEWORK_AVAILABLE = True
19
+ except ImportError:
20
+ SECURITY_FRAMEWORK_AVAILABLE = False
21
+ # Fallback to cryptography if mcp_security_framework is not available
22
+ from cryptography import x509
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ class CertificateExtractor:
28
+ """Extractor for certificate information."""
29
+
30
+ # Custom OID for roles (same as in RoleUtils)
31
+ ROLE_EXTENSION_OID = "1.3.6.1.4.1.99999.1"
32
+
33
+ @staticmethod
34
+ def extract_roles_from_certificate(cert_path: str) -> List[str]:
35
+ """
36
+ Extract roles from certificate.
37
+
38
+ Args:
39
+ cert_path: Path to certificate file
40
+
41
+ Returns:
42
+ List of roles found in certificate
43
+ """
44
+ if not SECURITY_FRAMEWORK_AVAILABLE:
45
+ logger.warning("mcp_security_framework not available, using fallback method")
46
+ return CertificateExtractor._extract_roles_from_certificate_fallback(cert_path)
47
+
48
+ try:
49
+ return extract_roles_from_certificate(cert_path)
50
+ except Exception as e:
51
+ logger.error(f"Failed to extract roles from certificate: {e}")
52
+ return []
53
+
54
+ @staticmethod
55
+ def _extract_roles_from_certificate_fallback(cert_path: str) -> List[str]:
56
+ """Fallback role extraction using cryptography."""
57
+ try:
58
+ with open(cert_path, "rb") as f:
59
+ cert = x509.load_pem_x509_certificate(f.read())
60
+
61
+ # Look for custom role extension
62
+ try:
63
+ role_extension = cert.extensions.get_extension_for_oid(
64
+ x509.ObjectIdentifier(CertificateExtractor.ROLE_EXTENSION_OID)
65
+ )
66
+ if role_extension:
67
+ # Parse roles from extension value
68
+ roles_str = role_extension.value.value.decode('utf-8')
69
+ return [role.strip() for role in roles_str.split(',') if role.strip()]
70
+ except x509.ExtensionNotFound:
71
+ pass
72
+
73
+ # Fallback: look for roles in subject alternative name
74
+ try:
75
+ san_extension = cert.extensions.get_extension_for_oid(
76
+ x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME
77
+ )
78
+ if san_extension:
79
+ roles = []
80
+ for name in san_extension.value:
81
+ if isinstance(name, x509.DNSName):
82
+ # Check if this looks like a role (e.g., role:admin)
83
+ if name.value.startswith('role:'):
84
+ roles.append(name.value[5:]) # Remove 'role:' prefix
85
+ return roles
86
+ except x509.ExtensionNotFound:
87
+ pass
88
+
89
+ return []
90
+
91
+ except Exception as e:
92
+ logger.error(f"Failed to extract roles from certificate (fallback): {e}")
93
+ return []
94
+
95
+ @staticmethod
96
+ def extract_roles_from_certificate_object(cert) -> List[str]:
97
+ """
98
+ Extract roles from certificate object.
99
+
100
+ Args:
101
+ cert: Certificate object
102
+
103
+ Returns:
104
+ List of roles found in certificate
105
+ """
106
+ try:
107
+ # Look for custom role extension
108
+ try:
109
+ role_extension = cert.extensions.get_extension_for_oid(
110
+ x509.ObjectIdentifier(CertificateExtractor.ROLE_EXTENSION_OID)
111
+ )
112
+ if role_extension:
113
+ # Parse roles from extension value
114
+ roles_str = role_extension.value.value.decode('utf-8')
115
+ return [role.strip() for role in roles_str.split(',') if role.strip()]
116
+ except x509.ExtensionNotFound:
117
+ pass
118
+
119
+ # Fallback: look for roles in subject alternative name
120
+ try:
121
+ san_extension = cert.extensions.get_extension_for_oid(
122
+ x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME
123
+ )
124
+ if san_extension:
125
+ roles = []
126
+ for name in san_extension.value:
127
+ if isinstance(name, x509.DNSName):
128
+ # Check if this looks like a role (e.g., role:admin)
129
+ if name.value.startswith('role:'):
130
+ roles.append(name.value[5:]) # Remove 'role:' prefix
131
+ return roles
132
+ except x509.ExtensionNotFound:
133
+ pass
134
+
135
+ return []
136
+
137
+ except Exception as e:
138
+ logger.error(f"Failed to extract roles from certificate object: {e}")
139
+ return []
140
+
141
+ @staticmethod
142
+ def extract_permissions_from_certificate(cert_path: str) -> List[str]:
143
+ """
144
+ Extract permissions from certificate.
145
+
146
+ Args:
147
+ cert_path: Path to certificate file
148
+
149
+ Returns:
150
+ List of permissions found in certificate
151
+ """
152
+ if not SECURITY_FRAMEWORK_AVAILABLE:
153
+ logger.warning("mcp_security_framework not available, using fallback method")
154
+ return CertificateExtractor._extract_permissions_from_certificate_fallback(cert_path)
155
+
156
+ try:
157
+ return extract_permissions_from_certificate(cert_path)
158
+ except Exception as e:
159
+ logger.error(f"Failed to extract permissions from certificate: {e}")
160
+ return []
161
+
162
+ @staticmethod
163
+ def _extract_permissions_from_certificate_fallback(cert_path: str) -> List[str]:
164
+ """Fallback permission extraction using cryptography."""
165
+ try:
166
+ with open(cert_path, "rb") as f:
167
+ cert = x509.load_pem_x509_certificate(f.read())
168
+
169
+ # Look for custom permission extension
170
+ try:
171
+ permission_extension = cert.extensions.get_extension_for_oid(
172
+ x509.ObjectIdentifier("1.3.6.1.4.1.99999.2") # Custom OID for permissions
173
+ )
174
+ if permission_extension:
175
+ # Parse permissions from extension value
176
+ permissions_str = permission_extension.value.value.decode('utf-8')
177
+ return [perm.strip() for perm in permissions_str.split(',') if perm.strip()]
178
+ except x509.ExtensionNotFound:
179
+ pass
180
+
181
+ return []
182
+
183
+ except Exception as e:
184
+ logger.error(f"Failed to extract permissions from certificate (fallback): {e}")
185
+ return []