mcp-proxy-adapter 2.1.16__py3-none-any.whl → 3.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 (84) hide show
  1. examples/__init__.py +19 -0
  2. examples/anti_patterns/README.md +51 -0
  3. examples/anti_patterns/__init__.py +9 -0
  4. examples/anti_patterns/bad_design/README.md +72 -0
  5. examples/anti_patterns/bad_design/global_state.py +170 -0
  6. examples/anti_patterns/bad_design/monolithic_command.py +272 -0
  7. examples/basic_example/README.md +131 -0
  8. examples/basic_example/__init__.py +8 -0
  9. examples/basic_example/commands/__init__.py +5 -0
  10. examples/basic_example/commands/echo_command.py +95 -0
  11. examples/basic_example/commands/math_command.py +151 -0
  12. examples/basic_example/commands/time_command.py +152 -0
  13. examples/basic_example/config.json +21 -0
  14. examples/basic_example/config.yaml +20 -0
  15. examples/basic_example/docs/EN/README.md +136 -0
  16. examples/basic_example/docs/RU/README.md +136 -0
  17. examples/basic_example/main.py +50 -0
  18. examples/basic_example/server.py +45 -0
  19. examples/basic_example/tests/conftest.py +243 -0
  20. examples/commands/echo_command.py +52 -0
  21. examples/commands/echo_result.py +65 -0
  22. examples/commands/get_date_command.py +98 -0
  23. examples/commands/new_uuid4_command.py +91 -0
  24. examples/complete_example/Dockerfile +24 -0
  25. examples/complete_example/README.md +92 -0
  26. examples/complete_example/__init__.py +8 -0
  27. examples/complete_example/commands/__init__.py +5 -0
  28. examples/complete_example/commands/system_command.py +327 -0
  29. examples/complete_example/config.json +41 -0
  30. examples/complete_example/configs/config.dev.yaml +40 -0
  31. examples/complete_example/configs/config.docker.yaml +40 -0
  32. examples/complete_example/docker-compose.yml +35 -0
  33. examples/complete_example/main.py +67 -0
  34. examples/complete_example/requirements.txt +20 -0
  35. examples/complete_example/server.py +85 -0
  36. examples/minimal_example/README.md +51 -0
  37. examples/minimal_example/__init__.py +8 -0
  38. examples/minimal_example/config.json +21 -0
  39. examples/minimal_example/config.yaml +26 -0
  40. examples/minimal_example/main.py +67 -0
  41. examples/minimal_example/simple_server.py +124 -0
  42. examples/minimal_example/tests/conftest.py +171 -0
  43. examples/minimal_example/tests/test_hello_command.py +111 -0
  44. examples/minimal_example/tests/test_integration.py +183 -0
  45. examples/server.py +69 -0
  46. examples/simple_server.py +137 -0
  47. examples/test_server.py +126 -0
  48. mcp_proxy_adapter/__init__.py +33 -1
  49. mcp_proxy_adapter/config.py +186 -0
  50. mcp_proxy_adapter/custom_openapi.py +125 -0
  51. mcp_proxy_adapter/framework.py +109 -0
  52. mcp_proxy_adapter/openapi.py +403 -0
  53. mcp_proxy_adapter/version.py +3 -0
  54. mcp_proxy_adapter-3.0.0.dist-info/METADATA +200 -0
  55. mcp_proxy_adapter-3.0.0.dist-info/RECORD +58 -0
  56. {mcp_proxy_adapter-2.1.16.dist-info → mcp_proxy_adapter-3.0.0.dist-info}/top_level.txt +1 -0
  57. mcp_proxy_adapter/adapter.py +0 -697
  58. mcp_proxy_adapter/analyzers/__init__.py +0 -1
  59. mcp_proxy_adapter/analyzers/docstring_analyzer.py +0 -199
  60. mcp_proxy_adapter/analyzers/type_analyzer.py +0 -151
  61. mcp_proxy_adapter/dispatchers/__init__.py +0 -1
  62. mcp_proxy_adapter/dispatchers/base_dispatcher.py +0 -85
  63. mcp_proxy_adapter/dispatchers/json_rpc_dispatcher.py +0 -235
  64. mcp_proxy_adapter/examples/analyze_config.py +0 -141
  65. mcp_proxy_adapter/examples/basic_integration.py +0 -155
  66. mcp_proxy_adapter/examples/docstring_and_schema_example.py +0 -69
  67. mcp_proxy_adapter/examples/extension_example.py +0 -72
  68. mcp_proxy_adapter/examples/help_best_practices.py +0 -67
  69. mcp_proxy_adapter/examples/help_usage.py +0 -64
  70. mcp_proxy_adapter/examples/mcp_proxy_client.py +0 -131
  71. mcp_proxy_adapter/examples/openapi_server.py +0 -383
  72. mcp_proxy_adapter/examples/project_structure_example.py +0 -47
  73. mcp_proxy_adapter/examples/testing_example.py +0 -64
  74. mcp_proxy_adapter/models.py +0 -47
  75. mcp_proxy_adapter/registry.py +0 -439
  76. mcp_proxy_adapter/schema.py +0 -257
  77. mcp_proxy_adapter/testing_utils.py +0 -112
  78. mcp_proxy_adapter/validators/__init__.py +0 -1
  79. mcp_proxy_adapter/validators/docstring_validator.py +0 -75
  80. mcp_proxy_adapter/validators/metadata_validator.py +0 -76
  81. mcp_proxy_adapter-2.1.16.dist-info/METADATA +0 -341
  82. mcp_proxy_adapter-2.1.16.dist-info/RECORD +0 -30
  83. {mcp_proxy_adapter-2.1.16.dist-info → mcp_proxy_adapter-3.0.0.dist-info}/WHEEL +0 -0
  84. {mcp_proxy_adapter-2.1.16.dist-info → mcp_proxy_adapter-3.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -1 +0,0 @@
1
-
@@ -1,199 +0,0 @@
1
- """
2
- Docstring analyzer for extracting information from function documentation.
3
- """
4
- import inspect
5
- from typing import Dict, Any, Optional, Callable, List, Tuple
6
- import docstring_parser
7
-
8
- class DocstringAnalyzer:
9
- """
10
- Docstring analyzer for extracting metadata from function documentation.
11
-
12
- This class is responsible for analyzing command handler function docstrings
13
- and extracting function descriptions, parameters, and return values.
14
- """
15
-
16
- def analyze(self, handler: Callable) -> Dict[str, Any]:
17
- """
18
- Analyzes function docstring and returns metadata.
19
-
20
- Args:
21
- handler: Handler function to analyze
22
-
23
- Returns:
24
- Dict[str, Any]: Metadata extracted from docstring
25
- """
26
- result = {
27
- "description": "",
28
- "summary": "",
29
- "parameters": {},
30
- "returns": {
31
- "description": ""
32
- }
33
- }
34
-
35
- # Get function signature
36
- sig = inspect.signature(handler)
37
-
38
- # Get docstring
39
- docstring = handler.__doc__ or ""
40
-
41
- # Parse docstring
42
- try:
43
- parsed_doc = docstring_parser.parse(docstring)
44
-
45
- # Extract general function description
46
- if parsed_doc.short_description:
47
- result["summary"] = parsed_doc.short_description
48
- result["description"] = parsed_doc.short_description
49
-
50
- if parsed_doc.long_description:
51
- # If both short and long descriptions exist, combine them
52
- if result["description"]:
53
- result["description"] = f"{result['description']}\n\n{parsed_doc.long_description}"
54
- else:
55
- result["description"] = parsed_doc.long_description
56
-
57
- # Extract parameter information
58
- for param in parsed_doc.params:
59
- param_name = param.arg_name
60
- param_desc = param.description or f"Parameter {param_name}"
61
- param_type = None
62
-
63
- # If parameter type is specified in docstring, use it
64
- if param.type_name:
65
- param_type = self._parse_type_from_docstring(param.type_name)
66
-
67
- # Add parameter to metadata
68
- if param_name not in result["parameters"]:
69
- result["parameters"][param_name] = {}
70
-
71
- result["parameters"][param_name]["description"] = param_desc
72
-
73
- if param_type:
74
- result["parameters"][param_name]["type"] = param_type
75
-
76
- # Extract return value information
77
- if parsed_doc.returns:
78
- result["returns"]["description"] = parsed_doc.returns.description or "Return value"
79
-
80
- if parsed_doc.returns.type_name:
81
- result["returns"]["type"] = self._parse_type_from_docstring(parsed_doc.returns.type_name)
82
-
83
- except Exception as e:
84
- # In case of parsing error, use docstring as is
85
- if docstring:
86
- result["description"] = docstring.strip()
87
-
88
- # Fill parameter information from signature if not found in docstring
89
- for param_name, param in sig.parameters.items():
90
- # Skip self for methods
91
- if param_name == 'self':
92
- continue
93
-
94
- # If parameter not yet added to metadata, add it
95
- if param_name not in result["parameters"]:
96
- result["parameters"][param_name] = {
97
- "description": f"Parameter {param_name}"
98
- }
99
-
100
- # Determine if parameter is required
101
- required = param.default == inspect.Parameter.empty
102
- result["parameters"][param_name]["required"] = required
103
-
104
- # Add default value if exists
105
- if param.default != inspect.Parameter.empty:
106
- # Some default values cannot be serialized to JSON
107
- # So we check if the value can be serialized
108
- if param.default is None or isinstance(param.default, (str, int, float, bool, list, dict)):
109
- result["parameters"][param_name]["default"] = param.default
110
-
111
- return result
112
-
113
- def validate(self, handler: Callable) -> Tuple[bool, List[str]]:
114
- """
115
- Validates that function docstring matches its formal parameters.
116
-
117
- Args:
118
- handler: Command handler function
119
-
120
- Returns:
121
- Tuple[bool, List[str]]: Validity flag and list of errors
122
- """
123
- errors = []
124
-
125
- # Get function formal parameters
126
- sig = inspect.signature(handler)
127
- formal_params = list(sig.parameters.keys())
128
-
129
- # Skip self parameter for methods
130
- if formal_params and formal_params[0] == 'self':
131
- formal_params = formal_params[1:]
132
-
133
- # Parse docstring
134
- docstring = handler.__doc__ or ""
135
- parsed_doc = docstring_parser.parse(docstring)
136
-
137
- # Check for function description
138
- if not parsed_doc.short_description and not parsed_doc.long_description:
139
- errors.append(f"Missing function description")
140
-
141
- # Get parameters from docstring
142
- doc_params = {param.arg_name: param for param in parsed_doc.params}
143
-
144
- # Check that all formal parameters are described in docstring
145
- for param in formal_params:
146
- if param not in doc_params and param != 'params': # 'params' is special case, can be dictionary of all parameters
147
- errors.append(f"Parameter '{param}' not described in function docstring")
148
-
149
- # Check for returns in docstring
150
- if not parsed_doc.returns and not any(t.type_name == 'Returns' for t in parsed_doc.meta):
151
- errors.append(f"Missing return value description in function docstring")
152
-
153
- return len(errors) == 0, errors
154
-
155
- def _parse_type_from_docstring(self, type_str: str) -> str:
156
- """
157
- Parses type from string representation in docstring.
158
-
159
- Args:
160
- type_str: String representation of type
161
-
162
- Returns:
163
- str: Type in OpenAPI format
164
- """
165
- # Simple mapping of string types to OpenAPI types
166
- type_map = {
167
- "str": "string",
168
- "string": "string",
169
- "int": "integer",
170
- "integer": "integer",
171
- "float": "number",
172
- "number": "number",
173
- "bool": "boolean",
174
- "boolean": "boolean",
175
- "list": "array",
176
- "array": "array",
177
- "dict": "object",
178
- "object": "object",
179
- "none": "null",
180
- "null": "null",
181
- }
182
-
183
- # Convert to lowercase and remove spaces
184
- cleaned_type = type_str.lower().strip()
185
-
186
- # Check for simple types
187
- if cleaned_type in type_map:
188
- return type_map[cleaned_type]
189
-
190
- # Check for List[X]
191
- if cleaned_type.startswith("list[") or cleaned_type.startswith("array["):
192
- return "array"
193
-
194
- # Check for Dict[X, Y]
195
- if cleaned_type.startswith("dict[") or cleaned_type.startswith("object["):
196
- return "object"
197
-
198
- # Default to object
199
- return "object"
@@ -1,151 +0,0 @@
1
- """
2
- Type analyzer for extracting information from function type annotations.
3
- """
4
- import inspect
5
- from typing import Dict, Any, List, Optional, Callable, Union, get_origin, get_args, get_type_hints
6
-
7
- class TypeAnalyzer:
8
- """
9
- Type analyzer for extracting information from function type annotations.
10
-
11
- This class is responsible for analyzing type annotations of command handler functions
12
- and converting them to JSON Schema/OpenAPI type format.
13
- """
14
-
15
- def __init__(self):
16
- # Mapping Python types to OpenAPI types
17
- self.type_map = {
18
- str: "string",
19
- int: "integer",
20
- float: "number",
21
- bool: "boolean",
22
- list: "array",
23
- dict: "object",
24
- Any: "object",
25
- None: "null",
26
- }
27
-
28
- def analyze(self, handler: Callable) -> Dict[str, Any]:
29
- """
30
- Analyzes function type annotations and returns metadata.
31
-
32
- Args:
33
- handler: Handler function to analyze
34
-
35
- Returns:
36
- Dict[str, Any]: Metadata about parameter types and return value
37
- """
38
- result = {
39
- "parameters": {},
40
- "returns": None
41
- }
42
-
43
- # Get function signature
44
- sig = inspect.signature(handler)
45
-
46
- # Get type annotations
47
- type_hints = self._get_type_hints(handler)
48
-
49
- # Analyze parameters
50
- for param_name, param in sig.parameters.items():
51
- # Skip self for methods
52
- if param_name == 'self':
53
- continue
54
-
55
- # If parameter is named params, assume it's a dictionary of all parameters
56
- if param_name == 'params':
57
- continue
58
-
59
- # Determine if parameter is required
60
- required = param.default == inspect.Parameter.empty
61
-
62
- # Determine parameter type
63
- param_type = "object" # Default type
64
-
65
- if param_name in type_hints:
66
- param_type = self._map_type_to_openapi(type_hints[param_name])
67
-
68
- # Create parameter metadata
69
- param_metadata = {
70
- "type": param_type,
71
- "required": required
72
- }
73
-
74
- # Add default value if exists
75
- if param.default != inspect.Parameter.empty:
76
- # Some default values cannot be serialized to JSON
77
- # So we convert them to string representation for such cases
78
- if param.default is None or isinstance(param.default, (str, int, float, bool, list, dict)):
79
- param_metadata["default"] = param.default
80
-
81
- # Add parameter to metadata
82
- result["parameters"][param_name] = param_metadata
83
-
84
- # Analyze return value
85
- if 'return' in type_hints:
86
- result["returns"] = self._map_type_to_openapi(type_hints['return'])
87
-
88
- return result
89
-
90
- def _get_type_hints(self, handler: Callable) -> Dict[str, Any]:
91
- """
92
- Gets type annotations of a function.
93
-
94
- Args:
95
- handler: Handler function
96
-
97
- Returns:
98
- Dict[str, Any]: Type annotations
99
- """
100
- try:
101
- return get_type_hints(handler)
102
- except Exception:
103
- # If failed to get annotations via get_type_hints,
104
- # extract them manually from __annotations__
105
- return getattr(handler, "__annotations__", {})
106
-
107
- def _map_type_to_openapi(self, type_hint: Any) -> Union[str, Dict[str, Any]]:
108
- """
109
- Converts Python type to OpenAPI type.
110
-
111
- Args:
112
- type_hint: Python type
113
-
114
- Returns:
115
- Union[str, Dict[str, Any]]: OpenAPI type string representation or schema
116
- """
117
- # Check for None
118
- if type_hint is None:
119
- return "null"
120
-
121
- # Handle primitive types
122
- if type_hint in self.type_map:
123
- return self.type_map[type_hint]
124
-
125
- # Check for generic types
126
- origin = get_origin(type_hint)
127
- if origin is not None:
128
- # Handle List[X], Dict[X, Y], etc.
129
- if origin in (list, List):
130
- args = get_args(type_hint)
131
- if args:
132
- item_type = self._map_type_to_openapi(args[0])
133
- return {
134
- "type": "array",
135
- "items": item_type if isinstance(item_type, dict) else {"type": item_type}
136
- }
137
- return "array"
138
- elif origin in (dict, Dict):
139
- # For dict we just return object, as OpenAPI
140
- # doesn't have a direct equivalent for Dict[X, Y]
141
- return "object"
142
- elif origin is Union:
143
- # For Union we take the first type that is not None
144
- args = get_args(type_hint)
145
- for arg in args:
146
- if arg is not type(None):
147
- return self._map_type_to_openapi(arg)
148
- return "object"
149
-
150
- # Default to object
151
- return "object"
@@ -1 +0,0 @@
1
-
@@ -1,85 +0,0 @@
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
@@ -1,235 +0,0 @@
1
- """
2
- Implementation of a JSON-RPC based command dispatcher.
3
-
4
- CHANGELOG:
5
- - 2024-06-13: execute() now always returns awaitable. If handler is sync and for any reason result is not awaitable, it is wrapped in an async function and awaited. This guarantees await-safety for all handler types and fixes 'object ... can't be used in await expression' errors in all environments.
6
- """
7
- from typing import Dict, Any, Callable, List, Optional, Union
8
- import inspect
9
- import logging
10
- import traceback
11
- from .base_dispatcher import BaseDispatcher
12
- import asyncio
13
-
14
- logger = logging.getLogger("command_registry")
15
-
16
- print('[DEBUG] LOADED json_rpc_dispatcher.py')
17
-
18
- class CommandError(Exception):
19
- """Base class for command errors"""
20
- pass
21
-
22
- class CommandNotFoundError(CommandError):
23
- """Error raised when attempting to execute a non-existent command"""
24
- pass
25
-
26
- class CommandExecutionError(CommandError):
27
- """Error raised during command execution"""
28
- pass
29
-
30
- class JsonRpcDispatcher(BaseDispatcher):
31
- """
32
- JSON-RPC based command dispatcher.
33
-
34
- Implements the BaseDispatcher interface for handling commands in JSON-RPC 2.0 format.
35
- Supports registration, execution, and retrieval of command information.
36
-
37
- Best practice:
38
- ----------------
39
- Register handlers explicitly using register_handler (no decorators!).
40
- Both sync and async handlers are supported.
41
-
42
- Example:
43
- import asyncio
44
- from mcp_proxy_adapter.dispatchers.json_rpc_dispatcher import JsonRpcDispatcher
45
-
46
- def sync_handler(x):
47
- return x + 1
48
-
49
- async def async_handler(x):
50
- await asyncio.sleep(0.1)
51
- return x * 2
52
-
53
- dispatcher = JsonRpcDispatcher()
54
- dispatcher.register_handler('sync', sync_handler, description='Sync handler')
55
- dispatcher.register_handler('async', async_handler, description='Async handler')
56
-
57
- # Call sync handler
58
- result_sync = asyncio.run(dispatcher.execute('sync', x=10))
59
- print(result_sync) # 11
60
-
61
- # Call async handler
62
- result_async = asyncio.run(dispatcher.execute('async', x=10))
63
- print(result_async) # 20
64
- """
65
-
66
- def __init__(self):
67
- """Initializes a new dispatcher instance"""
68
- self._handlers = {}
69
- self._metadata = {}
70
-
71
- # Register the built-in help command
72
- self.register_handler(
73
- command="help",
74
- handler=self._help_command,
75
- description=(
76
- "Returns information about available commands.\n"
77
- "Best practice: Register handlers explicitly using register_handler (no decorators).\n"
78
- "Example: dispatcher.register_handler('mycmd', my_handler, description='...')"
79
- ),
80
- summary="Command help",
81
- params={
82
- "cmdname": {
83
- "type": "string",
84
- "description": "Command name for detailed information",
85
- "required": False
86
- }
87
- }
88
- )
89
-
90
- def register_handler(
91
- self,
92
- command: str,
93
- handler: Callable,
94
- description: str = "",
95
- summary: str = "",
96
- params: Dict[str, Any] = None
97
- ) -> None:
98
- """
99
- Registers a command handler.
100
-
101
- Args:
102
- command: Command name
103
- handler: Command handler function
104
- description: Command description
105
- summary: Brief command summary
106
- params: Command parameters description
107
- """
108
- if not params:
109
- params = {}
110
-
111
- # Save the handler
112
- self._handlers[command] = handler
113
-
114
- # Save metadata
115
- self._metadata[command] = {
116
- "description": description,
117
- "summary": summary or command.replace("_", " ").title(),
118
- "params": params
119
- }
120
-
121
- logger.debug(f"Registered command: {command}")
122
-
123
- async def _call_handler_always_awaitable(self, handler, kwargs):
124
- loop = asyncio.get_running_loop()
125
- sig = inspect.signature(handler)
126
- params = sig.parameters
127
- try:
128
- if inspect.iscoroutinefunction(handler):
129
- if len(params) == 1 and 'params' in params:
130
- result = handler(params=kwargs)
131
- else:
132
- result = handler(**kwargs)
133
- else:
134
- if len(params) == 1 and 'params' in params:
135
- result = loop.run_in_executor(None, lambda: handler(params=kwargs))
136
- else:
137
- result = loop.run_in_executor(None, lambda: handler(**kwargs))
138
- if inspect.isawaitable(result):
139
- return await result
140
- else:
141
- async def _return_sync():
142
- return result
143
- return await _return_sync()
144
- except Exception as e:
145
- raise e
146
-
147
- async def execute(self, command: str, **kwargs) -> Any:
148
- """
149
- Executes a command with the specified parameters.
150
- """
151
- if command not in self._handlers:
152
- raise CommandNotFoundError(f"Command '{command}' not found")
153
- handler = self._handlers[command]
154
- try:
155
- return await self._call_handler_always_awaitable(handler, kwargs)
156
- except Exception as e:
157
- logger.error(f"Error executing command '{command}': {str(e)}")
158
- logger.debug(traceback.format_exc())
159
- raise CommandExecutionError(f"Error executing command '{command}': {str(e)}")
160
-
161
- def get_valid_commands(self) -> List[str]:
162
- """
163
- Returns a list of all registered command names.
164
-
165
- Returns:
166
- List[str]: List of command names
167
- """
168
- return list(self._handlers.keys())
169
-
170
- def get_command_info(self, command: str) -> Optional[Dict[str, Any]]:
171
- """
172
- Returns information about a command.
173
-
174
- Args:
175
- command: Command name
176
-
177
- Returns:
178
- Optional[Dict[str, Any]]: Command information or None if command not found
179
- """
180
- if command not in self._metadata:
181
- return None
182
-
183
- return self._metadata[command]
184
-
185
- def get_commands_info(self) -> Dict[str, Dict[str, Any]]:
186
- """
187
- Returns information about all registered commands.
188
-
189
- Returns:
190
- Dict[str, Dict[str, Any]]: Dictionary {command_name: information}
191
- """
192
- return self._metadata.copy()
193
-
194
- def _help_command(self, params: Dict[str, Any] = None) -> Dict[str, Any]:
195
- """
196
- Built-in help command for getting command information.
197
-
198
- Args:
199
- params: Command parameters
200
- cmdname: Command name for detailed information
201
-
202
- Returns:
203
- Dict[str, Any]: Command help information
204
- """
205
- if not params:
206
- params = {}
207
-
208
- # If specific command is specified, return information only about it
209
- if "cmdname" in params and params["cmdname"]:
210
- command = params["cmdname"]
211
- if command not in self._metadata:
212
- return {
213
- "error": f"Command '{command}' not found",
214
- "available_commands": list(self._metadata.keys())
215
- }
216
-
217
- return {
218
- "command": command,
219
- "info": self._metadata[command]
220
- }
221
-
222
- # Otherwise return brief information about all commands
223
- commands_info = {}
224
- for cmd, info in self._metadata.items():
225
- commands_info[cmd] = {
226
- "summary": info["summary"],
227
- "description": info["description"],
228
- "params_count": len(info["params"])
229
- }
230
-
231
- return {
232
- "commands": commands_info,
233
- "total": len(commands_info),
234
- "note": "Use the 'cmdname' parameter to get detailed information about a specific command"
235
- }