Skip to content

fix: make stdio shutdown more graceful #364

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 1 commit into from
Aug 18, 2025

Conversation

jokemanfire
Copy link
Collaborator

According to the protocol specifications

Motivation and Context

Make shutdown more graceful.

How Has This Been Tested?

Ut

Breaking Changes

No breaking change

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

@jokemanfire
Copy link
Collaborator Author

I'm not sure if adding a shutdown interface is necessary, although it may increase some flexibility for the caller, it may be redundant for the SDK. For me, the previous direct killing behavior was not very elegant, and I would like to hear more opinions.How about you ?
@4t145 @alexhancock

@4t145
Copy link
Collaborator

4t145 commented Aug 12, 2025

I prefer to have a easier approch to cleanup in drop, and give user more flexibility in another explicit method. Or maybe we can just provide an into_inner method so the user can do anything they like with inner process. We can also wrap this wait-and-kill approch into a method, just as @crepererum metioned.

@jokemanfire
Copy link
Collaborator Author

Approve , I will change it.

@jokemanfire
Copy link
Collaborator Author

How about expose the transport in running service? @4t145

@4t145
Copy link
Collaborator

4t145 commented Aug 13, 2025

@jokemanfire Just have a look on the original issue

  1. We can return back the original transport with quit reason. It's not good to put the transport into the RunningService structure, since we already hand over the control of transport into the service worker task, and we can only take it back after task over.
  2. We don't have to close the transport in drop, it's a historical issue. The ChildProcess get the transport abilities from AsyncRWTransport, and this transport just return a Ok(()) in close method, where we should implment the closing logic of ChildProcess in. So I think what we need to do is implement Transport trait for it instead of inherit from AsyncRWTransport.
  3. By default, we shall follow the recommand approch of official document. But we can still remain some flexibility to user. I think the proper way for user to costomize the closing logic is to create a new type based on current ChildProcess transport and shadow the original closing logic, in other word, inherit and overload.

@jokemanfire jokemanfire force-pushed the stdio branch 2 times, most recently from c494dbf to 9f38473 Compare August 16, 2025 02:11
@jokemanfire
Copy link
Collaborator Author

@4t145 @crepererum Have a look , and pls check.

@jokemanfire jokemanfire requested a review from crepererum August 16, 2025 02:15
@4t145
Copy link
Collaborator

4t145 commented Aug 18, 2025

And what's your opinion about give back the ownership of transport after shutdown? Maybe we don't implement this feature in this pr, but it still a point to be discussed.

According to the protocol specifications

Signed-off-by: jokemanfire <[email protected]>
@jokemanfire
Copy link
Collaborator Author

jokemanfire commented Aug 18, 2025

And what's your opinion about give back the ownership of transport after shutdown? Maybe we don't implement this feature in this pr, but it still a point to be discussed.

If we want to return ownership of the transport, I don't think it should be in the close interface. This can lead to misunderstandings in our understanding of the close interface. I suggest that if necessary, we can set up new interface extensions, and not every transport needs to implement ownership. I have added the into_inner interface for future extensions.

@4t145
Copy link
Collaborator

4t145 commented Aug 18, 2025

This can lead to misunderstandings in our understanding of the close interface

I mean, return it as serve task's result

maybe change this

pub async fn waiting(self) -> Result<QuitReason, tokio::task::JoinError>

to

pub async fn waiting(self) -> Result<(QuitReason, T), tokio::task::JoinError>

But it will make more difficult to make RunningService a unified type, makes it harder for server management. Just a idea in case of anybody want transport ownership back after service shutdown, in the future.

@jokemanfire jokemanfire merged commit 4e6c57e into modelcontextprotocol:main Aug 18, 2025
11 checks passed
@crepererum
Copy link

Thank you 🙂

@jokemanfire jokemanfire deleted the stdio branch August 18, 2025 14:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-core Core library changes T-transport Transport layer changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants