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).

Activity

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

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