Skip to content

Commit 77f9e64

Browse files
SSE client transport: tolerate return of RPC responses in POST requests. (#445)
* SSE client transport: handle return of RPC responses in POST requests. * Remove accidental change. * Address feedback * Remove unused namespace.
1 parent ee9b04b commit 77f9e64

File tree

2 files changed

+30
-14
lines changed

2 files changed

+30
-14
lines changed

src/ModelContextProtocol/Client/SseClientSessionTransport.cs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,26 +92,18 @@ public override async Task SendMessageAsync(
9292
StreamableHttpClientSessionTransport.CopyAdditionalHeaders(httpRequestMessage.Headers, _options.AdditionalHeaders);
9393
var response = await _httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false);
9494

95-
response.EnsureSuccessStatusCode();
96-
97-
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
98-
99-
if (string.IsNullOrEmpty(responseContent) || responseContent.Equals("accepted", StringComparison.OrdinalIgnoreCase))
100-
{
101-
LogAcceptedPost(Name, messageId);
102-
}
103-
else
95+
if (!response.IsSuccessStatusCode)
10496
{
10597
if (_logger.IsEnabled(LogLevel.Trace))
10698
{
107-
LogRejectedPostSensitive(Name, messageId, responseContent);
99+
LogRejectedPostSensitive(Name, messageId, await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false));
108100
}
109101
else
110102
{
111103
LogRejectedPost(Name, messageId);
112104
}
113105

114-
throw new InvalidOperationException("Failed to send message");
106+
response.EnsureSuccessStatusCode();
115107
}
116108
}
117109

tests/ModelContextProtocol.AspNetCore.Tests/SseIntegrationTests.cs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,22 @@ public async Task ConnectAndReceiveMessage_InMemoryServer_WithFullEndpointEventU
5858
Assert.True(true);
5959
}
6060

61+
[Fact]
62+
public async Task ConnectAndReceiveMessage_ServerReturningJsonInPostRequest()
63+
{
64+
await using var app = Builder.Build();
65+
MapAbsoluteEndpointUriMcp(app, respondInJson: true);
66+
67+
await app.StartAsync(TestContext.Current.CancellationToken);
68+
69+
await using var mcpClient = await ConnectMcpClientAsync();
70+
71+
// Send a test message through POST endpoint
72+
await mcpClient.SendNotificationAsync("test/message", new Envelope { Message = "Hello, SSE!" }, serializerOptions: JsonContext.Default.Options, cancellationToken: TestContext.Current.CancellationToken);
73+
74+
Assert.True(true);
75+
}
76+
6177
[Fact]
6278
public async Task ConnectAndReceiveNotification_InMemoryServer()
6379
{
@@ -220,7 +236,7 @@ public async Task EmptyAdditionalHeadersKey_Throws_InvalidOperationException()
220236
Assert.Equal("Failed to add header '' with value '' from AdditionalHeaders.", ex.Message);
221237
}
222238

223-
private static void MapAbsoluteEndpointUriMcp(IEndpointRouteBuilder endpoints)
239+
private static void MapAbsoluteEndpointUriMcp(IEndpointRouteBuilder endpoints, bool respondInJson = false)
224240
{
225241
var loggerFactory = endpoints.ServiceProvider.GetRequiredService<ILoggerFactory>();
226242
var optionsSnapshot = endpoints.ServiceProvider.GetRequiredService<IOptions<McpServerOptions>>();
@@ -267,7 +283,7 @@ private static void MapAbsoluteEndpointUriMcp(IEndpointRouteBuilder endpoints)
267283
await Results.BadRequest("Session not started.").ExecuteAsync(context);
268284
return;
269285
}
270-
var message = (JsonRpcMessage?)await context.Request.ReadFromJsonAsync(McpJsonUtilities.DefaultOptions.GetTypeInfo(typeof(JsonRpcMessage)), context.RequestAborted);
286+
var message = await context.Request.ReadFromJsonAsync<JsonRpcMessage>(McpJsonUtilities.DefaultOptions, context.RequestAborted);
271287
if (message is null)
272288
{
273289
await Results.BadRequest("No message in request body.").ExecuteAsync(context);
@@ -276,7 +292,15 @@ private static void MapAbsoluteEndpointUriMcp(IEndpointRouteBuilder endpoints)
276292

277293
await session.OnMessageReceivedAsync(message, context.RequestAborted);
278294
context.Response.StatusCode = StatusCodes.Status202Accepted;
279-
await context.Response.WriteAsync("Accepted");
295+
296+
if (respondInJson)
297+
{
298+
await context.Response.WriteAsJsonAsync(message, McpJsonUtilities.DefaultOptions, cancellationToken: context.RequestAborted);
299+
}
300+
else
301+
{
302+
await context.Response.WriteAsync("Accepted");
303+
}
280304
});
281305
}
282306

0 commit comments

Comments
 (0)