mcp-proxy-adapter 6.9.27__py3-none-any.whl → 6.9.29__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 +0 -6
  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 -912
  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.27.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/METADATA +2 -1
  208. mcp_proxy_adapter-6.9.29.dist-info/RECORD +235 -0
  209. {mcp_proxy_adapter-6.9.27.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/entry_points.txt +1 -1
  210. mcp_proxy_adapter-6.9.27.dist-info/RECORD +0 -149
  211. {mcp_proxy_adapter-6.9.27.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/WHEEL +0 -0
  212. {mcp_proxy_adapter-6.9.27.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/top_level.txt +0 -0
@@ -10,7 +10,6 @@ email: vasilyvz@gmail.com
10
10
  import json
11
11
  import sys
12
12
  from pathlib import Path
13
- from typing import Dict, Any, List
14
13
 
15
14
  # Add the project root to the path
16
15
  project_root = Path(__file__).parent.parent.parent
@@ -20,196 +19,10 @@ from mcp_proxy_adapter.core.config_validator import ConfigValidator
20
19
  from mcp_proxy_adapter.examples.config_builder import generate_complete_config
21
20
 
22
21
 
23
- def test_generator_validator_structure():
24
- """Test that generated configs have the correct structure."""
25
- print("🔍 Testing Generator-Validator Structure Compatibility")
26
- print("=" * 60)
27
-
28
- # Test basic HTTP configuration
29
- print("\n📋 Testing: HTTP Basic Configuration")
30
- print("-" * 40)
31
-
32
- try:
33
- # Generate configuration
34
- print(" 🔧 Generating configuration...")
35
- config = generate_complete_config(host="localhost", port=8080)
36
-
37
- # Check required sections exist
38
- required_sections = ["server", "logging", "commands", "debug", "ssl", "security", "roles", "proxy_registration"]
39
- missing_sections = []
40
-
41
- for section in required_sections:
42
- if section not in config:
43
- missing_sections.append(section)
44
-
45
- if missing_sections:
46
- print(f" ❌ FAIL - Missing sections: {missing_sections}")
47
- return False
48
-
49
- # Check server section
50
- server_required = ["host", "port", "protocol", "debug", "log_level"]
51
- server_missing = [key for key in server_required if key not in config["server"]]
52
-
53
- if server_missing:
54
- print(f" ❌ FAIL - Server section missing keys: {server_missing}")
55
- return False
56
-
57
- # Check protocol is HTTP
58
- if config["server"]["protocol"] != "http":
59
- print(f" ❌ FAIL - Expected protocol 'http', got '{config['server']['protocol']}'")
60
- return False
61
-
62
- # Check SSL is disabled
63
- if config["ssl"]["enabled"] != False:
64
- print(f" ❌ FAIL - Expected SSL disabled, got {config['ssl']['enabled']}")
65
- return False
66
-
67
- # Check security is disabled
68
- if config["security"]["enabled"] != False:
69
- print(f" ❌ FAIL - Expected security disabled, got {config['security']['enabled']}")
70
- return False
71
-
72
- # Check roles is disabled
73
- if config["roles"]["enabled"] != False:
74
- print(f" ❌ FAIL - Expected roles disabled, got {config['roles']['enabled']}")
75
- return False
76
-
77
- # Check proxy registration is disabled
78
- if config["proxy_registration"]["enabled"] != False:
79
- print(f" ❌ FAIL - Expected proxy registration disabled, got {config['proxy_registration']['enabled']}")
80
- return False
81
-
82
- print(" ✅ PASS - All required sections and fields present")
83
- print(" ✅ PASS - All features correctly disabled")
84
- print(" ✅ PASS - Protocol correctly set to HTTP")
85
-
86
- return True
87
-
88
- except Exception as e:
89
- print(f" 💥 EXCEPTION: {str(e)}")
90
- return False
91
-
92
-
93
- def test_config_modification():
94
- """Test that we can modify generated configs for different scenarios."""
95
- print("\n📋 Testing: Configuration Modification")
96
- print("-" * 40)
97
-
98
- try:
99
- # Generate base config
100
- config = generate_complete_config(host="localhost", port=8080)
101
-
102
- # Test 1: Enable HTTPS
103
- print(" 🔧 Testing HTTPS modification...")
104
- config["server"]["protocol"] = "https"
105
- config["ssl"]["enabled"] = True
106
-
107
- if config["server"]["protocol"] != "https":
108
- print(" ❌ FAIL - Could not change protocol to HTTPS")
109
- return False
110
-
111
- if config["ssl"]["enabled"] != True:
112
- print(" ❌ FAIL - Could not enable SSL")
113
- return False
114
-
115
- print(" ✅ PASS - HTTPS modification successful")
116
-
117
- # Test 2: Enable security
118
- print(" 🔧 Testing security modification...")
119
- config["security"]["enabled"] = True
120
- config["security"]["tokens"] = {"test_token": {"permissions": ["*"]}}
121
-
122
- if config["security"]["enabled"] != True:
123
- print(" ❌ FAIL - Could not enable security")
124
- return False
125
-
126
- if "tokens" not in config["security"]:
127
- print(" ❌ FAIL - Could not add tokens")
128
- return False
129
-
130
- print(" ✅ PASS - Security modification successful")
131
-
132
- # Test 3: Enable roles
133
- print(" 🔧 Testing roles modification...")
134
- config["roles"]["enabled"] = True
135
- config["roles"]["config_file"] = "./test_roles.json"
136
-
137
- if config["roles"]["enabled"] != True:
138
- print(" ❌ FAIL - Could not enable roles")
139
- return False
140
-
141
- if config["roles"]["config_file"] != "./test_roles.json":
142
- print(" ❌ FAIL - Could not set roles config file")
143
- return False
144
-
145
- print(" ✅ PASS - Roles modification successful")
146
-
147
- return True
148
-
149
- except Exception as e:
150
- print(f" 💥 EXCEPTION: {str(e)}")
151
- return False
152
22
 
153
23
 
154
- def test_validator_accepts_generated_config():
155
- """Test that validator accepts the basic generated config without file checks."""
156
- print("\n📋 Testing: Validator Accepts Generated Config")
157
- print("-" * 40)
158
-
159
- try:
160
- # Generate config
161
- config = generate_complete_config(host="localhost", port=8080)
162
-
163
- # Create a mock validator that doesn't check files
164
- class MockValidator(ConfigValidator):
165
- def _validate_file_existence(self):
166
- """Skip file existence checks for testing."""
167
- pass
168
-
169
- def _validate_certificate_file(self, cert_file, section, key):
170
- """Skip certificate validation for testing."""
171
- pass
172
-
173
- def _validate_key_file(self, key_file, section, key):
174
- """Skip key validation for testing."""
175
- pass
176
-
177
- def _validate_ca_certificate_file(self, ca_cert_file, section, key):
178
- """Skip CA certificate validation for testing."""
179
- pass
180
-
181
- # Validate with mock validator
182
- validator = MockValidator()
183
- validator.config_data = config
184
- results = validator.validate_config()
185
-
186
- errors = [r for r in results if r.level == "error"]
187
- warnings = [r for r in results if r.level == "warning"]
188
-
189
- if len(errors) > 0:
190
- print(f" ❌ FAIL - {len(errors)} validation errors:")
191
- for error in errors[:3]:
192
- print(f" • {error.message}")
193
- return False
194
-
195
- print(f" ✅ PASS - No validation errors ({len(warnings)} warnings)")
196
- return True
197
-
198
- except Exception as e:
199
- print(f" 💥 EXCEPTION: {str(e)}")
200
- return False
201
24
 
202
25
 
203
- def main():
204
- """Main test function."""
205
- print("🚀 Generator-Validator Structure Compatibility Test")
206
- print("=" * 70)
207
-
208
- tests = [
209
- ("Structure Test", test_generator_validator_structure),
210
- ("Modification Test", test_config_modification),
211
- ("Validator Acceptance Test", test_validator_accepts_generated_config)
212
- ]
213
26
 
214
27
  results = []
215
28
 
@@ -0,0 +1,25 @@
1
+ """
2
+ Integration modules for mcp_proxy_adapter.
3
+
4
+ This package contains integrations with external systems and libraries
5
+ to extend the functionality of the MCP Proxy Adapter framework.
6
+
7
+ Author: Vasiliy Zdanovskiy
8
+ email: vasilyvz@gmail.com
9
+ """
10
+
11
+ from .queuemgr_integration import (
12
+ QueueManagerIntegration,
13
+ QueueJobBase,
14
+ QueueJobResult,
15
+ QueueJobStatus,
16
+ QueueJobError,
17
+ )
18
+
19
+ __all__ = [
20
+ "QueueManagerIntegration",
21
+ "QueueJobBase",
22
+ "QueueJobResult",
23
+ "QueueJobStatus",
24
+ "QueueJobError",
25
+ ]
@@ -0,0 +1,462 @@
1
+ """
2
+ Queue Manager Integration for MCP Proxy Adapter.
3
+
4
+ This module provides integration between mcp_proxy_adapter and queuemgr
5
+ for managing background jobs and task queues.
6
+
7
+ Author: Vasiliy Zdanovskiy
8
+ email: vasilyvz@gmail.com
9
+ """
10
+
11
+ import logging
12
+ from pathlib import Path
13
+ from contextlib import asynccontextmanager
14
+ from typing import Optional, Dict, Any, Type
15
+
16
+ try:
17
+ from queuemgr.jobs.base import QueueJobBase as QueuemgrJobBase
18
+ from queuemgr.core.types import JobStatus as QueuemgrJobStatus
19
+ from queuemgr.exceptions import (
20
+ QueueManagerError,
21
+ JobNotFoundError,
22
+ JobAlreadyExistsError,
23
+ InvalidJobStateError,
24
+ JobExecutionError,
25
+ ProcessControlError,
26
+ ValidationError as QueuemgrValidationError,
27
+ TimeoutError as QueuemgrTimeoutError,
28
+ )
29
+ QUEUEMGR_AVAILABLE = True
30
+ except ImportError as e:
31
+ # Fallback for when queuemgr is not available
32
+ QUEUEMGR_AVAILABLE = False
33
+ QueuemgrJobBase = object
34
+ QueuemgrJobStatus = str
35
+ QueueManagerError = Exception
36
+ JobNotFoundError = Exception
37
+ JobAlreadyExistsError = Exception
38
+ InvalidJobStateError = Exception
39
+ JobExecutionError = Exception
40
+ ProcessControlError = Exception
41
+ QueuemgrValidationError = Exception
42
+ QueuemgrTimeoutError = Exception
43
+
44
+ from mcp_proxy_adapter.core.logging import get_global_logger
45
+ from mcp_proxy_adapter.core.errors import MicroserviceError, ValidationError
46
+
47
+
48
+ class QueueJobStatus:
49
+ """Job status constants for queue integration."""
50
+
51
+ PENDING = "pending"
52
+ RUNNING = "running"
53
+ COMPLETED = "completed"
54
+ FAILED = "failed"
55
+ STOPPED = "stopped"
56
+ DELETED = "deleted"
57
+
58
+
59
+ class QueueJobResult:
60
+ """Result of a queue job execution."""
61
+
62
+ def __init__(
63
+ self,
64
+ job_id: str,
65
+ status: str,
66
+ result: Optional[Dict[str, Any]] = None,
67
+ error: Optional[str] = None,
68
+ progress: int = 0,
69
+ description: str = "",
70
+ ):
71
+ """
72
+ Initialize queue job result.
73
+
74
+ Args:
75
+ job_id: Unique job identifier
76
+ status: Job status
77
+ result: Job result data
78
+ error: Error message if failed
79
+ progress: Progress percentage (0-100)
80
+ description: Job description
81
+ """
82
+ self.job_id = job_id
83
+ self.status = status
84
+ self.result = result or {}
85
+ self.error = error
86
+ self.progress = max(0, min(100, progress))
87
+ self.description = description
88
+
89
+
90
+ class QueueJobError(Exception):
91
+ """Exception raised for queue job errors."""
92
+
93
+ def __init__(self, job_id: str, message: str, original_error: Optional[Exception] = None):
94
+ """
95
+ Initialize queue job error.
96
+
97
+ Args:
98
+ job_id: Job identifier that failed
99
+ message: Error message
100
+ original_error: Original exception that caused the error
101
+ """
102
+ super().__init__(f"Job {job_id}: {message}")
103
+ self.job_id = job_id
104
+ self.original_error = original_error
105
+
106
+
107
+ class QueueJobBase(QueuemgrJobBase):
108
+ """
109
+ Base class for MCP Proxy Adapter queue jobs.
110
+
111
+ This class extends the queuemgr QueueJobBase to provide
112
+ MCP-specific functionality and error handling.
113
+ """
114
+
115
+ def __init__(self, job_id: str, params: Dict[str, Any]):
116
+ """
117
+ Initialize MCP queue job.
118
+
119
+ Args:
120
+ job_id: Unique job identifier
121
+ params: Job parameters
122
+ """
123
+ if not QUEUEMGR_AVAILABLE:
124
+ raise MicroserviceError(
125
+ "queuemgr is not available. Install it with: pip install queuemgr>=1.0.5"
126
+ )
127
+
128
+ super().__init__(job_id, params)
129
+ self.logger = get_global_logger()
130
+ self.mcp_params = params
131
+
132
+
133
+
134
+
135
+ class QueueManagerIntegration:
136
+ """
137
+ Queue Manager Integration for MCP Proxy Adapter.
138
+
139
+ This class provides a high-level interface for managing
140
+ background jobs using the queuemgr system.
141
+ """
142
+
143
+ def __init__(
144
+ self,
145
+ registry_path: str = "mcp_queue_registry.jsonl",
146
+ shutdown_timeout: float = 30.0,
147
+ max_concurrent_jobs: int = 10,
148
+ ):
149
+ """
150
+ Initialize queue manager integration.
151
+
152
+ Args:
153
+ registry_path: Path to the queue registry file
154
+ shutdown_timeout: Timeout for graceful shutdown
155
+ max_concurrent_jobs: Maximum number of concurrent jobs
156
+ """
157
+ if not QUEUEMGR_AVAILABLE:
158
+ raise MicroserviceError(
159
+ "queuemgr is not available. Install it with: pip install queuemgr>=1.0.5"
160
+ )
161
+
162
+ self.registry_path = registry_path
163
+ self.shutdown_timeout = shutdown_timeout
164
+ self.max_concurrent_jobs = max_concurrent_jobs
165
+ self.logger = get_global_logger()
166
+ self._queue_system: Optional[AsyncQueueSystem] = None
167
+ self._is_running = False
168
+
169
+ async def start(self) -> None:
170
+ """Start the queue manager integration."""
171
+ if self._is_running:
172
+ self.logger.warning("Queue manager integration is already running")
173
+ return
174
+
175
+ try:
176
+ self._queue_system = AsyncQueueSystem(
177
+ registry_path=self.registry_path,
178
+ shutdown_timeout=self.shutdown_timeout,
179
+ )
180
+ await self._queue_system.start()
181
+ self._is_running = True
182
+ self.logger.info("✅ Queue manager integration started")
183
+
184
+ except Exception as e:
185
+ self.logger.error(f"❌ Failed to start queue manager integration: {e}")
186
+ raise MicroserviceError(f"Failed to start queue manager: {str(e)}")
187
+
188
+ async def stop(self) -> None:
189
+ """Stop the queue manager integration."""
190
+ if not self._is_running:
191
+ self.logger.warning("Queue manager integration is not running")
192
+ return
193
+
194
+ try:
195
+ if self._queue_system:
196
+ await self._queue_system.stop()
197
+ self._is_running = False
198
+ self.logger.info("✅ Queue manager integration stopped")
199
+
200
+ except Exception as e:
201
+ self.logger.error(f"❌ Failed to stop queue manager integration: {e}")
202
+ raise MicroserviceError(f"Failed to stop queue manager: {str(e)}")
203
+
204
+ def is_running(self) -> bool:
205
+ """Check if the queue manager integration is running."""
206
+ return self._is_running and self._queue_system is not None
207
+
208
+ async def add_job(
209
+ self,
210
+ job_class: Type[QueueJobBase],
211
+ job_id: str,
212
+ params: Dict[str, Any],
213
+ ) -> QueueJobResult:
214
+ """
215
+ Add a job to the queue.
216
+
217
+ Args:
218
+ job_class: Job class to instantiate
219
+ job_id: Unique job identifier
220
+ params: Job parameters
221
+
222
+ Returns:
223
+ QueueJobResult with job information
224
+
225
+ Raises:
226
+ QueueJobError: If job cannot be added
227
+ """
228
+ if not self.is_running():
229
+ raise QueueJobError(job_id, "Queue manager is not running")
230
+
231
+ try:
232
+ await self._queue_system.add_job(job_class, job_id, params)
233
+ return QueueJobResult(
234
+ job_id=job_id,
235
+ status=QueueJobStatus.PENDING,
236
+ description="Job added to queue"
237
+ )
238
+
239
+ except JobAlreadyExistsError as e:
240
+ raise QueueJobError(job_id, f"Job already exists: {str(e)}", e)
241
+ except QueuemgrValidationError as e:
242
+ raise QueueJobError(job_id, f"Invalid job parameters: {str(e)}", e)
243
+ except Exception as e:
244
+ raise QueueJobError(job_id, f"Failed to add job: {str(e)}", e)
245
+
246
+ async def start_job(self, job_id: str) -> QueueJobResult:
247
+ """
248
+ Start a job in the queue.
249
+
250
+ Args:
251
+ job_id: Job identifier to start
252
+
253
+ Returns:
254
+ QueueJobResult with job status
255
+
256
+ Raises:
257
+ QueueJobError: If job cannot be started
258
+ """
259
+ if not self.is_running():
260
+ raise QueueJobError(job_id, "Queue manager is not running")
261
+
262
+ try:
263
+ await self._queue_system.start_job(job_id)
264
+ return QueueJobResult(
265
+ job_id=job_id,
266
+ status=QueueJobStatus.RUNNING,
267
+ description="Job started"
268
+ )
269
+
270
+ except JobNotFoundError as e:
271
+ raise QueueJobError(job_id, f"Job not found: {str(e)}", e)
272
+ except InvalidJobStateError as e:
273
+ raise QueueJobError(job_id, f"Invalid job state: {str(e)}", e)
274
+ except Exception as e:
275
+ raise QueueJobError(job_id, f"Failed to start job: {str(e)}", e)
276
+
277
+ async def stop_job(self, job_id: str) -> QueueJobResult:
278
+ """
279
+ Stop a job in the queue.
280
+
281
+ Args:
282
+ job_id: Job identifier to stop
283
+
284
+ Returns:
285
+ QueueJobResult with job status
286
+
287
+ Raises:
288
+ QueueJobError: If job cannot be stopped
289
+ """
290
+ if not self.is_running():
291
+ raise QueueJobError(job_id, "Queue manager is not running")
292
+
293
+ try:
294
+ await self._queue_system.stop_job(job_id)
295
+ return QueueJobResult(
296
+ job_id=job_id,
297
+ status=QueueJobStatus.STOPPED,
298
+ description="Job stopped"
299
+ )
300
+
301
+ except JobNotFoundError as e:
302
+ raise QueueJobError(job_id, f"Job not found: {str(e)}", e)
303
+ except ProcessControlError as e:
304
+ raise QueueJobError(job_id, f"Process control error: {str(e)}", e)
305
+ except Exception as e:
306
+ raise QueueJobError(job_id, f"Failed to stop job: {str(e)}", e)
307
+
308
+ async def delete_job(self, job_id: str) -> QueueJobResult:
309
+ """
310
+ Delete a job from the queue.
311
+
312
+ Args:
313
+ job_id: Job identifier to delete
314
+
315
+ Returns:
316
+ QueueJobResult with job status
317
+
318
+ Raises:
319
+ QueueJobError: If job cannot be deleted
320
+ """
321
+ if not self.is_running():
322
+ raise QueueJobError(job_id, "Queue manager is not running")
323
+
324
+ try:
325
+ await self._queue_system.delete_job(job_id)
326
+ return QueueJobResult(
327
+ job_id=job_id,
328
+ status=QueueJobStatus.DELETED,
329
+ description="Job deleted"
330
+ )
331
+
332
+ except JobNotFoundError as e:
333
+ raise QueueJobError(job_id, f"Job not found: {str(e)}", e)
334
+ except Exception as e:
335
+ raise QueueJobError(job_id, f"Failed to delete job: {str(e)}", e)
336
+
337
+ async def get_job_status(self, job_id: str) -> QueueJobResult:
338
+ """
339
+ Get the status of a job.
340
+
341
+ Args:
342
+ job_id: Job identifier to get status for
343
+
344
+ Returns:
345
+ QueueJobResult with job status and information
346
+
347
+ Raises:
348
+ QueueJobError: If job status cannot be retrieved
349
+ """
350
+ if not self.is_running():
351
+ raise QueueJobError(job_id, "Queue manager is not running")
352
+
353
+ try:
354
+ status_data = await self._queue_system.get_job_status(job_id)
355
+
356
+ # Convert queuemgr status to MCP status
357
+ mcp_status = self._convert_status(status_data.get("status", "unknown"))
358
+
359
+ return QueueJobResult(
360
+ job_id=job_id,
361
+ status=mcp_status,
362
+ result=status_data.get("result", {}),
363
+ error=status_data.get("error"),
364
+ progress=status_data.get("progress", 0),
365
+ description=status_data.get("description", ""),
366
+ )
367
+
368
+ except JobNotFoundError as e:
369
+ raise QueueJobError(job_id, f"Job not found: {str(e)}", e)
370
+ except Exception as e:
371
+ raise QueueJobError(job_id, f"Failed to get job status: {str(e)}", e)
372
+
373
+ async def list_jobs(self) -> List[QueueJobResult]:
374
+ """
375
+ List all jobs in the queue.
376
+
377
+ Returns:
378
+ List of QueueJobResult objects
379
+
380
+ Raises:
381
+ QueueJobError: If jobs cannot be listed
382
+ """
383
+ if not self.is_running():
384
+ raise QueueJobError("", "Queue manager is not running")
385
+
386
+ try:
387
+ jobs_data = await self._queue_system.list_jobs()
388
+
389
+ results = []
390
+ for job_data in jobs_data:
391
+ mcp_status = self._convert_status(job_data.get("status", "unknown"))
392
+ results.append(QueueJobResult(
393
+ job_id=job_data.get("job_id", "unknown"),
394
+ status=mcp_status,
395
+ result=job_data.get("result", {}),
396
+ error=job_data.get("error"),
397
+ progress=job_data.get("progress", 0),
398
+ description=job_data.get("description", ""),
399
+ ))
400
+
401
+ return results
402
+
403
+ except Exception as e:
404
+ raise QueueJobError("", f"Failed to list jobs: {str(e)}", e)
405
+
406
+
407
+ def _convert_status(self, queuemgr_status: str) -> str:
408
+ """
409
+ Convert queuemgr status to MCP status.
410
+
411
+ Args:
412
+ queuemgr_status: Status from queuemgr
413
+
414
+ Returns:
415
+ MCP-compatible status
416
+ """
417
+ status_mapping = {
418
+ "pending": QueueJobStatus.PENDING,
419
+ "running": QueueJobStatus.RUNNING,
420
+ "completed": QueueJobStatus.COMPLETED,
421
+ "failed": QueueJobStatus.FAILED,
422
+ "stopped": QueueJobStatus.STOPPED,
423
+ "deleted": QueueJobStatus.DELETED,
424
+ }
425
+
426
+ return status_mapping.get(queuemgr_status.lower(), QueueJobStatus.PENDING)
427
+
428
+
429
+ # Global queue manager instance
430
+ _global_queue_manager: Optional[QueueManagerIntegration] = None
431
+
432
+
433
+ async def get_global_queue_manager() -> QueueManagerIntegration:
434
+ """
435
+ Get global queue manager instance, initializing it if necessary.
436
+
437
+ Returns:
438
+ QueueManagerIntegration instance
439
+
440
+ Raises:
441
+ QueueJobError: If queue manager cannot be initialized
442
+ """
443
+ global _global_queue_manager
444
+
445
+ if _global_queue_manager is None:
446
+ # Initialize with default configuration
447
+ config = {
448
+ "queue_manager": {
449
+ "backend": "memory", # Default to in-memory for simplicity
450
+ "max_workers": 4,
451
+ }
452
+ }
453
+ _global_queue_manager = QueueManagerIntegration(config)
454
+ await _global_queue_manager.initialize()
455
+
456
+ return _global_queue_manager
457
+
458
+
459
+
460
+
461
+
462
+