fix(mcp): defer stale stack cleanup during reconnect
This commit is contained in:
@@ -74,6 +74,15 @@ class _FakeMcpTool(Tool):
|
||||
return "ok"
|
||||
|
||||
|
||||
class _CancelScopeStack:
|
||||
def __init__(self) -> None:
|
||||
self.closed = False
|
||||
|
||||
async def aclose(self) -> None:
|
||||
self.closed = True
|
||||
raise asyncio.CancelledError("Cancelled via cancel scope test")
|
||||
|
||||
|
||||
def _make_loop(tmp_path, *, mcp_servers: dict | None = None) -> AgentLoop:
|
||||
bus = MessageBus()
|
||||
provider = MagicMock()
|
||||
@@ -115,6 +124,18 @@ async def test_mcp_read_filter_drops_progress_notifications_without_progress_tok
|
||||
assert forwarded == [tool_change, valid_progress]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_close_mcp_swallows_sdk_cancel_scope_cleanup(tmp_path):
|
||||
loop = _make_loop(tmp_path)
|
||||
stack = _CancelScopeStack()
|
||||
loop._mcp_stacks["remote"] = stack # type: ignore[assignment]
|
||||
|
||||
await loop.close_mcp()
|
||||
|
||||
assert stack.closed is True
|
||||
assert loop._mcp_stacks == {}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_connect_mcp_retries_when_no_servers_connect(tmp_path, monkeypatch: pytest.MonkeyPatch):
|
||||
loop = _make_loop(tmp_path)
|
||||
@@ -220,6 +241,9 @@ async def test_reload_mcp_servers_adds_and_removes_tools_without_restart(
|
||||
assert removed["removed"] == ["browserbase"]
|
||||
assert not loop.tools.has("mcp_browserbase_navigate")
|
||||
assert "browserbase" not in loop._mcp_stacks
|
||||
|
||||
assert closed == []
|
||||
await loop.close_mcp()
|
||||
assert closed == ["browserbase"]
|
||||
|
||||
|
||||
@@ -281,6 +305,9 @@ async def test_request_mcp_reload_reaches_runtime_control_without_restart(
|
||||
assert result["removed"] == ["browserbase"]
|
||||
assert result["requires_restart"] is False
|
||||
assert not loop.tools.has("mcp_browserbase_navigate")
|
||||
|
||||
assert closed == []
|
||||
await loop.close_mcp()
|
||||
assert closed == ["browserbase"]
|
||||
|
||||
|
||||
@@ -376,12 +403,15 @@ async def test_mcp_tool_reconnects_after_session_terminated(
|
||||
|
||||
assert output == "recovered"
|
||||
assert connect_count == 2
|
||||
assert closed == ["remote"]
|
||||
assert closed == []
|
||||
assert sessions[0].call_count == 1
|
||||
assert sessions[1].call_count == 1
|
||||
assert "remote" in loop._mcp_stacks
|
||||
assert loop.tools.get("mcp_remote_quote") is not old_tool
|
||||
|
||||
await loop.close_mcp()
|
||||
assert closed == ["remote", "remote"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mcp_reconnect_handler_uses_sanitized_server_prefix(
|
||||
@@ -491,4 +521,7 @@ async def test_concurrent_mcp_reconnect_reuses_fresh_session(
|
||||
|
||||
assert outputs == ["fresh:alpha", "fresh:beta"]
|
||||
assert connect_count == 2
|
||||
assert closed == ["remote"]
|
||||
assert closed == []
|
||||
|
||||
await loop.close_mcp()
|
||||
assert closed == ["remote", "remote"]
|
||||
|
||||
Reference in New Issue
Block a user