Skip to content
Open
48 changes: 36 additions & 12 deletions src/ModelContextProtocol.Core/McpSessionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,8 @@
// Now that the request has been sent, register for cancellation. If we registered before,
// a cancellation request could arrive before the server knew about that request ID, in which
// case the server could ignore it.
LogRequestSentAwaitingResponse(EndpointName, request.Method, request.Id);
string? target = GetRequestTarget(request);
LogRequestSentAwaitingResponse(EndpointName, request.Method, request.Id, toolName: target);
JsonRpcMessage? response;
using (var registration = RegisterCancellation(cancellationToken, request))
{
Expand All @@ -451,7 +452,7 @@

if (response is JsonRpcError error)
{
LogSendingRequestFailed(EndpointName, request.Method, error.Error.Message, error.Error.Code);
LogSendingRequestFailed(EndpointName, request.Method, error.Error.Message, error.Error.Code, toolName: target);

Check failure on line 455 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Debug)

The best overload for 'LogSendingRequestFailed' does not have a parameter named 'toolName'

Check failure on line 455 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Debug)

The best overload for 'LogSendingRequestFailed' does not have a parameter named 'toolName'

Check failure on line 455 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

The best overload for 'LogSendingRequestFailed' does not have a parameter named 'toolName'

Check failure on line 455 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

The best overload for 'LogSendingRequestFailed' does not have a parameter named 'toolName'

Check failure on line 455 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Release)

The best overload for 'LogSendingRequestFailed' does not have a parameter named 'toolName'

Check failure on line 455 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Release)

The best overload for 'LogSendingRequestFailed' does not have a parameter named 'toolName'

Check failure on line 455 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

The best overload for 'LogSendingRequestFailed' does not have a parameter named 'toolName'

Check failure on line 455 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

The best overload for 'LogSendingRequestFailed' does not have a parameter named 'toolName'

Check failure on line 455 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

The best overload for 'LogSendingRequestFailed' does not have a parameter named 'toolName'

Check failure on line 455 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

The best overload for 'LogSendingRequestFailed' does not have a parameter named 'toolName'

Check failure on line 455 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

The best overload for 'LogSendingRequestFailed' does not have a parameter named 'toolName'

Check failure on line 455 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

The best overload for 'LogSendingRequestFailed' does not have a parameter named 'toolName'
throw new McpProtocolException($"Request failed (remote): {error.Error.Message}", (McpErrorCode)error.Error.Code);
}

Expand All @@ -464,11 +465,11 @@

if (_logger.IsEnabled(LogLevel.Trace))
{
LogRequestResponseReceivedSensitive(EndpointName, request.Method, success.Result?.ToJsonString() ?? "null");
LogRequestResponseReceivedSensitive(EndpointName, request.Method, success.Result?.ToJsonString() ?? "null", toolName: target);
}
else
{
LogRequestResponseReceived(EndpointName, request.Method);
LogRequestResponseReceived(EndpointName, request.Method, toolName: target);
}

return success;
Expand Down Expand Up @@ -769,6 +770,29 @@
return null;
}

/// <summary>
/// Extracts the target identifier (tool name, prompt name, or resource URI) from a request.
/// </summary>
/// <param name="request">The JSON-RPC request.</param>
/// <returns>The target identifier if available; otherwise, null.</returns>
private static string? GetRequestTarget(JsonRpcRequest request)
{
if (request.Params is not JsonObject paramsObj)
{
return null;
}

return request.Method switch
{
RequestMethods.ToolsCall => GetStringProperty(paramsObj, "name"),
RequestMethods.PromptsGet => GetStringProperty(paramsObj, "name"),
RequestMethods.ResourcesRead => GetStringProperty(paramsObj, "uri"),
RequestMethods.ResourcesSubscribe => GetStringProperty(paramsObj, "uri"),
RequestMethods.ResourcesUnsubscribe => GetStringProperty(paramsObj, "uri"),
_ => null
};
}

[LoggerMessage(Level = LogLevel.Information, Message = "{EndpointName} message processing canceled.")]
private partial void LogEndpointMessageProcessingCanceled(string endpointName);

Expand All @@ -784,8 +808,8 @@
[LoggerMessage(Level = LogLevel.Information, Message = "{EndpointName} received request for unknown request ID '{RequestId}'.")]
private partial void LogNoRequestFoundForMessageWithId(string endpointName, RequestId requestId);

