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
@@ -9,49 +9,56 @@ from fastapi import Request, Response
9
9
  from starlette.responses import JSONResponse
10
10
 
11
11
  from mcp_proxy_adapter.core.logging import logger
12
- from mcp_proxy_adapter.core.errors import MicroserviceError, CommandError, ValidationError
12
+ from mcp_proxy_adapter.core.errors import (
13
+ MicroserviceError,
14
+ CommandError,
15
+ ValidationError,
16
+ )
13
17
  from .base import BaseMiddleware
14
18
 
19
+
15
20
  class ErrorHandlingMiddleware(BaseMiddleware):
16
21
  """
17
22
  Middleware for handling and formatting errors.
18
23
  """
19
-
24
+
20
25
  def __init__(self, app):
21
26
  """
22
27
  Initialize error handling middleware.
23
-
28
+
24
29
  Args:
25
30
  app: FastAPI application
26
31
  """
27
32
  super().__init__(app)
28
-
29
- async def dispatch(self, request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response:
33
+
34
+ async def dispatch(
35
+ self, request: Request, call_next: Callable[[Request], Awaitable[Response]]
36
+ ) -> Response:
30
37
  """
31
38
  Processes request and catches errors.
32
-
39
+
33
40
  Args:
34
41
  request: Request.
35
42
  call_next: Next handler.
36
-
43
+
37
44
  Returns:
38
45
  Response.
39
46
  """
40
47
  try:
41
48
  # Call the next middleware or main handler
42
49
  return await call_next(request)
43
-
50
+
44
51
  except CommandError as e:
45
52
  # Command error
46
53
  request_id = getattr(request.state, "request_id", "unknown")
47
54
  logger.debug(f"[{request_id}] Command error: {str(e)}")
48
-
55
+
49
56
  # Проверяем, является ли запрос JSON-RPC
50
57
  is_jsonrpc = self._is_json_rpc_request(request)
51
-
58
+
52
59
  # Get JSON-RPC request ID if available
53
60
  request_id_jsonrpc = await self._get_json_rpc_id(request)
54
-
61
+
55
62
  # If request was JSON-RPC
56
63
  if is_jsonrpc:
57
64
  return JSONResponse(
@@ -61,26 +68,23 @@ class ErrorHandlingMiddleware(BaseMiddleware):
61
68
  "error": {
62
69
  "code": e.code,
63
70
  "message": str(e),
64
- "data": e.data if hasattr(e, "data") and e.data else None
71
+ "data": e.data if hasattr(e, "data") and e.data else None,
65
72
  },
66
- "id": request_id_jsonrpc
67
- }
73
+ "id": request_id_jsonrpc,
74
+ },
68
75
  )
69
-
76
+
70
77
  # Regular API error
71
- return JSONResponse(
72
- status_code=400,
73
- content=e.to_dict()
74
- )
75
-
78
+ return JSONResponse(status_code=400, content=e.to_dict())
79
+
76
80
  except ValidationError as e:
77
81
  # Validation error
78
82
  request_id = getattr(request.state, "request_id", "unknown")
79
83
  logger.debug(f"[{request_id}] Validation error: {str(e)}")
80
-
84
+
81
85
  # Get JSON-RPC request ID if available
82
86
  request_id_jsonrpc = await self._get_json_rpc_id(request)
83
-
87
+
84
88
  # If request was JSON-RPC
85
89
  if self._is_json_rpc_request(request):
86
90
  return JSONResponse(
@@ -90,26 +94,23 @@ class ErrorHandlingMiddleware(BaseMiddleware):
90
94
  "error": {
91
95
  "code": -32602,
92
96
  "message": "Invalid params",
93
- "data": e.data if hasattr(e, "data") and e.data else None
97
+ "data": e.data if hasattr(e, "data") and e.data else None,
94
98
  },
95
- "id": request_id_jsonrpc
96
- }
99
+ "id": request_id_jsonrpc,
100
+ },
97
101
  )
98
-
102
+
99
103
  # Regular API error
100
- return JSONResponse(
101
- status_code=400,
102
- content=e.to_dict()
103
- )
104
-
104
+ return JSONResponse(status_code=400, content=e.to_dict())
105
+
105
106
  except MicroserviceError as e:
106
107
  # Other microservice error
107
108
  request_id = getattr(request.state, "request_id", "unknown")
108
109
  logger.debug(f"[{request_id}] Microservice error: {str(e)}")
109
-
110
+
110
111
  # Get JSON-RPC request ID if available
