Module: MCP::Apps
Overview
Server-side helpers for the MCP Apps extension (SEP-1865, Extensions Track, Final):
interactive user interfaces delivered as ui:// HTML template resources that hosts
render for tool results. The extension is negotiated per SEP-2133 through
capabilities.extensions on both sides; a server declares EXTENSION_ID, registers
UI templates as ordinary resources, and links tools to templates via _meta.
Everything else defined by the extension (the sandboxed iframe, the ui/*
postMessage bridge methods, consent for UI-initiated calls) is HOST responsibility:
a server only ever receives ordinary resources/read and tools/call requests.
Because the extension is optional, a UI-enabled tool MUST still return a meaningful text-only result for clients that did not declare the capability; use #client_supports? to branch.
https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/2026-01-26/apps.mdx
Constant Summary collapse
- EXTENSION_ID =
Reverse-DNS extension identifier (note:
/ui, not/apps), shared wire vocabulary with the reference@modelcontextprotocol/ext-appspackage and the Python SDK'sAppsextension. "io.modelcontextprotocol/ui"- RESOURCE_MIME_TYPE =
UI template resources MUST use this parameterized MIME type.
"text/html;profile=mcp-app"- URI_SCHEME =
UI template resource URIs MUST use this scheme.
"ui://"- RESOURCE_URI_META_KEY =
Legacy flat
_metakey linking a tool to its template; the canonical shape is the nested_meta.ui.resourceUri. #tool_meta can emit both. "ui/resourceUri"
Instance Method Summary collapse
-
#capability(mime_types: [RESOURCE_MIME_TYPE]) ⇒ Object
The
capabilities.extensionsfragment advertising Apps support. -
#client_supports?(client_capabilities, mime_type: RESOURCE_MIME_TYPE) ⇒ Boolean
Whether the client declared Apps support for
mime_typein itscapabilities.extensions(symbol or string keys). -
#tool_meta(resource_uri:, visibility: nil, meta: nil, legacy: false) ⇒ Object
Builds the tool
_metalinking a tool to its UI template, merged non-destructively into caller-suppliedmeta. -
#ui_resource(uri:, name:, mime_type: RESOURCE_MIME_TYPE, **rest) ⇒ Object
Builds an
MCP::Resourcefor a UI template, enforcing the spec's MUSTs: aui://URI and thetext/html;profile=mcp-appMIME type by default.
Instance Method Details
#capability(mime_types: [RESOURCE_MIME_TYPE]) ⇒ Object
The capabilities.extensions fragment advertising Apps support. Pass to
MCP::Server::Capabilities.new(extensions: ...) or support_extensions(...),
or merge into a client's connect(capabilities: { extensions: ... }).
53 54 55 |
# File 'lib/mcp/apps.rb', line 53 def capability(mime_types: [RESOURCE_MIME_TYPE]) { EXTENSION_ID => { mimeTypes: mime_types } } end |
#client_supports?(client_capabilities, mime_type: RESOURCE_MIME_TYPE) ⇒ Boolean
Whether the client declared Apps support for mime_type in its capabilities.extensions
(symbol or string keys). UI-enabled tools use this to fall back to a text-only result for
clients without the extension.
87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/mcp/apps.rb', line 87 def client_supports?(client_capabilities, mime_type: RESOURCE_MIME_TYPE) extensions = read_key(client_capabilities, :extensions) declaration = read_key(extensions, EXTENSION_ID) return false unless declaration.is_a?(Hash) mime_types = read_key(declaration, :mimeTypes) # A declaration without mimeTypes advertises the extension without narrowing. return true if mime_types.nil? mime_types.is_a?(Array) && mime_types.include?(mime_type) end |
#tool_meta(resource_uri:, visibility: nil, meta: nil, legacy: false) ⇒ Object
Builds the tool _meta linking a tool to its UI template, merged non-destructively into
caller-supplied meta. visibility restricts who sees the tool (an array of "model" / "app").
legacy: true also writes the flat "ui/resourceUri" alias, matching the reference server helper
that keeps both key shapes in sync for older hosts.
71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/mcp/apps.rb', line 71 def (resource_uri:, visibility: nil, meta: nil, legacy: false) unless resource_uri.is_a?(String) && resource_uri.start_with?(URI_SCHEME) raise ArgumentError, "resource_uri must start with #{URI_SCHEME.inspect} (got #{resource_uri.inspect})" end ui_entry = { resourceUri: resource_uri } ui_entry[:visibility] = visibility if visibility merged = ( || {}).merge(ui: ui_entry) merged[RESOURCE_URI_META_KEY.to_sym] = resource_uri if legacy merged end |
#ui_resource(uri:, name:, mime_type: RESOURCE_MIME_TYPE, **rest) ⇒ Object
Builds an MCP::Resource for a UI template, enforcing the spec's MUSTs:
a ui:// URI and the text/html;profile=mcp-app MIME type by default.
59 60 61 62 63 64 65 |
# File 'lib/mcp/apps.rb', line 59 def ui_resource(uri:, name:, mime_type: RESOURCE_MIME_TYPE, **rest) unless uri.is_a?(String) && uri.start_with?(URI_SCHEME) raise ArgumentError, "MCP Apps template URIs must start with #{URI_SCHEME.inspect} (got #{uri.inspect})" end Resource.new(uri: uri, name: name, mime_type: mime_type, **rest) end |