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

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions