mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.6__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 (131) hide show
  1. mcp_proxy_adapter/__init__.py +9 -5
  2. mcp_proxy_adapter/__main__.py +1 -1
  3. mcp_proxy_adapter/api/app.py +227 -176
  4. mcp_proxy_adapter/api/handlers.py +68 -60
  5. mcp_proxy_adapter/api/middleware/__init__.py +7 -5
  6. mcp_proxy_adapter/api/middleware/base.py +19 -16
  7. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +44 -34
  8. mcp_proxy_adapter/api/middleware/error_handling.py +57 -67
  9. mcp_proxy_adapter/api/middleware/factory.py +50 -52
  10. mcp_proxy_adapter/api/middleware/logging.py +46 -30
  11. mcp_proxy_adapter/api/middleware/performance.py +19 -16
  12. mcp_proxy_adapter/api/middleware/protocol_middleware.py +80 -50
  13. mcp_proxy_adapter/api/middleware/transport_middleware.py +26 -24
  14. mcp_proxy_adapter/api/middleware/unified_security.py +70 -51
  15. mcp_proxy_adapter/api/middleware/user_info_middleware.py +43 -34
  16. mcp_proxy_adapter/api/schemas.py +69 -43
  17. mcp_proxy_adapter/api/tool_integration.py +83 -63
  18. mcp_proxy_adapter/api/tools.py +60 -50
  19. mcp_proxy_adapter/commands/__init__.py +15 -6
  20. mcp_proxy_adapter/commands/auth_validation_command.py +107 -110
  21. mcp_proxy_adapter/commands/base.py +108 -112
  22. mcp_proxy_adapter/commands/builtin_commands.py +28 -18
  23. mcp_proxy_adapter/commands/catalog_manager.py +394 -265
  24. mcp_proxy_adapter/commands/cert_monitor_command.py +222 -204
  25. mcp_proxy_adapter/commands/certificate_management_command.py +210 -213
  26. mcp_proxy_adapter/commands/command_registry.py +275 -226
  27. mcp_proxy_adapter/commands/config_command.py +48 -33
  28. mcp_proxy_adapter/commands/dependency_container.py +22 -23
  29. mcp_proxy_adapter/commands/dependency_manager.py +65 -56
  30. mcp_proxy_adapter/commands/echo_command.py +15 -15
  31. mcp_proxy_adapter/commands/health_command.py +31 -29
  32. mcp_proxy_adapter/commands/help_command.py +97 -61
  33. mcp_proxy_adapter/commands/hooks.py +65 -49
  34. mcp_proxy_adapter/commands/key_management_command.py +148 -147
  35. mcp_proxy_adapter/commands/load_command.py +58 -40
  36. mcp_proxy_adapter/commands/plugins_command.py +80 -54
  37. mcp_proxy_adapter/commands/protocol_management_command.py +60 -48
  38. mcp_proxy_adapter/commands/proxy_registration_command.py +107 -115
  39. mcp_proxy_adapter/commands/reload_command.py +43 -37
  40. mcp_proxy_adapter/commands/result.py +26 -33
  41. mcp_proxy_adapter/commands/role_test_command.py +26 -26
  42. mcp_proxy_adapter/commands/roles_management_command.py +176 -173
  43. mcp_proxy_adapter/commands/security_command.py +134 -122
  44. mcp_proxy_adapter/commands/settings_command.py +47 -56
  45. mcp_proxy_adapter/commands/ssl_setup_command.py +109 -129
  46. mcp_proxy_adapter/commands/token_management_command.py +129 -158
  47. mcp_proxy_adapter/commands/transport_management_command.py +41 -36
  48. mcp_proxy_adapter/commands/unload_command.py +42 -37
  49. mcp_proxy_adapter/config.py +36 -35
  50. mcp_proxy_adapter/core/__init__.py +19 -21
  51. mcp_proxy_adapter/core/app_factory.py +30 -9
  52. mcp_proxy_adapter/core/app_runner.py +81 -64
  53. mcp_proxy_adapter/core/auth_validator.py +176 -182
  54. mcp_proxy_adapter/core/certificate_utils.py +469 -426
  55. mcp_proxy_adapter/core/client.py +155 -126
  56. mcp_proxy_adapter/core/client_manager.py +60 -54
  57. mcp_proxy_adapter/core/client_security.py +120 -91
  58. mcp_proxy_adapter/core/config_converter.py +176 -143
  59. mcp_proxy_adapter/core/config_validator.py +12 -4
  60. mcp_proxy_adapter/core/crl_utils.py +21 -7
  61. mcp_proxy_adapter/core/errors.py +64 -20
  62. mcp_proxy_adapter/core/logging.py +34 -29
  63. mcp_proxy_adapter/core/mtls_asgi.py +29 -25
  64. mcp_proxy_adapter/core/mtls_asgi_app.py +66 -54
  65. mcp_proxy_adapter/core/protocol_manager.py +154 -104
  66. mcp_proxy_adapter/core/proxy_client.py +202 -144
  67. mcp_proxy_adapter/core/proxy_registration.py +7 -3
  68. mcp_proxy_adapter/core/role_utils.py +139 -125
  69. mcp_proxy_adapter/core/security_adapter.py +88 -77
  70. mcp_proxy_adapter/core/security_factory.py +50 -44
  71. mcp_proxy_adapter/core/security_integration.py +72 -24
  72. mcp_proxy_adapter/core/server_adapter.py +68 -64
  73. mcp_proxy_adapter/core/server_engine.py +71 -53
  74. mcp_proxy_adapter/core/settings.py +68 -58
  75. mcp_proxy_adapter/core/ssl_utils.py +69 -56
  76. mcp_proxy_adapter/core/transport_manager.py +72 -60
  77. mcp_proxy_adapter/core/unified_config_adapter.py +201 -150
  78. mcp_proxy_adapter/core/utils.py +4 -2
  79. mcp_proxy_adapter/custom_openapi.py +107 -99
  80. mcp_proxy_adapter/examples/basic_framework/main.py +9 -2
  81. mcp_proxy_adapter/examples/commands/__init__.py +1 -1
  82. mcp_proxy_adapter/examples/create_certificates_simple.py +182 -71
  83. mcp_proxy_adapter/examples/debug_request_state.py +38 -19
  84. mcp_proxy_adapter/examples/debug_role_chain.py +53 -20
  85. mcp_proxy_adapter/examples/demo_client.py +48 -36
  86. mcp_proxy_adapter/examples/examples/basic_framework/main.py +9 -2
  87. mcp_proxy_adapter/examples/examples/full_application/__init__.py +1 -0
  88. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +22 -10
  89. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +24 -17
  90. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +16 -3
  91. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +13 -3
  92. mcp_proxy_adapter/examples/examples/full_application/main.py +27 -2
  93. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +48 -14
  94. mcp_proxy_adapter/examples/full_application/__init__.py +1 -0
  95. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +22 -10
  96. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +24 -17
  97. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +16 -3
  98. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +13 -3
  99. mcp_proxy_adapter/examples/full_application/main.py +27 -2
  100. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +48 -14
  101. mcp_proxy_adapter/examples/generate_all_certificates.py +198 -73
  102. mcp_proxy_adapter/examples/generate_certificates.py +31 -16
  103. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +220 -74
  104. mcp_proxy_adapter/examples/generate_test_configs.py +68 -91
  105. mcp_proxy_adapter/examples/proxy_registration_example.py +76 -75
  106. mcp_proxy_adapter/examples/run_example.py +23 -5
  107. mcp_proxy_adapter/examples/run_full_test_suite.py +109 -71
  108. mcp_proxy_adapter/examples/run_proxy_server.py +22 -9
  109. mcp_proxy_adapter/examples/run_security_tests.py +103 -41
  110. mcp_proxy_adapter/examples/run_security_tests_fixed.py +72 -36
  111. mcp_proxy_adapter/examples/scripts/config_generator.py +288 -187
  112. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +185 -72
  113. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +220 -74
  114. mcp_proxy_adapter/examples/security_test_client.py +196 -127
  115. mcp_proxy_adapter/examples/setup_test_environment.py +17 -29
  116. mcp_proxy_adapter/examples/test_config.py +19 -4
  117. mcp_proxy_adapter/examples/test_config_generator.py +23 -7
  118. mcp_proxy_adapter/examples/test_examples.py +84 -56
  119. mcp_proxy_adapter/examples/universal_client.py +119 -62
  120. mcp_proxy_adapter/openapi.py +108 -115
  121. mcp_proxy_adapter/utils/config_generator.py +429 -274
  122. mcp_proxy_adapter/version.py +1 -2
  123. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/METADATA +1 -1
  124. mcp_proxy_adapter-6.3.6.dist-info/RECORD +144 -0
  125. mcp_proxy_adapter-6.3.6.dist-info/top_level.txt +2 -0
  126. mcp_proxy_adapter_issue_package/demonstrate_issue.py +178 -0
  127. mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
  128. mcp_proxy_adapter-6.3.4.dist-info/top_level.txt +0 -1
  129. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/WHEEL +0 -0
  130. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/entry_points.txt +0 -0
  131. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/licenses/LICENSE +0 -0
