@@ -522,21 +522,101 @@ def test_stop_with_background_thread_but_no_event_loop():
522522 # Verify cleanup occurred
523523 assert client ._background_thread is None
524524
525-
526- def test_mcp_client_state_reset_after_timeout ():
527- """Test that all client state is properly reset after timeout."""
528- def slow_transport ():
529- time .sleep (4 ) # Longer than timeout
530- return MagicMock ()
531525
532- client = MCPClient (slow_transport , startup_timeout = 2 )
533-
534- # First attempt should timeout
535- with pytest .raises (MCPClientInitializationError , match = "background thread did not start in 2 seconds" ):
536- client .start ()
526+ @pytest .mark .asyncio
527+ async def test_call_tool_async_with_default_timeout (mock_transport , mock_session ):
528+ """Test that call_tool_async uses default timeout when none specified."""
529+ from datetime import timedelta
537530
538- # Verify all state is reset
539- assert client ._background_thread is None
540- assert client ._background_thread_session is None
541- assert client ._background_thread_event_loop is None
542- assert not client ._init_future .done () # New future created
531+ mock_content = MCPTextContent (type = "text" , text = "Test message" )
532+ mock_result = MCPCallToolResult (isError = False , content = [mock_content ])
533+ mock_session .call_tool .return_value = mock_result
534+
535+ default_timeout = timedelta (minutes = 10 )
536+ with MCPClient (mock_transport ["transport_callable" ], default_tool_timeout = default_timeout ) as client :
537+ with (
538+ patch ("asyncio.run_coroutine_threadsafe" ) as mock_run_coroutine_threadsafe ,
539+ patch ("asyncio.wrap_future" ) as mock_wrap_future ,
540+ ):
541+ mock_future = MagicMock ()
542+ mock_run_coroutine_threadsafe .return_value = mock_future
543+
544+ # Create an async mock that resolves to the mock result
545+ async def mock_awaitable ():
546+ return mock_result
547+
548+ mock_wrap_future .return_value = mock_awaitable ()
549+
550+ result = await client .call_tool_async (
551+ tool_use_id = "test-123" ,
552+ name = "test_tool" ,
553+ arguments = {"param" : "value" },
554+ # No read_timeout_seconds specified - should use default
555+ )
556+
557+ # Verify the default timeout was used
558+ mock_run_coroutine_threadsafe .assert_called_once ()
559+ mock_wrap_future .assert_called_once_with (mock_future )
560+
561+ assert result ["status" ] == "success"
562+ assert result ["toolUseId" ] == "test-123"
563+
564+
565+ def test_call_tool_sync_with_default_timeout (mock_transport , mock_session ):
566+ """Test that call_tool_sync uses default timeout when none specified."""
567+ from datetime import timedelta
568+
569+ mock_content = MCPTextContent (type = "text" , text = "Test message" )
570+ mock_session .call_tool .return_value = MCPCallToolResult (isError = False , content = [mock_content ])
571+
572+ default_timeout = timedelta (minutes = 10 )
573+ with MCPClient (mock_transport ["transport_callable" ], default_tool_timeout = default_timeout ) as client :
574+ result = client .call_tool_sync (tool_use_id = "test-123" , name = "test_tool" , arguments = {"param" : "value" })
575+
576+ # The session.call_tool should have been called with the default timeout
577+ mock_session .call_tool .assert_called_once_with ("test_tool" , {"param" : "value" }, default_timeout )
578+
579+ assert result ["status" ] == "success"
580+ assert result ["toolUseId" ] == "test-123"
581+
582+
583+ def test_call_tool_sync_explicit_timeout_overrides_default (mock_transport , mock_session ):
584+ """Test that explicit timeout overrides default timeout."""
585+ from datetime import timedelta
586+
587+ mock_content = MCPTextContent (type = "text" , text = "Test message" )
588+ mock_session .call_tool .return_value = MCPCallToolResult (isError = False , content = [mock_content ])
589+
590+ default_timeout = timedelta (minutes = 10 )
591+ explicit_timeout = timedelta (minutes = 5 )
592+
593+ with MCPClient (mock_transport ["transport_callable" ], default_tool_timeout = default_timeout ) as client :
594+ result = client .call_tool_sync (
595+ tool_use_id = "test-123" ,
596+ name = "test_tool" ,
597+ arguments = {"param" : "value" },
598+ read_timeout_seconds = explicit_timeout ,
599+ )
600+
601+ # The session.call_tool should have been called with the explicit timeout, not default
602+ mock_session .call_tool .assert_called_once_with ("test_tool" , {"param" : "value" }, explicit_timeout )
603+
604+ assert result ["status" ] == "success"
605+ assert result ["toolUseId" ] == "test-123"
606+
607+
608+ def test_mcp_client_initialization_with_default_timeout ():
609+ """Test that MCPClient can be initialized with default_tool_timeout."""
610+ from datetime import timedelta
611+
612+ default_timeout = timedelta (minutes = 15 )
613+ client = MCPClient (MagicMock (), default_tool_timeout = default_timeout )
614+
615+ assert client ._default_tool_timeout == default_timeout
616+
617+
618+ def test_mcp_client_initialization_without_default_timeout ():
619+ """Test that MCPClient initializes with None default_tool_timeout when not specified."""
620+ client = MCPClient (MagicMock ())
621+
622+ assert client ._default_tool_timeout is None
0 commit comments