mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-07-10 05:48:07 +08:00
144 lines
4.6 KiB
Python
144 lines
4.6 KiB
Python
import sys
|
|
import textwrap
|
|
|
|
import pytest
|
|
|
|
from app.agent.mcp import AgentMcpManager, AgentMcpToolSpec
|
|
from app.agent.tools.impl.mcp import McpExternalTool
|
|
from app.schemas.agent import AgentMcpServerConfig
|
|
|
|
|
|
def _write_stdio_mcp_server(tmp_path):
|
|
"""写入一个用于测试的最小 stdio MCP 服务。"""
|
|
server_path = tmp_path / "stdio_mcp_server.py"
|
|
server_path.write_text(
|
|
textwrap.dedent(
|
|
"""
|
|
import json
|
|
import sys
|
|
|
|
TOOLS = [
|
|
{
|
|
"name": "echo",
|
|
"description": "Echo input text.",
|
|
"inputSchema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"text": {"type": "string", "description": "Text to echo"}
|
|
},
|
|
"required": ["text"],
|
|
},
|
|
}
|
|
]
|
|
|
|
for line in sys.stdin:
|
|
request = json.loads(line)
|
|
request_id = request.get("id")
|
|
method = request.get("method")
|
|
if request_id is None:
|
|
continue
|
|
if method == "initialize":
|
|
result = {
|
|
"protocolVersion": request["params"]["protocolVersion"],
|
|
"capabilities": {"tools": {}},
|
|
"serverInfo": {"name": "Fake MCP", "version": "1.0.0"},
|
|
}
|
|
elif method == "tools/list":
|
|
result = {"tools": TOOLS}
|
|
elif method == "tools/call":
|
|
args = request.get("params", {}).get("arguments", {})
|
|
result = {"content": [{"type": "text", "text": args.get("text", "")}]}
|
|
else:
|
|
result = {}
|
|
print(json.dumps({"jsonrpc": "2.0", "id": request_id, "result": result}), flush=True)
|
|
"""
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
return server_path
|
|
|
|
|
|
@pytest.mark.anyio
|
|
async def test_stdio_mcp_server_lists_tools(tmp_path):
|
|
"""stdio MCP 服务器应能被初始化并读取工具列表。"""
|
|
server_path = _write_stdio_mcp_server(tmp_path)
|
|
manager = AgentMcpManager()
|
|
server = AgentMcpServerConfig(
|
|
id="fake",
|
|
name="Fake MCP",
|
|
transport="stdio",
|
|
command=sys.executable,
|
|
args=[str(server_path)],
|
|
timeout=5,
|
|
)
|
|
|
|
tools = await manager.list_server_tools(server)
|
|
|
|
assert len(tools) == 1
|
|
assert tools[0].name == "echo"
|
|
assert tools[0].agent_tool_name == "mcp_fake_mcp_echo"
|
|
assert tools[0].input_schema["properties"]["text"]["type"] == "string"
|
|
|
|
|
|
@pytest.mark.anyio
|
|
async def test_stdio_mcp_server_calls_tool(tmp_path):
|
|
"""stdio MCP 工具应能通过 tools/call 返回内容。"""
|
|
server_path = _write_stdio_mcp_server(tmp_path)
|
|
manager = AgentMcpManager()
|
|
server = AgentMcpServerConfig(
|
|
id="fake",
|
|
name="Fake MCP",
|
|
transport="stdio",
|
|
command=sys.executable,
|
|
args=[str(server_path)],
|
|
timeout=5,
|
|
)
|
|
|
|
result = await manager.call_server_tool(server, "echo", {"text": "hello"})
|
|
|
|
assert result == {"content": [{"type": "text", "text": "hello"}]}
|
|
|
|
|
|
def test_normalize_server_generates_runtime_defaults():
|
|
"""MCP 配置规范化应补齐默认值并清理空字段。"""
|
|
manager = AgentMcpManager()
|
|
|
|
server = manager.normalize_server(
|
|
{
|
|
"id": "demo",
|
|
"name": "Demo",
|
|
"transport": "http",
|
|
"url": " https://example.com/mcp ",
|
|
"headers": {" Authorization ": "Bearer token", "": "ignored"},
|
|
"timeout": "bad",
|
|
}
|
|
)
|
|
|
|
assert server.id == "demo"
|
|
assert server.url == "https://example.com/mcp"
|
|
assert server.headers == {"Authorization": "Bearer token"}
|
|
assert server.timeout == 30
|
|
assert server.require_admin is True
|
|
|
|
|
|
def test_mcp_external_tool_uses_discovered_schema():
|
|
"""外部 MCP 工具应保留发现到的 JSON Schema。"""
|
|
server = AgentMcpServerConfig(id="fake", name="Fake MCP")
|
|
spec = AgentMcpToolSpec(
|
|
server=server,
|
|
name="echo",
|
|
agent_tool_name="mcp_fake_echo",
|
|
description="Echo input text.",
|
|
input_schema={
|
|
"type": "object",
|
|
"properties": {"text": {"type": "string"}},
|
|
"required": ["text"],
|
|
},
|
|
)
|
|
|
|
tool = McpExternalTool(spec=spec, session_id="session-1", user_id="10001")
|
|
|
|
assert tool.name == "mcp_fake_echo"
|
|
assert tool.args_schema["properties"]["text"]["type"] == "string"
|
|
assert tool.require_admin is True
|