Skip to content

.NET: Add MCP Apps support to AG-UI (.NET) - #5216

Open
Manuel Naujoks (halllo) wants to merge 2 commits into
microsoft:mainfrom
halllo:mcp-apps-via-agui
Open

Manuel Naujoks (halllo) wants to merge 2 commits into
microsoft:mainfrom
halllo:mcp-apps-via-agui

Conversation

@halllo

Copy link
Copy Markdown

Motivation and Context

MCP Apps is a new MCP extension that allows attaching interactive HTML UI components to MCP tools. When a tool is invoked, the AG-UI host passes a ui:// resource URI alongside the tool result so a capable client can render the HTML app in-place. This PR extends the framework's AG-UI implementation to support MCP Apps, and adds a full end-to-end sample and conformance tests for the AsAGUIEventStreamAsync protocol to ensure correctness of the SSE event stream.

Description

Framework extensions (Microsoft.Extensions.AI.Hosting.AGUI.AspNetCore):

  • Added ACTIVITY_SNAPSHOT event type to AGUIEventTypes, AGUIJsonSerializerContext, and a new ActivitySnapshotEvent model.
  • Updated ChatResponseUpdateAGUIExtensions and AGUIEndpointRouteBuilderExtensions to emit ACTIVITY_SNAPSHOT events when a proxied tool result includes a UI resource URI.
  • Added EmitToolMetadataMiddleware to attach tool metadata to the event stream.

New sample: dotnet/samples/02-agents/AGUI/Step06_McpApps/

