mcp-proxy-adapter 6.9.28__py3-none-any.whl → 6.9.30__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 (212) hide show
  1. mcp_proxy_adapter/__init__.py +10 -0
  2. mcp_proxy_adapter/__main__.py +8 -21
  3. mcp_proxy_adapter/api/app.py +10 -913
  4. mcp_proxy_adapter/api/core/__init__.py +18 -0
  5. mcp_proxy_adapter/api/core/app_factory.py +243 -0
  6. mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
  7. mcp_proxy_adapter/api/core/registration_manager.py +166 -0
  8. mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
  9. mcp_proxy_adapter/api/handlers.py +78 -199
  10. mcp_proxy_adapter/api/middleware/__init__.py +1 -44
  11. mcp_proxy_adapter/api/middleware/base.py +0 -42
  12. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +0 -85
  13. mcp_proxy_adapter/api/middleware/error_handling.py +1 -127
  14. mcp_proxy_adapter/api/middleware/factory.py +0 -94
  15. mcp_proxy_adapter/api/middleware/logging.py +0 -112
  16. mcp_proxy_adapter/api/middleware/performance.py +0 -35
  17. mcp_proxy_adapter/api/middleware/protocol_middleware.py +2 -98
  18. mcp_proxy_adapter/api/middleware/transport_middleware.py +0 -37
  19. mcp_proxy_adapter/api/middleware/unified_security.py +10 -10
  20. mcp_proxy_adapter/api/middleware/user_info_middleware.py +0 -118
  21. mcp_proxy_adapter/api/openapi/__init__.py +21 -0
  22. mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
  23. mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
  24. mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
  25. mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
  26. mcp_proxy_adapter/api/schemas.py +0 -61
  27. mcp_proxy_adapter/api/tool_integration.py +0 -117
  28. mcp_proxy_adapter/api/tools.py +0 -46
  29. mcp_proxy_adapter/cli/__init__.py +12 -0
  30. mcp_proxy_adapter/cli/commands/__init__.py +15 -0
  31. mcp_proxy_adapter/cli/commands/client.py +100 -0
  32. mcp_proxy_adapter/cli/commands/config_generate.py +21 -0
  33. mcp_proxy_adapter/cli/commands/config_validate.py +36 -0
  34. mcp_proxy_adapter/cli/commands/generate.py +259 -0
  35. mcp_proxy_adapter/cli/commands/server.py +174 -0
  36. mcp_proxy_adapter/cli/commands/sets.py +128 -0
  37. mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
  38. mcp_proxy_adapter/cli/examples/__init__.py +8 -0
  39. mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
  40. mcp_proxy_adapter/cli/examples/https_token.py +96 -0
  41. mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
  42. mcp_proxy_adapter/cli/main.py +63 -0
  43. mcp_proxy_adapter/cli/parser.py +324 -0
  44. mcp_proxy_adapter/cli/validators.py +231 -0
  45. mcp_proxy_adapter/client/jsonrpc_client.py +406 -0
  46. mcp_proxy_adapter/client/proxy.py +45 -0
  47. mcp_proxy_adapter/commands/__init__.py +44 -28
  48. mcp_proxy_adapter/commands/auth_validation_command.py +7 -344
  49. mcp_proxy_adapter/commands/base.py +19 -43
  50. mcp_proxy_adapter/commands/builtin_commands.py +0 -75
  51. mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
  52. mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
  53. mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
  54. mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
  55. mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
  56. mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
  57. mcp_proxy_adapter/commands/catalog_manager.py +58 -928
  58. mcp_proxy_adapter/commands/cert_monitor_command.py +0 -88
  59. mcp_proxy_adapter/commands/certificate_management_command.py +0 -45
  60. mcp_proxy_adapter/commands/command_registry.py +172 -904
  61. mcp_proxy_adapter/commands/config_command.py +0 -28
  62. mcp_proxy_adapter/commands/dependency_container.py +1 -70
  63. mcp_proxy_adapter/commands/dependency_manager.py +0 -128
  64. mcp_proxy_adapter/commands/echo_command.py +0 -34
  65. mcp_proxy_adapter/commands/health_command.py +0 -3
  66. mcp_proxy_adapter/commands/help_command.py +0 -159
  67. mcp_proxy_adapter/commands/hooks.py +0 -137
  68. mcp_proxy_adapter/commands/key_management_command.py +0 -25
  69. mcp_proxy_adapter/commands/load_command.py +7 -78
  70. mcp_proxy_adapter/commands/plugins_command.py +0 -16
  71. mcp_proxy_adapter/commands/protocol_management_command.py +0 -28
  72. mcp_proxy_adapter/commands/proxy_registration_command.py +0 -88
  73. mcp_proxy_adapter/commands/queue_commands.py +750 -0
  74. mcp_proxy_adapter/commands/registration_status_command.py +0 -43
  75. mcp_proxy_adapter/commands/registry/__init__.py +18 -0
  76. mcp_proxy_adapter/commands/registry/command_info.py +103 -0
  77. mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
  78. mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
  79. mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
  80. mcp_proxy_adapter/commands/reload_command.py +0 -80
  81. mcp_proxy_adapter/commands/result.py +25 -77
  82. mcp_proxy_adapter/commands/role_test_command.py +0 -44
  83. mcp_proxy_adapter/commands/roles_management_command.py +0 -199
  84. mcp_proxy_adapter/commands/security_command.py +0 -30
  85. mcp_proxy_adapter/commands/settings_command.py +0 -68
  86. mcp_proxy_adapter/commands/ssl_setup_command.py +0 -42
  87. mcp_proxy_adapter/commands/token_management_command.py +0 -1
  88. mcp_proxy_adapter/commands/transport_management_command.py +0 -20
  89. mcp_proxy_adapter/commands/unload_command.py +0 -71
  90. mcp_proxy_adapter/config.py +15 -626
  91. mcp_proxy_adapter/core/__init__.py +5 -39
  92. mcp_proxy_adapter/core/app_factory.py +14 -36
  93. mcp_proxy_adapter/core/app_runner.py +0 -27
  94. mcp_proxy_adapter/core/auth_validator.py +1 -93
  95. mcp_proxy_adapter/core/certificate/__init__.py +20 -0
  96. mcp_proxy_adapter/core/certificate/certificate_creator.py +371 -0
  97. mcp_proxy_adapter/core/certificate/certificate_extractor.py +183 -0
  98. mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
  99. mcp_proxy_adapter/core/certificate/certificate_validator.py +110 -0
  100. mcp_proxy_adapter/core/certificate/ssl_context_manager.py +70 -0
  101. mcp_proxy_adapter/core/certificate_utils.py +64 -903
  102. mcp_proxy_adapter/core/client.py +10 -9
  103. mcp_proxy_adapter/core/client_manager.py +0 -19
  104. mcp_proxy_adapter/core/client_security.py +0 -2
  105. mcp_proxy_adapter/core/config/__init__.py +18 -0
  106. mcp_proxy_adapter/core/config/config.py +195 -0
  107. mcp_proxy_adapter/core/config/config_factory.py +22 -0
  108. mcp_proxy_adapter/core/config/config_loader.py +66 -0
  109. mcp_proxy_adapter/core/config/feature_manager.py +31 -0
  110. mcp_proxy_adapter/core/config/simple_config.py +112 -0
  111. mcp_proxy_adapter/core/config/simple_config_generator.py +50 -0
  112. mcp_proxy_adapter/core/config/simple_config_validator.py +96 -0
  113. mcp_proxy_adapter/core/config_converter.py +0 -186
  114. mcp_proxy_adapter/core/config_validator.py +96 -1238
  115. mcp_proxy_adapter/core/errors.py +7 -42
  116. mcp_proxy_adapter/core/job_manager.py +54 -0
  117. mcp_proxy_adapter/core/logging.py +2 -22
  118. mcp_proxy_adapter/core/mtls_asgi.py +0 -20
  119. mcp_proxy_adapter/core/mtls_asgi_app.py +0 -12
  120. mcp_proxy_adapter/core/mtls_proxy.py +0 -80
  121. mcp_proxy_adapter/core/mtls_server.py +3 -173
  122. mcp_proxy_adapter/core/protocol_manager.py +1 -191
  123. mcp_proxy_adapter/core/proxy/__init__.py +22 -0
  124. mcp_proxy_adapter/core/proxy/auth_manager.py +27 -0
  125. mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +137 -0
  126. mcp_proxy_adapter/core/proxy/registration_client.py +60 -0
  127. mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
  128. mcp_proxy_adapter/core/proxy_client.py +0 -1
  129. mcp_proxy_adapter/core/proxy_registration.py +36 -913
  130. mcp_proxy_adapter/core/role_utils.py +0 -308
  131. mcp_proxy_adapter/core/security_adapter.py +1 -36
  132. mcp_proxy_adapter/core/security_factory.py +1 -150
  133. mcp_proxy_adapter/core/security_integration.py +0 -33
  134. mcp_proxy_adapter/core/server_adapter.py +1 -40
  135. mcp_proxy_adapter/core/server_engine.py +2 -173
  136. mcp_proxy_adapter/core/settings.py +0 -127
  137. mcp_proxy_adapter/core/signal_handler.py +0 -65
  138. mcp_proxy_adapter/core/ssl_utils.py +19 -137
  139. mcp_proxy_adapter/core/transport_manager.py +0 -151
  140. mcp_proxy_adapter/core/unified_config_adapter.py +1 -193
  141. mcp_proxy_adapter/core/utils.py +1 -182
  142. mcp_proxy_adapter/core/validation/__init__.py +21 -0
  143. mcp_proxy_adapter/core/validation/config_validator.py +211 -0
  144. mcp_proxy_adapter/core/validation/file_validator.py +73 -0
  145. mcp_proxy_adapter/core/validation/protocol_validator.py +191 -0
  146. mcp_proxy_adapter/core/validation/security_validator.py +58 -0
  147. mcp_proxy_adapter/core/validation/validation_result.py +27 -0
  148. mcp_proxy_adapter/custom_openapi.py +33 -652
  149. mcp_proxy_adapter/examples/bugfix_certificate_config.py +0 -23
  150. mcp_proxy_adapter/examples/check_config.py +0 -2
  151. mcp_proxy_adapter/examples/client_usage_example.py +164 -0
  152. mcp_proxy_adapter/examples/config_builder.py +13 -2
  153. mcp_proxy_adapter/examples/config_cli.py +0 -1
  154. mcp_proxy_adapter/examples/create_test_configs.py +0 -46
  155. mcp_proxy_adapter/examples/debug_request_state.py +0 -1
  156. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -47
  157. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -45
  158. mcp_proxy_adapter/examples/full_application/commands/echo_command.py +0 -12
  159. mcp_proxy_adapter/examples/full_application/commands/help_command.py +0 -12
  160. mcp_proxy_adapter/examples/full_application/commands/list_command.py +0 -7
  161. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +0 -2
  162. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -59
  163. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -54
  164. mcp_proxy_adapter/examples/full_application/main.py +186 -150
  165. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +0 -107
  166. mcp_proxy_adapter/examples/full_application/test_minimal_server.py +0 -24
  167. mcp_proxy_adapter/examples/full_application/test_server.py +0 -58
  168. mcp_proxy_adapter/examples/generate_config.py +65 -11
  169. mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
  170. mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
  171. mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
  172. mcp_proxy_adapter/examples/queue_server_example.py +85 -0
  173. mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
  174. mcp_proxy_adapter/examples/required_certificates.py +0 -2
  175. mcp_proxy_adapter/examples/run_full_test_suite.py +0 -29
  176. mcp_proxy_adapter/examples/run_proxy_server.py +31 -71
  177. mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -27
  178. mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
  179. mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
  180. mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
  181. mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
  182. mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
  183. mcp_proxy_adapter/examples/security_test_client.py +24 -1075
  184. mcp_proxy_adapter/examples/setup/__init__.py +24 -0
  185. mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
  186. mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
  187. mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
  188. mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
  189. mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
  190. mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
  191. mcp_proxy_adapter/examples/setup_test_environment.py +133 -1425
  192. mcp_proxy_adapter/examples/test_config.py +0 -3
  193. mcp_proxy_adapter/examples/test_config_builder.py +25 -405
  194. mcp_proxy_adapter/examples/test_examples.py +0 -1
  195. mcp_proxy_adapter/examples/test_framework_complete.py +0 -2
  196. mcp_proxy_adapter/examples/test_mcp_server.py +0 -1
  197. mcp_proxy_adapter/examples/test_protocol_examples.py +0 -1
  198. mcp_proxy_adapter/examples/universal_client.py +0 -6
  199. mcp_proxy_adapter/examples/update_config_certificates.py +0 -1
  200. mcp_proxy_adapter/examples/validate_generator_compatibility.py +0 -1
  201. mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +0 -187
  202. mcp_proxy_adapter/integrations/__init__.py +25 -0
  203. mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
  204. mcp_proxy_adapter/main.py +70 -62
  205. mcp_proxy_adapter/openapi.py +0 -22
  206. mcp_proxy_adapter/version.py +1 -1
  207. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/METADATA +2 -1
  208. mcp_proxy_adapter-6.9.30.dist-info/RECORD +235 -0
  209. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/entry_points.txt +1 -1
  210. mcp_proxy_adapter-6.9.28.dist-info/RECORD +0 -149
  211. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/WHEEL +0 -0
  212. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/top_level.txt +0 -0
