Skip to content

Unify process termination on POSIX & Windows (+ tests) #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

Merged
merged 3 commits into from
Jul 8, 2025

Conversation

felixweinberger
Copy link
Contributor

@felixweinberger felixweinberger commented Jun 26, 2025

Motivation and Context

#555, #559 address important but different ways MCP servers can hang or fail to successfully terminate on Windows & POSIX systems.

This PR re-implements #555 by aligning process termination between Windows and Unix to make sure SIGTERM-ignoring processes don't inadvertently cause hangs.

How Has This Been Tested?

New regression tests added for #555 and #559. These were developed and tested on both POSIX and Windows systems.

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 6 times, most recently 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 felixweinberger force-pushed the fweinberger/stream-cleanup-only-approach branch from 9e8d74b to 3f6c472 Compare June 30, 2025 15:39
@felixweinberger felixweinberger changed the title Add regression tests for #555, #559, #765 [WIP] Add regression tests for #555, #559, #765, #729, #850 Jul 1, 2025
@felixweinberger felixweinberger force-pushed the fweinberger/stream-cleanup-only-approach branch 15 times, most recently from cbaee2e to e8954dd Compare July 1, 2025 17:45
@felixweinberger felixweinberger changed the title [WIP] Add regression tests for #555, #559, #765, #729, #850 Unify process termination on POSIX & Windows and add comprehensive regression testing Jul 1, 2025
@felixweinberger felixweinberger force-pushed the fweinberger/stream-cleanup-only-approach branch 5 times, most recently from e817dfb to 7af9e65 Compare July 4, 2025 17:39
@felixweinberger
Copy link
Contributor Author

felixweinberger commented Jul 4, 2025

Hi @Kludex, @agronholm thanks for taking a look!

For context, we actually don't spawn windows processes with anyio.open_process on Windows anymore since #596. We now use subprocess.Popen - the reason was that trying to spawn processes with anyio way was causing NotImplementedError on Windows when using the SelectorEventLoop (apparently the default for Python 3.13+ and e.g. in Streamlit, Google AgentKit google/adk-python#1321), which was blocking Windows development of MCP servers in those environments.

However, your comment made me realize that we should probably explicitly guard for that case and still use anyio by default if possible as a first try, so I added a commit on top of this PR to attempt with anyio first and only in the NotImplementedError case use the FallbackProcess wrapper.

If this could somehow be fixed upstream in anyio that would of course be fantastic - though we did get feedback from Microsoft that Python's asyncio is relatively underdeveloped on Windows, which might be a root cause why we ran into this issue in the first place and might make it difficult to address there?

@felixweinberger felixweinberger marked this pull request as ready for review July 4, 2025 17:41
@agronholm
Copy link

Any idea why SelectorEventLoop would be the default there? It stopped being the Python default on Windows on Python 3.8.

felixweinberger and others added 2 commits July 7, 2025 14:59
The stdio cleanup was hanging indefinitely when processes ignored
termination signals or took too long to exit. This caused the MCP
client to freeze during shutdown, especially with servers that don't
handle SIGTERM properly.

This was already being handled on Windows, but not Unix systems.
This Commit unifies the two approaches, removing special logic
for windows process termination.

The fix introduces a 2-second timeout for process termination. If a
process doesn't exit gracefully within this window, it's forcefully
killed. This ensures the client always completes cleanup in bounded
time while still giving well-behaved servers a chance to exit cleanly.

This resolves hanging issues reported when MCP servers ignore standard
termination signals.

resolves #555

Also adds regression tests for #559.

Co-authored-by: Cristian Pufu <cristian.pufu@uipath.com>
This re-establishes behavior before #596 in the default case.

- Attempt to use anyio's native open_process function on Windows
- Fall back to subprocess.Popen only if NotImplementedError is raised
- This improves compatibility with event loops that support async subprocesses
- Extract fallback logic into separate function for clarity
@felixweinberger felixweinberger force-pushed the fweinberger/stream-cleanup-only-approach branch from 7af9e65 to c9b43bc Compare July 7, 2025 13:59
@felixweinberger
Copy link
Contributor Author

felixweinberger commented Jul 7, 2025

Any idea why SelectorEventLoop would be the default there? It stopped being the Python default on Windows on Python 3.8.

@agronholm Doing some research based on what @theailanguage raised in #596:

  1. Streamlit:
  1. MCP Adapters:
  1. Jupyter Notebooks (outdated, but as examples):

It seems that while the ProactorEventLoop is the default on Python 3.8+ on Windows, that doesn't necessarily carry over to all applications that might set the event loop internally to SelectorEventLoop for some reason? In such cases this leads to a NotImplementedError

felixweinberger added a commit that referenced this pull request Jul 7, 2025
This test shows that MCP server cleanup code in lifespan doesn't run
when the process is terminated, but does run when stdin is closed first
(as implemented in PR #1044).

The test includes:
- Demonstration of current broken behavior (cleanup doesn't run)
- Verification that stdin closure allows graceful shutdown
- Windows-specific ResourceWarning handling
- Detailed documentation of the issue and solution

Github-Issue:#1027
ihrpr
ihrpr previously approved these changes Jul 7, 2025
Copy link
Contributor

@ihrpr ihrpr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

left a few comments

- Add SIGTERM_IGNORING_PROCESS_TIMEOUT constant in tests to document timeout behavior
- Add PROCESS_TERMINATION_TIMEOUT constant to replace magic number in stdio client
- Restore deprecated terminate_windows_process function with original functionality
  to maintain backward compatibility for external users

The deprecated function is marked using @deprecated decorator following the
codebase convention, while preserving its original terminate-wait-kill behavior.
@felixweinberger felixweinberger force-pushed the fweinberger/stream-cleanup-only-approach branch from 9fd111e to 5771abc Compare July 8, 2025 10:50
@felixweinberger felixweinberger requested a review from ihrpr July 8, 2025 11:28
@felixweinberger felixweinberger merged commit cf72565 into main Jul 8, 2025
22 of 23 checks passed
@felixweinberger felixweinberger deleted the fweinberger/stream-cleanup-only-approach branch July 8, 2025 13:57
felixweinberger added a commit that referenced this pull request Jul 8, 2025
This test shows that MCP server cleanup code in lifespan doesn't run
when the process is terminated, but does run when stdin is closed first
(as implemented in PR #1044).

The test includes:
- Demonstration of current broken behavior (cleanup doesn't run)
- Verification that stdin closure allows graceful shutdown
- Windows-specific ResourceWarning handling
- Detailed documentation of the issue and solution

Github-Issue:#1027
felixweinberger added a commit that referenced this pull request Jul 8, 2025
This test shows that MCP server cleanup code in lifespan doesn't run
when the process is terminated, but does run when stdin is closed first
(as implemented in PR #1044).

The test includes:
- Demonstration of current broken behavior (cleanup doesn't run)
- Verification that stdin closure allows graceful shutdown
- Windows-specific ResourceWarning handling
- Detailed documentation of the issue and solution

Github-Issue:#1027
felixweinberger added a commit that referenced this pull request Jul 8, 2025
This test shows that MCP server cleanup code in lifespan doesn't run
when the process is terminated, but does run when stdin is closed first
(as implemented in PR #1044).

The test includes:
- Demonstration of current broken behavior (cleanup doesn't run)
- Verification that stdin closure allows graceful shutdown
- Windows-specific ResourceWarning handling
- Detailed documentation of the issue and solution

Github-Issue:#1027
felixweinberger added a commit that referenced this pull request Jul 8, 2025
This test shows that MCP server cleanup code in lifespan doesn't run
when the process is terminated, but does run when stdin is closed first
(as implemented in PR #1044).

The test includes:
- Demonstration of current broken behavior (cleanup doesn't run)
- Verification that stdin closure allows graceful shutdown
- Windows-specific ResourceWarning handling
- Detailed documentation of the issue and solution

Github-Issue:#1027
gspencergoog pushed a commit to gspencergoog/mcp-python-sdk that referenced this pull request Jul 29, 2025
* Initial refactor

* draft concept for servers

* remove unused

* Remove ai generated content

* fix navigation

* header

* clean up

* updated

* Initial refactor

* Remove ai generated content

* fix broken links

* feat: create docs-v2 structure with diataxis framework

- Create new docs-v2 directory with modern docs.json configuration
- Implement 3-tab navigation: Overview, Documentation, Community
- Structure Documentation section following diataxis framework:
  - Tutorials (learning-oriented)
  - Learn/Explanations (understanding-oriented)
  - How-to Guides (task-oriented)
  - Reference (information-oriented)
- Add 44 stub files for team members to populate
- Update package.json with docs-v2 scripts
- Use modern docs.json format with proper tabs support

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* format

* fix merge

* fix

* merge

* fix

* build new structure in existing docs folder

* purge docs-v2

* update structure

* create dummy structure

* small rephrasing

* change style to more technical

* merge

* simplify tool

* format

* structure

* titles

* add client concepts

* apply suggested changes

* format

* apply suggested changes

* Update architecture.mdx with comprehensive MCP overview

Replace placeholder content with detailed documentation covering:
- Core MCP concepts and scope
- Participants (host, client, server)
- Protocol layers (data and transport)
- Lifecycle management, primitives, and notifications

* Add comprehensive MCP protocol walkthrough to architecture docs

Includes step-by-step examples of initialization, tool discovery, execution, and real-time notifications with detailed JSON-RPC message exchanges.

Co-Authored-By: Assistant <noreply@anthropic.com>

* Improvements

* Improve MCP architecture documentation clarity and structure

- Fix typos and grammar issues throughout the document
- Add clearer explanations for MCP primitives and their use cases
- Restructure content for better readability and flow
- Enhance descriptions of client-exposed primitives with practical examples

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* MCP protocol -> MCP

* Add SDK documentation page

- Create comprehensive SDK documentation listing all official MCP SDKs
- Include TypeScript, Python, Java, Kotlin, C#, and Ruby SDKs
- Link to package registries and GitHub repositories
- Credit maintainers for each SDK

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* fixup

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Inna Harper <inna.hrpr@gmail.com>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update docs/docs/learn/architecture.mdx

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* Update architecture documentation

- Clarify that reference implementations are specifically for servers
- Simplify data/transport layer explanation by removing Info component
- Update client features description for clarity

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* prettier

* non code example

* fix broken links

* apply suggested changes

* small fixed

* clean up

* move lern after Getting Started

* Fix broken anchor links

* Remove reference to nonexistent section

While there is a "Transport Layer" section (which is linked in a prior
bullet point), there is no section for an overview of the transport
protocol.

* getting started intro

* Fix typos

* Shorten introduction in architecture overview

The automatically-generated table of contents will provide links to
individual sections, so the introduction need not do so comprehensively.
Instead, the introduction can just link to a few key sections.

* fix slyles after merge

* remove reference

* community tab

* use mcp

* use remote mcp

* use local mcp server

* link old pages

* update names for server and client

* sdks page

* add missing SDKs

* remove discord - communications page will be used

* initial landing page

* remove get started from landing page

* improve landing page

* more changes to index

* CI

* CI

* CI

* CI

* broken links

* CI

* fix oops

* docs: improve architecture documentation clarity

Enhance readability of MCP architecture docs by:
- Simplifying client-server relationship explanation
- Adding visual mermaid diagram to illustrate connections
- Clarifying server vs client features descriptions
- Converting Note block to inline text for better flow
- Fixing SDK reference link

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* address theos feedback

* server contepts feedback

* client concepts feedback

* sdks comments

* intro feedback

* remote server

* user page feednacl

* CI

* suggestes changes from basil

* format

* documentation as a first tab

* Remove protocol from specification tab

* bring back schema reference

* documentation page structure

* fix broken links

* reorganize documentation

* fix broken SDKs link

* move faq and remove how to

* server and client examples

* back to old structure in documents

* remove mcp logo on landing page

* format

* remove video (will be added back soon)

* fix AI-powered to AI-enable

* Add inverted logo to overview page with two-column layout

- Added logo2.jpg to the overview page
- Created two-column layout with text on left, logo on right
- Applied invert filter to flip logo colors from white-on-black to black-on-white
- Added rounded corners to logo image

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Reorganize overview page with MCP logo sidebar layout

Place "Connect your AI applications" content on left with MCP logo on right.
Ensure consistent section widths across the page for better visual alignment.
Add responsive design for mobile screens.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* remove logos

* remove hiding of the overview

---------

Co-authored-by: Jerome <jerome@anthropic.com>
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com>
Co-authored-by: David Soria Parra <davidsp@anthropic.com>
Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
gspencergoog pushed a commit to gspencergoog/mcp-python-sdk that referenced this pull request Jul 29, 2025
It appears that this was mistakenly left behind when the site was redesigned in modelcontextprotocol#1044.

The `<Card>` components containing the link information for each SDK are defined in the `docs/docs/sdk.mdx` file:
https://github.com/modelcontextprotocol/modelcontextprotocol/blob/0695a49/docs/docs/sdk.mdx

So the `.mdx` files under the `docs/links/sdks` directory no longer seem necessary.
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.

5 participants