anthropics / anthropics/claude-ai-mcp

Cowork plugin mode does not advertise extensions.io.modelcontextprotocol/ui; MCP App iframes never render for plugin-installed servers

Ouverte
#274 1 commentaire 2 réactions 0 personnes assignées Voir sur GitHub
bug
Langage dominant
Aucune donnée de langage
Étoiles
471
Forks
76
Métriques de merge des PR
Aucune PR mergée en 30 j

Description

### What happened?

## MCP App UI doesn't render when MCP server is in a Plugin
When an MCP server is installed as a Cowork plugin (via `Settings → Plugins → Install from file`), Cowork spawns the plugin's stdio MCP server but does not advertise the `extensions.io.modelcontextprotocol/ui` capability during initialize. Additionally, even when the server correctly returns `_meta.ui.resourceUri` on tool definitions, Cowork never issues `resources/read` for the referenced UI resource and no iframe is rendered. Tool results show as plain JSON in the chat.

## MCP App UI renders correctly when MCP server is via a Connector
The same MCP server bundle, served over Streamable HTTP behind ngrok and installed as a remote MCP connector instead of a plugin, does render the iframe correctly. The only thing that changes between the two paths is the surface that initiates the MCP session.

This was verified by dumping the plugin MCP server VM's logs through the MCP tool responses since the VMs themselves aren't accessible from the outside. The "log dumping via MCP tool" capability is included in the repo below for ease of verification.

## Captured initialize from the host

Wire trace captured from inside the plugin VM (via a repro_dump_logs tool that exposes the in-process JSON-RPC log through a tool result envelope, since plugin stderr is not surfaced to host log files):

```
{
"jsonrpc": "2.0",
"id": 0,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": { "roots": {}, "elicitation": {} },
"clientInfo": {
"name": "claude-code",
"title": "Claude Code",
"version": "2.1.128",
"description": "Anthropic's agentic coding tool"
}
}
}
```

`capabilities` contains only roots and elicitation. There is no extensions.io.modelcontextprotocol/ui entry. Per SEP-1865 this means the host has not negotiated MCP Apps support for this session, so the rendering path is correctly skipped — but skipping it is the bug, since the plugin server did advertise the extension on its side.

## Server-side response (proves `_meta.ui.resourceUri` is sent)

For completeness, here is the tools/list response the plugin sends, captured on the wire:

```
{
"result": {
"tools": [
{
"name": "repro_render_view",
"title": "Render the bug-#165 repro view",
"description": "...",
"inputSchema": { "type": "object", "properties": {} },
"_meta": {
"ui": { "resourceUri": "ui://repro/view.html" },
"ui/resourceUri": "ui://repro/view.html"
}
},
...
]
},
"jsonrpc": "2.0",
"id": 1
}
```

After this exchange, the host calls `tools/list` and `resources/list` (so it has full discovery of both the tool's UI ref and the resource) and then calls `tools/call repro_render_view`, but it never issues `resources/read ui://repro/view.html`. The bundle is byte-identical to the one used in HTTP/connector mode where the iframe does render, so it is not a server-side registration problem.

### What did you expect to happen?

Tools registered with `_meta.ui.resourceUri` on plugin-installed MCP servers should render their UI resource as an inline iframe in Cowork, the same way they do when installed as a connector or used in the regular Claude Desktop chat UI.

### Steps to reproduce

Repro repo: https://github.com/AgntUX/claude-ai-mcp-bug-165-repro

1. `cd shared && npm install && node build.mjs`
2. `cd plugin && zip -r ../repro-plugin.zip .`
3. Install the plugin in Cowork via Settings → Plugins → Install from file
4. In a chat, ask Cowork to call `repro_render_view` (no iframe renders; plain JSON shown)
5. Ask Cowork to call `repro_dump_logs` to retrieve the in-VM wire trace including the initialize frame above

For comparison, `cd http-server && ./start.sh` + `ngrok http 5170` + install as a custom connector renders the iframe correctly with the same bundle.

### Area

Tool Discovery / Invocation

### MCP Server (if applicable)

Custom - see included repo to reproduce

### Error messages or logs

```shell

```

### Additional context

Related

- [#61](https://github.com/anthropics/claude-ai-mcp/issues/61) ([comment](https://github.com/anthropics/claude-ai-mcp/issues/61#issuecomment-4285045628)) — @coilysiren observed the same capability gap from a different angle (clientInfo.name = "local-agent-mode-*" doesn't advertise the UI extension); @ochafik replied "looking into it!" This
issue is the same root cause, surfaced through plugin installation rather than direct stdio config, with a captured wire trace.
- #165 — same symptom (no iframe) but different root cause: in #165 the host advertises the UI extension and resources/read succeeds; the iframe handshake hangs after the fetch. That's downstream of where
this bug sits.

Guide de contribution

Aucun guide de contribution indexé pour ce dépôt

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.