A three-project sample demonstrating MCP Apps over AG-UI end-to-end:

  • McpServer/ — ASP.NET Core MCP server exposing a get-time tool with a bundled HTML app (TypeScript/Vite, compiled to a single HTML file). The tool is annotated with [McpMeta("ui", ...)] to attach the ui:// resource URI.
  • Server/ — AG-UI server that bridges the MCP server to AG-UI clients using MapAGUI with a ProxiedResultResolver overload to proxy resources/read and tools/call requests.
  • Client/ — Console client using AGUIChatClient and RunStreamingAsync to stream responses and display ACTIVITY_SNAPSHOT events (which carry the ui:// URI and tool result for browser-based clients to render).

Tests:

  • ChatResponseUpdateAGUIExtensionsTests.cs and AGUIEndpointRouteBuilderExtensionsTests.cs — unit tests for the new/updated extension methods.
  • ConformanceTests.cs — end-to-end tests for the sample, exercising the AsAGUIEventStreamAsync event stream.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? No — additive framework extensions and new sample only.

Copilot AI review requested due to automatic review settings April 11, 2026 12:58
@moonbox3 Evan Mattson (moonbox3) added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs .NET Usage: [Issues, PRs], Target: .Net labels Apr 11, 2026
@github-actions github-actions Bot changed the title Add MCP Apps support to AG-UI (.NET) .NET: Add MCP Apps support to AG-UI (.NET) Apr 11, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds MCP Apps support to the .NET AG-UI hosting + shared protocol layers by emitting ACTIVITY_SNAPSHOT events for tool results that include a ui:// resource URI, along with a new end-to-end sample and accompanying unit/conformance tests.

Changes:

  • Introduces ACTIVITY_SNAPSHOT to the AG-UI event model/serializer and emits it after tool results when MCP tool metadata includes a UI resource URI.
  • Extends MapAGUI with an optional ProxiedResultResolver to short-circuit proxied MCP resources/read and tools/call requests.
  • Adds the Step06_McpApps sample (MCP server + AG-UI server + client) and protocol/conformance tests.

Reviewed changes

Copilot reviewed 32 out of 33 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests.csproj Adds TestHost dependency for new endpoint-level unit tests.
dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/AGUIEndpointRouteBuilderExtensionsTests.cs Adds unit tests covering proxied-result short-circuiting behavior.
dotnet/tests/Microsoft.Agents.AI.AGUI.UnitTests/ChatResponseUpdateAGUIExtensionsTests.cs Adds AsAGUIEventStreamAsync protocol conformance tests including ACTIVITY_SNAPSHOT.
dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/EmitToolMetadataMiddleware.cs New middleware to attach tool metadata onto tool results for downstream AG-UI emission.
dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/AGUIEndpointRouteBuilderExtensions.cs Adds MapAGUI overload supporting proxied-result resolution and minimal SSE response path.
dotnet/src/Microsoft.Agents.AI.AGUI/Shared/ChatResponseUpdateAGUIExtensions.cs Emits ACTIVITY_SNAPSHOT after tool results when tool metadata includes MCP Apps UI URI; adds tool-result normalization helper.
dotnet/src/Microsoft.Agents.AI.AGUI/Shared/BaseEventJsonConverter.cs Adds JSON (de)serialization support for ACTIVITY_SNAPSHOT.
dotnet/src/Microsoft.Agents.AI.AGUI/Shared/AGUIJsonSerializerContext.cs Registers ActivitySnapshotEvent for source-generated JSON.
dotnet/src/Microsoft.Agents.AI.AGUI/Shared/AGUIEventTypes.cs Adds ACTIVITY_SNAPSHOT discriminator constant.
dotnet/src/Microsoft.Agents.AI.AGUI/Shared/ActivitySnapshotEvent.cs New event model for ACTIVITY_SNAPSHOT.
dotnet/samples/02-agents/AGUI/Step06_McpApps/Tests/Tests.csproj New sample test project for MCP Apps AG-UI conformance tests.
dotnet/samples/02-agents/AGUI/Step06_McpApps/Tests/ConformanceTests.cs Adds (skipped by default) integration/conformance tests for SSE + proxy flows.
dotnet/samples/02-agents/AGUI/Step06_McpApps/Server/Server.csproj New AG-UI server project for the MCP Apps sample.
dotnet/samples/02-agents/AGUI/Step06_McpApps/Server/Properties/launchSettings.json Launch settings for sample AG-UI server.
dotnet/samples/02-agents/AGUI/Step06_McpApps/Server/Program.cs Implements AG-UI↔MCP bridge, emits tool metadata, and proxies MCP requests via ProxiedResultResolver.
dotnet/samples/02-agents/AGUI/Step06_McpApps/Server/appsettings.json Sample server configuration.
dotnet/samples/02-agents/AGUI/Step06_McpApps/Server/appsettings.Development.json Sample server dev configuration.
dotnet/samples/02-agents/AGUI/Step06_McpApps/README.md Full guide documenting MCP Apps over AG-UI, proxy flows, and event stream shapes.
dotnet/samples/02-agents/AGUI/Step06_McpApps/McpServer/vite.config.ts Vite config for bundling the MCP App to a single HTML file.
dotnet/samples/02-agents/AGUI/Step06_McpApps/McpServer/Properties/launchSettings.json Launch settings for sample MCP server.
dotnet/samples/02-agents/AGUI/Step06_McpApps/McpServer/Program.cs Sample MCP server exposing get-time tool and ui:// HTML resource.
dotnet/samples/02-agents/AGUI/Step06_McpApps/McpServer/package.json Frontend MCP App build dependencies and script.
dotnet/samples/02-agents/AGUI/Step06_McpApps/McpServer/package-lock.json Locked frontend dependency graph for the MCP App build.
dotnet/samples/02-agents/AGUI/Step06_McpApps/McpServer/McpServer.csproj New MCP server project file.
dotnet/samples/02-agents/AGUI/Step06_McpApps/McpServer/get-time.ts MCP App frontend logic using @modelcontextprotocol/ext-apps.
dotnet/samples/02-agents/AGUI/Step06_McpApps/McpServer/get-time.html MCP App HTML entrypoint for the Vite build.
dotnet/samples/02-agents/AGUI/Step06_McpApps/McpServer/appsettings.json Sample MCP server configuration.
dotnet/samples/02-agents/AGUI/Step06_McpApps/McpServer/appsettings.Development.json Sample MCP server dev configuration.
dotnet/samples/02-agents/AGUI/Step06_McpApps/Client/Program.cs Sample console client that streams AG-UI responses and prints ACTIVITY_SNAPSHOT + proxied reads.
dotnet/samples/02-agents/AGUI/Step06_McpApps/Client/Client.csproj New sample client project file.
dotnet/samples/02-agents/AGUI/README.md Links and run instructions for the new Step06 MCP Apps sample.
dotnet/Directory.Packages.props Adds ModelContextProtocol.AspNetCore central package version.
dotnet/agent-framework-dotnet.slnx Includes new Step06 sample projects in the solution.
Files not reviewed (1)
  • dotnet/samples/02-agents/AGUI/Step06_McpApps/McpServer/package-lock.json: Language not supported

…or AsAGUIEventStreamAsync protocol conformance.
@halllo

Copy link
Copy Markdown
Author

Thinking about this deeper, I would like to get your feedback about this design decision in particular:

Currently the ActivitySnapshot gets created by looking at the tool meta directly:

if (functionResultContent.Result is AIContent aic && GetToolMetadata(aic) is JsonObject toolMetadata)
{
    toolArgsByCallId.TryGetValue(functionResultContent.CallId, out var toolInputJson);
    toolArgsByCallId.Remove(functionResultContent.CallId);
    var resourceUri = toolMetadata["ui"]?["resourceUri"]?.ToString() ?? string.Empty;
    yield return BuildActivitySnapshot(chatResponse.MessageId, resourceUri, resultContent, toolInputJson ?? "{}", jsonSerializerOptions);
}

On the other side, the ActivitySnapshot is turned into a DataContent.

private static ChatResponseUpdate CreateActivitySnapshotUpdate(
    ActivitySnapshotEvent activitySnapshot,
    string? conversationId,
    string? responseId,
    JsonSerializerOptions jsonSerializerOptions)
{
    byte[] jsonBytes = JsonSerializer.SerializeToUtf8Bytes(
        activitySnapshot.Content!.Value,
        jsonSerializerOptions.GetTypeInfo(typeof(JsonElement)));
    DataContent dataContent = new(jsonBytes, "application/json");

    return new ChatResponseUpdate(ChatRole.Assistant, [dataContent])
    {
        ConversationId = conversationId,
        ResponseId = responseId,
        CreatedAt = DateTimeOffset.UtcNow,
        AdditionalProperties = new AdditionalPropertiesDictionary
        {
            ["is_activity_snapshot"] = true,
            ["activity_type"] = activitySnapshot.ActivityType
        }
    };
}

Would you prefer the implementation to be more symmetric and also create the ActivitySnapshot from a DataContent?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs .NET Usage: [Issues, PRs], Target: .Net

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants