Skip to content

Commit fccaa0a

Browse files
author
Mike Kistler
committed
Move MCP Apps support to its own package
1 parent 35e7e59 commit fccaa0a

12 files changed

Lines changed: 140 additions & 121 deletions

File tree

‎ModelContextProtocol.slnx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
<Project Path="src/ModelContextProtocol.Analyzers/ModelContextProtocol.Analyzers.csproj" />
6767
<Project Path="src/ModelContextProtocol.AspNetCore/ModelContextProtocol.AspNetCore.csproj" />
6868
<Project Path="src/ModelContextProtocol.Core/ModelContextProtocol.Core.csproj" />
69+
<Project Path="src/ModelContextProtocol.ExtApps/ModelContextProtocol.ExtApps.csproj" />
6970
<Project Path="src/ModelContextProtocol/ModelContextProtocol.csproj" />
7071
</Folder>
7172
<Folder Name="/tests/">

‎docs/list-of-diagnostics.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ If you use experimental APIs, you will get one of the diagnostics shown below. T
2323

2424
| Diagnostic ID | Description |
2525
| :------------ | :---------- |
26-
| `MCPEXP001` | Experimental APIs for features in the MCP specification itself, including Tasks, Extensions, and the MCP Apps extension. Tasks provide a mechanism for asynchronous long-running operations that can be polled for status and results (see [MCP Tasks specification](https://modelcontextprotocol.io/specification/draft/basic/utilities/tasks)). Extensions provide a framework for extending the Model Context Protocol while maintaining interoperability (see [SEP-2133](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133)). MCP Apps is the first official MCP extension, enabling servers to deliver interactive UIs inside AI clients (see [MCP Apps specification](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/2026-01-26/apps.mdx)). |
26+
| `MCPEXP001` | Experimental APIs for features in the MCP specification itself, including Tasks and Extensions. Tasks provide a mechanism for asynchronous long-running operations that can be polled for status and results (see [MCP Tasks specification](https://modelcontextprotocol.io/specification/draft/basic/utilities/tasks)). Extensions provide a framework for extending the Model Context Protocol while maintaining interoperability (see [SEP-2133](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133)). MCP Apps is the first official MCP extension, enabling servers to deliver interactive UIs inside AI clients (see [MCP Apps specification](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/2026-01-26/apps.mdx)). |
2727
| `MCPEXP002` | Experimental SDK APIs unrelated to the MCP specification itself, including subclassing `McpClient`/`McpServer` (see [#1363](https://github.com/modelcontextprotocol/csharp-sdk/pull/1363)) and `RunSessionHandler`, which may be removed or change signatures in a future release (consider using `ConfigureSessionOptions` instead). |
2828

2929
## Obsolete APIs

‎src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ options.OpenWorld is not null ||
153153
if (options.AppUi is { } appUi)
154154
{
155155
seededMeta = seededMeta is not null ? CloneJsonObject(seededMeta) : new JsonObject();
156-
McpApps.ApplyUiToolMetaToJsonObject(appUi, seededMeta);
156+
McpAppsInternal.ApplyUiToolMetaToJsonObject(appUi, seededMeta);
157157
}
158158

159159
tool.Meta = function.UnderlyingMethod is not null ?
@@ -170,7 +170,7 @@ options.OpenWorld is not null ||
170170
// Auto-detect async methods and mark with taskSupport = "optional" unless explicitly configured.
171171
// This enables implicit task support for async tools: clients can choose to invoke them
172172
// synchronously (wait for completion) or as a task (receive taskId, poll for result).
173-
if (function.UnderlyingMethod is not null &&
173+
if (function.UnderlyingMethod is not null &&
174174
IsAsyncMethod(function.UnderlyingMethod) &&
175175
tool.Execution?.TaskSupport is null)
176176
{
@@ -426,7 +426,7 @@ internal static IReadOnlyList<object> CreateMetadata(MethodInfo method)
426426
return meta;
427427
}
428428

429-
/// <summary>Creates a shallow-content clone of a <see cref="JsonObject"/> so that keys can be added without mutating the original.</summary>
429+
/// <summary>Creates a copy of a <see cref="JsonObject"/> so that keys can be added without mutating the original.</summary>
430430
private static JsonObject CloneJsonObject(JsonObject source)
431431
{
432432
var clone = new JsonObject();

‎src/ModelContextProtocol.Core/Server/McpAppUiAttribute.cs‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@ namespace ModelContextProtocol.Server;
77
/// </summary>
88
/// <remarks>
99
/// <para>
10-
/// Apply this attribute alongside <see cref="McpServerToolAttribute"/> to associate a tool with a
11-
/// UI resource in the MCP Apps extension. When processed, it populates both the structured
12-
/// <c>_meta.ui</c> object and the legacy <c>_meta["ui/resourceUri"]</c> flat key in the tool's
13-
/// metadata for backward compatibility with older MCP hosts.
10+
/// Apply this attribute alongside <see cref="McpServerToolAttribute"/> to associate an MCP Apps
11+
/// UI resource with the tool. When processed, it populates the structured <c>_meta.ui</c> object
12+
/// in the tool's metadata.
1413
/// </para>
1514
/// <para>
1615
/// This attribute takes precedence over any raw <c>[McpMeta("ui", ...)]</c> attribute on the

‎src/ModelContextProtocol.Core/Server/McpApps.cs‎

Lines changed: 5 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -5,90 +5,14 @@
55
namespace ModelContextProtocol.Server;
66

77
/// <summary>
8-
/// Provides constants and helper methods for building MCP Apps-enabled servers.
8+
/// Internal helper methods for MCP Apps integration within the Core package.
9+
/// The public MCP Apps API surface is in the ModelContextProtocol.ExtApps package.
910
/// </summary>
10-
/// <remarks>
11-
/// <para>
12-
/// MCP Apps is an extension to the Model Context Protocol that enables MCP servers to deliver
13-
/// interactive user interfaces — dashboards, forms, visualizations, and more — directly inside
14-
/// conversational AI clients.
15-
/// </para>
16-
/// <para>
17-
/// Use the constants in this class when populating the <c>extensions</c> capability and the
18-
/// <c>_meta</c> field of tools and resources. Use <see cref="GetUiCapability"/> to check whether
19-
/// the connected client supports the MCP Apps extension.
20-
/// </para>
21-
/// </remarks>
22-
public static class McpApps
11+
internal static class McpAppsInternal
2312
{
2413
/// <summary>
25-
/// The MIME type used for MCP App HTML resources.
26-
/// </summary>
27-
/// <remarks>
28-
/// This MIME type should be used when registering UI resources with
29-
/// <c>text/html;profile=mcp-app</c> to indicate they are MCP App resources.
30-
/// </remarks>
31-
public const string ResourceMimeType = "text/html;profile=mcp-app";
32-
33-
/// <summary>
34-
/// The extension identifier used for MCP Apps capability negotiation.
35-
/// </summary>
36-
/// <remarks>
37-
/// This key is used in the <see cref="ClientCapabilities.Extensions"/> and
38-
/// <see cref="ServerCapabilities.Extensions"/> dictionaries to advertise support for
39-
/// the MCP Apps extension.
40-
/// </remarks>
41-
public const string ExtensionId = "io.modelcontextprotocol/ui";
42-
43-
/// <summary>
44-
/// The legacy flat <c>_meta</c> key for the UI resource URI.
45-
/// </summary>
46-
/// <remarks>
47-
/// <para>
48-
/// This key is used for backward compatibility with older MCP hosts that do not support
49-
/// the nested <c>_meta.ui</c> object. When populating UI metadata, both this key and the
50-
/// <c>ui</c> object should be set to the same resource URI value.
51-
/// </para>
52-
/// <para>
53-
/// This key is considered legacy; prefer <see cref="McpUiToolMeta.ResourceUri"/> for new implementations.
54-
/// </para>
55-
/// </remarks>
56-
public const string ResourceUriMetaKey = "ui/resourceUri";
57-
58-
/// <summary>
59-
/// Gets the MCP Apps client capability, if advertised by the connected client.
60-
/// </summary>
61-
/// <param name="capabilities">The client capabilities received during the MCP initialize handshake.</param>
62-
/// <returns>
63-
/// A <see cref="McpUiClientCapabilities"/> instance if the client advertises support for the MCP Apps extension;
64-
/// otherwise, <see langword="null"/>.
65-
/// </returns>
66-
/// <remarks>
67-
/// Use this method to determine whether the connected client supports the MCP Apps extension
68-
/// and to read the client's supported MIME types.
69-
/// </remarks>
70-
[Experimental(Experimentals.Apps_DiagnosticId, UrlFormat = Experimentals.Apps_Url)]
71-
public static McpUiClientCapabilities? GetUiCapability(ClientCapabilities? capabilities)
72-
{
73-
if (capabilities?.Extensions is not { } extensions ||
74-
!extensions.TryGetValue(ExtensionId, out var value))
75-
{
76-
return null;
77-
}
78-
79-
if (value is JsonElement element)
80-
{
81-
return element.ValueKind == JsonValueKind.Null ? null :
82-
JsonSerializer.Deserialize(element, McpJsonUtilities.JsonContext.Default.McpUiClientCapabilities);
83-
}
84-
85-
return null;
86-
}
87-
88-
/// <summary>
89-
/// Applies UI tool metadata to a <see cref="System.Text.Json.Nodes.JsonObject"/>, setting both the
90-
/// <c>ui</c> object key and the legacy <c>ui/resourceUri</c> flat key for backward compatibility.
91-
/// Keys already present in <paramref name="meta"/> are not overwritten.
14+
/// Applies UI tool metadata to a <see cref="System.Text.Json.Nodes.JsonObject"/>, setting the
15+
/// <c>ui</c> object key if not already present.
9216
/// </summary>
9317
/// <param name="appUi">The UI tool metadata to apply.</param>
9418
/// <param name="meta">The <see cref="System.Text.Json.Nodes.JsonObject"/> to populate.</param>
@@ -103,11 +27,5 @@ internal static void ApplyUiToolMetaToJsonObject(McpUiToolMeta appUi, System.Tex
10327
meta["ui"] = uiNode;
10428
}
10529
}
106-
107-
// Populate the legacy flat "ui/resourceUri" key if not already present.
108-
if (!meta.ContainsKey(ResourceUriMetaKey) && appUi.ResourceUri is not null)
109-
{
110-
meta[ResourceUriMetaKey] = appUi.ResourceUri;
111-
}
11230
}
11331
}

‎src/ModelContextProtocol.Core/Server/McpServerToolCreateOptions.cs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,7 @@ public sealed class McpServerToolCreateOptions
203203
/// <remarks>
204204
/// <para>
205205
/// When set, this metadata is merged into <see cref="Meta"/> during tool creation, populating
206-
/// both the structured <c>_meta.ui</c> object and the legacy <c>_meta["ui/resourceUri"]</c>
207-
/// flat key for backward compatibility with older MCP hosts.
206+
/// the structured <c>_meta.ui</c> object.
208207
/// </para>
209208
/// <para>
210209
/// Explicit entries already present in <see cref="Meta"/> take precedence over values from

‎src/ModelContextProtocol.Core/Server/McpUiClientCapabilities.cs‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,9 @@ namespace ModelContextProtocol.Server;
88
/// </summary>
99
/// <remarks>
1010
/// <para>
11-
/// This object is the value associated with the <see cref="McpApps.ExtensionId"/> key in the
11+
/// This object is the value associated with the <c>"io.modelcontextprotocol/ui"</c> key in the
1212
/// <see cref="Protocol.ClientCapabilities.Extensions"/> dictionary.
1313
/// </para>
14-
/// <para>
15-
/// Use <see cref="McpApps.GetUiCapability"/> to read this from <see cref="Protocol.ClientCapabilities"/>.
16-
/// </para>
1714
/// </remarks>
1815
[Experimental(Experimentals.Apps_DiagnosticId, UrlFormat = Experimentals.Apps_Url)]
1916
public sealed class McpUiClientCapabilities
@@ -22,8 +19,7 @@ public sealed class McpUiClientCapabilities
2219
/// Gets or sets the list of MIME types supported by the client for MCP App UI resources.
2320
/// </summary>
2421
/// <remarks>
25-
/// A client that supports MCP Apps must include <see cref="McpApps.ResourceMimeType"/>
26-
/// (<c>"text/html;profile=mcp-app"</c>) in this list.
22+
/// A client that supports MCP Apps must include <c>"text/html;profile=mcp-app"</c> in this list.
2723
/// </remarks>
2824
[JsonPropertyName("mimeTypes")]
2925
public IList<string>? MimeTypes { get; set; }

‎src/ModelContextProtocol.Core/Server/McpUiToolMeta.cs‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ namespace ModelContextProtocol.Server;
1212
/// It associates the tool with a UI resource (identified by a <c>ui://</c> URI) and optionally
1313
/// controls which principals (model, app) can call the tool.
1414
/// </para>
15-
/// <para>
16-
/// When this metadata is applied, both the structured <c>_meta.ui</c> object and the legacy
17-
/// <c>_meta["ui/resourceUri"]</c> flat key are populated for backward compatibility with older hosts.
18-
/// </para>
1915
/// </remarks>
2016
[Experimental(Experimentals.Apps_DiagnosticId, UrlFormat = Experimentals.Apps_Url)]
2117
public sealed class McpUiToolMeta
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>net10.0;net9.0;net8.0;netstandard2.0</TargetFrameworks>
5+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
6+
<IsPackable>true</IsPackable>
7+
<PackageId>ModelContextProtocol.ExtApps</PackageId>
8+
<Description>MCP Apps extension for the .NET Model Context Protocol (MCP) SDK</Description>
9+
<PackageReadmeFile>README.md</PackageReadmeFile>
10+
<!-- Suppress the experimental tasks/extensions/apps warning -->
11+
<NoWarn>$(NoWarn);MCPEXP001</NoWarn>
12+
<!-- New package with no published baseline yet -->
13+
<EnablePackageValidation>false</EnablePackageValidation>
14+
</PropertyGroup>
15+
16+
<PropertyGroup Condition="'$(TargetFramework)' != 'netstandard2.0'">
17+
<IsAotCompatible>true</IsAotCompatible>
18+
</PropertyGroup>
19+
20+
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
21+
<!-- CS0436: Allow ObsoleteAttribute to be redefined internally -->
22+
<NoWarn>$(NoWarn);CS0436</NoWarn>
23+
</PropertyGroup>
24+
25+
<ItemGroup>
26+
<Compile Include="..\Common\Experimentals.cs" Link="Experimentals.cs" />
27+
</ItemGroup>
28+
29+
<!-- Exclude polyfills inherited from Directory.Build.props; this package only needs the ExperimentalAttribute polyfill. -->
30+
<ItemGroup>
31+
<Compile Remove="..\Common\Polyfills\**\*.cs" />
32+
<Compile Include="..\Common\Polyfills\System\Diagnostics\CodeAnalysis\ExperimentalAttribute.cs"
33+
Link="Polyfills\ExperimentalAttribute.cs" />
34+
</ItemGroup>
35+
36+
<ItemGroup>
37+
<ProjectReference Include="..\ModelContextProtocol.Core\ModelContextProtocol.Core.csproj" />
38+
</ItemGroup>
39+
40+
<ItemGroup>
41+
<None Include="..\PACKAGE.md" Pack="true" PackagePath="\README.md" />
42+
</ItemGroup>
43+
44+
</Project>
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using ModelContextProtocol.Protocol;
2+
using System.Diagnostics.CodeAnalysis;
3+
using System.Text.Json;
4+
using System.Text.Json.Serialization.Metadata;
5+
6+
namespace ModelContextProtocol.Server;
7+
8+
/// <summary>
9+
/// Provides constants and helper methods for building MCP Apps-enabled servers.
10+
/// </summary>
11+
/// <remarks>
12+
/// <para>
13+
/// MCP Apps is an extension to the Model Context Protocol that enables MCP servers to deliver
14+
/// interactive user interfaces — dashboards, forms, visualizations, and more — directly inside
15+
/// conversational AI clients.
16+
/// </para>
17+
/// <para>
18+
/// Use the constants in this class when populating the <c>extensions</c> capability and the
19+
/// <c>_meta</c> field of tools and resources. Use <see cref="GetUiCapability"/> to check whether
20+
/// the connected client supports the MCP Apps extension.
21+
/// </para>
22+
/// </remarks>
23+
public static class McpApps
24+
{
25+
/// <summary>
26+
/// The MIME type used for MCP App HTML resources.
27+
/// </summary>
28+
/// <remarks>
29+
/// This MIME type should be used when registering UI resources with
30+
/// <c>text/html;profile=mcp-app</c> to indicate they are MCP App resources.
31+
/// </remarks>
32+
public const string ResourceMimeType = "text/html;profile=mcp-app";
33+
34+
/// <summary>
35+
/// The extension identifier used for MCP Apps capability negotiation.
36+
/// </summary>
37+
/// <remarks>
38+
/// This key is used in the <see cref="ClientCapabilities.Extensions"/> and
39+
/// <see cref="ServerCapabilities.Extensions"/> dictionaries to advertise support for
40+
/// the MCP Apps extension.
41+
/// </remarks>
42+
public const string ExtensionId = "io.modelcontextprotocol/ui";
43+
44+
/// <summary>
45+
/// Gets the MCP Apps client capability, if advertised by the connected client.
46+
/// </summary>
47+
/// <param name="capabilities">The client capabilities received during the MCP initialize handshake.</param>
48+
/// <returns>
49+
/// A <see cref="McpUiClientCapabilities"/> instance if the client advertises support for the MCP Apps extension;
50+
/// otherwise, <see langword="null"/>.
51+
/// </returns>
52+
/// <remarks>
53+
/// Use this method to determine whether the connected client supports the MCP Apps extension
54+
/// and to read the client's supported MIME types.
55+
/// </remarks>
56+
[Experimental(Experimentals.Apps_DiagnosticId, UrlFormat = Experimentals.Apps_Url)]
57+
public static McpUiClientCapabilities? GetUiCapability(ClientCapabilities? capabilities)
58+
{
59+
if (capabilities?.Extensions is not { } extensions ||
60+
!extensions.TryGetValue(ExtensionId, out var value))
61+
{
62+
return null;
63+
}
64+
65+
if (value is JsonElement element)
66+
{
67+
return element.ValueKind == JsonValueKind.Null ? null :
68+
(McpUiClientCapabilities?)JsonSerializer.Deserialize(
69+
element,
70+
(JsonTypeInfo)McpJsonUtilities.DefaultOptions.GetTypeInfo(typeof(McpUiClientCapabilities)));
71+
}
72+
73+
return null;
74+
}
75+
}

0 commit comments

Comments
 (0)