Skip to content

HTTP Streamable vs SSE endpoints #700

@pksorensen

Description

@pksorensen

When having worked with the spec in node and other platforms, there seem to be a clear developer path if one are creating legacy SSE + Message endpoint vs http streamble transport.

Coming to c# sdk its hard to understand when its using http streamble vs sse endpoints. Right now it is mixed up with disabling sateless which is confusing.

Example from source code: If its not stateless it automatically enable /sse endpoints. It means you cant do a statefull MCP server in c# atm without enabling the /sse endpoint, which then lead to additional endpoints that some clients might use due to fallback principals if an error happens on the http streamble endpoint "/".

This issue asks that we consider adding another option to controll if legacy endpoints should be enabled. What do people prefer? I am personally thinking that a option/feature flag where we basically sets the transport more clearly when configuring the app similar to when setting up stdio. I think the current approach makes it hard to couble code/setup to the spec.

        if (!streamableHttpHandler.HttpServerTransportOptions.Stateless)
        {
            // The GET and DELETE endpoints are not mapped in Stateless mode since there's no way to send unsolicited messages
            // for the GET to handle, and there is no server-side state for the DELETE to clean up.
            streamableHttpGroup.MapGet("", streamableHttpHandler.HandleGetRequestAsync)
                .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, contentTypes: ["text/event-stream"]));
            streamableHttpGroup.MapDelete("", streamableHttpHandler.HandleDeleteRequestAsync);

            // Map legacy HTTP with SSE endpoints only if not in Stateless mode, because we cannot guarantee the /message requests
            // will be handled by the same process as the /sse request.
            var sseHandler = endpoints.ServiceProvider.GetRequiredService<SseHandler>();
            var sseGroup = mcpGroup.MapGroup("")
                .WithDisplayName(b => $"MCP HTTP with SSE | {b.DisplayName}");

            sseGroup.MapGet("/sse", sseHandler.HandleSseRequestAsync)
                .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, contentTypes: ["text/event-stream"]));
            sseGroup.MapPost("/message", sseHandler.HandleMessageRequestAsync)
                .WithMetadata(new AcceptsMetadata(["application/json"]))
                .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status202Accepted));
        }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions