Skip to content

Streamable HTTP transport doesn't support single JSON responses (spec violation) #1466

@dhruvmodi13

Description

@dhruvmodi13

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:

  1. Connect to a server that returns single JSON responses (e.g., Microsoft Dataverse MCP at https://d365catalyst.crm.dynamics.com/api/mcp)

  2. 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);
  1. Observe the error:
Streamable HTTP POST response completed without a reply to request with ID: 1
  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:

  1. Check the response Content-Type header
  2. If application/json: Parse the complete response body as a single JSON-RPC message
  3. 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:

  1. Sees 200 OK and assumes Streamable HTTP works ✓
  2. Sets StreamableHttpClientSessionTransport as active transport ✓
  3. Later, when parsing the response, the streaming parser fails because the response is application/json, not text/event-stream
  4. 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: ModelContextProtocol versions 0.8.0-preview.1 and 1.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:

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Significant bug affecting many usersneeds reproInsufficient information to reproduce

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions