Skip to content

Commit 0cff3ec

Browse files
committed
feat(app-bridge): warn on second ui/initialize
If a View double-mounts (e.g. React StrictMode in dev) without closing the previous App, AppBridge now logs a console.warn when it receives a second ui/initialize. Behavior is unchanged — it still responds and the latest appInfo/appCapabilities replace the previous values — this is purely a diagnostic for host implementers.
1 parent 35f4dc8 commit 0cff3ec

2 files changed

Lines changed: 34 additions & 0 deletions

File tree

src/app-bridge.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import { z } from "zod/v4";
1616

1717
import { App } from "./app";
18+
import { LATEST_PROTOCOL_VERSION } from "./types";
1819
import {
1920
AppBridge,
2021
getToolUiResourceUri,
@@ -2093,6 +2094,30 @@ describe("App <-> AppBridge integration", () => {
20932094
});
20942095
});
20952096

2097+
it("AppBridge warns on a second ui/initialize (View double-mount)", async () => {
2098+
await bridge.connect(bridgeTransport);
2099+
await app.connect(appTransport);
2100+
expect(warnings()).toEqual([]);
2101+
2102+
// Simulate a second View instance re-running the handshake.
2103+
appTransport.send({
2104+
jsonrpc: "2.0",
2105+
id: 99,
2106+
method: "ui/initialize",
2107+
params: {
2108+
protocolVersion: LATEST_PROTOCOL_VERSION,
2109+
appInfo: testAppInfo,
2110+
appCapabilities: {},
2111+
},
2112+
});
2113+
await flush();
2114+
2115+
const doubleInit = warnings().filter((m) =>
2116+
/second ui\/initialize/.test(m),
2117+
);
2118+
expect(doubleInit).toHaveLength(1);
2119+
});
2120+
20962121
it("close() stops further notification delivery (StrictMode cleanup relies on this)", async () => {
20972122
const received: unknown[] = [];
20982123
app.addEventListener("toolresult", (r) => received.push(r));

src/app-bridge.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,6 +1463,15 @@ export class AppBridge extends ProtocolWithEvents<
14631463
): Promise<McpUiInitializeResult> {
14641464
const requestedVersion = request.params.protocolVersion;
14651465

1466+
if (this._appInfo !== undefined) {
1467+
console.warn(
1468+
"[ext-apps] AppBridge received a second ui/initialize. The View may " +
1469+
"be double-mounting (e.g. React StrictMode in dev) without closing " +
1470+
"the previous App instance. Responding normally; the latest " +
1471+
"appInfo/appCapabilities replace the previous values.",
1472+
);
1473+
}
1474+
14661475
this._appCapabilities = request.params.appCapabilities;
14671476
this._appInfo = request.params.appInfo;
14681477

0 commit comments

Comments
 (0)