Configure hosted stdio servers to log to stderr (#173)

* Configure hosted stdio servers to log to stderr

* Fix newline

* Use CreateApplicationBuilder where possible

* Address PR feedback
This commit is contained in:
Stephen Halter 2025-03-31 19:23:49 -07:00 • committed by GitHub
commit 8dc1f5d096
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 27 additions and 6 deletions

View file

@ -83,13 +83,24 @@ It includes a simple echo tool as an example (this is included in the same file
the employed overload of `WithTools` examines the current assembly for classes with the `McpServerToolType` attribute, and registers all methods with the
`McpTool` attribute as tools.)
```
dotnet add package ModelContextProtocol --prerelease
dotnet add package Microsoft.Extensions.Hosting
```
```csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Server;
using System.ComponentModel;
var builder = Host.CreateEmptyApplicationBuilder(settings: null);
var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole(consoleLogOptions =>
{
// Configure all logs to go to stderr
consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
});
builder.Services
.AddMcpServer()
.WithStdioServerTransport()

View file

@ -5,7 +5,7 @@ using Microsoft.Extensions.Hosting;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Transport;
var builder = Host.CreateEmptyApplicationBuilder(settings: null);
var builder = Host.CreateApplicationBuilder(args);
builder.Configuration
.AddEnvironmentVariables()

View file

@ -1,13 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Net.Http.Headers;
var builder = Host.CreateEmptyApplicationBuilder(settings: null);
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
builder.Logging.AddConsole(options =>
{
options.LogToStandardErrorThreshold = LogLevel.Trace;
});
builder.Services.AddSingleton(_ =>
{
var client = new HttpClient() { BaseAddress = new Uri("https://api.weather.gov") };

View file

@ -98,8 +98,8 @@ internal static partial class Log
[LoggerMessage(Level = LogLevel.Information, Message = "Creating process for transport for {endpointName} with command {command}, arguments {arguments}, environment {environment}, working directory {workingDirectory}, shutdown timeout {shutdownTimeout}")]
internal static partial void CreateProcessForTransport(this ILogger logger, string endpointName, string command, string? arguments, string environment, string workingDirectory, string shutdownTimeout);
[LoggerMessage(Level = LogLevel.Error, Message = "Transport for {endpointName} error: {data}")]
internal static partial void TransportError(this ILogger logger, string endpointName, string data);
[LoggerMessage(Level = LogLevel.Information, Message = "Transport for {endpointName} received stderr log: {data}")]
internal static partial void ReadStderr(this ILogger logger, string endpointName, string data);
[LoggerMessage(Level = LogLevel.Information, Message = "Transport process start failed for {endpointName}")]
internal static partial void TransportProcessStartFailed(this ILogger logger, string endpointName);

View file

@ -85,7 +85,7 @@ public sealed class StdioClientTransport : IClientTransport
process = new() { StartInfo = startInfo };
// Set up error logging
process.ErrorDataReceived += (sender, args) => logger.TransportError(endpointName, args.Data ?? "(no data)");
process.ErrorDataReceived += (sender, args) => logger.ReadStderr(endpointName, args.Data ?? "(no data)");
// We need both stdin and stdout to use a no-BOM UTF-8 encoding. On .NET Core,
// we can use ProcessStartInfo.StandardOutputEncoding/StandardInputEncoding, but

View file

@ -24,6 +24,10 @@ internal static class Program
return LoggerFactory.Create(builder =>
{
builder.AddConsole(options =>
{
options.LogToStandardErrorThreshold = LogLevel.Trace;
});
builder.AddSerilog();
});
}