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.
- adapters/__init__.py +16 -0
- analyzers/__init__.py +14 -0
- analyzers/docstring_analyzer.py +199 -0
- analyzers/type_analyzer.py +151 -0
- cli/__init__.py +12 -0
- cli/__main__.py +79 -0
- cli/command_runner.py +233 -0
- dispatchers/__init__.py +14 -0
- dispatchers/base_dispatcher.py +85 -0
- dispatchers/json_rpc_dispatcher.py +198 -0
- generators/__init__.py +14 -0
- generators/endpoint_generator.py +172 -0
- generators/openapi_generator.py +254 -0
- generators/rest_api_generator.py +207 -0
- mcp_proxy_adapter-1.0.0.dist-info/METADATA +262 -0
- mcp_proxy_adapter-1.0.0.dist-info/RECORD +28 -0
- mcp_proxy_adapter-1.0.0.dist-info/WHEEL +5 -0
- mcp_proxy_adapter-1.0.0.dist-info/licenses/LICENSE +21 -0
- mcp_proxy_adapter-1.0.0.dist-info/top_level.txt +7 -0
- openapi_schema/__init__.py +38 -0
- openapi_schema/command_registry.py +312 -0
- openapi_schema/rest_schema.py +510 -0
- openapi_schema/rpc_generator.py +307 -0
- openapi_schema/rpc_schema.py +416 -0
- validators/__init__.py +14 -0
- validators/base_validator.py +23 -0
- validators/docstring_validator.py +75 -0
- validators/metadata_validator.py +76 -0
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
|
dispatchers/__init__.py
ADDED
@@ -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
|
+
]
|