Skip to content

Feature Request: MCP Apps Support — Typed Metadata, Helpers, and Attribute Integration #1431

@mikekistler

Description

@mikekistler

MCP Apps Support for the C# SDK — Product Requirements Document

Background

MCP Apps is the first official extension to the Model Context Protocol, co-developed by Anthropic and OpenAI and released as an open standard in January 2026. It enables MCP servers to deliver interactive user interfaces — dashboards, forms, visualizations, and more — directly inside conversational AI clients such as Claude, ChatGPT, VS Code, and Goose.

The extension is specified in ext-apps/specification/2026-01-26/apps.mdx and uses existing MCP primitives (tools, resources, capabilities) augmented with:

  • ui:// URI scheme for UI resources
  • _meta.ui metadata on tools to associate them with UI resources
  • extensions capability negotiation via the "io.modelcontextprotocol/ui" key
  • text/html;profile=mcp-app MIME type for HTML UI resources
  • CSP configuration for sandboxed iframe security

The UI protocol itself (ui/initialize, ui/context, etc.) runs between the host (chat client iframe bridge) and the view (iframe app) — not through the standard MCP client/server channel. Therefore, the base SDKs primarily need to support capability negotiation, typed metadata, and convenience helpers for server authors.

Current State of SDK Support

TypeScript SDK (@modelcontextprotocol/sdk)

The TypeScript SDK provides the foundation, and a dedicated package @modelcontextprotocol/ext-apps delivers full MCP Apps support:

  • extensions field on ClientCapabilities and ServerCapabilities
  • Generic _meta on tool definitions
  • @modelcontextprotocol/ext-apps/server module with:
    • registerAppTool() — registers tools with normalized UI metadata
    • registerAppResource() — registers resources with default MCP Apps MIME type
    • getUiCapability() — reads MCP Apps capabilities from client
    • Typed interfaces: McpUiToolMeta, McpUiResourceMeta, McpUiResourceCsp, McpUiClientCapabilities
    • Constants: RESOURCE_MIME_TYPE, EXTENSION_ID, RESOURCE_URI_META_KEY

Python SDK (mcp)

  • extensions field exists on capabilities in _types.py
  • Generic _meta support on tool definitions
  • No dedicated MCP Apps helpers — servers must manually populate _meta.ui and serve ui:// resources

Java SDK

  • No explicit MCP Apps support
  • Open feature request: java-sdk#780 (status: waiting for triage)
  • The Spring AI mcp-annotations project is exploring ergonomic _meta attribute handling

C# SDK (ModelContextProtocol)

The C# SDK has the foundational primitives in place:

Feature Status Location
Extensions on ServerCapabilities ✅ Present IDictionary<string, object>?, marked [Experimental]
Extensions on ClientCapabilities ✅ Present Same as above
_meta on Tool ✅ Present JsonObject? Meta property
Resources capability ✅ Present Standard resource registration

A C# MCP server can support MCP Apps today by manually constructing the _meta JSON and serving ui:// resources. However, the experience is untyped, error-prone, and requires knowledge of the spec's internal structure.

Proposed Features

The goal is to add a convenience layer to the C# SDK — analogous to @modelcontextprotocol/ext-apps/server in TypeScript — that makes it easy for C# server authors to build MCP Apps.

F1: Constants

Define well-known constants used throughout the MCP Apps extension:

Constant Value Purpose
ResourceMimeType "text/html;profile=mcp-app" MIME type for MCP App HTML resources
ExtensionId "io.modelcontextprotocol/ui" Key in extensions for capability negotiation
ResourceUriMetaKey "ui/resourceUri" Legacy flat _meta key (deprecated but needed for backward compat)

F2: Typed Metadata Models

Provide strongly-typed C# classes for MCP Apps metadata, eliminating the need to construct raw JSON:

  • McpUiToolMeta — UI metadata for tools

    • ResourceUri (string?) — URI of the UI resource (e.g., "ui://weather/view.html")
    • Visibility (IList<McpUiToolVisibility>?) — Who can access the tool: "model", "app", or both (default)
  • McpUiResourceMeta — UI metadata for resources

    • Csp (McpUiResourceCsp?) — Content Security Policy configuration
    • Permissions (McpUiResourcePermissions?) — Sandbox permissions
    • Domain (string?) — Dedicated origin for CORS/OAuth
    • PrefersBorder (bool?) — Visual boundary preference
  • McpUiResourceCsp — CSP domain allowlists

    • ConnectDomains (IList<string>?) — Origins for fetch/XHR/WebSocket (connect-src)
    • ResourceDomains (IList<string>?) — Origins for scripts/styles/images/fonts
    • FrameDomains (IList<string>?) — Origins for nested iframes (frame-src)
    • BaseUris (IList<string>?) — Allowed base URIs (base-uri)
  • McpUiClientCapabilities — Client-advertised MCP Apps capabilities

    • MimeTypes (IList<string>?) — Supported MIME types (must include "text/html;profile=mcp-app")
  • McpUiToolVisibility — Enum or string constants: "model", "app"

F3: GetUiCapability() Helper

A helper method to extract MCP Apps capability settings from client capabilities:

public static McpUiClientCapabilities? GetUiCapability(ClientCapabilities? capabilities)
  • Reads capabilities.Extensions["io.modelcontextprotocol/ui"]
  • Deserializes to McpUiClientCapabilities
  • Returns null if extensions are missing or MCP Apps is not advertised

This enables server authors to conditionally register app-enhanced vs. text-only tools based on client support.

F4: Tool Registration Helper

A convenience method for registering tools with UI metadata:

public static void RegisterAppTool(McpServer server, string name, McpUiAppToolConfig config, ToolCallback callback)

Key behaviors:

  • Accepts a typed McpUiAppToolConfig that includes McpUiToolMeta
  • Normalizes metadata: if _meta.ui.resourceUri is set, also populates the legacy _meta["ui/resourceUri"] key (and vice versa) for compatibility with older hosts
  • Delegates to the standard RegisterTool method

F5: Resource Registration Helper

A convenience method for registering UI resources:

public static void RegisterAppResource(McpServer server, string name, string uri, McpUiAppResourceConfig config, ReadResourceCallback callback)

Key behaviors:

  • Defaults the MIME type to "text/html;profile=mcp-app" (can be overridden)
  • Accepts optional McpUiResourceMeta for CSP, permissions, domain configuration
  • Delegates to the standard RegisterResource method

F6: Attribute-Based UI Metadata (McpAppUiAttribute)

The C# SDK's idiomatic tool declaration pattern uses [McpServerTool] with attributes:

[McpServerTool]
[Description("Get current weather for a location")]
public string GetWeather(string location) => ...;

Today, UI metadata can technically be added via the generic [McpMeta] attribute with raw JSON:

[McpServerTool]
[McpMeta("ui", JsonValue = """{"resourceUri": "ui://weather/view.html"}""")]
public string GetWeather(string location) => ...;

This works because McpMetaAttribute supports arbitrary JSON values and the CreateMetaFromAttributes pipeline in AIFunctionMcpServerTool adds them to Tool.Meta. However, it has significant drawbacks:

  • Not type-safe — the JSON is a raw string; typos in field names silently pass
  • Not discoverable — no IntelliSense for nested resourceUri, visibility, etc.
  • No backward-compat normalization — doesn't also set the legacy _meta["ui/resourceUri"] key
  • Verbose — requires knowledge of the spec's exact JSON structure

A dedicated attribute would provide a first-class experience:

[McpServerTool]
[McpAppUi(ResourceUri = "ui://weather/view.html")]
[Description("Get current weather for a location")]
public string GetWeather(string location) => ...;

McpAppUiAttribute properties:

Property Type Description
ResourceUri string URI of the UI resource (e.g., "ui://weather/view.html")
Visibility string[]? Who can access the tool: "model", "app", or both (default)

Implementation considerations:

  • The attribute should be processed in the existing CreateMetaFromAttributes pipeline (or alongside it) in AIFunctionMcpServerTool
  • When present, it should populate both _meta.ui.resourceUri and the legacy _meta["ui/resourceUri"] key for backward compatibility with older hosts
  • It should take precedence over any raw [McpMeta("ui", ...)] on the same method
  • Following the SDK's existing pattern, this could be marked [Experimental] initially, matching the Extensions property

This is analogous to how McpServerToolAttribute already has typed properties for Destructive, ReadOnly, Idempotent, OpenWorld, and TaskSupport — rather than requiring developers to construct ToolAnnotations or ToolExecution JSON manually.

F7: Programmatic UI Metadata on McpServerToolCreateOptions

For tools created programmatically via McpServerTool.Create(delegate, options), the McpServerToolCreateOptions class should also support typed UI metadata. Currently developers would need to manually construct the Meta JsonObject:

// Current: manual JSON construction
var tool = McpServerTool.Create(handler, new McpServerToolCreateOptions
{
    Meta = new JsonObject
    {
        ["ui"] = JsonSerializer.SerializeToNode(new { resourceUri = "ui://weather/view.html" }),
        ["ui/resourceUri"] = "ui://weather/view.html"  // legacy key — easy to forget
    }
});

A typed property would be cleaner:

// Proposed: typed property
var tool = McpServerTool.Create(handler, new McpServerToolCreateOptions
{
    AppUi = new McpUiToolMeta
    {
        ResourceUri = "ui://weather/view.html",
        Visibility = ["model", "app"]
    }
});

The McpServerToolCreateOptions.AppUi property would:

  • Merge into Meta during tool creation, populating both _meta.ui and _meta["ui/resourceUri"]
  • Be overridden by explicit Meta entries if both are set (consistent with how McpMetaAttribute doesn't overwrite existing properties)

Out of Scope

The following are not in scope for this work:

  • UI protocol implementation (ui/initialize, ui/context, ui/message, etc.) — this runs between host and view iframe, not through the MCP SDK
  • App class / AppBridge class — these are client-side JavaScript constructs for the iframe
  • React/Blazor hooks — UI framework integrations for the view side
  • PostMessageTransport — browser-specific iframe communication

These would belong in a separate C# host-side or view-side package if needed.

References

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