11import shutil
22import sys
3+ import textwrap
34import time
45
6+ import anyio
57import pytest
68
79from mcp .client .session import ClientSession
@@ -178,3 +180,67 @@ async def test_stdio_client_immediate_completion():
178180 elapsed = end_time - start_time
179181 print (f"❌ Test failed after { elapsed :.1f} seconds: { e } " )
180182 raise
183+
184+
185+ @pytest .mark .anyio
186+ @pytest .mark .skipif (sys .platform == "win32" , reason = "Windows signal handling is different" )
187+ async def test_stdio_client_sigint_only_process ():
188+ """
189+ Test cleanup with a process that ignores SIGTERM but responds to SIGINT.
190+
191+ This tests Cristipufu's concern: processes that expect SIGINT (like many
192+ Node.js servers or interactive tools) may not respond to SIGTERM, causing
193+ hanging during cleanup.
194+ """
195+ # Create a Python script that ignores SIGTERM but handles SIGINT
196+ script_content = textwrap .dedent (
197+ """
198+ import signal
199+ import sys
200+ import time
201+
202+ # Ignore SIGTERM (what process.terminate() sends)
203+ signal.signal(signal.SIGTERM, signal.SIG_IGN)
204+
205+ # Handle SIGINT (Ctrl+C signal) by exiting cleanly
206+ def sigint_handler(signum, frame):
207+ sys.exit(0)
208+
209+ signal.signal(signal.SIGINT, sigint_handler)
210+
211+ # Keep running until SIGINT received
212+ while True:
213+ time.sleep(0.1)
214+ """
215+ )
216+
217+ server_params = StdioServerParameters (
218+ command = "python" ,
219+ args = ["-c" , script_content ],
220+ )
221+
222+ start_time = time .time ()
223+
224+ try :
225+ # Use anyio timeout to prevent test from hanging forever
226+ with anyio .fail_after (5.0 ):
227+ async with stdio_client (server_params ) as (read_stream , write_stream ):
228+ # Let the process start and begin ignoring SIGTERM
229+ await anyio .sleep (0.5 )
230+ # Exit context triggers cleanup - this should not hang
231+ pass
232+
233+ end_time = time .time ()
234+ elapsed = end_time - start_time
235+
236+ # Should complete quickly even with SIGTERM-ignoring process
237+ # This will fail if cleanup only uses process.terminate() without fallback
238+ assert elapsed < 5.0 , (
239+ f"stdio_client cleanup took { elapsed :.1f} seconds with SIGTERM-ignoring process. "
240+ f"Expected < 5.0 seconds. This suggests the cleanup needs SIGINT/SIGKILL fallback."
241+ )
242+ except TimeoutError :
243+ pytest .fail (
244+ "stdio_client cleanup timed out after 5.0 seconds with SIGTERM-ignoring process. "
245+ "This confirms the cleanup needs SIGINT/SIGKILL fallback for processes that ignore SIGTERM."
246+ )
0 commit comments