Skip to content

Configure hosted stdio servers to log to stderr #173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion samples/QuickstartClient/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Transport;

var builder = Host.CreateEmptyApplicationBuilder(settings: null);
var builder = Host.CreateApplicationBuilder(args);

builder.Configuration
.AddEnvironmentVariables()
Expand Down
8 changes: 7 additions & 1 deletion samples/QuickstartWeatherServer/Program.cs
Original file line number Diff line number Diff line change
@@ -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") };
Expand Down
4 changes: 2 additions & 2 deletions src/ModelContextProtocol/Logging/Log.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public async Task<ITransport> ConnectAsync(CancellationToken cancellationToken =
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
Expand Down
4 changes: 4 additions & 0 deletions tests/ModelContextProtocol.TestServer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ private static ILoggerFactory CreateLoggerFactory()

return LoggerFactory.Create(builder =>
{
builder.AddConsole(options =>
{
options.LogToStandardErrorThreshold = LogLevel.Trace;
});
builder.AddSerilog();
});
}
Expand Down