Skip to content

Commit f615123

Browse files
ochafikclaude
andauthored
Add double-connect guard to prevent protocol message handling errors (#450)
* fix: guard against double-connect in App and AppBridge (#429) Calling connect() twice on the same transport instance caused the MCP SDK's Protocol class to chain onmessage handlers, so each incoming message was processed twice. This made incoming responses get consumed by the first processing (resolving the handler) and then trigger a spurious "Received a response for an unknown message ID" error on the second processing pass, since the handler was already removed from the response map. Add an explicit guard in both App.connect() and AppBridge.connect() that throws if the instance is already connected. This converts the silent message-corruption into a clear, actionable error and prevents downstream confusion. Adds three regression tests: - AppBridge.connect() throws when already connected - App.connect() throws when already connected - Demonstrates the double-processing root cause and verifies it is fixed Fixes #429 https://claude.ai/code/session_01XU3FW1TpjBkyRbkBUH5gqR * test: replace redundant regression test with cleaner same-transport guard check The previous third test in "double-connect guard" never actually attempted a double-connect — it created fresh instances and verified normal operation, which every other test already covers. The stray bridge.onupdatemodelcontext and bridgeTransport.close() at the top were leftover noise. Replace it with a focused test that verifies the guard fires even when the caller passes the *same* transport object (not just a different one), which is the exact scenario described in #429. https://claude.ai/code/session_01XU3FW1TpjBkyRbkBUH5gqR --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8dbc06a commit f615123

File tree

3 files changed

+44
-0
lines changed

3 files changed

+44
-0
lines changed

src/app-bridge.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,40 @@ describe("App <-> AppBridge integration", () => {
615615
});
616616
});
617617

618+
describe("double-connect guard", () => {
619+
it("AppBridge.connect() throws if already connected", async () => {
620+
await bridge.connect(bridgeTransport);
621+
await app.connect(appTransport);
622+
623+
// Attempting to connect again with a different transport should throw
624+
const [, secondBridgeTransport] = InMemoryTransport.createLinkedPair();
625+
await expect(bridge.connect(secondBridgeTransport)).rejects.toThrow(
626+
"AppBridge is already connected",
627+
);
628+
});
629+
630+
it("App.connect() throws if already connected", async () => {
631+
await bridge.connect(bridgeTransport);
632+
await app.connect(appTransport);
633+
634+
// Attempting to connect again should throw
635+
const [secondAppTransport] = InMemoryTransport.createLinkedPair();
636+
await expect(app.connect(secondAppTransport)).rejects.toThrow(
637+
"App is already connected",
638+
);
639+
});
640+
641+
it("AppBridge.connect() throws even when called with the same transport", async () => {
642+
await bridge.connect(bridgeTransport);
643+
await app.connect(appTransport);
644+
645+
// Should throw regardless of whether it's the same or a different transport
646+
await expect(bridge.connect(bridgeTransport)).rejects.toThrow(
647+
"AppBridge is already connected",
648+
);
649+
});
650+
});
651+
618652
describe("ping", () => {
619653
it("App responds to ping from bridge", async () => {
620654
await bridge.connect(bridgeTransport);

src/app-bridge.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,6 +1409,11 @@ export class AppBridge extends Protocol<
14091409
* ```
14101410
*/
14111411
async connect(transport: Transport) {
1412+
if (this.transport) {
1413+
throw new Error(
1414+
"AppBridge is already connected. Call close() before connecting again.",
1415+
);
1416+
}
14121417
if (this._client) {
14131418
// When a client was passed to the constructor, automatically forward
14141419
// MCP requests/notifications between the view and the server

src/app.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,11 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
11181118
),
11191119
options?: RequestOptions,
11201120
): Promise<void> {
1121+
if (this.transport) {
1122+
throw new Error(
1123+
"App is already connected. Call close() before connecting again.",
1124+
);
1125+
}
11211126
await super.connect(transport);
11221127

11231128
try {

0 commit comments

Comments
 (0)