Skip to content

autoResize: true default causes layout bugs — need height management guidance #502

@psylch

Description

@psylch

Problem

autoResize: true is the default in the Apps SDK, and it is the #1 source of layout bugs for MCP App developers. We hit this issue four separate times during development of our Mermaid MCP App, each with a different root cause:

# Root Cause Symptom
1 height: 100vh + autoResize: true Infinite growth feedback loop: height reported → host adjusts → taller → report again → ∞
2 autoResize: true overriding explicit fixed height Set height: 500px in CSS, but autoResize kept reporting content height instead
3 Fullscreen exit with stale hostHeight After exiting fullscreen, height stayed at ~900px (the fullscreen value)
4 useApp() hook ignores autoResize: false useApp() internally creates App with autoResize: true always — the option is not exposed in UseAppOptions (removed in v1.0.1). Even @ts-expect-error workarounds don't work at runtime.

Issue #4 is particularly painful

The recommended useApp() hook cannot be used by any App that needs height control. Developers must drop down to manual new App() construction:

// Cannot use useApp() — it forces autoResize: true
const app = new App(
  { name: "my-app", version: "0.1.0" },
  {},
  { autoResize: false }  // only available via manual construction
);

Proposals

1. Change autoResize default to false

Most MCP Apps with non-trivial UI (diagrams, editors, canvases) need to control their own height. autoResize: true is only appropriate for simple content-driven displays. Making it opt-in would prevent the most common class of layout bugs.

2. Expose autoResize in useApp() hook

// Currently impossible — useApp() doesn't accept autoResize option
const app = useApp({ autoResize: false });

3. Document the three height strategies

The SDK should document these patterns explicitly:

Strategy When to use Implementation
Fixed height (recommended default) Most Apps autoResize: false + height: 500px + manual sendSizeChanged()
Content-driven with cap Simple display Apps autoResize: true + CSS max-height
Full viewport Fullscreen only requestDisplayMode("fullscreen") + height: 100vh

With explicit warning: 100vh + autoResize: true = infinite growth loop.

4. Improve fullscreen ↔ inline transitions

When displayMode changes from fullscreen to inline, the host should re-send containerDimensions with inline-mode values. Currently the App must remember its pre-fullscreen height and manually reset — which is error-prone.

An onDisplayModeChanged callback that includes the new dimensions would solve this cleanly:

app.ondisplaymodechanged = ({ mode, containerDimensions }) => {
  // mode: "inline" | "fullscreen"
  // containerDimensions: the dimensions for the new mode
};

Recommended pattern (for docs)

const DEFAULT_HEIGHT = 500;

// Do NOT use useApp() — it forces autoResize: true
const app = new App(
  { name: "my-app", version: "0.1.0" },
  {},
  { autoResize: false }
);

// Register handlers BEFORE connect
app.ontoolinput = (params) => { /* ... */ };
app.onhostcontextchanged = (ctx) => {
  const dims = ctx.containerDimensions;
  if (dims?.height) hostHeight = dims.height;
  else if (dims?.maxHeight) hostHeight = Math.min(DEFAULT_HEIGHT, dims.maxHeight);
  else hostHeight = DEFAULT_HEIGHT;
};

await app.connect(transport);

// Report initial size (host doesn't know without autoResize)
app.sendSizeChanged({
  width: document.documentElement.scrollWidth,
  height: DEFAULT_HEIGHT,
});

Context

From building 3 production MCP Apps (Mermaid, PlantUML, Music). Height management consumed more debugging time than any other single issue. Every MCP App developer will hit some variant of these bugs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions