Is your feature request related to a problem? Please describe.
When an MCP server process exits during McpClient.CreateAsync (e.g., the server fails validation and terminates with a non-zero exit code before completing the initialization handshake), the caller cannot access StdioClientCompletionDetails (ExitCode, ProcessId, StandardErrorTail) through any public API.
The McpClient.Completion API introduced in #1368 works perfectly for post-connection scenarios — when a server crashes after CreateAsync succeeds. However, for servers that exit during initialization (e.g., permission checks, feature gates, configuration errors), the McpClient instance is created and disposed internally by the static CreateAsync method and never returned to the caller. The only way to get the exit code is to parse the IOException message string or use reflection on the internal TransportClosedException type — neither of which is reliable or maintainable.
This is a problem for the Windows On-Device Registry (ODR), which returns specific exit codes during startup (42=Feature Disabled, 43=Agent Switch Disabled, 44=User Not Supported, 45=LAF Unavailable). Clients need to programmatically distinguish these to provide actionable error messages and recovery flows.
Describe the solution you'd like
When CreateAsync fails because the stdio server process exited, the caller should be able to access StdioClientCompletionDetails (ExitCode, ProcessId, StandardErrorTail) through a public API — the same typed completion details that McpClient.Completion provides for post-connection failures. For example, making TransportClosedException public so callers can catch (TransportClosedException ex) and access ex.Details, or exposing ClientCompletionDetails as a property on the IOException thrown by CreateAsync.
Describe alternatives you've considered
- Parsing
IOException.Message — The SDK's exception message contains "Exit code: N" text, but string parsing is fragile and not a reliable API contract.
- Reflection on internal
TransportClosedException — The transport's MessageReader.Completion faults with this internal exception which carries ClientCompletionDetails, but accessing its Details property requires reflection, is not AOT-safe, and couples to internal SDK implementation details.
- Manual
ConnectAsync + PreConnectedTransport wrapper — Calling StdioClientTransport.ConnectAsync() manually to retain the ITransport reference, then wrapping it for CreateAsync. This gives access to MessageReader.Completion on failure, but still requires reflection on the internal exception type to extract ClientCompletionDetails.
Additional context
This is the launch-failure counterpart to #1332 (resolved by #1368). The Completion API solved the post-connection case; this request is about closing the gap for the pre-connection/initialization failure case. SDK version: 1.1.0.
Is your feature request related to a problem? Please describe.
When an MCP server process exits during
McpClient.CreateAsync(e.g., the server fails validation and terminates with a non-zero exit code before completing the initialization handshake), the caller cannot accessStdioClientCompletionDetails(ExitCode, ProcessId, StandardErrorTail) through any public API.The
McpClient.CompletionAPI introduced in #1368 works perfectly for post-connection scenarios — when a server crashes afterCreateAsyncsucceeds. However, for servers that exit during initialization (e.g., permission checks, feature gates, configuration errors), theMcpClientinstance is created and disposed internally by the staticCreateAsyncmethod and never returned to the caller. The only way to get the exit code is to parse theIOExceptionmessage string or use reflection on the internalTransportClosedExceptiontype — neither of which is reliable or maintainable.This is a problem for the Windows On-Device Registry (ODR), which returns specific exit codes during startup (42=Feature Disabled, 43=Agent Switch Disabled, 44=User Not Supported, 45=LAF Unavailable). Clients need to programmatically distinguish these to provide actionable error messages and recovery flows.
Describe the solution you'd like
When
CreateAsyncfails because the stdio server process exited, the caller should be able to accessStdioClientCompletionDetails(ExitCode, ProcessId, StandardErrorTail) through a public API — the same typed completion details thatMcpClient.Completionprovides for post-connection failures. For example, makingTransportClosedExceptionpublic so callers cancatch (TransportClosedException ex)and accessex.Details, or exposingClientCompletionDetailsas a property on theIOExceptionthrown byCreateAsync.Describe alternatives you've considered
IOException.Message— The SDK's exception message contains "Exit code: N" text, but string parsing is fragile and not a reliable API contract.TransportClosedException— The transport'sMessageReader.Completionfaults with this internal exception which carriesClientCompletionDetails, but accessing itsDetailsproperty requires reflection, is not AOT-safe, and couples to internal SDK implementation details.ConnectAsync+PreConnectedTransportwrapper — CallingStdioClientTransport.ConnectAsync()manually to retain theITransportreference, then wrapping it forCreateAsync. This gives access toMessageReader.Completionon failure, but still requires reflection on the internal exception type to extractClientCompletionDetails.Additional context
This is the launch-failure counterpart to #1332 (resolved by #1368). The
CompletionAPI solved the post-connection case; this request is about closing the gap for the pre-connection/initialization failure case. SDK version: 1.1.0.