mcp-proxy-adapter 3.1.6__py3-none-any.whl → 4.0.0__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/api/app.py +65 -27
- mcp_proxy_adapter/api/handlers.py +1 -1
- mcp_proxy_adapter/api/middleware/error_handling.py +11 -10
- mcp_proxy_adapter/api/tool_integration.py +5 -2
- mcp_proxy_adapter/api/tools.py +3 -3
- mcp_proxy_adapter/commands/base.py +19 -1
- mcp_proxy_adapter/commands/command_registry.py +243 -6
- mcp_proxy_adapter/commands/hooks.py +260 -0
- mcp_proxy_adapter/commands/reload_command.py +211 -0
- mcp_proxy_adapter/commands/reload_settings_command.py +125 -0
- mcp_proxy_adapter/commands/settings_command.py +189 -0
- mcp_proxy_adapter/config.py +16 -1
- mcp_proxy_adapter/core/__init__.py +44 -0
- mcp_proxy_adapter/core/logging.py +87 -34
- mcp_proxy_adapter/core/settings.py +376 -0
- mcp_proxy_adapter/core/utils.py +2 -2
- mcp_proxy_adapter/custom_openapi.py +81 -2
- mcp_proxy_adapter/examples/README.md +124 -0
- mcp_proxy_adapter/examples/__init__.py +7 -0
- mcp_proxy_adapter/examples/basic_server/README.md +60 -0
- mcp_proxy_adapter/examples/basic_server/__init__.py +7 -0
- mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +39 -0
- mcp_proxy_adapter/examples/basic_server/config.json +35 -0
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +238 -0
- mcp_proxy_adapter/examples/basic_server/server.py +98 -0
- mcp_proxy_adapter/examples/custom_commands/README.md +127 -0
- mcp_proxy_adapter/examples/custom_commands/__init__.py +27 -0
- mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +250 -0
- mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +6 -0
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +103 -0
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +111 -0
- mcp_proxy_adapter/examples/custom_commands/config.json +62 -0
- mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +169 -0
- mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +215 -0
- mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +76 -0
- mcp_proxy_adapter/examples/custom_commands/custom_settings.json +96 -0
- mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +241 -0
- mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +135 -0
- mcp_proxy_adapter/examples/custom_commands/echo_command.py +122 -0
- mcp_proxy_adapter/examples/custom_commands/hooks.py +230 -0
- mcp_proxy_adapter/examples/custom_commands/intercept_command.py +123 -0
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +103 -0
- mcp_proxy_adapter/examples/custom_commands/server.py +223 -0
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +176 -0
- mcp_proxy_adapter/examples/deployment/README.md +49 -0
- mcp_proxy_adapter/examples/deployment/__init__.py +7 -0
- mcp_proxy_adapter/examples/deployment/config.development.json +8 -0
- {examples/basic_example → mcp_proxy_adapter/examples/deployment}/config.json +11 -7
- mcp_proxy_adapter/examples/deployment/config.production.json +12 -0
- mcp_proxy_adapter/examples/deployment/config.staging.json +11 -0
- mcp_proxy_adapter/examples/deployment/docker-compose.yml +31 -0
- mcp_proxy_adapter/examples/deployment/run.sh +43 -0
- mcp_proxy_adapter/examples/deployment/run_docker.sh +84 -0
- mcp_proxy_adapter/openapi.py +3 -2
- mcp_proxy_adapter/tests/api/test_custom_openapi.py +617 -0
- mcp_proxy_adapter/tests/api/test_handlers.py +522 -0
- mcp_proxy_adapter/tests/api/test_schemas.py +546 -0
- mcp_proxy_adapter/tests/api/test_tool_integration.py +531 -0
- mcp_proxy_adapter/tests/unit/test_base_command.py +391 -85
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-4.0.0.dist-info/RECORD +110 -0
- {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/WHEEL +1 -1
- {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/top_level.txt +0 -1
- examples/__init__.py +0 -19
- examples/anti_patterns/README.md +0 -51
- examples/anti_patterns/__init__.py +0 -9
- examples/anti_patterns/bad_design/README.md +0 -72
- examples/anti_patterns/bad_design/global_state.py +0 -170
- examples/anti_patterns/bad_design/monolithic_command.py +0 -272
- examples/basic_example/README.md +0 -245
- examples/basic_example/__init__.py +0 -8
- examples/basic_example/commands/__init__.py +0 -5
- examples/basic_example/commands/echo_command.py +0 -95
- examples/basic_example/commands/math_command.py +0 -151
- examples/basic_example/commands/time_command.py +0 -152
- examples/basic_example/docs/EN/README.md +0 -177
- examples/basic_example/docs/RU/README.md +0 -177
- examples/basic_example/server.py +0 -151
- examples/basic_example/tests/conftest.py +0 -243
- examples/check_vstl_schema.py +0 -106
- examples/commands/echo_command.py +0 -52
- examples/commands/echo_command_di.py +0 -152
- examples/commands/echo_result.py +0 -65
- examples/commands/get_date_command.py +0 -98
- examples/commands/new_uuid4_command.py +0 -91
- examples/complete_example/Dockerfile +0 -24
- examples/complete_example/README.md +0 -92
- examples/complete_example/__init__.py +0 -8
- examples/complete_example/commands/__init__.py +0 -5
- examples/complete_example/commands/system_command.py +0 -328
- examples/complete_example/config.json +0 -41
- examples/complete_example/configs/config.dev.yaml +0 -40
- examples/complete_example/configs/config.docker.yaml +0 -40
- examples/complete_example/docker-compose.yml +0 -35
- examples/complete_example/requirements.txt +0 -20
- examples/complete_example/server.py +0 -113
- examples/di_example/.pytest_cache/README.md +0 -8
- examples/di_example/server.py +0 -249
- examples/fix_vstl_help.py +0 -123
- examples/minimal_example/README.md +0 -65
- examples/minimal_example/__init__.py +0 -8
- examples/minimal_example/config.json +0 -14
- examples/minimal_example/main.py +0 -136
- examples/minimal_example/simple_server.py +0 -163
- examples/minimal_example/tests/conftest.py +0 -171
- examples/minimal_example/tests/test_hello_command.py +0 -111
- examples/minimal_example/tests/test_integration.py +0 -181
- examples/patch_vstl_service.py +0 -105
- examples/patch_vstl_service_mcp.py +0 -108
- examples/server.py +0 -69
- examples/simple_server.py +0 -128
- examples/test_package_3.1.4.py +0 -177
- examples/test_server.py +0 -134
- examples/tool_description_example.py +0 -82
- mcp_proxy_adapter/py.typed +0 -0
- mcp_proxy_adapter-3.1.6.dist-info/RECORD +0 -118
- {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,260 @@
|
|
1
|
+
"""
|
2
|
+
Module for command execution hooks.
|
3
|
+
|
4
|
+
This module provides a hook system that allows intercepting command execution
|
5
|
+
before and after the actual command runs.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
9
|
+
from dataclasses import dataclass
|
10
|
+
from enum import Enum
|
11
|
+
|
12
|
+
from mcp_proxy_adapter.core.logging import logger
|
13
|
+
|
14
|
+
|
15
|
+
class HookType(Enum):
|
16
|
+
"""Types of hooks."""
|
17
|
+
BEFORE_EXECUTION = "before_execution"
|
18
|
+
AFTER_EXECUTION = "after_execution"
|
19
|
+
|
20
|
+
|
21
|
+
@dataclass
|
22
|
+
class HookContext:
|
23
|
+
"""Context passed to hook functions."""
|
24
|
+
command_name: str
|
25
|
+
params: Dict[str, Any]
|
26
|
+
hook_type: HookType
|
27
|
+
standard_processing: bool = True
|
28
|
+
result: Optional[Any] = None
|
29
|
+
|
30
|
+
|
31
|
+
class CommandHooks:
|
32
|
+
"""
|
33
|
+
Manages command execution hooks.
|
34
|
+
"""
|
35
|
+
|
36
|
+
def __init__(self):
|
37
|
+
"""Initialize hooks manager."""
|
38
|
+
self._before_hooks: Dict[str, List[Callable]] = {}
|
39
|
+
self._after_hooks: Dict[str, List[Callable]] = {}
|
40
|
+
self._global_before_hooks: List[Callable] = []
|
41
|
+
self._global_after_hooks: List[Callable] = []
|
42
|
+
|
43
|
+
def register_before_hook(self, command_name: str, hook: Callable[[HookContext], None]) -> None:
|
44
|
+
"""
|
45
|
+
Register a hook to be executed before command execution.
|
46
|
+
|
47
|
+
Args:
|
48
|
+
command_name: Name of the command to hook into
|
49
|
+
hook: Hook function that takes HookContext as parameter
|
50
|
+
"""
|
51
|
+
if command_name not in self._before_hooks:
|
52
|
+
self._before_hooks[command_name] = []
|
53
|
+
self._before_hooks[command_name].append(hook)
|
54
|
+
logger.debug(f"Registered before hook for command: {command_name}")
|
55
|
+
|
56
|
+
def register_after_hook(self, command_name: str, hook: Callable[[HookContext], None]) -> None:
|
57
|
+
"""
|
58
|
+
Register a hook to be executed after command execution.
|
59
|
+
|
60
|
+
Args:
|
61
|
+
command_name: Name of the command to hook into
|
62
|
+
hook: Hook function that takes HookContext as parameter
|
63
|
+
"""
|
64
|
+
if command_name not in self._after_hooks:
|
65
|
+
self._after_hooks[command_name] = []
|
66
|
+
self._after_hooks[command_name].append(hook)
|
67
|
+
logger.debug(f"Registered after hook for command: {command_name}")
|
68
|
+
|
69
|
+
def register_global_before_hook(self, hook: Callable[[HookContext], None]) -> None:
|
70
|
+
"""
|
71
|
+
Register a global hook to be executed before any command.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
hook: Hook function that takes HookContext as parameter
|
75
|
+
"""
|
76
|
+
self._global_before_hooks.append(hook)
|
77
|
+
logger.debug("Registered global before hook")
|
78
|
+
|
79
|
+
def register_global_after_hook(self, hook: Callable[[HookContext], None]) -> None:
|
80
|
+
"""
|
81
|
+
Register a global hook to be executed after any command.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
hook: Hook function that takes HookContext as parameter
|
85
|
+
"""
|
86
|
+
self._global_after_hooks.append(hook)
|
87
|
+
logger.debug("Registered global after hook")
|
88
|
+
|
89
|
+
def unregister_before_hook(self, command_name: str, hook: Callable[[HookContext], None]) -> None:
|
90
|
+
"""
|
91
|
+
Unregister a before hook for a specific command.
|
92
|
+
|
93
|
+
Args:
|
94
|
+
command_name: Name of the command
|
95
|
+
hook: Hook function to unregister
|
96
|
+
"""
|
97
|
+
if command_name in self._before_hooks:
|
98
|
+
try:
|
99
|
+
self._before_hooks[command_name].remove(hook)
|
100
|
+
logger.debug(f"Unregistered before hook for command: {command_name}")
|
101
|
+
# Remove the command key if no hooks remain
|
102
|
+
if not self._before_hooks[command_name]:
|
103
|
+
del self._before_hooks[command_name]
|
104
|
+
except ValueError:
|
105
|
+
logger.warning(f"Hook not found for command: {command_name}")
|
106
|
+
|
107
|
+
def unregister_after_hook(self, command_name: str, hook: Callable[[HookContext], None]) -> None:
|
108
|
+
"""
|
109
|
+
Unregister an after hook for a specific command.
|
110
|
+
|
111
|
+
Args:
|
112
|
+
command_name: Name of the command
|
113
|
+
hook: Hook function to unregister
|
114
|
+
"""
|
115
|
+
if command_name in self._after_hooks:
|
116
|
+
try:
|
117
|
+
self._after_hooks[command_name].remove(hook)
|
118
|
+
logger.debug(f"Unregistered after hook for command: {command_name}")
|
119
|
+
# Remove the command key if no hooks remain
|
120
|
+
if not self._after_hooks[command_name]:
|
121
|
+
del self._after_hooks[command_name]
|
122
|
+
except ValueError:
|
123
|
+
logger.warning(f"Hook not found for command: {command_name}")
|
124
|
+
|
125
|
+
def unregister_global_before_hook(self, hook: Callable[[HookContext], None]) -> None:
|
126
|
+
"""
|
127
|
+
Unregister a global before hook.
|
128
|
+
|
129
|
+
Args:
|
130
|
+
hook: Hook function to unregister
|
131
|
+
"""
|
132
|
+
try:
|
133
|
+
self._global_before_hooks.remove(hook)
|
134
|
+
logger.debug("Unregistered global before hook")
|
135
|
+
except ValueError:
|
136
|
+
logger.warning("Global before hook not found")
|
137
|
+
|
138
|
+
def unregister_global_after_hook(self, hook: Callable[[HookContext], None]) -> None:
|
139
|
+
"""
|
140
|
+
Unregister a global after hook.
|
141
|
+
|
142
|
+
Args:
|
143
|
+
hook: Hook function to unregister
|
144
|
+
"""
|
145
|
+
try:
|
146
|
+
self._global_after_hooks.remove(hook)
|
147
|
+
logger.debug("Unregistered global after hook")
|
148
|
+
except ValueError:
|
149
|
+
logger.warning("Global after hook not found")
|
150
|
+
|
151
|
+
def execute_before_hooks(self, command_name: str, params: Dict[str, Any]) -> HookContext:
|
152
|
+
"""
|
153
|
+
Execute all before hooks for a command.
|
154
|
+
|
155
|
+
Args:
|
156
|
+
command_name: Name of the command
|
157
|
+
params: Command parameters
|
158
|
+
|
159
|
+
Returns:
|
160
|
+
HookContext with execution results
|
161
|
+
"""
|
162
|
+
context = HookContext(
|
163
|
+
command_name=command_name,
|
164
|
+
params=params,
|
165
|
+
hook_type=HookType.BEFORE_EXECUTION
|
166
|
+
)
|
167
|
+
|
168
|
+
# Execute global before hooks
|
169
|
+
for hook in self._global_before_hooks:
|
170
|
+
try:
|
171
|
+
hook(context)
|
172
|
+
except Exception as e:
|
173
|
+
logger.error(f"Error in global before hook for command {command_name}: {e}")
|
174
|
+
|
175
|
+
# Execute command-specific before hooks
|
176
|
+
if command_name in self._before_hooks:
|
177
|
+
for hook in self._before_hooks[command_name]:
|
178
|
+
try:
|
179
|
+
hook(context)
|
180
|
+
except Exception as e:
|
181
|
+
logger.error(f"Error in before hook for command {command_name}: {e}")
|
182
|
+
|
183
|
+
return context
|
184
|
+
|
185
|
+
def execute_after_hooks(self, command_name: str, params: Dict[str, Any], result: Any) -> HookContext:
|
186
|
+
"""
|
187
|
+
Execute all after hooks for a command.
|
188
|
+
|
189
|
+
Args:
|
190
|
+
command_name: Name of the command
|
191
|
+
params: Command parameters
|
192
|
+
result: Command execution result
|
193
|
+
|
194
|
+
Returns:
|
195
|
+
HookContext with execution results
|
196
|
+
"""
|
197
|
+
context = HookContext(
|
198
|
+
command_name=command_name,
|
199
|
+
params=params,
|
200
|
+
hook_type=HookType.AFTER_EXECUTION,
|
201
|
+
result=result
|
202
|
+
)
|
203
|
+
|
204
|
+
# Execute command-specific after hooks
|
205
|
+
if command_name in self._after_hooks:
|
206
|
+
for hook in self._after_hooks[command_name]:
|
207
|
+
try:
|
208
|
+
hook(context)
|
209
|
+
except Exception as e:
|
210
|
+
logger.error(f"Error in after hook for command {command_name}: {e}")
|
211
|
+
|
212
|
+
# Execute global after hooks
|
213
|
+
for hook in self._global_after_hooks:
|
214
|
+
try:
|
215
|
+
hook(context)
|
216
|
+
except Exception as e:
|
217
|
+
logger.error(f"Error in global after hook for command {command_name}: {e}")
|
218
|
+
|
219
|
+
return context
|
220
|
+
|
221
|
+
def clear_hooks(self, command_name: Optional[str] = None) -> None:
|
222
|
+
"""
|
223
|
+
Clear all hooks or hooks for a specific command.
|
224
|
+
|
225
|
+
Args:
|
226
|
+
command_name: If provided, clear hooks only for this command.
|
227
|
+
If None, clear all hooks.
|
228
|
+
"""
|
229
|
+
if command_name is None:
|
230
|
+
# Clear all hooks
|
231
|
+
self._before_hooks.clear()
|
232
|
+
self._after_hooks.clear()
|
233
|
+
self._global_before_hooks.clear()
|
234
|
+
self._global_after_hooks.clear()
|
235
|
+
logger.debug("Cleared all hooks")
|
236
|
+
else:
|
237
|
+
# Clear hooks for specific command
|
238
|
+
if command_name in self._before_hooks:
|
239
|
+
del self._before_hooks[command_name]
|
240
|
+
if command_name in self._after_hooks:
|
241
|
+
del self._after_hooks[command_name]
|
242
|
+
logger.debug(f"Cleared hooks for command: {command_name}")
|
243
|
+
|
244
|
+
def get_hook_info(self) -> Dict[str, Any]:
|
245
|
+
"""
|
246
|
+
Get information about registered hooks.
|
247
|
+
|
248
|
+
Returns:
|
249
|
+
Dictionary with hook information
|
250
|
+
"""
|
251
|
+
return {
|
252
|
+
"before_hooks": {cmd: len(hooks) for cmd, hooks in self._before_hooks.items()},
|
253
|
+
"after_hooks": {cmd: len(hooks) for cmd, hooks in self._after_hooks.items()},
|
254
|
+
"global_before_hooks": len(self._global_before_hooks),
|
255
|
+
"global_after_hooks": len(self._global_after_hooks)
|
256
|
+
}
|
257
|
+
|
258
|
+
|
259
|
+
# Global hooks instance
|
260
|
+
hooks = CommandHooks()
|
@@ -0,0 +1,211 @@
|
|
1
|
+
"""
|
2
|
+
Reload command for configuration and command discovery.
|
3
|
+
|
4
|
+
This command allows reloading configuration and rediscovering commands
|
5
|
+
without restarting the server.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Any, Dict, Optional
|
9
|
+
|
10
|
+
from mcp_proxy_adapter.commands.base import Command
|
11
|
+
from mcp_proxy_adapter.commands.command_registry import registry
|
12
|
+
from mcp_proxy_adapter.core.logging import logger
|
13
|
+
|
14
|
+
|
15
|
+
class ReloadResult:
|
16
|
+
"""
|
17
|
+
Result of reload operation.
|
18
|
+
"""
|
19
|
+
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
config_reloaded: bool,
|
23
|
+
commands_discovered: int,
|
24
|
+
custom_commands_preserved: int,
|
25
|
+
total_commands: int,
|
26
|
+
built_in_commands: int,
|
27
|
+
custom_commands: int,
|
28
|
+
server_restart_required: bool = True,
|
29
|
+
success: bool = True,
|
30
|
+
error_message: Optional[str] = None
|
31
|
+
):
|
32
|
+
"""
|
33
|
+
Initialize reload result.
|
34
|
+
|
35
|
+
Args:
|
36
|
+
config_reloaded: Whether configuration was reloaded successfully
|
37
|
+
commands_discovered: Number of commands discovered
|
38
|
+
custom_commands_preserved: Number of custom commands preserved
|
39
|
+
total_commands: Total number of commands after reload
|
40
|
+
built_in_commands: Number of built-in commands
|
41
|
+
custom_commands: Number of custom commands
|
42
|
+
server_restart_required: Whether server restart is required
|
43
|
+
success: Whether reload was successful
|
44
|
+
error_message: Error message if reload failed
|
45
|
+
"""
|
46
|
+
self.config_reloaded = config_reloaded
|
47
|
+
self.commands_discovered = commands_discovered
|
48
|
+
self.custom_commands_preserved = custom_commands_preserved
|
49
|
+
self.total_commands = total_commands
|
50
|
+
self.built_in_commands = built_in_commands
|
51
|
+
self.custom_commands = custom_commands
|
52
|
+
self.server_restart_required = server_restart_required
|
53
|
+
self.success = success
|
54
|
+
self.error_message = error_message
|
55
|
+
|
56
|
+
def to_dict(self) -> Dict[str, Any]:
|
57
|
+
"""
|
58
|
+
Convert result to dictionary.
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
Dictionary representation of the result.
|
62
|
+
"""
|
63
|
+
return {
|
64
|
+
"success": self.success,
|
65
|
+
"config_reloaded": self.config_reloaded,
|
66
|
+
"commands_discovered": self.commands_discovered,
|
67
|
+
"custom_commands_preserved": self.custom_commands_preserved,
|
68
|
+
"total_commands": self.total_commands,
|
69
|
+
"built_in_commands": self.built_in_commands,
|
70
|
+
"custom_commands": self.custom_commands,
|
71
|
+
"server_restart_required": self.server_restart_required,
|
72
|
+
"message": "Server restart required to apply configuration changes",
|
73
|
+
"error_message": self.error_message
|
74
|
+
}
|
75
|
+
|
76
|
+
def get_schema(self) -> Dict[str, Any]:
|
77
|
+
"""
|
78
|
+
Get JSON schema for the result.
|
79
|
+
|
80
|
+
Returns:
|
81
|
+
JSON schema dictionary.
|
82
|
+
"""
|
83
|
+
return {
|
84
|
+
"type": "object",
|
85
|
+
"properties": {
|
86
|
+
"success": {
|
87
|
+
"type": "boolean",
|
88
|
+
"description": "Whether reload was successful"
|
89
|
+
},
|
90
|
+
"config_reloaded": {
|
91
|
+
"type": "boolean",
|
92
|
+
"description": "Whether configuration was reloaded successfully"
|
93
|
+
},
|
94
|
+
"commands_discovered": {
|
95
|
+
"type": "integer",
|
96
|
+
"description": "Number of commands discovered"
|
97
|
+
},
|
98
|
+
"custom_commands_preserved": {
|
99
|
+
"type": "integer",
|
100
|
+
"description": "Number of custom commands preserved"
|
101
|
+
},
|
102
|
+
"total_commands": {
|
103
|
+
"type": "integer",
|
104
|
+
"description": "Total number of commands after reload"
|
105
|
+
},
|
106
|
+
"built_in_commands": {
|
107
|
+
"type": "integer",
|
108
|
+
"description": "Number of built-in commands"
|
109
|
+
},
|
110
|
+
"custom_commands": {
|
111
|
+
"type": "integer",
|
112
|
+
"description": "Number of custom commands"
|
113
|
+
},
|
114
|
+
"server_restart_required": {
|
115
|
+
"type": "boolean",
|
116
|
+
"description": "Whether server restart is required to apply changes"
|
117
|
+
},
|
118
|
+
"message": {
|
119
|
+
"type": "string",
|
120
|
+
"description": "Information message about the reload operation"
|
121
|
+
},
|
122
|
+
"error_message": {
|
123
|
+
"type": ["string", "null"],
|
124
|
+
"description": "Error message if reload failed"
|
125
|
+
}
|
126
|
+
},
|
127
|
+
"required": [
|
128
|
+
"success", "config_reloaded", "commands_discovered",
|
129
|
+
"custom_commands_preserved", "total_commands",
|
130
|
+
"built_in_commands", "custom_commands", "server_restart_required"
|
131
|
+
]
|
132
|
+
}
|
133
|
+
|
134
|
+
|
135
|
+
class ReloadCommand(Command):
|
136
|
+
"""
|
137
|
+
Command for reloading configuration and rediscovering commands.
|
138
|
+
Note: This command will trigger a server restart to apply configuration changes.
|
139
|
+
"""
|
140
|
+
|
141
|
+
name = "reload"
|
142
|
+
|
143
|
+
async def execute(self, **params) -> ReloadResult:
|
144
|
+
"""
|
145
|
+
Execute reload command.
|
146
|
+
|
147
|
+
Args:
|
148
|
+
**params: Command parameters (currently unused)
|
149
|
+
|
150
|
+
Returns:
|
151
|
+
ReloadResult with reload information
|
152
|
+
"""
|
153
|
+
try:
|
154
|
+
logger.info("🔄 Starting configuration and commands reload...")
|
155
|
+
|
156
|
+
# Perform reload
|
157
|
+
reload_info = registry.reload_config_and_commands()
|
158
|
+
|
159
|
+
# Create result
|
160
|
+
result = ReloadResult(
|
161
|
+
config_reloaded=reload_info.get("config_reloaded", False),
|
162
|
+
commands_discovered=reload_info.get("commands_discovered", 0),
|
163
|
+
custom_commands_preserved=reload_info.get("custom_commands_preserved", 0),
|
164
|
+
total_commands=reload_info.get("total_commands", 0),
|
165
|
+
built_in_commands=reload_info.get("built_in_commands", 0),
|
166
|
+
custom_commands=reload_info.get("custom_commands", 0),
|
167
|
+
server_restart_required=True,
|
168
|
+
success=True
|
169
|
+
)
|
170
|
+
|
171
|
+
logger.info(f"✅ Reload completed successfully: {result.to_dict()}")
|
172
|
+
return result
|
173
|
+
|
174
|
+
except Exception as e:
|
175
|
+
logger.error(f"❌ Reload failed: {str(e)}")
|
176
|
+
return ReloadResult(
|
177
|
+
config_reloaded=False,
|
178
|
+
commands_discovered=0,
|
179
|
+
custom_commands_preserved=0,
|
180
|
+
total_commands=0,
|
181
|
+
built_in_commands=0,
|
182
|
+
custom_commands=0,
|
183
|
+
server_restart_required=False,
|
184
|
+
success=False,
|
185
|
+
error_message=str(e)
|
186
|
+
)
|
187
|
+
|
188
|
+
@classmethod
|
189
|
+
def get_schema(cls) -> Dict[str, Any]:
|
190
|
+
"""
|
191
|
+
Get JSON schema for command parameters.
|
192
|
+
|
193
|
+
Returns:
|
194
|
+
JSON schema dictionary.
|
195
|
+
"""
|
196
|
+
return {
|
197
|
+
"type": "object",
|
198
|
+
"properties": {
|
199
|
+
"package_path": {
|
200
|
+
"type": "string",
|
201
|
+
"description": "Path to package with commands to discover",
|
202
|
+
"default": "mcp_proxy_adapter.commands"
|
203
|
+
},
|
204
|
+
"force_restart": {
|
205
|
+
"type": "boolean",
|
206
|
+
"description": "Force server restart to apply configuration changes",
|
207
|
+
"default": True
|
208
|
+
}
|
209
|
+
},
|
210
|
+
"additionalProperties": False
|
211
|
+
}
|
@@ -0,0 +1,125 @@
|
|
1
|
+
"""
|
2
|
+
Reload Settings Command
|
3
|
+
|
4
|
+
This command allows reloading configuration settings from files and environment variables.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Dict, Any
|
8
|
+
from mcp_proxy_adapter.commands.base import Command
|
9
|
+
from mcp_proxy_adapter.core.settings import reload_settings, get_custom_settings
|
10
|
+
from mcp_proxy_adapter.core.logging import get_logger
|
11
|
+
|
12
|
+
|
13
|
+
class ReloadSettingsResult:
|
14
|
+
"""
|
15
|
+
Result class for reload settings command.
|
16
|
+
"""
|
17
|
+
|
18
|
+
def __init__(
|
19
|
+
self,
|
20
|
+
success: bool,
|
21
|
+
message: str,
|
22
|
+
custom_settings: Dict[str, Any] = None,
|
23
|
+
error_message: str = None
|
24
|
+
):
|
25
|
+
self.success = success
|
26
|
+
self.message = message
|
27
|
+
self.custom_settings = custom_settings or {}
|
28
|
+
self.error_message = error_message
|
29
|
+
|
30
|
+
def to_dict(self) -> Dict[str, Any]:
|
31
|
+
"""Convert result to dictionary."""
|
32
|
+
result = {
|
33
|
+
"success": self.success,
|
34
|
+
"message": self.message,
|
35
|
+
"custom_settings": self.custom_settings
|
36
|
+
}
|
37
|
+
if self.error_message:
|
38
|
+
result["error_message"] = self.error_message
|
39
|
+
return result
|
40
|
+
|
41
|
+
def get_schema(self) -> Dict[str, Any]:
|
42
|
+
"""Get JSON schema for the result."""
|
43
|
+
return {
|
44
|
+
"type": "object",
|
45
|
+
"properties": {
|
46
|
+
"success": {
|
47
|
+
"type": "boolean",
|
48
|
+
"description": "Whether the operation was successful"
|
49
|
+
},
|
50
|
+
"message": {
|
51
|
+
"type": "string",
|
52
|
+
"description": "Operation result message"
|
53
|
+
},
|
54
|
+
"custom_settings": {
|
55
|
+
"type": "object",
|
56
|
+
"description": "Current custom settings after reload",
|
57
|
+
"additionalProperties": True
|
58
|
+
},
|
59
|
+
"error_message": {
|
60
|
+
"type": "string",
|
61
|
+
"description": "Error message if operation failed"
|
62
|
+
}
|
63
|
+
},
|
64
|
+
"required": ["success", "message", "custom_settings"]
|
65
|
+
}
|
66
|
+
|
67
|
+
|
68
|
+
class ReloadSettingsCommand(Command):
|
69
|
+
"""
|
70
|
+
Command to reload configuration settings.
|
71
|
+
"""
|
72
|
+
|
73
|
+
name = "reload_settings"
|
74
|
+
description = "Reload configuration settings from files and environment variables"
|
75
|
+
|
76
|
+
async def execute(self, **params) -> ReloadSettingsResult:
|
77
|
+
"""
|
78
|
+
Execute the reload settings command.
|
79
|
+
|
80
|
+
Args:
|
81
|
+
**params: Command parameters (not used)
|
82
|
+
|
83
|
+
Returns:
|
84
|
+
ReloadSettingsResult with operation status
|
85
|
+
"""
|
86
|
+
logger = get_logger("reload_settings_command")
|
87
|
+
|
88
|
+
try:
|
89
|
+
logger.info("🔄 Starting settings reload...")
|
90
|
+
|
91
|
+
# Reload configuration from files and environment variables
|
92
|
+
reload_settings()
|
93
|
+
|
94
|
+
# Get current custom settings
|
95
|
+
custom_settings = get_custom_settings()
|
96
|
+
|
97
|
+
logger.info("✅ Settings reloaded successfully")
|
98
|
+
logger.info(f"📋 Current custom settings: {custom_settings}")
|
99
|
+
|
100
|
+
return ReloadSettingsResult(
|
101
|
+
success=True,
|
102
|
+
message="Settings reloaded successfully from configuration files and environment variables",
|
103
|
+
custom_settings=custom_settings
|
104
|
+
)
|
105
|
+
|
106
|
+
except Exception as e:
|
107
|
+
error_msg = f"Failed to reload settings: {str(e)}"
|
108
|
+
logger.error(f"❌ {error_msg}")
|
109
|
+
|
110
|
+
return ReloadSettingsResult(
|
111
|
+
success=False,
|
112
|
+
message="Failed to reload settings",
|
113
|
+
custom_settings=get_custom_settings(),
|
114
|
+
error_message=error_msg
|
115
|
+
)
|
116
|
+
|
117
|
+
@classmethod
|
118
|
+
def get_schema(cls) -> Dict[str, Any]:
|
119
|
+
"""Get JSON schema for the command parameters."""
|
120
|
+
return {
|
121
|
+
"type": "object",
|
122
|
+
"description": "Reload configuration settings from files and environment variables",
|
123
|
+
"properties": {},
|
124
|
+
"additionalProperties": False
|
125
|
+
}
|