Skip to content

Add regression tests for #555, #559, #765 #1044

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

felixweinberger
Copy link
Contributor

@felixweinberger felixweinberger commented Jun 26, 2025

Motivation and Context

#555, #559, #765 address important but different ways MCP servers can hang if they don't respect SIGTERM or SIGINT.

This PR adds regressions tests to clearly demonstrate the edge cases addressed.

How Has This Been Tested?

New regression tests added (each fails without the relevant fix)

For #555 and #559:

  • See commit 2

For #765:

  • See commit 4

Breaking Changes

None.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@felixweinberger felixweinberger force-pushed the fweinberger/stream-cleanup-only-approach branch 2 times, most recently from 9f88ea7 to a27fcef Compare June 26, 2025 21:31
@felixweinberger felixweinberger force-pushed the fweinberger/stream-cleanup-only-approach branch 5 times, most recently from 98012bf to caa5628 Compare June 26, 2025 21:54
**NOTE: These tests FAIL without the changes introduced in #559**

- test_stdio_client_universal_timeout
- test_stdio_client_immediate_completion

await read_stream.aclose()
await write_stream.aclose()
await read_stream_writer.aclose()
await write_stream_reader.aclose()

These tests verify that stdio_client completes cleanup within reasonable
time for both slow-terminating and fast-exiting processes, preventing
the hanging issues reported in #559.

**NOTE: This test FAILS without the changes introduced in #555**

- test_stdio_client_sigint_only_process

try:
    process.terminate()
    with anyio.fail_after(2.0):
        await process.wait()
except TimeoutError:
    # If process doesn't terminate in time, force kill it
    process.kill()

This test verifies that on UNIX systems MCP servers that don't respect
SIGTERM but e.g. SIGINT still get terminated after a grace period.
@felixweinberger felixweinberger force-pushed the fweinberger/stream-cleanup-only-approach branch from caa5628 to 1d495ca Compare June 30, 2025 14:55
@felixweinberger felixweinberger changed the title Add regression tests for #559 and #555 Add regression tests for #559, #555, #765 Jun 30, 2025
@felixweinberger felixweinberger changed the title Add regression tests for #559, #555, #765 Add regression tests for #555, #559, #765 Jun 30, 2025
felixweinberger and others added 2 commits June 30, 2025 16:39
Following the MCP spec recommendations, the shutdown sequence now:
1. Closes stdin first to allow graceful server exit
2. Waits 2 seconds for the server to exit on its own
3. Only escalates to SIGTERM if the server doesn't exit
4. Finally uses SIGKILL as a last resort

This unified approach works consistently across all platforms and gives
well-behaved servers a chance to exit cleanly without signals.

Co-Authored-By: davenpi <[email protected]>
Added two tests to validate the stdin-first shutdown behavior:

1. test_stdio_client_graceful_stdin_exit: Verifies that a well-behaved
   server exits cleanly when stdin is closed, without needing signals

2. test_stdio_client_stdin_close_ignored: Tests proper escalation to
   SIGTERM when a process ignores stdin closure

These tests ensure the MCP spec shutdown sequence works correctly and
provides graceful exit opportunities before using forceful termination.
@felixweinberger felixweinberger force-pushed the fweinberger/stream-cleanup-only-approach branch from 9e8d74b to 3f6c472 Compare June 30, 2025 15:39
- Add process group support on POSIX systems via start_new_session=True
- Implement _terminate_process_with_children() and _kill_process_and_children()
  functions that properly handle child process cleanup
- On POSIX: Kill entire process group at once to avoid orphaning children
  when parent dies first
- On Windows: Use taskkill /T for process tree termination
- Add pid property to FallbackProcess for consistent interface
- Add proper type annotations (Process | FallbackProcess)
- Replace existing termination logic with new unified approach

This addresses issues from PRs #850 (npx child processes) and #729
(Windows process trees) in a unified, dependency-free way.

Reported-by:surya-prakash-susarla
Reported-by:jingx8885
Github-Issue:#547
- test_stdio_client_child_process_cleanup: Verifies that child processes
  spawned by the parent (like npx spawning node) are properly terminated
- test_stdio_client_nested_process_tree: Tests that deeply nested process
  trees (parent -> child -> grandchild) are all terminated
- test_stdio_client_early_parent_exit: Tests the race condition where
  parent exits during cleanup but children are still terminated via
  process group

These tests verify the fix works correctly across different subprocess
spawning scenarios without requiring external dependencies like psutil.
Replace platform-specific commands (ping/sleep) with a Python script
to ensure consistent behavior across all platforms. Also adjust timing
expectations to account for the full shutdown sequence on Windows:
- 2 seconds waiting for stdin closure response
- 2 seconds waiting for terminate() to complete
- Plus overhead

This prevents the test from timing out on Windows while still properly
testing the cleanup mechanism.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant