apps
MCP Apps extension (io.modelcontextprotocol/ui).
MCP Apps lets a tool carry a reference to an interactive UI: the tool's
_meta.ui.resourceUri points at a ui:// resource (an HTML document served
with the text/html;profile=mcp-app MIME type) that the host renders in a
sandboxed iframe. See https://modelcontextprotocol.io/specification/draft/extensions/apps
and the ext-apps spec for the wire format, and SEP-2133 for the extension framework.
This is a self-contained, additive Extension: it contributes tools and
resources and advertises the capability, but does not intercept any core method.
A server opts in by passing an Apps instance to MCPServer(extensions=[...]).
apps = Apps()
@apps.tool(resource_uri="ui://clock/app.html", description="Current time")
def get_time(ctx: Context) -> str:
return datetime.now(timezone.utc).isoformat()
apps.add_html_resource("ui://clock/app.html", CLOCK_HTML)
mcp = MCPServer("clock", extensions=[apps])
Per SEP-2133, an extension MUST degrade gracefully: a UI-enabled tool should
still return meaningful text for clients that did not negotiate Apps. Use
client_supports_apps(ctx) to branch on the client's advertised support. (The SDK
keeps Apps in-core under mcp.server.apps rather than a separate package; the
TypeScript and C# SDKs ship it as a standalone package.)
EXTENSION_ID
module-attribute
EXTENSION_ID = 'io.modelcontextprotocol/ui'
The MCP Apps extension identifier (the shipped TS/C# constant).
APP_MIME_TYPE
module-attribute
APP_MIME_TYPE = 'text/html;profile=mcp-app'
MIME type for a ui:// app resource.
Visibility
module-attribute
Visibility = Literal['model', 'app']
Where a UI-bound tool is surfaced (_meta.ui.visibility).
ResourcePermissions
Bases: BaseModel
Iframe permissions a ui:// resource requests (_meta.ui.permissions).
Source code in src/mcp/server/apps.py
55 56 57 58 59 60 61 62 63 | |
ResourceCsp
Bases: BaseModel
Content-Security-Policy domains for a ui:// resource (_meta.ui.csp).
Source code in src/mcp/server/apps.py
66 67 68 69 70 71 72 73 74 | |
Apps
Bases: Extension
The MCP Apps extension: bind tools to ui:// UI resources.
Register UI-bound tools with @apps.tool(resource_uri=...) and their HTML
with add_html_resource(...), then pass the instance to
MCPServer(extensions=[apps]).
Source code in src/mcp/server/apps.py
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | |
tool
tool(
*,
resource_uri: str,
visibility: Sequence[Visibility] | None = None,
meta: dict[str, Any] | None = None,
**tool_kwargs: Any
) -> Callable[[_CallableT], _CallableT]
Decorator registering a tool bound to a ui:// resource.
Stamps _meta.ui.resourceUri (and _meta.ui.visibility when given) on the
tool. tool_kwargs are forwarded to MCPServer.add_tool (name, title,
description, annotations, ...); pass meta= to merge extra _meta keys
alongside the ui entry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
resource_uri
|
str
|
The |
required |
visibility
|
Sequence[Visibility] | None
|
Where the tool is surfaced ( |
None
|
meta
|
dict[str, Any] | None
|
Additional |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/mcp/server/apps.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | |
add_html_resource
add_html_resource(
uri: str,
html: str,
*,
name: str | None = None,
title: str | None = None,
description: str | None = None,
csp: ResourceCsp | None = None,
permissions: ResourcePermissions | None = None,
domain: str | None = None,
prefers_border: bool | None = None
) -> None
Register a ui:// HTML resource served as text/html;profile=mcp-app.
csp, permissions, domain, and prefers_border populate the
resource's _meta.ui per the ext-apps spec.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
uri
|
str
|
The |
required |
html
|
str
|
The HTML document the host renders. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/mcp/server/apps.py
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
add_resource
add_resource(resource: Resource) -> None
Register a pre-built ui:// resource.
The escape hatch for resources add_html_resource cannot express (e.g. a
FileResource serving HTML from disk). A resource without an explicit
mime_type is served as text/html;profile=mcp-app — hosts will not
render a ui:// resource under any other MIME type, so an explicit
mismatch is rejected.
Raises:
| Type | Description |
|---|---|
ValueError
|
If the resource URI does not use the |
Source code in src/mcp/server/apps.py
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | |
tools
tools() -> Sequence[ToolBinding]
The bound tools.
Raises:
| Type | Description |
|---|---|
ValueError
|
If a tool's |
Source code in src/mcp/server/apps.py
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | |
client_supports_apps
Whether the connected client negotiated MCP Apps support.
Returns True only when the client advertised the extension AND listed the
text/html;profile=mcp-app MIME type in its settings, so a UI-enabled tool
can fall back to text-only output otherwise.
Source code in src/mcp/server/apps.py
217 218 219 220 221 222 223 224 225 226 227 228 229 230 | |