Skip to content

Re-usable views #430

Open
Open

Description

@idosal

Currently, the spec assumes a new view is rendered per tool call. Hosts may decide to implement variations of their own layout (e.g., a persistent side panel), but each invocation would still create a new instance under the hood. This approach was chosen to simplify the model for the MVP (#62).

Now that the spec is stable, we can examine deferred approaches, such as referencing previously rendered instances using viewSessionId (#295).

Activity

  1. self-assigned this
    on Feb 3, 2026
  2. sam-mfb commented on Feb 3, 2026

    @sam-mfb

    One use case for this is a situation like a board game where the AI plays its turns via direct tool calls and the user plays turns via view interactions. A nice experience would be for the view to not reload when context opens, but just receive new state and update. There may be more serious versions of this, but the concept is the same -- both the user and the model are exchanging interactions with the view being a shared "workspace".

  3. carstonhernke commented on Feb 3, 2026

    @carstonhernke

    Here is a use case that I am working on - a client-side map data visualization tool. This architecture is similar to Figma or Draw.io - most of the processing is done within the browser itself. Today users interact with the application (change the state) via the UI (add map layers, add filters, change the title), however in the future I envision LLMs/agents also being able to interact with the application (change the state) via postMessage().

    I think a nice demo use case would be editing technical diagrams that are written in Mermaid Syntax - AI tools are very good at writing these.

    Today the interaction goes one way:
    Ask LLM to generate diagram → LLM writes Mermaid Syntax → Renders to show user. User must then export to draw.io/lucidchart to edit with a UI

    It could look like this:
    Ask LLM to generate diagram → LLM writes Mermaid Syntax → Loads MCP UI instance that renders Mermaid Syntax → User renames boxes in diagram → LLM receives updated state via postMessage() → Interaction loop continues

    I think the main thing needed to accomplish this is a persisted UI instance that is loaded once, and then has an internal state and can receive & send with postMessage().

    Theoretically web applications can be designed to be stateless and render a given config each time they are loaded. However, there are still significant 'setup' tasks like loading external resources and setting up the DOM that would affect the UI if they had to be repeated every turn in the conversation.

    Some questions come to mind:

    • How is the history of a conversation with multiple turns rendered?
    • Can a user 'undo' actions? Is branching supported?
    • How is state persisted between sessions? The MCP server could maintain it's own state ('save my diagram for later'), or would the embedded iframe use localStorage/OPFS, or would the AI host remember tool usage/arguments?
  4. petrbroz commented on Feb 24, 2026

    @petrbroz

    Thank you @idosal for submitting this. Adding another use case:

    I'm building an MCP server that embeds an interactive 3D viewer for CAD designs. The intended conversation flow looks like this:

    Show me building XYZ

    Isolate all doors

    Color-code the doors by their material

    Each of these steps calls a different tool (preview_design, highlight_objects, color_objects), but all three should act on the same viewer instance. The 3D model is already loaded and initialized, re-loading it from scratch on every tool call takes several seconds and loses transient state like camera position, active selection, and visual overrides.

  5. psylch commented on Feb 24, 2026

    @psylch

    Same experience here — I built a Mermaid diagram editor as an MCP App and the state loss on each tool call is the main UX issue. Zoom, pan, selection, pending edits all gone. Looking forward to viewSessionId.

  6. carlos-cubas commented on Sep 2, 2026

    @carlos-cubas

    There is a second trigger for this class of loss that I think is worth separating from
    view reuse, because it bites within a single tool call and it is the host's to fix:

    A host that offers more than one display mode destroys the View when it moves it.
    Relocating an iframe in the DOM discards its nested browsing context, so a card that
    moves from an inline transcript position into a pip strip or a fullscreen region is
    reloaded — same tool call, same view instance, no re-render from the model.

    Two clauses in the current spec read as if the View survives that move:

    • "Host MUST return the resulting mode (whether updated or not) in the response to
      ui/request-display-mode."
      On our host that MUST was satisfiable and unobservable:
      measured, a grant that moved the card reloaded the frame, so the asking document was
      gone before its own response arrived. Asking for the mode it already held (no move)
      printed the answer fine; refusals always arrived, because a refusal moves nothing.
    • ui/notifications/host-context-changed says the View "SHOULD merge received fields
      with its current context state"
      , and names a display mode change as an example. There
      is no current state to merge into if the View was re-created by that same change.

    Hosts can fix this today, which is why I'd suggest a note rather than a protocol
    change. Element.prototype.moveBefore — the state-preserving atomic move — relocates a
    node without discarding it. Measured on Chrome 151 with a sandboxed srcdoc frame with
    no allow-same-origin, nested one level inside the wrapper being moved (i.e. the shape
    a host actually moves, not the easy case):

    move in-frame state
    appendChild into a new parent 3 → none (document replaced)
    moveBefore into a new parent 3 → 3 (same document)

    Two things cost me time and are worth having in any note:

    • it is on Element.prototype (the ParentNode mixin), not Node.prototype — a
      feature detect on Node reports unsupported in Chrome, where it demonstrably works,
      and then silently selects the destructive path;
    • a detached node cannot be moved atomically (HierarchyRequestError), which is every
      view's first mount.

    Availability: Chrome/Edge 133+, Firefox 144+; WebKit's standards position is support
    and unshipped, so a host needs an appendChild fallback and Safari keeps today's
    behaviour there.

    Suggestion: a non-normative host note under display modes — a host that relocates a
    mounted View in the DOM must move it atomically, or the View is destroyed and the two
    clauses above become unobservable. If widgetSessionId lands, the same requirement
    applies to a reused View: reuse gets you the same instance across tool calls, and a
    subsequent relocation still destroys it unless the move is atomic. Happy to open a PR
    against the spec text if that's useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions