Skip to content

Commit 89ced6b

Browse files
liadyclaude
andcommitted
spec: add Dynamic View Content via embedded resources
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>
1 parent cf87f2a commit 89ced6b

1 file changed

Lines changed: 139 additions & 2 deletions

File tree

specification/draft/apps.mdx

Lines changed: 139 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,18 @@ interface UIResourceMeta {
225225
* - omitted: host decides border
226226
*/
227227
prefersBorder?: boolean,
228+
/**
229+
* MIME types of dynamic content payloads this View renders
230+
*
231+
* When present, the View acts as a renderer for typed payloads returned
232+
* by its associated tools as embedded resources (see Data Passing:
233+
* Dynamic View Content). Does not affect the resource's own `mimeType`,
234+
* which remains `text/html;profile=mcp-app`.
235+
*
236+
* @example
237+
* ["application/a2ui+json"]
238+
*/
239+
contentMimeTypes?: string[],
228240
}
229241
```
230242

@@ -254,6 +266,7 @@ The resource content is returned via `resources/read`:
254266
};
255267
domain?: string;
256268
prefersBorder?: boolean;
269+
contentMimeTypes?: string[]; // Dynamic content payload types this View renders
257270
};
258271
};
259272
}];
@@ -262,7 +275,7 @@ The resource content is returned via `resources/read`:
262275

263276
#### Metadata Location
264277

265-
`UIResourceMeta` (CSP, permissions, domain, prefersBorder) may be provided on either or both:
278+
`UIResourceMeta` (CSP, permissions, domain, prefersBorder, contentMimeTypes) may be provided on either or both:
266279

267280
- **`resources/list`:** On the resource entry's `_meta.ui` field. Useful as a static default that hosts can review at connection time.
268281
- **`resources/read`:** On each content item's `_meta.ui` field. Useful for per-response overrides or dynamic metadata that is only known at read time.
@@ -1377,6 +1390,8 @@ View behavior (optional):
13771390

13781391
Host MUST send this notification when tool execution completes (if the View is displayed during tool execution).
13791392

1393+
When the host has negotiated dynamic content support (see Client\<\>Server Capability Negotiation), the delivered `CallToolResult` MUST include, unmodified, any embedded resource content blocks marked with `_meta.ui.content` (see Data Passing: Dynamic View Content). Hosts MAY omit unmarked content blocks per their existing policies.
1394+
13801395
`ui/notifications/tool-cancelled` - Tool execution was cancelled
13811396

13821397
```typescript
@@ -1718,6 +1733,7 @@ The tool's execution result:
17181733

17191734
- `content`: Text representation for model context and text-only hosts
17201735
- `structuredContent`: Structured data optimized for UI rendering (not added to model context)
1736+
- Marked embedded resources: Typed dynamic content payloads for the View (not added to model context; see Dynamic View Content below)
17211737
- `_meta`: Additional metadata (timestamps, version info, etc.) not intended for model context
17221738

17231739
#### 3. Interactive Updates
@@ -1735,6 +1751,90 @@ This pattern enables interactive, self-updating views.
17351751

17361752
Note: Tools with `visibility: ["app"]` are hidden from the agent but remain callable by apps via `tools/call`. This enables UI-only interactions (refresh buttons, form submissions) without exposing implementation details to the model. See the Visibility section under Resource Discovery for details.
17371753

1754+
#### 4. Dynamic View Content (via embedded resources)
1755+
1756+
Some UI systems are generative in nature: the server produces a declarative, typed UI description at tool-call time (a document, not code), and a generic predeclared View renders it. [A2UI](https://a2ui.org) (`application/a2ui+json`) is the primary example. `structuredContent` is a poor fit for these payloads: it is untyped (no MIME type), single-valued, bound to the tool's `outputSchema`, and offers no interoperability path for non-MCP-Apps hosts that natively render the payload format.
1757+
1758+
For these cases, tool results MAY carry dynamic content payloads as standard MCP embedded resource content blocks, marked for View consumption:
1759+
1760+
```typescript
1761+
interface McpUiContentBlockMeta {
1762+
/**
1763+
* URI of the ui:// renderer this payload targets.
1764+
*
1765+
* OPTIONAL. If omitted, the payload targets the calling tool's
1766+
* `_meta.ui.resourceUri`. Explicit targeting supports future
1767+
* multi-view tool results.
1768+
*/
1769+
rendererUri?: string;
1770+
}
1771+
1772+
// Embedded resource content block within CallToolResult.content:
1773+
{
1774+
type: "resource",
1775+
resource: {
1776+
uri: string, // Ephemeral payload identifier (any scheme except ui://)
1777+
mimeType: string, // MUST match a declared contentMimeTypes entry
1778+
text?: string, // Payload as string
1779+
blob?: string // OR base64-encoded payload
1780+
},
1781+
_meta: {
1782+
ui: {
1783+
content: McpUiContentBlockMeta
1784+
}
1785+
}
1786+
}
1787+
```
1788+
1789+
**Requirements:**
1790+
1791+
- The target View MUST declare the payload's `mimeType` in its `contentMimeTypes` (see UI Resource Format)
1792+
- Payload URIs are ephemeral identifiers per RFC 3986; servers are NOT required to serve them via `resources/read`. The `ui://` scheme MUST NOT be used for payload URIs; it remains reserved for renderable UI resources
1793+
- A tool result MAY contain multiple marked payloads; Views SHOULD process them in array order
1794+
- Marked payloads are presentation data. Consistent with `structuredContent`, servers SHOULD still return a meaningful text `content` block for model context and text-only hosts
1795+
1796+
**Host behavior** (when dynamic content support is negotiated, for tools linked to a View declaring `contentMimeTypes`):
1797+
1798+
- Host MUST deliver marked embedded resource blocks, unmodified, in the `CallToolResult` sent via `ui/notifications/tool-result`
1799+
- Host MUST deliver marked embedded resource blocks, unmodified, in `tools/call` responses returned to Views during the interactive phase
1800+
- Host SHOULD NOT add marked payloads to model context (they are presentation data, analogous to `structuredContent`). Host MAY note their presence to the model
1801+
- Host MAY drop marked blocks whose `mimeType` is not declared in the target View's `contentMimeTypes`, and SHOULD log such drops
1802+
- Host MAY enforce payload size limits; when dropping a payload, Host SHOULD deliver the remainder of the result rather than failing
1803+
1804+
No new messages are introduced: the existing `ui/notifications/tool-result` notification and proxied `tools/call` responses are the delivery channel. The View extracts marked payloads from the delivered result and renders them; interactive updates return new payloads through the same loop.
1805+
1806+
**Example (A2UI renderer):**
1807+
1808+
```json
1809+
// Renderer resource (predeclared, prefetchable, reviewable)
1810+
{
1811+
"uri": "ui://a2ui-server/renderer",
1812+
"name": "a2ui_renderer",
1813+
"mimeType": "text/html;profile=mcp-app",
1814+
"_meta": {
1815+
"ui": { "contentMimeTypes": ["application/a2ui+json"] }
1816+
}
1817+
}
1818+
1819+
// Tool result
1820+
{
1821+
"content": [
1822+
{ "type": "text", "text": "Found 3 flights TLV→SFO. Best: UA954, $1,240." },
1823+
{
1824+
"type": "resource",
1825+
"resource": {
1826+
"uri": "a2ui://a2ui-server/surfaces/flight-search-8f3a",
1827+
"mimeType": "application/a2ui+json",
1828+
"text": "{\"beginRendering\":{...}}"
1829+
},
1830+
"_meta": { "ui": { "content": {} } }
1831+
}
1832+
]
1833+
}
1834+
```
1835+
1836+
The renderer translates payload-level events into `tools/call` requests (typically to tools with `visibility: ["app"]`); responses carry new marked payloads (e.g., incremental A2UI updates), which the renderer applies. A host that natively renders a payload format MAY instead advertise that format in the top-level `mimeTypes` extension setting, render marked payloads directly, and skip instantiating the HTML renderer — allowing servers to serve a single tool response to MCP Apps hosts, native hosts, and text-only hosts.
1837+
17381838
### App-Provided Tools
17391839

17401840
Apps can register their own tools that hosts and agents can call, making apps **introspectable and accessible** to the model. This complements the existing capability where apps call server tools (via host proxy).
@@ -2191,7 +2291,8 @@ Clients advertise MCP Apps support in the initialize request using the extension
21912291
"capabilities": {
21922292
"extensions": {
21932293
"io.modelcontextprotocol/ui": {
2194-
"mimeTypes": ["text/html;profile=mcp-app"]
2294+
"mimeTypes": ["text/html;profile=mcp-app"],
2295+
"contentMimeTypes": ["application/a2ui+json"]
21952296
}
21962297
}
21972298
},
@@ -2206,6 +2307,7 @@ Clients advertise MCP Apps support in the initialize request using the extension
22062307
**Extension Settings:**
22072308
22082309
- `mimeTypes`: Array of supported content types (REQUIRED, e.g., `["text/html;profile=mcp-app"]`)
2310+
- `contentMimeTypes`: Array of dynamic content payload types the host will forward to Views per Data Passing: Dynamic View Content (OPTIONAL). Hosts MAY advertise `["*"]` to indicate they forward any payload type declared by a View's `contentMimeTypes` — hosts never need to interpret payloads, only route them into the sandboxed View. Servers SHOULD check this setting before registering renderer-pattern tools and SHOULD degrade to text-only or `structuredContent`-driven variants when absent. A host that natively renders a payload format (without an HTML renderer) advertises that format in `mimeTypes` instead.
22092311
22102312
Future versions may add additional settings:
22112313
@@ -2473,6 +2575,25 @@ Apps are forward-deployed emanations of server tools, running in the client cont
24732575
24742576
See [Security Implications: App-Provided Tools Security](#5-app-provided-tools-security) for detailed considerations.
24752577
2578+
#### 7. Dynamic View Content via Embedded Resources
2579+
2580+
**Decision:** Allow tool results to carry typed dynamic content payloads as marked embedded resources, delivered to the tool's predeclared View through existing channels.
2581+
2582+
**Rationale:**
2583+
2584+
- Enables generative UI formats (e.g., A2UI) where the server emits a declarative UI document at call time and a generic predeclared renderer interprets it
2585+
- Embedded resources are typed (MIME), multi-valued, URI-addressed, and part of core MCP — unlike `structuredContent`, which remains the channel for template-bound data
2586+
- Non-MCP-Apps hosts that natively render a payload format can consume the same tool response by reading the marked embedded resource directly, enabling one server response across host classes
2587+
- Requires no new messages: delivery rides on `ui/notifications/tool-result` and proxied `tools/call` responses
2588+
2589+
This does not revisit 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 as `structuredContent`, extended with typed, self-describing documents.
2590+
2591+
**Alternatives considered:**
2592+
2593+
- **`structuredContent`:** Untyped, single-valued, `outputSchema`-bound, and invisible to native payload-format hosts
2594+
- **Dedicated `ui/notifications/content` message:** Duplicates delivery semantics, complicates ordering relative to `tool-result`, and grows host surface area for no expressive gain. Server-push content injection outside tool calls can be added later as a separate message
2595+
- **MIME-type inference without a marker:** Servers legitimately return embedded resources for other purposes (files, records); the explicit `_meta.ui.content` marker makes routing intent unambiguous and provides a forward-compatible `rendererUri` slot for multi-view results
2596+
24762597
### Backward Compatibility
24772598

24782599
The proposal builds on the existing core protocol. There are no incompatibilities.
@@ -2642,6 +2763,20 @@ App tools MUST be tied to the app's lifecycle:
26422763
- Hosts MUST NOT persist app tool registrations across sessions
26432764
- Calling a tool from a closed app MUST return an error
26442765

2766+
#### 6. Dynamic Content Payloads
2767+
2768+
Dynamic View Content payloads are data, not code: they are interpreted by a renderer that is itself sandboxed, CSP-constrained, and reviewable under this specification's existing model. Declarative formats narrow the attack surface relative to arbitrary HTML precisely because the executable component (the renderer) is static and predeclared.
2769+
2770+
**View behavior:**
2771+
2772+
- Renderers MUST treat payloads as untrusted input (no `eval` or direct `innerHTML` of payload-derived strings)
2773+
- Payload-referenced network and media origins remain subject to the renderer's declared CSP; a payload cannot expand the View's network reach
2774+
2775+
**Host behavior:**
2776+
2777+
- Payloads flow through auditable JSON-RPC with declared MIME types; hosts MAY log, size-limit, and type-filter them
2778+
- Marked payloads are excluded from model context by default, limiting prompt injection surface
2779+
26452780
### Other risks
26462781
26472782
- **Social engineering:** UI can still display misleading content. Hosts should clearly indicate sandboxed UI boundaries.
@@ -2651,3 +2786,5 @@ App tools MUST be tied to the app's lifecycle:
26512786
26522787
- The resource prefix `ui://` will be reserved for MCP Apps
26532788
- The label `io.modelcontextprotocol/ui` is reserved
2789+
- The `_meta.ui.content` key on tool result content blocks is reserved for Dynamic View Content
2790+
- The `contentMimeTypes` field is reserved in `UIResourceMeta` and in the `io.modelcontextprotocol/ui` extension settings

0 commit comments

Comments
 (0)