-
Notifications
You must be signed in to change notification settings - Fork 665
Description
Describe the bug
The C# SDK's StreamableHttpClientSessionTransport only handles SSE streams but does not support single JSON responses, which are explicitly allowed by the Streamable HTTP transport specification.
According to the spec:
Server Responses: The server can respond either with:
- A single JSON response (
Content-Type: application/json)- An SSE stream (
Content-Type: text/event-stream) for multiple messages
The SDK unconditionally uses a streaming parser regardless of the response Content-Type, causing failures with spec-compliant MCP servers that return single JSON responses instead of SSE streams.
To Reproduce
Steps to reproduce the behavior:
-
Connect to a server that returns single JSON responses (e.g., Microsoft Dataverse MCP at
https://d365catalyst.crm.dynamics.com/api/mcp) -
Send an initialize request using the C# SDK:
var transportOptions = new HttpClientTransportOptions
{
Name = "test",
Endpoint = new Uri("https://d365catalyst.crm.dynamics.com/api/mcp"),
AdditionalHeaders = new Dictionary<string, string>
{
["Authorization"] = "Bearer <valid-token>"
}
};
var clientTransport = new HttpClientTransport(transportOptions, httpClient);
var mcpClient = await McpClient.CreateAsync(clientTransport, clientOptions, cancellationToken);
var tools = await mcpClient.ListToolsAsync(cancellationToken);- Observe the error:
Streamable HTTP POST response completed without a reply to request with ID: 1
- Verify server is working correctly using direct HTTP:
var jsonContent = JsonSerializer.Serialize(new
{
jsonrpc = "2.0",
id = 1,
method = "initialize",
@params = new
{
protocolVersion = "2024-11-05",
capabilities = new { },
clientInfo = new { name = "TestClient", version = "1.0.0" }
}
});
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(url, content);
// ✅ Returns 200 OK
// ✅ Content-Type: application/json; charset=utf-8
// ✅ Body: {"jsonrpc":"2.0","id":1,"result":{...}}Expected behavior
The SDK should:
- Check the response
Content-Typeheader - If
application/json: Parse the complete response body as a single JSON-RPC message - If
text/event-stream: Use the streaming parser for SSE events
Both response formats are valid per the MCP Streamable HTTP spec and should be supported.
Logs
Server Response (Spec-Compliant):
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 167
Mcp-Session-Id: a9259a04-549e-4abb-97d4-92b042362686
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"Microsoft Dataverse MCP Server","version":"1.0.0"}}}SDK Error:
Streamable HTTP POST response completed without a reply to request with ID: 1
Root Cause (from AutoDetectingClientSessionTransport.cs lines 65-77):
using var response = await streamableHttpTransport.SendHttpRequestAsync(message, cancellationToken);
if (response.IsSuccessStatusCode) // ← Only checks HTTP status, not Content-Type
{
LogUsingStreamableHttp(_name);
ActiveTransport = streamableHttpTransport; // ← Assumes it works without validating response format
}The AutoDetect logic:
- Sees
200 OKand assumes Streamable HTTP works ✓ - Sets
StreamableHttpClientSessionTransportas active transport ✓ - Later, when parsing the response, the streaming parser fails because the response is
application/json, nottext/event-stream✗ - No fallback mechanism exists at this point ✗
Additional context
Test Results:
| Test Method | Result | Details |
|---|---|---|
| Direct HTTP POST | ✅ Works | Receives valid JSON-RPC response |
| VS Code MCP Extension | ✅ Works | JavaScript SDK handles single JSON responses |
| C# SDK AutoDetect | ❌ Fails | False positive: detects "working" then fails to parse |
Affected Servers:
- Microsoft Dataverse MCP (
https://d365catalyst.crm.dynamics.com/api/mcp) - Any MCP server using Streamable HTTP with single JSON responses (spec-compliant behavior)
Environment:
- Package:
ModelContextProtocolversions0.8.0-preview.1and1.1.0(both affected) - .NET: 8.0
- Platform: Windows, Azure
Impact:
- SDK claims to support Streamable HTTP but only implements half of the spec (SSE streams only)
- AutoDetect mode gives false positives
- No workaround available within the SDK
Proposed Fix:
StreamableHttpClientSessionTransport.SendHttpRequestAsync() should check the response Content-Type:
var contentType = response.Content.Headers.ContentType?.MediaType;
if (contentType == "application/json")
{
// Single JSON response - parse directly
var body = await response.Content.ReadAsStringAsync(cancellationToken);
var jsonMessage = JsonSerializer.Deserialize<JsonRpcMessage>(body);
await _messageChannel.Writer.WriteAsync(jsonMessage, cancellationToken);
}
else if (contentType == "text/event-stream")
{
// SSE stream - use existing streaming parser
// ... existing logic ...
}Why This Matters:
Single JSON responses are simpler and more efficient for:
- Request-response patterns (most MCP operations)
- Servers that don't need server-to-client push notifications
- Simplified server implementation (no SSE infrastructure needed)
The MCP spec explicitly allows this mode, and the C# SDK should support it.
References: