mcp-proxy-adapter 2.1.15__tar.gz → 2.1.16__tar.gz
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-2.1.15/mcp_proxy_adapter.egg-info → mcp_proxy_adapter-2.1.16}/PKG-INFO +1 -1
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/adapter.py +2 -2
- mcp_proxy_adapter-2.1.16/mcp_proxy_adapter/dispatchers/__init__.py +1 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/dispatchers/base_dispatcher.py +2 -2
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/dispatchers/json_rpc_dispatcher.py +5 -5
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/examples/docstring_and_schema_example.py +3 -3
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/examples/extension_example.py +9 -9
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/examples/help_best_practices.py +10 -10
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/examples/help_usage.py +10 -10
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/examples/openapi_server.py +8 -9
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/examples/project_structure_example.py +2 -7
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/examples/testing_example.py +3 -14
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/registry.py +10 -8
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/testing_utils.py +3 -11
- mcp_proxy_adapter-2.1.16/mcp_proxy_adapter/validators/__init__.py +1 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16/mcp_proxy_adapter.egg-info}/PKG-INFO +1 -1
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter.egg-info/SOURCES.txt +2 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/pyproject.toml +1 -1
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/tests/test_adapter.py +60 -116
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/tests/test_adapter_coverage.py +12 -11
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/tests/test_mcp_proxy_adapter.py +66 -64
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/LICENSE +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/MANIFEST.in +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/README.md +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/code_index.yaml +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/docs/RU/FAQ_HOWTO.md +0 -0
- {mcp_proxy_adapter-2.1.15/mcp_proxy_adapter/analyzers → mcp_proxy_adapter-2.1.16/mcp_proxy_adapter}/__init__.py +0 -0
- {mcp_proxy_adapter-2.1.15/mcp_proxy_adapter/dispatchers → mcp_proxy_adapter-2.1.16/mcp_proxy_adapter/analyzers}/__init__.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/analyzers/docstring_analyzer.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/analyzers/type_analyzer.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/examples/analyze_config.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/examples/basic_integration.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/examples/mcp_proxy_client.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/models.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/schema.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/validators/docstring_validator.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/validators/metadata_validator.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter.egg-info/dependency_links.txt +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter.egg-info/requires.txt +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter.egg-info/top_level.txt +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/requirements.txt +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/setup.cfg +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/setup.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/tests/conftest.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/tests/test_basic_dispatcher.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/tests/test_command_registry.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/tests/test_examples.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/tests/test_mcp_proxy_adapter_basic.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/tests/test_part1.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/tests/test_part2.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/tests/test_schema.py +0 -0
- {mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/tests/test_simple_adapter.py +0 -0
@@ -236,7 +236,7 @@ class MCPProxyAdapter:
|
|
236
236
|
# Execute the command
|
237
237
|
logger.debug(f"Executing command {request.method} with parameters {request.params}")
|
238
238
|
try:
|
239
|
-
result =
|
239
|
+
result = self.registry.dispatcher.execute(
|
240
240
|
request.method,
|
241
241
|
**request.params
|
242
242
|
)
|
@@ -537,7 +537,7 @@ class MCPProxyAdapter:
|
|
537
537
|
|
538
538
|
# Execute the command
|
539
539
|
try:
|
540
|
-
result =
|
540
|
+
result = self.registry.dispatcher.execute(command, **params)
|
541
541
|
|
542
542
|
# Return result in MCP Proxy format
|
543
543
|
return {"result": result}
|
@@ -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
|
@@ -197,7 +197,7 @@ class JsonRpcDispatcher(BaseDispatcher):
|
|
197
197
|
|
198
198
|
Args:
|
199
199
|
params: Command parameters
|
200
|
-
|
200
|
+
cmdname: Command name for detailed information
|
201
201
|
|
202
202
|
Returns:
|
203
203
|
Dict[str, Any]: Command help information
|
@@ -206,8 +206,8 @@ class JsonRpcDispatcher(BaseDispatcher):
|
|
206
206
|
params = {}
|
207
207
|
|
208
208
|
# If specific command is specified, return information only about it
|
209
|
-
if "
|
210
|
-
command = params["
|
209
|
+
if "cmdname" in params and params["cmdname"]:
|
210
|
+
command = params["cmdname"]
|
211
211
|
if command not in self._metadata:
|
212
212
|
return {
|
213
213
|
"error": f"Command '{command}' not found",
|
@@ -231,5 +231,5 @@ class JsonRpcDispatcher(BaseDispatcher):
|
|
231
231
|
return {
|
232
232
|
"commands": commands_info,
|
233
233
|
"total": len(commands_info),
|
234
|
-
"note": "Use the '
|
234
|
+
"note": "Use the 'cmdname' parameter to get detailed information about a specific command"
|
235
235
|
}
|
@@ -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():
|
{mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/examples/help_usage.py
RENAMED
@@ -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) ===")
|
{mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/examples/openapi_server.py
RENAMED
@@ -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)
|
{mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/examples/testing_example.py
RENAMED
@@ -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__":
|
@@ -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
|
+
|
{mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter.egg-info/SOURCES.txt
RENAMED
@@ -6,6 +6,7 @@ pyproject.toml
|
|
6
6
|
requirements.txt
|
7
7
|
setup.py
|
8
8
|
docs/RU/FAQ_HOWTO.md
|
9
|
+
mcp_proxy_adapter/__init__.py
|
9
10
|
mcp_proxy_adapter/adapter.py
|
10
11
|
mcp_proxy_adapter/models.py
|
11
12
|
mcp_proxy_adapter/registry.py
|
@@ -32,6 +33,7 @@ mcp_proxy_adapter/examples/mcp_proxy_client.py
|
|
32
33
|
mcp_proxy_adapter/examples/openapi_server.py
|
33
34
|
mcp_proxy_adapter/examples/project_structure_example.py
|
34
35
|
mcp_proxy_adapter/examples/testing_example.py
|
36
|
+
mcp_proxy_adapter/validators/__init__.py
|
35
37
|
mcp_proxy_adapter/validators/docstring_validator.py
|
36
38
|
mcp_proxy_adapter/validators/metadata_validator.py
|
37
39
|
tests/conftest.py
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "mcp-proxy-adapter"
|
7
|
-
version = "2.1.
|
7
|
+
version = "2.1.16"
|
8
8
|
description = "Adapter for exposing Command Registry commands as tools for AI models via MCP Proxy."
|
9
9
|
readme = "README.md"
|
10
10
|
requires-python = ">=3.9"
|
@@ -12,7 +12,6 @@ from typing import Dict, Any, List, Optional, Callable, Type
|
|
12
12
|
import inspect
|
13
13
|
from fastapi import FastAPI
|
14
14
|
from fastapi.testclient import TestClient
|
15
|
-
import asyncio
|
16
15
|
|
17
16
|
# Add parent directory to import path
|
18
17
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
@@ -63,71 +62,6 @@ class BaseDispatcher:
|
|
63
62
|
"""Returns information about all commands."""
|
64
63
|
raise NotImplementedError("Method must be overridden in subclass")
|
65
64
|
|
66
|
-
# Общая информация о командах для моков
|
67
|
-
COMMANDS_INFO = {
|
68
|
-
"success": {
|
69
|
-
"description": "Successful command",
|
70
|
-
"params": {
|
71
|
-
"value": {
|
72
|
-
"type": "integer",
|
73
|
-
"description": "Input value",
|
74
|
-
"required": False,
|
75
|
-
"default": 1
|
76
|
-
}
|
77
|
-
}
|
78
|
-
},
|
79
|
-
"error": {
|
80
|
-
"description": "Command with error",
|
81
|
-
"params": {}
|
82
|
-
},
|
83
|
-
"param": {
|
84
|
-
"description": "Command with parameters",
|
85
|
-
"params": {
|
86
|
-
"required_param": {
|
87
|
-
"type": "string",
|
88
|
-
"description": "Required parameter",
|
89
|
-
"required": True
|
90
|
-
},
|
91
|
-
"optional_param": {
|
92
|
-
"type": "integer",
|
93
|
-
"description": "Optional parameter",
|
94
|
-
"required": False,
|
95
|
-
"default": 0
|
96
|
-
}
|
97
|
-
}
|
98
|
-
},
|
99
|
-
"execute": {
|
100
|
-
"description": "Universal command for executing other commands",
|
101
|
-
"params": {
|
102
|
-
"query": {
|
103
|
-
"type": "string",
|
104
|
-
"description": "Command or query to execute",
|
105
|
-
"required": False
|
106
|
-
}
|
107
|
-
}
|
108
|
-
}
|
109
|
-
}
|
110
|
-
|
111
|
-
# Универсальный обработчик для команды 'execute' в моках
|
112
|
-
def mock_execute_command(query=None, **params):
|
113
|
-
"""Universal command for executing other commands in mocks."""
|
114
|
-
if not query:
|
115
|
-
return {"available_commands": list(MOCK_COMMANDS.keys()), "received_params": params}
|
116
|
-
if query not in COMMANDS_INFO:
|
117
|
-
return {"error": f"Unknown command: {query}"}
|
118
|
-
# Получаем функцию-обработчик
|
119
|
-
handler = MOCK_COMMANDS.get(query)
|
120
|
-
if not handler:
|
121
|
-
return {"error": f"Handler not found for command: {query}"}
|
122
|
-
return handler(**params)
|
123
|
-
|
124
|
-
# Словарь команд для универсального вызова
|
125
|
-
MOCK_COMMANDS = {
|
126
|
-
"success": success_command,
|
127
|
-
"error": error_command,
|
128
|
-
"param": param_command
|
129
|
-
}
|
130
|
-
|
131
65
|
# Mock for command dispatcher
|
132
66
|
class MockDispatcher(BaseDispatcher):
|
133
67
|
"""Mock for command dispatcher in tests."""
|
@@ -137,24 +71,66 @@ class MockDispatcher(BaseDispatcher):
|
|
137
71
|
"success": success_command,
|
138
72
|
"error": error_command,
|
139
73
|
"param": param_command,
|
140
|
-
"execute":
|
74
|
+
"execute": self.execute_from_params
|
75
|
+
}
|
76
|
+
self.commands_info = {
|
77
|
+
"success": {
|
78
|
+
"description": "Successful command",
|
79
|
+
"params": {
|
80
|
+
"value": {
|
81
|
+
"type": "integer",
|
82
|
+
"description": "Input value",
|
83
|
+
"required": False,
|
84
|
+
"default": 1
|
85
|
+
}
|
86
|
+
}
|
87
|
+
},
|
88
|
+
"error": {
|
89
|
+
"description": "Command with error",
|
90
|
+
"params": {}
|
91
|
+
},
|
92
|
+
"param": {
|
93
|
+
"description": "Command with parameters",
|
94
|
+
"params": {
|
95
|
+
"required_param": {
|
96
|
+
"type": "string",
|
97
|
+
"description": "Required parameter",
|
98
|
+
"required": True
|
99
|
+
},
|
100
|
+
"optional_param": {
|
101
|
+
"type": "integer",
|
102
|
+
"description": "Optional parameter",
|
103
|
+
"required": False,
|
104
|
+
"default": 0
|
105
|
+
}
|
106
|
+
}
|
107
|
+
},
|
108
|
+
"execute": {
|
109
|
+
"description": "Universal command for executing other commands",
|
110
|
+
"params": {
|
111
|
+
"query": {
|
112
|
+
"type": "string",
|
113
|
+
"description": "Command or query to execute",
|
114
|
+
"required": False
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
141
118
|
}
|
142
|
-
self.commands_info = COMMANDS_INFO
|
143
|
-
|
144
|
-
async def execute(self, command, **params):
|
145
|
-
if command not in self.commands:
|
146
|
-
raise KeyError(f"Unknown command: {command}")
|
147
|
-
handler = self.commands[command]
|
148
|
-
if inspect.iscoroutinefunction(handler):
|
149
|
-
return await handler(**params)
|
150
|
-
loop = asyncio.get_running_loop()
|
151
|
-
return await loop.run_in_executor(None, lambda: handler(**params))
|
152
119
|
|
153
|
-
|
120
|
+
def execute_from_params(self, **params):
|
121
|
+
"""Executes command based on parameters."""
|
154
122
|
if "query" in params and params["query"] in self.commands:
|
155
123
|
command = params.pop("query")
|
156
|
-
return
|
157
|
-
return {
|
124
|
+
return self.execute(command, **params)
|
125
|
+
return {
|
126
|
+
"available_commands": self.get_valid_commands(),
|
127
|
+
"received_params": params
|
128
|
+
}
|
129
|
+
|
130
|
+
def execute(self, command, **params):
|
131
|
+
if command not in self.commands:
|
132
|
+
raise KeyError(f"Unknown command: {command}")
|
133
|
+
return self.commands[command](**params)
|
158
134
|
|
159
135
|
def get_valid_commands(self):
|
160
136
|
return list(self.commands.keys())
|
@@ -198,7 +174,6 @@ def adapter(registry):
|
|
198
174
|
def test_app(adapter):
|
199
175
|
"""Creates a test FastAPI application with configured adapter."""
|
200
176
|
app = FastAPI()
|
201
|
-
adapter.registry.dispatcher = MockDispatcher()
|
202
177
|
adapter.register_endpoints(app)
|
203
178
|
return TestClient(app)
|
204
179
|
|
@@ -520,15 +495,16 @@ def test_configure_logger():
|
|
520
495
|
default_logger = configure_logger()
|
521
496
|
assert default_logger.name == "mcp_proxy_adapter"
|
522
497
|
|
523
|
-
|
524
|
-
async def test_params_only_format(test_app):
|
498
|
+
def test_params_only_format(test_app):
|
525
499
|
"""Test request format with only params."""
|
526
500
|
# Test request with only params field
|
527
501
|
response = test_app.post("/cmd", json={
|
528
502
|
"params": {"query": "success", "value": 5}
|
529
503
|
})
|
504
|
+
|
530
505
|
assert response.status_code == 200
|
531
506
|
data = response.json()
|
507
|
+
assert "result" in data
|
532
508
|
assert data["result"] == {"result": 10}
|
533
509
|
|
534
510
|
# Test request with command in params
|
@@ -550,36 +526,4 @@ async def test_params_only_format(test_app):
|
|
550
526
|
data = response.json()
|
551
527
|
assert "result" in data
|
552
528
|
assert "available_commands" in data["result"]
|
553
|
-
assert "received_params" in data["result"]
|
554
|
-
|
555
|
-
def test_execute_from_params():
|
556
|
-
dispatcher = SyncMockDispatcher()
|
557
|
-
result = dispatcher.execute_from_params(query="success", value=2)
|
558
|
-
assert result == {"result": 4}
|
559
|
-
result2 = dispatcher.execute_from_params()
|
560
|
-
assert "available_commands" in result2
|
561
|
-
|
562
|
-
class SyncMockDispatcher(BaseDispatcher):
|
563
|
-
def __init__(self):
|
564
|
-
self.commands = {
|
565
|
-
"success": success_command,
|
566
|
-
"error": error_command,
|
567
|
-
"param": param_command,
|
568
|
-
"execute": mock_execute_command
|
569
|
-
}
|
570
|
-
self.commands_info = COMMANDS_INFO
|
571
|
-
def execute_from_params(self, **params):
|
572
|
-
if "query" in params and params["query"] in self.commands:
|
573
|
-
command = params.pop("query")
|
574
|
-
return self.execute(command, **params)
|
575
|
-
return {"available_commands": self.get_valid_commands(), "received_params": params}
|
576
|
-
def execute(self, command, **params):
|
577
|
-
if command not in self.commands:
|
578
|
-
raise KeyError(f"Unknown command: {command}")
|
579
|
-
return self.commands[command](**params)
|
580
|
-
def get_valid_commands(self):
|
581
|
-
return list(self.commands.keys())
|
582
|
-
def get_command_info(self, command):
|
583
|
-
return self.commands_info.get(command)
|
584
|
-
def get_commands_info(self):
|
585
|
-
return self.commands_info
|
529
|
+
assert "received_params" in data["result"]
|
@@ -8,8 +8,6 @@ import json
|
|
8
8
|
import logging
|
9
9
|
from unittest.mock import MagicMock, patch
|
10
10
|
from typing import Dict, Any, List, Optional
|
11
|
-
import asyncio
|
12
|
-
import inspect
|
13
11
|
|
14
12
|
# Add parent directory to path for imports
|
15
13
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
@@ -67,28 +65,31 @@ class MockDispatcher:
|
|
67
65
|
}
|
68
66
|
|
69
67
|
def execute_from_params(self, **params):
|
68
|
+
"""Executes command based on parameters."""
|
70
69
|
if "query" in params and params["query"] in self.commands:
|
71
70
|
command = params.pop("query")
|
72
|
-
|
73
|
-
|
74
|
-
|
71
|
+
return self.execute(command, **params)
|
72
|
+
return {
|
73
|
+
"available_commands": self.get_valid_commands(),
|
74
|
+
"received_params": params
|
75
|
+
}
|
75
76
|
|
76
|
-
|
77
|
+
def execute(self, command, **params):
|
78
|
+
"""Executes command with specified parameters."""
|
77
79
|
if command not in self.commands:
|
78
80
|
raise KeyError(f"Unknown command: {command}")
|
79
|
-
|
80
|
-
if inspect.iscoroutinefunction(handler):
|
81
|
-
return await handler(**params)
|
82
|
-
loop = asyncio.get_running_loop()
|
83
|
-
return await loop.run_in_executor(None, lambda: handler(**params))
|
81
|
+
return self.commands[command](**params)
|
84
82
|
|
85
83
|
def get_valid_commands(self):
|
84
|
+
"""Returns list of available commands."""
|
86
85
|
return list(self.commands.keys())
|
87
86
|
|
88
87
|
def get_command_info(self, command):
|
88
|
+
"""Returns information about command."""
|
89
89
|
return self.commands_info.get(command)
|
90
90
|
|
91
91
|
def get_commands_info(self):
|
92
|
+
"""Returns information about all commands."""
|
92
93
|
return self.commands_info
|
93
94
|
|
94
95
|
# Mock for CommandRegistry
|
@@ -10,8 +10,6 @@ import pytest
|
|
10
10
|
import tempfile
|
11
11
|
from unittest.mock import MagicMock, patch
|
12
12
|
import types
|
13
|
-
import inspect
|
14
|
-
import asyncio
|
15
13
|
|
16
14
|
# Добавляем путь к исходникам
|
17
15
|
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
@@ -137,20 +135,20 @@ class MockDispatcher:
|
|
137
135
|
}
|
138
136
|
|
139
137
|
def execute_from_params(self, **params):
|
138
|
+
"""Executes command based on parameters."""
|
140
139
|
if "query" in params and params["query"] in self.commands:
|
141
140
|
command = params.pop("query")
|
142
|
-
|
143
|
-
|
144
|
-
|
141
|
+
return self.execute(command, **params)
|
142
|
+
return {
|
143
|
+
"available_commands": self.get_valid_commands(),
|
144
|
+
"received_params": params
|
145
|
+
}
|
145
146
|
|
146
|
-
|
147
|
+
def execute(self, command, **params):
|
148
|
+
"""Executes command with specified parameters."""
|
147
149
|
if command not in self.commands:
|
148
150
|
raise KeyError(f"Unknown command: {command}")
|
149
|
-
|
150
|
-
if inspect.iscoroutinefunction(handler):
|
151
|
-
return await handler(**params)
|
152
|
-
loop = asyncio.get_running_loop()
|
153
|
-
return await loop.run_in_executor(None, lambda: handler(**params))
|
151
|
+
return self.commands[command](**params)
|
154
152
|
|
155
153
|
def get_valid_commands(self):
|
156
154
|
"""Returns list of available commands."""
|
@@ -339,16 +337,10 @@ class HelpDispatcher(MockDispatcher):
|
|
339
337
|
if project_help:
|
340
338
|
self.commands["help"] = self.help_command
|
341
339
|
self.commands_info["help"] = {"description": "Project help command", "params": {"command": {"type": "string", "required": False}}}
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
return result
|
347
|
-
async def adapter_help_command(self, **params):
|
348
|
-
result = self.adapter_help(**params)
|
349
|
-
if inspect.isawaitable(result):
|
350
|
-
return await result
|
351
|
-
return result
|
340
|
+
def help_command(self, **params):
|
341
|
+
return self.project_help(**params)
|
342
|
+
def adapter_help_command(self, **params):
|
343
|
+
return self.adapter_help(**params)
|
352
344
|
|
353
345
|
class HelpRegistry(MockRegistry):
|
354
346
|
def __init__(self, project_help=None, adapter_help=None):
|
@@ -376,75 +368,71 @@ def test_successful_command_execution(test_app):
|
|
376
368
|
assert data["result"] == {"result": 10}
|
377
369
|
|
378
370
|
# === HELP WRAPPER TESTS ===
|
379
|
-
|
380
|
-
async def test_help_project_no_param(monkeypatch, help_project_command, help_adapter_command):
|
371
|
+
def test_help_project_no_param(monkeypatch, help_project_command, help_adapter_command):
|
381
372
|
"""help реализован в проекте, вызов без параметров: должен вызываться help проекта."""
|
382
373
|
registry = HelpRegistry(project_help=help_project_command, adapter_help=help_adapter_command)
|
383
374
|
adapter = MCPProxyAdapter(registry)
|
384
|
-
|
375
|
+
# monkeypatch: simulate help-wrapper logic
|
376
|
+
result = registry.dispatcher.help_command()
|
385
377
|
assert result["source"] == "project"
|
386
378
|
assert "commands" in result
|
387
379
|
|
388
|
-
|
389
|
-
async def test_help_project_with_param(monkeypatch, help_project_command, help_adapter_command):
|
380
|
+
def test_help_project_with_param(monkeypatch, help_project_command, help_adapter_command):
|
390
381
|
"""help реализован в проекте, вызов с существующим параметром: должен вызываться help проекта."""
|
391
382
|
registry = HelpRegistry(project_help=help_project_command, adapter_help=help_adapter_command)
|
392
383
|
adapter = MCPProxyAdapter(registry)
|
393
|
-
result =
|
384
|
+
result = registry.dispatcher.help_command(command="success")
|
394
385
|
assert result["source"] == "project"
|
395
386
|
assert result["command"] == "success"
|
396
387
|
assert "info" in result
|
397
388
|
|
398
|
-
|
399
|
-
async def test_help_project_with_wrong_param(monkeypatch, help_project_command, help_adapter_command):
|
389
|
+
def test_help_project_with_wrong_param(monkeypatch, help_project_command, help_adapter_command):
|
400
390
|
"""help реализован в проекте, вызов с несуществующим параметром: help проекта возвращает ошибку, вызывается help-адаптер."""
|
401
391
|
registry = HelpRegistry(project_help=help_project_command, adapter_help=help_adapter_command)
|
402
392
|
adapter = MCPProxyAdapter(registry)
|
403
|
-
|
393
|
+
# Симулируем: если help проекта вернул ошибку, вызываем help-адаптер
|
394
|
+
result = registry.dispatcher.help_command(command="unknown")
|
404
395
|
if "error" in result:
|
405
|
-
adapter_result =
|
396
|
+
adapter_result = registry.dispatcher.adapter_help_command(command="unknown")
|
406
397
|
assert adapter_result["source"] == "adapter"
|
407
398
|
assert "error" in adapter_result
|
408
399
|
else:
|
409
400
|
assert False, "Project help should return error for unknown command"
|
410
401
|
|
411
|
-
|
412
|
-
async def test_help_adapter_no_project(monkeypatch, help_adapter_command):
|
402
|
+
def test_help_adapter_no_project(monkeypatch, help_adapter_command):
|
413
403
|
"""help не реализован в проекте, вызов без параметров: должен вызываться help-адаптер."""
|
414
404
|
registry = HelpRegistry(project_help=None, adapter_help=help_adapter_command)
|
415
405
|
adapter = MCPProxyAdapter(registry)
|
416
|
-
|
406
|
+
# Симулируем: help-адаптер вызывается напрямую
|
407
|
+
result = registry.dispatcher.adapter_help_command()
|
417
408
|
assert result["source"] == "adapter"
|
418
409
|
assert "commands" in result
|
419
410
|
|
420
|
-
|
421
|
-
async def test_help_adapter_with_param_no_project(monkeypatch, help_adapter_command):
|
411
|
+
def test_help_adapter_with_param_no_project(monkeypatch, help_adapter_command):
|
422
412
|
"""help не реализован в проекте, вызов с параметром: должен вызываться help-адаптер."""
|
423
413
|
registry = HelpRegistry(project_help=None, adapter_help=help_adapter_command)
|
424
414
|
adapter = MCPProxyAdapter(registry)
|
425
|
-
result =
|
415
|
+
result = registry.dispatcher.adapter_help_command(command="success")
|
426
416
|
assert result["source"] == "adapter"
|
427
417
|
assert result["command"] == "success"
|
428
418
|
assert "info" in result
|
429
419
|
|
430
420
|
# === COVERAGE BOOST TESTS ===
|
431
|
-
|
432
|
-
async def test_dispatcher_keyerror():
|
421
|
+
def test_dispatcher_keyerror():
|
433
422
|
"""Test KeyError for unknown command in MockDispatcher."""
|
434
423
|
dispatcher = MockDispatcher()
|
435
424
|
with pytest.raises(KeyError):
|
436
|
-
|
425
|
+
dispatcher.execute("unknown")
|
437
426
|
|
438
|
-
|
439
|
-
async def test_dispatcher_type_error():
|
427
|
+
def test_dispatcher_type_error():
|
440
428
|
"""Test TypeError for wrong param type in type_error_command."""
|
441
429
|
dispatcher = MockDispatcher()
|
430
|
+
# Явно добавляем type_error команду, если вдруг отсутствует
|
442
431
|
dispatcher.commands["type_error"] = type_error_command
|
443
432
|
with pytest.raises(TypeError):
|
444
|
-
|
433
|
+
dispatcher.execute("type_error", param="not_an_int")
|
445
434
|
|
446
|
-
|
447
|
-
async def test_execute_from_params_edge():
|
435
|
+
def test_execute_from_params_edge():
|
448
436
|
"""Test execute_from_params with unknown query and empty params."""
|
449
437
|
dispatcher = MockDispatcher()
|
450
438
|
result = dispatcher.execute_from_params(query="unknown")
|
@@ -467,8 +455,7 @@ def test_adapter_fixtures(adapter, adapter_with_openapi, no_schema_adapter, no_o
|
|
467
455
|
assert isinstance(no_optimize_adapter, MCPProxyAdapter)
|
468
456
|
assert isinstance(custom_prefix_adapter, MCPProxyAdapter)
|
469
457
|
|
470
|
-
|
471
|
-
async def test_custom_logger_fixture(custom_logger):
|
458
|
+
def test_custom_logger_fixture(custom_logger):
|
472
459
|
"""Test custom_logger fixture."""
|
473
460
|
logger, log_records = custom_logger
|
474
461
|
logger.info("test message")
|
@@ -508,57 +495,72 @@ def test_help_dispatcher_and_registry():
|
|
508
495
|
assert registry.generators
|
509
496
|
|
510
497
|
# === DETAILED HELP-COMMAND TESTS ===
|
511
|
-
|
512
|
-
async def test_project_help_priority(monkeypatch, help_project_command, help_adapter_command):
|
498
|
+
def test_project_help_priority(monkeypatch, help_project_command, help_adapter_command):
|
513
499
|
"""If project help exists, it must always be called first (no param)."""
|
514
500
|
registry = HelpRegistry(project_help=help_project_command, adapter_help=help_adapter_command)
|
515
501
|
adapter = MCPProxyAdapter(registry)
|
516
|
-
|
502
|
+
# Симулируем вызов help без параметров
|
503
|
+
result = registry.dispatcher.help_command()
|
517
504
|
assert result["source"] == "project"
|
518
505
|
assert "commands" in result
|
506
|
+
# Адаптер не должен вызываться
|
519
507
|
assert not ("adapter" in result.get("source", ""))
|
520
508
|
|
521
|
-
|
522
|
-
async def test_project_help_with_param_success(monkeypatch, help_project_command, help_adapter_command):
|
509
|
+
def test_project_help_with_param_success(monkeypatch, help_project_command, help_adapter_command):
|
523
510
|
"""Project help with valid param: must return project info, not adapter."""
|
524
511
|
registry = HelpRegistry(project_help=help_project_command, adapter_help=help_adapter_command)
|
525
512
|
adapter = MCPProxyAdapter(registry)
|
526
|
-
result =
|
513
|
+
result = registry.dispatcher.help_command(command="success")
|
527
514
|
assert result["source"] == "project"
|
528
515
|
assert result["command"] == "success"
|
529
516
|
assert "info" in result
|
517
|
+
# Адаптер не должен вызываться
|
530
518
|
assert not ("adapter" in result.get("source", ""))
|
531
519
|
|
532
|
-
|
533
|
-
async def test_project_help_with_param_not_found(monkeypatch, help_project_command, help_adapter_command):
|
520
|
+
def test_project_help_with_param_not_found(monkeypatch, help_project_command, help_adapter_command):
|
534
521
|
"""Project help with unknown param: must call adapter help after project help error."""
|
535
522
|
registry = HelpRegistry(project_help=help_project_command, adapter_help=help_adapter_command)
|
536
523
|
adapter = MCPProxyAdapter(registry)
|
537
|
-
result =
|
524
|
+
result = registry.dispatcher.help_command(command="unknown")
|
525
|
+
# Проектный help возвращает ошибку
|
538
526
|
assert "error" in result
|
539
|
-
|
527
|
+
# После ошибки вызывается help-адаптер
|
528
|
+
adapter_result = registry.dispatcher.adapter_help_command(command="unknown")
|
540
529
|
assert adapter_result["source"] == "adapter"
|
541
530
|
assert "error" in adapter_result
|
542
531
|
|
543
|
-
|
544
|
-
|
532
|
+
def test_project_help_with_param_exception(monkeypatch, help_adapter_command):
|
533
|
+
"""Project help raises exception: adapter help must be called as fallback."""
|
534
|
+
def broken_help(**params):
|
535
|
+
raise RuntimeError("project help failed")
|
536
|
+
registry = HelpRegistry(project_help=broken_help, adapter_help=help_adapter_command)
|
537
|
+
adapter = MCPProxyAdapter(registry)
|
538
|
+
# Симулируем: если проектный help падает, вызываем help-адаптер
|
539
|
+
try:
|
540
|
+
registry.dispatcher.help_command(command="any")
|
541
|
+
except Exception as e:
|
542
|
+
adapter_result = registry.dispatcher.adapter_help_command(command="any")
|
543
|
+
assert adapter_result["source"] == "adapter"
|
544
|
+
assert "commands" in adapter_result or "error" in adapter_result
|
545
|
+
|
546
|
+
def test_project_help_with_param_none(monkeypatch, help_project_command, help_adapter_command):
|
545
547
|
"""Project help with param=None: must return project help info."""
|
546
548
|
registry = HelpRegistry(project_help=help_project_command, adapter_help=help_adapter_command)
|
547
549
|
adapter = MCPProxyAdapter(registry)
|
548
|
-
result =
|
550
|
+
result = registry.dispatcher.help_command(command=None)
|
549
551
|
assert result["source"] == "project"
|
550
552
|
assert "commands" in result
|
551
553
|
|
552
|
-
|
553
|
-
async def test_project_help_returns_unexpected_type(monkeypatch, help_adapter_command):
|
554
|
+
def test_project_help_returns_unexpected_type(monkeypatch, help_adapter_command):
|
554
555
|
"""Project help returns unexpected type: adapter help must be called as fallback."""
|
555
556
|
def weird_help(**params):
|
556
557
|
return "not a dict"
|
557
558
|
registry = HelpRegistry(project_help=weird_help, adapter_help=help_adapter_command)
|
558
559
|
adapter = MCPProxyAdapter(registry)
|
559
|
-
result =
|
560
|
+
result = registry.dispatcher.help_command(command="any")
|
561
|
+
# Если результат не dict, вызываем help-адаптер
|
560
562
|
if not isinstance(result, dict):
|
561
|
-
adapter_result =
|
563
|
+
adapter_result = registry.dispatcher.adapter_help_command(command="any")
|
562
564
|
assert adapter_result["source"] == "adapter"
|
563
565
|
assert "commands" in adapter_result or "error" in adapter_result
|
564
566
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/analyzers/type_analyzer.py
RENAMED
File without changes
|
{mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/examples/analyze_config.py
RENAMED
File without changes
|
File without changes
|
{mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter/examples/mcp_proxy_client.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter.egg-info/requires.txt
RENAMED
File without changes
|
{mcp_proxy_adapter-2.1.15 → mcp_proxy_adapter-2.1.16}/mcp_proxy_adapter.egg-info/top_level.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|