mcp-proxy-adapter 6.9.43__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 +47 -0
- mcp_proxy_adapter/__main__.py +13 -0
- mcp_proxy_adapter/api/__init__.py +0 -0
- mcp_proxy_adapter/api/app.py +66 -0
- mcp_proxy_adapter/api/core/__init__.py +18 -0
- mcp_proxy_adapter/api/core/app_factory.py +355 -0
- mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
- mcp_proxy_adapter/api/core/registration_context.py +356 -0
- mcp_proxy_adapter/api/core/registration_manager.py +266 -0
- mcp_proxy_adapter/api/core/registration_tasks.py +84 -0
- mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
- mcp_proxy_adapter/api/handlers.py +181 -0
- mcp_proxy_adapter/api/middleware/__init__.py +21 -0
- mcp_proxy_adapter/api/middleware/base.py +54 -0
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +73 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +76 -0
- mcp_proxy_adapter/api/middleware/factory.py +147 -0
- mcp_proxy_adapter/api/middleware/logging.py +31 -0
- mcp_proxy_adapter/api/middleware/performance.py +51 -0
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +140 -0
- mcp_proxy_adapter/api/middleware/transport_middleware.py +87 -0
- mcp_proxy_adapter/api/middleware/unified_security.py +223 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +132 -0
- mcp_proxy_adapter/api/openapi/__init__.py +21 -0
- mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
- mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
- mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
- mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
- mcp_proxy_adapter/api/schemas.py +270 -0
- mcp_proxy_adapter/api/tool_integration.py +131 -0
- mcp_proxy_adapter/api/tools.py +163 -0
- mcp_proxy_adapter/cli/__init__.py +12 -0
- mcp_proxy_adapter/cli/commands/__init__.py +15 -0
- mcp_proxy_adapter/cli/commands/client.py +100 -0
- mcp_proxy_adapter/cli/commands/config_generate.py +35 -0
- mcp_proxy_adapter/cli/commands/config_validate.py +74 -0
- mcp_proxy_adapter/cli/commands/generate.py +259 -0
- mcp_proxy_adapter/cli/commands/server.py +174 -0
- mcp_proxy_adapter/cli/commands/sets.py +128 -0
- mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
- mcp_proxy_adapter/cli/examples/__init__.py +8 -0
- mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
- mcp_proxy_adapter/cli/examples/https_token.py +96 -0
- mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
- mcp_proxy_adapter/cli/main.py +63 -0
- mcp_proxy_adapter/cli/parser.py +338 -0
- mcp_proxy_adapter/cli/validators.py +231 -0
- mcp_proxy_adapter/client/jsonrpc_client/__init__.py +9 -0
- mcp_proxy_adapter/client/jsonrpc_client/client.py +42 -0
- mcp_proxy_adapter/client/jsonrpc_client/command_api.py +45 -0
- mcp_proxy_adapter/client/jsonrpc_client/proxy_api.py +224 -0
- mcp_proxy_adapter/client/jsonrpc_client/queue_api.py +60 -0
- mcp_proxy_adapter/client/jsonrpc_client/transport.py +108 -0
- mcp_proxy_adapter/client/proxy.py +123 -0
- mcp_proxy_adapter/commands/__init__.py +66 -0
- mcp_proxy_adapter/commands/auth_validation_command.py +69 -0
- mcp_proxy_adapter/commands/base.py +389 -0
- mcp_proxy_adapter/commands/builtin_commands.py +30 -0
- mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
- mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
- mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
- mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
- mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
- mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
- mcp_proxy_adapter/commands/catalog_manager.py +97 -0
- mcp_proxy_adapter/commands/cert_monitor_command.py +552 -0
- mcp_proxy_adapter/commands/certificate_management_command.py +562 -0
- mcp_proxy_adapter/commands/command_registry.py +298 -0
- mcp_proxy_adapter/commands/config_command.py +102 -0
- mcp_proxy_adapter/commands/dependency_container.py +40 -0
- mcp_proxy_adapter/commands/dependency_manager.py +143 -0
- mcp_proxy_adapter/commands/echo_command.py +48 -0
- mcp_proxy_adapter/commands/health_command.py +142 -0
- mcp_proxy_adapter/commands/help_command.py +175 -0
- mcp_proxy_adapter/commands/hooks.py +172 -0
- mcp_proxy_adapter/commands/key_management_command.py +484 -0
- mcp_proxy_adapter/commands/load_command.py +123 -0
- mcp_proxy_adapter/commands/plugins_command.py +246 -0
- mcp_proxy_adapter/commands/protocol_management_command.py +216 -0
- mcp_proxy_adapter/commands/proxy_registration_command.py +319 -0
- mcp_proxy_adapter/commands/queue_commands.py +750 -0
- mcp_proxy_adapter/commands/registration_status_command.py +76 -0
- mcp_proxy_adapter/commands/registry/__init__.py +18 -0
- mcp_proxy_adapter/commands/registry/command_info.py +103 -0
- mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
- mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
- mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
- mcp_proxy_adapter/commands/reload_command.py +136 -0
- mcp_proxy_adapter/commands/result.py +157 -0
- mcp_proxy_adapter/commands/role_test_command.py +99 -0
- mcp_proxy_adapter/commands/roles_management_command.py +502 -0
- mcp_proxy_adapter/commands/security_command.py +472 -0
- mcp_proxy_adapter/commands/settings_command.py +113 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +306 -0
- mcp_proxy_adapter/commands/token_management_command.py +500 -0
- mcp_proxy_adapter/commands/transport_management_command.py +129 -0
- mcp_proxy_adapter/commands/unload_command.py +92 -0
- mcp_proxy_adapter/config.py +32 -0
- mcp_proxy_adapter/core/__init__.py +8 -0
- mcp_proxy_adapter/core/app_factory.py +560 -0
- mcp_proxy_adapter/core/app_runner.py +318 -0
- mcp_proxy_adapter/core/auth_validator.py +508 -0
- mcp_proxy_adapter/core/certificate/__init__.py +20 -0
- mcp_proxy_adapter/core/certificate/certificate_creator.py +372 -0
- mcp_proxy_adapter/core/certificate/certificate_extractor.py +185 -0
- mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
- mcp_proxy_adapter/core/certificate/certificate_validator.py +388 -0
- mcp_proxy_adapter/core/certificate/ssl_context_manager.py +65 -0
- mcp_proxy_adapter/core/certificate_utils.py +249 -0
- mcp_proxy_adapter/core/client.py +608 -0
- mcp_proxy_adapter/core/client_manager.py +271 -0
- mcp_proxy_adapter/core/client_security.py +411 -0
- mcp_proxy_adapter/core/config/__init__.py +18 -0
- mcp_proxy_adapter/core/config/config.py +237 -0
- mcp_proxy_adapter/core/config/config_factory.py +22 -0
- mcp_proxy_adapter/core/config/config_loader.py +66 -0
- mcp_proxy_adapter/core/config/feature_manager.py +31 -0
- mcp_proxy_adapter/core/config/simple_config.py +116 -0
- mcp_proxy_adapter/core/config/simple_config_generator.py +100 -0
- mcp_proxy_adapter/core/config/simple_config_validator.py +380 -0
- mcp_proxy_adapter/core/config_converter.py +252 -0
- mcp_proxy_adapter/core/config_validator.py +211 -0
- mcp_proxy_adapter/core/crl_utils.py +362 -0
- mcp_proxy_adapter/core/errors.py +276 -0
- mcp_proxy_adapter/core/job_manager.py +54 -0
- mcp_proxy_adapter/core/logging.py +250 -0
- mcp_proxy_adapter/core/mtls_asgi.py +140 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/mtls_proxy.py +229 -0
- mcp_proxy_adapter/core/mtls_server.py +154 -0
- mcp_proxy_adapter/core/protocol_manager.py +232 -0
- mcp_proxy_adapter/core/proxy/__init__.py +19 -0
- mcp_proxy_adapter/core/proxy/auth_manager.py +26 -0
- mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +160 -0
- mcp_proxy_adapter/core/proxy/registration_client.py +186 -0
- mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
- mcp_proxy_adapter/core/proxy_client.py +184 -0
- mcp_proxy_adapter/core/proxy_registration.py +80 -0
- mcp_proxy_adapter/core/role_utils.py +103 -0
- mcp_proxy_adapter/core/security_adapter.py +343 -0
- mcp_proxy_adapter/core/security_factory.py +96 -0
- mcp_proxy_adapter/core/security_integration.py +342 -0
- mcp_proxy_adapter/core/server_adapter.py +251 -0
- mcp_proxy_adapter/core/server_engine.py +217 -0
- mcp_proxy_adapter/core/settings.py +260 -0
- mcp_proxy_adapter/core/signal_handler.py +107 -0
- mcp_proxy_adapter/core/ssl_utils.py +161 -0
- mcp_proxy_adapter/core/transport_manager.py +153 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +471 -0
- mcp_proxy_adapter/core/utils.py +101 -0
- mcp_proxy_adapter/core/validation/__init__.py +21 -0
- mcp_proxy_adapter/core/validation/config_validator.py +219 -0
- mcp_proxy_adapter/core/validation/file_validator.py +131 -0
- mcp_proxy_adapter/core/validation/protocol_validator.py +190 -0
- mcp_proxy_adapter/core/validation/security_validator.py +140 -0
- mcp_proxy_adapter/core/validation/validation_result.py +27 -0
- mcp_proxy_adapter/custom_openapi.py +58 -0
- mcp_proxy_adapter/examples/__init__.py +16 -0
- mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +52 -0
- mcp_proxy_adapter/examples/bugfix_certificate_config.py +261 -0
- mcp_proxy_adapter/examples/cert_manager_bugfix.py +203 -0
- mcp_proxy_adapter/examples/check_config.py +413 -0
- mcp_proxy_adapter/examples/client_usage_example.py +164 -0
- mcp_proxy_adapter/examples/commands/__init__.py +5 -0
- mcp_proxy_adapter/examples/config_builder.py +234 -0
- mcp_proxy_adapter/examples/config_cli.py +282 -0
- mcp_proxy_adapter/examples/create_test_configs.py +174 -0
- mcp_proxy_adapter/examples/debug_request_state.py +130 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +191 -0
- mcp_proxy_adapter/examples/demo_client.py +287 -0
- mcp_proxy_adapter/examples/full_application/__init__.py +13 -0
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +8 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +45 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +52 -0
- mcp_proxy_adapter/examples/full_application/commands/echo_command.py +32 -0
- mcp_proxy_adapter/examples/full_application/commands/help_command.py +54 -0
- mcp_proxy_adapter/examples/full_application/commands/list_command.py +57 -0
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +5 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +29 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +27 -0
- mcp_proxy_adapter/examples/full_application/main.py +264 -0
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +81 -0
- mcp_proxy_adapter/examples/full_application/run_mtls.py +252 -0
- mcp_proxy_adapter/examples/full_application/run_simple.py +152 -0
- mcp_proxy_adapter/examples/full_application/test_minimal_server.py +45 -0
- mcp_proxy_adapter/examples/full_application/test_server.py +163 -0
- mcp_proxy_adapter/examples/full_application/test_simple_server.py +62 -0
- mcp_proxy_adapter/examples/generate_config.py +502 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +335 -0
- mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
- mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
- mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
- mcp_proxy_adapter/examples/queue_server_example.py +85 -0
- mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
- mcp_proxy_adapter/examples/required_certificates.py +208 -0
- mcp_proxy_adapter/examples/run_example.py +77 -0
- mcp_proxy_adapter/examples/run_full_test_suite.py +619 -0
- mcp_proxy_adapter/examples/run_proxy_server.py +153 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +435 -0
- mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
- mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
- mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
- mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
- mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
- mcp_proxy_adapter/examples/security_test_client.py +72 -0
- mcp_proxy_adapter/examples/setup/__init__.py +24 -0
- mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
- mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
- mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
- mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
- mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
- mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
- mcp_proxy_adapter/examples/setup_test_environment.py +235 -0
- mcp_proxy_adapter/examples/simple_protocol_test.py +125 -0
- mcp_proxy_adapter/examples/test_chk_hostname_automated.py +211 -0
- mcp_proxy_adapter/examples/test_config.py +205 -0
- mcp_proxy_adapter/examples/test_config_builder.py +110 -0
- mcp_proxy_adapter/examples/test_examples.py +308 -0
- mcp_proxy_adapter/examples/test_framework_complete.py +267 -0
- mcp_proxy_adapter/examples/test_mcp_server.py +187 -0
- mcp_proxy_adapter/examples/test_protocol_examples.py +337 -0
- mcp_proxy_adapter/examples/universal_client.py +674 -0
- mcp_proxy_adapter/examples/update_config_certificates.py +135 -0
- mcp_proxy_adapter/examples/validate_generator_compatibility.py +385 -0
- mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +61 -0
- mcp_proxy_adapter/integrations/__init__.py +25 -0
- mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
- mcp_proxy_adapter/main.py +313 -0
- mcp_proxy_adapter/openapi.py +375 -0
- mcp_proxy_adapter/schemas/base_schema.json +114 -0
- mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
- mcp_proxy_adapter/schemas/roles.json +37 -0
- mcp_proxy_adapter/schemas/roles_schema.json +162 -0
- mcp_proxy_adapter/version.py +5 -0
- mcp_proxy_adapter-6.9.43.dist-info/METADATA +739 -0
- mcp_proxy_adapter-6.9.43.dist-info/RECORD +242 -0
- mcp_proxy_adapter-6.9.43.dist-info/WHEEL +5 -0
- mcp_proxy_adapter-6.9.43.dist-info/entry_points.txt +12 -0
- mcp_proxy_adapter-6.9.43.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for defining errors and exceptions for the microservice.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MicroserviceError(Exception):
|
|
10
|
+
"""
|
|
11
|
+
Base class for all microservice exceptions.
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
message: Error message.
|
|
15
|
+
code: Error code.
|
|
16
|
+
data: Additional error data.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self, message: str, code: int = -32000, data: Optional[Dict[str, Any]] = None
|
|
21
|
+
):
|
|
22
|
+
"""
|
|
23
|
+
Initialize the error.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
message: Error message.
|
|
27
|
+
code: Error code according to JSON-RPC standard.
|
|
28
|
+
data: Additional error data.
|
|
29
|
+
"""
|
|
30
|
+
self.message = message
|
|
31
|
+
self.code = code
|
|
32
|
+
self.data = data or {}
|
|
33
|
+
super().__init__(message)
|
|
34
|
+
|
|
35
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
36
|
+
"""Convert error to dictionary format."""
|
|
37
|
+
result = {
|
|
38
|
+
"code": self.code,
|
|
39
|
+
"message": self.message
|
|
40
|
+
}
|
|
41
|
+
if self.data:
|
|
42
|
+
result["data"] = self.data
|
|
43
|
+
return result
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ParseError(MicroserviceError):
|
|
48
|
+
"""
|
|
49
|
+
Error while parsing JSON request.
|
|
50
|
+
JSON-RPC Error code: -32700
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(
|
|
54
|
+
self, message: str = "Parse error", data: Optional[Dict[str, Any]] = None
|
|
55
|
+
):
|
|
56
|
+
super().__init__(message, code=-32700, data=data)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class InvalidRequestError(MicroserviceError):
|
|
60
|
+
"""
|
|
61
|
+
Invalid JSON-RPC request format.
|
|
62
|
+
JSON-RPC Error code: -32600
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def __init__(
|
|
66
|
+
self, message: str = "Invalid Request", data: Optional[Dict[str, Any]] = None
|
|
67
|
+
):
|
|
68
|
+
super().__init__(message, code=-32600, data=data)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class MethodNotFoundError(MicroserviceError):
|
|
72
|
+
"""
|
|
73
|
+
Method not found error.
|
|
74
|
+
JSON-RPC Error code: -32601
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
def __init__(
|
|
78
|
+
self, message: str = "Method not found", data: Optional[Dict[str, Any]] = None
|
|
79
|
+
):
|
|
80
|
+
super().__init__(message, code=-32601, data=data)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class InvalidParamsError(MicroserviceError):
|
|
84
|
+
"""
|
|
85
|
+
Invalid method parameters.
|
|
86
|
+
JSON-RPC Error code: -32602
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def __init__(
|
|
90
|
+
self, message: str = "Invalid params", data: Optional[Dict[str, Any]] = None
|
|
91
|
+
):
|
|
92
|
+
super().__init__(message, code=-32602, data=data)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class InternalError(MicroserviceError):
|
|
96
|
+
"""
|
|
97
|
+
Internal server error.
|
|
98
|
+
JSON-RPC Error code: -32603
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
def __init__(
|
|
102
|
+
self, message: str = "Internal error", data: Optional[Dict[str, Any]] = None
|
|
103
|
+
):
|
|
104
|
+
super().__init__(message, code=-32603, data=data)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class ValidationError(MicroserviceError):
|
|
108
|
+
"""
|
|
109
|
+
Input data validation error.
|
|
110
|
+
JSON-RPC Error code: -32602 (using Invalid params code)
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(
|
|
114
|
+
self, message: str = "Validation error", data: Optional[Dict[str, Any]] = None
|
|
115
|
+
):
|
|
116
|
+
super().__init__(message, code=-32602, data=data)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class CommandError(MicroserviceError):
|
|
120
|
+
"""
|
|
121
|
+
Command execution error.
|
|
122
|
+
JSON-RPC Error code: -32000 (server error)
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
def __init__(
|
|
126
|
+
self,
|
|
127
|
+
message: str = "Command execution error",
|
|
128
|
+
data: Optional[Dict[str, Any]] = None,
|
|
129
|
+
):
|
|
130
|
+
super().__init__(message, code=-32000, data=data)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class NotFoundError(MicroserviceError):
|
|
134
|
+
"""
|
|
135
|
+
"Not found" error.
|
|
136
|
+
JSON-RPC Error code: -32601 (using Method not found code)
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
def __init__(
|
|
140
|
+
self, message: str = "Resource not found", data: Optional[Dict[str, Any]] = None
|
|
141
|
+
):
|
|
142
|
+
super().__init__(message, code=-32601, data=data)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class ConfigurationError(MicroserviceError):
|
|
146
|
+
"""
|
|
147
|
+
Configuration error.
|
|
148
|
+
JSON-RPC Error code: -32603 (using Internal error code)
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
def __init__(
|
|
152
|
+
self,
|
|
153
|
+
message: str = "Configuration error",
|
|
154
|
+
data: Optional[Dict[str, Any]] = None,
|
|
155
|
+
):
|
|
156
|
+
super().__init__(message, code=-32603, data=data)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class AuthenticationError(MicroserviceError):
|
|
160
|
+
"""
|
|
161
|
+
Authentication error.
|
|
162
|
+
JSON-RPC Error code: -32001 (server error)
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
def __init__(
|
|
166
|
+
self,
|
|
167
|
+
message: str = "Authentication error",
|
|
168
|
+
data: Optional[Dict[str, Any]] = None,
|
|
169
|
+
):
|
|
170
|
+
super().__init__(message, code=-32001, data=data)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class AuthorizationError(MicroserviceError):
|
|
174
|
+
"""
|
|
175
|
+
Authorization error.
|
|
176
|
+
JSON-RPC Error code: -32002 (server error)
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
def __init__(
|
|
180
|
+
self,
|
|
181
|
+
message: str = "Authorization error",
|
|
182
|
+
data: Optional[Dict[str, Any]] = None,
|
|
183
|
+
):
|
|
184
|
+
super().__init__(message, code=-32002, data=data)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class TimeoutError(MicroserviceError):
|
|
188
|
+
"""
|
|
189
|
+
Timeout error.
|
|
190
|
+
JSON-RPC Error code: -32003 (server error)
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
def __init__(
|
|
194
|
+
self, message: str = "Timeout error", data: Optional[Dict[str, Any]] = None
|
|
195
|
+
):
|
|
196
|
+
super().__init__(message, code=-32003, data=data)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@dataclass
|
|
202
|
+
class ValidationResult:
|
|
203
|
+
"""Result of configuration validation."""
|
|
204
|
+
level: str # "error", "warning", "info"
|
|
205
|
+
message: str
|
|
206
|
+
section: Optional[str] = None
|
|
207
|
+
key: Optional[str] = None
|
|
208
|
+
suggestion: Optional[str] = None
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class ConfigError(MicroserviceError):
|
|
212
|
+
"""Configuration validation error."""
|
|
213
|
+
|
|
214
|
+
def __init__(self, message: str, validation_results: Optional[List[ValidationResult]] = None):
|
|
215
|
+
"""
|
|
216
|
+
Initialize configuration error.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
message: Error message
|
|
220
|
+
validation_results: List of validation results that caused the error
|
|
221
|
+
"""
|
|
222
|
+
super().__init__(message, code=-32001, data={"type": "configuration_error"})
|
|
223
|
+
self.validation_results = validation_results or []
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class MissingConfigKeyError(ConfigError):
|
|
228
|
+
"""Missing required configuration key."""
|
|
229
|
+
|
|
230
|
+
def __init__(self, key: str, section: str = None):
|
|
231
|
+
location = f"{section}.{key}" if section else key
|
|
232
|
+
message = f"Required configuration key '{location}' is missing"
|
|
233
|
+
super().__init__(message)
|
|
234
|
+
self.key = key
|
|
235
|
+
self.section = section
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class InvalidConfigValueError(ConfigError):
|
|
239
|
+
"""Invalid configuration value."""
|
|
240
|
+
|
|
241
|
+
def __init__(self, key: str, value: Any, expected_type: str, section: str = None):
|
|
242
|
+
location = f"{section}.{key}" if section else key
|
|
243
|
+
message = f"Invalid value for '{location}': got {type(value).__name__}, expected {expected_type}"
|
|
244
|
+
super().__init__(message)
|
|
245
|
+
self.key = key
|
|
246
|
+
self.section = section
|
|
247
|
+
self.value = value
|
|
248
|
+
self.expected_type = expected_type
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class MissingConfigSectionError(ConfigError):
|
|
252
|
+
"""Missing required configuration section."""
|
|
253
|
+
|
|
254
|
+
def __init__(self, section: str):
|
|
255
|
+
message = f"Required configuration section '{section}' is missing"
|
|
256
|
+
super().__init__(message)
|
|
257
|
+
self.section = section
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class MissingConfigFileError(ConfigError):
|
|
261
|
+
"""Missing configuration file."""
|
|
262
|
+
|
|
263
|
+
def __init__(self, file_path: str):
|
|
264
|
+
message = f"Configuration file '{file_path}' does not exist"
|
|
265
|
+
super().__init__(message)
|
|
266
|
+
self.file_path = file_path
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class InvalidConfigFileError(ConfigError):
|
|
270
|
+
"""Invalid configuration file format."""
|
|
271
|
+
|
|
272
|
+
def __init__(self, file_path: str, reason: str):
|
|
273
|
+
message = f"Invalid configuration file '{file_path}': {reason}"
|
|
274
|
+
super().__init__(message)
|
|
275
|
+
self.file_path = file_path
|
|
276
|
+
self.reason = reason
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Simple in-memory job manager for long-running demo commands.
|
|
6
|
+
Not for production use.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import uuid
|
|
11
|
+
from typing import Any, Dict, Optional, Awaitable
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class JobRecord:
|
|
15
|
+
def __init__(self, task: asyncio.Task):
|
|
16
|
+
self.task = task
|
|
17
|
+
self.status = "running"
|
|
18
|
+
self.result: Optional[Any] = None
|
|
19
|
+
self.error: Optional[str] = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
_jobs: Dict[str, JobRecord] = {}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def enqueue_coroutine(coro: Awaitable[Any]) -> str:
|
|
26
|
+
job_id = str(uuid.uuid4())
|
|
27
|
+
task = asyncio.create_task(_run_job(job_id, coro))
|
|
28
|
+
_jobs[job_id] = JobRecord(task)
|
|
29
|
+
return job_id
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def _run_job(job_id: str, coro):
|
|
33
|
+
rec = _jobs[job_id]
|
|
34
|
+
try:
|
|
35
|
+
rec.result = await coro
|
|
36
|
+
rec.status = "completed"
|
|
37
|
+
except Exception as exc: # noqa: BLE001
|
|
38
|
+
rec.error = str(exc)
|
|
39
|
+
rec.status = "failed"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_job_status(job_id: str) -> Dict[str, Any]:
|
|
43
|
+
rec = _jobs.get(job_id)
|
|
44
|
+
if not rec:
|
|
45
|
+
return {"exists": False}
|
|
46
|
+
return {
|
|
47
|
+
"exists": True,
|
|
48
|
+
"status": rec.status,
|
|
49
|
+
"done": rec.task.done(),
|
|
50
|
+
"result": rec.result,
|
|
51
|
+
"error": rec.error,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for configuring logging in the microservice.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import uuid
|
|
9
|
+
from logging.handlers import RotatingFileHandler
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CustomFormatter(logging.Formatter):
|
|
14
|
+
"""
|
|
15
|
+
Custom formatter for logs with colored output in console.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
grey = "\x1b[38;20m"
|
|
19
|
+
yellow = "\x1b[33;20m"
|
|
20
|
+
red = "\x1b[31;20m"
|
|
21
|
+
bold_red = "\x1b[31;1m"
|
|
22
|
+
reset = "\x1b[0m"
|
|
23
|
+
format_str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
24
|
+
|
|
25
|
+
FORMATS = {
|
|
26
|
+
logging.DEBUG: grey + format_str + reset,
|
|
27
|
+
logging.INFO: grey + format_str + reset,
|
|
28
|
+
logging.WARNING: yellow + format_str + reset,
|
|
29
|
+
logging.ERROR: red + format_str + reset,
|
|
30
|
+
logging.CRITICAL: bold_red + format_str + reset,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
def format(self, record):
|
|
34
|
+
log_fmt = self.FORMATS.get(record.levelno)
|
|
35
|
+
formatter = logging.Formatter(log_fmt)
|
|
36
|
+
return formatter.format(record)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class RequestContextFilter(logging.Filter):
|
|
40
|
+
"""
|
|
41
|
+
Filter for adding request context to logs.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(self, request_id: Optional[str] = None):
|
|
45
|
+
super().__init__()
|
|
46
|
+
self.request_id = request_id
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class RequestLogger:
|
|
50
|
+
"""
|
|
51
|
+
Logger class for logging requests with context.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(self, logger_name: str, request_id: Optional[str] = None):
|
|
55
|
+
"""
|
|
56
|
+
Initialize request get_global_logger().
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
logger_name: Logger name.
|
|
60
|
+
request_id: Request identifier.
|
|
61
|
+
"""
|
|
62
|
+
self.logger = logging.getLogger(logger_name)
|
|
63
|
+
self.request_id = request_id or str(uuid.uuid4())
|
|
64
|
+
self.filter = RequestContextFilter(self.request_id)
|
|
65
|
+
get_global_logger().addFilter(self.filter)
|
|
66
|
+
|
|
67
|
+
def debug(self, msg: str, *args, **kwargs):
|
|
68
|
+
"""Log message with DEBUG level."""
|
|
69
|
+
get_global_logger().debug(f"[{self.request_id}] {msg}", *args, **kwargs)
|
|
70
|
+
|
|
71
|
+
def info(self, msg: str, *args, **kwargs):
|
|
72
|
+
"""Log message with INFO level."""
|
|
73
|
+
get_global_logger().info(f"[{self.request_id}] {msg}", *args, **kwargs)
|
|
74
|
+
|
|
75
|
+
def warning(self, msg: str, *args, **kwargs):
|
|
76
|
+
"""Log message with WARNING level."""
|
|
77
|
+
get_global_logger().warning(f"[{self.request_id}] {msg}", *args, **kwargs)
|
|
78
|
+
|
|
79
|
+
def error(self, msg: str, *args, **kwargs):
|
|
80
|
+
"""Log message with ERROR level."""
|
|
81
|
+
get_global_logger().error(f"[{self.request_id}] {msg}", *args, **kwargs)
|
|
82
|
+
|
|
83
|
+
def exception(self, msg: str, *args, **kwargs):
|
|
84
|
+
"""Log exception with traceback."""
|
|
85
|
+
get_global_logger().exception(f"[{self.request_id}] {msg}", *args, **kwargs)
|
|
86
|
+
|
|
87
|
+
def critical(self, msg: str, *args, **kwargs):
|
|
88
|
+
"""Log message with CRITICAL level."""
|
|
89
|
+
get_global_logger().critical(f"[{self.request_id}] {msg}", *args, **kwargs)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def setup_logging(
|
|
93
|
+
level: Optional[str] = None,
|
|
94
|
+
log_file: Optional[str] = None,
|
|
95
|
+
max_bytes: Optional[int] = None,
|
|
96
|
+
backup_count: Optional[int] = None,
|
|
97
|
+
rotation_type: Optional[str] = None,
|
|
98
|
+
rotation_when: Optional[str] = None,
|
|
99
|
+
rotation_interval: Optional[int] = None,
|
|
100
|
+
) -> logging.Logger:
|
|
101
|
+
"""
|
|
102
|
+
Configure logging for the microservice.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
level: Logging level. By default, taken from configuration.
|
|
106
|
+
log_file: Path to log file. By default, taken from configuration.
|
|
107
|
+
max_bytes: Maximum log file size in bytes. By default, taken from configuration.
|
|
108
|
+
backup_count: Number of rotation files. By default, taken from configuration.
|
|
109
|
+
rotation_type: Type of log rotation ('size' or 'time'). By default, taken from configuration.
|
|
110
|
+
rotation_when: Time unit for rotation (D, H, M, S). By default, taken from configuration.
|
|
111
|
+
rotation_interval: Interval for rotation. By default, taken from configuration.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Configured get_global_logger().
|
|
115
|
+
"""
|
|
116
|
+
# Use provided parameters or defaults
|
|
117
|
+
level = level or "INFO"
|
|
118
|
+
log_file = log_file
|
|
119
|
+
rotation_type = rotation_type or "size"
|
|
120
|
+
log_dir = "./logs"
|
|
121
|
+
log_file_name = "mcp_proxy_adapter.log"
|
|
122
|
+
error_log_file = "mcp_proxy_adapter_error.log"
|
|
123
|
+
access_log_file = "mcp_proxy_adapter_access.log"
|
|
124
|
+
|
|
125
|
+
# Get rotation settings
|
|
126
|
+
max_file_size_str = "10MB"
|
|
127
|
+
backup_count = backup_count or 5
|
|
128
|
+
|
|
129
|
+
# Parse max file size (e.g., "10MB" -> 10 * 1024 * 1024)
|
|
130
|
+
max_bytes = max_bytes or _parse_file_size(max_file_size_str)
|
|
131
|
+
|
|
132
|
+
# Get format settings
|
|
133
|
+
log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
134
|
+
date_format = "%Y-%m-%d %H:%M:%S"
|
|
135
|
+
|
|
136
|
+
# Get output settings
|
|
137
|
+
console_output = True
|
|
138
|
+
file_output = True
|
|
139
|
+
|
|
140
|
+
# Convert string logging level to constant
|
|
141
|
+
numeric_level = getattr(logging, level.upper(), None)
|
|
142
|
+
if not isinstance(numeric_level, int):
|
|
143
|
+
numeric_level = logging.INFO
|
|
144
|
+
|
|
145
|
+
# Create root logger
|
|
146
|
+
logger = logging.getLogger("mcp_proxy_adapter")
|
|
147
|
+
logger.setLevel(numeric_level)
|
|
148
|
+
logger.handlers = [] # Clear handlers in case of repeated call
|
|
149
|
+
|
|
150
|
+
# Create formatter
|
|
151
|
+
formatter = logging.Formatter(log_format, date_format)
|
|
152
|
+
|
|
153
|
+
# Create console handler if enabled
|
|
154
|
+
if console_output:
|
|
155
|
+
console_handler = logging.StreamHandler(sys.stdout)
|
|
156
|
+
console_handler.setLevel(numeric_level)
|
|
157
|
+
console_handler.setFormatter(CustomFormatter())
|
|
158
|
+
logger.addHandler(console_handler)
|
|
159
|
+
|
|
160
|
+
# Create file handlers if file output is enabled
|
|
161
|
+
if file_output and log_dir:
|
|
162
|
+
# Create directory for log files if it doesn't exist
|
|
163
|
+
if not os.path.exists(log_dir):
|
|
164
|
+
os.makedirs(log_dir, exist_ok=True)
|
|
165
|
+
|
|
166
|
+
# Main log file
|
|
167
|
+
if log_file_name:
|
|
168
|
+
main_log_path = os.path.join(log_dir, log_file_name)
|
|
169
|
+
main_handler = RotatingFileHandler(
|
|
170
|
+
main_log_path,
|
|
171
|
+
maxBytes=max_bytes,
|
|
172
|
+
backupCount=backup_count,
|
|
173
|
+
encoding="utf-8",
|
|
174
|
+
)
|
|
175
|
+
main_handler.setLevel(numeric_level)
|
|
176
|
+
main_handler.setFormatter(formatter)
|
|
177
|
+
logger.addHandler(main_handler)
|
|
178
|
+
|
|
179
|
+
# Error log file
|
|
180
|
+
if error_log_file:
|
|
181
|
+
error_log_path = os.path.join(log_dir, error_log_file)
|
|
182
|
+
error_handler = RotatingFileHandler(
|
|
183
|
+
error_log_path,
|
|
184
|
+
maxBytes=max_bytes,
|
|
185
|
+
backupCount=backup_count,
|
|
186
|
+
encoding="utf-8",
|
|
187
|
+
)
|
|
188
|
+
error_handler.setLevel(logging.ERROR)
|
|
189
|
+
error_handler.setFormatter(formatter)
|
|
190
|
+
logger.addHandler(error_handler)
|
|
191
|
+
|
|
192
|
+
# Access log file (for HTTP requests)
|
|
193
|
+
if access_log_file:
|
|
194
|
+
access_log_path = os.path.join(log_dir, access_log_file)
|
|
195
|
+
access_handler = RotatingFileHandler(
|
|
196
|
+
access_log_path,
|
|
197
|
+
maxBytes=max_bytes,
|
|
198
|
+
backupCount=backup_count,
|
|
199
|
+
encoding="utf-8",
|
|
200
|
+
)
|
|
201
|
+
access_handler.setLevel(logging.INFO)
|
|
202
|
+
access_handler.setFormatter(formatter)
|
|
203
|
+
logger.addHandler(access_handler)
|
|
204
|
+
|
|
205
|
+
# Configure loggers for external libraries
|
|
206
|
+
log_levels = {}
|
|
207
|
+
for logger_name, logger_level in log_levels.items():
|
|
208
|
+
lib_logger = logging.getLogger(logger_name)
|
|
209
|
+
lib_logger.setLevel(getattr(logging, logger_level.upper(), logging.INFO))
|
|
210
|
+
|
|
211
|
+
return logger
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _parse_file_size(size_str) -> int:
|
|
215
|
+
"""
|
|
216
|
+
Parse file size string to bytes.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
size_str: Size string (e.g., "10MB", "1GB", "100KB") or int
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
Size in bytes
|
|
223
|
+
"""
|
|
224
|
+
# If it's already an int, return it
|
|
225
|
+
if isinstance(size_str, int):
|
|
226
|
+
return size_str
|
|
227
|
+
|
|
228
|
+
# Convert to string and parse
|
|
229
|
+
size_str = str(size_str).upper()
|
|
230
|
+
if size_str.endswith("KB"):
|
|
231
|
+
return int(size_str[:-2]) * 1024
|
|
232
|
+
elif size_str.endswith("MB"):
|
|
233
|
+
return int(size_str[:-2]) * 1024 * 1024
|
|
234
|
+
elif size_str.endswith("GB"):
|
|
235
|
+
return int(size_str[:-2]) * 1024 * 1024 * 1024
|
|
236
|
+
else:
|
|
237
|
+
# Assume bytes if no unit specified
|
|
238
|
+
return int(size_str)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# Global get_global_logger() for use throughout the application
|
|
242
|
+
# Initialize lazily to avoid import-time errors
|
|
243
|
+
logger = None
|
|
244
|
+
|
|
245
|
+
def get_global_logger():
|
|
246
|
+
"""Get the global logger, initializing it if necessary."""
|
|
247
|
+
global logger
|
|
248
|
+
if logger is None:
|
|
249
|
+
logger = setup_logging()
|
|
250
|
+
return logger
|