Closed
Description
It appears that the most basic version of starting/stopping an MCP server leaves a handle or reference open which prevents node from exiting.
I've observed that if I process.stdin.destroy()
then I can unblock MCP and get the whatever handles it is using to close.
Versions:
node: v23.2.0
@modelcontextprotocol/sdk: 1.0.4
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const server = new Server(
{
name: 'foo/bar',
version: '0.0.1',
},
{
capabilities: {
resources: {},
tools: {},
},
},
);
async function main() {
const transport = new StdioServerTransport();
server.connect(transport);
await server.close();
await transport.close();
console.warn('Will I hang forever here? Unfortunately, yes.');
// Destroying stdin prevents the hang, but it's ugly:
// process.stdin.destroy();
}
main();
The uncleaned handles appear to be added in server.connect(transport);
since it will exit cleanly if I comment that line.