Private/New-McpSession.ps1
|
function Register-McpExitHandler { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingEmptyCatchBlock', '', Justification = 'Best-effort kill at exit; nothing useful can be done on failure')] [CmdletBinding()] param() if ($script:ExitHandlerRegistered) { return } $sessions = $script:Sessions $null = Register-EngineEvent -SourceIdentifier PowerShell.Exiting -SupportEvent -Action { foreach ($session in @($sessions.Values)) { try { if (-not $session.Process.HasExited) { $session.Process.Kill($true) } } catch { } } }.GetNewClosure() $script:ExitHandlerRegistered = $true } function Stop-McpProcess { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingEmptyCatchBlock', '', Justification = 'Each step is a best-effort shutdown escalation; a failure just falls through to the next')] [CmdletBinding()] param( [Parameter(Mandatory)]$Session ) $process = $Session.Process try { $Session.StdIn.Close() } catch { } try { if (-not $process.HasExited) { $null = $process.WaitForExit(2000) } } catch { } try { if (-not $process.HasExited) { $process.Kill($true) $null = $process.WaitForExit(5000) } } catch { } $tail = Get-McpStderrTail -Session $Session -Lines 50 if ($tail) { Write-Verbose "[$($Session.Name)] stderr:`n$tail" } try { $process.Dispose() } catch { } } function Remove-McpSession { [CmdletBinding()] param( [Parameter(Mandatory)]$Session ) Remove-McpProxyFunction -Session $Session Stop-McpProcess -Session $Session $null = $script:Sessions.Remove($Session.Name) } function New-McpSession { [CmdletBinding()] param( [Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][string]$Command, [string[]]$Arguments = @(), [System.Collections.IDictionary]$Environment, [string]$WorkingDirectory, [int]$InitializeTimeoutSec, [int]$RequestTimeoutSec, [int]$DiscoverTimeoutSec ) if ($script:Sessions.ContainsKey($Name)) { throw (New-McpError -ErrorId 'McpSessionExists' -Category ResourceExists -TargetObject $Name ` -Message "An MCP session named '$Name' already exists. Disconnect it first with: Disconnect-McpServer -Server '$Name'") } if ($InitializeTimeoutSec -le 0) { $InitializeTimeoutSec = $script:DefaultInitializeTimeoutSec } if ($RequestTimeoutSec -le 0) { $RequestTimeoutSec = $script:DefaultRequestTimeoutSec } if ($DiscoverTimeoutSec -le 0) { $DiscoverTimeoutSec = $InitializeTimeoutSec } $startParams = @{ Command = $Command; Arguments = $Arguments } if ($Environment) { $startParams.Environment = $Environment } if ($WorkingDirectory) { $startParams.WorkingDirectory = $WorkingDirectory } $proc = Start-McpProcess @startParams $session = [pscustomobject]@{ PSTypeName = 'PSMcpClient.Session' Name = $Name Command = $Command Arguments = $Arguments Process = $proc.Process StdIn = $proc.StdIn StdOut = $proc.StdOut StderrTask = $proc.StderrTask PendingRead = $null NextId = 0 IgnoredIds = [System.Collections.Generic.List[string]]::new() Era = $null NegotiatedVersion = $null ServerInfo = $null Capabilities = $null Instructions = $null Tools = $null ToolsStale = $false ProxyRegistered = $false ProxyFunctions = @() ProxyPrefix = $null ProxyNames = $null InitializeTimeoutSec = $InitializeTimeoutSec RequestTimeoutSec = $RequestTimeoutSec DiscoverTimeoutSec = $DiscoverTimeoutSec } try { Initialize-McpSession -Session $session } catch { Stop-McpProcess -Session $session throw } $script:Sessions[$Name] = $session Register-McpExitHandler $session } |