Add server-side Streamable HTTP transport support (#330)

- Changes IJsonRpcMessage to an abstract base class so RelatedTransport will always be available
- Streamable HTTP supports multiple concurrent HTTP request with their own indpendent SSE response streams
- RelatedTransport indicates the source or destination of the JsonRpcMessage

- Changes the default RequestId to a JSON number for better compatibility with MCP servers in the wild
This commit is contained in:
Stephen Halter 2025-04-18 17:07:23 -07:00 • committed by GitHub
commit 7dcba357d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,4 +1,5 @@
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Protocol.Transport;
using ModelContextProtocol.Utils;
using System.Text.Json;
using System.Text.Json.Nodes;
@ -30,7 +31,7 @@ internal sealed class RequestHandlers : Dictionary<string, Func<JsonRpcRequest,
/// </remarks>
public void Set<TRequest, TResponse>(
string method,
Func<TRequest?, CancellationToken, ValueTask<TResponse>> handler,
Func<TRequest?, ITransport?, CancellationToken, ValueTask<TResponse>> handler,
JsonTypeInfo<TRequest> requestTypeInfo,
JsonTypeInfo<TResponse> responseTypeInfo)
{
@ -42,7 +43,7 @@ internal sealed class RequestHandlers : Dictionary<string, Func<JsonRpcRequest,
this[method] = async (request, cancellationToken) =>
{
TRequest? typedRequest = JsonSerializer.Deserialize(request.Params, requestTypeInfo);
object? result = await handler(typedRequest, cancellationToken).ConfigureAwait(false);
object? result = await handler(typedRequest, request.RelatedTransport, cancellationToken).ConfigureAwait(false);
return JsonSerializer.SerializeToNode(result, responseTypeInfo);
};
}