0

I'm trying to implement a simple MCP Server like this:

McpServer.sync(new StdioServerTransportProvider())
    .serverInfo("test", "1.0.0")
    .requestTimeout(Duration.ofSeconds(120))
    .capabilities(getServerCapabilities())
    .tools(createTestToolSpecs())
    .build();

When invoked from a main method, this works just fine and I can for example use an MCP client to list & invoke tools. As build() is a non-blocking call, which causes the main method to return immediately, I guess this code snippet starts one or more non-daemon threads that cause the JVM to keep running until those threads terminate. When an MCP client requests server termination, my MCP server process successfully terminates.

However, I need to integrate this into a larger Picocli-based CLI application, where the MCP server needs to be started from a Callable::call method, with the return value of that method being passed to System::exit. This causes the MCP server to be terminated immediately, which I'd obviously like to avoid.

I tried adding a simple while (true) { sleep(); } loop after building the MCP server instance, and this successfully allows MCP clients to interact with the MCP server. However, now the MCP server is no longer being properly terminated when the MCP client exits or otherwise requests server termination.

This causes many MCP server processes to remain running when they are no longer needed. I tried adding a shutdown handler to explicitly call McpServer::closeGracefully, but that doesn't help; MCP server processes are not being terminated.

I couldn't find any methods that allow for explicitly blocking until server termination, so how do I properly handle MCP Server lifecycle in this scenario?

1 Answer 1

0

Initially I was looking for a MCP Java SDK API calls for accomplishing this, then I figured that I could also just iterate over all non-daemon threads and call Thread::join on each of those. Running the following after building the MCP server instance seems to work fine:

        // Build MCP server
        McpServer.sync(...)
            ...
            .build();

        // Join all non-daemon threads to ensure proper shutdown of MCP server
        Thread.getAllStackTraces().keySet().stream()
            .filter(t->!t.isDaemon() && t!=Thread.currentThread())
            .forEach(t-> {
                try {
                    t.join();
                } catch (InterruptedException e) {
                    LOG.warn("Interrupted while joining thread "+t.getName(), e);
                }
            });

Of course, I'm still interested in any better answers.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.