[LoggerMessage(Level = LogLevel.Warning, Message = "{EndpointName} request failed for method '{Method}': {ErrorMessage} ({ErrorCode}).")]
private partial void LogSendingRequestFailed(string endpointName, string method, string errorMessage, int errorCode);
[LoggerMessage(Level = LogLevel.Warning, Message = "{EndpointName} request failed for method '{Method}' (target: '{ToolName}'): {ErrorMessage} ({ErrorCode}).")]

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Debug)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Debug)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Debug)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Release)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Release)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Release)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)

Check failure on line 811 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

Template 'ToolName' is not provided as argument to the logging method (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1014)
private partial void LogSendingRequestFailed(string endpointName, string method, string errorMessage, int errorCode, string? targetName = null);

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Debug)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Debug)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Debug)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Release)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Release)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Release)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

Check failure on line 812 in src/ModelContextProtocol.Core/McpSessionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

Argument 'targetName' is not referenced from the logging message (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1015)

[LoggerMessage(Level = LogLevel.Warning, Message = "{EndpointName} received invalid response for method '{Method}'.")]
private partial void LogSendingRequestInvalidResponseType(string endpointName, string method);
Expand All @@ -799,11 +823,11 @@
[LoggerMessage(Level = LogLevel.Information, Message = "{EndpointName} canceled request '{RequestId}' per client notification. Reason: '{Reason}'.")]
private partial void LogRequestCanceled(string endpointName, RequestId requestId, string? reason);

[LoggerMessage(Level = LogLevel.Debug, Message = "{EndpointName} Request response received for method {method}")]
private partial void LogRequestResponseReceived(string endpointName, string method);
[LoggerMessage(Level = LogLevel.Debug, Message = "{EndpointName} Request response received for method {method} (tool: '{ToolName}')")]
private partial void LogRequestResponseReceived(string endpointName, string method, string? toolName = null);

[LoggerMessage(Level = LogLevel.Trace, Message = "{EndpointName} Request response received for method {method}. Response: '{Response}'.")]
private partial void LogRequestResponseReceivedSensitive(string endpointName, string method, string response);
[LoggerMessage(Level = LogLevel.Trace, Message = "{EndpointName} Request response received for method {method} (tool: '{ToolName}'). Response: '{Response}'.")]
private partial void LogRequestResponseReceivedSensitive(string endpointName, string method, string response, string? toolName = null);

[LoggerMessage(Level = LogLevel.Debug, Message = "{EndpointName} read {MessageType} message from channel.")]
private partial void LogMessageRead(string endpointName, string messageType);
Expand All @@ -820,8 +844,8 @@
[LoggerMessage(Level = LogLevel.Warning, Message = "{EndpointName} received request for method '{Method}', but no handler is available.")]
private partial void LogNoHandlerFoundForRequest(string endpointName, string method);

[LoggerMessage(Level = LogLevel.Debug, Message = "{EndpointName} waiting for response to request '{RequestId}' for method '{Method}'.")]
private partial void LogRequestSentAwaitingResponse(string endpointName, string method, RequestId requestId);
[LoggerMessage(Level = LogLevel.Debug, Message = "{EndpointName} waiting for response to request '{RequestId}' for method '{Method}' (tool: '{ToolName}').")]
private partial void LogRequestSentAwaitingResponse(string endpointName, string method, RequestId requestId, string? toolName = null);

