1
>
/*---------------------------------------------------------------------------------------------
mcpStdioStateHandler.ts
2
>
* Copyright (c) Microsoft Corporation. All rights reserved.
3
>
* Licensed under the MIT License. See License.txt in the project root for license information.
4
>
*--------------------------------------------------------------------------------------------*/
5
>
6
>
import { ChildProcessWithoutNullStreams } from 'child_process';
7
>
import { TimeoutTimer } from '../../../../base/common/async.js';
8
>
import { IDisposable } from '../../../../base/common/lifecycle.js';
9
>
import { killTree } from '../../../../base/node/processes.js';
10
>
import { isWindows } from '../../../../base/common/platform.js';
11
>
12
>
const enum McpProcessState {
13
>
Running,
14
>
StdinEnded,
15
>
KilledPolite,
16
>
KilledForceful,
17
>
}
18
>
19
>
/**
20
>
* Manages graceful shutdown of MCP stdio connections following the MCP specification.
21
>
*
22
>
* Per spec, shutdown should:
23
>
* 1. Close the input stream to the child process
24
>
* 2. Wait for the server to exit, or send SIGTERM if it doesn't exit within 10 seconds
25
>
* 3. Send SIGKILL if the server doesn't exit within 10 seconds after SIGTERM
26
>
* 4. Allow forceful killing if called twice
27
>
*/
28
>
export class McpStdioStateHandler implements IDisposable {
29
>
private static readonly GRACE_TIME_MS = 10_000;
30
>
31
>
private _procState = McpProcessState.Running;
32
>
private _nextTimeout?: IDisposable;
33
>
34
>
public get stopped() {
35
return this._procState !== McpProcessState.Running;
36
}
38
>
constructor(
39
private readonly _child: ChildProcessWithoutNullStreams,
40
private readonly _graceTimeMs: number = McpStdioStateHandler.GRACE_TIME_MS
41
) { }
43
>
/**
44
>
* Initiates graceful shutdown. If called while shutdown is already in progress,
45
>
* forces immediate termination.
46
>
*/
47
>
public stop(): void {
48
if (this._procState === McpProcessState.Running) {
49
let graceTime = this._graceTimeMs;