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.
Files changed (118) hide show
  1. mcp_proxy_adapter/api/app.py +65 -27
  2. mcp_proxy_adapter/api/handlers.py +1 -1
  3. mcp_proxy_adapter/api/middleware/error_handling.py +11 -10
  4. mcp_proxy_adapter/api/tool_integration.py +5 -2
  5. mcp_proxy_adapter/api/tools.py +3 -3
  6. mcp_proxy_adapter/commands/base.py +19 -1
  7. mcp_proxy_adapter/commands/command_registry.py +243 -6
  8. mcp_proxy_adapter/commands/hooks.py +260 -0
  9. mcp_proxy_adapter/commands/reload_command.py +211 -0
  10. mcp_proxy_adapter/commands/reload_settings_command.py +125 -0
  11. mcp_proxy_adapter/commands/settings_command.py +189 -0
  12. mcp_proxy_adapter/config.py +16 -1
  13. mcp_proxy_adapter/core/__init__.py +44 -0
  14. mcp_proxy_adapter/core/logging.py +87 -34
  15. mcp_proxy_adapter/core/settings.py +376 -0
  16. mcp_proxy_adapter/core/utils.py +2 -2
  17. mcp_proxy_adapter/custom_openapi.py +81 -2
  18. mcp_proxy_adapter/examples/README.md +124 -0
  19. mcp_proxy_adapter/examples/__init__.py +7 -0
  20. mcp_proxy_adapter/examples/basic_server/README.md +60 -0
  21. mcp_proxy_adapter/examples/basic_server/__init__.py +7 -0
  22. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +39 -0
  23. mcp_proxy_adapter/examples/basic_server/config.json +35 -0
  24. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +238 -0
  25. mcp_proxy_adapter/examples/basic_server/server.py +98 -0
  26. mcp_proxy_adapter/examples/custom_commands/README.md +127 -0
  27. mcp_proxy_adapter/examples/custom_commands/__init__.py +27 -0
  28. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +250 -0
  29. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +6 -0
  30. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +103 -0
  31. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +111 -0
  32. mcp_proxy_adapter/examples/custom_commands/config.json +62 -0
  33. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +169 -0
  34. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +215 -0
  35. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +76 -0
  36. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +96 -0
  37. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +241 -0
  38. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +135 -0
  39. mcp_proxy_adapter/examples/custom_commands/echo_command.py +122 -0
  40. mcp_proxy_adapter/examples/custom_commands/hooks.py +230 -0
  41. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +123 -0
  42. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +103 -0
  43. mcp_proxy_adapter/examples/custom_commands/server.py +223 -0
  44. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +176 -0
  45. mcp_proxy_adapter/examples/deployment/README.md +49 -0
  46. mcp_proxy_adapter/examples/deployment/__init__.py +7 -0
  47. mcp_proxy_adapter/examples/deployment/config.development.json +8 -0
  48. {examples/basic_example → mcp_proxy_adapter/examples/deployment}/config.json +11 -7
  49. mcp_proxy_adapter/examples/deployment/config.production.json +12 -0
  50. mcp_proxy_adapter/examples/deployment/config.staging.json +11 -0
  51. mcp_proxy_adapter/examples/deployment/docker-compose.yml +31 -0
  52. mcp_proxy_adapter/examples/deployment/run.sh +43 -0
  53. mcp_proxy_adapter/examples/deployment/run_docker.sh +84 -0
  54. mcp_proxy_adapter/openapi.py +3 -2
  55. mcp_proxy_adapter/tests/api/test_custom_openapi.py +617 -0
  56. mcp_proxy_adapter/tests/api/test_handlers.py +522 -0
  57. mcp_proxy_adapter/tests/api/test_schemas.py +546 -0
  58. mcp_proxy_adapter/tests/api/test_tool_integration.py +531 -0
  59. mcp_proxy_adapter/tests/unit/test_base_command.py +391 -85
  60. mcp_proxy_adapter/version.py +1 -1
  61. {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/METADATA +1 -1
  62. mcp_proxy_adapter-4.0.0.dist-info/RECORD +110 -0
  63. {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/WHEEL +1 -1
  64. {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/top_level.txt +0 -1
  65. examples/__init__.py +0 -19
  66. examples/anti_patterns/README.md +0 -51
  67. examples/anti_patterns/__init__.py +0 -9
  68. examples/anti_patterns/bad_design/README.md +0 -72
  69. examples/anti_patterns/bad_design/global_state.py +0 -170
  70. examples/anti_patterns/bad_design/monolithic_command.py +0 -272
  71. examples/basic_example/README.md +0 -245
  72. examples/basic_example/__init__.py +0 -8
  73. examples/basic_example/commands/__init__.py +0 -5
  74. examples/basic_example/commands/echo_command.py +0 -95
  75. examples/basic_example/commands/math_command.py +0 -151
  76. examples/basic_example/commands/time_command.py +0 -152
  77. examples/basic_example/docs/EN/README.md +0 -177
  78. examples/basic_example/docs/RU/README.md +0 -177
  79. examples/basic_example/server.py +0 -151
  80. examples/basic_example/tests/conftest.py +0 -243
  81. examples/check_vstl_schema.py +0 -106
  82. examples/commands/echo_command.py +0 -52
  83. examples/commands/echo_command_di.py +0 -152
  84. examples/commands/echo_result.py +0 -65
  85. examples/commands/get_date_command.py +0 -98
  86. examples/commands/new_uuid4_command.py +0 -91
  87. examples/complete_example/Dockerfile +0 -24
  88. examples/complete_example/README.md +0 -92
  89. examples/complete_example/__init__.py +0 -8
  90. examples/complete_example/commands/__init__.py +0 -5
  91. examples/complete_example/commands/system_command.py +0 -328
  92. examples/complete_example/config.json +0 -41
  93. examples/complete_example/configs/config.dev.yaml +0 -40
  94. examples/complete_example/configs/config.docker.yaml +0 -40
  95. examples/complete_example/docker-compose.yml +0 -35
  96. examples/complete_example/requirements.txt +0 -20
  97. examples/complete_example/server.py +0 -113
  98. examples/di_example/.pytest_cache/README.md +0 -8
  99. examples/di_example/server.py +0 -249
  100. examples/fix_vstl_help.py +0 -123
  101. examples/minimal_example/README.md +0 -65
  102. examples/minimal_example/__init__.py +0 -8
  103. examples/minimal_example/config.json +0 -14
  104. examples/minimal_example/main.py +0 -136
  105. examples/minimal_example/simple_server.py +0 -163
  106. examples/minimal_example/tests/conftest.py +0 -171
  107. examples/minimal_example/tests/test_hello_command.py +0 -111
  108. examples/minimal_example/tests/test_integration.py +0 -181
  109. examples/patch_vstl_service.py +0 -105
  110. examples/patch_vstl_service_mcp.py +0 -108
  111. examples/server.py +0 -69
  112. examples/simple_server.py +0 -128
  113. examples/test_package_3.1.4.py +0 -177
  114. examples/test_server.py +0 -134
  115. examples/tool_description_example.py +0 -82
  116. mcp_proxy_adapter/py.typed +0 -0
  117. mcp_proxy_adapter-3.1.6.dist-info/RECORD +0 -118
  118. {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,241 @@
1
+ """
2
+ Custom Settings Manager Example
3
+
4
+ This module demonstrates how to create a custom settings manager
5
+ that extends the framework's settings system with application-specific settings.
6
+ """
7
+
8
+ import json
9
+ import os
10
+ from typing import Dict, Any, Optional
11
+ from mcp_proxy_adapter.core.settings import (
12
+ add_custom_settings,
13
+ get_custom_settings,
14
+ get_custom_setting_value,
15
+ set_custom_setting_value
16
+ )
17
+ from mcp_proxy_adapter.core.logging import get_logger
18
+
19
+
20
+ class CustomSettingsManager:
21
+ """
22
+ Custom settings manager for the extended server example.
23
+
24
+ This class demonstrates how to:
25
+ 1. Load custom settings from JSON files
26
+ 2. Add them to the framework's settings system
27
+ 3. Provide convenient access methods
28
+ 4. Handle settings validation and defaults
29
+ """
30
+
31
+ def __init__(self, config_file: str = "custom_settings.json"):
32
+ """
33
+ Initialize the custom settings manager.
34
+
35
+ Args:
36
+ config_file: Path to custom settings JSON file
37
+ """
38
+ self.config_file = config_file
39
+ self.logger = get_logger("custom_settings_manager")
40
+ self._load_custom_settings()
41
+
42
+ def _load_custom_settings(self) -> None:
43
+ """Load custom settings from JSON file."""
44
+ try:
45
+ if os.path.exists(self.config_file):
46
+ with open(self.config_file, 'r', encoding='utf-8') as f:
47
+ custom_settings = json.load(f)
48
+
49
+ self.logger.info(f"📁 Loaded custom settings from: {self.config_file}")
50
+ self.logger.debug(f"📋 Custom settings: {custom_settings}")
51
+
52
+ # Add to framework's settings system
53
+ add_custom_settings(custom_settings)
54
+
55
+ else:
56
+ self.logger.warning(f"⚠️ Custom settings file not found: {self.config_file}")
57
+ self.logger.info("📝 Using default custom settings")
58
+
59
+ # Use default settings
60
+ default_settings = self._get_default_settings()
61
+ add_custom_settings(default_settings)
62
+
63
+ except Exception as e:
64
+ self.logger.error(f"❌ Failed to load custom settings: {e}")
65
+ self.logger.info("📝 Using default custom settings")
66
+
67
+ # Use default settings on error
68
+ default_settings = self._get_default_settings()
69
+ add_custom_settings(default_settings)
70
+
71
+ def _get_default_settings(self) -> Dict[str, Any]:
72
+ """Get default custom settings."""
73
+ return {
74
+ "application": {
75
+ "name": "Extended MCP Proxy Server",
76
+ "version": "2.0.0",
77
+ "environment": "development"
78
+ },
79
+ "features": {
80
+ "advanced_hooks": True,
81
+ "custom_commands": True,
82
+ "data_transformation": True,
83
+ "command_interception": True,
84
+ "performance_monitoring": False
85
+ },
86
+ "security": {
87
+ "enable_authentication": False,
88
+ "max_request_size": "10MB",
89
+ "rate_limiting": {
90
+ "enabled": False,
91
+ "requests_per_minute": 100
92
+ }
93
+ },
94
+ "monitoring": {
95
+ "enable_metrics": True,
96
+ "metrics_interval": 60,
97
+ "health_check_interval": 30
98
+ },
99
+ "custom_commands": {
100
+ "auto_echo": {
101
+ "enabled": True,
102
+ "max_length": 1000
103
+ },
104
+ "data_transform": {
105
+ "enabled": True,
106
+ "transform_types": ["uppercase", "lowercase", "reverse"]
107
+ },
108
+ "intercept": {
109
+ "enabled": True,
110
+ "bypass_conditions": ["input_value == 0"]
111
+ }
112
+ }
113
+ }
114
+
115
+ def get_application_name(self) -> str:
116
+ """Get application name."""
117
+ return get_custom_setting_value("application.name", "Extended MCP Proxy Server")
118
+
119
+ def get_application_version(self) -> str:
120
+ """Get application version."""
121
+ return get_custom_setting_value("application.version", "2.0.0")
122
+
123
+ def get_environment(self) -> str:
124
+ """Get application environment."""
125
+ return get_custom_setting_value("application.environment", "development")
126
+
127
+ def is_feature_enabled(self, feature_name: str) -> bool:
128
+ """Check if a feature is enabled."""
129
+ return get_custom_setting_value(f"features.{feature_name}", False)
130
+
131
+ def get_security_setting(self, setting_name: str, default: Any = None) -> Any:
132
+ """Get security setting."""
133
+ return get_custom_setting_value(f"security.{setting_name}", default)
134
+
135
+ def get_monitoring_setting(self, setting_name: str, default: Any = None) -> Any:
136
+ """Get monitoring setting."""
137
+ return get_custom_setting_value(f"monitoring.{setting_name}", default)
138
+
139
+ def get_custom_command_setting(self, command_name: str, setting_name: str, default: Any = None) -> Any:
140
+ """Get custom command setting."""
141
+ return get_custom_setting_value(f"custom_commands.{command_name}.{setting_name}", default)
142
+
143
+ def set_custom_setting(self, key: str, value: Any) -> None:
144
+ """Set a custom setting."""
145
+ set_custom_setting_value(key, value)
146
+ self.logger.info(f"🔧 Set custom setting: {key} = {value}")
147
+
148
+ def get_all_custom_settings(self) -> Dict[str, Any]:
149
+ """Get all custom settings."""
150
+ return get_custom_settings()
151
+
152
+ def reload_settings(self) -> None:
153
+ """Reload custom settings from file."""
154
+ self.logger.info("🔄 Reloading custom settings...")
155
+ self._load_custom_settings()
156
+ self.logger.info("✅ Custom settings reloaded")
157
+
158
+ def validate_settings(self) -> Dict[str, Any]:
159
+ """
160
+ Validate current settings and return validation results.
161
+
162
+ Returns:
163
+ Dictionary with validation results
164
+ """
165
+ validation_results = {
166
+ "valid": True,
167
+ "errors": [],
168
+ "warnings": []
169
+ }
170
+
171
+ # Validate required settings
172
+ required_settings = [
173
+ "application.name",
174
+ "application.version",
175
+ "features.advanced_hooks"
176
+ ]
177
+
178
+ for setting in required_settings:
179
+ if get_custom_setting_value(setting) is None:
180
+ validation_results["valid"] = False
181
+ validation_results["errors"].append(f"Missing required setting: {setting}")
182
+
183
+ # Validate feature dependencies
184
+ if self.is_feature_enabled("data_transformation"):
185
+ if not self.is_feature_enabled("custom_commands"):
186
+ validation_results["warnings"].append(
187
+ "data_transformation requires custom_commands to be enabled"
188
+ )
189
+
190
+ # Validate security settings
191
+ if self.get_security_setting("enable_authentication"):
192
+ if not self.get_security_setting("rate_limiting.enabled"):
193
+ validation_results["warnings"].append(
194
+ "Authentication enabled but rate limiting is disabled"
195
+ )
196
+
197
+ return validation_results
198
+
199
+ def print_settings_summary(self) -> None:
200
+ """Print a summary of current settings."""
201
+ self.logger.info("📊 Custom Settings Summary:")
202
+ self.logger.info(f" Application: {self.get_application_name()} v{self.get_application_version()}")
203
+ self.logger.info(f" Environment: {self.get_environment()}")
204
+
205
+ # Features
206
+ features = []
207
+ for feature in ["advanced_hooks", "custom_commands", "data_transformation", "command_interception"]:
208
+ if self.is_feature_enabled(feature):
209
+ features.append(feature)
210
+
211
+ self.logger.info(f" Enabled Features: {', '.join(features) if features else 'None'}")
212
+
213
+ # Security
214
+ auth_enabled = self.get_security_setting("enable_authentication", False)
215
+ rate_limiting = self.get_security_setting("rate_limiting.enabled", False)
216
+ self.logger.info(f" Security: Auth={auth_enabled}, Rate Limiting={rate_limiting}")
217
+
218
+ # Monitoring
219
+ metrics_enabled = self.get_monitoring_setting("enable_metrics", False)
220
+ self.logger.info(f" Monitoring: Metrics={metrics_enabled}")
221
+
222
+
223
+ # Convenience functions for easy access
224
+ def get_app_name() -> str:
225
+ """Get application name."""
226
+ return get_custom_setting_value("application.name", "Extended MCP Proxy Server")
227
+
228
+
229
+ def is_feature_enabled(feature_name: str) -> bool:
230
+ """Check if a feature is enabled."""
231
+ return get_custom_setting_value(f"features.{feature_name}", False)
232
+
233
+
234
+ def get_security_setting(setting_name: str, default: Any = None) -> Any:
235
+ """Get security setting."""
236
+ return get_custom_setting_value(f"security.{setting_name}", default)
237
+
238
+
239
+ def get_monitoring_setting(setting_name: str, default: Any = None) -> Any:
240
+ """Get monitoring setting."""
241
+ return get_custom_setting_value(f"monitoring.{setting_name}", default)
@@ -0,0 +1,135 @@
1
+ """
2
+ Data Transform Command Example
3
+
4
+ A command that demonstrates advanced hooks with data transformation.
5
+ """
6
+
7
+ from typing import Dict, Any, Optional
8
+ from mcp_proxy_adapter.commands.base import Command
9
+ from mcp_proxy_adapter.commands.result import CommandResult
10
+
11
+
12
+ class DataTransformResult(CommandResult):
13
+ """
14
+ Result of the data transform command execution.
15
+ """
16
+
17
+ def __init__(self, original_data: Dict[str, Any], transformed_data: Dict[str, Any],
18
+ processing_info: Dict[str, Any]):
19
+ """
20
+ Initialize data transform command result.
21
+
22
+ Args:
23
+ original_data: Original input data
24
+ transformed_data: Transformed output data
25
+ processing_info: Information about processing steps
26
+ """
27
+ self.original_data = original_data
28
+ self.transformed_data = transformed_data
29
+ self.processing_info = processing_info
30
+
31
+ def to_dict(self) -> Dict[str, Any]:
32
+ """
33
+ Convert result to dictionary.
34
+
35
+ Returns:
36
+ Dict[str, Any]: Result as dictionary
37
+ """
38
+ return {
39
+ "original_data": self.original_data,
40
+ "transformed_data": self.transformed_data,
41
+ "processing_info": self.processing_info,
42
+ "command_type": "data_transform"
43
+ }
44
+
45
+ @classmethod
46
+ def get_schema(cls) -> Dict[str, Any]:
47
+ """
48
+ Get JSON schema for the result.
49
+
50
+ Returns:
51
+ Dict[str, Any]: JSON schema
52
+ """
53
+ return {
54
+ "type": "object",
55
+ "properties": {
56
+ "original_data": {"type": "object"},
57
+ "transformed_data": {"type": "object"},
58
+ "processing_info": {"type": "object"},
59
+ "command_type": {"type": "string"}
60
+ }
61
+ }
62
+
63
+
64
+ class DataTransformCommand(Command):
65
+ """
66
+ Data transform command for demonstrating advanced hooks.
67
+ """
68
+
69
+ name = "data_transform"
70
+ result_class = DataTransformResult
71
+
72
+ async def execute(self, data: Optional[Dict[str, Any]] = None,
73
+ transform_type: Optional[str] = None, **kwargs) -> DataTransformResult:
74
+ """
75
+ Execute data transform command.
76
+
77
+ Args:
78
+ data: Input data to transform
79
+ transform_type: Type of transformation to apply
80
+ **kwargs: Additional parameters
81
+
82
+ Returns:
83
+ DataTransformResult: Data transform command result
84
+ """
85
+ # Get original data (may be modified by hooks)
86
+ original_data = data or {}
87
+ transform_type = transform_type or "default"
88
+
89
+ # Apply transformation based on type
90
+ if transform_type == "uppercase":
91
+ transformed_data = {k: str(v).upper() for k, v in original_data.items()}
92
+ elif transform_type == "lowercase":
93
+ transformed_data = {k: str(v).lower() for k, v in original_data.items()}
94
+ elif transform_type == "reverse":
95
+ transformed_data = {k: str(v)[::-1] for k, v in original_data.items()}
96
+ else:
97
+ transformed_data = original_data.copy()
98
+
99
+ # Add processing info
100
+ processing_info = {
101
+ "transform_type": transform_type,
102
+ "input_keys": list(original_data.keys()),
103
+ "output_keys": list(transformed_data.keys()),
104
+ "hook_enhanced": kwargs.get("hook_enhanced", False),
105
+ "data_modified": kwargs.get("data_modified", False)
106
+ }
107
+
108
+ return DataTransformResult(
109
+ original_data=original_data,
110
+ transformed_data=transformed_data,
111
+ processing_info=processing_info
112
+ )
113
+
114
+ @classmethod
115
+ def get_schema(cls) -> Dict[str, Any]:
116
+ """
117
+ Get JSON schema for command parameters.
118
+
119
+ Returns:
120
+ Dict[str, Any]: JSON schema
121
+ """
122
+ return {
123
+ "type": "object",
124
+ "properties": {
125
+ "data": {
126
+ "type": "object",
127
+ "description": "Input data to transform"
128
+ },
129
+ "transform_type": {
130
+ "type": "string",
131
+ "enum": ["uppercase", "lowercase", "reverse", "default"],
132
+ "description": "Type of transformation to apply"
133
+ }
134
+ }
135
+ }
@@ -0,0 +1,122 @@
1
+ """
2
+ Echo Command Example
3
+
4
+ A simple echo command that returns the input message.
5
+ """
6
+
7
+ from typing import Dict, Any, Optional
8
+ from datetime import datetime
9
+ from mcp_proxy_adapter.commands.base import Command
10
+ from mcp_proxy_adapter.commands.result import SuccessResult
11
+
12
+
13
+ class EchoResult(SuccessResult):
14
+ """
15
+ Result of the echo command execution.
16
+ """
17
+
18
+ def __init__(self, message: str, timestamp: str):
19
+ """
20
+ Initialize echo command result.
21
+
22
+ Args:
23
+ message: The echoed message
24
+ timestamp: Timestamp of execution
25
+ """
26
+ super().__init__(
27
+ data={
28
+ "message": message,
29
+ "timestamp": timestamp,
30
+ "echoed": True
31
+ }
32
+ )
33
+
34
+ @classmethod
35
+ def get_schema(cls) -> Dict[str, Any]:
36
+ """
37
+ Get JSON schema for result validation.
38
+
39
+ Returns:
40
+ Dict[str, Any]: JSON schema
41
+ """
42
+ return {
43
+ "type": "object",
44
+ "properties": {
45
+ "data": {
46
+ "type": "object",
47
+ "properties": {
48
+ "message": {"type": "string"},
49
+ "timestamp": {"type": "string"},
50
+ "echoed": {"type": "boolean"}
51
+ },
52
+ "required": ["message", "timestamp", "echoed"]
53
+ }
54
+ },
55
+ "required": ["data"]
56
+ }
57
+
58
+
59
+ class EchoCommand(Command):
60
+ """
61
+ Echo command that returns the input message.
62
+ """
63
+
64
+ name = "echo"
65
+ result_class = EchoResult
66
+
67
+ async def execute(self, message: Optional[str] = None, **kwargs) -> EchoResult:
68
+ """
69
+ Execute echo command.
70
+
71
+ Args:
72
+ message: Message to echo (optional)
73
+ **kwargs: Additional parameters
74
+
75
+ Returns:
76
+ EchoResult: Echo command result
77
+ """
78
+ # Use provided message or default
79
+ if message is None:
80
+ message = kwargs.get("text", "Hello, World!")
81
+
82
+ # Check if hook added timestamp
83
+ hook_timestamp = kwargs.get("hook_timestamp")
84
+ if hook_timestamp:
85
+ # Use hook timestamp if available
86
+ timestamp = hook_timestamp
87
+ else:
88
+ # Get current timestamp
89
+ timestamp = datetime.now().isoformat()
90
+
91
+ # Add hook metadata to result
92
+ result = EchoResult(message=message, timestamp=timestamp)
93
+
94
+ # Add hook information if available
95
+ if kwargs.get("hook_processed"):
96
+ result.data["hook_processed"] = True
97
+ result.data["hook_timestamp"] = hook_timestamp
98
+
99
+ return result
100
+
101
+ @classmethod
102
+ def get_schema(cls) -> Dict[str, Any]:
103
+ """
104
+ Get JSON schema for command parameters.
105
+
106
+ Returns:
107
+ Dict[str, Any]: JSON schema
108
+ """
109
+ return {
110
+ "type": "object",
111
+ "properties": {
112
+ "message": {
113
+ "type": "string",
114
+ "description": "Message to echo",
115
+ "default": "Hello, World!"
116
+ },
117
+ "text": {
118
+ "type": "string",
119
+ "description": "Alternative parameter name for message"
120
+ }
121
+ }
122
+ }