mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_proxy_adapter/__init__.py +9 -5
- mcp_proxy_adapter/__main__.py +1 -1
- mcp_proxy_adapter/api/app.py +227 -176
- mcp_proxy_adapter/api/handlers.py +68 -60
- mcp_proxy_adapter/api/middleware/__init__.py +7 -5
- mcp_proxy_adapter/api/middleware/base.py +19 -16
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +44 -34
- mcp_proxy_adapter/api/middleware/error_handling.py +57 -67
- mcp_proxy_adapter/api/middleware/factory.py +50 -52
- mcp_proxy_adapter/api/middleware/logging.py +46 -30
- mcp_proxy_adapter/api/middleware/performance.py +19 -16
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +80 -50
- mcp_proxy_adapter/api/middleware/transport_middleware.py +26 -24
- mcp_proxy_adapter/api/middleware/unified_security.py +70 -51
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +43 -34
- mcp_proxy_adapter/api/schemas.py +69 -43
- mcp_proxy_adapter/api/tool_integration.py +83 -63
- mcp_proxy_adapter/api/tools.py +60 -50
- mcp_proxy_adapter/commands/__init__.py +15 -6
- mcp_proxy_adapter/commands/auth_validation_command.py +107 -110
- mcp_proxy_adapter/commands/base.py +108 -112
- mcp_proxy_adapter/commands/builtin_commands.py +28 -18
- mcp_proxy_adapter/commands/catalog_manager.py +394 -265
- mcp_proxy_adapter/commands/cert_monitor_command.py +222 -204
- mcp_proxy_adapter/commands/certificate_management_command.py +210 -213
- mcp_proxy_adapter/commands/command_registry.py +275 -226
- mcp_proxy_adapter/commands/config_command.py +48 -33
- mcp_proxy_adapter/commands/dependency_container.py +22 -23
- mcp_proxy_adapter/commands/dependency_manager.py +65 -56
- mcp_proxy_adapter/commands/echo_command.py +15 -15
- mcp_proxy_adapter/commands/health_command.py +31 -29
- mcp_proxy_adapter/commands/help_command.py +97 -61
- mcp_proxy_adapter/commands/hooks.py +65 -49
- mcp_proxy_adapter/commands/key_management_command.py +148 -147
- mcp_proxy_adapter/commands/load_command.py +58 -40
- mcp_proxy_adapter/commands/plugins_command.py +80 -54
- mcp_proxy_adapter/commands/protocol_management_command.py +60 -48
- mcp_proxy_adapter/commands/proxy_registration_command.py +107 -115
- mcp_proxy_adapter/commands/reload_command.py +43 -37
- mcp_proxy_adapter/commands/result.py +26 -33
- mcp_proxy_adapter/commands/role_test_command.py +26 -26
- mcp_proxy_adapter/commands/roles_management_command.py +176 -173
- mcp_proxy_adapter/commands/security_command.py +134 -122
- mcp_proxy_adapter/commands/settings_command.py +47 -56
- mcp_proxy_adapter/commands/ssl_setup_command.py +109 -129
- mcp_proxy_adapter/commands/token_management_command.py +129 -158
- mcp_proxy_adapter/commands/transport_management_command.py +41 -36
- mcp_proxy_adapter/commands/unload_command.py +42 -37
- mcp_proxy_adapter/config.py +36 -35
- mcp_proxy_adapter/core/__init__.py +19 -21
- mcp_proxy_adapter/core/app_factory.py +30 -9
- mcp_proxy_adapter/core/app_runner.py +81 -64
- mcp_proxy_adapter/core/auth_validator.py +176 -182
- mcp_proxy_adapter/core/certificate_utils.py +469 -426
- mcp_proxy_adapter/core/client.py +155 -126
- mcp_proxy_adapter/core/client_manager.py +60 -54
- mcp_proxy_adapter/core/client_security.py +120 -91
- mcp_proxy_adapter/core/config_converter.py +176 -143
- mcp_proxy_adapter/core/config_validator.py +12 -4
- mcp_proxy_adapter/core/crl_utils.py +21 -7
- mcp_proxy_adapter/core/errors.py +64 -20
- mcp_proxy_adapter/core/logging.py +34 -29
- mcp_proxy_adapter/core/mtls_asgi.py +29 -25
- mcp_proxy_adapter/core/mtls_asgi_app.py +66 -54
- mcp_proxy_adapter/core/protocol_manager.py +154 -104
- mcp_proxy_adapter/core/proxy_client.py +202 -144
- mcp_proxy_adapter/core/proxy_registration.py +7 -3
- mcp_proxy_adapter/core/role_utils.py +139 -125
- mcp_proxy_adapter/core/security_adapter.py +88 -77
- mcp_proxy_adapter/core/security_factory.py +50 -44
- mcp_proxy_adapter/core/security_integration.py +72 -24
- mcp_proxy_adapter/core/server_adapter.py +68 -64
- mcp_proxy_adapter/core/server_engine.py +71 -53
- mcp_proxy_adapter/core/settings.py +68 -58
- mcp_proxy_adapter/core/ssl_utils.py +69 -56
- mcp_proxy_adapter/core/transport_manager.py +72 -60
- mcp_proxy_adapter/core/unified_config_adapter.py +201 -150
- mcp_proxy_adapter/core/utils.py +4 -2
- mcp_proxy_adapter/custom_openapi.py +107 -99
- mcp_proxy_adapter/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/commands/__init__.py +1 -1
- mcp_proxy_adapter/examples/create_certificates_simple.py +182 -71
- mcp_proxy_adapter/examples/debug_request_state.py +38 -19
- mcp_proxy_adapter/examples/debug_role_chain.py +53 -20
- mcp_proxy_adapter/examples/demo_client.py +48 -36
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/generate_all_certificates.py +198 -73
- mcp_proxy_adapter/examples/generate_certificates.py +31 -16
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/generate_test_configs.py +68 -91
- mcp_proxy_adapter/examples/proxy_registration_example.py +76 -75
- mcp_proxy_adapter/examples/run_example.py +23 -5
- mcp_proxy_adapter/examples/run_full_test_suite.py +109 -71
- mcp_proxy_adapter/examples/run_proxy_server.py +22 -9
- mcp_proxy_adapter/examples/run_security_tests.py +103 -41
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +72 -36
- mcp_proxy_adapter/examples/scripts/config_generator.py +288 -187
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +185 -72
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/security_test_client.py +196 -127
- mcp_proxy_adapter/examples/setup_test_environment.py +17 -29
- mcp_proxy_adapter/examples/test_config.py +19 -4
- mcp_proxy_adapter/examples/test_config_generator.py +23 -7
- mcp_proxy_adapter/examples/test_examples.py +84 -56
- mcp_proxy_adapter/examples/universal_client.py +119 -62
- mcp_proxy_adapter/openapi.py +108 -115
- mcp_proxy_adapter/utils/config_generator.py +429 -274
- mcp_proxy_adapter/version.py +1 -2
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-6.3.6.dist-info/RECORD +144 -0
- mcp_proxy_adapter-6.3.6.dist-info/top_level.txt +2 -0
- mcp_proxy_adapter_issue_package/demonstrate_issue.py +178 -0
- mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
- mcp_proxy_adapter-6.3.4.dist-info/top_level.txt +0 -1
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/licenses/LICENSE +0 -0
@@ -22,77 +22,68 @@ from mcp_proxy_adapter.core.logging import logger
|
|
22
22
|
@dataclass
|
23
23
|
class ProxyRegistrationCommandResult(SuccessResult):
|
24
24
|
"""Result of proxy registration command."""
|
25
|
-
|
25
|
+
|
26
26
|
operation: str
|
27
27
|
success: bool
|
28
28
|
server_key: Optional[str] = None
|
29
29
|
message: str = ""
|
30
30
|
details: Optional[Dict[str, Any]] = None
|
31
|
-
|
31
|
+
|
32
32
|
def to_dict(self) -> Dict[str, Any]:
|
33
33
|
"""Convert result to dictionary."""
|
34
34
|
result = {
|
35
35
|
"operation": self.operation,
|
36
36
|
"success": self.success,
|
37
|
-
"message": self.message
|
37
|
+
"message": self.message,
|
38
38
|
}
|
39
|
-
|
39
|
+
|
40
40
|
if self.server_key:
|
41
41
|
result["server_key"] = self.server_key
|
42
|
-
|
42
|
+
|
43
43
|
if self.details:
|
44
44
|
result["details"] = self.details
|
45
|
-
|
45
|
+
|
46
46
|
return result
|
47
|
-
|
47
|
+
|
48
48
|
@classmethod
|
49
49
|
def get_schema(cls) -> Dict[str, Any]:
|
50
50
|
"""Get JSON schema for result."""
|
51
51
|
return {
|
52
52
|
"type": "object",
|
53
53
|
"properties": {
|
54
|
-
"operation": {
|
55
|
-
"type": "string",
|
56
|
-
"description": "Operation performed"
|
57
|
-
},
|
54
|
+
"operation": {"type": "string", "description": "Operation performed"},
|
58
55
|
"success": {
|
59
56
|
"type": "boolean",
|
60
|
-
"description": "Whether operation was successful"
|
57
|
+
"description": "Whether operation was successful",
|
61
58
|
},
|
62
59
|
"server_key": {
|
63
60
|
"type": "string",
|
64
|
-
"description": "Server key for registered server"
|
61
|
+
"description": "Server key for registered server",
|
65
62
|
},
|
66
|
-
"message": {
|
67
|
-
|
68
|
-
"description": "Result message"
|
69
|
-
},
|
70
|
-
"details": {
|
71
|
-
"type": "object",
|
72
|
-
"description": "Additional details"
|
73
|
-
}
|
63
|
+
"message": {"type": "string", "description": "Result message"},
|
64
|
+
"details": {"type": "object", "description": "Additional details"},
|
74
65
|
},
|
75
|
-
"required": ["operation", "success", "message"]
|
66
|
+
"required": ["operation", "success", "message"],
|
76
67
|
}
|
77
68
|
|
78
69
|
|
79
70
|
class ProxyRegistrationCommand(Command):
|
80
71
|
"""Proxy registration command with security framework integration."""
|
81
|
-
|
72
|
+
|
82
73
|
name = "proxy_registration"
|
83
74
|
descr = "Proxy registration operations (register, unregister, heartbeat, discover)"
|
84
75
|
category = "proxy"
|
85
76
|
author = "Vasiliy Zdanovskiy"
|
86
77
|
email = "vasilyvz@gmail.com"
|
87
|
-
|
78
|
+
|
88
79
|
# In-memory registry for testing
|
89
80
|
_registry: Dict[str, Dict[str, Any]] = {}
|
90
81
|
_server_counter = 1
|
91
|
-
|
82
|
+
|
92
83
|
async def execute(self, **kwargs) -> ProxyRegistrationCommandResult:
|
93
84
|
"""
|
94
85
|
Execute proxy registration command.
|
95
|
-
|
86
|
+
|
96
87
|
Args:
|
97
88
|
operation: Operation to perform (register, unregister, heartbeat, discover)
|
98
89
|
server_id: Server ID for registration
|
@@ -108,39 +99,43 @@ class ProxyRegistrationCommand(Command):
|
|
108
99
|
copy_number: Copy number for unregistration
|
109
100
|
timestamp: Timestamp for heartbeat
|
110
101
|
status: Status for heartbeat
|
111
|
-
|
102
|
+
|
112
103
|
Returns:
|
113
104
|
ProxyRegistrationCommandResult
|
114
105
|
"""
|
115
106
|
operation = kwargs.get("operation", "register")
|
116
|
-
|
107
|
+
|
117
108
|
# Check user permissions
|
118
109
|
context = kwargs.get("context", {})
|
119
110
|
user_info = context.get("user", {})
|
120
111
|
user_permissions = user_info.get("permissions", [])
|
121
|
-
|
112
|
+
|
122
113
|
# Define required permissions for each operation
|
123
114
|
operation_permissions = {
|
124
115
|
"register": ["register"],
|
125
116
|
"unregister": ["unregister"],
|
126
117
|
"heartbeat": ["heartbeat"],
|
127
|
-
"discover": ["discover"]
|
118
|
+
"discover": ["discover"],
|
128
119
|
}
|
129
|
-
|
120
|
+
|
130
121
|
required_permissions = operation_permissions.get(operation, ["read"])
|
131
|
-
|
122
|
+
|
132
123
|
# Check if user has required permissions
|
133
|
-
logger.info(
|
124
|
+
logger.info(
|
125
|
+
f"Checking permissions: user_permissions={user_permissions}, required={required_permissions}"
|
126
|
+
)
|
134
127
|
if not self._check_permissions(user_permissions, required_permissions):
|
135
128
|
return ProxyRegistrationCommandResult(
|
136
129
|
operation=operation,
|
137
130
|
success=False,
|
138
|
-
message=f"Permission denied: {operation} requires {required_permissions}"
|
131
|
+
message=f"Permission denied: {operation} requires {required_permissions}",
|
139
132
|
)
|
140
|
-
|
133
|
+
|
141
134
|
logger.info(f"Executing proxy registration operation: {operation}")
|
142
|
-
logger.debug(
|
143
|
-
|
135
|
+
logger.debug(
|
136
|
+
f"User permissions: {user_permissions}, required: {required_permissions}"
|
137
|
+
)
|
138
|
+
|
144
139
|
if operation == "register":
|
145
140
|
return await self._handle_register(kwargs)
|
146
141
|
elif operation == "unregister":
|
@@ -153,10 +148,12 @@ class ProxyRegistrationCommand(Command):
|
|
153
148
|
return ProxyRegistrationCommandResult(
|
154
149
|
operation=operation,
|
155
150
|
success=False,
|
156
|
-
message=f"Unknown operation: {operation}"
|
151
|
+
message=f"Unknown operation: {operation}",
|
157
152
|
)
|
158
|
-
|
159
|
-
async def _handle_register(
|
153
|
+
|
154
|
+
async def _handle_register(
|
155
|
+
self, kwargs: Dict[str, Any]
|
156
|
+
) -> ProxyRegistrationCommandResult:
|
160
157
|
"""Handle registration operation."""
|
161
158
|
server_id = kwargs.get("server_id")
|
162
159
|
server_url = kwargs.get("server_url")
|
@@ -167,19 +164,21 @@ class ProxyRegistrationCommand(Command):
|
|
167
164
|
endpoints = kwargs.get("endpoints", {})
|
168
165
|
auth_method = kwargs.get("auth_method", "none")
|
169
166
|
security_enabled = kwargs.get("security_enabled", False)
|
170
|
-
|
167
|
+
|
171
168
|
if not server_id or not server_url:
|
172
169
|
return ProxyRegistrationCommandResult(
|
173
170
|
operation="register",
|
174
171
|
success=False,
|
175
|
-
message="Missing required parameters: server_id and server_url"
|
172
|
+
message="Missing required parameters: server_id and server_url",
|
176
173
|
)
|
177
|
-
|
174
|
+
|
178
175
|
# Check if server already exists
|
179
|
-
existing_servers = [
|
176
|
+
existing_servers = [
|
177
|
+
key for key in self._registry.keys() if key.startswith(server_id)
|
178
|
+
]
|
180
179
|
copy_number = len(existing_servers) + 1
|
181
180
|
server_key = f"{server_id}_{copy_number}"
|
182
|
-
|
181
|
+
|
183
182
|
# Create server record
|
184
183
|
server_record = {
|
185
184
|
"server_id": server_id,
|
@@ -193,13 +192,13 @@ class ProxyRegistrationCommand(Command):
|
|
193
192
|
"security_enabled": security_enabled,
|
194
193
|
"registered_at": int(time.time()),
|
195
194
|
"last_heartbeat": int(time.time()),
|
196
|
-
"status": "active"
|
195
|
+
"status": "active",
|
197
196
|
}
|
198
|
-
|
197
|
+
|
199
198
|
self._registry[server_key] = server_record
|
200
|
-
|
199
|
+
|
201
200
|
logger.info(f"Registered server: {server_key} at {server_url}")
|
202
|
-
|
201
|
+
|
203
202
|
return ProxyRegistrationCommandResult(
|
204
203
|
operation="register",
|
205
204
|
success=True,
|
@@ -208,62 +207,66 @@ class ProxyRegistrationCommand(Command):
|
|
208
207
|
details={
|
209
208
|
"server_id": server_id,
|
210
209
|
"copy_number": copy_number,
|
211
|
-
"registered_at": server_record["registered_at"]
|
212
|
-
}
|
210
|
+
"registered_at": server_record["registered_at"],
|
211
|
+
},
|
213
212
|
)
|
214
|
-
|
215
|
-
async def _handle_unregister(
|
213
|
+
|
214
|
+
async def _handle_unregister(
|
215
|
+
self, kwargs: Dict[str, Any]
|
216
|
+
) -> ProxyRegistrationCommandResult:
|
216
217
|
"""Handle unregistration operation."""
|
217
218
|
server_id = kwargs.get("server_id")
|
218
219
|
copy_number = kwargs.get("copy_number", 1)
|
219
|
-
|
220
|
+
|
220
221
|
if not server_id:
|
221
222
|
return ProxyRegistrationCommandResult(
|
222
223
|
operation="unregister",
|
223
224
|
success=False,
|
224
|
-
message="Missing required parameter: server_id"
|
225
|
+
message="Missing required parameter: server_id",
|
225
226
|
)
|
226
|
-
|
227
|
+
|
227
228
|
server_key = f"{server_id}_{copy_number}"
|
228
|
-
|
229
|
+
|
229
230
|
if server_key in self._registry:
|
230
231
|
del self._registry[server_key]
|
231
232
|
logger.info(f"Unregistered server: {server_key}")
|
232
|
-
|
233
|
+
|
233
234
|
return ProxyRegistrationCommandResult(
|
234
235
|
operation="unregister",
|
235
236
|
success=True,
|
236
237
|
message=f"Server unregistered successfully: {server_key}",
|
237
|
-
details={"unregistered": True}
|
238
|
+
details={"unregistered": True},
|
238
239
|
)
|
239
240
|
else:
|
240
241
|
return ProxyRegistrationCommandResult(
|
241
242
|
operation="unregister",
|
242
243
|
success=True,
|
243
244
|
message=f"Server not found in registry: {server_key}",
|
244
|
-
details={"unregistered": False}
|
245
|
+
details={"unregistered": False},
|
245
246
|
)
|
246
|
-
|
247
|
-
async def _handle_heartbeat(
|
247
|
+
|
248
|
+
async def _handle_heartbeat(
|
249
|
+
self, kwargs: Dict[str, Any]
|
250
|
+
) -> ProxyRegistrationCommandResult:
|
248
251
|
"""Handle heartbeat operation."""
|
249
252
|
server_id = kwargs.get("server_id")
|
250
253
|
server_key = kwargs.get("server_key")
|
251
254
|
timestamp = kwargs.get("timestamp", int(time.time()))
|
252
255
|
status = kwargs.get("status", "healthy")
|
253
|
-
|
256
|
+
|
254
257
|
if not server_key:
|
255
258
|
return ProxyRegistrationCommandResult(
|
256
259
|
operation="heartbeat",
|
257
260
|
success=False,
|
258
|
-
message="Missing required parameter: server_key"
|
261
|
+
message="Missing required parameter: server_key",
|
259
262
|
)
|
260
|
-
|
263
|
+
|
261
264
|
if server_key in self._registry:
|
262
265
|
self._registry[server_key]["last_heartbeat"] = timestamp
|
263
266
|
self._registry[server_key]["status"] = status
|
264
|
-
|
267
|
+
|
265
268
|
logger.debug(f"Heartbeat received for server: {server_key}")
|
266
|
-
|
269
|
+
|
267
270
|
return ProxyRegistrationCommandResult(
|
268
271
|
operation="heartbeat",
|
269
272
|
success=True,
|
@@ -271,21 +274,23 @@ class ProxyRegistrationCommand(Command):
|
|
271
274
|
details={
|
272
275
|
"server_key": server_key,
|
273
276
|
"timestamp": timestamp,
|
274
|
-
"status": status
|
275
|
-
}
|
277
|
+
"status": status,
|
278
|
+
},
|
276
279
|
)
|
277
280
|
else:
|
278
281
|
return ProxyRegistrationCommandResult(
|
279
282
|
operation="heartbeat",
|
280
283
|
success=False,
|
281
|
-
message=f"Server not found: {server_key}"
|
284
|
+
message=f"Server not found: {server_key}",
|
282
285
|
)
|
283
|
-
|
284
|
-
async def _handle_discover(
|
286
|
+
|
287
|
+
async def _handle_discover(
|
288
|
+
self, kwargs: Dict[str, Any]
|
289
|
+
) -> ProxyRegistrationCommandResult:
|
285
290
|
"""Handle discovery operation."""
|
286
291
|
# Return all registered servers
|
287
292
|
proxies = []
|
288
|
-
|
293
|
+
|
289
294
|
for server_key, server_record in self._registry.items():
|
290
295
|
# Check if server is active (heartbeat within last 5 minutes)
|
291
296
|
last_heartbeat = server_record.get("last_heartbeat", 0)
|
@@ -303,41 +308,43 @@ class ProxyRegistrationCommand(Command):
|
|
303
308
|
"security_enabled": server_record["security_enabled"],
|
304
309
|
"registered_at": server_record["registered_at"],
|
305
310
|
"last_heartbeat": server_record["last_heartbeat"],
|
306
|
-
"status": server_record["status"]
|
311
|
+
"status": server_record["status"],
|
307
312
|
}
|
308
313
|
proxies.append(proxy_info)
|
309
|
-
|
314
|
+
|
310
315
|
logger.info(f"Discovery request returned {len(proxies)} active servers")
|
311
|
-
|
316
|
+
|
312
317
|
return ProxyRegistrationCommandResult(
|
313
318
|
operation="discover",
|
314
319
|
success=True,
|
315
320
|
message=f"Found {len(proxies)} active proxy servers",
|
316
|
-
details={"proxies": proxies}
|
321
|
+
details={"proxies": proxies},
|
317
322
|
)
|
318
|
-
|
319
|
-
def _check_permissions(
|
323
|
+
|
324
|
+
def _check_permissions(
|
325
|
+
self, user_permissions: List[str], required_permissions: List[str]
|
326
|
+
) -> bool:
|
320
327
|
"""
|
321
328
|
Check if user has required permissions.
|
322
|
-
|
329
|
+
|
323
330
|
Args:
|
324
331
|
user_permissions: User's permissions
|
325
332
|
required_permissions: Required permissions
|
326
|
-
|
333
|
+
|
327
334
|
Returns:
|
328
335
|
True if user has required permissions
|
329
336
|
"""
|
330
337
|
# Admin has all permissions
|
331
338
|
if "*" in user_permissions:
|
332
339
|
return True
|
333
|
-
|
340
|
+
|
334
341
|
# Check if user has all required permissions
|
335
342
|
for required in required_permissions:
|
336
343
|
if required not in user_permissions:
|
337
344
|
return False
|
338
|
-
|
345
|
+
|
339
346
|
return True
|
340
|
-
|
347
|
+
|
341
348
|
@classmethod
|
342
349
|
def get_schema(cls) -> Dict[str, Any]:
|
343
350
|
"""Get JSON schema for command parameters."""
|
@@ -348,62 +355,47 @@ class ProxyRegistrationCommand(Command):
|
|
348
355
|
"type": "string",
|
349
356
|
"enum": ["register", "unregister", "heartbeat", "discover"],
|
350
357
|
"description": "Operation to perform",
|
351
|
-
"default": "register"
|
358
|
+
"default": "register",
|
352
359
|
},
|
353
360
|
"server_id": {
|
354
361
|
"type": "string",
|
355
|
-
"description": "Server ID for registration"
|
362
|
+
"description": "Server ID for registration",
|
356
363
|
},
|
357
364
|
"server_url": {
|
358
365
|
"type": "string",
|
359
|
-
"description": "Server URL for registration"
|
360
|
-
},
|
361
|
-
"server_name": {
|
362
|
-
"type": "string",
|
363
|
-
"description": "Server name"
|
364
|
-
},
|
365
|
-
"description": {
|
366
|
-
"type": "string",
|
367
|
-
"description": "Server description"
|
368
|
-
},
|
369
|
-
"version": {
|
370
|
-
"type": "string",
|
371
|
-
"description": "Server version"
|
366
|
+
"description": "Server URL for registration",
|
372
367
|
},
|
368
|
+
"server_name": {"type": "string", "description": "Server name"},
|
369
|
+
"description": {"type": "string", "description": "Server description"},
|
370
|
+
"version": {"type": "string", "description": "Server version"},
|
373
371
|
"capabilities": {
|
374
372
|
"type": "array",
|
375
373
|
"items": {"type": "string"},
|
376
|
-
"description": "Server capabilities"
|
377
|
-
},
|
378
|
-
"endpoints": {
|
379
|
-
"type": "object",
|
380
|
-
"description": "Server endpoints"
|
374
|
+
"description": "Server capabilities",
|
381
375
|
},
|
376
|
+
"endpoints": {"type": "object", "description": "Server endpoints"},
|
382
377
|
"auth_method": {
|
383
378
|
"type": "string",
|
384
|
-
"description": "Authentication method"
|
379
|
+
"description": "Authentication method",
|
385
380
|
},
|
386
381
|
"security_enabled": {
|
387
382
|
"type": "boolean",
|
388
|
-
"description": "Whether security is enabled"
|
383
|
+
"description": "Whether security is enabled",
|
389
384
|
},
|
390
385
|
"server_key": {
|
391
386
|
"type": "string",
|
392
|
-
"description": "Server key for unregistration/heartbeat"
|
387
|
+
"description": "Server key for unregistration/heartbeat",
|
393
388
|
},
|
394
389
|
"copy_number": {
|
395
390
|
"type": "integer",
|
396
|
-
"description": "Copy number for unregistration"
|
391
|
+
"description": "Copy number for unregistration",
|
397
392
|
},
|
398
393
|
"timestamp": {
|
399
394
|
"type": "integer",
|
400
|
-
"description": "Timestamp for heartbeat"
|
395
|
+
"description": "Timestamp for heartbeat",
|
401
396
|
},
|
402
|
-
"status": {
|
403
|
-
"type": "string",
|
404
|
-
"description": "Status for heartbeat"
|
405
|
-
}
|
397
|
+
"status": {"type": "string", "description": "Status for heartbeat"},
|
406
398
|
},
|
407
399
|
"required": ["operation"],
|
408
|
-
"additionalProperties": False
|
409
|
-
}
|
400
|
+
"additionalProperties": False,
|
401
|
+
}
|
@@ -16,7 +16,7 @@ class ReloadResult:
|
|
16
16
|
"""
|
17
17
|
Result of reload operation.
|
18
18
|
"""
|
19
|
-
|
19
|
+
|
20
20
|
def __init__(
|
21
21
|
self,
|
22
22
|
config_reloaded: bool,
|
@@ -27,11 +27,11 @@ class ReloadResult:
|
|
27
27
|
total_commands: int = 0,
|
28
28
|
server_restart_required: bool = True,
|
29
29
|
success: bool = True,
|
30
|
-
error_message: Optional[str] = None
|
30
|
+
error_message: Optional[str] = None,
|
31
31
|
):
|
32
32
|
"""
|
33
33
|
Initialize reload result.
|
34
|
-
|
34
|
+
|
35
35
|
Args:
|
36
36
|
config_reloaded: Whether configuration was reloaded successfully
|
37
37
|
builtin_commands: Number of built-in commands registered
|
@@ -51,11 +51,11 @@ class ReloadResult:
|
|
51
51
|
self.server_restart_required = server_restart_required
|
52
52
|
self.success = success
|
53
53
|
self.error_message = error_message
|
54
|
-
|
54
|
+
|
55
55
|
def to_dict(self) -> Dict[str, Any]:
|
56
56
|
"""
|
57
57
|
Convert result to dictionary.
|
58
|
-
|
58
|
+
|
59
59
|
Returns:
|
60
60
|
Dictionary representation of the result.
|
61
61
|
"""
|
@@ -69,9 +69,9 @@ class ReloadResult:
|
|
69
69
|
"total_commands": self.total_commands,
|
70
70
|
"server_restart_required": self.server_restart_required,
|
71
71
|
"message": "Server restart required to apply configuration changes",
|
72
|
-
"error_message": self.error_message
|
72
|
+
"error_message": self.error_message,
|
73
73
|
}
|
74
|
-
|
74
|
+
|
75
75
|
@classmethod
|
76
76
|
def get_schema(cls) -> Dict[str, Any]:
|
77
77
|
"""
|
@@ -85,49 +85,55 @@ class ReloadResult:
|
|
85
85
|
"properties": {
|
86
86
|
"success": {
|
87
87
|
"type": "boolean",
|
88
|
-
"description": "Whether reload was successful"
|
88
|
+
"description": "Whether reload was successful",
|
89
89
|
},
|
90
90
|
"config_reloaded": {
|
91
91
|
"type": "boolean",
|
92
|
-
"description": "Whether configuration was reloaded successfully"
|
92
|
+
"description": "Whether configuration was reloaded successfully",
|
93
93
|
},
|
94
94
|
"builtin_commands": {
|
95
95
|
"type": "integer",
|
96
|
-
"description": "Number of built-in commands registered"
|
96
|
+
"description": "Number of built-in commands registered",
|
97
97
|
},
|
98
98
|
"custom_commands": {
|
99
99
|
"type": "integer",
|
100
|
-
"description": "Number of custom commands registered"
|
100
|
+
"description": "Number of custom commands registered",
|
101
101
|
},
|
102
102
|
"loaded_commands": {
|
103
103
|
"type": "integer",
|
104
|
-
"description": "Number of commands loaded from directory"
|
104
|
+
"description": "Number of commands loaded from directory",
|
105
105
|
},
|
106
106
|
"remote_commands": {
|
107
107
|
"type": "integer",
|
108
|
-
"description": "Number of commands loaded from remote plugins"
|
108
|
+
"description": "Number of commands loaded from remote plugins",
|
109
109
|
},
|
110
110
|
"total_commands": {
|
111
111
|
"type": "integer",
|
112
|
-
"description": "Total number of commands after reload"
|
112
|
+
"description": "Total number of commands after reload",
|
113
113
|
},
|
114
114
|
"server_restart_required": {
|
115
115
|
"type": "boolean",
|
116
|
-
"description": "Whether server restart is required to apply changes"
|
116
|
+
"description": "Whether server restart is required to apply changes",
|
117
117
|
},
|
118
118
|
"message": {
|
119
119
|
"type": "string",
|
120
|
-
"description": "Information message about the reload operation"
|
120
|
+
"description": "Information message about the reload operation",
|
121
121
|
},
|
122
122
|
"error_message": {
|
123
123
|
"type": ["string", "null"],
|
124
|
-
"description": "Error message if reload failed"
|
125
|
-
}
|
124
|
+
"description": "Error message if reload failed",
|
125
|
+
},
|
126
126
|
},
|
127
127
|
"required": [
|
128
|
-
"success",
|
129
|
-
"
|
130
|
-
|
128
|
+
"success",
|
129
|
+
"config_reloaded",
|
130
|
+
"builtin_commands",
|
131
|
+
"custom_commands",
|
132
|
+
"loaded_commands",
|
133
|
+
"remote_commands",
|
134
|
+
"total_commands",
|
135
|
+
"server_restart_required",
|
136
|
+
],
|
131
137
|
}
|
132
138
|
|
133
139
|
|
@@ -136,30 +142,30 @@ class ReloadCommand(Command):
|
|
136
142
|
Command for reloading configuration and rediscovering commands.
|
137
143
|
Uses the unified initialization logic.
|
138
144
|
"""
|
139
|
-
|
145
|
+
|
140
146
|
name = "reload"
|
141
|
-
|
147
|
+
|
142
148
|
async def execute(self, **params) -> ReloadResult:
|
143
149
|
"""
|
144
150
|
Execute reload command.
|
145
|
-
|
151
|
+
|
146
152
|
Args:
|
147
153
|
**params: Command parameters (config_path)
|
148
|
-
|
154
|
+
|
149
155
|
Returns:
|
150
156
|
ReloadResult with reload information
|
151
157
|
"""
|
152
158
|
try:
|
153
159
|
logger.info("🔄 Starting configuration and commands reload...")
|
154
|
-
|
160
|
+
|
155
161
|
# Get config path from parameters
|
156
162
|
config_path = params.get("config_path")
|
157
163
|
if not config_path:
|
158
164
|
logger.warning("No config_path provided, using default configuration")
|
159
|
-
|
165
|
+
|
160
166
|
# Perform reload using unified initialization
|
161
167
|
reload_info = registry.reload_system(config_path=config_path)
|
162
|
-
|
168
|
+
|
163
169
|
# Create result
|
164
170
|
result = ReloadResult(
|
165
171
|
config_reloaded=reload_info.get("config_reloaded", False),
|
@@ -169,12 +175,12 @@ class ReloadCommand(Command):
|
|
169
175
|
remote_commands=reload_info.get("remote_commands", 0),
|
170
176
|
total_commands=reload_info.get("total_commands", 0),
|
171
177
|
server_restart_required=True, # Default to True as per tests
|
172
|
-
success=True
|
178
|
+
success=True,
|
173
179
|
)
|
174
|
-
|
180
|
+
|
175
181
|
logger.info(f"✅ Reload completed successfully: {result.to_dict()}")
|
176
182
|
return result
|
177
|
-
|
183
|
+
|
178
184
|
except Exception as e:
|
179
185
|
logger.error(f"❌ Reload failed: {str(e)}")
|
180
186
|
return ReloadResult(
|
@@ -186,14 +192,14 @@ class ReloadCommand(Command):
|
|
186
192
|
total_commands=0,
|
187
193
|
server_restart_required=False,
|
188
194
|
success=False,
|
189
|
-
error_message=str(e)
|
195
|
+
error_message=str(e),
|
190
196
|
)
|
191
|
-
|
197
|
+
|
192
198
|
@classmethod
|
193
199
|
def get_schema(cls) -> Dict[str, Any]:
|
194
200
|
"""
|
195
201
|
Get JSON schema for command parameters.
|
196
|
-
|
202
|
+
|
197
203
|
Returns:
|
198
204
|
JSON schema dictionary.
|
199
205
|
"""
|
@@ -203,8 +209,8 @@ class ReloadCommand(Command):
|
|
203
209
|
"config_path": {
|
204
210
|
"type": "string",
|
205
211
|
"description": "Path to configuration file to reload",
|
206
|
-
"default": None
|
212
|
+
"default": None,
|
207
213
|
}
|
208
214
|
},
|
209
|
-
"additionalProperties": False
|
210
|
-
}
|
215
|
+
"additionalProperties": False,
|
216
|
+
}
|