Plugin-bundled MCP server is double-registered in Cowork; interactive MCP-UI ("app") tools only render via the connector-UUID registration, not the mcp__plugin_… one

Resolved 💬 2 comments Opened Jun 9, 2026 by sguillema Closed Jul 16, 2026

Summary

Installing a plugin that bundles a remote MCP server (.mcp.json) into Cowork produces two registrations of the same server:

  1. the plugin-bundled registration — mcp__plugin_<plugin>_<server>__<tool>
  2. an auto-registered user connector — mcp__<uuid>__<tool>

Both expose the same tools. But Cowork's interactive-app / Live-Artifacts renderer is wired only to the connector (UUID) registration. When the model calls a tool that returns an MCP-UI resource (an interactive "app") via the plugin-bundled name, Cowork shows raw tool output instead of the interactive UI. The identical call via the connector-UUID name renders correctly.

This forces plugin authors to ship a PreToolUse hook that denies the plugin-bundled interactive tools and steers the model to re-issue against the connector registration — a workaround for what looks like a double-registration / routing bug.

Environment

  • Claude Code / Cowork version: Claude 1.11187.4 (584005) 2026-06-05T20:15:04.000Z
  • OS: macOS 26.3.1
  • Plugin: a plugin bundling a remote HTTP MCP server via .mcp.json, e.g.

``json
{ "mcpServers": { "example": { "type": "http", "url": "https://mcp.example.com/mcp" } } }
``

  • The MCP server exposes a tool that returns an MCP-UI resource (ui://…) — i.e. an interactive app, not just text/JSON.

Steps to reproduce

  1. Build a plugin whose .mcp.json registers a remote MCP server (server key e.g. example) that exposes an interactive tool returning a ui://… resource, e.g. open_widget.
  2. Install the plugin in Cowork. Observe the same server's tools appear under both mcp__plugin_<plugin>_example__open_widget and mcp__<uuid>__open_widget.
  3. Have the model call mcp__plugin_<plugin>_example__open_widget.
  4. Then call mcp__<uuid>__open_widget with identical arguments.

Expected

Both registrations render the interactive MCP-UI app, or the plugin-bundled server is not double-registered as a separate connector at all (single canonical registration).

Actual

  • The plugin-bundled call (step 3) returns raw output; the interactive UI does not display.
  • The connector-UUID call (step 4) renders the interactive UI correctly.

Mechanism / corroborating evidence

This appears to be the interaction of two known behaviours:

  • The Cowork artifact path routes MCP calls through a relay that requires a UUID server id and rejects non-UUID server names — see #55788 ("Live Artifacts CoworkArtifacts.callMcpTool reject … non-UUID server name").
  • Plugin + SDK double-load produces shadowing of duplicate server registrations — see #54858 (interactive MCP widget fails due to SDK double-load shadowing), #53535 ("Shadowing with no-ops to prevent SDK double-load"), and #50586 (a connector shown twice in /plugin).

Net effect: the plugin-bundled registration is the one the model tends to call by default (stable name), but it is not the registration the interactive renderer is bound to.

Current workaround

A PreToolUse hook matching the plugin-bundled interactive tools that returns permissionDecision: "deny" with a reason instructing the model to re-issue the exact call against the connector-UUID registration. This works but is brittle (hard-codes the mcp__plugin_<plugin>_<server>__ prefix and the tool suffixes) and only mitigates the routing symptom — it does not fix the double-registration itself.

Suggested fix / questions

  1. Don't double-register a plugin-bundled MCP server as a separate UUID connector — keep a single canonical registration.
  2. If both must exist, bind the interactive-app renderer to whichever registration the model calls (route by tool, not by registration), so the plugin-bundled name renders too.
  3. At minimum, document the behaviour and the recommended workaround.

Related

  • #55788 (open) — relay rejects non-UUID server name
  • #54858 (closed) — interactive MCP widget fails via double-load shadowing
  • #53535 (closed dup) — "Shadowing with no-ops to prevent SDK double-load"
  • #50586 (closed) — connector listed twice in /plugin
  • #47614 (open) — Cowork custom connectors use UUID as server name

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