mcp-proxy-adapter 3.0.0__py3-none-any.whl → 3.0.1__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 (84) hide show
  1. examples/basic_example/README.md +123 -9
  2. examples/basic_example/config.json +4 -0
  3. examples/basic_example/docs/EN/README.md +46 -5
  4. examples/basic_example/docs/RU/README.md +46 -5
  5. examples/basic_example/server.py +127 -21
  6. examples/complete_example/commands/system_command.py +1 -0
  7. examples/complete_example/server.py +65 -11
  8. examples/minimal_example/README.md +20 -6
  9. examples/minimal_example/config.json +7 -14
  10. examples/minimal_example/main.py +109 -40
  11. examples/minimal_example/simple_server.py +53 -14
  12. examples/minimal_example/tests/conftest.py +1 -1
  13. examples/minimal_example/tests/test_integration.py +8 -10
  14. examples/simple_server.py +12 -21
  15. examples/test_server.py +22 -14
  16. examples/tool_description_example.py +82 -0
  17. mcp_proxy_adapter/api/__init__.py +0 -0
  18. mcp_proxy_adapter/api/app.py +391 -0
  19. mcp_proxy_adapter/api/handlers.py +229 -0
  20. mcp_proxy_adapter/api/middleware/__init__.py +49 -0
  21. mcp_proxy_adapter/api/middleware/auth.py +146 -0
  22. mcp_proxy_adapter/api/middleware/base.py +79 -0
  23. mcp_proxy_adapter/api/middleware/error_handling.py +198 -0
  24. mcp_proxy_adapter/api/middleware/logging.py +96 -0
  25. mcp_proxy_adapter/api/middleware/performance.py +83 -0
  26. mcp_proxy_adapter/api/middleware/rate_limit.py +152 -0
  27. mcp_proxy_adapter/api/schemas.py +305 -0
  28. mcp_proxy_adapter/api/tool_integration.py +223 -0
  29. mcp_proxy_adapter/api/tools.py +198 -0
  30. mcp_proxy_adapter/commands/__init__.py +19 -0
  31. mcp_proxy_adapter/commands/base.py +301 -0
  32. mcp_proxy_adapter/commands/command_registry.py +231 -0
  33. mcp_proxy_adapter/commands/config_command.py +113 -0
  34. mcp_proxy_adapter/commands/health_command.py +136 -0
  35. mcp_proxy_adapter/commands/help_command.py +193 -0
  36. mcp_proxy_adapter/commands/result.py +215 -0
  37. mcp_proxy_adapter/config.py +9 -0
  38. mcp_proxy_adapter/core/__init__.py +0 -0
  39. mcp_proxy_adapter/core/errors.py +173 -0
  40. mcp_proxy_adapter/core/logging.py +205 -0
  41. mcp_proxy_adapter/core/utils.py +138 -0
  42. mcp_proxy_adapter/py.typed +0 -0
  43. mcp_proxy_adapter/schemas/base_schema.json +114 -0
  44. mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
  45. mcp_proxy_adapter/tests/__init__.py +0 -0
  46. mcp_proxy_adapter/tests/api/__init__.py +3 -0
  47. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +115 -0
  48. mcp_proxy_adapter/tests/api/test_middleware.py +336 -0
  49. mcp_proxy_adapter/tests/commands/__init__.py +3 -0
  50. mcp_proxy_adapter/tests/commands/test_config_command.py +211 -0
  51. mcp_proxy_adapter/tests/commands/test_echo_command.py +127 -0
  52. mcp_proxy_adapter/tests/commands/test_help_command.py +133 -0
  53. mcp_proxy_adapter/tests/conftest.py +131 -0
  54. mcp_proxy_adapter/tests/functional/__init__.py +3 -0
  55. mcp_proxy_adapter/tests/functional/test_api.py +235 -0
  56. mcp_proxy_adapter/tests/integration/__init__.py +3 -0
  57. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +130 -0
  58. mcp_proxy_adapter/tests/integration/test_integration.py +255 -0
  59. mcp_proxy_adapter/tests/performance/__init__.py +3 -0
  60. mcp_proxy_adapter/tests/performance/test_performance.py +189 -0
  61. mcp_proxy_adapter/tests/stubs/__init__.py +10 -0
  62. mcp_proxy_adapter/tests/stubs/echo_command.py +104 -0
  63. mcp_proxy_adapter/tests/test_api_endpoints.py +271 -0
  64. mcp_proxy_adapter/tests/test_api_handlers.py +289 -0
  65. mcp_proxy_adapter/tests/test_base_command.py +123 -0
  66. mcp_proxy_adapter/tests/test_batch_requests.py +117 -0
  67. mcp_proxy_adapter/tests/test_command_registry.py +245 -0
  68. mcp_proxy_adapter/tests/test_config.py +127 -0
  69. mcp_proxy_adapter/tests/test_utils.py +65 -0
  70. mcp_proxy_adapter/tests/unit/__init__.py +3 -0
  71. mcp_proxy_adapter/tests/unit/test_base_command.py +130 -0
  72. mcp_proxy_adapter/tests/unit/test_config.py +217 -0
  73. mcp_proxy_adapter/version.py +1 -1
  74. {mcp_proxy_adapter-3.0.0.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/METADATA +1 -1
  75. mcp_proxy_adapter-3.0.1.dist-info/RECORD +109 -0
  76. examples/basic_example/config.yaml +0 -20
  77. examples/basic_example/main.py +0 -50
  78. examples/complete_example/main.py +0 -67
  79. examples/minimal_example/config.yaml +0 -26
  80. mcp_proxy_adapter/framework.py +0 -109
  81. mcp_proxy_adapter-3.0.0.dist-info/RECORD +0 -58
  82. {mcp_proxy_adapter-3.0.0.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/WHEEL +0 -0
  83. {mcp_proxy_adapter-3.0.0.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/licenses/LICENSE +0 -0
  84. {mcp_proxy_adapter-3.0.0.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,231 @@
1
+ """
2
+ Module for registering and managing commands.
3
+ """
4
+
5
+ import importlib
6
+ import inspect
7
+ import os
8
+ import pkgutil
9
+ from typing import Any, Dict, List, Optional, Type, TypeVar, cast
10
+
11
+ from mcp_proxy_adapter.commands.base import Command
12
+ from mcp_proxy_adapter.core.errors import NotFoundError
13
+ from mcp_proxy_adapter.core.logging import logger
14
+
15
+ T = TypeVar("T", bound=Command)
16
+
17
+
18
+ class CommandRegistry:
19
+ """
20
+ Registry for registering and finding commands.
21
+ """
22
+
23
+ def __init__(self):
24
+ """
25
+ Initialize command registry.
26
+ """
27
+ self._commands: Dict[str, Type[Command]] = {}
28
+
29
+ def register(self, command_class: Type[Command]) -> None:
30
+ """
31
+ Registers command class in the registry.
32
+
33
+ Args:
34
+ command_class: Command class to register.
35
+
36
+ Raises:
37
+ ValueError: If command with the same name is already registered.
38
+ """
39
+ if not hasattr(command_class, "name") or not command_class.name:
40
+ # Use class name if name attribute is not set
41
+ command_name = command_class.__name__.lower()
42
+ if command_name.endswith("command"):
43
+ command_name = command_name[:-7] # Remove "command" suffix
44
+ else:
45
+ command_name = command_class.name
46
+
47
+ if command_name in self._commands:
48
+ logger.debug(f"Command '{command_name}' is already registered, skipping")
49
+ raise ValueError(f"Command '{command_name}' is already registered")
50
+
51
+ logger.debug(f"Registering command: {command_name}")
52
+ self._commands[command_name] = command_class
53
+
54
+ def unregister(self, command_name: str) -> None:
55
+ """
56
+ Removes command from registry.
57
+
58
+ Args:
59
+ command_name: Command name to remove.
60
+
61
+ Raises:
62
+ NotFoundError: If command is not found.
63
+ """
64
+ if command_name not in self._commands:
65
+ raise NotFoundError(f"Command '{command_name}' not found")
66
+
67
+ logger.debug(f"Unregistering command: {command_name}")
68
+ del self._commands[command_name]
69
+
70
+ def command_exists(self, command_name: str) -> bool:
71
+ """
72
+ Checks if command exists in registry.
73
+
74
+ Args:
75
+ command_name: Command name to check.
76
+
77
+ Returns:
78
+ True if command exists, False otherwise.
79
+ """
80
+ return command_name in self._commands
81
+
82
+ def get_command(self, command_name: str) -> Type[Command]:
83
+ """
84
+ Gets command class by name.
85
+
86
+ Args:
87
+ command_name: Command name.
88
+
89
+ Returns:
90
+ Command class.
91
+
92
+ Raises:
93
+ NotFoundError: If command is not found.
94
+ """
95
+ if command_name not in self._commands:
96
+ raise NotFoundError(f"Command '{command_name}' not found")
97
+
98
+ return self._commands[command_name]
99
+
100
+ def get_all_commands(self) -> Dict[str, Type[Command]]:
101
+ """
102
+ Returns all registered commands.
103
+
104
+ Returns:
105
+ Dictionary with command names and their classes.
106
+ """
107
+ return dict(self._commands)
108
+
109
+ def get_command_info(self, command_name: str) -> Dict[str, Any]:
110
+ """
111
+ Gets information about a command.
112
+
113
+ Args:
114
+ command_name: Command name.
115
+
116
+ Returns:
117
+ Dictionary with command information.
118
+
119
+ Raises:
120
+ NotFoundError: If command is not found.
121
+ """
122
+ command_class = self.get_command(command_name)
123
+
124
+ return {
125
+ "name": command_name,
126
+ "description": command_class.__doc__ or "",
127
+ "params": command_class.get_param_info(),
128
+ "schema": command_class.get_schema(),
129
+ "result_schema": command_class.get_result_schema()
130
+ }
131
+
132
+ def get_command_metadata(self, command_name: str) -> Dict[str, Any]:
133
+ """
134
+ Get complete metadata for a command.
135
+
136
+ Args:
137
+ command_name: Command name
138
+
139
+ Returns:
140
+ Dict with command metadata
141
+
142
+ Raises:
143
+ NotFoundError: If command is not found
144
+ """
145
+ command_class = self.get_command(command_name)
146
+ return command_class.get_metadata()
147
+
148
+ def get_all_metadata(self) -> Dict[str, Dict[str, Any]]:
149
+ """
150
+ Get metadata for all registered commands.
151
+
152
+ Returns:
153
+ Dict with command names as keys and metadata as values
154
+ """
155
+ metadata = {}
156
+ for name, command_class in self._commands.items():
157
+ metadata[name] = command_class.get_metadata()
158
+ return metadata
159
+
160
+ def get_all_commands_info(self) -> Dict[str, Dict[str, Any]]:
161
+ """
162
+ Gets information about all registered commands.
163
+
164
+ Returns:
165
+ Dictionary with information about all commands.
166
+ """
167
+ commands_info = {}
168
+ for name in self._commands:
169
+ commands_info[name] = self.get_command_info(name)
170
+ return commands_info
171
+
172
+ def discover_commands(self, package_path: str = "mcp_proxy_adapter.commands") -> None:
173
+ """
174
+ Automatically discovers and registers commands in the specified package.
175
+
176
+ Args:
177
+ package_path: Path to package with commands.
178
+ """
179
+ logger.info(f"Discovering commands in package: {package_path}")
180
+
181
+ try:
182
+ package = importlib.import_module(package_path)
183
+ package_dir = os.path.dirname(package.__file__ or "")
184
+
185
+ for _, module_name, is_pkg in pkgutil.iter_modules([package_dir]):
186
+ if is_pkg:
187
+ # Recursively traverse subpackages
188
+ self.discover_commands(f"{package_path}.{module_name}")
189
+ elif module_name.endswith("_command"):
190
+ # Import only command modules
191
+ module_path = f"{package_path}.{module_name}"
192
+ logger.debug(f"Found command module: {module_path}")
193
+
194
+ try:
195
+ module = importlib.import_module(module_path)
196
+
197
+ # Find all command classes in the module
198
+ for name, obj in inspect.getmembers(module):
199
+ if (inspect.isclass(obj) and
200
+ issubclass(obj, Command) and
201
+ obj != Command and
202
+ not inspect.isabstract(obj)):
203
+
204
+ # Get command name before registration
205
+ command_name = obj.name if hasattr(obj, "name") and obj.name else obj.__name__.lower()
206
+ if command_name.endswith("command"):
207
+ command_name = command_name[:-7] # Remove "command" suffix
208
+
209
+ # Register the command only if it doesn't exist
210
+ if not self.command_exists(command_name):
211
+ self.register(cast(Type[Command], obj))
212
+ else:
213
+ logger.debug(f"Command '{command_name}' is already registered, skipping")
214
+ except ValueError as e:
215
+ # Skip already registered commands
216
+ logger.debug(f"Skipping command registration: {str(e)}")
217
+ except Exception as e:
218
+ logger.error(f"Error loading command module {module_path}: {e}")
219
+ except Exception as e:
220
+ logger.error(f"Error discovering commands: {e}")
221
+
222
+ def clear(self) -> None:
223
+ """
224
+ Clears command registry.
225
+ """
226
+ logger.debug("Clearing command registry")
227
+ self._commands.clear()
228
+
229
+
230
+ # Global command registry instance
231
+ registry = CommandRegistry()
@@ -0,0 +1,113 @@
1
+ """
2
+ Config command implementation for managing service configuration.
3
+ """
4
+
5
+ from typing import Dict, Any, Optional
6
+
7
+ from mcp_proxy_adapter.commands.base import Command
8
+ from mcp_proxy_adapter.commands.result import SuccessResult
9
+ from mcp_proxy_adapter.config import config as config_instance
10
+
11
+
12
+ class ConfigResult(SuccessResult):
13
+ """
14
+ Config operation result.
15
+ """
16
+
17
+ def __init__(self, config: Dict[str, Any], operation: str, message: str = None):
18
+ """
19
+ Initialize config result.
20
+
21
+ Args:
22
+ config: Configuration values
23
+ operation: Operation performed
24
+ message: Optional message
25
+ """
26
+ super().__init__(data={
27
+ "config": config,
28
+ "operation": operation
29
+ }, message=message)
30
+
31
+
32
+ class ConfigCommand(Command):
33
+ """
34
+ Command for managing service configuration.
35
+ """
36
+ name = "config"
37
+ description = "Get or set configuration values"
38
+ result_class = ConfigResult
39
+
40
+ async def execute(self, operation: str = "get", path: str = None, value: Any = None) -> ConfigResult:
41
+ """
42
+ Execute the command.
43
+
44
+ Args:
45
+ operation: Operation to perform (get, set)
46
+ path: Configuration path (dot notation)
47
+ value: Value to set (for set operation)
48
+
49
+ Returns:
50
+ Config operation result
51
+ """
52
+ message = None
53
+ result_config = {}
54
+
55
+ if operation == "get":
56
+ if path:
57
+ # Get specific config value
58
+ result_config = {path: config_instance.get(path)}
59
+ else:
60
+ # Get all config
61
+ result_config = config_instance.get_all()
62
+ message = f"Configuration retrieved successfully"
63
+
64
+ elif operation == "set":
65
+ if path and value is not None:
66
+ # Set config value
67
+ config_instance.set(path, value)
68
+ # Save config
69
+ config_instance.save()
70
+ result_config = {path: value}
71
+ message = f"Configuration updated successfully"
72
+ else:
73
+ # Error - missing required parameters
74
+ raise ValueError("Both 'path' and 'value' are required for 'set' operation")
75
+
76
+ else:
77
+ # Invalid operation
78
+ raise ValueError(f"Invalid operation: {operation}. Valid operations: get, set")
79
+
80
+ return ConfigResult(
81
+ config=result_config,
82
+ operation=operation,
83
+ message=message
84
+ )
85
+
86
+ @classmethod
87
+ def get_schema(cls) -> Dict[str, Any]:
88
+ """
89
+ Returns JSON schema for command parameters validation.
90
+
91
+ Returns:
92
+ Dictionary with JSON schema.
93
+ """
94
+ return {
95
+ "type": "object",
96
+ "properties": {
97
+ "operation": {
98
+ "type": "string",
99
+ "enum": ["get", "set"],
100
+ "default": "get",
101
+ "description": "Operation to perform (get or set)"
102
+ },
103
+ "path": {
104
+ "type": "string",
105
+ "description": "Configuration path in dot notation (e.g. 'server.host')"
106
+ },
107
+ "value": {
108
+ "description": "Value to set (required for 'set' operation)"
109
+ }
110
+ },
111
+ "required": ["operation"],
112
+ "additionalProperties": False
113
+ }
@@ -0,0 +1,136 @@
1
+ """
2
+ Module with health command implementation.
3
+ """
4
+
5
+ import os
6
+ import platform
7
+ import sys
8
+ import psutil
9
+ from datetime import datetime
10
+ from typing import Dict, Any, Optional
11
+
12
+ from mcp_proxy_adapter.commands.base import Command
13
+ from mcp_proxy_adapter.commands.result import CommandResult, SuccessResult
14
+ from mcp_proxy_adapter.commands.command_registry import registry
15
+
16
+
17
+ class HealthResult(SuccessResult):
18
+ """
19
+ Result of the health command execution.
20
+ """
21
+
22
+ def __init__(self, status: str, version: str, uptime: float, components: Dict[str, Any]):
23
+ """
24
+ Initialize health command result.
25
+
26
+ Args:
27
+ status: Server status ("ok" or "error")
28
+ version: Server version
29
+ uptime: Server uptime in seconds
30
+ components: Dictionary with components status
31
+ """
32
+ super().__init__(
33
+ data={
34
+ "status": status,
35
+ "version": version,
36
+ "uptime": uptime,
37
+ "components": components
38
+ }
39
+ )
40
+
41
+ @classmethod
42
+ def get_schema(cls) -> Dict[str, Any]:
43
+ """
44
+ Get JSON schema for result validation.
45
+
46
+ Returns:
47
+ Dict[str, Any]: JSON schema
48
+ """
49
+ return {
50
+ "type": "object",
51
+ "properties": {
52
+ "data": {
53
+ "type": "object",
54
+ "properties": {
55
+ "status": {"type": "string"},
56
+ "version": {"type": "string"},
57
+ "uptime": {"type": "number"},
58
+ "components": {
59
+ "type": "object",
60
+ "properties": {
61
+ "system": {"type": "object"},
62
+ "process": {"type": "object"},
63
+ "commands": {"type": "object"}
64
+ }
65
+ }
66
+ },
67
+ "required": ["status", "version", "uptime", "components"]
68
+ }
69
+ },
70
+ "required": ["data"]
71
+ }
72
+
73
+
74
+ class HealthCommand(Command):
75
+ """
76
+ Command that returns information about server health and status.
77
+ """
78
+
79
+ name = "health"
80
+ result_class = HealthResult
81
+
82
+ async def execute(self) -> HealthResult:
83
+ """
84
+ Execute health command.
85
+
86
+ Returns:
87
+ HealthResult: Health command result
88
+ """
89
+ # Get version from package
90
+ try:
91
+ from mcp_proxy_adapter.version import __version__ as version
92
+ except ImportError:
93
+ version = "unknown"
94
+
95
+ # Get process start time
96
+ process = psutil.Process(os.getpid())
97
+ start_time = datetime.fromtimestamp(process.create_time())
98
+ uptime_seconds = (datetime.now() - start_time).total_seconds()
99
+
100
+ # Get system information
101
+ memory_info = process.memory_info()
102
+
103
+ return HealthResult(
104
+ status="ok",
105
+ version=version,
106
+ uptime=uptime_seconds,
107
+ components={
108
+ "system": {
109
+ "python_version": sys.version,
110
+ "platform": platform.platform(),
111
+ "cpu_count": os.cpu_count()
112
+ },
113
+ "process": {
114
+ "pid": os.getpid(),
115
+ "memory_usage_mb": memory_info.rss / (1024 * 1024),
116
+ "start_time": start_time.isoformat()
117
+ },
118
+ "commands": {
119
+ "registered_count": len(registry.get_all_commands())
120
+ }
121
+ }
122
+ )
123
+
124
+ @classmethod
125
+ def get_schema(cls) -> Dict[str, Any]:
126
+ """
127
+ Get JSON schema for command parameters validation.
128
+
129
+ Returns:
130
+ Dict[str, Any]: JSON schema
131
+ """
132
+ return {
133
+ "type": "object",
134
+ "additionalProperties": False,
135
+ "description": "Command doesn't accept any parameters"
136
+ }
@@ -0,0 +1,193 @@
1
+ """
2
+ Module with help command implementation.
3
+ """
4
+
5
+ from typing import Dict, Any, Optional
6
+
7
+ from mcp_proxy_adapter.commands.base import Command
8
+ from mcp_proxy_adapter.commands.result import CommandResult
9
+ from mcp_proxy_adapter.commands.command_registry import registry
10
+ from mcp_proxy_adapter.core.errors import NotFoundError
11
+
12
+
13
+ class HelpResult(CommandResult):
14
+ """
15
+ Result of the help command execution.
16
+ """
17
+
18
+ def __init__(self, commands_info: Optional[Dict[str, Any]] = None, command_info: Optional[Dict[str, Any]] = None):
19
+ """
20
+ Initialize help command result.
21
+
22
+ Args:
23
+ commands_info: Information about all commands (for request without parameters)
24
+ command_info: Information about a specific command (for request with cmdname parameter)
25
+ """
26
+ self.commands_info = commands_info
27
+ self.command_info = command_info
28
+
29
+ def to_dict(self) -> Dict[str, Any]:
30
+ """
31
+ Convert result to dictionary.
32
+
33
+ Returns:
34
+ Dict[str, Any]: Result as dictionary
35
+ """
36
+ if self.command_info:
37
+ return {
38
+ "cmdname": self.command_info["name"],
39
+ "info": {
40
+ "description": self.command_info["description"],
41
+ "summary": self.command_info["summary"],
42
+ "params": self.command_info["params"],
43
+ "examples": self.command_info["examples"]
44
+ }
45
+ }
46
+
47
+ # For list of all commands, return as is (already formatted)
48
+ result = self.commands_info.copy()
49
+
50
+ # Add total count and note about usage
51
+ result["total"] = len(result["commands"])
52
+ result["note"] = "To get detailed information about a specific command, call help with parameter: POST /cmd {\"command\": \"help\", \"params\": {\"cmdname\": \"<command_name>\"}}. Only 'cmdname' parameter is supported."
53
+
54
+ return result
55
+
56
+ @classmethod
57
+ def get_schema(cls) -> Dict[str, Any]:
58
+ """
59
+ Get JSON schema for result validation.
60
+
61
+ Returns:
62
+ Dict[str, Any]: JSON schema
63
+ """
64
+ return {
65
+ "type": "object",
66
+ "oneOf": [
67
+ {
68
+ "properties": {
69
+ "commands": {
70
+ "type": "object",
71
+ "additionalProperties": {
72
+ "type": "object",
73
+ "properties": {
74
+ "description": {"type": "string"}
75
+ }
76
+ }
77
+ },
78
+ "tool_info": {
79
+ "type": "object",
80
+ "properties": {
81
+ "name": {"type": "string"},
82
+ "description": {"type": "string"},
83
+ "version": {"type": "string"}
84
+ }
85
+ },
86
+ "help_usage": {
87
+ "type": "object"
88
+ },
89
+ "total": {"type": "integer"},
90
+ "note": {"type": "string"}
91
+ },
92
+ "required": ["commands"]
93
+ },
94
+ {
95
+ "properties": {
96
+ "cmdname": {"type": "string"},
97
+ "info": {
98
+ "type": "object",
99
+ "properties": {
100
+ "description": {"type": "string"},
101
+ "summary": {"type": "string"},
102
+ "params": {"type": "object"},
103
+ "examples": {"type": "array"}
104
+ }
105
+ }
106
+ },
107
+ "required": ["cmdname", "info"]
108
+ }
109
+ ]
110
+ }
111
+
112
+
113
+ class HelpCommand(Command):
114
+ """
115
+ Command for getting help information about available commands.
116
+ """
117
+
118
+ name = "help"
119
+ result_class = HelpResult
120
+
121
+ async def execute(self, cmdname: Optional[str] = None) -> HelpResult:
122
+ """
123
+ Execute help command.
124
+
125
+ Args:
126
+ cmdname: Name of the command to get information about (optional)
127
+
128
+ Returns:
129
+ HelpResult: Help command result
130
+
131
+ Raises:
132
+ NotFoundError: If specified command not found
133
+ """
134
+ # If cmdname is provided, return information about specific command
135
+ if cmdname:
136
+ try:
137
+ # Get command metadata from registry
138
+ command_metadata = registry.get_command_metadata(cmdname)
139
+ return HelpResult(command_info=command_metadata)
140
+ except NotFoundError:
141
+ # If command not found, raise error
142
+ raise NotFoundError(f"Command '{cmdname}' not found")
143
+
144
+ # Otherwise, return information about all available commands
145
+ # and tool metadata
146
+
147
+ # Get metadata for all commands
148
+ all_metadata = registry.get_all_metadata()
149
+
150
+ # Prepare response format with tool metadata
151
+ result = {
152
+ "tool_info": {
153
+ "name": "MCP-Proxy API Service",
154
+ "description": "JSON-RPC API for microservice command execution",
155
+ "version": "1.0.0"
156
+ },
157
+ "help_usage": {
158
+ "description": "Get information about commands",
159
+ "examples": [
160
+ {"command": "help", "description": "List of all available commands"},
161
+ {"command": "help", "params": {"cmdname": "command_name"}, "description": "Get detailed information about a specific command"}
162
+ ]
163
+ },
164
+ "commands": {}
165
+ }
166
+
167
+ # Add brief information about commands
168
+ for name, metadata in all_metadata.items():
169
+ result["commands"][name] = {
170
+ "summary": metadata["summary"],
171
+ "params_count": len(metadata["params"])
172
+ }
173
+
174
+ return HelpResult(commands_info=result)
175
+
176
+ @classmethod
177
+ def get_schema(cls) -> Dict[str, Any]:
178
+ """
179
+ Get JSON schema for command parameters validation.
180
+
181
+ Returns:
182
+ Dict[str, Any]: JSON schema
183
+ """
184
+ return {
185
+ "type": "object",
186
+ "properties": {
187
+ "cmdname": {
188
+ "type": "string",
189
+ "description": "Name of command to get information about"
190
+ }
191
+ },
192
+ "additionalProperties": False
193
+ }