@@ -1,27 +1,29 @@
1
1
  """
2
- Module with API request handlers.
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ HTTP handlers for the MCP Proxy Adapter API.
6
+ Provides JSON-RPC handling and health/commands endpoints.
3
7
  """
4
8
 
9
+ from __future__ import annotations
10
+
5
11
  import asyncio
6
- import json
7
12
  import time
8
- from typing import Any, Dict, List, Optional, Union
13
+ from typing import Any, Dict, List, Optional
9
14
 
10
- from fastapi import HTTPException, Request
11
- from fastapi.responses import JSONResponse
15
+ from fastapi import Request
12
16
 
13
17
  from mcp_proxy_adapter.commands.command_registry import registry
14
18
  from mcp_proxy_adapter.core.errors import (
15
19
  MicroserviceError,
16
20
  NotFoundError,
17
- ParseError,
18
21
  InvalidRequestError,
19
22
  MethodNotFoundError,
20
23
  InvalidParamsError,
21
24
  InternalError,
22
- CommandError,
23
25
  )
24
- from mcp_proxy_adapter.core.logging import get_global_logger, RequestLogger, get_logger
26
+ from mcp_proxy_adapter.core.logging import get_global_logger, RequestLogger
25
27
 
26
28
 
27
29
  async def execute_command(
@@ -30,113 +32,64 @@ async def execute_command(
30
32
  request_id: Optional[str] = None,
31
33
  request: Optional[Request] = None,
32
34
  ) -> Dict[str, Any]:
33
- """
34
- Executes a command with the specified name and parameters.
35
+ """Execute a registered command by name with parameters.
35
36
 
36
- Args:
37
- command_name: Command name.
38
- params: Command parameters.
39
- request_id: Optional request identifier for logging context.
40
-
41
- Returns:
42
- Command execution result.
43
-
44
- Raises:
45
- MethodNotFoundError: If command is not found.
46
- MicroserviceError: In case of command execution error.
37
+ Raises MethodNotFoundError if command is not found.
38
+ Wraps unexpected exceptions into InternalError.
47
39
  """
48
- # Create request get_global_logger() if request_id is provided
49
- log = RequestLogger(__name__, request_id) if request_id else get_global_logger()
40
+ logger = RequestLogger(__name__, request_id) if request_id else get_global_logger()
50
41
 
51
42
  try:
52
- log.info(f"Executing command: {command_name}")
43
+ logger.info(f"Executing command: {command_name}")
53
44
 
54
- # Execute before command hooks
45
+ # Resolve command
55
46
  try:
56
- from mcp_proxy_adapter.commands.hooks import hooks
57
-
58
- hooks.execute_before_command_hooks(command_name, params)
59
- log.debug(f"Executed before command hooks for: {command_name}")
60
- except Exception as e:
61
- log.warning(f"Failed to execute before command hooks: {e}")
62
-
63
- # Get command class from registry and execute with parameters
64
- start_time = time.time()
65
-
66
- # Use Command.run that handles instances with dependencies properly
67
- command_class = registry.get_command(command_name)
68
-
69
- # Create context with user info from request state
70
- context = {}
71
- if request and hasattr(request.state, "user_id"):
72
- context["user"] = {
73
- "id": getattr(request.state, "user_id", None),
74
- "role": getattr(request.state, "user_role", "guest"),
75
- "roles": getattr(request.state, "user_roles", ["guest"]),
76
- "permissions": getattr(request.state, "user_permissions", ["read"]),
77
- }
78
-
79
- # Add timeout to prevent hanging commands
47
+ command_class = registry.get_command(command_name)
48
+ except Exception:
49
+ raise MethodNotFoundError(f"Method not found: {command_name}")
50
+
51
+ # Build context (user info if middleware set state)
52
+ context: Dict[str, Any] = {}
53
+ if request is not None and hasattr(request, "state"):
54
+ user_id = getattr(request.state, "user_id", None)
55
+ user_role = getattr(request.state, "user_role", None)
56
+ user_roles = getattr(request.state, "user_roles", None)
57
+ if user_id or user_role or user_roles:
58
+ context["user"] = {
59
+ "id": user_id,
60
+ "role": user_role,
61
+ "roles": user_roles or [],
62
+ }
63
+
64
+ # Execute with timeout
65
+ started_at = time.time()
80
66
  try:
81
- result = await asyncio.wait_for(
82
- command_class.run(**params, context=context),
83
- timeout=10.0 # 10 seconds timeout
67
+ result_obj = await asyncio.wait_for(
68
+ command_class.run(**params, context=context), timeout=30.0
84
69
  )
85
70
  except asyncio.TimeoutError:
86
- execution_time = time.time() - start_time
87
- log.error(f"Command '{command_name}' timed out after {execution_time:.3f} sec")
88
- raise InternalError(f"Command execution timed out after 10 seconds")
71
+ elapsed = time.time() - started_at
72
+ raise InternalError(f"Command timed out after {elapsed:.2f}s")
89
73
 
90
- execution_time = time.time() - start_time
74
+ elapsed = time.time() - started_at
75
+ logger.info(f"Command '{command_name}' executed in {elapsed:.3f}s")
76
+ return result_obj.to_dict()
91
77
 
92
- log.info(f"Command '{command_name}' executed in {execution_time:.3f} sec")
93
-
94
- # Execute after command hooks
95
- try:
96
- hooks.execute_after_command_hooks(command_name, params, result)
97
- log.debug(f"Executed after command hooks for: {command_name}")
98
- except Exception as e:
99
- log.warning(f"Failed to execute after command hooks: {e}")
100
-
101
- # Return result
102
- return result.to_dict()
103
- except NotFoundError as e:
104
- log.error(f"Command not found: {command_name}")
105
- # Преобразуем в MethodNotFoundError для соответствия JSON-RPC
106
- raise MethodNotFoundError(f"Method not found: {command_name}")
107
- except Exception as e:
108
- log.exception(f"Error executing command '{command_name}': {e}")
109
- if isinstance(e, MicroserviceError):
110
- raise e
111
- # Все остальные ошибки оборачиваем в InternalError
112
- raise InternalError(
113
- f"Error executing command: {str(e)}", data={"original_error": str(e)}
114
- )
78
+ except MicroserviceError:
79
+ raise
80
+ except Exception as exc:
81
+ logger.exception(f"Unhandled error in command '{command_name}': {exc}")
82
+ raise InternalError("Internal error", data={"error": str(exc)})
115
83
 
116
84
 
117
85
  async def handle_batch_json_rpc(
118
86
  batch_requests: List[Dict[str, Any]], request: Optional[Request] = None
119
87
  ) -> List[Dict[str, Any]]:
120
- """
121
- Handles batch JSON-RPC requests.
122
-
123
- Args:
124
- batch_requests: List of JSON-RPC request data.
125
- request: Original FastAPI request object.
126
-
127
- Returns:
128
- List of JSON-RPC responses.
129
- """
130
- responses = []
131
-
132
- # Get request_id from request state if available
88
+ """Handle batch JSON-RPC requests."""
89
+ responses: List[Dict[str, Any]] = []
133
90
  request_id = getattr(request.state, "request_id", None) if request else None
134
-
135
- for request_data in batch_requests:
136
- # Process each request in the batch
137
- response = await handle_json_rpc(request_data, request_id, request)
138
- responses.append(response)
139
-
91
+ for item in batch_requests:
92
+ responses.append(await handle_json_rpc(item, request_id, request))
140
93
  return responses
141
94
 
142
95
 
@@ -145,120 +98,63 @@ async def handle_json_rpc(
145
98
  request_id: Optional[str] = None,
146
99
  request: Optional[Request] = None,
147
100
  ) -> Dict[str, Any]:
148
- """
149
- Handles JSON-RPC request with support for both standard JSON-RPC and simplified formats.
101
+ """Handle a single JSON-RPC request with strict 2.0 compatibility.
150
102
 
151
- Args:
152
- request_data: JSON-RPC request data.
153
- request_id: Optional request identifier for logging context.
154
-
155
- Returns:
156
- JSON-RPC response.
103
+ Also supports simplified form: {"command": "echo", "params": {...}}.
157
104
  """
158
- # Create request get_global_logger() if request_id is provided
159
- log = RequestLogger(__name__, request_id) if request_id else get_global_logger()
105
+ logger = RequestLogger(__name__, request_id) if request_id else get_global_logger()
160
106
 
161
- # Support both standard JSON-RPC and simplified formats
162
- method = None
163
- params = {}
164
- json_rpc_id = None
107
+ method: Optional[str]
108
+ params: Dict[str, Any]
109
+ json_rpc_id: Any
165
110
 
166
- # Check if it's a standard JSON-RPC request
167
111
  if "jsonrpc" in request_data:
168
- # Standard JSON-RPC format
169
112
  if request_data.get("jsonrpc") != "2.0":
170
- return _create_error_response(
171
- InvalidRequestError("Invalid Request. Expected jsonrpc: 2.0"),
172
- request_data.get("id"),
173
- )
174
-
113
+ return _error_response(InvalidRequestError("Invalid Request: jsonrpc must be '2.0'"), request_data.get("id"))
175
114
  method = request_data.get("method")
176
115
  params = request_data.get("params", {})
177
116
  json_rpc_id = request_data.get("id")
178
-
179
117
  if not method:
180
- return _create_error_response(
181
- InvalidRequestError("Invalid Request. Method is required"), json_rpc_id
182
- )
118
+ return _error_response(InvalidRequestError("Invalid Request: method is required"), json_rpc_id)
183
119
  else:
184
- # Simplified format: {"command": "help"} or {"command": "echo", "params": {...}}
120
+ # Simplified
185
121
  method = request_data.get("command")
186
122
  params = request_data.get("params", {})
187
- json_rpc_id = request_data.get("id", 1) # Default ID for simplified format
188
-
123
+ json_rpc_id = request_data.get("id", 1)
189
124
  if not method:
190
- return _create_error_response(
191
- InvalidRequestError("Invalid Request. Command is required"), json_rpc_id
192
- )
193
-
194
- log.info(f"Using simplified format for command: {method}")
195
-
196
- log.info(f"Executing JSON-RPC method: {method}")
125
+ return _error_response(InvalidRequestError("Invalid Request: command is required"), json_rpc_id)
197
126
 
198
127
  try:
199
- # Execute command with detailed logging
200
- log.info(f"🔍 Starting command execution: {method}")
201
- log.debug(f"📋 Command params: {params}")
202
-
203
128
  result = await execute_command(method, params, request_id, request)
204
-
205
- log.info(f"✅ Command {method} completed successfully")
206
-
207
- # Form successful response
208
129
  return {"jsonrpc": "2.0", "result": result, "id": json_rpc_id}
209
- except MicroserviceError as e:
210
- # Method execution error
211
- log.error(f"❌ Method execution error: {str(e)}")
212
- log.error(f"📊 Error type: {type(e).__name__}")
213
- return _create_error_response(e, json_rpc_id)
214
- except Exception as e:
215
- # Internal server error
216
- log.exception(f"❌ Unhandled error in JSON-RPC handler: {e}")
217
- log.error(f"📊 Exception type: {type(e).__name__}")
218
- log.error(f"📊 Exception details: {repr(e)}")
219
- return _create_error_response(
220
- InternalError("Internal error", data={"error": str(e), "error_type": type(e).__name__}), json_rpc_id
221
- )
130
+ except MicroserviceError as err:
131
+ return _error_response(err, json_rpc_id)
132
+ except Exception as exc:
133
+ logger.exception(f"Unhandled error in JSON-RPC handler: {exc}")
134
+ return _error_response(InternalError("Internal error", data={"error": str(exc)}), json_rpc_id)
222
135
 
223
136
 
224
- def _create_error_response(error: MicroserviceError, request_id: Any) -> Dict[str, Any]:
225
- """
226
- Creates JSON-RPC error response.
227
-
228
- Args:
229
- error: Error object.
230
- request_id: Request ID from client.
231
-
232
- Returns:
233
- JSON-RPC error response dictionary.
234
- """
137
+ def _error_response(error: MicroserviceError, request_id: Any) -> Dict[str, Any]:
235
138
  return {"jsonrpc": "2.0", "error": error.to_dict(), "id": request_id}
236
139
 
237
140
 
238
141
  async def get_server_health() -> Dict[str, Any]:
239
- """
240
- Gets server health information.
241
-
242
- Returns:
243
- Dictionary with server health information.
244
- """
142
+ """Return server health info."""
245
143
  import os
246
144
  import platform
247
145
  import sys
248
146
  import psutil
249
147
  from datetime import datetime
250
148
 
251
- # Get process start time
252
149
  process = psutil.Process(os.getpid())
253
150
  start_time = datetime.fromtimestamp(process.create_time())
254
151
  uptime_seconds = (datetime.now() - start_time).total_seconds()
255
-
256
- # Get system information
257
- memory_info = process.memory_info()
152
+ mem = process.memory_info().rss / (1024 * 1024)
258
153
 
259
154
  return {
260
155
  "status": "ok",
261
- "version": "1.0.0", # Should be replaced with actual version
156
+ "model": "mcp-proxy-adapter",
157
+ "version": "1.0.0",
262
158
  "uptime": uptime_seconds,
263
159
  "components": {
264
160
  "system": {
@@ -268,7 +164,7 @@ async def get_server_health() -> Dict[str, Any]:
268
164
  },
269
165
  "process": {
270
166
  "pid": os.getpid(),
271
- "memory_usage_mb": memory_info.rss / (1024 * 1024),
167
+ "memory_usage_mb": mem,
272
168
  "start_time": start_time.isoformat(),
273
169
  },
274
170
  "commands": {"registered_count": len(registry.get_all_commands())},
@@ -277,26 +173,9 @@ async def get_server_health() -> Dict[str, Any]:
277
173
 
278
174
 
279
175
  async def get_commands_list() -> Dict[str, Dict[str, Any]]:
280
- """
281
- Gets list of available commands.
282
-
283
- Returns:
284
- Dictionary with information about available commands.
285
- """
286
- result = {}
287
-
288
- # Get all registered commands
289
- all_commands = registry.get_all_commands()
290
-
291
- for command_name, command_class in all_commands.items():
292
- # Get schema information for the command
293
- schema = command_class.get_schema()
294
-
295
- # Add to result
296
- result[command_name] = {
297
- "name": command_name,
298
- "schema": schema,
299
- "description": schema.get("description", ""),
300
- }
301
-
176
+ """Return list of registered commands with schemas."""
177
+ result: Dict[str, Dict[str, Any]] = {}
178
+ for name, cls in registry.get_all_commands().items():
179
+ schema = cls.get_schema()
180
+ result[name] = {"name": name, "schema": schema, "description": schema.get("description", "")}
302
181
  return result
@@ -8,9 +8,8 @@ from fastapi import FastAPI
8
8
 
9
9
  from mcp_proxy_adapter.core.logging import get_global_logger
10
10
  from mcp_proxy_adapter.config import config
11
- from .base import BaseMiddleware
12
11
  from .factory import MiddlewareFactory
13
- from .protocol_middleware import setup_protocol_middleware
12
+ # from .protocol_middleware import setup_protocol_middleware
14
13
 
15
14
  # Export mcp_security_framework availability
16
15
  try:
@@ -20,45 +19,3 @@ except ImportError:
20
19
  mcp_security_framework = False
21
20
 
22
21
 
23
- def setup_middleware(app: FastAPI, app_config: Optional[Dict[str, Any]] = None) -> None:
24
- """
25
- Sets up middleware for application using the new middleware factory.
26
-
27
- Args:
28
- app: FastAPI application instance.
29
- app_config: Application configuration dictionary (optional)
30
- """
31
- # Use provided configuration or fallback to global config
32
- current_config = app_config if app_config is not None else config.get_all()
33
-
34
- # Add protocol middleware FIRST (before other middleware)
35
- setup_protocol_middleware(app, current_config)
36
-
37
- # Create middleware factory
38
- factory = MiddlewareFactory(app, current_config)
39
-
40
- # Validate middleware configuration
41
- if not factory.validate_middleware_config():
42
- get_global_logger().error("Middleware configuration validation failed")
43
- raise SystemExit(1)
44
-
45
- get_global_logger().info("Using unified security middleware")
46
- middleware_list = factory.create_all_middleware()
47
-
48
- # Add middleware to application AFTER protocol middleware
49
- for middleware in middleware_list:
50
- # For ASGI middleware, we need to wrap the application
51
- if hasattr(middleware, "dispatch"):
52
- # This is a proper ASGI middleware
53
- app.middleware("http")(middleware.dispatch)
54
- else:
55
- get_global_logger().warning(
56
- f"Middleware {middleware.__class__.__name__} doesn't have dispatch method"
57
- )
58
-
59
- # Log middleware information
60
- middleware_info = factory.get_middleware_info()
61
- get_global_logger().info(f"Middleware setup completed:")
62
- get_global_logger().info(f" - Total middleware: {middleware_info['total_middleware']}")
63
- get_global_logger().info(f" - Types: {', '.join(middleware_info['middleware_types'])}")
64
- get_global_logger().info(f" - Security enabled: {middleware_info['security_enabled']}")
@@ -16,48 +16,6 @@ class BaseMiddleware(BaseHTTPMiddleware):
16
16
  Base class for all middleware.
17
17
  """
18
18
 
19
- async def dispatch(
20
- self, request: Request, call_next: Callable[[Request], Awaitable[Response]]
21
- ) -> Response:
22
- """
23
- Method that will be overridden in child classes.
24
-
25
- Args:
26
- request: Request.
27
- call_next: Next handler.
28
-
29
- Returns:
30
- Response.
31
- """
32
- middleware_name = self.__class__.__name__
33
- get_global_logger().info(f"🔍 STEP 1: {middleware_name}.dispatch START - {request.method} {request.url.path}")
34
- get_global_logger().info(f"🔍 STEP 1.1: {middleware_name} - Request headers: {dict(request.headers)}")
35
- get_global_logger().info(f"🔍 STEP 1.2: {middleware_name} - Request URL: {request.url}")
36
- get_global_logger().info(f"🔍 STEP 1.3: {middleware_name} - Request scope: {request.scope}")
37
-
38
- try:
39
- # Process request before calling the main handler
40
- get_global_logger().info(f"🔍 STEP 2: {middleware_name}.before_request START")
41
- await self.before_request(request)
42
- get_global_logger().info(f"🔍 STEP 3: {middleware_name}.before_request COMPLETED")
43
-
44
- # Call the next middleware or main handler
45
- get_global_logger().info(f"🔍 STEP 4: {middleware_name}.call_next START - About to call next middleware/endpoint")
46
- response = await call_next(request)
47
- get_global_logger().info(f"🔍 STEP 5: {middleware_name}.call_next COMPLETED - Status: {response.status_code}")
48
- get_global_logger().info(f"🔍 STEP 5.1: {middleware_name} - Response headers: {dict(response.headers)}")
49
-
50
- # Process response after calling the main handler
51
- get_global_logger().info(f"🔍 STEP 6: {middleware_name}.after_response START")
52
- response = await self.after_response(request, response)
53
- get_global_logger().info(f"🔍 STEP 7: {middleware_name}.after_response COMPLETED")
54
-
55
- get_global_logger().info(f"🔍 STEP 8: {middleware_name}.dispatch COMPLETED SUCCESSFULLY")
56
- return response
57
- except Exception as e:
58
- get_global_logger().error(f"❌ STEP ERROR: {middleware_name}.dispatch ERROR: {str(e)}", exc_info=True)
59
- # If an error occurred, call the error handler
60
- return await self.handle_error(request, e)
61
19
 
62
20
  async def before_request(self, request: Request) -> None:
63
21
  """
@@ -9,7 +9,6 @@ email: vasilyvz@gmail.com
9
9
 
10
10
  import json
11
11
  import logging
12
- from typing import Dict, Any, Optional, Callable, Awaitable
13
12
  from fastapi import Request, Response
14
13
  from starlette.middleware.base import BaseHTTPMiddleware
15
14
 
@@ -47,90 +46,6 @@ class CommandPermissionMiddleware(BaseHTTPMiddleware):
47
46
 
48
47
  get_global_logger().info("Command permission middleware initialized")
49
48
 
50
- async def dispatch(
51
- self, request: Request, call_next: Callable[[Request], Awaitable[Response]]
52
- ) -> Response:
53
- """
54
- Process request and check command permissions.
55
-
56
- Args:
57
- request: Request object
58
- call_next: Next handler
59
-
60
- Returns:
61
- Response object
62
- """
63
- # Only check permissions for /cmd endpoint
64
- if request.url.path != "/cmd":
65
- return await call_next(request)
66
-
67
- try:
68
- # Get request body
69
- body = await request.body()
70
- if not body:
71
- return await call_next(request)
72
-
73
- # Parse JSON-RPC request
74
- try:
75
- data = json.loads(body)
76
- except json.JSONDecodeError:
77
- return await call_next(request)
78
-
79
- # Extract method (command name)
80
- method = data.get("method")
81
- if not method:
82
- return await call_next(request)
83
-
84
- # Check if method requires permissions
85
- if method not in self.command_permissions:
86
- return await call_next(request)
87
-
88
- required_permissions = self.command_permissions[method]
89
-
90
- # Get user info from request state
91
- user_info = getattr(request.state, "user", None)
92
- if not user_info:
93
- get_global_logger().warning(f"No user info found for command {method}")
94
- return await call_next(request)
95
-
96
- user_roles = user_info.get("roles", [])
97
- user_permissions = user_info.get("permissions", [])
98
-
99
- get_global_logger().debug(
100
- f"Checking permissions for {method}: user_roles={user_roles}, required={required_permissions}"
101
- )
102
-
103
- # Check if user has required permissions
104
- has_permission = self._check_permissions(
105
- user_roles, user_permissions, required_permissions
106
- )
107
-
108
- if not has_permission:
109
- get_global_logger().warning(
110
- f"Permission denied for {method}: user_roles={user_roles}, required={required_permissions}"
111
- )
112
-
113
- # Return permission denied response
114
- error_response = {
115
- "error": {
116
- "code": 403,
117
- "message": f"Permission denied: {method} requires {required_permissions}",
118
- "type": "permission_denied",
119
- }
120
- }
121
-
122
- return Response(
123
- content=json.dumps(error_response),
124
- status_code=403,
125
- media_type="application/json",
126
- )
127
-
128
- get_global_logger().debug(f"Permission granted for {method}")
129
- return await call_next(request)
130
-
131
- except Exception as e:
132
- get_global_logger().error(f"Error in command permission middleware: {e}")
133
- return await call_next(request)
134
49
 
135
50
  def _check_permissions(
136
51
  self, user_roles: list, user_permissions: list, required_permissions: list