ADR-0033 — MCP Apps module and the io.modelcontextprotocol/ui capability¶
- Status: Accepted
- Date: 2026-07-31
Context¶
MCP Apps (SEP-1865, modelcontextprotocol/ext-apps) was declared
unimplemented at the 2026-07-28 revision boundary
(ADR-0022), on the
grounds that it looked like a presentation-layer concern — sandboxed
iframes, a postMessage JS bridge — far outside mocapi's tool/prompt/
resource handler model. Revisiting the spec shows that assessment was
too broad: the extension splits cleanly into a server surface
(declarative metadata — serve ui:// HTML resources, link tools to
their UI via _meta, declare the capability) and a host/in-iframe
JavaScript surface (the sandbox handshake, ui/initialize,
postMessage JSON-RPC, display-mode negotiation, app-registered
tools). Only the second half is out of mocapi's reach; the first half
is a shallow, additive, server-side extension that fits the existing
annotation-driven handler model exactly.
Two foundation seams landed ahead of this decision on
feat/extension-foundation-seams: meta-annotation-aware handler
discovery (ADR-0032)
lets a composed annotation register a handler without a bespoke
registration SPI, and ServerCapabilitiesCustomizer
(ADR-0031) lets an optional
module declare an extensions capability entry without core
enumerating it. Both were built with Apps (and Tasks) as the driving
use case, and the @McpAppResource pattern they enable is proven by a
passing end-to-end test, not merely assumed. What remains is deciding
whether to build the module at all, and on what surface.
Decision¶
Introduce an optional module, mocapi-apps, that implements the MCP
Apps server surface only, stopping at the language boundary
described above.
ui://resources. Authors declare an HTML resource with@McpAppResource(meta-annotated@McpResource, mimeType defaulted totext/html;profile=mcp-app,urialiased through via@AliasFor). It is served through the existingresources/list/resources/readpath — no new handler kind.- Tool↔UI linkage. A companion
@McpUi(resourceUri, visibility)annotation on an@McpToolmethod links the tool to its UI resource.visibility(model/app) is emitted as metadata only; mocapi does not enforce it (host-side concern, distinct from the auth-Guardvisibility ≡ invocationmodel of ADR-0012). - Capability declaration.
UiCapabilityCustomizer(aServerCapabilitiesCustomizer) unconditionally declarescapabilities.extensions["io.modelcontextprotocol/ui"] = { "mimeTypes": ["text/html;profile=mcp-app"] }whenevermocapi-appsis on the classpath. mocapi is stateless and has noinitializehandshake to gate on, so the capability and_meta.uiare always emitted; a non-Apps host simply ignores metadata it doesn't recognize (the spec's text-only fallback). - Out of scope, explicitly. The
postMessageJSON-RPC protocol,ui/initialize,ui/notifications/sandbox-proxy-ready/sandbox-resource-ready, display-mode negotiation, app-registered tools, andsampling/createMessageoverpostMessageare host/ in-iframe JavaScript. mocapi serves the author's HTML bytes content-agnostically; when the in-iframe app calls a server tool, it arrives at mocapi as an ordinarytools/callwith no Apps-specific server code involved.
Consequences¶
Authors get two ergonomic annotations, @McpAppResource and @McpUi,
and nothing else changes about how they write tools or resources — the
per-call data the UI renders rides the normal CallToolResult. Adding
mocapi-apps to the classpath is the only integration step; omitting
it leaves the core inert (no descriptor gains _meta, discovery
behaves identically for direct annotations). A non-Apps host is
unaffected: it sees an unrecognized extensions entry and unrecognized
_meta.ui fields, both of which the spec requires it to ignore.
Cost: mocapi now ships and maintains a module whose HTML-serving surface it cannot validate beyond "well-formed resource" — CSP/sandbox correctness and rendering are entirely host- and author-responsibility. This ADR does not attempt to police that.
Non-goals: no postMessage/iframe/JS-bridge code of any kind; no
call-time UiContext (the tool↔UI link is static descriptor metadata,
resolved before any invocation — see
ADR-0034 for the
mechanism); no app-registered tools or display-mode negotiation. If a
real use case for a per-response _meta.ui override appears (the
draft-only dynamic case), it is a follow-up, not an extension of this
decision.
This ADR flips the MCP Apps entry in ADR-0022 from declined to accepted-and-implemented.
Code anchors:
mocapi-apps/src/main/java/com/callibrity/mocapi/apps/UiCapabilityCustomizer.javamocapi-apps/src/main/java/com/callibrity/mocapi/apps/McpAppResource.javamocapi-apps/src/main/java/com/callibrity/mocapi/apps/McpUi.java