diff --git a/src/ModelContextProtocol.Core/ProcessHelper.cs b/src/ModelContextProtocol.Core/ProcessHelper.cs
index c8bae0c48..a0e2498e6 100644
--- a/src/ModelContextProtocol.Core/ProcessHelper.cs
+++ b/src/ModelContextProtocol.Core/ProcessHelper.cs
@@ -8,36 +8,25 @@ namespace ModelContextProtocol;
///
internal static class ProcessHelper
{
- private static readonly bool _isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
- private static readonly TimeSpan _defaultTimeout = TimeSpan.FromSeconds(30);
-
- ///
- /// Kills a process and all of its child processes (entire process tree).
- ///
- /// The process to terminate along with its child processes.
- ///
- /// This method uses a default timeout of 30 seconds when waiting for processes to exit.
- /// On Windows, this uses the "taskkill" command with the /T flag.
- /// On non-Windows platforms, it recursively identifies and terminates child processes.
- ///
- public static void KillTree(this Process process) => process.KillTree(_defaultTimeout);
-
///
/// Kills a process and all of its child processes (entire process tree) with a specified timeout.
///
/// The process to terminate along with its child processes.
/// The maximum time to wait for the processes to exit.
///
- /// On Windows, this uses the "taskkill" command with the /T flag to terminate the process tree.
- /// On non-Windows platforms, it recursively identifies and terminates child processes.
+ /// On .NET Core 3.0+ this uses Process.Kill(entireProcessTree: true).
+ /// On .NET Standard 2.0, it uses platform-specific commands (taskkill on Windows, pgrep/kill on Unix).
/// The method waits for the specified timeout for processes to exit before continuing.
/// This is particularly useful for applications that spawn child processes (like Node.js)
/// that wouldn't be terminated automatically when the parent process exits.
///
public static void KillTree(this Process process, TimeSpan timeout)
{
+#if NETSTANDARD2_0
+ // Process.Kill(entireProcessTree) is not available on .NET Standard 2.0.
+ // Use platform-specific commands to kill the process tree.
var pid = process.Id;
- if (_isWindows)
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
RunProcessAndWaitForExit(
"taskkill",
@@ -53,8 +42,20 @@ public static void KillTree(this Process process, TimeSpan timeout)
{
KillProcessUnix(childId, timeout);
}
+
KillProcessUnix(pid, timeout);
}
+#else
+ try
+ {
+ process.Kill(entireProcessTree: true);
+ }
+ catch
+ {
+ // Process has already exited
+ return;
+ }
+#endif
// wait until the process finishes exiting/getting killed.
// We don't want to wait forever here because the task is already supposed to be dieing, we just want to give it long enough
@@ -62,26 +63,20 @@ public static void KillTree(this Process process, TimeSpan timeout)
process.WaitForExit((int)timeout.TotalMilliseconds);
}
+#if NETSTANDARD2_0
private static void GetAllChildIdsUnix(int parentId, ISet children, TimeSpan timeout)
{
- var exitcode = RunProcessAndWaitForExit(
- "pgrep",
- $"-P {parentId}",
- timeout,
- out var stdout);
+ int exitcode = RunProcessAndWaitForExit("pgrep", $"-P {parentId}", timeout, out var stdout);
if (exitcode == 0 && !string.IsNullOrEmpty(stdout))
{
using var reader = new StringReader(stdout);
- while (true)
+ while (reader.ReadLine() is string text)
{
- var text = reader.ReadLine();
- if (text == null)
- return;
-
if (int.TryParse(text, out var id))
{
children.Add(id);
+
// Recursively get the children
GetAllChildIdsUnix(id, children, timeout);
}
@@ -89,14 +84,8 @@ private static void GetAllChildIdsUnix(int parentId, ISet children, TimeSpa
}
}
- private static void KillProcessUnix(int processId, TimeSpan timeout)
- {
- RunProcessAndWaitForExit(
- "kill",
- $"-TERM {processId}",
- timeout,
- out var _);
- }
+ private static void KillProcessUnix(int processId, TimeSpan timeout) =>
+ RunProcessAndWaitForExit("kill", $"-TERM {processId}", timeout, out _);
private static int RunProcessAndWaitForExit(string fileName, string arguments, TimeSpan timeout, out string? stdout)
{
@@ -112,19 +101,21 @@ private static int RunProcessAndWaitForExit(string fileName, string arguments, T
stdout = null;
- var process = Process.Start(startInfo);
- if (process == null)
- return -1;
-
- if (process.WaitForExit((int)timeout.TotalMilliseconds))
- {
- stdout = process.StandardOutput.ReadToEnd();
- }
- else
+ if (Process.Start(startInfo) is { } process)
{
- process.Kill();
+ if (process.WaitForExit((int)timeout.TotalMilliseconds))
+ {
+ stdout = process.StandardOutput.ReadToEnd();
+ }
+ else
+ {
+ process.Kill();
+ }
+
+ return process.ExitCode;
}
- return process.ExitCode;
+ return -1;
}
+#endif
}