From 218b5a378cfdd3bd3460d8111ed06ba17605082c Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sun, 23 Mar 2025 16:53:47 -0400 Subject: [PATCH] Fix stdio encoding to always be UTF8 --- .../Transport/StdioClientTransport.cs | 35 +++++++++++++++++-- .../Transport/StdioServerTransport.cs | 13 ++++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/ModelContextProtocol/Protocol/Transport/StdioClientTransport.cs b/src/ModelContextProtocol/Protocol/Transport/StdioClientTransport.cs index 5479a3067..b87c2f015 100644 --- a/src/ModelContextProtocol/Protocol/Transport/StdioClientTransport.cs +++ b/src/ModelContextProtocol/Protocol/Transport/StdioClientTransport.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.Text; using System.Text.Json; using ModelContextProtocol.Configuration; using ModelContextProtocol.Logging; @@ -59,7 +60,7 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default) _shutdownCts = new CancellationTokenSource(); - var startInfo = new ProcessStartInfo + ProcessStartInfo startInfo = new() { FileName = _options.Command, RedirectStandardInput = true, @@ -68,6 +69,8 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default) UseShellExecute = false, CreateNoWindow = true, WorkingDirectory = _options.WorkingDirectory ?? Environment.CurrentDirectory, + StandardOutputEncoding = Encoding.UTF8, + StandardErrorEncoding = Encoding.UTF8, }; if (!string.IsNullOrWhiteSpace(_options.Arguments)) @@ -92,11 +95,39 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default) // Set up error logging _process.ErrorDataReceived += (sender, args) => _logger.TransportError(EndpointName, args.Data ?? "(no data)"); - if (!_process.Start()) + bool processStarted; +#if NET + startInfo.StandardInputEncoding = Encoding.UTF8; + processStarted = _process.Start(); +#else + // netstandard2.0 lacks ProcessStartInfo.StandardInputEncoding. Instead, it always + // uses Console.InputEncoding, so we need to temporarily change Console.InputEncoding + // if it's not already UTF-8. + Encoding oldStdinEncoding = Console.InputEncoding; + if (oldStdinEncoding.CodePage == Encoding.UTF8.CodePage) + { + processStarted = _process.Start(); + } + else + { + try + { + Console.InputEncoding = Encoding.UTF8; + processStarted = _process.Start(); + } + finally + { + Console.InputEncoding = oldStdinEncoding; + } + } +#endif + + if (!processStarted) { _logger.TransportProcessStartFailed(EndpointName); throw new McpTransportException("Failed to start MCP server process"); } + _logger.TransportProcessStarted(EndpointName, _process.Id); _processStarted = true; _process.BeginErrorReadLine(); diff --git a/src/ModelContextProtocol/Protocol/Transport/StdioServerTransport.cs b/src/ModelContextProtocol/Protocol/Transport/StdioServerTransport.cs index d63fe38ec..8b3f4f659 100644 --- a/src/ModelContextProtocol/Protocol/Transport/StdioServerTransport.cs +++ b/src/ModelContextProtocol/Protocol/Transport/StdioServerTransport.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using System.Text; namespace ModelContextProtocol.Protocol.Transport; @@ -80,7 +81,17 @@ public StdioServerTransport(string serverName, ILoggerFactory? loggerFactory = n : base(loggerFactory) { Throw.IfNull(serverName); - + + if (Console.InputEncoding.CodePage != Encoding.UTF8.CodePage) + { + Console.InputEncoding = Encoding.UTF8; + } + + if (Console.OutputEncoding.CodePage != Encoding.UTF8.CodePage) + { + Console.OutputEncoding = Encoding.UTF8; + } + _serverName = serverName; _logger = (ILogger?)loggerFactory?.CreateLogger() ?? NullLogger.Instance; }