@@ -20,15 +20,15 @@ logger = logging.getLogger(__name__)
20
20
  class MTLSASGIApp:
21
21
  """
22
22
  ASGI application wrapper for mTLS support.
23
-
23
+
24
24
  Extracts client certificates from SSL context and stores them in ASGI scope
25
25
  for access by FastAPI middleware.
26
26
  """
27
-
27
+
28
28
  def __init__(self, app, ssl_config: Dict[str, Any]):
29
29
  """
30
30
  Initialize MTLS ASGI app.
31
-
31
+
32
32
  Args:
33
33
  app: The underlying ASGI application
34
34
  ssl_config: SSL configuration dictionary
@@ -36,13 +36,15 @@ class MTLSASGIApp:
36
36
  self.app = app
37
37
  self.ssl_config = ssl_config
38
38
  self.client_cert_required = ssl_config.get("client_cert_required", True)
39
-
40
- logger.info(f"MTLS ASGI app initialized: client_cert_required={self.client_cert_required}")
41
-
39
+
40
+ logger.info(
41
+ f"MTLS ASGI app initialized: client_cert_required={self.client_cert_required}"
42
+ )
43
+
42
44
  async def __call__(self, scope: Dict[str, Any], receive, send):
43
45
  """
44
46
  Handle ASGI request with mTLS support.
45
-
47
+
46
48
  Args:
47
49
  scope: ASGI scope dictionary
48
50
  receive: ASGI receive callable
@@ -55,27 +57,31 @@ class MTLSASGIApp:
55
57
  if client_cert:
56
58
  # Store certificate in scope for middleware access
57
59
  scope["client_certificate"] = client_cert
58
- logger.debug(f"Client certificate extracted: {client_cert.get('subject', {})}")
60
+ logger.debug(
61
+ f"Client certificate extracted: {client_cert.get('subject', {})}"
62
+ )
59
63
  elif self.client_cert_required:
60
64
  logger.warning("Client certificate required but not provided")
61
65
  # Return 401 Unauthorized
62
66
  await self._send_unauthorized_response(send)
63
67
  return
64
-
68
+
65
69
  # Call the underlying application
66
70
  await self.app(scope, receive, send)
67
-
71
+
68
72
  except Exception as e:
69
73
  logger.error(f"Error in MTLS ASGI app: {e}")
70
74
  await self._send_error_response(send, str(e))
71
-
72
- def _extract_client_certificate(self, scope: Dict[str, Any]) -> Optional[Dict[str, Any]]:
75
+
76
+ def _extract_client_certificate(
77
+ self, scope: Dict[str, Any]
78
+ ) -> Optional[Dict[str, Any]]:
73
79
  """
74
80
  Extract client certificate from SSL context.
75
-
81
+
76
82
  Args:
77
83
  scope: ASGI scope dictionary
78
-
84
+
79
85
  Returns:
80
86
  Certificate dictionary or None if not found
81
87
  """
@@ -84,9 +90,9 @@ class MTLSASGIApp:
84
90
  if not ssl_context:
85
91
  logger.debug("No SSL context found in scope")
86
92
  return None
87
-
93
+
88
94
  # Try to get peer certificate
89
- if hasattr(ssl_context, 'getpeercert'):
95
+ if hasattr(ssl_context, "getpeercert"):
90
96
  cert_data = ssl_context.getpeercert(binary_form=True)
91
97
  if cert_data:
92
98
  # Parse certificate
