mcp-proxy-adapter 2.1.15__py3-none-any.whl → 2.1.17__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.
- mcp_proxy_adapter/__init__.py +1 -0
- mcp_proxy_adapter/dispatchers/base_dispatcher.py +2 -2
- mcp_proxy_adapter/dispatchers/json_rpc_dispatcher.py +35 -8
- mcp_proxy_adapter/examples/docstring_and_schema_example.py +3 -3
- mcp_proxy_adapter/examples/extension_example.py +9 -9
- mcp_proxy_adapter/examples/help_best_practices.py +10 -10
- mcp_proxy_adapter/examples/help_usage.py +10 -10
- mcp_proxy_adapter/examples/openapi_server.py +8 -9
- mcp_proxy_adapter/examples/project_structure_example.py +2 -7
- mcp_proxy_adapter/examples/testing_example.py +3 -14
- mcp_proxy_adapter/registry.py +10 -8
- mcp_proxy_adapter/testing_utils.py +3 -11
- mcp_proxy_adapter/validators/__init__.py +1 -0
- {mcp_proxy_adapter-2.1.15.dist-info → mcp_proxy_adapter-2.1.17.dist-info}/METADATA +37 -2
- mcp_proxy_adapter-2.1.17.dist-info/RECORD +30 -0
- mcp_proxy_adapter-2.1.15.dist-info/RECORD +0 -28
- {mcp_proxy_adapter-2.1.15.dist-info → mcp_proxy_adapter-2.1.17.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-2.1.15.dist-info → mcp_proxy_adapter-2.1.17.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-2.1.15.dist-info → mcp_proxy_adapter-2.1.17.dist-info}/top_level.txt +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -34,7 +34,7 @@ class BaseDispatcher(ABC):
|
|
34
34
|
pass
|
35
35
|
|
36
36
|
@abstractmethod
|
37
|
-
|
37
|
+
def execute(self, command: str, **kwargs) -> Any:
|
38
38
|
"""
|
39
39
|
Executes a command with the specified parameters.
|
40
40
|
|
@@ -49,7 +49,7 @@ class BaseDispatcher(ABC):
|
|
49
49
|
CommandNotFoundError: If command is not found
|
50
50
|
CommandExecutionError: On command execution error
|
51
51
|
"""
|
52
|
-
|
52
|
+
pass
|
53
53
|
|
54
54
|
@abstractmethod
|
55
55
|
def get_valid_commands(self) -> List[str]:
|
@@ -79,7 +79,7 @@ class JsonRpcDispatcher(BaseDispatcher):
|
|
79
79
|
),
|
80
80
|
summary="Command help",
|
81
81
|
params={
|
82
|
-
"
|
82
|
+
"cmdname": {
|
83
83
|
"type": "string",
|
84
84
|
"description": "Command name for detailed information",
|
85
85
|
"required": False
|
@@ -141,7 +141,22 @@ class JsonRpcDispatcher(BaseDispatcher):
|
|
141
141
|
async def _return_sync():
|
142
142
|
return result
|
143
143
|
return await _return_sync()
|
144
|
-
except
|
144
|
+
except TypeError as e:
|
145
|
+
# Попробовать вызвать handler(params=kwargs), если ошибка связана с лишними именованными аргументами
|
146
|
+
if (len(params) == 1 and 'params' in params):
|
147
|
+
try:
|
148
|
+
if inspect.iscoroutinefunction(handler):
|
149
|
+
result = handler(params=kwargs)
|
150
|
+
else:
|
151
|
+
result = loop.run_in_executor(None, lambda: handler(params=kwargs))
|
152
|
+
if inspect.isawaitable(result):
|
153
|
+
return await result
|
154
|
+
else:
|
155
|
+
async def _return_sync():
|
156
|
+
return result
|
157
|
+
return await _return_sync()
|
158
|
+
except Exception:
|
159
|
+
pass
|
145
160
|
raise e
|
146
161
|
|
147
162
|
async def execute(self, command: str, **kwargs) -> Any:
|
@@ -197,23 +212,35 @@ class JsonRpcDispatcher(BaseDispatcher):
|
|
197
212
|
|
198
213
|
Args:
|
199
214
|
params: Command parameters
|
200
|
-
|
215
|
+
cmdname: Command name for detailed information
|
201
216
|
|
202
217
|
Returns:
|
203
218
|
Dict[str, Any]: Command help information
|
204
219
|
"""
|
205
220
|
if not params:
|
206
221
|
params = {}
|
207
|
-
|
208
|
-
# If specific command is specified, return information only about it
|
222
|
+
# Если передан неправильный параметр 'command', возвращаем понятную ошибку
|
209
223
|
if "command" in params and params["command"]:
|
210
|
-
|
224
|
+
return {
|
225
|
+
"error": "Parameter 'command' is not supported. Use 'cmdname' instead.",
|
226
|
+
"hint": "Send params: {\"cmdname\": \"your_command\"}",
|
227
|
+
"example": {"jsonrpc": "2.0", "method": "help", "params": {"cmdname": "your_command"}, "id": 1}
|
228
|
+
}
|
229
|
+
# Если handler вызывается синхронно и возвращает coroutine, возвращаем явную ошибку
|
230
|
+
if inspect.iscoroutinefunction(self._help_command):
|
231
|
+
return {
|
232
|
+
"error": "Help handler must be awaited. Call as await dispatcher.execute('help', ...) in async context.",
|
233
|
+
"hint": "Use async endpoint or await the result in your code.",
|
234
|
+
"example": "result = await dispatcher.execute('help', cmdname='your_command')"
|
235
|
+
}
|
236
|
+
# If specific command is specified, return information only about it
|
237
|
+
if "cmdname" in params and params["cmdname"]:
|
238
|
+
command = params["cmdname"]
|
211
239
|
if command not in self._metadata:
|
212
240
|
return {
|
213
241
|
"error": f"Command '{command}' not found",
|
214
242
|
"available_commands": list(self._metadata.keys())
|
215
243
|
}
|
216
|
-
|
217
244
|
return {
|
218
245
|
"command": command,
|
219
246
|
"info": self._metadata[command]
|
@@ -231,5 +258,5 @@ class JsonRpcDispatcher(BaseDispatcher):
|
|
231
258
|
return {
|
232
259
|
"commands": commands_info,
|
233
260
|
"total": len(commands_info),
|
234
|
-
"note": "Use the '
|
261
|
+
"note": "Use the 'cmdname' parameter to get detailed information about a specific command"
|
235
262
|
}
|
@@ -33,13 +33,13 @@ class MyRegistry:
|
|
33
33
|
return self.commands_info.get(command)
|
34
34
|
def get_commands_info(self):
|
35
35
|
return self.commands_info
|
36
|
-
|
36
|
+
def execute(self, command, **params):
|
37
37
|
if command == "sum":
|
38
|
-
return
|
38
|
+
return self.sum_numbers(**params)
|
39
39
|
raise KeyError(f"Unknown command: {command}")
|
40
40
|
def add_generator(self, generator):
|
41
41
|
pass
|
42
|
-
|
42
|
+
def sum_numbers(self, a: int, b: int) -> int:
|
43
43
|
"""
|
44
44
|
Returns the sum of two numbers.
|
45
45
|
|
@@ -20,7 +20,7 @@ class MyRegistry:
|
|
20
20
|
self.commands = {"ping": self.ping, "help": self.help_command}
|
21
21
|
self.commands_info = {
|
22
22
|
"ping": {"description": "Ping command (returns pong)", "params": {}},
|
23
|
-
"help": {"description": "Show help for commands", "params": {"
|
23
|
+
"help": {"description": "Show help for commands", "params": {"cmdname": {"type": "string", "description": "Command name", "required": False}}}
|
24
24
|
}
|
25
25
|
def get_valid_commands(self):
|
26
26
|
return list(self.commands.keys())
|
@@ -33,7 +33,7 @@ class MyRegistry:
|
|
33
33
|
command = args[0]
|
34
34
|
params = {k: v for k, v in params.items()}
|
35
35
|
else:
|
36
|
-
command = params.pop("
|
36
|
+
command = params.pop("cmdname", None)
|
37
37
|
if command == "ping":
|
38
38
|
return self.ping()
|
39
39
|
if command == "help":
|
@@ -44,13 +44,13 @@ class MyRegistry:
|
|
44
44
|
def ping(self):
|
45
45
|
"""Ping command."""
|
46
46
|
return {"result": "pong"}
|
47
|
-
def help_command(self,
|
47
|
+
def help_command(self, cmdname: str = None):
|
48
48
|
"""Custom help logic: returns info for command or all commands."""
|
49
|
-
if not
|
49
|
+
if not cmdname:
|
50
50
|
return {"commands": list(self.commands_info.keys())}
|
51
|
-
if
|
52
|
-
return {"command":
|
53
|
-
return {"error": f"Command '{
|
51
|
+
if cmdname in self.commands_info:
|
52
|
+
return {"command": cmdname, "info": self.commands_info[cmdname]}
|
53
|
+
return {"error": f"Command '{cmdname}' not found"}
|
54
54
|
|
55
55
|
if __name__ == "__main__":
|
56
56
|
registry = MyRegistry()
|
@@ -64,9 +64,9 @@ if __name__ == "__main__":
|
|
64
64
|
print("Help (all)", result_help_all)
|
65
65
|
|
66
66
|
# Call help (ping)
|
67
|
-
result_help_ping = registry.execute("help",
|
67
|
+
result_help_ping = registry.execute("help", cmdname="ping")
|
68
68
|
print("Help (ping)", result_help_ping)
|
69
69
|
|
70
70
|
# Call help (notfound)
|
71
|
-
result_help_notfound = registry.execute("help",
|
71
|
+
result_help_notfound = registry.execute("help", cmdname="notfound")
|
72
72
|
print("Help (notfound)", result_help_notfound)
|
@@ -21,33 +21,33 @@ from mcp_proxy_adapter.testing_utils import MockRegistry
|
|
21
21
|
registry = MockRegistry()
|
22
22
|
adapter = MCPProxyAdapter(registry)
|
23
23
|
|
24
|
-
def robust_help(
|
24
|
+
def robust_help(cmdname: str = None) -> Dict[str, Any]:
|
25
25
|
"""
|
26
26
|
Best practice: always check for project help, handle errors, fallback to adapter help.
|
27
27
|
"""
|
28
28
|
dispatcher = registry.dispatcher
|
29
29
|
if "help" in dispatcher.get_valid_commands():
|
30
30
|
try:
|
31
|
-
if
|
32
|
-
return dispatcher.help_command(
|
31
|
+
if cmdname:
|
32
|
+
return dispatcher.help_command(cmdname=cmdname)
|
33
33
|
return dispatcher.help_command()
|
34
34
|
except Exception as e:
|
35
35
|
# Log error, fallback to adapter help
|
36
36
|
print(f"[WARN] Project help failed: {e}. Fallback to adapter help.")
|
37
|
-
return fallback_adapter_help(
|
37
|
+
return fallback_adapter_help(cmdname)
|
38
38
|
else:
|
39
|
-
return fallback_adapter_help(
|
39
|
+
return fallback_adapter_help(cmdname)
|
40
40
|
|
41
|
-
def fallback_adapter_help(
|
41
|
+
def fallback_adapter_help(cmdname: str = None) -> Dict[str, Any]:
|
42
42
|
"""
|
43
43
|
Fallback: call adapter's help (simulate REST/JSON-RPC call).
|
44
44
|
"""
|
45
45
|
dispatcher = registry.dispatcher
|
46
|
-
if not
|
46
|
+
if not cmdname:
|
47
47
|
return {"source": "adapter", "commands": dispatcher.get_valid_commands()}
|
48
|
-
if
|
49
|
-
return {"source": "adapter", "command":
|
50
|
-
return {"source": "adapter", "error": f"Command '{
|
48
|
+
if cmdname in dispatcher.get_valid_commands():
|
49
|
+
return {"source": "adapter", "command": cmdname, "info": {"description": "Adapter help for command"}}
|
50
|
+
return {"source": "adapter", "error": f"Command '{cmdname}' not found (adapter)", "available_commands": dispatcher.get_valid_commands()}
|
51
51
|
|
52
52
|
# --- Example test cases ---
|
53
53
|
def test_help():
|
@@ -24,29 +24,29 @@ registry = MockRegistry()
|
|
24
24
|
adapter = MCPProxyAdapter(registry)
|
25
25
|
|
26
26
|
# --- Best practice: always check if 'help' is in commands ---
|
27
|
-
def call_help(
|
27
|
+
def call_help(cmdname: str = None) -> Dict[str, Any]:
|
28
28
|
"""Call help command with or without parameter."""
|
29
29
|
dispatcher = registry.dispatcher
|
30
30
|
if "help" in dispatcher.get_valid_commands():
|
31
|
-
if
|
31
|
+
if cmdname:
|
32
32
|
try:
|
33
|
-
return dispatcher.help_command(
|
33
|
+
return dispatcher.help_command(cmdname=cmdname)
|
34
34
|
except Exception as e:
|
35
35
|
print(f"Project help failed: {e}. Fallback to adapter help.")
|
36
|
-
return adapter_help(
|
36
|
+
return adapter_help(cmdname)
|
37
37
|
else:
|
38
38
|
return dispatcher.help_command()
|
39
39
|
else:
|
40
|
-
return adapter_help(
|
40
|
+
return adapter_help(cmdname)
|
41
41
|
|
42
|
-
def adapter_help(
|
42
|
+
def adapter_help(cmdname: str = None) -> Dict[str, Any]:
|
43
43
|
"""Fallback: call adapter's help (simulate)."""
|
44
44
|
dispatcher = registry.dispatcher
|
45
|
-
if not
|
45
|
+
if not cmdname:
|
46
46
|
return {"source": "adapter", "commands": dispatcher.get_valid_commands()}
|
47
|
-
if
|
48
|
-
return {"source": "adapter", "command":
|
49
|
-
return {"source": "adapter", "error": f"Command '{
|
47
|
+
if cmdname in dispatcher.get_valid_commands():
|
48
|
+
return {"source": "adapter", "command": cmdname, "info": {"description": "Adapter help for command"}}
|
49
|
+
return {"source": "adapter", "error": f"Command '{cmdname}' not found (adapter)", "available_commands": dispatcher.get_valid_commands()}
|
50
50
|
|
51
51
|
if __name__ == "__main__":
|
52
52
|
print("=== Project help (no param) ===")
|
@@ -171,7 +171,7 @@ class MockDispatcher:
|
|
171
171
|
"help": {
|
172
172
|
"description": "Show information about available commands or a specific command.",
|
173
173
|
"params": {
|
174
|
-
"
|
174
|
+
"cmdname": {
|
175
175
|
"type": "string",
|
176
176
|
"description": "Command name for detailed info",
|
177
177
|
"required": False
|
@@ -278,19 +278,18 @@ class MockDispatcher:
|
|
278
278
|
|
279
279
|
def help_command(self, **params):
|
280
280
|
"""Return info about all commands or a specific command."""
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
info = self.commands_info.get(command)
|
281
|
+
cmdname = params.get("cmdname")
|
282
|
+
if cmdname:
|
283
|
+
info = self.commands_info.get(cmdname)
|
285
284
|
if info:
|
286
|
-
return {"command":
|
285
|
+
return {"command": cmdname, "info": info}
|
287
286
|
else:
|
288
|
-
return {"error": f"Command '{
|
289
|
-
# Если параметр
|
287
|
+
return {"error": f"Command '{cmdname}' not found", "available_commands": list(self.commands_info.keys())}
|
288
|
+
# Если параметр cmdname не указан, возвращаем краткую информацию обо всех
|
290
289
|
return {
|
291
290
|
"commands": {cmd: {"description": info["description"], "params": info["params"]} for cmd, info in self.commands_info.items()},
|
292
291
|
"total": len(self.commands_info),
|
293
|
-
"note": "Use the '
|
292
|
+
"note": "Use the 'cmdname' parameter to get detailed information about a specific command"
|
294
293
|
}
|
295
294
|
|
296
295
|
# --- Создание registry и FastAPI-приложения на верхнем уровне ---
|
@@ -10,7 +10,6 @@ Run:
|
|
10
10
|
"""
|
11
11
|
import os
|
12
12
|
import sys
|
13
|
-
import asyncio
|
14
13
|
sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
|
15
14
|
from fastapi import FastAPI
|
16
15
|
from mcp_proxy_adapter.adapter import MCPProxyAdapter
|
@@ -27,7 +26,7 @@ class MyRegistry:
|
|
27
26
|
return self.commands_info.get(command)
|
28
27
|
def get_commands_info(self):
|
29
28
|
return self.commands_info
|
30
|
-
|
29
|
+
def execute(self, command, **params):
|
31
30
|
if command == "hello":
|
32
31
|
return {"message": "Hello, world!"}
|
33
32
|
raise KeyError(f"Unknown command: {command}")
|
@@ -45,8 +44,4 @@ adapter.register_endpoints(app)
|
|
45
44
|
|
46
45
|
if __name__ == "__main__":
|
47
46
|
import uvicorn
|
48
|
-
uvicorn.run(app, host="0.0.0.0", port=8000)
|
49
|
-
|
50
|
-
# Call sync handler
|
51
|
-
result_sync = registry.execute('hello')
|
52
|
-
print(result_sync) # {'message': 'Hello, world!'}
|
47
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
@@ -17,14 +17,8 @@ from mcp_proxy_adapter.adapter import MCPProxyAdapter
|
|
17
17
|
class MyRegistry:
|
18
18
|
def __init__(self):
|
19
19
|
self.dispatcher = self
|
20
|
-
self.commands = {
|
21
|
-
|
22
|
-
"async_double": self.async_double
|
23
|
-
}
|
24
|
-
self.commands_info = {
|
25
|
-
"echo": {"description": "Echo input string", "params": {"text": {"type": "string", "description": "Text to echo", "required": True}}},
|
26
|
-
"async_double": {"description": "Double the input asynchronously", "params": {"x": {"type": "integer", "description": "Value to double", "required": True}}}
|
27
|
-
}
|
20
|
+
self.commands = {"echo": self.echo}
|
21
|
+
self.commands_info = {"echo": {"description": "Echo input string", "params": {"text": {"type": "string", "description": "Text to echo", "required": True}}}}
|
28
22
|
def get_valid_commands(self):
|
29
23
|
return list(self.commands.keys())
|
30
24
|
def get_command_info(self, command):
|
@@ -34,17 +28,12 @@ class MyRegistry:
|
|
34
28
|
def execute(self, command, **params):
|
35
29
|
if command == "echo":
|
36
30
|
return self.echo(**params)
|
37
|
-
if command == "async_double":
|
38
|
-
return self.async_double(**params)
|
39
31
|
raise KeyError(f"Unknown command: {command}")
|
40
32
|
def add_generator(self, generator):
|
41
33
|
pass
|
42
34
|
def echo(self, text: str) -> str:
|
43
35
|
"""Echo input string."""
|
44
36
|
return text
|
45
|
-
async def async_double(self, x: int) -> int:
|
46
|
-
await asyncio.sleep(0.01)
|
47
|
-
return x * 2
|
48
37
|
|
49
38
|
def test_echo():
|
50
39
|
registry = MyRegistry()
|
@@ -67,7 +56,7 @@ result_sync = registry.execute('echo', text='hi')
|
|
67
56
|
print(result_sync) # hi
|
68
57
|
|
69
58
|
# Call async handler
|
70
|
-
result_async = asyncio.run(registry.execute('
|
59
|
+
result_async = asyncio.run(registry.execute('async', x=10))
|
71
60
|
print(result_async) # 20
|
72
61
|
|
73
62
|
if __name__ == "__main__":
|
mcp_proxy_adapter/registry.py
CHANGED
@@ -416,16 +416,18 @@ class CommandRegistry:
|
|
416
416
|
|
417
417
|
return stats
|
418
418
|
|
419
|
-
|
419
|
+
def execute(self, command: str, **kwargs) -> Any:
|
420
420
|
"""
|
421
|
-
Executes a command
|
421
|
+
Executes a command through the dispatcher.
|
422
|
+
|
423
|
+
Args:
|
424
|
+
command: Command name
|
425
|
+
**kwargs: Command parameters
|
426
|
+
|
427
|
+
Returns:
|
428
|
+
Any: Command execution result
|
422
429
|
"""
|
423
|
-
|
424
|
-
raise KeyError(f"Unknown command: {command}")
|
425
|
-
handler = self._commands_info[command]["handler"]
|
426
|
-
if inspect.iscoroutinefunction(handler):
|
427
|
-
return await handler(**kwargs)
|
428
|
-
return handler(**kwargs)
|
430
|
+
return self.dispatcher.execute(command, **kwargs)
|
429
431
|
|
430
432
|
def get_commands_info(self) -> Dict[str, Dict[str, Any]]:
|
431
433
|
"""
|
@@ -4,9 +4,6 @@ Test utilities for MCP Proxy Adapter: mock dispatcher, registry, and OpenAPI gen
|
|
4
4
|
Can be used in examples and tests.
|
5
5
|
"""
|
6
6
|
|
7
|
-
import asyncio
|
8
|
-
import inspect
|
9
|
-
|
10
7
|
def success_command(value: int = 1) -> dict:
|
11
8
|
return {"result": value * 2}
|
12
9
|
|
@@ -68,18 +65,13 @@ class MockDispatcher:
|
|
68
65
|
def execute_from_params(self, **params):
|
69
66
|
if "query" in params and params["query"] in self.commands:
|
70
67
|
command = params.pop("query")
|
71
|
-
|
72
|
-
return result
|
68
|
+
return self.execute(command, **params)
|
73
69
|
return {"available_commands": self.get_valid_commands(), "received_params": params}
|
74
70
|
|
75
|
-
|
71
|
+
def execute(self, command, **params):
|
76
72
|
if command not in self.commands:
|
77
73
|
raise KeyError(f"Unknown command: {command}")
|
78
|
-
|
79
|
-
if inspect.iscoroutinefunction(handler):
|
80
|
-
return await handler(**params)
|
81
|
-
loop = asyncio.get_running_loop()
|
82
|
-
return await loop.run_in_executor(None, lambda: handler(**params))
|
74
|
+
return self.commands[command](**params)
|
83
75
|
|
84
76
|
def get_valid_commands(self):
|
85
77
|
return list(self.commands.keys())
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: mcp-proxy-adapter
|
3
|
-
Version: 2.1.
|
3
|
+
Version: 2.1.17
|
4
4
|
Summary: Adapter for exposing Command Registry commands as tools for AI models via MCP Proxy.
|
5
5
|
Home-page: https://github.com/vasilyvz/mcp-proxy-adapter
|
6
6
|
Author: Vasiliy VZ
|
@@ -338,4 +338,39 @@ This project uses GitHub Actions for continuous integration and automated publis
|
|
338
338
|
- All tests are run on every push and pull request.
|
339
339
|
- On push of a new tag (vX.Y.Z), the package is built and published to PyPI automatically.
|
340
340
|
|
341
|
-
See `.github/workflows/publish.yml` for details.
|
341
|
+
See `.github/workflows/publish.yml` for details.
|
342
|
+
|
343
|
+
## Встроенная команда help: правила и типовые ошибки
|
344
|
+
|
345
|
+
### Как работает
|
346
|
+
- Команда `help` всегда встроена в MCPProxyAdapter и не требует реализации или регистрации со стороны пользователя.
|
347
|
+
- Для получения справки по конкретной команде используйте параметр `cmdname`:
|
348
|
+
```json
|
349
|
+
{"jsonrpc": "2.0", "method": "help", "params": {"cmdname": "имя_команды"}, "id": 1}
|
350
|
+
```
|
351
|
+
- Для получения списка всех команд:
|
352
|
+
```json
|
353
|
+
{"jsonrpc": "2.0", "method": "help", "id": 1}
|
354
|
+
```
|
355
|
+
|
356
|
+
### Типовые ошибки и их решения
|
357
|
+
- **Ошибка: передан параметр `command` вместо `cmdname`**
|
358
|
+
- Ответ:
|
359
|
+
```json
|
360
|
+
{"error": "Parameter 'command' is not supported. Use 'cmdname' instead.", "hint": "Send params: {\"cmdname\": \"your_command\"}", ...}
|
361
|
+
```
|
362
|
+
- **Решение:** всегда используйте `cmdname`.
|
363
|
+
- **Ошибка сериализации coroutine**
|
364
|
+
- Причина: handler вызывается без await, либо возвращает coroutine.
|
365
|
+
- Ответ:
|
366
|
+
```json
|
367
|
+
{"error": "Help handler must be awaited. Call as await dispatcher.execute('help', ...) in async context.", ...}
|
368
|
+
```
|
369
|
+
- **Решение:** всегда await-ить dispatcher.execute в async endpoint.
|
370
|
+
|
371
|
+
### Важно для интеграторов и пользователей
|
372
|
+
- Не реализуйте свой обработчик help — используйте встроенный.
|
373
|
+
- Не используйте параметр `command` — только `cmdname`.
|
374
|
+
- Все ошибки help-команды теперь сопровождаются понятной подсказкой и примером корректного запроса.
|
375
|
+
|
376
|
+
---
|
@@ -0,0 +1,30 @@
|
|
1
|
+
mcp_proxy_adapter/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
2
|
+
mcp_proxy_adapter/adapter.py,sha256=x5pT-t4uT12O3GzLurrKBSQ_hwVpjhCRx5oZ5AdZnpY,28856
|
3
|
+
mcp_proxy_adapter/models.py,sha256=8zVWU6ly18pWozOnKQ2gsGpmTgL37-fFE_Fr1SDW-Nk,2530
|
4
|
+
mcp_proxy_adapter/registry.py,sha256=jgC4TKaPbMbAsoxvGp2ToaOE4drD-VfZug7WJbm4IW4,15853
|
5
|
+
mcp_proxy_adapter/schema.py,sha256=HZM0TTQTSi8ha1TEeVevdCyGZOUPoT1soB7Nex0hV50,10947
|
6
|
+
mcp_proxy_adapter/testing_utils.py,sha256=RWjQFNSUtVkeP0qNzp6_jrT6_tub3w_052DrRmvxVk0,4243
|
7
|
+
mcp_proxy_adapter/analyzers/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
8
|
+
mcp_proxy_adapter/analyzers/docstring_analyzer.py,sha256=T3FLJEo_uChShfiEKRl8GpVoHvh5HiudZkxnj4KixfA,7541
|
9
|
+
mcp_proxy_adapter/analyzers/type_analyzer.py,sha256=6Wac7osKwF03waFSwQ8ZM0Wqn_zAP2D-I4WMEpR0hQM,5230
|
10
|
+
mcp_proxy_adapter/dispatchers/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
11
|
+
mcp_proxy_adapter/dispatchers/base_dispatcher.py,sha256=S5_Xri058jAmOWeit1tedB_GMZQ9RLcNcYabA83ZF6k,2288
|
12
|
+
mcp_proxy_adapter/dispatchers/json_rpc_dispatcher.py,sha256=DBhO8DKPBKKBsARlq6IesnOFvl4GU6XVDP8gB6hZMpk,9885
|
13
|
+
mcp_proxy_adapter/examples/analyze_config.py,sha256=vog7TNHDw5ZoYhQLbAvZvEoufmQwH54KJzQBJrSq5w4,4283
|
14
|
+
mcp_proxy_adapter/examples/basic_integration.py,sha256=mtRval4VSUgTb_C2p8U_DPPSEKA08dZYKZk-bOrE4H4,4470
|
15
|
+
mcp_proxy_adapter/examples/docstring_and_schema_example.py,sha256=wFg3Cf2Jgve0J5kFzApvFSII8JOsOGaych64hIC7FqQ,2183
|
16
|
+
mcp_proxy_adapter/examples/extension_example.py,sha256=W5fcvPHjpDSPQnmhAWDJqZtLoUfY7h58ZzFoFXDF3Fc,2525
|
17
|
+
mcp_proxy_adapter/examples/help_best_practices.py,sha256=ByHMDiBT9V-cHoSMr2q0PmbteKELY8mTGeJrvAxOWpY,2646
|
18
|
+
mcp_proxy_adapter/examples/help_usage.py,sha256=pEwb8-QhiWuBPCK9cbHW-oMKxk4WMo345SHIAl-dosg,2577
|
19
|
+
mcp_proxy_adapter/examples/mcp_proxy_client.py,sha256=z4IzFlGigVTQSb8TpcrQ_a0migsmC58LnNwc8wZmTfw,3811
|
20
|
+
mcp_proxy_adapter/examples/openapi_server.py,sha256=HUcnv_XEEur1kLuAotHuwwAhykApVXgVj4miOgk8DYA,13229
|
21
|
+
mcp_proxy_adapter/examples/project_structure_example.py,sha256=sswTo6FZb1F5juHa0FYG3cgvrh3wfgGfJu2bBy5tCm4,1460
|
22
|
+
mcp_proxy_adapter/examples/testing_example.py,sha256=OxFUhGP9OXiu9eWjSpytpQ5MzoR9uww3M4jYb0_v7dc,2004
|
23
|
+
mcp_proxy_adapter/validators/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
24
|
+
mcp_proxy_adapter/validators/docstring_validator.py,sha256=Onpq2iNJ1qF4ejkJJIlBkLROuSNIVALHVmXIgkCpaFI,2934
|
25
|
+
mcp_proxy_adapter/validators/metadata_validator.py,sha256=uCrn38-VYYn89l6f5CC_GoTAHAweaOW2Z6Esro1rtGw,3155
|
26
|
+
mcp_proxy_adapter-2.1.17.dist-info/licenses/LICENSE,sha256=OkApFEwdgMCt_mbvUI-eIwKMSTe38K3XnU2DT5ub-wI,1072
|
27
|
+
mcp_proxy_adapter-2.1.17.dist-info/METADATA,sha256=Ru_MBUNRwbAxzhofC-0M8JD-NmnzmI2-h9wbgned1Z4,10822
|
28
|
+
mcp_proxy_adapter-2.1.17.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
|
29
|
+
mcp_proxy_adapter-2.1.17.dist-info/top_level.txt,sha256=JZT7vPLBYrtroX-ij68JBhJYbjDdghcV-DFySRy-Nnw,18
|
30
|
+
mcp_proxy_adapter-2.1.17.dist-info/RECORD,,
|
@@ -1,28 +0,0 @@
|
|
1
|
-
mcp_proxy_adapter/adapter.py,sha256=x5pT-t4uT12O3GzLurrKBSQ_hwVpjhCRx5oZ5AdZnpY,28856
|
2
|
-
mcp_proxy_adapter/models.py,sha256=8zVWU6ly18pWozOnKQ2gsGpmTgL37-fFE_Fr1SDW-Nk,2530
|
3
|
-
mcp_proxy_adapter/registry.py,sha256=IXzMthYRHvEq37Y99ID49kv1ovqb-RFccKznBKxBRuc,15926
|
4
|
-
mcp_proxy_adapter/schema.py,sha256=HZM0TTQTSi8ha1TEeVevdCyGZOUPoT1soB7Nex0hV50,10947
|
5
|
-
mcp_proxy_adapter/testing_utils.py,sha256=5drf9PFUcmUiShNZXN5x6FSbDzB-2jCAb1RvleUanW0,4510
|
6
|
-
mcp_proxy_adapter/analyzers/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
7
|
-
mcp_proxy_adapter/analyzers/docstring_analyzer.py,sha256=T3FLJEo_uChShfiEKRl8GpVoHvh5HiudZkxnj4KixfA,7541
|
8
|
-
mcp_proxy_adapter/analyzers/type_analyzer.py,sha256=6Wac7osKwF03waFSwQ8ZM0Wqn_zAP2D-I4WMEpR0hQM,5230
|
9
|
-
mcp_proxy_adapter/dispatchers/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
10
|
-
mcp_proxy_adapter/dispatchers/base_dispatcher.py,sha256=G9_dMwboNmpvg9OapWKEXI52QsEiIigfjLMs_7tMbNg,2356
|
11
|
-
mcp_proxy_adapter/dispatchers/json_rpc_dispatcher.py,sha256=sdRwvI5f-2dtI7U_sv6-pqUvxBMirgUDl_P7la3EV0A,8054
|
12
|
-
mcp_proxy_adapter/examples/analyze_config.py,sha256=vog7TNHDw5ZoYhQLbAvZvEoufmQwH54KJzQBJrSq5w4,4283
|
13
|
-
mcp_proxy_adapter/examples/basic_integration.py,sha256=mtRval4VSUgTb_C2p8U_DPPSEKA08dZYKZk-bOrE4H4,4470
|
14
|
-
mcp_proxy_adapter/examples/docstring_and_schema_example.py,sha256=8a6k7_wG1afnjzqCD6W_LBM-N8k3t0w1H_au3-14ds8,2201
|
15
|
-
mcp_proxy_adapter/examples/extension_example.py,sha256=2UnrcHw0yRZuFzyvW6zsJ8_NTmGWU79fnCEG6w8VRDY,2525
|
16
|
-
mcp_proxy_adapter/examples/help_best_practices.py,sha256=Bit9Ywl9vGvM_kuV8DJ6pIDK4mY4mF2Gia9rLc56RpI,2646
|
17
|
-
mcp_proxy_adapter/examples/help_usage.py,sha256=JIUsZofdLFyI7FcwPF-rLxipF1-HaZINzVK1KBh0vxA,2577
|
18
|
-
mcp_proxy_adapter/examples/mcp_proxy_client.py,sha256=z4IzFlGigVTQSb8TpcrQ_a0migsmC58LnNwc8wZmTfw,3811
|
19
|
-
mcp_proxy_adapter/examples/openapi_server.py,sha256=5gRM-EHvMsnNtS_M6l_pNPN5EkSf4X1Lcq4E1Xs5tp0,13387
|
20
|
-
mcp_proxy_adapter/examples/project_structure_example.py,sha256=aWQwQqNn3JOCAB6ngYx_JOUh8uy73B6Q51r6HsPUUmM,1593
|
21
|
-
mcp_proxy_adapter/examples/testing_example.py,sha256=s_ln2U7sMdCey87gmrh9-SjZMF2EW602uGhI1rDsCaM,2461
|
22
|
-
mcp_proxy_adapter/validators/docstring_validator.py,sha256=Onpq2iNJ1qF4ejkJJIlBkLROuSNIVALHVmXIgkCpaFI,2934
|
23
|
-
mcp_proxy_adapter/validators/metadata_validator.py,sha256=uCrn38-VYYn89l6f5CC_GoTAHAweaOW2Z6Esro1rtGw,3155
|
24
|
-
mcp_proxy_adapter-2.1.15.dist-info/licenses/LICENSE,sha256=OkApFEwdgMCt_mbvUI-eIwKMSTe38K3XnU2DT5ub-wI,1072
|
25
|
-
mcp_proxy_adapter-2.1.15.dist-info/METADATA,sha256=nUnnfN0k4rxhUdLgJKk6hJd_6S9I5hswuG3baEHL42Q,8886
|
26
|
-
mcp_proxy_adapter-2.1.15.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
|
27
|
-
mcp_proxy_adapter-2.1.15.dist-info/top_level.txt,sha256=JZT7vPLBYrtroX-ij68JBhJYbjDdghcV-DFySRy-Nnw,18
|
28
|
-
mcp_proxy_adapter-2.1.15.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|