mcp-proxy-adapter 3.1.4__py3-none-any.whl → 3.1.6__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.
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Скрипт для проверки схемы команды help в сервисе VSTL
4
+ """
5
+
6
+ import json
7
+ import requests
8
+ from typing import Dict, Any, Optional
9
+
10
+ # URL и заголовки для VSTL сервиса
11
+ VSTL_URL = "http://localhost:8007/cmd"
12
+ HEADERS = {"Content-Type": "application/json"}
13
+
14
+ def send_json_rpc(method: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
15
+ """
16
+ Отправляет JSON-RPC запрос и возвращает ответ
17
+
18
+ Args:
19
+ method: Имя метода
20
+ params: Параметры запроса
21
+
22
+ Returns:
23
+ Dict[str, Any]: Ответ сервера
24
+ """
25
+ # Формируем JSON-RPC запрос
26
+ payload = {
27
+ "jsonrpc": "2.0",
28
+ "method": method,
29
+ "id": 1
30
+ }
31
+
32
+ # Добавляем параметры, если они есть
33
+ if params is not None:
34
+ payload["params"] = params
35
+
36
+ print(f"Отправляем запрос: {json.dumps(payload, indent=2)}")
37
+
38
+ # Отправляем запрос
39
+ response = requests.post(VSTL_URL, json=payload, headers=HEADERS)
40
+
41
+ # Возвращаем ответ
42
+ return response.json()
43
+
44
+ def test_help_command():
45
+ """
46
+ Проверяет команду help в различных вариантах
47
+ """
48
+ print("\n=== Проверка команды help без параметров ===")
49
+ response = send_json_rpc("help")
50
+ print(f"Ответ: {json.dumps(response, indent=2)}")
51
+
52
+ print("\n=== Проверка команды help с пустыми параметрами ===")
53
+ response = send_json_rpc("help", {})
54
+ print(f"Ответ: {json.dumps(response, indent=2)}")
55
+
56
+ print("\n=== Проверка команды help с параметром cmdname=null ===")
57
+ response = send_json_rpc("help", {"cmdname": None})
58
+ print(f"Ответ: {json.dumps(response, indent=2)}")
59
+
60
+ print("\n=== Проверка команды help с параметром cmdname=\"config\" ===")
61
+ response = send_json_rpc("help", {"cmdname": "config"})
62
+ print(f"Ответ: {json.dumps(response, indent=2)}")
63
+
64
+ # Проверяем workaround с передачей строки "null"
65
+ print("\n=== Проверка команды help с параметром cmdname=\"null\" ===")
66
+ response = send_json_rpc("help", {"cmdname": "null"})
67
+ print(f"Ответ: {json.dumps(response, indent=2)}")
68
+
69
+ def check_schema():
70
+ """
71
+ Проверяет схему команд и ищет обязательные параметры
72
+ """
73
+ print("\n=== Проверка схемы команд ===")
74
+
75
+ # Запрашиваем список всех доступных команд
76
+ health_response = send_json_rpc("health")
77
+ print(f"Здоровье сервиса: {json.dumps(health_response, indent=2)}")
78
+
79
+ # Проверяем команду config для получения схемы
80
+ config_response = send_json_rpc("config", {"operation": "get"})
81
+ print(f"Конфигурация: {json.dumps(config_response, indent=2)}")
82
+
83
+ # Пробуем с явным указанием строки вместо null
84
+ print("\n=== Проверка команды help с cmdname=\"\" (пустая строка) ===")
85
+ response = send_json_rpc("help", {"cmdname": ""})
86
+ print(f"Ответ: {json.dumps(response, indent=2)}")
87
+
88
+ # Создаем свой вариант с переопределением параметров
89
+ print("\n=== Проверка специального запроса с kwargs=null ===")
90
+ # Прямая отправка JSON с null значением для kwargs
91
+ special_payload = {
92
+ "jsonrpc": "2.0",
93
+ "method": "help",
94
+ "params": {"kwargs": None},
95
+ "id": 1
96
+ }
97
+ response = requests.post(VSTL_URL, json=special_payload, headers=HEADERS)
98
+ print(f"Ответ: {json.dumps(response.json(), indent=2)}")
99
+
100
+ if __name__ == "__main__":
101
+ print("=== Диагностика проблемы с командой help в сервисе VSTL ===")
102
+ try:
103
+ test_help_command()
104
+ check_schema()
105
+ except Exception as e:
106
+ print(f"Ошибка при выполнении: {e}")
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Скрипт для исправления ошибки 'name null is not defined' в сервисе VSTL.
4
+
5
+ Этот скрипт обходит проблему с обработкой null в сервисе vstl
6
+ при помощи модификации JSON-RPC запросов, чтобы заменять null на None.
7
+
8
+ Использование:
9
+ python patch_vstl_service.py
10
+ """
11
+
12
+ import sys
13
+ import json
14
+ import requests
15
+ from typing import Dict, Any, Optional
16
+
17
+ # URL и заголовки для VSTL сервиса
18
+ VSTL_URL = "http://localhost:8000/cmd"
19
+ HEADERS = {"Content-Type": "application/json"}
20
+
21
+ def safe_call_vstl(command: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
22
+ """
23
+ Безопасно вызывает команду в сервисе VSTL, обрабатывая null значения.
24
+
25
+ Args:
26
+ command: Имя команды
27
+ params: Параметры для команды
28
+
29
+ Returns:
30
+ Dict[str, Any]: Ответ от сервиса
31
+ """
32
+ # Обработка null значений - заменяем null на None для Python
33
+ safe_params = {}
34
+ if params:
35
+ for key, value in params.items():
36
+ if value == "null" or value == "none":
37
+ safe_params[key] = None
38
+ else:
39
+ safe_params[key] = value
40
+
41
+ # Безопасно сериализуем параметры, чтобы null значения были корректно обработаны
42
+ payload = {
43
+ "jsonrpc": "2.0",
44
+ "method": command,
45
+ "params": safe_params or {},
46
+ "id": 1
47
+ }
48
+
49
+ # Отправляем запрос
50
+ response = requests.post(VSTL_URL, json=payload, headers=HEADERS)
51
+ return response.json()
52
+
53
+ def test_vstl_commands():
54
+ """
55
+ Тестирует различные команды в сервисе VSTL с безопасной обработкой null.
56
+ """
57
+ print("=== Тестирование команд VSTL с патчем для обработки null ===\n")
58
+
59
+ # Проверяем команду health
60
+ print("1. Команда health:")
61
+ response = safe_call_vstl("health", {})
62
+ print(f"Ответ: {json.dumps(response, indent=2)}\n")
63
+
64
+ # Проверяем команду help без параметров
65
+ print("2. Команда help без параметров:")
66
+ response = safe_call_vstl("help", {})
67
+ print(f"Ответ: {json.dumps(response, indent=2)}")
68
+
69
+ # Если команда help сработала, выведем список всех доступных команд
70
+ if response.get("result") and not response.get("error"):
71
+ commands_info = response["result"].get("commands", {})
72
+ print(f"\nДоступные команды ({len(commands_info)}):")
73
+ for cmd_name, cmd_info in commands_info.items():
74
+ print(f" - {cmd_name}: {cmd_info.get('summary', 'Нет описания')}")
75
+
76
+ # Проверяем команду help с параметром cmdname
77
+ print("\n3. Команда help с параметром cmdname:")
78
+ response = safe_call_vstl("help", {"cmdname": "health"})
79
+ print(f"Ответ: {json.dumps(response, indent=2)}\n")
80
+
81
+ # Проверяем команду config
82
+ print("4. Команда config:")
83
+ response = safe_call_vstl("config", {"operation": "get"})
84
+ print(f"Ответ: {json.dumps(response, indent=2)}\n")
85
+
86
+ # Выводим рекомендации по полному исправлению
87
+ print("\n=== Рекомендации по полному исправлению проблемы с null в VSTL ===")
88
+ print("""
89
+ 1. Проверьте исходный код сервиса VSTL на наличие использования переменной 'null'
90
+ без её объявления (обратите внимание на файл help_command.py)
91
+
92
+ 2. Замените все использования JavaScript-стиля null на Python None:
93
+ - Поиск: if value == null
94
+ - Замена: if value is None
95
+
96
+ 3. Обновите сервис до последней версии mcp_proxy_adapter 3.1.6 и перезапустите
97
+
98
+ 4. Если это невозможно, используйте этот скрипт как промежуточное решение,
99
+ чтобы безопасно вызывать команды VSTL с корректной обработкой null.
100
+
101
+ 5. Внесите исправления в метод validate_params, как показано в fix_vstl_help.py
102
+ """)
103
+
104
+ if __name__ == "__main__":
105
+ test_vstl_commands()
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Скрипт для исправления ошибки 'name null is not defined' в сервисе VSTL через MCP Proxy API.
4
+
5
+ Этот скрипт демонстрирует как обойти проблему с null в сервисе vstl,
6
+ используя стандартные средства MCP Proxy API.
7
+ """
8
+
9
+ import os
10
+ import sys
11
+ import json
12
+ import subprocess
13
+ from typing import Dict, Any, Optional
14
+
15
+ def call_vstl_command(command: str, params: Optional[Dict[str, Any]] = None):
16
+ """
17
+ Вызывает команду vstl через MCP Proxy API.
18
+
19
+ Args:
20
+ command: Название команды
21
+ params: Параметры команды
22
+
23
+ Returns:
24
+ Результат выполнения команды
25
+ """
26
+ # Формируем параметры для команды mcp_MCP-Proxy_vstl
27
+ if params is None:
28
+ params = {}
29
+
30
+ # Сериализуем параметры в JSON
31
+ params_json = json.dumps(params)
32
+
33
+ # Формируем команду для вызова MCP Proxy API
34
+ cmd = [
35
+ "curl", "-s",
36
+ "-X", "POST",
37
+ "-H", "Content-Type: application/json",
38
+ "-d", f'{{"jsonrpc":"2.0","method":"{command}","params":{params_json},"id":1}}',
39
+ "http://localhost:8000/api/vstl"
40
+ ]
41
+
42
+ # Выполняем команду
43
+ try:
44
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
45
+ # Парсим результат как JSON
46
+ return json.loads(result.stdout)
47
+ except subprocess.CalledProcessError as e:
48
+ print(f"Ошибка выполнения команды: {e}")
49
+ print(f"STDOUT: {e.stdout}")
50
+ print(f"STDERR: {e.stderr}")
51
+ return {"error": str(e)}
52
+ except json.JSONDecodeError as e:
53
+ print(f"Ошибка декодирования JSON: {e}")
54
+ print(f"Ответ: {result.stdout}")
55
+ return {"error": "Неверный формат JSON в ответе"}
56
+
57
+ def test_vstl_commands():
58
+ """
59
+ Тестирует различные команды vstl с обходом проблемы null.
60
+ """
61
+ print("=== Тестирование команд VSTL через MCP Proxy API ===\n")
62
+
63
+ # Проверяем команду health
64
+ print("1. Команда health:")
65
+ response = call_vstl_command("health", {})
66
+ print(f"Ответ: {json.dumps(response, indent=2)}\n")
67
+
68
+ # Проверяем команду help без параметров
69
+ print("2. Команда help без параметров:")
70
+ response = call_vstl_command("help", {})
71
+ print(f"Ответ: {json.dumps(response, indent=2)}")
72
+
73
+ # Если есть ошибка в команде help без параметров, попробуем еще несколько вариантов
74
+ if "error" in response:
75
+ print("\n2.1. Попытка обойти ошибку - вызов help с корректными параметрами:")
76
+ response = call_vstl_command("help", {"cmdname": None})
77
+ print(f"Ответ: {json.dumps(response, indent=2)}")
78
+
79
+ # Проверяем команду help с параметром cmdname
80
+ print("\n3. Команда help с параметром cmdname:")
81
+ response = call_vstl_command("help", {"cmdname": "health"})
82
+ print(f"Ответ: {json.dumps(response, indent=2)}\n")
83
+
84
+ # Проверяем команду config
85
+ print("4. Команда config:")
86
+ response = call_vstl_command("config", {"operation": "get"})
87
+ print(f"Ответ: {json.dumps(response, indent=2)}\n")
88
+
89
+ # Выводим рекомендации по исправлению
90
+ print("\n=== Рекомендации по исправлению проблемы с null в VSTL ===")
91
+ print("""
92
+ 1. Проблема с обработкой null в JavaScript-совместимых API - это распространенная ошибка.
93
+ В JavaScript null - это ключевое слово, а в Python - это None.
94
+
95
+ 2. Для полного решения проблемы необходимо исправить реализацию сервиса VSTL:
96
+ - Найти в коде места, где используется 'null' как переменная
97
+ - Заменить на корректное использование None
98
+ - Добавить к аргументам метода execute в help_command.py параметр **kwargs
99
+ - Обновить метод validate_params для обработки строковых представлений null
100
+
101
+ 3. До исправления сервера можно использовать следующие обходные пути:
102
+ - Использовать MCP Proxy API с корректными значениями параметров (None вместо null)
103
+ - Использовать промежуточный слой, который будет преобразовывать запросы
104
+ - Избегать отправки параметров null/None в командах, где это возможно
105
+ """)
106
+
107
+ if __name__ == "__main__":
108
+ test_vstl_commands()
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- Скрипт для проверки работы улучшенной обработки null в версии 3.1.4.
3
+ Скрипт для проверки работы улучшенной обработки null в версии 3.1.6.
4
4
 
5
5
  Этот скрипт:
6
- 1. Проверяет, что версия пакета 3.1.4
6
+ 1. Проверяет, что версия пакета 3.1.6
7
7
  2. Тестирует улучшенный метод validate_params
8
8
  3. Проверяет, что команда help правильно обрабатывает null значения
9
9
  """
@@ -26,7 +26,7 @@ def check_version():
26
26
  """
27
27
  Проверяет версию установленного пакета mcp_proxy_adapter.
28
28
  """
29
- expected_version = "3.1.4"
29
+ expected_version = "3.1.6"
30
30
 
31
31
  print(f"\n=== Проверка версии ===")
32
32
  print(f"Установленная версия: {__version__}")
@@ -146,7 +146,7 @@ def main():
146
146
  """
147
147
  Основная функция для запуска тестов.
148
148
  """
149
- print("=== Проверка MCP Proxy Adapter 3.1.4 ===")
149
+ print("=== Проверка MCP Proxy Adapter 3.1.6 ===")
150
150
 
151
151
  # Проверяем версию пакета
152
152
  version_ok = check_version()
@@ -200,6 +200,27 @@ def create_app() -> FastAPI:
200
200
  "error": e.to_dict()
201
201
  }
202
202
  )
203
+ except NotFoundError as e:
204
+ # Специальная обработка для help-команды: возвращаем result с пустым commands и error
205
+ if command_name == "help":
206
+ return {
207
+ "result": {
208
+ "success": False,
209
+ "commands": {},
210
+ "error": str(e),
211
+ "note": "To get detailed information about a specific command, call help with parameter: POST /cmd {\"command\": \"help\", \"params\": {\"cmdname\": \"<command_name>\"}}"
212
+ }
213
+ }
214
+ # Для остальных команд — стандартная ошибка
215
+ return JSONResponse(
216
+ status_code=200,
217
+ content={
218
+ "error": {
219
+ "code": e.code,
220
+ "message": str(e)
221
+ }
222
+ }
223
+ )
203
224
 
204
225
  except json.JSONDecodeError:
205
226
  req_logger.error("JSON decode error")
@@ -1,5 +1,20 @@
1
1
  """
2
2
  Module for registering and managing commands.
3
+
4
+ Example: Registering a command instance (for dependency injection)
5
+ ---------------------------------------------------------------
6
+
7
+ .. code-block:: python
8
+
9
+ from mcp_proxy_adapter.commands.command_registry import registry
10
+ from my_commands import MyCommand
11
+
12
+ # Suppose MyCommand requires a service dependency
13
+ service = MyService()
14
+ my_command_instance = MyCommand(service=service)
15
+ registry.register(my_command_instance)
16
+
17
+ # Now, when the command is executed, the same instance (with dependencies) will be used
3
18
  """