@@ -98,18 +104,18 @@ class MTLSASGIApp:
98
104
  else:
99
105
  logger.debug("SSL context has no getpeercert method")
100
106
  return None
101
-
107
+
102
108
  except Exception as e:
103
109
  logger.error(f"Failed to extract client certificate: {e}")
104
110
  return None
105
-
111
+
106
112
  def _cert_to_dict(self, cert: x509.Certificate) -> Dict[str, Any]:
107
113
  """
108
114
  Convert x509 certificate to dictionary.
109
-
115
+
110
116
  Args:
111
117
  cert: x509 certificate object
112
-
118
+
113
119
  Returns:
114
120
  Certificate dictionary
115
121
  """
@@ -118,12 +124,12 @@ class MTLSASGIApp:
118
124
  subject = {}
119
125
  for name in cert.subject:
120
126
  subject[name.oid._name] = name.value
121
-
127
+
122
128
  # Extract issuer
123
129
  issuer = {}
124
130
  for name in cert.issuer:
125
131
  issuer[name.oid._name] = name.value
126
-
132
+
127
133
  return {
128
134
  "subject": subject,
129
135
  "issuer": issuer,
@@ -133,54 +139,60 @@ class MTLSASGIApp:
133
139
  "version": cert.version.value,
134
140
  "signature_algorithm_oid": cert.signature_algorithm_oid._name,
135
141
  "public_key": {
136
- "key_size": cert.public_key().key_size if hasattr(cert.public_key(), 'key_size') else None,
137
- "public_numbers": str(cert.public_key().public_numbers()) if hasattr(cert.public_key(), 'public_numbers') else None
138
- }
142
+ "key_size": (
143
+ cert.public_key().key_size
144
+ if hasattr(cert.public_key(), "key_size")
145
+ else None
146
+ ),
147
+ "public_numbers": (
148
+ str(cert.public_key().public_numbers())
149
+ if hasattr(cert.public_key(), "public_numbers")
150
+ else None
151
+ ),
152
+ },
139
153
  }
140
154
  except Exception as e:
141
155
  logger.error(f"Failed to convert certificate to dict: {e}")
142
156
  return {"error": str(e)}
143
-
157
+
144
158
  async def _send_unauthorized_response(self, send):
145
159
  """Send 401 Unauthorized response."""
146
- await send({
147
- "type": "http.response.start",
148
- "status": 401,
149
- "headers": [
150
- (b"content-type", b"application/json"),
151
- (b"content-length", b"0")
152
- ]
153
- })
154
- await send({
155
- "type": "http.response.body",
156
- "body": b""
157
- })
158
-
160
+ await send(
161
+ {
162
+ "type": "http.response.start",
163
+ "status": 401,
164
+ "headers": [
165
+ (b"content-type", b"application/json"),
166
+ (b"content-length", b"0"),
167
+ ],
168
+ }
169
+ )
170
+ await send({"type": "http.response.body", "body": b""})
171
+
159
172
  async def _send_error_response(self, send, error_message: str):
160
173
  """Send error response."""
161
- body = f'{{"error": "{error_message}"}}'.encode('utf-8')
162
- await send({
163
- "type": "http.response.start",
164
- "status": 500,
165
- "headers": [
166
- (b"content-type", b"application/json"),
167
- (b"content-length", str(len(body)).encode())
168
- ]
169
- })
170
- await send({
171
- "type": "http.response.body",
172
- "body": body
173
- })
174
+ body = f'{{"error": "{error_message}"}}'.encode("utf-8")
175
+ await send(
176
+ {
177
+ "type": "http.response.start",
178
+ "status": 500,
179
+ "headers": [
180
+ (b"content-type", b"application/json"),
181
+ (b"content-length", str(len(body)).encode()),
182
+ ],
183
+ }
184
+ )
185
+ await send({"type": "http.response.body", "body": body})
174
186
 
175
187
 
176
188
  def create_mtls_asgi_app(app, ssl_config: Dict[str, Any]):
177
189
  """
178
190
  Create MTLS ASGI application wrapper.
179
-
191
+
180
192
  Args:
181
193
  app: The underlying ASGI application
182
194
  ssl_config: SSL configuration dictionary
183
-
195
+
184
196
  Returns:
185
197
  MTLS ASGI app wrapper
186
198
  """