Description
Description
The AG-UI ASP.NET Core hosting extension (Microsoft.Agents.AI.Hosting.AGUI.AspNetCore) only supports Server-Sent Events (SSE), and there is no public extension point to select or plug in an alternative transport. I'd like MapAGUI (and the wider AG-UI hosting extension) to be extensible to support other transports — WebSockets, webhooks, or a custom/handoff transport — so the implementation matches the transport-agnostic guarantee of the AG-UI spec.
What problem does it solve?
The AG-UI specification is explicitly transport agnostic:
Transport Agnostic: AG-UI doesn't mandate how events are delivered, supporting various transport mechanisms including Server-Sent Events (SSE), webhooks, WebSockets, and more. This flexibility lets developers choose the transport that best fits their architecture.
— https://docs.ag-ui.com/concepts/architecture (see also https://docs.ag-ui.com/concepts/capabilities#transport)
The MAF AG-UI extension, however, hard-codes SSE via sealed/internal types, so consumers cannot honour the transport-agnostic contract the spec advertises. MapAGUI always terminates the request by returning an SSE result, with no parameter, option, or abstraction to change the transport:
AGUIEndpointRouteBuilderExtensions.MapAGUI
return endpoints.MapPost(pattern, async ([FromBody] RunAgentInput? input, HttpContext context, CancellationToken cancellationToken) =>
{
// ...
var sseLogger = context.RequestServices.GetRequiredService<ILogger<AGUIServerSentEventsResult>>();
return new AGUIServerSentEventsResult(eventsWithSessionSave, sseLogger); // <-- transport hard-coded to SSE
});
The SSE result type is internal sealed, so it cannot be reused, substituted, or extended by consumers, and the content type is fixed:
dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/AGUIServerSentEventsResult.cs
internal sealed partial class AGUIServerSentEventsResult : IResult, IDisposable
{
// ...
httpContext.Response.ContentType = "text/event-stream"; // <-- only SSE
httpContext.Response.Headers.CacheControl = "no-cache,no-store";
httpContext.Response.Headers.Pragma = "no-cache";
}
There is no ITransport-style abstraction, no WebSocket implementation, and no hook to delegate event delivery to another mechanism.
What would the expected behavior be?
The SSE implementation should be one transport among several behind a public abstraction, rather than the only, hard-wired option. Concretely, I'd expect to be able to:
- choose a transport when mapping the endpoint (e.g. a
MapAGUI(..., transport) overload and/or DI-based selection), and
- implement a custom transport that receives the
IAsyncEnumerable<BaseEvent> stream and is responsible for delivering it.
Motivating use case. We want to stream agent updates back to the browser using Azure SignalR Service in serverless mode rather than holding a long-lived SSE connection open on the hosting process. Delegating the streaming leg to Azure SignalR Service removes per-session long-lived connections from our hosts and lets the managed service fan out updates to clients. The ask is broader than SignalR, though — the core need is a transport-extensible AG-UI host so the community/Microsoft can add WebSocket and webhook transports, and hosts can implement custom handoff transports (such as Azure SignalR Service) without forking the package.
Alternatives considered.
- Fork or copy the SSE result type — not viable:
AGUIServerSentEventsResult is internal sealed, so a custom transport means reimplementing the AG-UI event serialisation and endpoint wiring by hand and keeping it in sync with the package.
- Wrap/proxy the SSE endpoint behind our own service that re-publishes to Azure SignalR Service — adds an extra hop and still keeps a long-lived SSE connection on the host, which is exactly what we're trying to avoid.
- Wait for AG-UI clients to standardise on SSE only — contradicts the spec's stated transport-agnostic design.
Suggested direction (non-prescriptive):
- Introduce a public transport abstraction (e.g.
IAGUITransport / a transport/result factory) that takes the IAsyncEnumerable<BaseEvent> stream and is responsible for delivering it.
- Make the existing SSE behaviour the default implementation of that abstraction, and make
AGUIServerSentEventsResult (or an equivalent) public/replaceable.
- Add a
MapAGUI(..., transport) overload and/or DI-based transport selection.
- Optionally ship a WebSocket transport in-box, and document how to implement a handoff transport (e.g. Azure SignalR Service).
References:
Code Sample
// Today: SSE is the only possible outcome — no way to select another transport.
app.MapAGUI(agentBuilder, "/agent");
// Desired (illustrative): a public abstraction so the transport can be chosen/extended.
app.MapAGUI(agentBuilder, "/agent", transport: new WebSocketAGUITransport());
// or a handoff transport that streams events via an external service (e.g. Azure SignalR Service serverless).
app.MapAGUI(agentBuilder, "/agent", transport: new AzureSignalRAGUITransport(/* ... */));
// A custom transport would receive the AG-UI event stream and own delivery:
public sealed class AzureSignalRAGUITransport : IAGUITransport
{
public Task DeliverAsync(
HttpContext context,
IAsyncEnumerable<BaseEvent> events,
CancellationToken cancellationToken)
{
// hand the event stream off to Azure SignalR Service (serverless) instead of SSE
}
}
Language/SDK
.NET
Description
Description
The AG-UI ASP.NET Core hosting extension (
Microsoft.Agents.AI.Hosting.AGUI.AspNetCore) only supports Server-Sent Events (SSE), and there is no public extension point to select or plug in an alternative transport. I'd likeMapAGUI(and the wider AG-UI hosting extension) to be extensible to support other transports — WebSockets, webhooks, or a custom/handoff transport — so the implementation matches the transport-agnostic guarantee of the AG-UI spec.What problem does it solve?
The AG-UI specification is explicitly transport agnostic:
The MAF AG-UI extension, however, hard-codes SSE via sealed/internal types, so consumers cannot honour the transport-agnostic contract the spec advertises.
MapAGUIalways terminates the request by returning an SSE result, with no parameter, option, or abstraction to change the transport:AGUIEndpointRouteBuilderExtensions.MapAGUIThe SSE result type is
internal sealed, so it cannot be reused, substituted, or extended by consumers, and the content type is fixed:dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/AGUIServerSentEventsResult.csThere is no
ITransport-style abstraction, no WebSocket implementation, and no hook to delegate event delivery to another mechanism.What would the expected behavior be?
The SSE implementation should be one transport among several behind a public abstraction, rather than the only, hard-wired option. Concretely, I'd expect to be able to:
MapAGUI(..., transport)overload and/or DI-based selection), andIAsyncEnumerable<BaseEvent>stream and is responsible for delivering it.Motivating use case. We want to stream agent updates back to the browser using Azure SignalR Service in serverless mode rather than holding a long-lived SSE connection open on the hosting process. Delegating the streaming leg to Azure SignalR Service removes per-session long-lived connections from our hosts and lets the managed service fan out updates to clients. The ask is broader than SignalR, though — the core need is a transport-extensible AG-UI host so the community/Microsoft can add WebSocket and webhook transports, and hosts can implement custom handoff transports (such as Azure SignalR Service) without forking the package.
Alternatives considered.
AGUIServerSentEventsResultisinternal sealed, so a custom transport means reimplementing the AG-UI event serialisation and endpoint wiring by hand and keeping it in sync with the package.Suggested direction (non-prescriptive):
IAGUITransport/ a transport/result factory) that takes theIAsyncEnumerable<BaseEvent>stream and is responsible for delivering it.AGUIServerSentEventsResult(or an equivalent) public/replaceable.MapAGUI(..., transport)overload and/or DI-based transport selection.References:
MapAGUI(SSE hard-coded):agent-framework/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/AGUIEndpointRouteBuilderExtensions.cs
Line 34 in 7e9c043
AGUIServerSentEventsResult(internal sealed,text/event-stream):dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/AGUIServerSentEventsResult.csCode Sample
Language/SDK
.NET