summaryrefslogtreecommitdiff
path: root/scripts/python/p0_regression.py
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-06-30 19:03:25 +0900
committertanaka takashi <isazaka@gmail.com>2026-06-30 19:03:25 +0900
commit05c8e0a31a0d4192c590e833b24393208e1c751f (patch)
treef7c2c82c9327abde67bf3acf1d0258573b3bc6c2 /scripts/python/p0_regression.py
parent4bc721c3bf7589b519c5730d093f704ce56e3f09 (diff)
fix: MCPCをvendored fork化しMCP準拠と接続安定性を改善
- Git submoduleを廃止してMCPCを直接管理 - Windowsで254リクエスト付近に発生する切断を修正 - JSON-RPC 2.0エラー応答を標準化 - tool schemaとruntimeの必須・任意引数を一致 - 1000回連続呼び出しの回帰テストを追加
Diffstat (limited to 'scripts/python/p0_regression.py')
-rw-r--r--scripts/python/p0_regression.py107
1 files changed, 107 insertions, 0 deletions
diff --git a/scripts/python/p0_regression.py b/scripts/python/p0_regression.py
new file mode 100644
index 0000000..61cd426
--- /dev/null
+++ b/scripts/python/p0_regression.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+"""P0 MCP SDK interoperability and long-running connection regression test."""
+
+from __future__ import annotations
+
+import argparse
+import asyncio
+import json
+import os
+import shutil
+import tempfile
+from pathlib import Path
+
+from mcp.client.session import ClientSession
+from mcp.client.stdio import StdioServerParameters, stdio_client
+from mcp.types import CallToolResult
+
+
+def payload(result: CallToolResult) -> dict:
+ if result.isError:
+ raise AssertionError(f"tool error: {result.content!r}")
+ texts = [item.text for item in result.content if item.type == "text"]
+ return json.loads("".join(texts))
+
+
+async def run(server: Path, iterations: int) -> None:
+ data_dir = Path(tempfile.mkdtemp(prefix="tiny-convmem-sdk-p0-"))
+ params = StdioServerParameters(
+ command=str(server.resolve()),
+ args=["--data-dir", str(data_dir)],
+ env=dict(os.environ),
+ )
+ completed = 0
+ with tempfile.TemporaryFile(mode="w+", encoding="utf-8") as errlog:
+ try:
+ async with stdio_client(params, errlog=errlog) as (read_stream, write_stream):
+ async with ClientSession(read_stream, write_stream) as session:
+ await session.initialize()
+ listed = await session.list_tools()
+ assert {tool.name for tool in listed.tools} == {
+ "write_message",
+ "read_messages",
+ "search_messages",
+ "delete_message",
+ "rebuild_index",
+ "get_system_health",
+ }
+ for index in range(20):
+ payload(
+ await session.call_tool(
+ "write_message",
+ {
+ "session_id": "target",
+ "role": "user",
+ "content": f"sdk needle target {index}",
+ },
+ )
+ )
+ for index in range(10):
+ payload(
+ await session.call_tool(
+ "write_message",
+ {
+ "session_id": "other",
+ "role": "user",
+ "content": f"sdk needle other {index}",
+ },
+ )
+ )
+
+ for index in range(iterations):
+ arguments = {"query": "needle"}
+ if index % 2:
+ arguments["session_id"] = "target"
+ results = payload(
+ await session.call_tool("search_messages", arguments)
+ )["results"]
+ assert len(results) == 20
+ if index % 2:
+ assert all(item["session_id"] == "target" for item in results)
+ completed += 1
+ errlog.flush()
+ errlog.seek(0)
+ server_stderr = errlog.read()
+ if "AddressSanitizer" in server_stderr or "ERROR:" in server_stderr:
+ raise AssertionError(f"server diagnostics reported a failure:\n{server_stderr}")
+ except Exception as exc:
+ errlog.flush()
+ errlog.seek(0)
+ raise RuntimeError(
+ f"failed after {completed}/{iterations} calls; server stderr:\n{errlog.read()}"
+ ) from exc
+ finally:
+ shutil.rmtree(data_dir, ignore_errors=True)
+
+
+def main() -> None:
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--server", type=Path, required=True)
+ parser.add_argument("--iterations", type=int, default=1000)
+ args = parser.parse_args()
+ asyncio.run(run(args.server, args.iterations))
+ print(f"Python MCP SDK completed {args.iterations} alternating searches")
+
+
+if __name__ == "__main__":
+ main()