4
19
 
5
20
  import importlib
@@ -3,61 +3,112 @@ Module with help command implementation.
3
3
  """
4
4
 
5
5
  from typing import Dict, Any, Optional
6
+ import logging
7
+ import traceback
6
8
 
7
9
  from mcp_proxy_adapter.commands.base import Command
8
10
  from mcp_proxy_adapter.commands.result import CommandResult
9
11
  from mcp_proxy_adapter.commands.command_registry import registry
10
12
  from mcp_proxy_adapter.core.errors import NotFoundError
11
13
 
14
+ # Добавляем логирование
15
+ logger = logging.getLogger("mcp_proxy_adapter.commands.help_command")
16
+
12
17
 
13
18
  class HelpResult(CommandResult):
14
19
  """
15
20
  Result of the help command execution.
16
21
  """
17
-
22
+
18
23
  def __init__(self, commands_info: Optional[Dict[str, Any]] = None, command_info: Optional[Dict[str, Any]] = None):
19
24
  """
20
25
  Initialize help command result.
21
-
26
+
22
27
  Args:
23
28
  commands_info: Information about all commands (for request without parameters)
24
29
  command_info: Information about a specific command (for request with cmdname parameter)
25
30
  """
31
+ logger.debug(f"HelpResult.__init__: commands_info={commands_info is not None}, command_info={command_info is not None}")
26
32
  self.commands_info = commands_info
27
33
  self.command_info = command_info
28
-
34
+
29
35
  def to_dict(self) -> Dict[str, Any]:
30
36
  """