[LoggerMessage(Level = LogLevel.Debug, Message = "{EndpointName} sending message.")]
private partial void LogSendingMessage(string endpointName);
Expand Down
2 changes: 2 additions & 0 deletions tests/Common/Utils/MockLoggerProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace ModelContextProtocol.Tests.Utils;
public class MockLoggerProvider() : ILoggerProvider
{
public ConcurrentQueue<(string Category, LogLevel LogLevel, EventId EventId, string Message, Exception? Exception)> LogMessages { get; } = [];
public ConcurrentQueue<(string Category, LogLevel LogLevel, EventId EventId, string Message, Exception? Exception, object? State)> LogMessagesWithState { get; } = [];

public ILogger CreateLogger(string categoryName)
{
Expand All @@ -22,6 +23,7 @@ public void Log<TState>(
LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
mockProvider.LogMessages.Enqueue((category, logLevel, eventId, formatter(state, exception), exception));
mockProvider.LogMessagesWithState.Enqueue((category, logLevel, eventId, formatter(state, exception), exception, state));
}

public bool IsEnabled(LogLevel logLevel) => true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,20 @@ namespace ModelContextProtocol.Tests.Configuration;

public partial class McpServerBuilderExtensionsToolsTests : ClientServerTestBase
{
private MockLoggerProvider _mockLoggerProvider = new();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test could be updated to use the MockLoggerProvider in the base class if #1054 gets merged first.


public McpServerBuilderExtensionsToolsTests(ITestOutputHelper testOutputHelper)
: base(testOutputHelper)
{
// Configure LoggerFactory to use Debug level and add MockLoggerProvider
LoggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder =>
{
builder.AddProvider(XunitLoggerProvider);
builder.AddProvider(_mockLoggerProvider);
builder.SetMinimumLevel(LogLevel.Debug);
});
}

private MockLoggerProvider _mockLoggerProvider = new();

protected override void ConfigureServices(ServiceCollection services, IMcpServerBuilder mcpServerBuilder)
{
mcpServerBuilder
Expand Down Expand Up @@ -733,6 +740,86 @@ await client.SendNotificationAsync(
await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => await invokeTask);
}

[Fact]
public async Task ToolName_Captured_In_Structured_Logging()
{
await using McpClient client = await CreateMcpClientForServer();

// Call a tool that will succeed
var result = await client.CallToolAsync(
"echo",
new Dictionary<string, object?> { ["message"] = "test" },
cancellationToken: TestContext.Current.CancellationToken);

Assert.NotNull(result);

// Verify that the tool name is captured in structured logging
// The LogMessagesWithState should contain log entries with tool name in the state
var relevantLogs = _mockLoggerProvider.LogMessagesWithState
.Where(m => m.Category == "ModelContextProtocol.Client.McpClient" &&
m.Message.Contains("tools/call"))
.ToList();

Assert.NotEmpty(relevantLogs);

// Check that at least one log entry has the tool name in its structured state
// This demonstrates how users can extract the tool name from TState in a custom ILoggerProvider
// The State object is IReadOnlyList<KeyValuePair<string, object?>> which contains
// structured logging parameters like "ToolName", "Method", "EndpointName", etc.
bool foundToolName = relevantLogs.Any(log =>
{
if (log.State is IReadOnlyList<KeyValuePair<string, object?>> stateList)
{
return stateList.Any(kvp =>
kvp.Key == "ToolName" &&
kvp.Value?.ToString() == "echo");
}
return false;
});

Assert.True(foundToolName, "Tool name 'echo' was not found in structured logging state");
}

[Fact]
public async Task ToolName_Captured_In_Structured_Logging_OnToolError()
{
await using McpClient client = await CreateMcpClientForServer();

// Call a tool that will error - note that tool errors are returned as CallToolResult with IsError=true,
// not thrown as exceptions per the MCP spec
var result = await client.CallToolAsync(
"throw_exception",
cancellationToken: TestContext.Current.CancellationToken);

// Verify the tool error was returned properly
Assert.NotNull(result);
Assert.True(result.IsError);

// Verify that the tool name is captured in structured logging
// even when the tool encounters an error
var relevantLogs = _mockLoggerProvider.LogMessagesWithState
.Where(m => m.LogLevel == LogLevel.Debug &&
(m.Message.Contains("waiting for response") || m.Message.Contains("response received")) &&
m.Message.Contains("tools/call"))
.ToList();

Assert.NotEmpty(relevantLogs);

// Check that at least one log entry has the tool name in its structured state
bool foundToolName = relevantLogs.Any(log =>
{
if (log.State is IReadOnlyList<KeyValuePair<string, object?>> stateList)
{
return stateList.Any(kvp =>
kvp.Key == "ToolName" &&
kvp.Value?.ToString() == "throw_exception");
}
return false;
});

Assert.True(foundToolName, "Tool name 'throw_exception' was not found in structured logging state");
}

[McpServerToolType]
public sealed class EchoTool(ObjectWithId objectFromDI)
{
Expand Down
Loading