111
112
  request_id_jsonrpc = await self._get_json_rpc_id(request)
112
-
113
+
113
114
  # If request was JSON-RPC
114
115
  if self._is_json_rpc_request(request):
115
116
  return JSONResponse(
@@ -119,71 +120,60 @@ class ErrorHandlingMiddleware(BaseMiddleware):
119
120
  "error": {
120
121
  "code": -32000,
121
122
  "message": str(e),
122
- "data": e.data if hasattr(e, "data") and e.data else None
123
+ "data": e.data if hasattr(e, "data") and e.data else None,
123
124
  },
124
- "id": request_id_jsonrpc
125
- }
125
+ "id": request_id_jsonrpc,
126
+ },
126
127
  )
127
-
128
+
128
129
  # Regular API error
129
- return JSONResponse(
130
- status_code=400,
131
- content=e.to_dict()
132
- )
133
-
130
+ return JSONResponse(status_code=400, content=e.to_dict())
131
+
134
132
  except Exception as e:
135
133
  # Unexpected error
136
134
  request_id = getattr(request.state, "request_id", "unknown")
137
135
  logger.debug(f"[{request_id}] Unexpected error: {str(e)}")
138
-
136
+
139
137
  # Get JSON-RPC request ID if available
140
138
  request_id_jsonrpc = await self._get_json_rpc_id(request)
141
-
139
+
142
140
  # If request was JSON-RPC
143
141
  if self._is_json_rpc_request(request):
144
142
  return JSONResponse(
145
143
  status_code=500,
146
144
  content={
147
145
  "jsonrpc": "2.0",
148
- "error": {
149
- "code": -32603,
150
- "message": "Internal error"
151
- },
152
- "id": request_id_jsonrpc
153
- }
146
+ "error": {"code": -32603, "message": "Internal error"},
147
+ "id": request_id_jsonrpc,
148
+ },
154
149
  )
155
-
150
+
156
151
  # Regular API error
157
152
  return JSONResponse(
158
153
  status_code=500,
159
- content={
160
- "error": {
161
- "code": 500,
162
- "message": "Internal server error"
163
- }
164
- }
154
+ content={"error": {"code": 500, "message": "Internal server error"}},
165
155
  )
166
-
156
+
167
157
  def _is_json_rpc_request(self, request: Request) -> bool:
168
158
  """
169
159
  Checks if request is a JSON-RPC request.
170
-
160
+
171
161
  Args:
172
162
  request: Request.
173
-
163
+
174
164
  Returns:
175
165
  True if request is JSON-RPC, False otherwise.
176
166
  """
177
167
  # Only requests to /api/jsonrpc are JSON-RPC requests
178
168
  return request.url.path == "/api/jsonrpc"
179
-
169
+
180
170
  async def _get_json_rpc_id(self, request: Request) -> Optional[Any]:
181
171
  """
182
172
  Gets JSON-RPC request ID.
183
-
173
+
184
174
  Args:
185
175
  request: Request.
186
-
176
+
187
177
  Returns:
188
178
  JSON-RPC request ID if available, None otherwise.
189
179
  """
@@ -191,18 +181,18 @@ class ErrorHandlingMiddleware(BaseMiddleware):
191
181
  # Use request state to avoid body parsing if already done
192
182
  if hasattr(request.state, "json_rpc_id"):
193
183
  return request.state.json_rpc_id
194
-
184
+
195
185
  # Parse request body
196
186
  body = await request.body()
197
187
  if body:
198
188
  body_text = body.decode("utf-8")
199
189
  body_json = json.loads(body_text)
200
190
  request_id = body_json.get("id")
201
-
191
+
202
192
  # Save ID in request state to avoid reparsing
203
193
  request.state.json_rpc_id = request_id
204
194
  return request_id
205
195
  except Exception as e:
206
196
  logger.warning(f"Error parsing JSON-RPC ID: {str(e)}")
207
-
208
- return None
197
+
198
+ return None
@@ -22,15 +22,15 @@ from .user_info_middleware import UserInfoMiddleware
22
22
  class MiddlewareFactory:
23
23
  """
24
24
  Factory for creating and managing middleware components.
25
-
25
+
26
26
  Provides methods to create middleware components with proper configuration
27
27
  and dependency management.
28
28
  """
29
-
29
+
30
30
  def __init__(self, app: FastAPI, config: Dict[str, Any]):
