mcp-proxy-adapter 1.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.
cli/command_runner.py ADDED
@@ -0,0 +1,233 @@
1
+ """
2
+ Command line utility for executing commands.
3
+
4
+ Provides a command line interface for the command dispatcher,
5
+ allowing execution of registered commands and getting help information.
6
+ """
7
+
8
+ import argparse
9
+ import json
10
+ import sys
11
+ from typing import Any, Dict, List, Optional, Sequence
12
+
13
+ from command_registry.dispatchers.base_dispatcher import BaseDispatcher
14
+
15
+
16
+ class CommandRunner:
17
+ """
18
+ Command line utility for executing commands.
19
+
20
+ Converts command line arguments into dispatcher command calls.
21
+ Provides ability to get help information about commands.
22
+ """
23
+
24
+ def __init__(self, dispatcher: BaseDispatcher):
25
+ """
26
+ Initializes CommandRunner.
27
+
28
+ Args:
29
+ dispatcher: Command dispatcher for executing commands
30
+ """
31
+ self.dispatcher = dispatcher
32
+
33
+ def build_parser(self) -> argparse.ArgumentParser:
34
+ """
35
+ Creates argument parser based on registered commands.
36
+
37
+ Returns:
38
+ Command line argument parser
39
+ """
40
+ parser = argparse.ArgumentParser(
41
+ description="Execute commands from command registry",
42
+ add_help=False, # Disable standard help
43
+ )
44
+
45
+ # Add main arguments
46
+ parser.add_argument(
47
+ "command",
48
+ help="Command name to execute or 'help' to get list of commands",
49
+ nargs="?",
50
+ default="help",
51
+ )
52
+
53
+ parser.add_argument(
54
+ "--help", "-h",
55
+ action="store_true",
56
+ help="Show help for specified command",
57
+ dest="show_help",
58
+ )
59
+
60
+ parser.add_argument(
61
+ "--json",
62
+ action="store_true",
63
+ help="Output result in JSON format",
64
+ )
65
+
66
+ return parser
67
+
68
+ def _handle_help_command(self, args: Optional[List[str]] = None) -> None:
69
+ """
70
+ Outputs help information about available commands.
71
+
72
+ Args:
73
+ args: List of arguments, if first argument is command name,
74
+ outputs help for that command
75
+ """
76
+ if args and len(args) > 0 and args[0] != "help":
77
+ # Help for specific command
78
+ command_name = args[0]
79
+ if command_name not in self.dispatcher.get_valid_commands():
80
+ print(f"Unknown command: {command_name}", file=sys.stderr)
81
+ return
82
+
83
+ info = self.dispatcher.get_command_info(command_name)
84
+
85
+ print(f"\nCommand: {command_name}\n")
86
+
87
+ if info.get("description"):
88
+ print(f"Description: {info['description']}\n")
89
+
90
+ if "parameters" in info and info["parameters"]:
91
+ print("Parameters:")
92
+ for name, param_info in info["parameters"].items():
93
+ param_type = param_info.get("type", "any")
94
+ required = param_info.get("required", False)
95
+ description = param_info.get("description", "")
96
+
97
+ req_str = " (required)" if required else ""
98
+ print(f" {name} ({param_type}){req_str}")
99
+ if description:
100
+ print(f" {description}")
101
+ print()
102
+
103
+ if "returns" in info and info["returns"]:
104
+ print(f"Returns: {info['returns']}")
105
+
106
+ print("\nUsage:")
107
+ params_str = " ".join(
108
+ f"--{name}=<value>" for name in info.get("parameters", {})
109
+ )
110
+ print(f" python -m command_registry.cli {command_name} {params_str}")
111
+ else:
112
+ # General help
113
+ commands = self.dispatcher.get_commands_info()
114
+
115
+ print("\nAvailable commands:\n")
116
+ for name, info in commands.items():
117
+ description = info.get("description", "No description")
118
+ # Show only first line of description for brevity
119
+ short_desc = description.split("\n")[0]
120
+ print(f" {name:<20} {short_desc}")
121
+
122
+ print("\nTo get detailed information about a command use:")
123
+ print(" python -m command_registry.cli help <command>")
124
+ print(" python -m command_registry.cli <command> --help")
125
+
126
+ def run(self, args: Sequence[str]) -> None:
127
+ """
128
+ Runs command based on provided arguments.
129
+
130
+ Args:
131
+ args: Command line arguments (without program name)
132
+ """
133
+ parser = self.build_parser()
134
+
135
+ # Parse arguments
136
+ parsed_args, remaining = parser.parse_known_args(args)
137
+
138
+ # Handle help command or --help flag
139
+ if parsed_args.command == "help" or parsed_args.show_help:
140
+ if parsed_args.command == "help":
141
+ self._handle_help_command(remaining)
142
+ else:
143
+ self._handle_help_command([parsed_args.command])
144
+ return
145
+
146
+ # Check command existence
147
+ if parsed_args.command not in self.dispatcher.get_valid_commands():
148
+ print(f"Unknown command: {parsed_args.command}", file=sys.stderr)
149
+ print("Use 'help' to get list of available commands", file=sys.stderr)
150
+ sys.exit(1)
151
+
152
+ # Convert remaining arguments to command parameters
153
+ command_params = {}
154
+ command_info = self.dispatcher.get_command_info(parsed_args.command)
155
+ expected_params = command_info.get("parameters", {})
156
+
157
+ # Parse parameters from remaining arguments
158
+ i = 0
159
+ while i < len(remaining):
160
+ arg = remaining[i]
161
+
162
+ # Support --param=value format
163
+ if arg.startswith("--") and "=" in arg:
164
+ param_name, value = arg[2:].split("=", 1)
165
+ command_params[param_name] = self._parse_value(value)
166
+ i += 1
167
+ # Support --param value format
168
+ elif arg.startswith("--"):
169
+ param_name = arg[2:]
170
+ if i + 1 < len(remaining) and not remaining[i + 1].startswith("--"):
171
+ command_params[param_name] = self._parse_value(remaining[i + 1])
172
+ i += 2
173
+ else:
174
+ # Boolean flag
175
+ command_params[param_name] = True
176
+ i += 1
177
+ else:
178
+ print(f"Unknown argument: {arg}", file=sys.stderr)
179
+ i += 1
180
+
181
+ # Check required parameters
182
+ for param_name, param_info in expected_params.items():
183
+ if param_info.get("required", False) and param_name not in command_params:
184
+ print(f"Missing required parameter: {param_name}", file=sys.stderr)
185
+ sys.exit(1)
186
+
187
+ try:
188
+ # Execute command
189
+ result = self.dispatcher.execute(parsed_args.command, **command_params)
190
+
191
+ # Output result
192
+ if parsed_args.json:
193
+ print(json.dumps(result, ensure_ascii=False, indent=2))
194
+ elif result is not None:
195
+ print(result)
196
+ except Exception as e:
197
+ print(f"Error executing command: {e}", file=sys.stderr)
198
+ sys.exit(1)
199
+
200
+ def _parse_value(self, value_str: str) -> Any:
201
+ """
202
+ Parses string value into corresponding type.
203
+
204
+ Args:
205
+ value_str: String representation of value
206
+
207
+ Returns:
208
+ Parsed value of corresponding type
209
+ """
210
+ # Boolean values
211
+ if value_str.lower() in ("true", "yes", "y", "1"):
212
+ return True
213
+ if value_str.lower() in ("false", "no", "n", "0"):
214
+ return False
215
+
216
+ # Numbers
217
+ try:
218
+ if "." in value_str:
219
+ return float(value_str)
220
+ return int(value_str)
221
+ except ValueError:
222
+ pass
223
+
224
+ # JSON
225
+ if (value_str.startswith("{") and value_str.endswith("}")) or \
226
+ (value_str.startswith("[") and value_str.endswith("]")):
227
+ try:
228
+ return json.loads(value_str)
229
+ except json.JSONDecodeError:
230
+ pass
231
+
232
+ # Default - string
233
+ return value_str
@@ -0,0 +1,14 @@
1
+ """
2
+ Command dispatchers for registering and executing commands.
3
+
4
+ This module contains base classes and implementations of command dispatchers
5
+ that are responsible for registering and executing commands.
6
+ """
7
+
8
+ from .base_dispatcher import BaseDispatcher
9
+ from .json_rpc_dispatcher import JsonRpcDispatcher
10
+
11
+ __all__ = [
12
+ 'BaseDispatcher',
13
+ 'JsonRpcDispatcher',
14
+ ]
@@ -0,0 +1,85 @@
1
+ """
2
+ Base command dispatcher class.
3
+ """
4
+ from abc import ABC, abstractmethod
5
+ from typing import Dict, Any, Callable, List, Optional
6
+
7
+ class BaseDispatcher(ABC):
8
+ """
9
+ Abstract base class for command dispatchers.
10
+
11
+ Defines the interface that all command dispatchers must implement.
12
+ Dispatchers are responsible for registering and executing commands.
13
+ """
14
+
15
+ @abstractmethod
16
+ def register_handler(
17
+ self,
18
+ command: str,
19
+ handler: Callable,
20
+ description: str = "",
21
+ summary: str = "",
22
+ params: Dict[str, Any] = None
23
+ ) -> None:
24
+ """
25
+ Registers a command handler.
26
+
27
+ Args:
28
+ command: Command name
29
+ handler: Command handler function
30
+ description: Command description
31
+ summary: Brief command summary
32
+ params: Command parameters description
33
+ """
34
+ pass
35
+
36
+ @abstractmethod
37
+ def execute(self, command: str, **kwargs) -> Any:
38
+ """
39
+ Executes a command with the specified parameters.
40
+
41
+ Args:
42
+ command: Command name
43
+ **kwargs: Command parameters
44
+
45
+ Returns:
46
+ Any: Command execution result
47
+
48
+ Raises:
49
+ CommandNotFoundError: If command is not found
50
+ CommandExecutionError: On command execution error
51
+ """
52
+ pass
53
+
54
+ @abstractmethod
55
+ def get_valid_commands(self) -> List[str]:
56
+ """
57
+ Returns a list of all registered command names.
58
+
59
+ Returns:
60
+ List[str]: List of command names
61
+ """
62
+ pass
63
+
64
+ @abstractmethod
65
+ def get_command_info(self, command: str) -> Optional[Dict[str, Any]]:
66
+ """
67
+ Returns information about a command.
68
+
69
+ Args:
70
+ command: Command name
71
+
72
+ Returns:
73
+ Optional[Dict[str, Any]]: Command information or None if command not found
74
+ """
75
+ pass
76
+
77
+ @abstractmethod
78
+ def get_commands_info(self) -> Dict[str, Dict[str, Any]]:
79
+ """
80
+ Returns information about all registered commands.
81
+
82
+ Returns:
83
+ Dict[str, Dict[str, Any]]: Dictionary {command_name: information}
84
+ """
85
+ pass
@@ -0,0 +1,198 @@
1
+ """
2
+ Implementation of a JSON-RPC based command dispatcher.
3
+ """
4
+ from typing import Dict, Any, Callable, List, Optional, Union
5
+ import inspect
6
+ import logging
7
+ import traceback
8
+ from .base_dispatcher import BaseDispatcher
9
+
10
+ logger = logging.getLogger("command_registry")
11
+
12
+ class CommandError(Exception):
13
+ """Base class for command errors"""
14
+ pass
15
+
16
+ class CommandNotFoundError(CommandError):
17
+ """Error raised when attempting to execute a non-existent command"""
18
+ pass
19
+
20
+ class CommandExecutionError(CommandError):
21
+ """Error raised during command execution"""
22
+ pass
23
+
24
+ class JsonRpcDispatcher(BaseDispatcher):
25
+ """
26
+ JSON-RPC based command dispatcher.
27
+
28
+ Implements the BaseDispatcher interface for handling commands in JSON-RPC 2.0 format.
29
+ Supports registration, execution, and retrieval of command information.
30
+ """
31
+
32
+ def __init__(self):
33
+ """Initializes a new dispatcher instance"""
34
+ self._handlers = {}
35
+ self._metadata = {}
36
+
37
+ # Register the built-in help command
38
+ self.register_handler(
39
+ command="help",
40
+ handler=self._help_command,
41
+ description="Returns information about available commands",
42
+ summary="Command help",
43
+ params={
44
+ "command": {
45
+ "type": "string",
46
+ "description": "Command name for detailed information",
47
+ "required": False
48
+ }
49
+ }
50
+ )
51
+
52
+ def register_handler(
53
+ self,
54
+ command: str,
55
+ handler: Callable,
56
+ description: str = "",
57
+ summary: str = "",
58
+ params: Dict[str, Any] = None
59
+ ) -> None:
60
+ """
61
+ Registers a command handler.
62
+
63
+ Args:
64
+ command: Command name
65
+ handler: Command handler function
66
+ description: Command description
67
+ summary: Brief command summary
68
+ params: Command parameters description
69
+ """
70
+ if not params:
71
+ params = {}
72
+
73
+ # Save the handler
74
+ self._handlers[command] = handler
75
+
76
+ # Save metadata
77
+ self._metadata[command] = {
78
+ "description": description,
79
+ "summary": summary or command.replace("_", " ").title(),
80
+ "params": params
81
+ }
82
+
83
+ logger.debug(f"Registered command: {command}")
84
+
85
+ def execute(self, command: str, **kwargs) -> Any:
86
+ """
87
+ Executes a command with the specified parameters.
88
+
89
+ Args:
90
+ command: Command name
91
+ **kwargs: Command parameters
92
+
93
+ Returns:
94
+ Any: Command execution result
95
+
96
+ Raises:
97
+ CommandNotFoundError: If command is not found
98
+ CommandExecutionError: On command execution error
99
+ """
100
+ # Check if command exists
101
+ if command not in self._handlers:
102
+ raise CommandNotFoundError(f"Command '{command}' not found")
103
+
104
+ handler = self._handlers[command]
105
+
106
+ try:
107
+ # Get function signature
108
+ sig = inspect.signature(handler)
109
+
110
+ # If function accepts params dictionary, pass all parameters in it
111
+ if len(sig.parameters) == 1 and list(sig.parameters.keys())[0] == 'params':
112
+ return handler(params=kwargs)
113
+
114
+ # Otherwise pass parameters as named arguments
115
+ return handler(**kwargs)
116
+ except Exception as e:
117
+ # Log the error
118
+ logger.error(f"Error executing command '{command}': {str(e)}")
119
+ logger.debug(traceback.format_exc())
120
+
121
+ # Re-raise the exception
122
+ raise CommandExecutionError(f"Error executing command '{command}': {str(e)}")
123
+
124
+ def get_valid_commands(self) -> List[str]:
125
+ """
126
+ Returns a list of all registered command names.
127
+
128
+ Returns:
129
+ List[str]: List of command names
130
+ """
131
+ return list(self._handlers.keys())
132
+
133
+ def get_command_info(self, command: str) -> Optional[Dict[str, Any]]:
134
+ """
135
+ Returns information about a command.
136
+
137
+ Args:
138
+ command: Command name
139
+
140
+ Returns:
141
+ Optional[Dict[str, Any]]: Command information or None if command not found
142
+ """
143
+ if command not in self._metadata:
144
+ return None
145
+
146
+ return self._metadata[command]
147
+
148
+ def get_commands_info(self) -> Dict[str, Dict[str, Any]]:
149
+ """
150
+ Returns information about all registered commands.
151
+
152
+ Returns:
153
+ Dict[str, Dict[str, Any]]: Dictionary {command_name: information}
154
+ """
155
+ return self._metadata.copy()
156
+
157
+ def _help_command(self, params: Dict[str, Any] = None) -> Dict[str, Any]:
158
+ """
159
+ Built-in help command for getting command information.
160
+
161
+ Args:
162
+ params: Command parameters
163
+ command: Command name for detailed information
164
+
165
+ Returns:
166
+ Dict[str, Any]: Command help information
167
+ """
168
+ if not params:
169
+ params = {}
170
+
171
+ # If specific command is specified, return information only about it
172
+ if "command" in params and params["command"]:
173
+ command = params["command"]
174
+ if command not in self._metadata:
175
+ return {
176
+ "error": f"Command '{command}' not found",
177
+ "available_commands": list(self._metadata.keys())
178
+ }
179
+
180
+ return {
181
+ "command": command,
182
+ "info": self._metadata[command]
183
+ }
184
+
185
+ # Otherwise return brief information about all commands
186
+ commands_info = {}
187
+ for cmd, info in self._metadata.items():
188
+ commands_info[cmd] = {
189
+ "summary": info["summary"],
190
+ "description": info["description"],
191
+ "params_count": len(info["params"])
192
+ }
193
+
194
+ return {
195
+ "commands": commands_info,
196
+ "total": len(commands_info),
197
+ "note": "Use the 'command' parameter to get detailed information about a specific command"
198
+ }
generators/__init__.py ADDED
@@ -0,0 +1,14 @@
1
+ """
2
+ Генераторы API на основе команд и их метаданных.
3
+
4
+ Этот модуль содержит классы для автоматической генерации API интерфейсов
5
+ (REST, OpenAPI и др.) на основе зарегистрированных команд.
6
+ """
7
+
8
+ from command_registry.generators.rest_api_generator import RestApiGenerator
9
+ from command_registry.generators.openapi_generator import OpenApiGenerator
10
+
11
+ __all__ = [
12
+ 'RestApiGenerator',
13
+ 'OpenApiGenerator',
14
+ ]