Description
Describe the bug
The C# MCP server, when using streaming http transport, erroneously closes the SSE response stream too early. It happens when a Sampling request is made from within a Tool request; the sampling request has the same request id as the pending tool request, and the stream mistakes the sampling request for the response to the tool request.
To Reproduce
Steps to reproduce the behavior:
- Create a MCP server, http streaming, stateless = false, with a tool configured.
- The tool should make a sampling request back to the client
- From a client, supporting sampling, invoke the tool (the request id should be 1)
- After the tool makes the sampling request, the SSE response stream is disposed.
- The sampling client returns a sampling response to the tool, which returns it's final response to the client
- The final tool response is discarded, because the SSE response stream is already disposed.
- The client hangs, waiting for the tool response.
Expected behavior
The SSE response stream should only be closed when the final tool response is returned.
Logs
n/a
Additional context
I believe the bug is in StreamableHttpPostTransport:
There is a message filter looking for a message with an id equal to the pending request id, in order to determine when to complete the response stream. This checks for messages of class JsonRpcMessageWithId - but both JsonRpcRequest and JsonRpcResponse are subclasses.
The sampling request is a JsonRpcRequest, and gets its id from an auto-incremented id in McpSession:
csharp-sdk/src/ModelContextProtocol/McpSession.cs
Lines 364 to 368 in 317f468
The bug happens if the incoming request id is 1 (typical) the outgoing sampling request also gets an id of 1 (or obviously, some other situation where the ids collide)
ergo: The request ids for the incoming and outgoing requests are being conflated. They are in separate domains, and must not be compared.
Note: the sampling extension methods don't set an id for the sampling request, which is why it ends up with 1:
csharp-sdk/src/ModelContextProtocol/Server/McpServerExtensions.cs
Lines 35 to 40 in 317f468
Possible Fix
Probably StopOnFinalResponseFilter() should check that the message is of type JsonRpcResponse rather than JsonRpcMessageWithId.