Add Server-Side MCP Apps Helpers per SEP-1865#441
Open
koic wants to merge 1 commit into
Open
Conversation
## Motivation and Context SEP-1865 "MCP Apps - Interactive User Interfaces for MCP" is Final on the Extensions Track: (modelcontextprotocol/modelcontextprotocol#1865) an optional extension, negotiated through the SEP-2133 `capabilities.extensions` mechanism, that lets a server ship HTML user interfaces which the host renders for tool results. The normative spec lives in the ext-apps repository (`specification/2026-01-26/apps.mdx`). The Python SDK implements it in-SDK (python-sdk#3003, the `Apps` extension); the TypeScript ecosystem ships it as the separate `@modelcontextprotocol/ext-apps` package on top of the core SDK's SEP-2133 support. Server-side support is a thin convention layer over primitives this SDK already has (SEP-2133 extension capabilities, `_meta` on tools and resources, free-form resource URIs and MIME types). New `MCP::Apps` module: - Wire vocabulary constants shared with the reference implementations: `EXTENSION_ID` (`"io.modelcontextprotocol/ui"` - note `/ui`, not `/apps`), `RESOURCE_MIME_TYPE` (`"text/html;profile=mcp-app"`), `URI_SCHEME` (`"ui://"`), and the legacy flat meta key `RESOURCE_URI_META_KEY` (`"ui/resourceUri"`). - `Apps.capability(mime_types:)` builds the `capabilities.extensions` fragment for `support_extensions` on the server or `connect` on the client. - `Apps.ui_resource(uri:, name:, ...)` builds an `MCP::Resource` for a UI template, enforcing the spec's MUSTs (a `ui://` URI; the parameterized HTML MIME type by default). - `Apps.tool_meta(resource_uri:, visibility:, meta:, legacy:)` builds the `_meta.ui.resourceUri` linkage (optionally also emitting the legacy flat alias, matching the reference server helper that keeps both shapes in sync), merged non-destructively into caller-supplied meta. - `Apps.client_supports?(client_capabilities, mime_type:)` gates the text-only fallback the extension requires for clients that did not declare the capability (symbol/string tolerant). Everything else the extension defines (the sandboxed iframe, the `ui/*` postMessage bridge methods, consent for UI-initiated calls) is HOST responsibility and out of scope for a server SDK; a server only ever receives ordinary `resources/read` and `tools/call` requests. The README section states this boundary. Resolves modelcontextprotocol#376. ## How Has This Been Tested? New `test/mcp/apps_test.rb` pins the wire vocabulary and covers the capability fragment, `ui_resource` scheme validation and MIME default, `tool_meta` non-destructive merging, visibility, the legacy alias, and `client_supports?` across symbol/string keys, mimeTypes narrowing, and missing declarations. New integration tests in `test/mcp/server_test.rb` drive a full server: the `initialize` result advertises the extension, tools/list serializes `_meta.ui.resourceUri`, `resources/read` serves the HTML template, and `client_supports?` distinguishes hosts with and without the declared extension. `bundle exec rake` (tests, RuboCop, and conformance baseline) passes. Also verified end-to-end over stdio: a client connecting with the extension declared negotiates the capability, discovers the tool linkage via `_meta.ui.resourceUri`, and reads the `ui://` template as `text/html;profile=mcp-app` content. ## Breaking Changes None. Purely additive helper module; no existing code paths change.
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.
Motivation and Context
SEP-1865 "MCP Apps - Interactive User Interfaces for MCP" (modelcontextprotocol/modelcontextprotocol#1865) is Final on the Extensions Track: an optional extension, negotiated through the SEP-2133
capabilities.extensionsmechanism, that lets a server ship HTML user interfaces which the host renders for tool results. The normative spec lives in the ext-apps repository (specification/2026-01-26/apps.mdx). The Python SDK implements it in-SDK (python-sdk#3003, theAppsextension); the TypeScript ecosystem ships it as the separate@modelcontextprotocol/ext-appspackage on top of the core SDK's SEP-2133 support.Server-side support is a thin convention layer over primitives this SDK already has (SEP-2133 extension capabilities,
_metaon tools and resources, free-form resource URIs and MIME types). NewMCP::Appsmodule:EXTENSION_ID("io.modelcontextprotocol/ui"- note/ui, not/apps),RESOURCE_MIME_TYPE("text/html;profile=mcp-app"),URI_SCHEME("ui://"), and the legacy flat meta keyRESOURCE_URI_META_KEY("ui/resourceUri").Apps.capability(mime_types:)builds thecapabilities.extensionsfragment forsupport_extensionson the server orconnecton the client.Apps.ui_resource(uri:, name:, ...)builds anMCP::Resourcefor a UI template, enforcing the spec's MUSTs (aui://URI; the parameterized HTML MIME type by default).Apps.tool_meta(resource_uri:, visibility:, meta:, legacy:)builds the_meta.ui.resourceUrilinkage (optionally also emitting the legacy flat alias, matching the reference server helper that keeps both shapes in sync), merged non-destructively into caller-supplied meta.Apps.client_supports?(client_capabilities, mime_type:)gates the text-only fallback the extension requires for clients that did not declare the capability (symbol/string tolerant).Everything else the extension defines (the sandboxed iframe, the
ui/*postMessage bridge methods, consent for UI-initiated calls) is HOST responsibility and out of scope for a server SDK; a server only ever receives ordinaryresources/readandtools/callrequests. The README section states this boundary.Resolves #376.
How Has This Been Tested?
New
test/mcp/apps_test.rbpins the wire vocabulary and covers the capability fragment,ui_resourcescheme validation and MIME default,tool_metanon-destructive merging, visibility, the legacy alias, andclient_supports?across symbol/string keys, mimeTypes narrowing, and missing declarations.New integration tests in
test/mcp/server_test.rbdrive a full server: theinitializeresult advertises the extension, tools/list serializes_meta.ui.resourceUri,resources/readserves the HTML template, andclient_supports?distinguishes hosts with and without the declared extension.bundle exec rake(tests, RuboCop, and conformance baseline) passes. Also verified end-to-end over stdio: a client connecting with the extension declared negotiates the capability, discovers the tool linkage via_meta.ui.resourceUri, and reads theui://template astext/html;profile=mcp-appcontent.Breaking Changes
None. Purely additive helper module; no existing code paths change.
Types of changes
Checklist