effect/ai: support the MCP Apps extension (`io.modelcontextprotocol/ui`)
Los mantenedores suelen responder en 1 día
@effect-bot ya está trabajando en esto.
Desde el 5/11/2026.
Evaluación
Este issue todavía no se ha evaluado.
Descripción
Need
MCP Apps is the
stable MCP extension that lets a server give the human a visual for a tool call. A tool links to a ui:// HTML
resource. The host renders it in a sandboxed iframe, and the page (the spec calls it the View) talks to the host over
JSON-RPC on postMessage. A tool result serves two readers: structuredContent holds the data for the page and the
agent, and content holds the text for the model and for hosts without Apps. Claude (web and desktop), VS Code,
ChatGPT, Goose and others render MCP Apps.
An Effect MCP server can link a tool to a UI today only by hand: an untyped Tool.Meta record, a hand-written
extensions key, and a content effect that returns a raw ReadResourceResult. The page itself has no Effect
support. Concretely:
- No typed extension announcement,
ui://resource, resource_meta.ui(CSP, permissions, domain, border) or tool
_meta.ui(resourceUri,visibility).registerResourcehas no_metaoption. - No way to check whether the host supports Apps, other than reading
clientCapabilities.extensionsby hand. contentis always the JSON ofstructuredContent.McpServerignores a tool'stoModelOutput, so a tool
can't give the model a text summary while the page gets the data.- A handler can't see the JSON-RPC id of its own
tools/call.hostContext.toolInfo.idis the only standard key
a page has for the call that created it while that call is still running, so a server can't tie a page's polling
calls to the run they belong to. Handlers also can't seeMcp-Session-Id, the scope in which that id is unique
over sessionful HTTP.clientIdis assigned per HTTP POST, so it can't serve as that scope. McpClient.toolkitbinds tools markedvisibility: ["app"]for the model, though the spec says hosts MUST NOT
give them to the agent.McpClientcan't declare the extension.- Nothing for the page: the handshake, tool input and result, host context, calling server tools, display modes,
sizing and teardown.
The motivating consumer is the effect/test MCP server. Its tool results stay Schema'd data the agent acts on, and the
human gets a live view of a run, run reports and snapshot diffs in the host.
Prior art
- #6353 asks for first-class MCP Apps support: a
ui://resource kind and typedTool.Metakeys. This issue covers
that request. - Effect-TS/effect-smol#1440 added the
_metaandextensionspassthrough on MCP schemas, and
Effect-TS/effect-smol#1653 exposed the client capabilities to handlers. Both are what makes MCP Apps possible today. - @lloydrichards's examples (edu_mcp-apps,
interactivethings-data-viz-mcp-lab, and
the helpers posted on #6353) build MCP Apps on the currentMcpServer.makeUiResource,makeUiRenderTooland
makeUiAppToolare the shape the server half below makes first-class. The polling dashboard there is the pattern
the effect/test live view follows. - #8631 (Tasks) and the Skills extension set the pattern for extensions: a public module per extension, a server that
declares the extension once it is in use, andMcpClientoptions grouped underextensions. This proposal follows
it.
Alternatives considered
- Keep hand-writing
_metaand HTML resources. This works for the linkage, but nothing is typed or validated,
the model gets JSON where the spec suggests a summary, and the page side gets no help. - Use
@modelcontextprotocol/ext-appson the page. It is Promise- and zod-based, built on the TypeScript MCP
SDK, and useson*setters that must be registered beforeconnect(). It can't reuse the server'sTool
definitions to type calls and results. - Extend
McpClientto be the page's bridge.McpClientis request/response. The bridge is bidirectional: the
host sends notifications and theui/resource-teardownrequest. It also has its own handshake (ui/initialize). - Vary a tool's
_metaby host. In 2026-07-28,tools/listmust not vary per connection, and a
cacheScope: "public"list may be served to any client. Hosts without Apps already treat a linked tool as a plain
tool, andEnabledWhencovers text-only variants. - Correlate a page with its call through a server-minted handle in the result. The handle arrives only when the
call completes, which is too late for a long-running call. The request id is available from the start.
Solution
Two PRs, opened after #8631 merges because it changes the same files. The server and host parts come first, then the
page module. Both cover the stable 2026-01-26 spec plus the draft's ui/download-file and
ui/notifications/request-teardown. Names below are provisional.
1. Server and host
A new module, effect/ai/McpApps, for the extension itself:
- Schemas and constants:
ResourceMeta(with its CSP and permissions parts),ToolMeta,ClientCapabilities,
EXTENSION_IDandMIME_TYPE. McpApps.registerResourceandMcpApps.resource. The URI must beui://…, or registration fails with
IllegalArgumentError. The MIME typetext/html;profile=mcp-appgoes on the listing and on every content item,
and the resource metadata goes as_meta.uion both.- Registering an app resource declares the extension (
{}) on every protocol revision. An explicitextensions
record still merges in. - A tool annotation that
registerToolkitmerges into the tool's_meta.ui. Registration dies ifTool.Metaalso
setsui, or ifresourceUri(optional, as app-only tools have none) isn'tui://. Only the nested form is
written, not the deprecated_meta["ui/resourceUri"]. McpApps.isSupported(capabilities), usable inEnabledWhenand in handlers.
General fixes, in the existing modules:
- A
_metaoption forMcpServer.registerResourceandMcpServer.resource(the listing entry). toModelOutputbecomes the MCPcontent(text, image, audio, resource link or embedded resource), and
structuredContentstays the encoded success. Tools withouttoModelOutputare unchanged.McpRequestContextgainsrequestId, the JSON-RPC id as received, andsessionId. The docs say where the id is
unique on each transport: within the process on stdio, within the session over sessionful HTTP, and on 2026-07-28
only among the sender's requests still in flight. Fix theclientIddocs.McpClient.make({ extensions: { ui: true } })declares{ mimeTypes: ["text/html;profile=mcp-app"] }.
McpClient.toolkitskips tools whose visibility lacks"model".
Out of scope: result _meta, ui:// templates, and the host-side bridge (rendering, CSP, proxying).
2. The page
effect/ai/McpAppView, platform-agnostic:
- A
McpAppViewservice and layer over anypostMessage-capable target. It performs theui/initialize
handshake and buffers host messages from the moment it connects. - Host data as Effect types:
toolInput(anEffect)toolInputPartial(aStream)toolResult(anEffectthat fails withToolCancelled)hostContext(aSubscribablethat merges partial updates)hostInfoandhostCapabilities
toolCall(tool)decodes the input and the result with the tool's schemas.callTool(tool, params)calls a
server tool through the host, typed from the sameTooldefinitions. A failure is aToolErrorthat keeps the
result, as inMcpClient.callTool.readResource,openLink,sendMessage,updateModelContext,requestDisplayMode,sendSizeChanged,log,
downloadFileandrequestTeardown. They fail fast when the host lacks a capability the stable spec defines.run(program): onui/resource-teardown, the program and every fiber the bridge owns are interrupted, their
finalizers run, and only then does the host get its answer. Any other host request the page doesn't implement gets
JSON-RPC-32601(method not found).- It must work in opaque-origin documents, which is how hosts load pages (cf. #9196).
- A public in-memory test host for pages, in the style of
McpTest.
@effect/platform-browser adds only what needs the DOM: ResizeObserver auto-resize (on by default) and opt-in
helpers for the host's theme, CSS variables and fonts. Is that the right home, or should these stay in the core
module's docs as examples?
Out of scope: app-provided tools and WebMCP (see modelcontextprotocol/ext-apps#797; not planned), sampling from the
page, framework bindings, and HTML bundling.
Development uses MCPJam Inspector to render and check pages against
emulated hosts. The repo's tests use the in-memory test host.
- Lenguaje dominante
- TypeScript
- Estrellas
- 16.7k
- Forks
- 808
- Merge medio
- 11 h 31 min
- PR fusionados (30 d)
- 834
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de Effect-TS/effect
-
Xml: `positions` decode option that puts `line` and `column` on `StartElement` eventsPosiblemente ocupada @effect-bot la tomó hoy. Abierto
Effect-TS/effect#10400 · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
Flat RPC client and AtomRpc query/mutation: type-check cost grows with RPCs in the group × tags calledPosiblemente ocupada @effect-bot la tomó hace 1 día. Abierto
Effect-TS/effect#10396 · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
Support `format: "password"` as `Redacted` in JSON Schema import/export and the OpenAPI generatorPosiblemente ocupada @effect-bot la tomó hace 1 día. Abiertoenhancement
Effect-TS/effect#10394 · 2 comentarios · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
Versioned schemas with version negotiation in HttpApi, Rpc and clusterPosiblemente ocupada @effect-bot la tomó hace 1 día. Abierto
Effect-TS/effect#10383 · 2 comentarios · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
Schema encoding can return an internal symbol across module copiesPosiblemente ocupada @schickling-assistant la tomó hace 1 día. Abierto
Dificultad 4/5 3-5 días Aptitud para principiantes 25/100
Los mantenedores suelen responder en 1 día
Todos los issues de Effect-TS/effect
Issues similares
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
shoelace-style/webawesome#2979 ·
Los mantenedores suelen responder en 1 día
-
Chip source filter and denial markers miss Storage rows when a Storage activity source is pickedAbiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 73/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 77/100
omdsh-dev/DSH-better-sidebar#1141 ·
Los mantenedores suelen responder en 2 días