Skip to content
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
14 changes: 3 additions & 11 deletions src/ModelContextProtocol/Client/SseClientSessionTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,26 +92,18 @@ public override async Task SendMessageAsync(
StreamableHttpClientSessionTransport.CopyAdditionalHeaders(httpRequestMessage.Headers, _options.AdditionalHeaders);
var response = await _httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false);

response.EnsureSuccessStatusCode();

var responseContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);

if (string.IsNullOrEmpty(responseContent) || responseContent.Equals("accepted", StringComparison.OrdinalIgnoreCase))
{
LogAcceptedPost(Name, messageId);
}
else
if (!response.IsSuccessStatusCode)
{
if (_logger.IsEnabled(LogLevel.Trace))
{
LogRejectedPostSensitive(Name, messageId, responseContent);
LogRejectedPostSensitive(Name, messageId, await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false));
}
else
{
LogRejectedPost(Name, messageId);
}

throw new InvalidOperationException("Failed to send message");
response.EnsureSuccessStatusCode();
}
}

Expand Down
30 changes: 27 additions & 3 deletions tests/ModelContextProtocol.AspNetCore.Tests/SseIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ public async Task ConnectAndReceiveMessage_InMemoryServer_WithFullEndpointEventU
Assert.True(true);
}

[Fact]
public async Task ConnectAndReceiveMessage_ServerReturningJsonInPostRequest()
{
await using var app = Builder.Build();
MapAbsoluteEndpointUriMcp(app, respondInJson: true);

await app.StartAsync(TestContext.Current.CancellationToken);

await using var mcpClient = await ConnectMcpClientAsync();

// Send a test message through POST endpoint
await mcpClient.SendNotificationAsync("test/message", new Envelope { Message = "Hello, SSE!" }, serializerOptions: JsonContext.Default.Options, cancellationToken: TestContext.Current.CancellationToken);

Assert.True(true);
}

[Fact]
public async Task ConnectAndReceiveNotification_InMemoryServer()
{
Expand Down Expand Up @@ -220,7 +236,7 @@ public async Task EmptyAdditionalHeadersKey_Throws_InvalidOperationException()
Assert.Equal("Failed to add header '' with value '' from AdditionalHeaders.", ex.Message);
}

private static void MapAbsoluteEndpointUriMcp(IEndpointRouteBuilder endpoints)
private static void MapAbsoluteEndpointUriMcp(IEndpointRouteBuilder endpoints, bool respondInJson = false)
{
var loggerFactory = endpoints.ServiceProvider.GetRequiredService<ILoggerFactory>();
var optionsSnapshot = endpoints.ServiceProvider.GetRequiredService<IOptions<McpServerOptions>>();
Expand Down Expand Up @@ -267,7 +283,7 @@ private static void MapAbsoluteEndpointUriMcp(IEndpointRouteBuilder endpoints)
await Results.BadRequest("Session not started.").ExecuteAsync(context);
return;
}
var message = (JsonRpcMessage?)await context.Request.ReadFromJsonAsync(McpJsonUtilities.DefaultOptions.GetTypeInfo(typeof(JsonRpcMessage)), context.RequestAborted);
var message = await context.Request.ReadFromJsonAsync<JsonRpcMessage>(McpJsonUtilities.DefaultOptions, context.RequestAborted);
if (message is null)
{
await Results.BadRequest("No message in request body.").ExecuteAsync(context);
Expand All @@ -276,7 +292,15 @@ private static void MapAbsoluteEndpointUriMcp(IEndpointRouteBuilder endpoints)

await session.OnMessageReceivedAsync(message, context.RequestAborted);
context.Response.StatusCode = StatusCodes.Status202Accepted;
await context.Response.WriteAsync("Accepted");

if (respondInJson)
{
await context.Response.WriteAsJsonAsync(message, McpJsonUtilities.DefaultOptions, cancellationToken: context.RequestAborted);
}
else
{
await context.Response.WriteAsync("Accepted");
}
});
}

Expand Down
Loading