31
31
  """
32
32
  Initialize middleware factory.
33
-
33
+
34
34
  Args:
35
35
  app: FastAPI application
36
36
  config: Application configuration
@@ -38,136 +38,134 @@ class MiddlewareFactory:
38
38
  self.app = app
39
39
  self.config = config
40
40
  self.middleware_stack: List[BaseMiddleware] = []
41
-
41
+
42
42
  logger.info("Middleware factory initialized")
43
-
43
+
44
44
  def create_security_middleware(self) -> Optional[UnifiedSecurityMiddleware]:
45
45
  """
46
46
  Create unified security middleware.
47
-
47
+
48
48
  Returns:
49
49
  UnifiedSecurityMiddleware instance or None if creation failed
50
50
  """
51
51
  try:
52
52
  security_config = self.config.get("security", {})
53
-
53
+
54
54
  if not security_config.get("enabled", True):
55
55
  logger.info("Security middleware disabled by configuration")
56
56
  return None
57
-
57
+
58
58
  middleware = UnifiedSecurityMiddleware(self.app, self.config)
59
59
  self.middleware_stack.append(middleware)
60
-
60
+
61
61
  logger.info("Unified security middleware created successfully")
62
62
  return middleware
63
-
63
+
64
64
  except Exception as e:
65
65
  logger.error(f"Failed to create unified security middleware: {e}")
66
66
  return None
67
-
67
+
68
68
  def create_error_handling_middleware(self) -> Optional[ErrorHandlingMiddleware]:
69
69
  """
70
70
  Create error handling middleware.
71
-
71
+
72
72
  Returns:
73
73
  ErrorHandlingMiddleware instance or None if creation failed
74
74
  """
75
75
  try:
76
76
  # Import here to avoid circular imports
77
77
  from .error_handling import ErrorHandlingMiddleware
78
-
78
+
79
79
  middleware = ErrorHandlingMiddleware(self.app)
80
80
  self.middleware_stack.append(middleware)
81
-
81
+
82
82
  logger.info("Error handling middleware created successfully")
83
83
  return middleware
84
-
84
+
85
85
  except Exception as e:
86
86
  logger.error(f"Failed to create error handling middleware: {e}")
87
87
  return None
88
-
88
+
89
89
  def create_logging_middleware(self) -> Optional[LoggingMiddleware]:
90
90
  """
91
91
  Create logging middleware.
92
-
92
+
93
93
  Returns:
94
94
  LoggingMiddleware instance or None if creation failed
95
95
  """
96
96
  try:
97
97
  # Import here to avoid circular imports
98
98
  from .logging import LoggingMiddleware
99
-
99
+
100
100
  middleware = LoggingMiddleware(self.app, self.config)
101
101
  self.middleware_stack.append(middleware)
102
-
102
+
103
103
  logger.info("Logging middleware created successfully")
104
104
  return middleware
105
-
105
+
106
106
  except Exception as e:
107
107
  logger.error(f"Failed to create logging middleware: {e}")
108
108
  return None
109
-
109
+
110
110
  def create_user_info_middleware(self) -> Optional[UserInfoMiddleware]:
111
111
  """
112
112
  Create user info middleware.
113
-
113
+
114
114
  Returns:
115
115
  UserInfoMiddleware instance or None if creation failed
116
116
  """
117
117
  try:
118
118
  middleware = UserInfoMiddleware(self.app, self.config)
119
119
  self.middleware_stack.append(middleware)
120
-
120
+
121
121
  logger.info("User info middleware created successfully")
122
122
  return middleware
123
-
123
+
124
124
  except Exception as e:
125
125
  logger.error(f"Failed to create user info middleware: {e}")
126
126
  return None
127
-
128
127
 
129
-
130
128
  def create_all_middleware(self) -> List[BaseMiddleware]:
131
129
  """
132
130
  Create all required middleware components.
133
-
131
+
134
132
  Returns:
135
133
  List of created middleware instances
136
134
  """
137
135
  middleware_list = []
138
-
136
+
139
137
  # Create security middleware (unified)
140
138
  security_middleware = self.create_security_middleware()
141
139
  if security_middleware:
142
140
  middleware_list.append(security_middleware)
143
-
141
+
144
142
  # Create error handling middleware
145
143
  error_middleware = self.create_error_handling_middleware()
146
144
  if error_middleware:
147
145
  middleware_list.append(error_middleware)
148
-
146
+
149
147
  # Create logging middleware
150
148
  logging_middleware = self.create_logging_middleware()
151
149
  if logging_middleware:
152
150
  middleware_list.append(logging_middleware)
153
-
151
+
154
152
  # Create user info middleware
155
153
  user_info_middleware = self.create_user_info_middleware()
156
154
  if user_info_middleware:
157
155
  middleware_list.append(user_info_middleware)
158
-
156
+
159
157
  logger.info(f"Created {len(middleware_list)} middleware components")
160
158
  return middleware_list
161
-
162
159
 
163
-
164
- def get_middleware_by_type(self, middleware_type: Type[BaseMiddleware]) -> Optional[BaseMiddleware]:
160
+ def get_middleware_by_type(
161
+ self, middleware_type: Type[BaseMiddleware]
162
+ ) -> Optional[BaseMiddleware]:
165
163
  """
166
164
  Get middleware instance by type.
167
-
165
+
168
166
  Args:
169
167
  middleware_type: Type of middleware to find
170
-
168
+
171
169
  Returns:
172
170
  Middleware instance or None if not found
173
171
  """
@@ -175,31 +173,31 @@ class MiddlewareFactory:
175
173
  if isinstance(middleware, middleware_type):
176
174
  return middleware
177
175
  return None
178
-
176
+
179
177
  def get_security_middleware(self) -> Optional[UnifiedSecurityMiddleware]:
180
178
  """
181
179
  Get unified security middleware instance.
182
-
180
+
183
181
  Returns:
184
182
  UnifiedSecurityMiddleware instance or None if not found
185
183
  """
186
184
  return self.get_middleware_by_type(UnifiedSecurityMiddleware)
187
-
185
+
188
186
  def validate_middleware_config(self) -> bool:
189
187
  """
190
188
  Validate middleware configuration.
191
-
189
+
192
190
  Returns:
193
191
  True if configuration is valid, False otherwise
194
192
  """
195
193
  try:
196
194
  security_config = self.config.get("security", {})
197
-
195
+
198
196
  # Validate security configuration
199
197
  if not SecurityFactory.validate_config(self.config):
200
198
  logger.error("Security configuration validation failed")
201
199
  return False
202
-
200
+
203
201
  # Validate middleware-specific configurations
204
202
  if security_config.get("enabled", True):
205
203
  # Check required fields for security middleware
@@ -207,37 +205,37 @@ class MiddlewareFactory:
207
205
  if not isinstance(auth_config, dict):
208
206
  logger.error("Auth configuration must be a dictionary")
209
207
  return False
210
-
208
+
211
209
  ssl_config = security_config.get("ssl", {})
212
210
  if not isinstance(ssl_config, dict):
213
211
  logger.error("SSL configuration must be a dictionary")
214
212
  return False
215
-
213
+
216
214
  logger.info("Middleware configuration validation passed")
217
215
  return True
218
-
216
+
219
217
  except Exception as e:
220
218
  logger.error(f"Middleware configuration validation failed: {e}")
221
219
  return False
222
-
220
+
223
221
  def get_middleware_info(self) -> Dict[str, Any]:
224
222
  """
225
223
  Get information about created middleware.
226
-
224
+
227
225
  Returns:
228
226
  Dictionary with middleware information
229
227
  """
230
228
  info = {
231
229
  "total_middleware": len(self.middleware_stack),
232
230
  "middleware_types": [],
233
- "security_enabled": False
231
+ "security_enabled": False,
234
232
  }
235
-
233
+
236
234
  for middleware in self.middleware_stack:
237
235
  middleware_type = type(middleware).__name__
238
236
  info["middleware_types"].append(middleware_type)
239
-
237
+
240
238
  if isinstance(middleware, UnifiedSecurityMiddleware):
241
239
  info["security_enabled"] = True
242
-
240
+
243
241
  return info
@@ -12,56 +12,59 @@ from fastapi import Request, Response
12
12
  from mcp_proxy_adapter.core.logging import logger, RequestLogger
13
13
  from .base import BaseMiddleware
14
14
 
15
+
15
16
  class LoggingMiddleware(BaseMiddleware):
16
17
  """
17
18
  Middleware for logging requests and responses.
18
19
  """
19
-
20
+
20
21
  def __init__(self, app, config: Dict[str, Any] = None):
21
22
  """
22
23
  Initialize logging middleware.
23
-
24
+
24
25
  Args:
25
26
  app: FastAPI application
26
27
  config: Application configuration (optional)
27
28
  """
28
29
  super().__init__(app)
29
30
  self.config = config or {}
30
-
31
- async def dispatch(self, request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response:
31
+
32
+ async def dispatch(
33
+ self, request: Request, call_next: Callable[[Request], Awaitable[Response]]
34
+ ) -> Response:
32
35
  """
