mirror of
https://github.com/mims-harvard/ToolUniverse.git
synced 2026-09-19 07:31:47 +08:00
286 lines
9.0 KiB
Python
286 lines
9.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
测试 MCP stdio 模式下关闭和开启 hooks 的情况
|
|
"""
|
|
import subprocess
|
|
import json
|
|
import time
|
|
import sys
|
|
|
|
|
|
def run_stdio_test(hooks_enabled=False):
|
|
"""运行 stdio 测试"""
|
|
print(f"\n{'='*60}")
|
|
print(f"测试模式: {'开启 hooks' if hooks_enabled else '关闭 hooks'}")
|
|
print(f"{'='*60}")
|
|
|
|
# 构建命令
|
|
cmd = [
|
|
sys.executable,
|
|
"-c",
|
|
f"""
|
|
import sys
|
|
sys.path.insert(0, 'src')
|
|
from tooluniverse.smcp_server import run_stdio_server
|
|
sys.argv = ['tooluniverse-smcp-stdio'] + (['--hooks'] if {hooks_enabled} else [])
|
|
run_stdio_server()
|
|
""",
|
|
]
|
|
|
|
print(f"启动命令: {' '.join(cmd[:3])} ...")
|
|
|
|
# 启动服务器进程
|
|
process = subprocess.Popen(
|
|
cmd,
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True,
|
|
bufsize=0,
|
|
)
|
|
|
|
try:
|
|
# 等待服务器启动并读取启动日志
|
|
time.sleep(3)
|
|
|
|
# 读取并丢弃启动日志
|
|
print("读取启动日志...")
|
|
while True:
|
|
line = process.stdout.readline()
|
|
if not line:
|
|
break
|
|
print(f"启动日志: {line.strip()}")
|
|
if "Starting ToolUniverse SMCP Server" in line:
|
|
break
|
|
|
|
# 发送初始化请求
|
|
init_request = {
|
|
"jsonrpc": "2.0",
|
|
"id": 1,
|
|
"method": "initialize",
|
|
"params": {
|
|
"protocolVersion": "2024-11-05",
|
|
"capabilities": {},
|
|
"clientInfo": {"name": "test-client", "version": "1.0.0"},
|
|
},
|
|
}
|
|
|
|
print("发送初始化请求...")
|
|
process.stdin.write(json.dumps(init_request) + "\n")
|
|
process.stdin.flush()
|
|
|
|
# 读取初始化响应
|
|
init_response = process.stdout.readline()
|
|
print(f"初始化响应: {init_response.strip()}")
|
|
|
|
# 发送 tools/list 请求
|
|
list_request = {"jsonrpc": "2.0", "id": 2, "method": "tools/list"}
|
|
|
|
print("发送 tools/list 请求...")
|
|
process.stdin.write(json.dumps(list_request) + "\n")
|
|
process.stdin.flush()
|
|
|
|
# 读取 tools/list 响应
|
|
list_response = process.stdout.readline()
|
|
print(f"tools/list 响应长度: {len(list_response)} 字符")
|
|
|
|
# 发送测试工具调用请求
|
|
test_request = {
|
|
"jsonrpc": "2.0",
|
|
"id": 3,
|
|
"method": "tools/call",
|
|
"params": {
|
|
"name": "OpenTargets_get_target_gene_ontology_by_ensemblID",
|
|
"arguments": {"ensemblId": "ENSG00000012048"},
|
|
},
|
|
}
|
|
|
|
print("发送测试工具调用请求...")
|
|
process.stdin.write(json.dumps(test_request) + "\n")
|
|
process.stdin.flush()
|
|
|
|
# 读取工具调用响应(可能需要等待更长时间)
|
|
print("等待工具调用响应...")
|
|
start_time = time.time()
|
|
|
|
# 读取多行响应,直到找到 JSON 响应
|
|
tool_response = ""
|
|
timeout = 30 # 30秒超时
|
|
while time.time() - start_time < timeout:
|
|
line = process.stdout.readline()
|
|
if not line:
|
|
time.sleep(0.1)
|
|
continue
|
|
|
|
tool_response += line
|
|
print(f"收到响应行: {repr(line)}")
|
|
|
|
# 检查是否是 JSON 响应
|
|
try:
|
|
json.loads(line.strip())
|
|
break
|
|
except json.JSONDecodeError:
|
|
continue
|
|
|
|
end_time = time.time()
|
|
response_time = end_time - start_time
|
|
response_length = len(tool_response)
|
|
|
|
print(f"工具调用响应时间: {response_time:.2f} 秒")
|
|
print(f"工具调用响应长度: {response_length} 字符")
|
|
print(f"原始响应内容: {repr(tool_response)}")
|
|
|
|
# 尝试解析 JSON 响应
|
|
json_response = None
|
|
for line in tool_response.split("\n"):
|
|
if line.strip().startswith('{"jsonrpc"'):
|
|
try:
|
|
json_response = json.loads(line.strip())
|
|
break
|
|
except json.JSONDecodeError:
|
|
continue
|
|
|
|
if json_response:
|
|
print("✅ 成功解析 JSON 响应")
|
|
print(f"响应 ID: {json_response.get('id')}")
|
|
if "result" in json_response:
|
|
print("✅ 工具调用成功")
|
|
elif "error" in json_response:
|
|
print(f"❌ 工具调用失败: {json_response['error']}")
|
|
else:
|
|
print("⚠️ 未找到有效的 JSON 响应")
|
|
|
|
# 继续等待实际的工具调用响应
|
|
print("等待工具调用完成...")
|
|
time.sleep(5) # 等待工具执行完成
|
|
|
|
# 读取工具调用的实际响应
|
|
actual_response = ""
|
|
while True:
|
|
line = process.stdout.readline()
|
|
if not line:
|
|
break
|
|
actual_response += line
|
|
print(f"工具响应行: {repr(line)}")
|
|
|
|
# 检查是否是 JSON 响应
|
|
try:
|
|
json.loads(line.strip())
|
|
break
|
|
except json.JSONDecodeError:
|
|
continue
|
|
|
|
if actual_response:
|
|
print(f"工具调用实际响应长度: {len(actual_response)} 字符")
|
|
|
|
# 尝试解析工具调用响应
|
|
tool_json_response = None
|
|
for line in actual_response.split("\n"):
|
|
if line.strip().startswith('{"jsonrpc"'):
|
|
try:
|
|
tool_json_response = json.loads(line.strip())
|
|
break
|
|
except json.JSONDecodeError:
|
|
continue
|
|
|
|
if tool_json_response and "result" in tool_json_response:
|
|
result_content = tool_json_response["result"]
|
|
if "content" in result_content:
|
|
content_text = str(result_content["content"])
|
|
content_length = len(content_text)
|
|
print(f"工具响应内容长度: {content_length} 字符")
|
|
|
|
# 检查是否是摘要
|
|
if "summary" in content_text.lower() or "摘要" in content_text:
|
|
print("✅ 检测到摘要内容")
|
|
else:
|
|
print("📄 原始内容(未摘要)")
|
|
else:
|
|
print("⚠️ 工具响应中没有 content 字段")
|
|
else:
|
|
print("⚠️ 无法解析工具调用响应")
|
|
|
|
return {
|
|
"hooks_enabled": hooks_enabled,
|
|
"response_time": response_time,
|
|
"response_length": response_length,
|
|
"success": True,
|
|
}
|
|
|
|
except Exception as e:
|
|
print(f"❌ 测试失败: {e}")
|
|
return {
|
|
"hooks_enabled": hooks_enabled,
|
|
"response_time": None,
|
|
"response_length": None,
|
|
"success": False,
|
|
"error": str(e),
|
|
}
|
|
finally:
|
|
# 清理进程
|
|
try:
|
|
process.terminate()
|
|
process.wait(timeout=5)
|
|
except Exception:
|
|
process.kill()
|
|
|
|
|
|
def main():
|
|
"""主函数"""
|
|
print("MCP stdio 模式 hooks 测试")
|
|
print("测试工具: OpenTargets_get_target_gene_ontology_by_ensemblID")
|
|
print("测试参数: ensemblId=ENSG00000012048")
|
|
|
|
# 测试关闭 hooks
|
|
result_no_hooks = run_stdio_test(hooks_enabled=False)
|
|
|
|
# 测试开启 hooks
|
|
result_with_hooks = run_stdio_test(hooks_enabled=True)
|
|
|
|
# 对比结果
|
|
print(f"\n{'='*60}")
|
|
print("测试结果对比")
|
|
print(f"{'='*60}")
|
|
|
|
print("关闭 hooks:")
|
|
if result_no_hooks["success"]:
|
|
print(
|
|
f" ✅ 成功 - 响应时间: {result_no_hooks['response_time']:.2f}s, 长度: {result_no_hooks['response_length']} 字符"
|
|
)
|
|
else:
|
|
print(f" ❌ 失败 - {result_no_hooks.get('error', '未知错误')}")
|
|
|
|
print("开启 hooks:")
|
|
if result_with_hooks["success"]:
|
|
print(
|
|
f" ✅ 成功 - 响应时间: {result_with_hooks['response_time']:.2f}s, 长度: {result_with_hooks['response_length']} 字符"
|
|
)
|
|
else:
|
|
print(f" ❌ 失败 - {result_with_hooks.get('error', '未知错误')}")
|
|
|
|
# 性能对比
|
|
if result_no_hooks["success"] and result_with_hooks["success"]:
|
|
time_diff = (
|
|
result_with_hooks["response_time"] - result_no_hooks["response_time"]
|
|
)
|
|
length_diff = (
|
|
result_with_hooks["response_length"] - result_no_hooks["response_length"]
|
|
)
|
|
|
|
print("\n性能对比:")
|
|
print(
|
|
f" 时间差异: {time_diff:+.2f}s ({'hooks 更慢' if time_diff > 0 else 'hooks 更快'})"
|
|
)
|
|
print(
|
|
f" 长度差异: {length_diff:+d} 字符 ({'hooks 更长' if length_diff > 0 else 'hooks 更短'})"
|
|
)
|
|
|
|
if abs(time_diff) < 1.0:
|
|
print(" ✅ 时间差异在可接受范围内")
|
|
else:
|
|
print(" ⚠️ 时间差异较大,需要进一步优化")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|