1- using  System . Diagnostics ; 
2- using  System . Text . Json ; 
1+ using  Microsoft . Extensions . Logging ; 
2+ using  Microsoft . Extensions . Logging . Abstractions ; 
33using  ModelContextProtocol . Configuration ; 
44using  ModelContextProtocol . Logging ; 
55using  ModelContextProtocol . Protocol . Messages ; 
66using  ModelContextProtocol . Utils ; 
77using  ModelContextProtocol . Utils . Json ; 
8- using  Microsoft . Extensions . Logging ; 
9- using  Microsoft . Extensions . Logging . Abstractions ; 
8+ using  System . Diagnostics ; 
9+ using  System . Text ; 
10+ using  System . Text . Json ; 
1011
1112namespace  ModelContextProtocol . Protocol . Transport ; 
1213
@@ -59,6 +60,8 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default)
5960
6061            _shutdownCts  =  new  CancellationTokenSource ( ) ; 
6162
63+             UTF8Encoding  noBomUTF8  =  new ( encoderShouldEmitUTF8Identifier :  false ) ; 
64+ 
6265            var  startInfo  =  new  ProcessStartInfo 
6366            { 
6467                FileName  =  _options . Command , 
@@ -68,6 +71,11 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default)
6871                UseShellExecute  =  false , 
6972                CreateNoWindow  =  true , 
7073                WorkingDirectory  =  _options . WorkingDirectory  ??  Environment . CurrentDirectory , 
74+                 StandardOutputEncoding  =  noBomUTF8 , 
75+                 StandardErrorEncoding  =  noBomUTF8 , 
76+ #if NET 
77+                 StandardInputEncoding  =  noBomUTF8 , 
78+ #endif
7179            } ; 
7280
7381            if  ( ! string . IsNullOrWhiteSpace ( _options . Arguments ) ) 
@@ -92,13 +100,35 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default)
92100            // Set up error logging 
93101            _process . ErrorDataReceived  +=  ( sender ,  args )  =>  _logger . TransportError ( EndpointName ,  args . Data  ??  "(no data)" ) ; 
94102
95-             if  ( ! _process . Start ( ) ) 
103+             // We need both stdin and stdout to use a no-BOM UTF-8 encoding. On .NET Core, 
104+             // we can use ProcessStartInfo.StandardOutputEncoding/StandardInputEncoding, but 
105+             // StandardInputEncoding doesn't exist on .NET Framework; instead, it always picks 
106+             // up the encoding from Console.InputEncoding. As such, when not targeting .NET Core, 
107+             // we temporarily change Console.InputEncoding to no-BOM UTF-8 around the Process.Start 
108+             // call, to ensure it picks up the correct encoding. 
109+ #if NET 
110+             _processStarted  =  _process . Start ( ) ; 
111+ #else
112+             Encoding  originalInputEncoding  =  Console . InputEncoding ; 
113+             try 
114+             { 
115+                 Console . InputEncoding  =  noBomUTF8 ; 
116+                 _processStarted  =  _process . Start ( ) ; 
117+             } 
118+             finally 
119+             { 
120+                 Console . InputEncoding  =  originalInputEncoding ; 
121+             } 
122+ #endif
123+ 
124+             if  ( ! _processStarted ) 
96125            { 
97126                _logger . TransportProcessStartFailed ( EndpointName ) ; 
98127                throw  new  McpTransportException ( "Failed to start MCP server process" ) ; 
99128            } 
129+ 
100130            _logger . TransportProcessStarted ( EndpointName ,  _process . Id ) ; 
101-             _processStarted   =   true ; 
131+             
102132            _process . BeginErrorReadLine ( ) ; 
103133
104134            // Start reading messages in the background 
@@ -134,9 +164,10 @@ public override async Task SendMessageAsync(IJsonRpcMessage message, Cancellatio
134164        { 
135165            var  json  =  JsonSerializer . Serialize ( message ,  _jsonOptions . GetTypeInfo < IJsonRpcMessage > ( ) ) ; 
136166            _logger . TransportSendingMessage ( EndpointName ,  id ,  json ) ; 
167+             _logger . TransportMessageBytesUtf8 ( EndpointName ,  json ) ; 
137168
138-             // Write the message followed by a newline 
139-             await  _process ! . StandardInput . WriteLineAsync ( json . AsMemory ( ) ,   cancellationToken ) . ConfigureAwait ( false ) ; 
169+             // Write the message followed by a newline using our UTF-8 writer  
170+             await  _process ! . StandardInput . WriteLineAsync ( json ) . ConfigureAwait ( false ) ; 
140171            await  _process . StandardInput . FlushAsync ( cancellationToken ) . ConfigureAwait ( false ) ; 
141172
142173            _logger . TransportSentMessage ( EndpointName ,  id ) ; 
@@ -161,12 +192,10 @@ private async Task ReadMessagesAsync(CancellationToken cancellationToken)
161192        { 
162193            _logger . TransportEnteringReadMessagesLoop ( EndpointName ) ; 
163194
164-             using  var  reader  =  _process ! . StandardOutput ; 
165- 
166-             while  ( ! cancellationToken . IsCancellationRequested  &&  ! _process . HasExited ) 
195+             while  ( ! cancellationToken . IsCancellationRequested  &&  ! _process ! . HasExited ) 
167196            { 
168197                _logger . TransportWaitingForMessage ( EndpointName ) ; 
169-                 var  line  =  await  reader . ReadLineAsync ( cancellationToken ) . ConfigureAwait ( false ) ; 
198+                 var  line  =  await  _process . StandardOutput . ReadLineAsync ( cancellationToken ) . ConfigureAwait ( false ) ; 
170199                if  ( line  ==  null ) 
171200                { 
172201                    _logger . TransportEndOfStream ( EndpointName ) ; 
@@ -179,6 +208,7 @@ private async Task ReadMessagesAsync(CancellationToken cancellationToken)
179208                } 
180209
181210                _logger . TransportReceivedMessage ( EndpointName ,  line ) ; 
211+                 _logger . TransportMessageBytesUtf8 ( EndpointName ,  line ) ; 
182212
183213                await  ProcessMessageAsync ( line ,  cancellationToken ) . ConfigureAwait ( false ) ; 
184214            } 
@@ -230,28 +260,27 @@ private async Task ProcessMessageAsync(string line, CancellationToken cancellati
230260    private  async  Task  CleanupAsync ( CancellationToken  cancellationToken ) 
231261    { 
232262        _logger . TransportCleaningUp ( EndpointName ) ; 
233-         if  ( _process  !=  null  &&  _processStarted  &&  ! _process . HasExited ) 
263+ 
264+         if  ( _process  is  Process  process  &&  _processStarted  &&  ! process . HasExited ) 
234265        { 
235266            try 
236267            { 
237-                 // Try to close stdin to signal the process to exit 
238-                 _logger . TransportClosingStdin ( EndpointName ) ; 
239-                 _process . StandardInput . Close ( ) ; 
240- 
241268                // Wait for the process to exit 
242269                _logger . TransportWaitingForShutdown ( EndpointName ) ; 
243270
244271                // Kill the while process tree because the process may spawn child processes 
245272                // and Node.js does not kill its children when it exits properly 
246-                 _process . KillTree ( _options . ShutdownTimeout ) ; 
273+                 process . KillTree ( _options . ShutdownTimeout ) ; 
247274            } 
248275            catch  ( Exception  ex ) 
249276            { 
250277                _logger . TransportShutdownFailed ( EndpointName ,  ex ) ; 
251278            } 
252- 
253-             _process . Dispose ( ) ; 
254-             _process  =  null ; 
279+             finally 
280+             { 
281+                 process . Dispose ( ) ; 
282+                 _process  =  null ; 
283+             } 
255284        } 
256285
257286        if  ( _shutdownCts  is  {  }  shutdownCts ) 
@@ -261,29 +290,30 @@ private async Task CleanupAsync(CancellationToken cancellationToken)
261290            _shutdownCts  =  null ; 
262291        } 
263292
264-         if  ( _readTask  !=   null ) 
293+         if  ( _readTask  is   Task   readTask ) 
265294        { 
266295            try 
267296            { 
268297                _logger . TransportWaitingForReadTask ( EndpointName ) ; 
269-                 await  _readTask . WaitAsync ( TimeSpan . FromSeconds ( 5 ) ,  cancellationToken ) . ConfigureAwait ( false ) ; 
298+                 await  readTask . WaitAsync ( TimeSpan . FromSeconds ( 5 ) ,  cancellationToken ) . ConfigureAwait ( false ) ; 
270299            } 
271300            catch  ( TimeoutException ) 
272301            { 
273302                _logger . TransportCleanupReadTaskTimeout ( EndpointName ) ; 
274-                 // Continue with cleanup 
275303            } 
276304            catch  ( OperationCanceledException ) 
277305            { 
278306                _logger . TransportCleanupReadTaskCancelled ( EndpointName ) ; 
279-                 // Ignore cancellation 
280307            } 
281308            catch  ( Exception  ex ) 
282309            { 
283310                _logger . TransportCleanupReadTaskFailed ( EndpointName ,  ex ) ; 
284311            } 
285-             _readTask  =  null ; 
286-             _logger . TransportReadTaskCleanedUp ( EndpointName ) ; 
312+             finally 
313+             { 
314+                 _logger . TransportReadTaskCleanedUp ( EndpointName ) ; 
315+                 _readTask  =  null ; 
316+             } 
287317        } 
288318
289319        SetConnected ( false ) ; 
0 commit comments