33
36
  Processes request and logs information about it.
34
-
37
+
35
38
  Args:
36
39
  request: Request.
37
40
  call_next: Next handler.
38
-
41
+
39
42
  Returns:
40
43
  Response.
41
44
  """
42
45
  # Generate unique ID for request
43
46
  request_id = str(uuid.uuid4())
44
47
  request.state.request_id = request_id
45
-
48
+
46
49
  # Create context logger for this request
47
50
  req_logger = RequestLogger("mcp_proxy_adapter.api.middleware", request_id)
48
-
51
+
49
52
  # Log request start
50
53
  start_time = time.time()
51
-
54
+
52
55
  # Request information
53
56
  method = request.method
54
57
  url = str(request.url)
55
58
  client_host = request.client.host if request.client else "unknown"
56
-
59
+
57
60
  # Check if this is an OpenAPI schema request (should be logged at DEBUG level)
58
61
  is_openapi_request = "/openapi.json" in url
59
-
62
+
60
63
  if is_openapi_request:
61
64
  req_logger.debug(f"Request started: {method} {url} | Client: {client_host}")
62
65
  else:
63
66
  req_logger.info(f"Request started: {method} {url} | Client: {client_host}")
64
-
67
+
65
68
  # Log request body if not GET or HEAD
66
69
  if method not in ["GET", "HEAD"]:
67
70
  try:
@@ -75,10 +78,15 @@ class LoggingMiddleware(BaseMiddleware):
75
78
  if isinstance(body_json, dict) and "params" in body_json:
76
79
  # Replace sensitive fields with "***"
77
80
  if isinstance(body_json["params"], dict):
78
- for sensitive_field in ["password", "token", "secret", "api_key"]:
81
+ for sensitive_field in [
82
+ "password",
83
+ "token",
84
+ "secret",
85
+ "api_key",
86
+ ]:
79
87
  if sensitive_field in body_json["params"]:
80
88
  body_json["params"][sensitive_field] = "***"
81
-
89
+
82
90
  req_logger.debug(f"Request body: {json.dumps(body_json)}")
83
91
  except json.JSONDecodeError:
84
92
  # If not JSON, log as is
@@ -87,36 +95,44 @@ class LoggingMiddleware(BaseMiddleware):
87
95
  req_logger.warning(f"Error logging request body: {str(e)}")
88
96
  except Exception as e:
89
97
  req_logger.warning(f"Error reading request body: {str(e)}")
90
-
98
+
91
99
  # Call the next middleware or main handler
92
100
  try:
93
101
  response = await call_next(request)
94
-
102
+
95
103
  # Log request completion
96
104
  process_time = time.time() - start_time
97
105
  status_code = response.status_code
98
-
106
+
99
107
  if is_openapi_request:
100
- req_logger.debug(f"Request completed: {method} {url} | Status: {status_code} | "
101
- f"Time: {process_time:.3f}s")
108
+ req_logger.debug(
109
+ f"Request completed: {method} {url} | Status: {status_code} | "
110
+ f"Time: {process_time:.3f}s"
111
+ )
102
112
  else:
103
- req_logger.info(f"Request completed: {method} {url} | Status: {status_code} | "
104
- f"Time: {process_time:.3f}s")
105
-
113
+ req_logger.info(
114
+ f"Request completed: {method} {url} | Status: {status_code} | "
115
+ f"Time: {process_time:.3f}s"
116
+ )
117
+
106
118
  # Add request ID to response headers
107
119
  response.headers["X-Request-ID"] = request_id
108
120
  response.headers["X-Process-Time"] = f"{process_time:.3f}s"
109
-
121
+
110
122
  return response
111
123
  except Exception as e:
112
124
  # Log error
113
125
  process_time = time.time() - start_time
114
-
126
+
115
127
  if is_openapi_request:
116
- req_logger.debug(f"Request failed: {method} {url} | Error: {str(e)} | "
117
- f"Time: {process_time:.3f}s")
128
+ req_logger.debug(
129
+ f"Request failed: {method} {url} | Error: {str(e)} | "
130
+ f"Time: {process_time:.3f}s"
131
+ )
118
132
  else:
119
- req_logger.error(f"Request failed: {method} {url} | Error: {str(e)} | "
120
- f"Time: {process_time:.3f}s")
121
-
122
- raise
133
+ req_logger.error(
134
+ f"Request failed: {method} {url} | Error: {str(e)} | "
135
+ f"Time: {process_time:.3f}s"
136
+ )
137
+
138
+ raise