mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.5__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.
- mcp_proxy_adapter/__init__.py +9 -5
- mcp_proxy_adapter/__main__.py +1 -1
- mcp_proxy_adapter/api/app.py +227 -176
- mcp_proxy_adapter/api/handlers.py +68 -60
- mcp_proxy_adapter/api/middleware/__init__.py +7 -5
- mcp_proxy_adapter/api/middleware/base.py +19 -16
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +44 -34
- mcp_proxy_adapter/api/middleware/error_handling.py +57 -67
- mcp_proxy_adapter/api/middleware/factory.py +50 -52
- mcp_proxy_adapter/api/middleware/logging.py +46 -30
- mcp_proxy_adapter/api/middleware/performance.py +19 -16
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +80 -50
- mcp_proxy_adapter/api/middleware/transport_middleware.py +26 -24
- mcp_proxy_adapter/api/middleware/unified_security.py +70 -51
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +43 -34
- mcp_proxy_adapter/api/schemas.py +69 -43
- mcp_proxy_adapter/api/tool_integration.py +83 -63
- mcp_proxy_adapter/api/tools.py +60 -50
- mcp_proxy_adapter/commands/__init__.py +15 -6
- mcp_proxy_adapter/commands/auth_validation_command.py +107 -110
- mcp_proxy_adapter/commands/base.py +108 -112
- mcp_proxy_adapter/commands/builtin_commands.py +28 -18
- mcp_proxy_adapter/commands/catalog_manager.py +394 -265
- mcp_proxy_adapter/commands/cert_monitor_command.py +222 -204
- mcp_proxy_adapter/commands/certificate_management_command.py +210 -213
- mcp_proxy_adapter/commands/command_registry.py +275 -226
- mcp_proxy_adapter/commands/config_command.py +48 -33
- mcp_proxy_adapter/commands/dependency_container.py +22 -23
- mcp_proxy_adapter/commands/dependency_manager.py +65 -56
- mcp_proxy_adapter/commands/echo_command.py +15 -15
- mcp_proxy_adapter/commands/health_command.py +31 -29
- mcp_proxy_adapter/commands/help_command.py +97 -61
- mcp_proxy_adapter/commands/hooks.py +65 -49
- mcp_proxy_adapter/commands/key_management_command.py +148 -147
- mcp_proxy_adapter/commands/load_command.py +58 -40
- mcp_proxy_adapter/commands/plugins_command.py +80 -54
- mcp_proxy_adapter/commands/protocol_management_command.py +60 -48
- mcp_proxy_adapter/commands/proxy_registration_command.py +107 -115
- mcp_proxy_adapter/commands/reload_command.py +43 -37
- mcp_proxy_adapter/commands/result.py +26 -33
- mcp_proxy_adapter/commands/role_test_command.py +26 -26
- mcp_proxy_adapter/commands/roles_management_command.py +176 -173
- mcp_proxy_adapter/commands/security_command.py +134 -122
- mcp_proxy_adapter/commands/settings_command.py +47 -56
- mcp_proxy_adapter/commands/ssl_setup_command.py +109 -129
- mcp_proxy_adapter/commands/token_management_command.py +129 -158
- mcp_proxy_adapter/commands/transport_management_command.py +41 -36
- mcp_proxy_adapter/commands/unload_command.py +42 -37
- mcp_proxy_adapter/config.py +36 -35
- mcp_proxy_adapter/core/__init__.py +19 -21
- mcp_proxy_adapter/core/app_factory.py +30 -9
- mcp_proxy_adapter/core/app_runner.py +81 -64
- mcp_proxy_adapter/core/auth_validator.py +176 -182
- mcp_proxy_adapter/core/certificate_utils.py +469 -426
- mcp_proxy_adapter/core/client.py +155 -126
- mcp_proxy_adapter/core/client_manager.py +60 -54
- mcp_proxy_adapter/core/client_security.py +108 -88
- mcp_proxy_adapter/core/config_converter.py +176 -143
- mcp_proxy_adapter/core/config_validator.py +12 -4
- mcp_proxy_adapter/core/crl_utils.py +21 -7
- mcp_proxy_adapter/core/errors.py +64 -20
- mcp_proxy_adapter/core/logging.py +34 -29
- mcp_proxy_adapter/core/mtls_asgi.py +29 -25
- mcp_proxy_adapter/core/mtls_asgi_app.py +66 -54
- mcp_proxy_adapter/core/protocol_manager.py +154 -104
- mcp_proxy_adapter/core/proxy_client.py +202 -144
- mcp_proxy_adapter/core/proxy_registration.py +7 -3
- mcp_proxy_adapter/core/role_utils.py +139 -125
- mcp_proxy_adapter/core/security_adapter.py +88 -77
- mcp_proxy_adapter/core/security_factory.py +50 -44
- mcp_proxy_adapter/core/security_integration.py +72 -24
- mcp_proxy_adapter/core/server_adapter.py +68 -64
- mcp_proxy_adapter/core/server_engine.py +71 -53
- mcp_proxy_adapter/core/settings.py +68 -58
- mcp_proxy_adapter/core/ssl_utils.py +69 -56
- mcp_proxy_adapter/core/transport_manager.py +72 -60
- mcp_proxy_adapter/core/unified_config_adapter.py +201 -150
- mcp_proxy_adapter/core/utils.py +4 -2
- mcp_proxy_adapter/custom_openapi.py +107 -99
- mcp_proxy_adapter/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/commands/__init__.py +1 -1
- mcp_proxy_adapter/examples/create_certificates_simple.py +182 -71
- mcp_proxy_adapter/examples/debug_request_state.py +38 -19
- mcp_proxy_adapter/examples/debug_role_chain.py +53 -20
- mcp_proxy_adapter/examples/demo_client.py +48 -36
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/generate_all_certificates.py +198 -73
- mcp_proxy_adapter/examples/generate_certificates.py +31 -16
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/generate_test_configs.py +68 -91
- mcp_proxy_adapter/examples/proxy_registration_example.py +76 -75
- mcp_proxy_adapter/examples/run_example.py +23 -5
- mcp_proxy_adapter/examples/run_full_test_suite.py +109 -71
- mcp_proxy_adapter/examples/run_proxy_server.py +22 -9
- mcp_proxy_adapter/examples/run_security_tests.py +103 -41
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +72 -36
- mcp_proxy_adapter/examples/scripts/config_generator.py +288 -187
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +185 -72
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/security_test_client.py +196 -127
- mcp_proxy_adapter/examples/setup_test_environment.py +17 -29
- mcp_proxy_adapter/examples/test_config.py +19 -4
- mcp_proxy_adapter/examples/test_config_generator.py +23 -7
- mcp_proxy_adapter/examples/test_examples.py +84 -56
- mcp_proxy_adapter/examples/universal_client.py +119 -62
- mcp_proxy_adapter/openapi.py +108 -115
- mcp_proxy_adapter/utils/config_generator.py +429 -274
- mcp_proxy_adapter/version.py +1 -2
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-6.3.5.dist-info/RECORD +143 -0
- mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/top_level.txt +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
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
150
|
-
|
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
|
-
|
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(
|
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 [
|
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(
|
101
|
-
|
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(
|
104
|
-
|
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(
|
117
|
-
|
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(
|
120
|
-
|
121
|
-
|
122
|
-
|
133
|
+
req_logger.error(
|
134
|
+
f"Request failed: {method} {url} | Error: {str(e)} | "
|
135
|
+
f"Time: {process_time:.3f}s"
|
136
|
+
)
|
137
|
+
|
138
|
+
raise
|