Skip to content

io.modelcontextprotocol.client.transport.StdioClientTransport#closeGracefully #496

Description

@meng1995vip

Bug description
When npx is launched on Windows, a related subprocess is initiated. When using the io.modelcontextprotocol.client.transport.StdioClientTransport#closeGracefully method to close, this.process.destroy() will only terminate the current process, but the subprocess cannot be closed, resulting in the main method not being able to end normally. Therefore, the taskkill pid /F /T command needs to be used to terminate it.

Environment
jdk 17
mcp 0.12.0-SNAPSHOT
windows 11
Steps to reproduce
StdioClientTransport stdioClientTransport = new StdioClientTransport(ServerParameters.builder("D:\dev\nodejs\npx.cmd").args(Arrays.asList("-y","@wopal/mcp-server-hotnews")).build());
McpSyncClient mcpClient = McpClient.sync(stdioClientTransport)
.requestTimeout(Duration.ofSeconds(20L))
.capabilities(McpSchema.ClientCapabilities.builder()
.roots(true)
.sampling()
.build())
.build();
mcpClient.initialize();
McpSchema.ListToolsResult listToolsResult = mcpClient.listTools();
List<McpSchema.Tool> tools = listToolsResult.tools();
System.out.println(tools.get(0).name());
mcpClient.closeGracefully();

Expected behavior
Close the child processes simultaneously

Minimal Complete Reproducible example
StdioClientTransport stdioClientTransport = new StdioClientTransport(ServerParameters.builder("D:\dev\nodejs\npx.cmd").args(Arrays.asList("-y","@wopal/mcp-server-hotnews")).build());
McpSyncClient mcpClient = McpClient.sync(stdioClientTransport)
.requestTimeout(Duration.ofSeconds(20L))
.capabilities(McpSchema.ClientCapabilities.builder()
.roots(true)
.sampling()
.build())
.build();
mcpClient.initialize();
McpSchema.ListToolsResult listToolsResult = mcpClient.listTools();
List<McpSchema.Tool> tools = listToolsResult.tools();
System.out.println(tools.get(0).name());
mcpClient.closeGracefully();

Image

Activity

  1. chemicL commented on Feb 20, 2026

    @chemicL
    Member

    Thanks.

    Perhaps something like the below snippet can be used to address this from the Java side:

    Process process = Runtime.getRuntime().exec("npx ... ");
    
    // When it's time to kill it:
    process.descendants().forEach(ProcessHandle::destroyForcibly);
    process.destroyForcibly();

    Potentially, this can be combined with:

    process.onExit().thenAccept(p -> {
        // Perform the additional cleanup here
    });
    
  2. added
    P2Moderate issues affecting some users, edge cases, potentially valuable feature
    ready for workThe goal is clear and work towards it can be commenced
    on Feb 20, 2026
  3. k-krawczyk commented on May 17, 2026

    @k-krawczyk

    Hi! I'd like to take this.

    Plan: use ProcessHandle.descendants() (JDK 9+, available since the repo's Java 17 baseline) instead of the OS-specific taskkill from the issue body — same effect, one cross-platform code path (Windows, macOS, Linux), no shelling out.

    Implementation:

    • closeGracefully() walks process.descendants(), destroys each, then destroys the root.
    • A small package-private destroyProcessTree(Process) helper, visible for tests.

    Tests added (StdioClientTransportDestroyTreeTests):

    • root with multiple long-running descendants (sh -c 'sleep 60 & sleep 60 & wait', POSIX-only via @DisabledOnOs(WINDOWS) since CI runs on Ubuntu)
    • root with no descendants
    • safe on an already-terminated process

    ./mvnw verify is green locally on macOS across all 11 modules, including StdioMcpSync/AsyncClientTests which exercise the close path through real npx.

    Caveat: I'm on macOS, so the original Windows symptom is verified only by reasoning about the JDK API, not exercised end-to-end. Happy to switch to explicit taskkill if maintainers prefer that route.

    PR coming shortly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Moderate issues affecting some users, edge cases, potentially valuable featurearea/clientenhancementNew feature or requestready for workThe goal is clear and work towards it can be commenced

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions