Fix: Remove charset=utf-8 from Content-Type: application/json in HTTP transport (#1528)
This commit is contained in:
parent
af6fcff595
commit
a04c3e4f50
2 changed files with 53 additions and 2 deletions
|
|
@ -12,7 +12,7 @@ namespace ModelContextProtocol.Client;
|
|||
|
||||
internal class McpHttpClient(HttpClient httpClient)
|
||||
{
|
||||
internal static readonly MediaTypeHeaderValue s_applicationJsonContentType = new("application/json") { CharSet = "utf-8" };
|
||||
internal static readonly MediaTypeHeaderValue s_applicationJsonContentType = new("application/json");
|
||||
|
||||
internal virtual async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, JsonRpcMessage? message, CancellationToken cancellationToken)
|
||||
{
|
||||
|
|
@ -32,7 +32,7 @@ internal class McpHttpClient(HttpClient httpClient)
|
|||
}
|
||||
|
||||
#if NET
|
||||
return JsonContent.Create(message, McpJsonUtilities.JsonContext.Default.JsonRpcMessage);
|
||||
return JsonContent.Create(message, McpJsonUtilities.JsonContext.Default.JsonRpcMessage, s_applicationJsonContentType);
|
||||
#else
|
||||
var bytes = JsonSerializer.SerializeToUtf8Bytes(message, McpJsonUtilities.JsonContext.Default.JsonRpcMessage);
|
||||
var content = new ByteArrayContent(bytes);
|
||||
|
|
|
|||
|
|
@ -248,6 +248,57 @@ public class HttpClientTransportTests : LoggedTest
|
|||
Assert.False(transportBase.IsConnected);
|
||||
}
|
||||
|
||||
// Strict server mock used in Content-Type tests below.
|
||||
// Returns 200 only for bare "application/json", otherwise 415.
|
||||
private static Func<HttpRequestMessage, Task<HttpResponseMessage>> StrictJsonContentTypeHandler =>
|
||||
(request) =>
|
||||
{
|
||||
if (request.Method == HttpMethod.Post)
|
||||
{
|
||||
var contentType = request.Content?.Headers.ContentType;
|
||||
if (contentType?.CharSet is not null)
|
||||
{
|
||||
return Task.FromResult(new HttpResponseMessage
|
||||
{
|
||||
StatusCode = HttpStatusCode.UnsupportedMediaType,
|
||||
Content = new StringContent("Content-Type must be 'application/json'"),
|
||||
});
|
||||
}
|
||||
|
||||
return Task.FromResult(new HttpResponseMessage
|
||||
{
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
Content = new StringContent(
|
||||
"""{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-03-26","capabilities":{},"serverInfo":{"name":"Test","version":"1.0"}}}""",
|
||||
Encoding.UTF8,
|
||||
"application/json"),
|
||||
});
|
||||
}
|
||||
|
||||
throw new IOException("Abort");
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public async Task SendMessageAsync_StrictServer_Returns200_WhenContentTypeIsApplicationJson()
|
||||
{
|
||||
// Regression test for https://github.com/modelcontextprotocol/csharp-sdk/issues/1527
|
||||
// SDK must send bare "application/json" — no charset parameter.
|
||||
var options = new HttpClientTransportOptions
|
||||
{
|
||||
Endpoint = new Uri("http://localhost:8080"),
|
||||
TransportMode = HttpTransportMode.StreamableHttp,
|
||||
};
|
||||
|
||||
using var mockHttpHandler = new MockHttpHandler();
|
||||
using var httpClient = new HttpClient(mockHttpHandler);
|
||||
await using var transport = new HttpClientTransport(options, httpClient, LoggerFactory);
|
||||
mockHttpHandler.RequestHandler = StrictJsonContentTypeHandler;
|
||||
|
||||
// Succeeds only if the SDK sends Content-Type: application/json (no charset)
|
||||
await using var session = await transport.ConnectAsync(TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(session);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task StreamableHttp_InitialGetSseConnection_DoesNotCountAgainstMaxReconnectionAttempts()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue