modelcontextprotocol / modelcontextprotocol/ext-apps

Bug: file_manager widget renders content then infinite re-render loop (oncalltool handler replaced storm) in Claude Desktop

Open
#701 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
2.9k
Forks
387
Avg merge
3h 21m
Merged PRs (30d)
6

Description

Summary

A FastMCP-based MCP server's file_manager widget (the FileUpload provider's drag-and-drop upload UI) renders as a permanent "waiting for content" box in Claude Desktop. Unlike #671 (iframe never renders at all), here the iframe does load and does receive its tool input — the host then enters an infinite React re-render loop, re-registering the widget's oncalltool handler on every pass, so the widget never settles or paints.

This is a regression: the same widget rendered correctly in an earlier Claude Desktop build with the identical server + prefab-ui 0.20.2 pin (the pin has not changed on the server side since it was introduced). The only variable that changed is the Claude Desktop host build, which points at a host-side cause.

Environment
  • Host: Claude Desktop, macOS (auto-updated build as of 2026-07-07).
  • Host bundle (from DevTools, uniquely identifies the client build): vendor-react-CNDMs4aN.js, c818056db-lTLLP9En.js, cfe55e8af-BbSXaC4B.js.
  • Server: FastMCP 3.4.2, prefab-ui 0.20.2, third-party (3P) MCP server exposing the FileUpload provider's file_manager tool + its ui:// renderer resource.
  • Display mode: inline, dark theme.
What the console shows

The renderer loads and receives content cleanly — no CSP block, no missing resource:

[Prefab] hostContext: theme=dark, displayMode=inline
[Prefab] ontoolinput: complete

Then, hundreds of repetitions of the host re-registering the tool-call handler, interleaved with a tight React fiber loop (ns → ts → ns → ts … in vendor-react-*.js):

[MCP Apps] oncalltool handler replaced. Previous handler will no longer be called.
warnIfRequestHandlerReplaced @ c818056db-lTLLP9En.js:3
set oncalltool @ c818056db-lTLLP9En.js:3
(anonymous) @ cfe55e8af-BbSXaC4B.js:1
...
ns @ vendor-react-CNDMs4aN.js:10
ts @ vendor-react-CNDMs4aN.js:10
ns @ vendor-react-CNDMs4aN.js:10
ts @ vendor-react-CNDMs4aN.js:10
... (repeats ~hundreds of times) ...

The one benign line (Unrecognized Content-Security-Policy directive 'webrtc'.) is unrelated — the host simply doesn't recognize that directive; resource-src correctly allows cdn.jsdelivr.net.

Diagnosis

The widget's content arrives (ontoolinput: complete), so this is not a content-delivery, CSP, or "iframe never renders" failure. The host container re-renders the widget in an unbounded loop, and each render re-sets oncalltool (hence the repeated warnIfRequestHandlerReplaced warning). The loop never converges → the widget never finishes → user sees "waiting for content."

Steps to reproduce
  1. Run a FastMCP MCP server with the FileUpload provider enabled (exposes the file_manager widget + a ui:// renderer resource).
  2. Connect it to Claude Desktop as a third-party MCP server.
  3. Invoke file_manager so the upload widget renders inline.
  4. Observe the DevTools console: [Prefab] ontoolinput: complete followed by an unbounded stream of [MCP Apps] oncalltool handler replaced + vendor-react fiber calls; the widget shows "waiting for content."
Ask

What drives the host's unbounded re-render of the widget container here? Is the host recreating/re-rendering the widget on a state change (e.g., per oncalltool registration, tool-input update, or resource read) without a stable dependency, such that a widget that sets oncalltool per render feeds a render→register→render loop? A guard against re-registering an identical handler (or memoizing the container render) would likely break the cycle.

Related: #671 (different symptom — never renders), and #236 on the client repo (iframes render in 1P but not 3P mode).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the issue with the FastMCP FileUpload provider and inspect the listed Claude Desktop bundles: vendor-react-CNDMs4aN.js, c818056db-lTLLP9En.js, and cfe55e8af-BbSXaC4B.js. Trace the repeated oncalltool registration and React fiber loop; done means the file_manager widget renders in Claude Desktop without an unbounded re-render cycle.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
46/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.