Skip to content

Rewrite Start-IshRemoteMcpServer loops forever on stdin EOF instead of exiting when a client closes the input stream #243

Description

@ddemeyer

Problem

Start-IshRemoteMcpServer never exits when the MCP client closes the stdio input stream
(EOF). The server spins in the while ($ActivateWhileLoop) loop forever, leaving orphaned
hidden pwsh processes behind.

Condition

Any client that shuts down by closing the server's stdin triggers it. The MCP stdio
transport spec (2026-07-28 and earlier bindings) states a server SHOULD exit promptly
when its reads return end-of-file.

Root cause

Start-IshRemoteMcpServer.ps1:58-59 treats EOF as an empty line:

$inputLine = [Console]::In.ReadLine()
if ([string]::IsNullOrEmpty($inputLine)) { continue }

ReadLine() returns $null on EOF. [string]::IsNullOrEmpty($null) is $true, so EOF
falls into the same continue branch as an empty line and the loop never breaks.

Solution

Break out of the loop on EOF, logging the shutdown first:

while ($ActivateWhileLoop) {
    $inputLine = [Console]::In.ReadLine()
    if ($null -eq $inputLine) {
        Write-IshRemoteLog -LogEntry @{ Level = 'Info'; Message = "stdin closed, exiting MCP Server" }
        break
    }
    if ([string]::IsNullOrEmpty($inputLine)) { continue }
    ...
}

$null -eq $inputLine is used (not -eq $null) so an empty line still goes through the
existing skip branch, while only true EOF ends the server.

Files

  • Source/ISHRemote/Trisoft.ISHRemote/Scripts/Public/Start-IshRemoteMcpServer.ps1

Test plan

  • Extend Start-IshRemoteMcpServer.Tests.ps1 with a case that redirects [Console]::In
    to an empty [System.IO.StringReader] (its first ReadLine() returns $null), saves
    the original reader first, calls
    Start-IshRemoteMcpServer -CmdletsToRegister @('Get-IshFolder') -ActivateWhileLoop $true,
    and asserts it returns (instead of hanging) and restores the original console input in a
    finally block.
  • Manual check with a real MCP client: stop the client, confirm the hidden pwsh process
    exits.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions