anthropics / anthropics/claude-ai-mcp
Cowork plugin mode does not advertise extensions.io.modelcontextprotocol/ui; MCP App iframes never render for plugin-installed servers
- 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.