diff --git a/tests/tools/test_mcp_tool_session_expired.py b/tests/tools/test_mcp_tool_session_expired.py index 4533282e70..59601ba1c3 100644 --- a/tests/tools/test_mcp_tool_session_expired.py +++ b/tests/tools/test_mcp_tool_session_expired.py @@ -53,6 +53,17 @@ def test_is_session_expired_detects_session_terminated(): assert _is_session_expired_error(RuntimeError("Session terminated")) is True +def test_is_session_expired_detects_stale_pipe_and_closed_transport_variants(): + """Stdio/AnyIO stale-pipe failures usually surface as closed-resource + or broken-pipe text, not an HTTP session-expired JSON-RPC error.""" + from tools.mcp_tool import _is_session_expired_error + assert _is_session_expired_error(RuntimeError("ClosedResourceError")) is True + assert _is_session_expired_error(RuntimeError("closed resource in MCP child")) is True + assert _is_session_expired_error(RuntimeError("transport is closed")) is True + assert _is_session_expired_error(RuntimeError("Broken pipe while writing request")) is True + assert _is_session_expired_error(RuntimeError("End of file from MCP server")) is True + + def test_is_session_expired_is_case_insensitive(): """Match uses lower-cased comparison so servers that emit the message in different cases (SDK formatter quirks) still trigger.""" diff --git a/tools/mcp_tool.py b/tools/mcp_tool.py index b2e0ae802c..5c45f1c4f2 100644 --- a/tools/mcp_tool.py +++ b/tools/mcp_tool.py @@ -1739,6 +1739,12 @@ _SESSION_EXPIRED_MARKERS: tuple = ( "session not found", "unknown session", "session terminated", + "closedresourceerror", + "closed resource", + "transport is closed", + "connection closed", + "broken pipe", + "end of file", )