spec: Dynamic View Content via embedded resources#699
Draft
liady wants to merge 1 commit into
Draft
Conversation
Adds standardized support for typed dynamic content payloads (e.g. A2UI, application/a2ui+json) returned from tool calls as marked embedded resources, delivered by the host to the tool's predeclared renderer View: - contentMimeTypes renderer declaration on UIResourceMeta - _meta.ui.content marker (McpUiContentBlockMeta) on embedded resource content blocks in tool results - Normative host forwarding rules over ui/notifications/tool-result and proxied tools/call responses; payloads excluded from model context - contentMimeTypes extension setting for capability negotiation, with a native-rendering path via the top-level mimeTypes array - Rationale (decision #7), security mitigation (#6), and reservations Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@modelcontextprotocol/ext-apps
@modelcontextprotocol/server-basic-preact
@modelcontextprotocol/server-basic-react
@modelcontextprotocol/server-basic-solid
@modelcontextprotocol/server-basic-svelte
@modelcontextprotocol/server-basic-vanillajs
@modelcontextprotocol/server-basic-vue
@modelcontextprotocol/server-budget-allocator
@modelcontextprotocol/server-cohort-heatmap
@modelcontextprotocol/server-customer-segmentation
@modelcontextprotocol/server-debug
@modelcontextprotocol/server-lazy-auth
@modelcontextprotocol/server-map
@modelcontextprotocol/server-pdf
@modelcontextprotocol/server-scenario-modeler
@modelcontextprotocol/server-shadertoy
@modelcontextprotocol/server-sheet-music
@modelcontextprotocol/server-system-monitor
@modelcontextprotocol/server-threejs
@modelcontextprotocol/server-transcript
@modelcontextprotocol/server-video-resource
@modelcontextprotocol/server-wiki-explorer
commit: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
Spec-only PR. The SDK implementation (types, helpers, worked example) is stacked on top as a separate draft PR (#700) so spec discussion isn't entangled with code review.
Motivation
See issue #169 .
A growing class of UI systems is generative: the server produces a declarative, typed UI description at tool-call time - a document, not code - and a generic runtime renders it. A2UI (
application/a2ui+json) is the primary example, and there is active discussion about whether A2UI support should live inside the MCP Apps extension rather than as a parallel mechanism.MCP Apps today has no good channel for these payloads:
structuredContentis untyped (no MIME type), single-valued, bound to the tool'soutputSchema, and invisible to hosts that render the payload format natively.uri, amimeType, andtext/blobcontent - but there is no standardized contract for how MCP Apps hosts treat them: hosts today may strip them fromui/notifications/tool-resultor inject large presentation payloads into model context, and servers can't detect either.This PR adds that contract to
specification/draft/apps.mdx: Dynamic View Content - typed payloads returned from tool calls as marked embedded resources, forwarded by the host to the tool's predeclared renderer view.The proposal
Zero new messages. Delivery rides entirely on the existing
ui/notifications/tool-resultnotification and proxiedtools/callresponses. The proposal reduces to four primitives:contentMimeTypes?: string[]onUIResourceMeta: the view declares which payload MIME types it renders. Participates in the existing list-vs-read metadata precedence rules._meta.ui.content(McpUiContentBlockMeta, with an optionalrendererUrifor future multi-view results) on embedded resource content blocks inCallToolResult.content.tool-resultnotifications and in proxiedtools/callresponses); SHOULD NOT be added to model context; MAY be type-filtered against the renderer's declaredcontentMimeTypesand size-limited.contentMimeTypesextension setting; hosts MAY advertise["*"]to forward any declared payload type opaquely (hosts never interpret payloads, only route them into the sandboxed view). Servers check it and degrade to text-only/structuredContentvariants when absent.sequenceDiagram autonumber actor User participant View as Renderer View (ui:// resource) participant Host as Host (AppBridge) participant Server as MCP Server Note over Host,Server: initialize - extensions["io.modelcontextprotocol/ui"]:<br/>{ mimeTypes: ["text/html#59;profile=mcp-app"], contentMimeTypes: ["application/a2ui+json"] } Host->>Server: resources/read ui://server/renderer Server-->>Host: HTML renderer, _meta.ui.contentMimeTypes: ["application/a2ui+json"] Note over Host: Renderer is predeclared, prefetchable,<br/>reviewable - unchanged from SEP-1865 Host->>Server: tools/call search-flights (agent-initiated) Server-->>Host: CallToolResult: text fallback +<br/>embedded resource (application/a2ui+json, _meta.ui.content) Host->>View: ui/notifications/tool-result - marked blocks forwarded unmodified Note over Host: Marked payloads are NOT added to model context View->>View: extract marked payloads, render surface User->>View: clicks a button in the rendered surface View->>Host: tools/call select-flight (visibility: ["app"]) Host->>Server: proxied tools/call Server-->>Host: new marked payload (incremental update) Host-->>View: tools/call response - marked blocks forwarded unmodified View->>View: apply update (interactive loop)Relationship to SEP-1865's rejection of embedded resources
This deliberately does not revisit design decision #1 (Predeclared Resources vs. Inline Embedding): the renderer remains a predeclared, prefetchable, reviewable
ui://resource. Embedded resources here carry only data payloads consumed by that renderer - the same template/data split the spec already establishes withstructuredContent, extended to typed, multi-valued, self-describing documents.One server response, three host classes
The interop payoff of using core-MCP embedded resources (rather than an Apps-private channel) is that a server can serve a single tool response to every kind of host:
mimeTypes: ["text/html;profile=mcp-app"],contentMimeTypes: ["application/a2ui+json"]ui://renderer, forwards payloads to itmimeTypes: [..., "application/a2ui+json"]contentfallbackThis directly addresses the "A2UI-over-MCP vs. A2UI-over-MCP-Apps" question: both are the same server response - the difference is only which capability the host advertises. A2UI-specific conventions (payload semantics,
userAction/ tool-call mapping) can layer on top of this generic mechanism as SDK/renderer guidance rather than spec text.Design decisions (rejected alternatives in the Rationale section)
_meta.ui.contentmarker rather than MIME inference: servers legitimately return embedded resources for other purposes (files, records for the user); the marker makes routing intent unambiguous, and itsrendererUrislot supports future multi-view tool results (already listed under SEP-1865's future considerations).contentMimeTypesdeclared on the resource, not the tool: the renderer owns the parsing contract, multiple tools share it, and the declaration is reviewable at connection time alongside CSP.McpUiToolMetastays untouched.ui/notifications/contentmessage: it would duplicate delivery semantics, complicate ordering relative totool-result, and grow host surface area for no expressive gain. Server-push content injection outside tool calls can be added later without conflicting with this design.resources/read, andui://remains reserved for renderable UI resources.Security
Payloads are data, not code: they are interpreted by a renderer that is itself sandboxed, CSP-constrained, and reviewable under the existing model. The new Security section adds: renderers MUST treat payloads as untrusted input (no
eval/innerHTMLof payload-derived strings); payload-referenced origins remain subject to the renderer's declared CSP; payloads flow through auditable JSON-RPC with declared MIME types (hosts may log, size-limit, type-filter); and marked payloads are excluded from model context by default - strictly safer than today's ambiguity around embedded resources in results.Backward compatibility
Fully additive. Hosts that don't advertise
contentMimeTypesbehave exactly as today; servers detect this and degrade. No existing messages, tools, or resources change. New reservations: the_meta.ui.contentkey on tool result content blocks, and thecontentMimeTypesfield inUIResourceMetaand in the extension settings.Changes
One file:
specification/draft/apps.mdx-UIResourceMeta.contentMimeTypes+ mirror in theresources/readresponse shape + the Metadata Location field listui/notifications/tool-resultcontentMimeTypesextension setting under Capability Negotiation_meta.ui.content,contentMimeTypes🤖 Generated with Claude Code