31
37
  Convert result to dictionary.
32
-
38
+
33
39
  Returns:
34
40
  Dict[str, Any]: Result as dictionary
35
41
  """
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"]
42
+ try:
43
+ logger.debug(f"HelpResult.to_dict: command_info={self.command_info is not None}, commands_info={self.commands_info is not None}")
44
+
45
+ # Защита от None для self.command_info
46
+ if self.command_info is not None:
47
+ logger.debug(f"HelpResult.to_dict: returning command_info for {self.command_info.get('name', 'unknown')}")
48
+ # Делаем безопасное получение всех полей с дефолтными значениями
49
+ return {
50
+ "cmdname": self.command_info.get("name", "unknown"),
51
+ "info": {
52
+ "description": self.command_info.get("description", ""),
53
+ "summary": self.command_info.get("summary", ""),
54
+ "params": self.command_info.get("params", {}),
55
+ "examples": self.command_info.get("examples", [])
56
+ }
44
57
  }
58
+
59
+ # Защита от None для self.commands_info
60
+ if self.commands_info is None:
61
+ logger.warning("HelpResult.to_dict: commands_info is None, создаем пустой результат")
62
+ # Возвращаем пустой список команд вместо ошибки
63
+ return {
64
+ "tool_info": {
65
+ "name": "MCP-Proxy API Service",
66
+ "description": "JSON-RPC API for microservice command execution",
67
+ "version": "1.0.0"
68
+ },
69
+ "help_usage": {
70
+ "description": "Get information about commands",
71
+ "examples": [
72
+ {"command": "help", "description": "List of all available commands"},
73
+ {"command": "help", "params": {"cmdname": "command_name"}, "description": "Get detailed information about a specific command"}
74
+ ]
75
+ },
76
+ "commands": {},
77
+ "total": 0,
78
+ "note": "To get detailed information about a specific command, call help with parameter: POST /cmd {\"command\": \"help\", \"params\": {\"cmdname\": \"<command_name>\"}}"
79
+ }
80
+
81
+ # For list of all commands, return as is (already formatted)
82
+ logger.debug(f"HelpResult.to_dict: processing commands_info with {len(self.commands_info.get('commands', {}))} commands")
83
+ result = self.commands_info.copy()
84
+
85
+ # Add total count and note about usage
86
+ commands = result.get("commands", {})
87
+ result["total"] = len(commands)
88
+ result["note"] = "To get detailed information about a specific command, call help with parameter: POST /cmd {\"command\": \"help\", \"params\": {\"cmdname\": \"<command_name>\"}}"
89
+
90
+ logger.debug(f"HelpResult.to_dict: returning result with {result['total']} commands")
91
+ return result
92
+ except Exception as e:
93
+ logger.error(f"Ошибка в HelpResult.to_dict: {e}")
94
+ logger.debug(f"Трассировка: {traceback.format_exc()}")
95
+ # В случае неожиданной ошибки возвращаем базовый ответ вместо ошибки
96
+ return {
97
+ "tool_info": {
98
+ "name": "MCP-Proxy API Service",
99
+ "description": "JSON-RPC API for microservice command execution",
100
+ "version": "1.0.0"
101
+ },
102
+ "commands": {},
103
+ "total": 0,
104
+ "error": str(e)
45
105
  }
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
-
106
+
56
107
  @classmethod
57
108
  def get_schema(cls) -> Dict[str, Any]:
58
109
  """
59
110
  Get JSON schema for result validation.
60
-
111
+
61
112
  Returns:
62
113
  Dict[str, Any]: JSON schema
63
114
  """
@@ -114,72 +165,116 @@ class HelpCommand(Command):
114
165
  """
115
166
  Command for getting help information about available commands.
116
167
  """
117
-
168
+
118
169
  name = "help"
119
170
  result_class = HelpResult
120
-
171
+
121
172
  async def execute(self, cmdname: Optional[str] = None, **kwargs) -> HelpResult:
122
173
  """
123
174
  Execute help command.
124
-
175
+
125
176
  Args:
126
177
  cmdname: Name of the command to get information about (optional)
127
178
  **kwargs: Any additional parameters (will be ignored)
128
-
179
+
129
180
  Returns:
130
181
  HelpResult: Help command result
131
-
182
+
132
183
  Raises:
133
184
  NotFoundError: If specified command not found
134
185
  """
135
- # Handle case when cmdname is provided
136
- # Important: explicitly check for None or empty string
137
- if cmdname is not None and cmdname != "":
138
- try:
139
- # Get command metadata from registry
140
- command_metadata = registry.get_command_metadata(cmdname)
141
- return HelpResult(command_info=command_metadata)
142
- except NotFoundError:
143
- # If command not found, raise error
144
- raise NotFoundError(f"Command '{cmdname}' not found")
145
-
146
- # Otherwise, return information about all available commands
147
- # and tool metadata
148
-
149
- # Get metadata for all commands
150
- all_metadata = registry.get_all_metadata()
151
-
152
- # Prepare response format with tool metadata
153
- result = {
154
- "tool_info": {
155
- "name": "MCP-Proxy API Service",
156
- "description": "JSON-RPC API for microservice command execution",
157
- "version": "1.0.0"
158
- },
159
- "help_usage": {
160
- "description": "Get information about commands",
161
- "examples": [
162
- {"command": "help", "description": "List of all available commands"},
163
- {"command": "help", "params": {"cmdname": "command_name"}, "description": "Get detailed information about a specific command"}
164
- ]
165
- },
166
- "commands": {}
167
- }
168
-
169
- # Add brief information about commands
170
- for name, metadata in all_metadata.items():
171
- result["commands"][name] = {
172
- "summary": metadata["summary"],
173
- "params_count": len(metadata["params"])
186
+ logger.debug(f"HelpCommand.execute начало: cmdname={cmdname}, kwargs={kwargs}")
187
+
188
+ try:
189
+ # Handle case when cmdname is provided
190
+ if cmdname is not None and cmdname != "":
191
+ logger.debug(f"Обработка запроса для конкретной команды: {cmdname}")
192
+ try:
193
+ # Get command metadata from registry
194
+ command_metadata = registry.get_command_metadata(cmdname)
195
+ logger.debug(f"Получены метаданные для команды {cmdname}")
196
+ return HelpResult(command_info=command_metadata)
197
+ except NotFoundError:
198
+ logger.warning(f"Команда '{cmdname}' не найдена")
199
+ # Получаем список всех команд
200
+ all_commands = list(registry.get_all_metadata().keys())
201
+ if all_commands:
202
+ example_cmd = all_commands[0]
203
+ example = {
204
+ "command": "help",
205
+ "params": {"cmdname": example_cmd}
206
+ }
207
+ note = f"Use help with an existing command name to get detailed info. For example: help with cmdname '{example_cmd}'. To list all commands: call help without parameters."
208
+ else:
209
+ example = {"command": "help"}
210
+ note = "No commands registered. To list all commands: call help without parameters."
211
+ return HelpResult(commands_info={
212
+ "commands": {},
213
+ "error": f"Command '{cmdname}' not found",
214
+ "example": example,
215
+ "note": note
216
+ })
217
+
218
+ # Otherwise, return information about all available commands
219
+ logger.debug("Обработка запроса для всех команд")
220
+
221
+ # Get metadata for all commands
222
+ all_metadata = registry.get_all_metadata()
223
+ logger.debug(f"Получены метаданные для {len(all_metadata)} команд")
224
+
225
+ # Prepare response format with tool metadata
226
+ result = {
227
+ "tool_info": {
228
+ "name": "MCP-Proxy API Service",
229
+ "description": "JSON-RPC API for microservice command execution",
230
+ "version": "1.0.0"
231
+ },
232
+ "help_usage": {
233
+ "description": "Get information about commands",
234
+ "examples": [
235
+ {"command": "help", "description": "List of all available commands"},
236
+ {"command": "help", "params": {"cmdname": "command_name"}, "description": "Get detailed information about a specific command"}
237
+ ]
238
+ },
239
+ "commands": {}
174
240
  }
175
-
176
- return HelpResult(commands_info=result)
177
-
241
+
242
+ # Add brief information about commands
243
+ for name, metadata in all_metadata.items():
244
+ try:
245
+ logger.debug(f"Обработка метаданных команды {name}")
246
+ # Безопасное получение параметров с проверкой на наличие ключей
247
+ result["commands"][name] = {
248
+ "summary": metadata.get("summary", ""),
249
+ "params_count": len(metadata.get("params", {}))
250
+ }
251
+ except Exception as e:
252
+ logger.error(f"Ошибка при обработке метаданных команды {name}: {e}")
253
+ logger.debug(f"Метаданные команды {name}: {metadata}")
254
+ # Пропускаем проблемную команду
255
+ continue
256
+
257
+ logger.debug(f"HelpCommand.execute завершение: возвращаем результат с {len(result['commands'])} командами")
258
+ return HelpResult(commands_info=result)
259
+ except Exception as e:
260
+ logger.error(f"Неожиданная ошибка в HelpCommand.execute: {e}")
261
+ logger.debug(f"Трассировка: {traceback.format_exc()}")
262
+ # В случае неожиданной ошибки возвращаем пустой результат вместо ошибки
263
+ return HelpResult(commands_info={
264
+ "tool_info": {
265
+ "name": "MCP-Proxy API Service",
266
+ "description": "JSON-RPC API for microservice command execution",
267
+ "version": "1.0.0"
268
+ },
269
+ "commands": {},
270
+ "error": str(e)
271
+ })
272
+
178
273
  @classmethod
179
274
  def get_schema(cls) -> Dict[str, Any]:
180
275
  """
181
276
  Get JSON schema for command parameters validation.
182
-
277
+
183
278
  Returns:
184
279
  Dict[str, Any]: JSON schema
185
280
  """
@@ -97,12 +97,15 @@ async def test_help_command_with_invalid_cmdname(mock_registry):
97
97
  # Setup mocks
98
98
  mock_registry.get_command_metadata.side_effect = NotFoundError("Command not found")
99
99
 
100
- # Execute command and check exception
100
+ # Execute command and check result fields
101
101
  command = HelpCommand()
102
- with pytest.raises(NotFoundError) as excinfo:
103
- await command.execute(cmdname="non_existent")
104
-
105
- assert "not found" in str(excinfo.value)
102
+ result = await command.execute(cmdname="non_existent")
103
+ result_dict = result.to_dict()
104
+ assert "error" in result_dict
105
+ assert "example" in result_dict
106
+ assert "note" in result_dict
107
+ assert result_dict["error"].startswith("Command")
108
+ assert result_dict["example"]["command"] == "help"
106
109
 
107
110
 
108
111
  def test_help_result_schema():
@@ -92,12 +92,11 @@ def test_cmd_help_unknown_command(client):
92
92
  assert "result" in response.json()
93
93
  result = response.json()["result"]
94
94
 
95
- assert "success" in result and result["success"] is False
96
95
  assert "error" in result
97
- error = result["error"]
98
-
99
- assert error["code"] == -32601
100
- assert "not found" in error["message"]
96
+ assert "example" in result
97
+ assert "note" in result
98
+ assert result["error"].startswith("Command")
99
+ assert result["example"]["command"] == "help"
101
100
 
102
101
 
103
102
  def test_cmd_unknown_command(client):
@@ -242,4 +242,40 @@ def test_clear_registry():
242
242
 
243
243
  # Clear registry
244
244
  registry.clear()
245
- assert len(registry._commands) == 0
245
+ assert len(registry._commands) == 0
246
+
247
+
248
+ def test_register_command_instance():
249
+ """Test registering a command instance (with dependencies)."""
250
+ registry = CommandRegistry()
251
+
252
+ class Service:
253
+ def __init__(self, value):
254
+ self.value = value
255
+
256
+ class CommandWithDependency(Command):
257
+ name = "command_with_dep"
258
+ result_class = MockResult
259
+ def __init__(self, service: Service):
260
+ self.service = service
261
+ async def execute(self, **kwargs):
262
+ # Return the value from the injected service
263
+ result = MockResult()
264
+ result.service_value = self.service.value
265
+ return result
266
+
267
+ service = Service(value=42)
268
+ command_instance = CommandWithDependency(service=service)
269
+ registry.register(command_instance)
270
+
271
+ # Проверяем, что экземпляр зарегистрирован
272
+ assert registry.has_instance("command_with_dep")
273
+ # Проверяем, что get_command_instance возвращает именно этот экземпляр
274
+ assert registry.get_command_instance("command_with_dep") is command_instance
275
+ # Проверяем, что execute использует внедрённый сервис
276
+ import asyncio
277
+ result = asyncio.run(
278
+ registry.get_command_instance("command_with_dep").execute()
279
+ )
280
+ assert hasattr(result, "service_value")
281
+ assert result.service_value == 42
@@ -1,3 +1,3 @@
1
1
  """Version information for MCP Microservice."""
2
2
 
3
- __version__ = "3.1.4"
3
+ __version__ = "3.1.6"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-proxy-adapter
3
- Version: 3.1.4
3
+ Version: 3.1.6
4
4
  Summary: Reliable microservice with unified JSON-RPC endpoint
5
5
  Home-page: https://github.com/yourusername/mcp-proxy-adapter
6
6
  Author: MCP Team
@@ -1,8 +1,11 @@
1
1
  examples/__init__.py,sha256=sLYNpeoiE-X5q7fmJb7NFMmhiIn0543mgJj16q1qmk0,593
2
+ examples/check_vstl_schema.py,sha256=s-VVoY8ysuvVr64JAX6uvxuwjEB7FjbJf0ek7XYc1Nc,4320
2
3
  examples/fix_vstl_help.py,sha256=GvFepKbCD-a2O2LEflMGeXnsgNMUNfNezPFU6QB_7tI,5208
4
+ examples/patch_vstl_service.py,sha256=MU-PxZ9gAQx-gT0tyxoWIswDbdV4cYd-r-T9gq4aziw,4715
5
+ examples/patch_vstl_service_mcp.py,sha256=jiMKzx8hANFVtM0Yv-DJbThwM4r81z9dslF_v9tkAf4,5254
3
6
  examples/server.py,sha256=gnRTE_k7C0A255dLyaJWyA4YU0H6Elc7osr_JQvsQhQ,2286
4
7
  examples/simple_server.py,sha256=Bkczmz5Qs473xJ0_AJjBpqWT-oWctwED98A067z05zQ,3768
5
- examples/test_package_3.1.4.py,sha256=OnEC4N4QOfqrh7DPsQrUV9L9TEG240CwkdM9NY2HBXU,7180
8
+ examples/test_package_3.1.4.py,sha256=GZZANEIrZkw0gfsFK-PKi4lGZ6Fen25RFHSHrOOhR4w,7180
6
9
  examples/test_server.py,sha256=cKWJ4tlHqZsRKyeuXbZ1dQ7TU9riJWcDan__wK7YH_Y,3729
7
10
  examples/tool_description_example.py,sha256=blamrx_1oHCG4NnvIiYnQxphAEDqb7-TALPALJFj51s,3280
8
11
  examples/anti_patterns/README.md,sha256=1-Hby6Wf3kAC0XOV_jOvuHL-kmTypWOUfE_rEU3Knu8,2045
@@ -52,9 +55,9 @@ mcp_proxy_adapter/config.py,sha256=MjgZAld6TiD0F5oCyEaJhYhfEXVZxc5G5ke2SLKCV9A,5
52
55
  mcp_proxy_adapter/custom_openapi.py,sha256=tAE289B76nUdd2tjbiyow2Jftj0Yd-A8I2ndTD6R-5c,11706
53
56
  mcp_proxy_adapter/openapi.py,sha256=jyl5EPXcFhzFKEEMXxHeqF1U-SsYvtdlaKGU2QrekpU,13889
54
57
  mcp_proxy_adapter/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
- mcp_proxy_adapter/version.py,sha256=WXry8RiuhtfzpY1KvYxY0nYxtli-yU85Nax9ZjK3axY,71
58
+ mcp_proxy_adapter/version.py,sha256=VRQ07cLW2F_OHDoYAsBuYZHDoj1M-ztVO8Joii9m1Iw,71
56
59
  mcp_proxy_adapter/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
- mcp_proxy_adapter/api/app.py,sha256=q3yYsNEBFXYr2TOJFxfh6bbF53NkmmgfzSFOtVq7xdc,14353
60
+ mcp_proxy_adapter/api/app.py,sha256=TdsZjAwdMr_tYGjmmH3D2voVmQOU_Lw9raKbbYhXkk0,15424
58
61
  mcp_proxy_adapter/api/handlers.py,sha256=lc_4eakQgQVlnGjnVkOY-mIMkfyLk4iRfwdrWTyvuvM,7194
59
62
  mcp_proxy_adapter/api/schemas.py,sha256=xOmiSwHaapY6myEFnLu7o-LWVPM7vwmLYZXFo2c6NfE,12381
60
63
  mcp_proxy_adapter/api/tool_integration.py,sha256=mQNFiCkd4plY_A3fkG6auaM8D_1XiC9Jxp4Zrm1ngYE,10161
@@ -68,11 +71,11 @@ mcp_proxy_adapter/api/middleware/performance.py,sha256=dHBxTF43LEGXMKHMH3A8ybKmw
68
71
  mcp_proxy_adapter/api/middleware/rate_limit.py,sha256=DIv_-ZUVmL-jEo_A5BlfnasZf25zT84AiIJDUUnXkpM,5041
69
72
  mcp_proxy_adapter/commands/__init__.py,sha256=bHZZcVYkXVL9g-YZOnWkHOxSP2WzT-I4_OleYycQhbw,610
70
73
  mcp_proxy_adapter/commands/base.py,sha256=lKKoN_9tJYIeOFKgQRGwWZHy_EvWP8bVB1EhIouTbi0,13740
71
- mcp_proxy_adapter/commands/command_registry.py,sha256=x55k4guzOlm7fVRolJ_iSFPRx2Snq4yBaMQdzVmsK0A,10460
74
+ mcp_proxy_adapter/commands/command_registry.py,sha256=3KNmG1Blg1UrThZNU3vGj_2I4ZTFBjUgYZmk7QBOb4w,10998
72
75
  mcp_proxy_adapter/commands/config_command.py,sha256=-Z6BGaEQTf859l56zZpHYBeZFeIVdpMYybDrd7LOPIg,3553
73
76
  mcp_proxy_adapter/commands/dependency_container.py,sha256=Uz9OPRAUZN7tsVrMVgXgPQcsRD2N-e2Ixg9XarPOlnY,3410
74
77
  mcp_proxy_adapter/commands/health_command.py,sha256=_tzxHwB_8vo53VBC6HnBv5fSfZL1pEuwlbrCcy_K78c,4087
75
- mcp_proxy_adapter/commands/help_command.py,sha256=TzJEtGX9zcGkCX3H-qZgms_eEfP7Xkt1x3YNRhmXfWM,6976
78
+ mcp_proxy_adapter/commands/help_command.py,sha256=dfqNt1h2H6vQJ9rLySWa_-0-QzesnN-Mgx0cz-uFlIo,13051
76
79
  mcp_proxy_adapter/commands/result.py,sha256=2WjftiAuhlyzOKmPJlQHo_b08ZCzWoK7cquUHFLVE-E,5534
77
80
  mcp_proxy_adapter/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
81
  mcp_proxy_adapter/core/errors.py,sha256=s34OxiIR4NCJu_pYSigKXqrIvRjUUK2OWw0X4dpDjIA,5151
@@ -86,7 +89,7 @@ mcp_proxy_adapter/tests/test_api_endpoints.py,sha256=ePtWCf0szD1JeY9WdHAhcKnuOzo
86
89
  mcp_proxy_adapter/tests/test_api_handlers.py,sha256=LeHO0o6eCxan6mt_8ZkUwSbeY7qYfoYMJ-bT_sSgdxc,11011
87
90
  mcp_proxy_adapter/tests/test_base_command.py,sha256=nSIi_mfjux8TL--65pMBfyg91EiLjJhI2P_ASWqyW-U,3779
88
91
  mcp_proxy_adapter/tests/test_batch_requests.py,sha256=9-gvhPq48AcEwGlhwgn3DWNhpleLA0f4luZNYMrqlXY,4103
89
- mcp_proxy_adapter/tests/test_command_registry.py,sha256=o5HHxlQ-D2ML0ufJK-lXQv-qYWccvalOHGVvpc8QFTU,6285
92
+ mcp_proxy_adapter/tests/test_command_registry.py,sha256=ywi5gM7D7NHcNOkdwXOgpJqHjwGADZEaB3ueM3nR0gM,7683
90
93
  mcp_proxy_adapter/tests/test_config.py,sha256=i4YbFhB3WI1wWKCxkG6l-UpFv2LAbhh4hmGipmYG1d0,3928
91
94
  mcp_proxy_adapter/tests/test_utils.py,sha256=K8DqdWDAV-cXjjeykehHpG5phfq5ydu2HLJzaAL282Y,1653
92
95
  mcp_proxy_adapter/tests/api/__init__.py,sha256=QzjeBIhrRLqfUKYmxVSCbMOoni5MAXgyAx9HxxB6JRs,27
@@ -95,11 +98,11 @@ mcp_proxy_adapter/tests/api/test_middleware.py,sha256=3Rgc09VJ7JG6_06oIgAVq6u7qJ
95
98
  mcp_proxy_adapter/tests/commands/__init__.py,sha256=DZxhtyr__AleyXN1s8Ef887tK5nsoHKfW4QXyzLaP0E,36
96
99
  mcp_proxy_adapter/tests/commands/test_config_command.py,sha256=ud0Y57xUhFiyrUKDyA0eBZ8IOKiGDpioijtwY-detC4,6522
97
100
  mcp_proxy_adapter/tests/commands/test_echo_command.py,sha256=c2dmqfx3ZfvDedy5vuxArFv7iHsReepMmD2Lchg4l3E,3437
98
- mcp_proxy_adapter/tests/commands/test_help_command.py,sha256=OJCZMS0BqUUNNSecipd3dOFokUATiET3gpCoVAxPXPA,4116
101
+ mcp_proxy_adapter/tests/commands/test_help_command.py,sha256=0kylFDGSYV-YStVCf_j7_4EF8VDaClIZbJz3V2V2-DI,4272
99
102
  mcp_proxy_adapter/tests/functional/__init__.py,sha256=muVNR6LD5o7DsDaLrbLTFsavUNcnyM608-mr-dqzgDU,59
100
103
  mcp_proxy_adapter/tests/functional/test_api.py,sha256=OaGB-WAWVyX4b0b-mCdXBNhwwYABYeBPO3IDnA3I00c,7114
101
104
  mcp_proxy_adapter/tests/integration/__init__.py,sha256=JZpeNG1PBRZ3k5zfq6NH3GyzDc1Yx1ZUgwi6eLBxs1o,60
102
- mcp_proxy_adapter/tests/integration/test_cmd_integration.py,sha256=OqBxh52WPijKaRrIHA54QDJQLBz_nwlCywF9k5jxa_Y,3430
105
+ mcp_proxy_adapter/tests/integration/test_cmd_integration.py,sha256=BS3lA6ayveMckHcy1WJjyqR7l7WTcjmGiCYSnMjVfO0,3415
103
106
  mcp_proxy_adapter/tests/integration/test_integration.py,sha256=Rf8GTxdZOvZzrtgqWN2fp-36qUU5rpHdV9zhN1JxNw8,6619
104
107
  mcp_proxy_adapter/tests/performance/__init__.py,sha256=2kHf6g4LmYnFuV4EALXzo7Qk9vHGA9DXZFt7nORRFjM,60
105
108
  mcp_proxy_adapter/tests/performance/test_performance.py,sha256=Djrnu-SsXRrc_Prj1Aw8OoPzPUwHJds-Itk3aX6o01w,5730
@@ -108,8 +111,8 @@ mcp_proxy_adapter/tests/stubs/echo_command.py,sha256=Y7SA4IB5Lo_ncn78SDm9iRZvJSK
108
111
  mcp_proxy_adapter/tests/unit/__init__.py,sha256=RS-5UoSCcLKtr2jrAaZw_NG9MquA6BZmxq-P6cTw9ok,53
109
112
  mcp_proxy_adapter/tests/unit/test_base_command.py,sha256=ldDXQYk2eijbTgZioSBAhHzSAa_SuBKYqCutCEzUYTE,3924
110
113
  mcp_proxy_adapter/tests/unit/test_config.py,sha256=SZ62LXFOv_fsV0fmSIBdHWvapEyexKrioFRQo0I4pkg,5900
111
- mcp_proxy_adapter-3.1.4.dist-info/licenses/LICENSE,sha256=OkApFEwdgMCt_mbvUI-eIwKMSTe38K3XnU2DT5ub-wI,1072
112
- mcp_proxy_adapter-3.1.4.dist-info/METADATA,sha256=G_zv6VyoWPW8P9AStPqByO-7ujKlx8JMgv8Yx_cxqOY,7537
113
- mcp_proxy_adapter-3.1.4.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
114
- mcp_proxy_adapter-3.1.4.dist-info/top_level.txt,sha256=kxq3OC7vBtsFdy9dDVse4cOl-SV_QlvcTeSkuw_jw3I,27
115
- mcp_proxy_adapter-3.1.4.dist-info/RECORD,,
114
+ mcp_proxy_adapter-3.1.6.dist-info/licenses/LICENSE,sha256=OkApFEwdgMCt_mbvUI-eIwKMSTe38K3XnU2DT5ub-wI,1072
115
+ mcp_proxy_adapter-3.1.6.dist-info/METADATA,sha256=RbYESwlltxLaJLm8rVosrLljcIEkI1IT9VAVpWave9g,7537
116
+ mcp_proxy_adapter-3.1.6.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
117
+ mcp_proxy_adapter-3.1.6.dist-info/top_level.txt,sha256=kxq3OC7vBtsFdy9dDVse4cOl-SV_QlvcTeSkuw_jw3I,27
118
+ mcp_proxy_adapter-3.1.6.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.4.0)
2
+ Generator: setuptools (80.7.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5