Skip to content

fix: Echo widget useEffect cleanup never calls App.close(), leaving zombie PostMessageTransport listener #55

Description

@nickytonline

Background

Echo.tsx manually wires App in a useEffect but the cleanup never calls activeApp.close(), leaving the PostMessageTransport listener alive on unmount. Under React StrictMode's dev-mode double-invoke, the first App instance becomes a zombie listener receiving every host message alongside the live one.

return () => {
  isMounted = false; // only guards callbacks, doesn't close the transport
};

This is the same bug that @modelcontextprotocol/ext-apps 1.7.0 fixed inside the useApp() hook (#631).

Proposed fix

Refactor Echo.tsx to use the useApp() hook from @modelcontextprotocol/ext-apps/react, which handles the connect/close lifecycle correctly and is a better reference implementation for template users. As part of this, update CLAUDE.md / AGENTS.md and skill docs to recommend useApp() as the preferred pattern over manual useEffect wiring.

Alternatively, keep the manual pattern but add defaultApp.close() to the cleanup (only for the internally-owned instance, not the injected app prop used in tests/Storybook).

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